diff -r 000000000000 -r 2b3e5ec03512 servlib/routing/TcaRouter.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlib/routing/TcaRouter.cc Thu Apr 21 14:57:45 2011 +0100 @@ -0,0 +1,977 @@ +/* + * Copyright 2005-2006 University of Waterloo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "conv_layers/ConvergenceLayer.h" +#include "bundling/BundleActions.h" +#include "contacts/ContactManager.h" +#include "bundling/BundleDaemon.h" +#include "routing/RouteTable.h" +#include +#include "contacts/InterfaceTable.h" +#include "conv_layers/TCPConvergenceLayer.h" + +#include "TcaRouter.h" + +namespace dtn { + + + +// Consts + +static const std::string BL = "tca://localhost/bundlelayer"; + + +/////////////////////////////////////////////////////////////////////////////// +// Functions + + +// get_payload_str is a convenient one-call function to get the payload +// of a simple bundle (must not contain any nulls) +static std::string +get_payload_str(const Bundle* b) +{ + size_t len = b->payload().length(); + u_char data[len+1]; + const u_char* p = b->payload().read_data(0, len, data); + return (const char*)p; +} + + +// debug: check for expected number of args in control bundle, w/ message +static bool +check_nargs(const TcaControlBundle& cb, uint n_expected) +{ + if (cb.args_.size() != n_expected) + { + log_err_p("dtn/tca", "TcaRouter: bundle '%s' contains wrong number " + "of args. %d expected.", cb.str().c_str(), n_expected); + return false; + } + return true; +} + + +// debug: log bundle wrapper (source -> dest) and optionally payload too +static void +log_bundle(const std::string& comment, const Bundle* b, bool include_payload) +{ + (void)comment; + (void)b; + + if (include_payload) + log_debug_p("/dtn/tca", "%s [%s] -> [%s] : '%s'", comment.c_str(), + b->source().str().c_str(), b->dest().c_str(), + get_payload_str(b).c_str()); + else + log_debug_p("/dtn/tca", "%s [%s] -> [%s]", comment.c_str(), + b->source().str().c_str(), b->dest().c_str()); +} + + +static void +log_controlbundle(const TcaControlBundle& cb) +{ + log_debug_p("/dtn/tca", " code='%s', args=%d", + cb.code_.c_str(), (u_int)cb.args_.size()); + for (unsigned int i=0; ifind("tcp0"); +} +*/ + +/////////////////////////////////////////////////////////////////////////////// +// class TcaRouter + +TcaRouter::TcaRouter(Role role) + : TableBasedRouter("TcaRouter", "TcaRouter") +{ + role_ = role; + + // Construct the eid of the local admin app. + admin_app_ = BundleDaemon::instance()->local_eid(); + admin_app_.set_app("admin"); + + logpathf("/dtn/tca"); + + log_info("TcaRouter started: role='%s', admin_app='%s'", + get_role_str().c_str(), admin_app_.c_str()); +} + + +std::string +TcaRouter::get_role_str() const +{ + switch (role_) + { + case TCA_MOBILE: return "mobile"; + case TCA_ROUTER: return "router"; + case TCA_GATEWAY: return "gateway"; + default: return "null"; + } + +} + + +void +TcaRouter::handle_bundle_received(BundleReceivedEvent* event) +{ + Bundle* bundle = event->bundleref_.object(); + // log_debug("TcaRouter: handle bundle received: *%p", bundle); + + // Special handling for certain TCA bundles + // Control bundles are identified by their dest eids. + // Unbound data bundles need special handling as + // TCA control bundles are the following: + // Check for control bundles here (REG, COA, ASK, ADV) + + const EndpointID& dest = bundle->dest(); + + if (dest.scheme_str() == "tca") + { + log_bundle("TcaRouter: tca bundle received", bundle, true); + + TcaEndpointID tca_dest(dest); + + if (tca_dest.ssp() == "//registry") + { + handle_register(bundle); + } + + else if (tca_dest.app() == "admin.coa") + { + handle_coa(bundle); + } + + else if (tca_dest.ssp().substr(0,11) == "//anonymous") + { + handle_anonymous_bundle(bundle); + } + + else if (tca_dest.ssp() == "//localhost/bundlelayer") + { + handle_bl_control_bundle(bundle); + } + + else if (tca_dest.host() != admin_app_.host()) + { + // What we're really checking for is whether the bundle is home. + // ie. addressed to some app on the local host. + // If not, then it's a late-bound data bundle: + handle_tca_unbound_bundle(bundle); + } + } + + else + { + // Not a tca control or data bundle. Just forward the bundle. + fwd_to_matching(bundle); + } +} + + +void +TcaRouter::handle_bundle_transmitted(BundleTransmittedEvent* event) +{ + Bundle* b = event->bundleref_.object(); + log_debug("TcaRouter: handle bundle transmitted: *%p", b); + + const EndpointID& dest = b->dest(); + + if (dest.scheme_str() == "tca") + { + // TODO: These control codes appended to app (eg. admin.coa) were + // a bad idea and should be eliminated. I did this initially so that + // we could switch on control code without inspecting bundle payload. + // However, if the bundle is addressed to an admin app, then it's + // fair to assume it is a control bundle and inspect the payload. + // The "adv" case below uses the preferred method. ask and coa should + // follow suit. + TcaEndpointID tca_dest(dest); + if (tca_dest.app() == "admin.coa") + { + TcaControlBundle cb(get_payload_str(b)); + on_coa_transmitted(b, cb); + } + else if (tca_dest.app() == "admin.ask") + { + TcaControlBundle cb(get_payload_str(b)); + on_ask_transmitted(b, cb); + } + else if (tca_dest.app() == "admin") + { + TcaControlBundle cb(get_payload_str(b)); + if (cb.code_ == "adv") + { + on_adv_transmitted(b, cb); + } + } + } +} + + +// Note: ContactUp and ContactDown are generated by a sending node when a +// connection is opened. As such, they're probably not relevant to TCA. + +void +TcaRouter::handle_contact_up(ContactUpEvent* event) +{ + ASSERT(event->contact_->link() != NULL); + ASSERT(!event->contact_->link()->isdeleted()); + + // Note: *must* call the base class handler so that existing bundles + // can be checked against the new contact. + TableBasedRouter::handle_contact_up(event); + log_debug("TcaRouter::contact up"); + post_bundle(BL, admin_app_, "contact_up"); +} + + +void +TcaRouter::handle_contact_down(ContactDownEvent* event) +{ + (void)event; + log_debug("TcaRouter::contact down"); + post_bundle(BL, admin_app_, "contact_down"); + +} + + +void +TcaRouter::handle_link_available(LinkAvailableEvent* event) +{ + ASSERT(event->link_ != NULL); + ASSERT(!event->link_->isdeleted()); + + // Note: *must* call the base class handler so that existing bundles + // can be checked against the new link. + TableBasedRouter::handle_link_available(event); + log_debug("TcaRouter::link available"); + post_bundle(BL, admin_app_, "link_available"); +} + + +void +TcaRouter::handle_link_unavailable(LinkUnavailableEvent* event) +{ + (void)event; + log_debug("TcaRouter::link unavailable"); + post_bundle(BL, admin_app_, "link_unavailable"); +} + + +void +TcaRouter::handle_shutdown_request(ShutdownRequest* event) +{ + (void)event; + log_debug("TcaRouter::daemon shutdown"); + post_bundle(BL, admin_app_, "daemon_shutdown"); +} + + +// fwd_to_all "broadcasts" a bundle to all tca routes (ie. "tca://*") +// This is dead code at the moment. + +int +TcaRouter::fwd_to_all(Bundle* bundle) +{ + RouteEntryVec matches; + RouteEntryVec::iterator iter; + + std::string pattern = "tca://*"; + EndpointID tca_all = pattern; + + route_table_->get_matching(tca_all, &matches); + + int count = 0; + for (iter = matches.begin(); iter != matches.end(); ++iter) + { + log_debug("TcaRouter::fwd_to_all: %s", + (*iter)->dest_pattern().str().c_str()); + fwd_to_nexthop(bundle, *iter); + ++count; + } + + log_debug("TcaRouter::fwd_to_all dest='%s': %d matches", + bundle->dest().c_str(), count); + return count; +} + + +// fwd_to_matching function overridden from TableBasedRouter +// This is necessary to filter outgoing bundles. fwd_to_matching is +// called via TableBasedRouter::check_next_hop on a variety of events +// including link_available and on contact_up. + +int +TcaRouter::fwd_to_matching(Bundle* bundle, const LinkRef& next_hop) +{ + ForwardingRule fwd_rule = get_forwarding_rule(bundle); + return fwd_to_matching_r(bundle, next_hop, fwd_rule); +} + + +// Specialized fwd_to_matching function with selective default-route handling +// You can foward to the default route either never, always, or "if necessary" +// where "if necessary" means iff there are no non-default routes. + +int +TcaRouter::fwd_to_matching_r(Bundle* bundle, const LinkRef& next_hop, + ForwardingRule fwd_rule) +{ + log_debug("TcaRouter::fwd_to_matching_r: owner='%s'", + bundle->owner().c_str()); + log_debug("TcaRouter::fwd_to_matching_r: fwd_rule=%d", fwd_rule); + + if (fwd_rule == FWD_NEVER) return 0; // no matches + + RouteEntryVec matches; + RouteEntryVec::iterator iter; + + route_table_->get_matching(bundle->dest(), &matches); + + // First step - split out the default route + RouteEntry* default_route = NULL; + RouteEntryVec hard_matches; // non-default matches + for (iter = matches.begin(); iter != matches.end(); ++iter) + { + if ((*iter)->dest_pattern().str() == "tca://*") + { + default_route = *iter; + } + else + { + hard_matches.push_back(*iter); + } + } + + if (fwd_rule == FWD_UDR_EXCLUSIVELY) + { + if (default_route != NULL) + { + fwd_to_nexthop(bundle, default_route); + return 1; // 1 match + } + } + + int count = 0; + + // handle default route... + if (fwd_rule == FWD_UDR_ALWAYS || + (fwd_rule == FWD_UDR_IFNECESSARY && hard_matches.size() == 0)) + { + if (default_route != NULL) + { + fwd_to_nexthop(bundle, default_route); + ++ count; + } + } + + // handle hard matches... + for (iter = hard_matches.begin(); iter != hard_matches.end(); ++iter) + { + if (next_hop == NULL || (next_hop == (*iter)->link())) + { + fwd_to_nexthop(bundle, *iter); + ++count; + } + else + { + log_debug("fwd_to_matching_r dest='%s': " + "ignoring match %s since next_hop link %s set", + bundle->dest().c_str(), (*iter)->link()->name(), + next_hop->name()); + } + } + + log_debug("fwd_to_matching_r dest='%s': %d matches", + bundle->dest().c_str(), count); + + return count; +} + + +bool +TcaRouter::on_coa_transmitted(Bundle* b, const TcaControlBundle& cb) +{ + log_debug("TcaRouter: COA bundle transmitted"); + + TcaWrappedBundle wb(cb); + + log_debug(" coa: source=%s, dest=%s", + b->source().c_str(), + b->dest().c_str()); + + // todo: use a WrappedBundle here + std::string coa_sent_payload = "coa_sent:"; + coa_sent_payload += b->source().str(); + coa_sent_payload += "\t"; + coa_sent_payload += b->dest().str(); + coa_sent_payload += "\t"; + coa_sent_payload += cb.args_[0]; // link_addr of mobile, from body of coa + + log_debug(" coa_sent, payload='%s'", coa_sent_payload.c_str()); + post_bundle(BL, admin_app_, coa_sent_payload); + + return true; +} + + +bool +TcaRouter::on_ask_transmitted(Bundle* b, const TcaControlBundle& cb) +{ + log_debug("TcaRouter: ASK bundle transmitted"); + + if (!check_nargs(cb, 1)) return false; + + // todo: use a WrappedBundle here + std::string ask_sent= "ask_sent:"; + ask_sent += b->source().str(); + ask_sent += "\t"; + ask_sent += b->dest().str(); + ask_sent += "\t"; + ask_sent += cb.args_[0]; + + log_debug(" ask sent, payload='%s'", ask_sent.c_str()); + post_bundle(BL, admin_app_, ask_sent); + + return false; +} + + +bool +TcaRouter::on_adv_transmitted(Bundle* b, const TcaControlBundle& cb) +{ + log_debug("TcaRouter: ADV bundle transmitted"); + + if (!check_nargs(cb, 2)) return false; + + // todo: use a WrappedBundle here + std::string adv_sent= "adv_sent:"; + adv_sent += b->source().str(); + adv_sent += "\t"; + adv_sent += b->dest().str(); + adv_sent += "\t"; + adv_sent += cb.args_[0]; + adv_sent += "\t"; + adv_sent += cb.args_[1]; + + log_debug(" adv_sent, payload='%s'", adv_sent.c_str()); + post_bundle(BL, admin_app_, adv_sent); + + return false; +} + + +bool +TcaRouter::handle_register(Bundle* b) +{ + // DK: New stuff - experimental + // Register bundles come either from + // 1) A client application (zero args, source eid important), or + // 2) An admin app (2 args) + + if (b->source().str() == admin_app_.str()) + { + // The local admin app sent this bundle. Just forward it to the + // default route: + LinkRef link("TcaRouter::handle_register: fwd_to_matching null"); + fwd_to_matching_r(b, link, FWD_UDR_EXCLUSIVELY); + } + else + { + // The bundle came from either a local app (non-admin) or + // else another node. In either case, deliver it to local + // admin app. + TcaControlBundle cb(get_payload_str(b)); + + TcaWrappedBundle reg_received("reg_received", + b->source().str(), b->dest().str()); + + log_debug("TcaRouter::handle_register:"); + log_controlbundle(cb); + + if (cb.args_.size() == 2) + { + // This bundle came from another node, with mobile_eid + // and last_hop fields filled in. + reg_received.append_arg(cb.args_[0]); + reg_received.append_arg(cb.args_[1]); + } + else + { + // This bundle came from an app on the local node + // so fill in mobile_eid and NULL last_hop + reg_received.append_arg(b->source().str()); + reg_received.append_arg("NULL"); + } + + post_bundle(BL, admin_app_, reg_received.str()); + } + + return true; +} + + +bool +TcaRouter::handle_coa(Bundle* b) +{ + log_debug("TcaRouter: COA bundle received"); + + // Propagate it along the reverse path, ignoring default route: + LinkRef link("TcaRouter::handle_coa: fwd_to_matching null"); + fwd_to_matching_r(b, link, FWD_UDR_NEVER); + + // The old route is deleted after the coa bundle has been + // transmitted, from on_coa_transmitted + return true; +} + + +bool +TcaRouter::handle_anonymous_bundle(Bundle* b) +{ + // these are bundles addressed to a destination eid that begins + // with tca://anonymous + // so far, the "ask" bundle is the only one of its kind + + TcaEndpointID dest(b->dest()); + + TcaControlBundle cb(get_payload_str(b)); + + if (cb.code_ == "ask") + { + return handle_ask(b, cb); + } + else + { + log_debug("TcaRouter:: unrecognized anonymous bundle code '%s'", + cb.code_.c_str()); + return false; + } +} + + +bool +TcaRouter::handle_ask(Bundle* b, const TcaControlBundle& cb) +{ + if (is_local_source(b)) + { + // if the ask originated at this node, just forward it to + // matching, omitting the default route. + LinkRef link("TcaRouter::handle_ask: fwd_to_matching null"); + fwd_to_matching_r(b, link, FWD_UDR_NEVER); + } + else + { + if (!check_nargs(cb, 1)) return false; + + // generate ask_received for local admin app + std::string payload = "ask_received:"; + payload += b->source().str(); + payload += "\t"; + payload += b->dest().str(); + payload += "\t"; + payload += cb.args_[0]; + + post_bundle(BL, admin_app_, payload); + } + + return true; +} + + + +// handle_tca_control_bundle handles bundles addressed to +// "localhost/bundlelayer" from the local control application + +bool +TcaRouter::handle_bl_control_bundle(Bundle* b) +{ + TcaControlBundle cb(get_payload_str(b)); + + // handle ctl bundles: + if (cb.code_ == "ask") + { + return handle_ask(b, cb); + } + else if (cb.code_ == "get_routes") + { + return handle_get_routes(b, cb); + } + else if (cb.code_ == "add_route") + { + return handle_add_route(b, cb); + } + else if (cb.code_ == "del_route") + { + return handle_del_route(b, cb); + } + + log_debug("TcaRouter: unknown control bundle type '%s'", cb.code_.c_str()); + return false; + +} + + +bool +TcaRouter::handle_bl_ask(Bundle* b, const TcaControlBundle& cb) +{ + (void)cb; + // Note: We should never get here! asks should be addressed to the + // control app, not bundle layer. + return post_bundle(BL, b->source(), + "adv:Don\'t ASK me. You should probably ASK the Control App," + " not the Bundle Layer."); +} + + +bool +TcaRouter::handle_get_routes(Bundle* b, const TcaControlBundle& cb) +{ + if (!check_nargs(cb, 1)) return false; + + log_debug("TcaRouter:: get_routes bundle received. body = '%s'", + cb.args_[0].c_str()); + + RouteEntryVec matches; + RouteEntryVec::iterator iter; + + EndpointIDPattern pattern(cb.args_[0]); + route_table_->get_matching(pattern, &matches); + + std::string response = "routes:"; + for (iter = matches.begin(); iter != matches.end(); ++iter) + { + response += (*iter)->dest_pattern().str().c_str(); + response += "\t"; + } + + post_bundle(BL, b->source(), response); + + return true; +} + + +bool +TcaRouter::handle_add_route(Bundle* b, const TcaControlBundle& cb) +{ + (void)b; + + if (!check_nargs(cb, 2)) return false; + + const std::string& pattern = cb.args_[0]; + const std::string& link = cb.args_[1]; + + log_debug("TcaRouter:: add_route bundle received. " + "pattern='%s', link='%s'\n", pattern.c_str(), link.c_str()); + + if (pattern.length() == 0 || link.length() == 0) return false; + + // TODO: Some syntax-checking would be a very good idea. Right now, + // just blast away: + return create_route(pattern, link); +} + + +bool +TcaRouter::handle_del_route(Bundle* b, const TcaControlBundle& cb) +{ + (void)b; + + if (!check_nargs(cb, 1)) return false; + + log_debug("TcaRouter:: del_route bundle received. body = '%s'", + cb.args_[0].c_str()); + + // TODO: Some syntax-checking would be a very good idea. Right now, + // just blast away: + route_table_->del_entries(cb.args_[0]); + return true; +} + + +// handle_tca_unbound_bundle handles routing of regular late-bound tca data +// bundles. +// This function assumes that this router is not the dest endpoint. +// for the bundle. The logic goes like this: +// First, forward the bundle to any existing route to dest, using the default +// route iff there are no other matches. +// If there are no matches at all, and if the local node is a gateway, +// then push a "unb" bundle up to the control app for special handling. + +bool +TcaRouter::handle_tca_unbound_bundle(Bundle* bundle) +{ + log_debug("TcaRouter::handle_tca_unbound_bundle..."); + + LinkRef link("TcaRouter::handle_tca_unbound_bundle: fwd_to_matching null"); + int n_matches = fwd_to_matching_r(bundle, link, FWD_UDR_IFNECESSARY); + + if (n_matches == 0) + { + if (role_ == TCA_ROUTER) + { + // If I'm a router, this is an error! All tca bundles must have + // a default route up to a gateway. + log_err("TcaRouter: Error. TCA_ROUTER has no route to dest %s", + bundle->dest().c_str()); + return false; + } + else if (role_ == TCA_GATEWAY) + { + // If I'm a gateway, try late-binding... + // Leave the unbound bundle alone, but push a control bundle up + // to the control app, which will get registration and create a + // route if possible. + std::string payload = "unb:"; + payload += bundle->dest().str(); + post_bundle(BL, admin_app_, payload); + } + } + return true; +} + + + +bool +TcaRouter::is_local_source(Bundle* b) +{ + TcaEndpointID src(b->source()); + return src.get_hostid() == admin_app_.get_hostid(); +} + + +TcaRouter::ForwardingRule +TcaRouter::get_forwarding_rule(Bundle* b) +{ + // all non-tca bundles should always be forwarded to all routes + if (b->dest().scheme_str() != "tca") return FWD_UDR_ALWAYS; + + TcaEndpointID dest(b->dest()); + + if (dest.ssp() == "//registry") + { + // forward to default route (exclusively) iff sent by the local + // admin app + if (b->source().str() == admin_app_.str()) return FWD_UDR_EXCLUSIVELY; + else return FWD_NEVER; + } + + else if (dest.app() == "admin.coa") + { + // Never us the default route for the coa bundles. They are on their + // way "down" the tree to a mobile and we don't want them getting + // forwarded back up to a gateway too. + return FWD_UDR_NEVER; + } + + else if (dest.ssp().substr(0,11) == "//anonymous") + { + // Assume this is an ask bundle. (WARNING: If there are ever any + // anonymous bundles other than ASK, this should be changed!) + // For ask, forward only if originating at local node, and never + // to the default route. + if (is_local_source(b)) return FWD_UDR_NEVER; + else return FWD_NEVER; + } + + else if (dest.ssp() == "//localhost/bundlelayer") + { + // Never forward bundles sent to the local bundle layer + return FWD_NEVER; + } + + // These are all the special control dest cases. What remains are any + // ordinarily addressed tca bundles + else + { + if (dest.host() == admin_app_.host()) + { + // If addressed to local admin app, do not forward. This bundle + // is home. + return FWD_NEVER; + } + else + { + // Treat this as an unbound tca bundle. Forward to matches, using + // default route iff no other matches. + return FWD_UDR_IFNECESSARY; + } + } +} + + +// create_link creates a link for the given link_addr iff it doesn't already +// exist. +// link_addr must be in the form "tcp://host:port" + +LinkRef +TcaRouter::create_link(const std::string& link_addr) +{ + // Note that deleting the old one and re-creating it is the wrong + // thing to do, because when the link is re-created all pending + // bundles (inlcuding the register bundle that initiated this) will + // be matched and forwarded to the new link. + + EndpointID link_eid(link_addr); + std::string clayer_name = link_eid.scheme_str(); + const std::string& ssp = link_eid.ssp(); + std::string host = ssp.substr(2, ssp.length()); // chop off the "//" + + ContactManager* p_man = BundleDaemon::instance()->contactmgr(); + + // Check if there's an existing link of the same name. + LinkRef p_link("TcaRouter::create_link: return value"); + + p_link = p_man->find_link(host.c_str()); + if (p_link != NULL) return p_link; + + ConvergenceLayer* cl = ConvergenceLayer::find_clayer(clayer_name.c_str()); + if (!cl) { + log_err("TcaRouter: create_link failed: invalid convergence layer" + " '%s'", clayer_name.c_str()); + return p_link; + } + + p_link = Link::create_link(host, Link::ONDEMAND, cl, host.c_str(), 0, NULL); + if (p_link == NULL) return p_link; + + // Add the link to contact manager's table, which posts a + // LinkCreatedEvent to the daemon + if (!p_man->add_new_link(p_link)) { + p_link->delete_link(); + log_err("TcaRouter::create_link: " + "failed to add new link %s", p_link->name()); + p_link = NULL; + return p_link; + } + + return p_link; +} + + +RouteEntry* +TcaRouter::create_route(const std::string& pattern, const LinkRef& p_link) +{ + + log_debug("TcaRouter::create_route: pattern=%s, p_link=%p", + pattern.c_str(), p_link.object()); + + RouteEntry* p_entry = new RouteEntry(pattern, p_link); + p_entry->set_action(ForwardingInfo::COPY_ACTION); + + route_table_->add_entry(p_entry); + + return p_entry; +} + + +bool +TcaRouter::create_route(const std::string& pattern, + const std::string& link_addr) +{ + // First find the right link, or create a new one if necesary + LinkRef p_link = create_link(link_addr); + if (p_link == NULL) + { + log_err("TcaRouter::create_route: create_link failed"); + return false; + } + + // Now create the new route entry + if (!create_route(pattern, p_link)) + { + log_err("TcaRouter::create_route: create_route failed"); + return false; + } + + return true; +} + + +bool +TcaRouter::post_bundle(const EndpointID& src, const EndpointID& dest, + const std::string& payload) +{ + + log_debug("TcaRouter::post_bundle: [%s] -> [%s] : '%s'\n", + src.c_str(), dest.c_str(), payload.c_str()); + + // Construct bundle + Bundle* b = new Bundle(); + + // if source is unspecified, use bundlelayer + if (src.length() == 0) + b->mutable_source()->assign("tca://localhost/bundlelayer"); + else + b->mutable_source()->assign(src); + + b->mutable_dest()->assign(dest); + b->mutable_custodian()->assign(BundleDaemon::instance()->local_eid()); + b->mutable_replyto()->assign(BundleDaemon::instance()->local_eid()); + + b->mutable_payload()->set_data(payload); + + // We need to set non-zero expiration or else the bundle + // expires as soon as it arrives. + b->set_expiration(3600); + + // The default values are ok for the rest of the bundle fields. + + // Post the bundle by generating a BundleReceivedEvent + BundleReceivedEvent* p_event = new BundleReceivedEvent(b, EVENTSRC_ADMIN); + BundleDaemon::instance()->post(p_event); + + return true; +} + + +// dead code at the moment: +bool +TcaRouter::push_wrapped_bundle(const std::string& code, + const EndpointID& src, + const EndpointID& dest, + const std::string& bsp) +{ + std::string payload = code; + payload += ":"; + payload += src.str(); + payload += "\t"; + payload += dest.str(); + payload += "\t"; + payload += bsp; + return post_bundle(BL, admin_app_, payload); +} + + + + +} // namespace dtn