diff -r 000000000000 -r 2b3e5ec03512 servlib/prophet/Encounter.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlib/prophet/Encounter.cc Thu Apr 21 14:57:45 2011 +0100 @@ -0,0 +1,1059 @@ +/* + * Copyright 2007 Baylor University + * + * 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. + */ + +#include "BundleCore.h" +#include "HelloTLV.h" +#include "RIBDTLV.h" +#include "RIBTLV.h" +#include "OfferTLV.h" +#include "ResponseTLV.h" +#include "TLVCreator.h" +#include "Encounter.h" + +#define NEXT_TID (++next_tid_ == 0) ? ++next_tid_ : next_tid_ + +#define PROPHET_TLV(_tlv, _result, _tid) do { \ + _tlv = new ProphetTLV( \ + oracle_->core()->prophet_id(), \ + oracle_->core()->prophet_id(next_hop_), \ + _result, \ + local_instance_, \ + remote_instance_, \ + (_tid == 0) ? NEXT_TID : _tid); \ + } while (0) + +#define SEND_ACK(_tid) send_hello(HelloTLV::ACK, \ + ProphetTLV::NoSuccessAck,_tid) +#define SEND_SYN(_tid) send_hello(HelloTLV::SYN, \ + ProphetTLV::NoSuccessAck,_tid) +#define SEND_SYNACK(_tid) send_hello(HelloTLV::SYNACK, \ + ProphetTLV::NoSuccessAck,_tid) +#define SEND_RSTACK(_tid) send_hello(HelloTLV::RSTACK,\ + ProphetTLV::Failure,_tid) + +#define LOG(_level, _args...) oracle_->core()->print_log( \ + name_.c_str(), BundleCore::_level, _args ) + +#define SET_STATE(_new) do { LOG(LOG_DEBUG, "state_ %s -> %s %s:%d", \ + state_to_str(state_), state_to_str(_new), __FILE__, __LINE__); \ + state_ = _new; \ + } while (0) + +#define UPDATE_PEER_VERIFIER(_sender_instance) do { \ + remote_instance_ = _sender_instance; \ + LOG(LOG_DEBUG, "update peer verifier %d", \ + (_sender_instance)); } while (0) \ + +#define ASSIGN_ROLES(_s,_r) do { \ + if (synsender_) { \ + _s = oracle_->core()->local_eid(); \ + _r = next_hop_->remote_eid(); } \ + else { \ + _s = next_hop_->remote_eid(); \ + _r = oracle_->core()->local_eid(); }\ + } while (0) + + +namespace prophet +{ + +Encounter::Encounter(const Link* nexthop, + Oracle* oracle, + u_int16_t instance) + : ExpirationHandler(), + oracle_(oracle), + local_instance_(instance), + remote_instance_(0), + tid_(0), + next_tid_(0), + timeout_(oracle_->params()->hello_interval() * 100), + next_hop_(nexthop), + tlv_(NULL), + synsender_(oracle_->core()->local_eid().compare( + next_hop_->remote_eid()) < 0), + state_(WAIT_NB), + synsent_(false), + estab_(false), + neighbor_gone_(false), + remote_nodes_(oracle_->core(),"remote",false), + hello_rate_(0), + data_sent_(time(0)), + data_rcvd_(time(0)), + alarm_(NULL) +{ + std::string name("encounter-"); + char buff[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + size_t len = snprintf(buff,10,"%d",instance); + name.append(buff,len); + ExpirationHandler::set_name(name.c_str()); + + if (synsender_) + if (send_hello(HelloTLV::SYN)) + SET_STATE(SYNSENT); + + // set up reminder for timeout_ milliseconds + alarm_ = oracle_->core()->create_alarm(this,timeout_); + if (alarm_ == NULL) neighbor_gone_ = true; + + LOG(LOG_DEBUG,"constructor(%s,%u)",nexthop->remote_eid(),instance); + +} + +Encounter::Encounter(const Encounter& e) + : ExpirationHandler(e), + oracle_(e.oracle_), + local_instance_(e.local_instance_), + remote_instance_(e.remote_instance_), + tid_(e.tid_), + timeout_(e.timeout_), + next_hop_(e.next_hop_), + tlv_(e.tlv_), + synsender_(e.synsender_), + state_(e.state_), + synsent_(e.synsent_), + estab_(e.estab_), + neighbor_gone_(e.neighbor_gone_), + local_ribd_(e.local_ribd_), + remote_ribd_(e.remote_ribd_), + remote_offers_(e.remote_offers_), + local_response_(e.local_response_), + remote_nodes_(e.remote_nodes_), + hello_rate_(e.hello_rate_), + data_sent_(e.data_sent_), + data_rcvd_(e.data_rcvd_), + alarm_(NULL) +{ + LOG(LOG_DEBUG,"copy constructor(%u)",local_instance_); + alarm_ = oracle_->core()->create_alarm(this,e.alarm_->time_remaining()); + if (alarm_ == NULL) neighbor_gone_ = true; +} + +Encounter::~Encounter() +{ + if (alarm_ != NULL && alarm_->pending()) + alarm_->cancel(); +} + +void +Encounter::hello_interval_changed() +{ + if (neighbor_gone_) return; + + // nothing to do if it's the same value + u_int timeout = oracle_->params()->hello_interval() * 100; + if (timeout == timeout_) return; + + // nothing to do for alarm_ unless it is pending + if (alarm_->pending()) + { + if (alarm_->time_remaining() == 0) + // no change, let timeout handler catch it + goto set_timeout; + + // grab the difference from old to new + u_int diff; + u_int new_alarm; + if (timeout_ > timeout) + { + // new timeout is shorter than old + diff = timeout_ - timeout; + new_alarm = alarm_->time_remaining() < diff ? + 0 : alarm_->time_remaining() - diff; + } + else + { + // new timeout is longer than old + diff = timeout - timeout_; + new_alarm = alarm_->time_remaining() + diff; + } + + // take the old timeout value off the schedule + alarm_->cancel(); + + // write new timeout value to alarm + alarm_ = oracle_->core()->create_alarm(this,new_alarm); + + // give up on error + if (alarm_ == NULL) neighbor_gone_ = true; + } + +set_timeout: + LOG(LOG_DEBUG,"hello_interval changed (%u -> %u)", timeout_,timeout); + + // store the new timeout + timeout_ = timeout; +} + +bool +Encounter::receive_tlv(ProphetTLV* tlv) +{ + if (neighbor_gone_) return false; + + if (tlv == NULL) + return false; + + LOG(LOG_DEBUG,"receive_tlv"); + + // update member pointer + tlv_ = tlv; + + // update timestamp + data_rcvd_ = time(0); + + // disarm + if (alarm_->pending()) + alarm_->cancel(); + + // capture transaction id + tid_ = tlv_->transaction_id(); + + BaseTLV* bt = NULL; + bool ok = true; + // distribute the individual TLVs to the correct handler + while ( (bt = tlv_->get_tlv()) != NULL && ok ) + { + ok = dispatch_tlv(bt); + delete bt; + } + + // clean up memory + delete tlv_; + tlv_ = NULL; + + if (ok) + { + // clean up old alarm: host implementation cleans up cancelled + // alarms, else we clean up our own spent alarms + if (alarm_->pending()) + alarm_->cancel(); + else + delete alarm_; + // reschedule the timeout handler + alarm_ = oracle_->core()->create_alarm(this,timeout_,true); + + // give up on error + if (alarm_ == NULL) neighbor_gone_ = true; + } + else + // fail out on error + neighbor_gone_ = true; + + return ok; +} + +bool +Encounter::dispatch_tlv(BaseTLV* tlv) +{ + LOG(LOG_DEBUG,"dispatch_tlv"); + + bool ok = false; + bool pre_dispatch = estab_; + + switch (tlv->typecode()) + { + case BaseTLV::HELLO_TLV: + ok = handle_hello_tlv(tlv); + if (ok && !pre_dispatch && estab_) + { + // Hello procedure just completed ... move from ESTAB to + // either CREATE_DR (synsender_) or WAIT_DICT (!synsender_) + if (synsender_) + { + SET_STATE(CREATE_DR); + bool ok = send_dictionary_rib(); + if (ok) SET_STATE(SEND_DR); + return ok; + } + else + SET_STATE(WAIT_DICT); + } + return ok; + case BaseTLV::RIBD_TLV: + if (! estab_) break; + return handle_ribd_tlv(tlv); + case BaseTLV::RIB_TLV: + if (! estab_) break; + return handle_rib_tlv(tlv); + case BaseTLV::OFFER_TLV: + if (! estab_) break; + ok = handle_offer_tlv(tlv); + if (ok && synsender_ && state_ == WAIT_INFO) + { + // finished Initiator phase, now switch to Listener + SET_STATE(WAIT_DICT); + } + return ok; + case BaseTLV::RESPONSE_TLV: + if (! estab_) break; + ok = handle_response_tlv(tlv); + if (ok && !synsender_ && state_ == WAIT_INFO) + { + // finished Listener phase, now switch to Initiator + SET_STATE(CREATE_DR); + ok = send_dictionary_rib(); + if (ok) SET_STATE(SEND_DR); + } + return ok; + case BaseTLV::ERROR_TLV: + case BaseTLV::UNKNOWN_TLV: + default: + return false; + } + + // not reached unless !estab && tlv->typecode() != HELLO_TLV + + hello_rate_ = 2; + if (state_ == SYNSENT) + return SEND_SYN(NEXT_TID); + else if (state_ == SYNRCVD) + return SEND_SYNACK(tid_); + + // unless ERROR or UNKNOWN, do not fail out of this peering session + return true; +} + +void +Encounter::handle_bundle_received(const Bundle* b) +{ + if (state_ != REQUEST) return; + if (b == NULL) return; + + LOG(LOG_DEBUG,"handle_bundle_received: " + "%s %u:%u",b->destination_id().c_str(), + b->creation_ts(), b->sequence_num()); + + // reduce dest id to node id + std::string eid = oracle_->core()->get_route(b->destination_id()); + + // translate node id to sid + u_int sid = local_ribd_.find(eid); + + // check for dictionary error + if (sid == Dictionary::INVALID_SID) return; + + // reduce list of responses by this one + local_response_.remove_entry( + b->creation_ts(), + b->sequence_num(), + sid); + + // clean up previous alarm: host implementation cleans up cancelled + // alarms, else we clean up our own spent alarms + if (alarm_->pending()) + alarm_->cancel(); + else + delete alarm_; + + // set up reminder for timeout_ milliseconds + alarm_ = oracle_->core()->create_alarm(this,timeout_,true); + + // give up on error + if (alarm_ == NULL) neighbor_gone_ = true; +} + +void +Encounter::handle_timeout() +{ + LOG(LOG_DEBUG,"handle_timeout"); + + if (neighbor_gone_) return; + + bool ok = false; + switch (state_) + { + case WAIT_NB: + ok = true; + SET_STATE(WAIT_NB); + break; + case SYNSENT: + ok = SEND_SYN(NEXT_TID); + SET_STATE(SYNSENT); + break; + case SYNRCVD: + ok = SEND_SYNACK(tid_); + SET_STATE(SYNRCVD); + break; + case ESTAB: + // should not be reached + ok = false; + break; + case WAIT_DICT: + ok = SEND_ACK(tid_); + SET_STATE(WAIT_DICT); + break; + case WAIT_RIB: + ok = SEND_ACK(tid_); + SET_STATE(WAIT_DICT); + break; + case OFFER: + ok = send_offer(); + SET_STATE(OFFER); + break; + case SEND_DR: + ok = send_dictionary_rib(); + if (ok) SET_STATE(SEND_DR); + break; + case REQUEST: + ok = send_response(); + break; + case WAIT_INFO: + // if time_diff(now,data_sent) > hello_dead/2 + // start InfoExch again + if (time(0) - data_rcvd_ > + (oracle_->params()->hello_dead() * + oracle_->params()->hello_interval() / 20)) + { + if (synsender_) + { + SET_STATE(CREATE_DR); + ok = send_dictionary_rib(); + if (ok) SET_STATE(SEND_DR); + } + else + { + SET_STATE(WAIT_DICT); + ok = true; + } + } + else + ok = true; + break; + default: + break; + } + // if error sending message, or if silence exceeds max, then + // signal session death and fail out + if (!ok || + (time(0) - data_rcvd_) > + (oracle_->params()->hello_dead() * + /* convert 100's of ms to seconds */ + oracle_->params()->hello_interval() / 10)) + { + neighbor_gone_ = true; + return; + } + + // clean up previous alarm: host implementation cleans up cancelled + // alarms, else we clean up our own spent alarms + if (alarm_->pending()) + alarm_->cancel(); + else + delete alarm_; + + // set up reminder for timeout_ milliseconds + alarm_ = oracle_->core()->create_alarm(this,timeout_,true); + + // give up on error + if (alarm_ == NULL) neighbor_gone_ = true; +} + +bool +Encounter::handle_hello_tlv(BaseTLV* tlv) +{ + if (tlv_ == NULL) return false; + HelloTLV* hello = static_cast(tlv); + if (hello == NULL || hello->typecode() != BaseTLV::HELLO_TLV) + return false; + + LOG(LOG_DEBUG,"handle_hello_tlv(%s,%u) from %s", + hello->hf_str(),hello->timer(),hello->sender().c_str()); + + // build out truth table as laid out in Section 5.2 + bool hello_a = (remote_instance_ == tlv_->sender_instance()); + LOG(LOG_DEBUG,"hello_a %s remote_instance_ %u tlv %u", + hello_a ? "true" : "false", remote_instance_, + tlv_->sender_instance()); + bool hello_b = hello_a && + (oracle_->core()->prophet_id(next_hop_) == tlv_->source()); + LOG(LOG_DEBUG,"hello_b %s next_hop_ %s tlv %s", + hello_b ? "true" : "false", + oracle_->core()->prophet_id(next_hop_).c_str(), + tlv_->source().c_str()); + bool hello_c = (local_instance_ == tlv_->receiver_instance()); + LOG(LOG_DEBUG,"hello_c %s local_instance_ %u tlv %u", + hello_c ? "true" : "false", + local_instance_, tlv_->receiver_instance()); + + // negotiate a common timeout by choosing minimum + u_int timeout = std::min((u_int)oracle_->params()->hello_interval(), + (u_int)hello->timer(), std::less()); + + if (!estab_ && (timeout * 100) != timeout_) + { + LOG(LOG_DEBUG,"timeout_ %u -> %u (line %d)", + timeout_, timeout, __LINE__); + // convert to milliseconds + timeout_ = timeout * 100; + } + + bool ok = true; + switch (hello->hf()) + { + case HelloTLV::SYN: + if (estab_) + { + // note 2, 5.2.1 + hello_rate_ = 2; + ok = SEND_ACK(tid_); + } + else + if (state_ == SYNSENT || + state_ == SYNRCVD || + state_ == WAIT_NB) + { + UPDATE_PEER_VERIFIER(tlv_->sender_instance()); + LOG(LOG_DEBUG,"handle_hello_tlv(SYN): state_ %s remote_instance_ %u", + state_to_str(state_),remote_instance_); + ok = SEND_SYNACK(tid_); + SET_STATE(SYNRCVD); + } + break; + case HelloTLV::SYNACK: + if (estab_) + { + // note 2, 5.2.1 + hello_rate_ = 2; + ok = SEND_ACK(tid_); + } + else + if (state_ == SYNSENT) + { + if ( hello_c) + { + hello_rate_ = 0; + UPDATE_PEER_VERIFIER(tlv_->sender_instance()); + if ((ok = SEND_ACK(tid_)) != 0) + { + SET_STATE(ESTAB); + estab_ = true; + } + } + else + { + ok = SEND_RSTACK(tid_); + SET_STATE(SYNSENT); + } + } + else + if (state_ == SYNRCVD) + { + if (hello_c) + { + hello_rate_ = 0; + UPDATE_PEER_VERIFIER(tlv_->sender_instance()); + if ((ok = SEND_ACK(tid_)) != 0) + { + SET_STATE(ESTAB); + estab_ = true; + } + } + else + { + ok = SEND_RSTACK(tid_); + SET_STATE(SYNRCVD); + } + } + break; + case HelloTLV::ACK: + if (estab_ && !(hello_b && hello_c)) + { + ok = SEND_RSTACK(tid_); + break; + } + if (state_ == SYNSENT) + { + ok = SEND_RSTACK(tid_); + SET_STATE(SYNSENT); + } + else + if (state_ == SYNRCVD) + { + if (hello_b && hello_c) + { + SET_STATE(ESTAB); + estab_ = true; + } + else + { + LOG(LOG_DEBUG,"handle_hello_tlv(ACK): state_ SYNRCVD " + "remote_instance_ %u tlv instance %u", + remote_instance_,tlv_->sender_instance()); + ok = SEND_RSTACK(tid_); + SET_STATE(SYNRCVD); + } + } + else + if (state_ == WAIT_RIB || state_ == OFFER) + { + SET_STATE(WAIT_DICT); + } + else + if (state_ == REQUEST) + { + SET_STATE(CREATE_DR); + ok = send_dictionary_rib(); + if (ok) SET_STATE(SEND_DR); + } + else + if (state_ == WAIT_INFO) + { + // remote says "What up?" so we wake up and move on + if (time(0) - data_sent_ > oracle_->params()->hello_interval()) + { + if (synsender_) + { + SET_STATE(CREATE_DR); + bool ok = send_dictionary_rib(); + if (ok) SET_STATE(SEND_DR); + return ok; + } + else + { + SET_STATE(WAIT_DICT); + } + } + } + break; + case HelloTLV::RSTACK: + if (hello_a && hello_c && !synsent_) + { + // signal end of session + return false; + } + break; + case HelloTLV::HF_UNKNOWN: + default: + break; + } + + return ok; +} + +bool +Encounter::handle_ribd_tlv(BaseTLV* tlv) +{ + LOG(LOG_DEBUG,"handle_ribd_tlv"); + + RIBDTLV* ribd = static_cast(tlv); + if (ribd == NULL) + return false; + + // figure out which one of us is sender and which is receiver + std::string sender, receiver; + ASSIGN_ROLES(sender,receiver); + + switch (state_) + { + case WAIT_INFO: + // remote says "What up?" so we wake up and move on + if (time(0) - data_sent_ > oracle_->params()->hello_interval()) + { + if (synsender_) + return false; + else + SET_STATE(WAIT_DICT); + } + case WAIT_DICT: + case WAIT_RIB: + remote_ribd_ = ribd->ribd(sender,receiver); + remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__); + SET_STATE(WAIT_RIB); + return true; + case OFFER: + SET_STATE(OFFER); + return send_offer(); + default: + break; + } + return false; +} + +bool +Encounter::handle_rib_tlv(BaseTLV* tlv) +{ + LOG(LOG_DEBUG,"handle_rib_tlv"); + + RIBTLV* rib = static_cast(tlv); + if (rib == NULL) + return false; + + remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__); + Table* nodes = oracle_->nodes(); + if (state_ == WAIT_RIB) + { + // update p for remote peer + nodes->update_route(next_hop_->remote_eid(), + rib->relay(), + rib->custody(), + rib->internet()); + // then update p for each RIB entry + nodes->update_transitive(next_hop_->remote_eid(), + rib->nodes(), + remote_ribd_); + // keep a local copy of remote's RIB + remote_nodes_.assign(rib->nodes(), + remote_ribd_); + SET_STATE(OFFER); + return send_offer(); + } + return false; +} + +bool +Encounter::handle_offer_tlv(BaseTLV* tlv) +{ + LOG(LOG_DEBUG,"handle_offer_tlv"); + + OfferTLV* offer = static_cast(tlv); + if (offer == NULL) + { + LOG(LOG_DEBUG,"failed to downcast tlv"); + return false; + } + + if (state_ == SEND_DR || state_ == REQUEST) + { + remote_offers_ = offer->list(); + LOG(LOG_DEBUG,"received %zu offers",remote_offers_.size()); + SET_STATE(REQUEST); + return send_response(); + } + else if (state_ == WAIT_INFO) + { + // ignore + return true; + } + else + { + LOG(LOG_ERR,"received offer tlv when state_ == %s",state_str()); + } + + return false; +} + +bool +Encounter::handle_response_tlv(BaseTLV* tlv) +{ + LOG(LOG_DEBUG,"handle_response_tlv"); + + ResponseTLV* response = static_cast(tlv); + if (response == NULL) + return false; + + switch (state_) + { + case WAIT_RIB: + SET_STATE(WAIT_DICT); + return SEND_ACK(tid_); + case OFFER: + // empty request signals state change + if (response->list().empty()) + { + LOG(LOG_DEBUG,"received empty request"); + SET_STATE(WAIT_INFO); + return true; + } + else + { + LOG(LOG_DEBUG,"received %zu requests",response->list().size()); + // enumerate bundle list from repository + BundleList bundles = oracle_->core()->bundles(); + // dump the dictionary + remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__); + + // walk the list of requests + for(BundleResponseList::const_iterator i = + response->list().begin(); + i != response->list().end(); + i++) + { + // look up the requested bundle + std::string eid = remote_ribd_.find((*i)->sid()); + const Bundle* b = oracle_->core()->find(bundles, + eid,(*i)->creation_ts(),(*i)->seqno()); + + // ignore failure (after logging) + if (b == NULL) + { + LOG(LOG_ERR,"failed to locate bundle for request " + "%u -> %s, %u, %u", (*i)->sid(), + remote_ribd_.find((*i)->sid()).c_str(), + (*i)->creation_ts(),(*i)->seqno()); + continue; + } + + // skip if send fails, try again next timeout + if (!oracle_->core()->send_bundle(b,next_hop_)) + { + LOG(LOG_ERR,"failed to send bundle for request " + "%s, %u, %u", remote_ribd_.find((*i)->sid()).c_str(), + (*i)->creation_ts(),(*i)->seqno()); + continue; + } + + // update timestamp on outbound data + data_sent_ = time(0); + + } + SET_STATE(OFFER); + return true; + } + default: + LOG(LOG_ERR,"received response when state_ == %s", + state_to_str(state_)); + break; + } + + return false; +} + +bool +Encounter::send_hello(HelloTLV::hello_hf_t hf, + ProphetTLV::header_result_t hr, + u_int32_t tid) +{ + // Impose a simple flow control; hello_rate_ is set by the constructor + // and by handle_hello_tlv + if ((state_ != WAIT_NB) && + (hello_rate_ > 0) && + (hf != HelloTLV::RSTACK) && + (time(0) - data_sent_ < timeout_ / hello_rate_) ) + return true; // don't kill the Encounter, but neither do we + // send this Hello message + + // Create Hello TLV with hello function hf + HelloTLV* ht = new HelloTLV(hf, + oracle_->params()->hello_interval(), + oracle_->core()->local_eid()); + + LOG(LOG_DEBUG,"send_hello(%s,%u)", + ht->hf_str(),ht->timer()); + + // Create outbound TLV + ProphetTLV* tlv = NULL; + PROPHET_TLV(tlv,hr,tid); + if (tlv == NULL) + { + delete ht; + delete tlv; + return false; + } + + // Attach the Hello TLV + if (!tlv->add_tlv(ht)) + { + delete ht; + delete tlv; + return false; + } + + // Submit TLV as a bundle to the host bundle core + bool ok = send_tlv(tlv); + + // set flag on SYN sent + if (ok && (hf == HelloTLV::SYN || hf == HelloTLV::SYNACK)) + synsent_ = true; + + return ok; +} + +bool +Encounter::send_dictionary_rib(ProphetTLV::header_result_t hr, + u_int32_t tid) +{ + LOG(LOG_DEBUG,"send_dictionary_rib"); + + if (state_ != CREATE_DR && state_ != SEND_DR) + return false; + + // figure out which one of us is sender and which is receiver + std::string sender, receiver; + ASSIGN_ROLES(sender,receiver); + + // Create the dictionary TLV + RIBDTLV* ribdtlv = TLVCreator::ribd(oracle_->core(), + oracle_->nodes(), + sender, receiver); + + if (ribdtlv == NULL) return false; + + // hold on to that dictionary, for creating RIB and Offer + local_ribd_ = ribdtlv->ribd(sender,receiver); + local_ribd_.dump(oracle_->core(),__FILE__,__LINE__); + + // Create outbound TLV + ProphetTLV* tlv = NULL; + PROPHET_TLV(tlv,hr,tid); + if (tlv == NULL) + { + delete ribdtlv; + return false; + } + + // Attach the RIBD TLV + if (!tlv->add_tlv(ribdtlv)) + { + delete ribdtlv; + delete tlv; + return false; + } + + RIBTLV* rib = TLVCreator::rib(oracle_, + local_ribd_, + oracle_->params()->relay_node(), + oracle_->core()->custody_accepted(), + oracle_->params()->internet_gw()); + + if (rib == NULL) + { + delete tlv; + return false; + } + + // Attach the RIB TLV + if (!tlv->add_tlv(rib)) + { + delete rib; + delete tlv; + return false; + } + + // Submit TLV as a bundle to the host bundle core + return send_tlv(tlv); +} + +bool +Encounter::send_offer(ProphetTLV::header_result_t hr, + u_int32_t tid) +{ + LOG(LOG_DEBUG,"send_offer"); + remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__); + + // remote nodes is filled in by handle_rib_tlv + OfferTLV* offer = TLVCreator::offer(oracle_, + next_hop_, + remote_ribd_, + remote_nodes_); + + if (offer == NULL) return false; + + // Create outbound TLV + ProphetTLV* tlv = NULL; + PROPHET_TLV(tlv,hr,tid); + if (tlv == NULL) + { + delete offer; + return false; + } + + // Attach the RIBD TLV + if (!tlv->add_tlv(offer)) + { + delete offer; + delete tlv; + return false; + } + + // Submit TLV as a bundle to the host bundle core + return send_tlv(tlv); +} + +bool +Encounter::send_response(ProphetTLV::header_result_t hr, + u_int32_t tid) +{ + LOG(LOG_DEBUG,"send_response"); + local_ribd_.dump(oracle_->core(),__FILE__,__LINE__); + + ResponseTLV* response = TLVCreator::response(oracle_, + remote_offers_, + local_response_, + local_ribd_); + + if (response == NULL) return false; + + // Create outbound TLV + ProphetTLV* tlv = NULL; + PROPHET_TLV(tlv,hr,tid); + if (tlv == NULL) + { + delete response; + return false; + } + + // Attach the RIBD TLV + if (!tlv->add_tlv(response)) + { + delete response; + delete tlv; + return false; + } + + // empty response means state change + bool wait_info = local_response_.empty(); + + // Submit TLV as a bundle to the host bundle core + bool ok = send_tlv(tlv); + + if (wait_info && ok) + SET_STATE(WAIT_INFO); + + return ok; +} + +bool +Encounter::send_tlv(ProphetTLV* tlv) +{ + // weed out the oddball + if (tlv == NULL) return false; + + LOG(LOG_DEBUG,"send_tlv(%u) with %zu TLVs",tid_,tlv->size()); + + // create a new facade bundle with src and dst + // expire after HELLO_DEAD interval seconds + // (convert from 100's of ms) + Bundle* b = oracle_->core()->create_bundle( + tlv->source(),tlv->destination(), + oracle_->params()->hello_dead() * + oracle_->params()->hello_interval() / 10); + + // create a buffer to move data between Prophet and bundle host, with + // some slush factor + u_char* buf = new u_char[tlv->length() + 512]; + + size_t len = tlv->serialize(buf,tlv->length() + 512); + bool ok = oracle_->core()->write_bundle(b,buf,len); + + // clean up memory + delete [] buf; + delete tlv; + + if (ok) + { + ok = oracle_->core()->send_bundle(b, next_hop_); + if (ok) + { + // update timestamp + data_sent_ = time(0); + } + else + LOG(LOG_ERR,"failed to send TLV"); + } + + return ok; +} + +}; // namespace prophet