servlib/prophet/Encounter.cc
changeset 0 2b3e5ec03512
equal deleted inserted replaced
-1:000000000000 0:2b3e5ec03512
       
     1 /*
       
     2  *    Copyright 2007 Baylor University
       
     3  * 
       
     4  *    Licensed under the Apache License, Version 2.0 (the "License");
       
     5  *    you may not use this file except in compliance with the License.
       
     6  *    You may obtain a copy of the License at
       
     7  * 
       
     8  *        http://www.apache.org/licenses/LICENSE-2.0
       
     9  * 
       
    10  *    Unless required by applicable law or agreed to in writing, software
       
    11  *    distributed under the License is distributed on an "AS IS" BASIS,
       
    12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    13  *    See the License for the specific language governing permissions and
       
    14  *    limitations under the License.
       
    15  */
       
    16 
       
    17 #include "BundleCore.h"
       
    18 #include "HelloTLV.h"
       
    19 #include "RIBDTLV.h"
       
    20 #include "RIBTLV.h"
       
    21 #include "OfferTLV.h"
       
    22 #include "ResponseTLV.h"
       
    23 #include "TLVCreator.h"
       
    24 #include "Encounter.h"
       
    25 
       
    26 #define NEXT_TID (++next_tid_ == 0) ? ++next_tid_ : next_tid_
       
    27 
       
    28 #define PROPHET_TLV(_tlv, _result, _tid) do { \
       
    29     _tlv = new ProphetTLV(                    \
       
    30             oracle_->core()->prophet_id(),    \
       
    31             oracle_->core()->prophet_id(next_hop_), \
       
    32             _result,                          \
       
    33             local_instance_,                  \
       
    34             remote_instance_,                 \
       
    35             (_tid == 0) ? NEXT_TID : _tid); \
       
    36     } while (0)
       
    37 
       
    38 #define SEND_ACK(_tid) send_hello(HelloTLV::ACK, \
       
    39                                   ProphetTLV::NoSuccessAck,_tid)
       
    40 #define SEND_SYN(_tid) send_hello(HelloTLV::SYN, \
       
    41                                   ProphetTLV::NoSuccessAck,_tid)
       
    42 #define SEND_SYNACK(_tid) send_hello(HelloTLV::SYNACK, \
       
    43                                      ProphetTLV::NoSuccessAck,_tid)
       
    44 #define SEND_RSTACK(_tid) send_hello(HelloTLV::RSTACK,\
       
    45                                      ProphetTLV::Failure,_tid)
       
    46 
       
    47 #define LOG(_level, _args...) oracle_->core()->print_log( \
       
    48         name_.c_str(), BundleCore::_level,  _args )
       
    49 
       
    50 #define SET_STATE(_new) do { LOG(LOG_DEBUG, "state_ %s -> %s %s:%d", \
       
    51         state_to_str(state_), state_to_str(_new), __FILE__, __LINE__); \
       
    52         state_ = _new; \
       
    53     } while (0)
       
    54 
       
    55 #define UPDATE_PEER_VERIFIER(_sender_instance) do {     \
       
    56         remote_instance_ = _sender_instance;            \
       
    57         LOG(LOG_DEBUG, "update peer verifier %d",       \
       
    58                        (_sender_instance)); } while (0) \
       
    59 
       
    60 #define ASSIGN_ROLES(_s,_r) do { \
       
    61         if (synsender_) { \
       
    62             _s = oracle_->core()->local_eid(); \
       
    63             _r = next_hop_->remote_eid(); } \
       
    64         else { \
       
    65             _s = next_hop_->remote_eid(); \
       
    66             _r = oracle_->core()->local_eid(); }\
       
    67     } while (0) 
       
    68 
       
    69 
       
    70 namespace prophet
       
    71 {
       
    72 
       
    73 Encounter::Encounter(const Link* nexthop,
       
    74                      Oracle* oracle,
       
    75                      u_int16_t instance)
       
    76     : ExpirationHandler(),
       
    77       oracle_(oracle),
       
    78       local_instance_(instance),
       
    79       remote_instance_(0),
       
    80       tid_(0),
       
    81       next_tid_(0),
       
    82       timeout_(oracle_->params()->hello_interval() * 100),
       
    83       next_hop_(nexthop),
       
    84       tlv_(NULL),
       
    85       synsender_(oracle_->core()->local_eid().compare(
       
    86                   next_hop_->remote_eid()) < 0),
       
    87       state_(WAIT_NB),
       
    88       synsent_(false),
       
    89       estab_(false),
       
    90       neighbor_gone_(false),
       
    91       remote_nodes_(oracle_->core(),"remote",false),
       
    92       hello_rate_(0),
       
    93       data_sent_(time(0)),
       
    94       data_rcvd_(time(0)),
       
    95       alarm_(NULL)
       
    96 {
       
    97     std::string name("encounter-");
       
    98     char buff[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
       
    99     size_t len = snprintf(buff,10,"%d",instance);
       
   100     name.append(buff,len);
       
   101     ExpirationHandler::set_name(name.c_str());
       
   102 
       
   103     if (synsender_)
       
   104         if (send_hello(HelloTLV::SYN))
       
   105             SET_STATE(SYNSENT);
       
   106 
       
   107     // set up reminder for timeout_ milliseconds
       
   108     alarm_ = oracle_->core()->create_alarm(this,timeout_);
       
   109     if (alarm_ == NULL) neighbor_gone_ = true;
       
   110 
       
   111     LOG(LOG_DEBUG,"constructor(%s,%u)",nexthop->remote_eid(),instance);
       
   112 
       
   113 }
       
   114 
       
   115 Encounter::Encounter(const Encounter& e)
       
   116     : ExpirationHandler(e),
       
   117       oracle_(e.oracle_),
       
   118       local_instance_(e.local_instance_),
       
   119       remote_instance_(e.remote_instance_),
       
   120       tid_(e.tid_),
       
   121       timeout_(e.timeout_),
       
   122       next_hop_(e.next_hop_),
       
   123       tlv_(e.tlv_),
       
   124       synsender_(e.synsender_),
       
   125       state_(e.state_),
       
   126       synsent_(e.synsent_),
       
   127       estab_(e.estab_),
       
   128       neighbor_gone_(e.neighbor_gone_),
       
   129       local_ribd_(e.local_ribd_),
       
   130       remote_ribd_(e.remote_ribd_),
       
   131       remote_offers_(e.remote_offers_),
       
   132       local_response_(e.local_response_),
       
   133       remote_nodes_(e.remote_nodes_),
       
   134       hello_rate_(e.hello_rate_),
       
   135       data_sent_(e.data_sent_),
       
   136       data_rcvd_(e.data_rcvd_),
       
   137       alarm_(NULL)
       
   138 {
       
   139     LOG(LOG_DEBUG,"copy constructor(%u)",local_instance_);
       
   140     alarm_ = oracle_->core()->create_alarm(this,e.alarm_->time_remaining());
       
   141     if (alarm_ == NULL) neighbor_gone_ = true;
       
   142 }
       
   143 
       
   144 Encounter::~Encounter()
       
   145 {
       
   146     if (alarm_ != NULL && alarm_->pending())
       
   147         alarm_->cancel();
       
   148 }
       
   149 
       
   150 void
       
   151 Encounter::hello_interval_changed()
       
   152 {
       
   153     if (neighbor_gone_) return;
       
   154 
       
   155     // nothing to do if it's the same value
       
   156     u_int timeout = oracle_->params()->hello_interval() * 100;
       
   157     if (timeout == timeout_) return;
       
   158 
       
   159     // nothing to do for alarm_ unless it is pending
       
   160     if (alarm_->pending()) 
       
   161     {
       
   162         if (alarm_->time_remaining() == 0)
       
   163             // no change, let timeout handler catch it
       
   164             goto set_timeout;
       
   165 
       
   166         // grab the difference from old to new
       
   167         u_int diff;
       
   168         u_int new_alarm;
       
   169         if (timeout_ > timeout) 
       
   170         {
       
   171             // new timeout is shorter than old
       
   172             diff = timeout_ - timeout;
       
   173             new_alarm = alarm_->time_remaining() < diff ? 
       
   174                         0 : alarm_->time_remaining() - diff;
       
   175         }
       
   176         else
       
   177         {
       
   178             // new timeout is longer than old
       
   179             diff = timeout - timeout_;
       
   180             new_alarm = alarm_->time_remaining() + diff;
       
   181         }
       
   182 
       
   183         // take the old timeout value off the schedule
       
   184         alarm_->cancel();
       
   185 
       
   186         // write new timeout value to alarm
       
   187         alarm_ = oracle_->core()->create_alarm(this,new_alarm);
       
   188 
       
   189         // give up on error
       
   190         if (alarm_ == NULL) neighbor_gone_ = true;
       
   191     }
       
   192 
       
   193 set_timeout:
       
   194     LOG(LOG_DEBUG,"hello_interval changed (%u -> %u)", timeout_,timeout);
       
   195 
       
   196     // store the new timeout
       
   197     timeout_ = timeout;
       
   198 }
       
   199 
       
   200 bool
       
   201 Encounter::receive_tlv(ProphetTLV* tlv)
       
   202 {
       
   203     if (neighbor_gone_) return false;
       
   204 
       
   205     if (tlv == NULL)
       
   206         return false;
       
   207 
       
   208     LOG(LOG_DEBUG,"receive_tlv");
       
   209 
       
   210     // update member pointer
       
   211     tlv_ = tlv;
       
   212 
       
   213     // update timestamp
       
   214     data_rcvd_ = time(0);
       
   215 
       
   216     // disarm
       
   217     if (alarm_->pending())
       
   218         alarm_->cancel();
       
   219 
       
   220     // capture transaction id
       
   221     tid_ = tlv_->transaction_id();
       
   222 
       
   223     BaseTLV* bt = NULL; 
       
   224     bool ok = true;
       
   225     // distribute the individual TLVs to the correct handler
       
   226     while ( (bt = tlv_->get_tlv()) != NULL && ok )
       
   227     {
       
   228         ok = dispatch_tlv(bt);
       
   229         delete bt;
       
   230     }
       
   231 
       
   232     // clean up memory
       
   233     delete tlv_;
       
   234     tlv_ = NULL;
       
   235 
       
   236     if (ok)
       
   237     {
       
   238         // clean up old alarm: host implementation cleans up cancelled
       
   239         // alarms, else we clean up our own spent alarms
       
   240         if (alarm_->pending())
       
   241             alarm_->cancel();
       
   242         else
       
   243             delete alarm_;
       
   244         // reschedule the timeout handler
       
   245         alarm_ = oracle_->core()->create_alarm(this,timeout_,true);
       
   246 
       
   247         // give up on error
       
   248         if (alarm_ == NULL) neighbor_gone_ = true;
       
   249     } 
       
   250     else
       
   251         // fail out on error
       
   252         neighbor_gone_ = true;
       
   253 
       
   254     return ok;
       
   255 }
       
   256 
       
   257 bool
       
   258 Encounter::dispatch_tlv(BaseTLV* tlv)
       
   259 {
       
   260     LOG(LOG_DEBUG,"dispatch_tlv");
       
   261 
       
   262     bool ok = false;
       
   263     bool pre_dispatch = estab_;
       
   264 
       
   265     switch (tlv->typecode())
       
   266     {
       
   267         case BaseTLV::HELLO_TLV:
       
   268             ok = handle_hello_tlv(tlv);
       
   269             if (ok && !pre_dispatch && estab_)
       
   270             {
       
   271                 // Hello procedure just completed ... move from ESTAB to 
       
   272                 // either CREATE_DR (synsender_) or WAIT_DICT (!synsender_)
       
   273                 if (synsender_)
       
   274                 {
       
   275                     SET_STATE(CREATE_DR);
       
   276                     bool ok = send_dictionary_rib();
       
   277                     if (ok) SET_STATE(SEND_DR);
       
   278                     return ok;
       
   279                 }
       
   280                 else
       
   281                     SET_STATE(WAIT_DICT);
       
   282             }
       
   283             return ok;
       
   284         case BaseTLV::RIBD_TLV:
       
   285             if (! estab_) break;
       
   286             return handle_ribd_tlv(tlv);
       
   287         case BaseTLV::RIB_TLV:
       
   288             if (! estab_) break;
       
   289             return handle_rib_tlv(tlv);
       
   290         case BaseTLV::OFFER_TLV:
       
   291             if (! estab_) break;
       
   292             ok = handle_offer_tlv(tlv);
       
   293             if (ok && synsender_ && state_ == WAIT_INFO)
       
   294             {
       
   295                 // finished Initiator phase, now switch to Listener
       
   296                 SET_STATE(WAIT_DICT);
       
   297             }
       
   298             return ok;
       
   299         case BaseTLV::RESPONSE_TLV:
       
   300             if (! estab_) break;
       
   301             ok = handle_response_tlv(tlv);
       
   302             if (ok && !synsender_ && state_ == WAIT_INFO)
       
   303             {
       
   304                 // finished Listener phase, now switch to Initiator
       
   305                 SET_STATE(CREATE_DR);
       
   306                 ok = send_dictionary_rib();
       
   307                 if (ok) SET_STATE(SEND_DR);
       
   308             }
       
   309             return ok;
       
   310         case BaseTLV::ERROR_TLV:
       
   311         case BaseTLV::UNKNOWN_TLV:
       
   312         default:
       
   313             return false;
       
   314     }
       
   315 
       
   316     // not reached unless !estab && tlv->typecode() != HELLO_TLV
       
   317 
       
   318     hello_rate_ = 2;
       
   319     if (state_ == SYNSENT)
       
   320         return SEND_SYN(NEXT_TID);
       
   321     else if (state_ == SYNRCVD)
       
   322         return SEND_SYNACK(tid_);
       
   323 
       
   324     // unless ERROR or UNKNOWN, do not fail out of this peering session
       
   325     return true;
       
   326 }
       
   327 
       
   328 void
       
   329 Encounter::handle_bundle_received(const Bundle* b)
       
   330 {
       
   331     if (state_ != REQUEST) return;
       
   332     if (b == NULL) return;
       
   333 
       
   334     LOG(LOG_DEBUG,"handle_bundle_received: "
       
   335             "%s %u:%u",b->destination_id().c_str(),
       
   336             b->creation_ts(), b->sequence_num());
       
   337 
       
   338     // reduce dest id to node id
       
   339     std::string eid = oracle_->core()->get_route(b->destination_id());
       
   340 
       
   341     // translate node id to sid
       
   342     u_int sid = local_ribd_.find(eid);
       
   343 
       
   344     // check for dictionary error
       
   345     if (sid == Dictionary::INVALID_SID) return;
       
   346 
       
   347     // reduce list of responses by this one
       
   348     local_response_.remove_entry(
       
   349             b->creation_ts(),
       
   350             b->sequence_num(),
       
   351             sid);
       
   352 
       
   353     // clean up previous alarm: host implementation cleans up cancelled
       
   354     // alarms, else we clean up our own spent alarms
       
   355     if (alarm_->pending())
       
   356         alarm_->cancel();
       
   357     else
       
   358         delete alarm_;
       
   359 
       
   360     // set up reminder for timeout_ milliseconds
       
   361     alarm_ = oracle_->core()->create_alarm(this,timeout_,true);
       
   362 
       
   363     // give up on error
       
   364     if (alarm_ == NULL) neighbor_gone_ = true;
       
   365 }
       
   366 
       
   367 void
       
   368 Encounter::handle_timeout()
       
   369 {
       
   370     LOG(LOG_DEBUG,"handle_timeout");
       
   371 
       
   372     if (neighbor_gone_) return;
       
   373 
       
   374     bool ok = false;
       
   375     switch (state_)
       
   376     {
       
   377         case WAIT_NB:
       
   378             ok = true;
       
   379             SET_STATE(WAIT_NB);
       
   380             break;
       
   381         case SYNSENT:
       
   382             ok = SEND_SYN(NEXT_TID);
       
   383             SET_STATE(SYNSENT);
       
   384             break;
       
   385         case SYNRCVD:
       
   386             ok = SEND_SYNACK(tid_);
       
   387             SET_STATE(SYNRCVD);
       
   388             break;
       
   389         case ESTAB:
       
   390             // should not be reached
       
   391             ok = false;
       
   392             break;
       
   393         case WAIT_DICT:
       
   394             ok = SEND_ACK(tid_);
       
   395             SET_STATE(WAIT_DICT);
       
   396             break;
       
   397         case WAIT_RIB:
       
   398             ok = SEND_ACK(tid_);
       
   399             SET_STATE(WAIT_DICT);
       
   400             break;
       
   401         case OFFER:
       
   402             ok = send_offer();
       
   403             SET_STATE(OFFER);
       
   404             break;
       
   405         case SEND_DR:
       
   406             ok = send_dictionary_rib();
       
   407             if (ok) SET_STATE(SEND_DR);
       
   408             break;
       
   409         case REQUEST:
       
   410             ok = send_response();
       
   411             break;
       
   412         case WAIT_INFO:
       
   413             // if time_diff(now,data_sent) > hello_dead/2 
       
   414             // start InfoExch again
       
   415             if (time(0) - data_rcvd_ >
       
   416                 (oracle_->params()->hello_dead() *
       
   417                  oracle_->params()->hello_interval() / 20))
       
   418             {
       
   419                 if (synsender_)
       
   420                 {
       
   421                     SET_STATE(CREATE_DR);
       
   422                     ok = send_dictionary_rib();
       
   423                     if (ok) SET_STATE(SEND_DR);
       
   424                 }
       
   425                 else
       
   426                 {
       
   427                     SET_STATE(WAIT_DICT);
       
   428                     ok = true;
       
   429                 }
       
   430             }
       
   431             else
       
   432                 ok = true;
       
   433             break;
       
   434         default:
       
   435             break;
       
   436     }
       
   437     // if error sending message, or if silence exceeds max, then
       
   438     // signal session death and fail out
       
   439     if (!ok ||
       
   440         (time(0) - data_rcvd_) >
       
   441         (oracle_->params()->hello_dead() *
       
   442                             /* convert 100's of ms to seconds */
       
   443          oracle_->params()->hello_interval() / 10))
       
   444     {
       
   445         neighbor_gone_ = true;
       
   446         return;
       
   447     }
       
   448 
       
   449     // clean up previous alarm: host implementation cleans up cancelled
       
   450     // alarms, else we clean up our own spent alarms
       
   451     if (alarm_->pending())
       
   452         alarm_->cancel();
       
   453     else
       
   454         delete alarm_;
       
   455 
       
   456     // set up reminder for timeout_ milliseconds
       
   457     alarm_ = oracle_->core()->create_alarm(this,timeout_,true);
       
   458 
       
   459     // give up on error
       
   460     if (alarm_ == NULL) neighbor_gone_ = true;
       
   461 }
       
   462 
       
   463 bool
       
   464 Encounter::handle_hello_tlv(BaseTLV* tlv)
       
   465 {
       
   466     if (tlv_ == NULL) return false;
       
   467     HelloTLV* hello = static_cast<HelloTLV*>(tlv);
       
   468     if (hello == NULL || hello->typecode() != BaseTLV::HELLO_TLV)
       
   469         return false;
       
   470 
       
   471     LOG(LOG_DEBUG,"handle_hello_tlv(%s,%u) from %s",
       
   472             hello->hf_str(),hello->timer(),hello->sender().c_str());
       
   473 
       
   474     // build out truth table as laid out in Section 5.2
       
   475     bool hello_a = (remote_instance_ == tlv_->sender_instance());
       
   476     LOG(LOG_DEBUG,"hello_a %s remote_instance_ %u tlv %u",
       
   477             hello_a ? "true" : "false", remote_instance_,
       
   478             tlv_->sender_instance());
       
   479     bool hello_b = hello_a &&
       
   480         (oracle_->core()->prophet_id(next_hop_) == tlv_->source());
       
   481     LOG(LOG_DEBUG,"hello_b %s next_hop_ %s tlv %s",
       
   482             hello_b ? "true" : "false", 
       
   483             oracle_->core()->prophet_id(next_hop_).c_str(),
       
   484             tlv_->source().c_str());
       
   485     bool hello_c = (local_instance_ == tlv_->receiver_instance());
       
   486     LOG(LOG_DEBUG,"hello_c %s local_instance_ %u tlv %u",
       
   487             hello_c ? "true" : "false",
       
   488             local_instance_, tlv_->receiver_instance());
       
   489 
       
   490     // negotiate a common timeout by choosing minimum
       
   491     u_int timeout = std::min((u_int)oracle_->params()->hello_interval(),
       
   492                              (u_int)hello->timer(), std::less<u_int>());
       
   493 
       
   494     if (!estab_ && (timeout * 100) != timeout_)
       
   495     {
       
   496         LOG(LOG_DEBUG,"timeout_ %u -> %u (line %d)",
       
   497                 timeout_, timeout, __LINE__);
       
   498         // convert to milliseconds
       
   499         timeout_ = timeout * 100;
       
   500     }
       
   501 
       
   502     bool ok = true;
       
   503     switch (hello->hf())
       
   504     {
       
   505     case HelloTLV::SYN:
       
   506         if (estab_)
       
   507         {
       
   508             // note 2, 5.2.1
       
   509             hello_rate_ = 2;
       
   510             ok = SEND_ACK(tid_);
       
   511         }
       
   512         else
       
   513         if (state_ == SYNSENT ||
       
   514             state_ == SYNRCVD ||
       
   515             state_ == WAIT_NB)
       
   516         {
       
   517             UPDATE_PEER_VERIFIER(tlv_->sender_instance());
       
   518             LOG(LOG_DEBUG,"handle_hello_tlv(SYN): state_ %s remote_instance_ %u",
       
   519                     state_to_str(state_),remote_instance_);
       
   520             ok = SEND_SYNACK(tid_);
       
   521             SET_STATE(SYNRCVD);
       
   522         }
       
   523         break;
       
   524     case HelloTLV::SYNACK:
       
   525         if (estab_)
       
   526         {
       
   527             // note 2, 5.2.1
       
   528             hello_rate_ = 2;
       
   529             ok = SEND_ACK(tid_);
       
   530         }
       
   531         else
       
   532         if (state_ == SYNSENT)
       
   533         {
       
   534             if ( hello_c)
       
   535             {
       
   536                 hello_rate_ = 0;
       
   537                 UPDATE_PEER_VERIFIER(tlv_->sender_instance());
       
   538                 if ((ok = SEND_ACK(tid_)) != 0)
       
   539                 {
       
   540                     SET_STATE(ESTAB);
       
   541                     estab_ = true;
       
   542                 }
       
   543             }
       
   544             else
       
   545             {
       
   546                 ok = SEND_RSTACK(tid_);
       
   547                 SET_STATE(SYNSENT);
       
   548             }
       
   549         }
       
   550         else
       
   551         if (state_ == SYNRCVD)
       
   552         {
       
   553             if (hello_c)
       
   554             {
       
   555                 hello_rate_ = 0;
       
   556                 UPDATE_PEER_VERIFIER(tlv_->sender_instance());
       
   557                 if ((ok = SEND_ACK(tid_)) != 0)
       
   558                 {
       
   559                     SET_STATE(ESTAB);
       
   560                     estab_ = true;
       
   561                 }
       
   562             }
       
   563             else
       
   564             {
       
   565                 ok = SEND_RSTACK(tid_);
       
   566                 SET_STATE(SYNRCVD);
       
   567             }
       
   568         }
       
   569         break;
       
   570     case HelloTLV::ACK:
       
   571         if (estab_ && !(hello_b && hello_c))
       
   572         {
       
   573             ok = SEND_RSTACK(tid_);
       
   574             break;
       
   575         }
       
   576         if (state_ == SYNSENT)
       
   577         {
       
   578             ok = SEND_RSTACK(tid_);
       
   579             SET_STATE(SYNSENT);
       
   580         }
       
   581         else
       
   582         if (state_ == SYNRCVD)
       
   583         {
       
   584             if (hello_b && hello_c)
       
   585             {
       
   586                 SET_STATE(ESTAB);
       
   587                 estab_ = true;
       
   588             }
       
   589             else
       
   590             {
       
   591                 LOG(LOG_DEBUG,"handle_hello_tlv(ACK): state_ SYNRCVD "
       
   592                         "remote_instance_ %u tlv instance %u",
       
   593                         remote_instance_,tlv_->sender_instance());
       
   594                 ok = SEND_RSTACK(tid_);
       
   595                 SET_STATE(SYNRCVD);
       
   596             }
       
   597         }
       
   598         else
       
   599         if (state_ == WAIT_RIB || state_ == OFFER)
       
   600         {
       
   601             SET_STATE(WAIT_DICT);
       
   602         }
       
   603         else
       
   604         if (state_ == REQUEST)
       
   605         {
       
   606             SET_STATE(CREATE_DR);
       
   607             ok = send_dictionary_rib();
       
   608             if (ok) SET_STATE(SEND_DR);
       
   609         }
       
   610         else
       
   611         if (state_ == WAIT_INFO)
       
   612         {
       
   613             // remote says "What up?" so we wake up and move on
       
   614             if (time(0) - data_sent_ > oracle_->params()->hello_interval())
       
   615             {
       
   616                 if (synsender_)
       
   617                 {
       
   618                     SET_STATE(CREATE_DR);
       
   619                     bool ok = send_dictionary_rib();
       
   620                     if (ok) SET_STATE(SEND_DR);
       
   621                     return ok;
       
   622                 }
       
   623                 else
       
   624                 {
       
   625                     SET_STATE(WAIT_DICT);
       
   626                 }
       
   627             }
       
   628         }
       
   629         break;
       
   630     case HelloTLV::RSTACK:
       
   631         if (hello_a && hello_c && !synsent_)
       
   632         {
       
   633             // signal end of session
       
   634             return false;
       
   635         }
       
   636         break;
       
   637     case HelloTLV::HF_UNKNOWN:
       
   638     default:
       
   639         break;
       
   640     }
       
   641 
       
   642     return ok;
       
   643 }
       
   644 
       
   645 bool
       
   646 Encounter::handle_ribd_tlv(BaseTLV* tlv)
       
   647 {
       
   648     LOG(LOG_DEBUG,"handle_ribd_tlv");
       
   649 
       
   650     RIBDTLV* ribd = static_cast<RIBDTLV*>(tlv);
       
   651     if (ribd == NULL)
       
   652         return false;
       
   653 
       
   654     // figure out which one of us is sender and which is receiver
       
   655     std::string sender, receiver;
       
   656     ASSIGN_ROLES(sender,receiver);
       
   657 
       
   658     switch (state_)
       
   659     {
       
   660     case WAIT_INFO:
       
   661         // remote says "What up?" so we wake up and move on
       
   662         if (time(0) - data_sent_ > oracle_->params()->hello_interval())
       
   663         {
       
   664             if (synsender_)
       
   665                 return false;
       
   666             else
       
   667                 SET_STATE(WAIT_DICT);
       
   668         }
       
   669     case WAIT_DICT:
       
   670     case WAIT_RIB:
       
   671         remote_ribd_ = ribd->ribd(sender,receiver);
       
   672         remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__);
       
   673         SET_STATE(WAIT_RIB);
       
   674         return true;
       
   675     case OFFER:
       
   676         SET_STATE(OFFER);
       
   677         return send_offer();
       
   678     default:
       
   679         break;
       
   680     }
       
   681     return false;
       
   682 }
       
   683 
       
   684 bool
       
   685 Encounter::handle_rib_tlv(BaseTLV* tlv)
       
   686 {
       
   687     LOG(LOG_DEBUG,"handle_rib_tlv");
       
   688 
       
   689     RIBTLV* rib = static_cast<RIBTLV*>(tlv);
       
   690     if (rib == NULL)
       
   691         return false;
       
   692 
       
   693     remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__);
       
   694     Table* nodes = oracle_->nodes();
       
   695     if (state_ == WAIT_RIB)
       
   696     {
       
   697         // update p for remote peer
       
   698         nodes->update_route(next_hop_->remote_eid(),
       
   699                             rib->relay(),
       
   700                             rib->custody(),
       
   701                             rib->internet());
       
   702         // then update p for each RIB entry
       
   703         nodes->update_transitive(next_hop_->remote_eid(),
       
   704                                  rib->nodes(),
       
   705                                  remote_ribd_);
       
   706         // keep a local copy of remote's RIB
       
   707         remote_nodes_.assign(rib->nodes(),
       
   708                              remote_ribd_);
       
   709         SET_STATE(OFFER);
       
   710         return send_offer();
       
   711     } 
       
   712     return false;
       
   713 }
       
   714 
       
   715 bool
       
   716 Encounter::handle_offer_tlv(BaseTLV* tlv)
       
   717 {
       
   718     LOG(LOG_DEBUG,"handle_offer_tlv");
       
   719 
       
   720     OfferTLV* offer = static_cast<OfferTLV*>(tlv);
       
   721     if (offer == NULL)
       
   722     {
       
   723         LOG(LOG_DEBUG,"failed to downcast tlv");
       
   724         return false;
       
   725     }
       
   726 
       
   727     if (state_ == SEND_DR || state_ == REQUEST)
       
   728     {
       
   729         remote_offers_ = offer->list();
       
   730         LOG(LOG_DEBUG,"received %zu offers",remote_offers_.size()); 
       
   731         SET_STATE(REQUEST);
       
   732         return send_response();
       
   733     }
       
   734     else if (state_ == WAIT_INFO)
       
   735     {
       
   736         // ignore
       
   737         return true;
       
   738     }
       
   739     else
       
   740     {
       
   741         LOG(LOG_ERR,"received offer tlv when state_ == %s",state_str());
       
   742     }
       
   743 
       
   744     return false;
       
   745 }
       
   746 
       
   747 bool
       
   748 Encounter::handle_response_tlv(BaseTLV* tlv)
       
   749 {
       
   750     LOG(LOG_DEBUG,"handle_response_tlv");
       
   751 
       
   752     ResponseTLV* response = static_cast<ResponseTLV*>(tlv);
       
   753     if (response == NULL)
       
   754         return false;
       
   755 
       
   756     switch (state_)
       
   757     {
       
   758     case WAIT_RIB:
       
   759         SET_STATE(WAIT_DICT);
       
   760         return SEND_ACK(tid_);
       
   761     case OFFER:
       
   762         // empty request signals state change
       
   763         if (response->list().empty())
       
   764         {
       
   765             LOG(LOG_DEBUG,"received empty request");
       
   766             SET_STATE(WAIT_INFO);
       
   767             return true;
       
   768         }
       
   769         else
       
   770         {
       
   771             LOG(LOG_DEBUG,"received %zu requests",response->list().size());
       
   772             // enumerate bundle list from repository
       
   773             BundleList bundles = oracle_->core()->bundles();
       
   774             // dump the dictionary 
       
   775             remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__);
       
   776 
       
   777             // walk the list of requests
       
   778             for(BundleResponseList::const_iterator i =
       
   779                     response->list().begin();
       
   780                 i != response->list().end();
       
   781                 i++)
       
   782             {
       
   783                 // look up the requested bundle
       
   784                 std::string eid = remote_ribd_.find((*i)->sid());
       
   785                 const Bundle* b = oracle_->core()->find(bundles,
       
   786                         eid,(*i)->creation_ts(),(*i)->seqno());
       
   787 
       
   788                 // ignore failure (after logging)
       
   789                 if (b == NULL) 
       
   790                 {
       
   791                     LOG(LOG_ERR,"failed to locate bundle for request "
       
   792                         "%u -> %s, %u, %u", (*i)->sid(),
       
   793                         remote_ribd_.find((*i)->sid()).c_str(),
       
   794                         (*i)->creation_ts(),(*i)->seqno());
       
   795                     continue;
       
   796                 }
       
   797 
       
   798                 // skip if send fails, try again next timeout
       
   799                 if (!oracle_->core()->send_bundle(b,next_hop_))
       
   800                 {
       
   801                     LOG(LOG_ERR,"failed to send bundle for request "
       
   802                         "%s, %u, %u", remote_ribd_.find((*i)->sid()).c_str(),
       
   803                         (*i)->creation_ts(),(*i)->seqno());
       
   804                     continue;
       
   805                 }
       
   806 
       
   807                 // update timestamp on outbound data
       
   808                 data_sent_ = time(0);
       
   809 
       
   810             }
       
   811             SET_STATE(OFFER);
       
   812             return true;
       
   813         }
       
   814     default:
       
   815         LOG(LOG_ERR,"received response when state_ == %s",
       
   816                 state_to_str(state_));
       
   817         break;
       
   818     }
       
   819 
       
   820     return false;
       
   821 }
       
   822 
       
   823 bool
       
   824 Encounter::send_hello(HelloTLV::hello_hf_t hf,
       
   825                       ProphetTLV::header_result_t hr,
       
   826                       u_int32_t tid)
       
   827 {
       
   828     // Impose a simple flow control; hello_rate_ is set by the constructor
       
   829     // and by handle_hello_tlv
       
   830     if ((state_ != WAIT_NB) &&
       
   831             (hello_rate_ > 0) &&
       
   832             (hf != HelloTLV::RSTACK) &&
       
   833             (time(0) - data_sent_ < timeout_ / hello_rate_) )
       
   834         return true; // don't kill the Encounter, but neither do we
       
   835                      // send this Hello message
       
   836 
       
   837     // Create Hello TLV with hello function hf
       
   838     HelloTLV* ht = new HelloTLV(hf,
       
   839                                 oracle_->params()->hello_interval(),
       
   840                                 oracle_->core()->local_eid());
       
   841 
       
   842     LOG(LOG_DEBUG,"send_hello(%s,%u)",
       
   843             ht->hf_str(),ht->timer());
       
   844 
       
   845     // Create outbound TLV
       
   846     ProphetTLV* tlv = NULL;
       
   847     PROPHET_TLV(tlv,hr,tid);
       
   848     if (tlv == NULL)
       
   849     {
       
   850         delete ht;
       
   851         delete tlv;
       
   852         return false;
       
   853     }
       
   854 
       
   855     // Attach the Hello TLV
       
   856     if (!tlv->add_tlv(ht))
       
   857     {
       
   858         delete ht;
       
   859         delete tlv;
       
   860         return false;
       
   861     }
       
   862 
       
   863     // Submit TLV as a bundle to the host bundle core
       
   864     bool ok = send_tlv(tlv);
       
   865 
       
   866     // set flag on SYN sent 
       
   867     if (ok && (hf == HelloTLV::SYN || hf == HelloTLV::SYNACK))
       
   868         synsent_ = true;
       
   869 
       
   870     return ok;
       
   871 }
       
   872 
       
   873 bool
       
   874 Encounter::send_dictionary_rib(ProphetTLV::header_result_t hr,
       
   875                                u_int32_t tid)
       
   876 {
       
   877     LOG(LOG_DEBUG,"send_dictionary_rib");
       
   878 
       
   879     if (state_ != CREATE_DR && state_ != SEND_DR)
       
   880         return false;
       
   881 
       
   882     // figure out which one of us is sender and which is receiver
       
   883     std::string sender, receiver;
       
   884     ASSIGN_ROLES(sender,receiver);
       
   885 
       
   886     // Create the dictionary TLV
       
   887     RIBDTLV* ribdtlv = TLVCreator::ribd(oracle_->core(),
       
   888                                         oracle_->nodes(),
       
   889                                         sender, receiver);
       
   890 
       
   891     if (ribdtlv == NULL) return false;
       
   892 
       
   893     // hold on to that dictionary, for creating RIB and Offer
       
   894     local_ribd_ = ribdtlv->ribd(sender,receiver);
       
   895     local_ribd_.dump(oracle_->core(),__FILE__,__LINE__);
       
   896 
       
   897     // Create outbound TLV
       
   898     ProphetTLV* tlv = NULL;
       
   899     PROPHET_TLV(tlv,hr,tid);
       
   900     if (tlv == NULL)
       
   901     {
       
   902         delete ribdtlv;
       
   903         return false;
       
   904     }
       
   905 
       
   906     // Attach the RIBD TLV
       
   907     if (!tlv->add_tlv(ribdtlv))
       
   908     {
       
   909         delete ribdtlv;
       
   910         delete tlv;
       
   911         return false;
       
   912     }
       
   913 
       
   914     RIBTLV* rib = TLVCreator::rib(oracle_,
       
   915                                   local_ribd_,
       
   916                                   oracle_->params()->relay_node(),
       
   917                                   oracle_->core()->custody_accepted(),
       
   918                                   oracle_->params()->internet_gw());
       
   919 
       
   920     if (rib == NULL)
       
   921     {
       
   922         delete tlv;
       
   923         return false;
       
   924     }
       
   925 
       
   926     // Attach the RIB TLV
       
   927     if (!tlv->add_tlv(rib))
       
   928     {
       
   929         delete rib;
       
   930         delete tlv;
       
   931         return false;
       
   932     }
       
   933 
       
   934     // Submit TLV as a bundle to the host bundle core
       
   935     return send_tlv(tlv);
       
   936 }
       
   937 
       
   938 bool
       
   939 Encounter::send_offer(ProphetTLV::header_result_t hr,
       
   940                       u_int32_t tid)
       
   941 {
       
   942     LOG(LOG_DEBUG,"send_offer");
       
   943     remote_ribd_.dump(oracle_->core(),__FILE__,__LINE__);
       
   944 
       
   945     // remote nodes is filled in by handle_rib_tlv
       
   946     OfferTLV* offer = TLVCreator::offer(oracle_,
       
   947                                         next_hop_,
       
   948                                         remote_ribd_,
       
   949                                         remote_nodes_);
       
   950 
       
   951     if (offer == NULL) return false;
       
   952 
       
   953     // Create outbound TLV
       
   954     ProphetTLV* tlv = NULL;
       
   955     PROPHET_TLV(tlv,hr,tid);
       
   956     if (tlv == NULL)
       
   957     {
       
   958         delete offer;
       
   959         return false;
       
   960     }
       
   961 
       
   962     // Attach the RIBD TLV
       
   963     if (!tlv->add_tlv(offer))
       
   964     {
       
   965         delete offer;
       
   966         delete tlv;
       
   967         return false;
       
   968     }
       
   969 
       
   970     // Submit TLV as a bundle to the host bundle core
       
   971     return send_tlv(tlv);
       
   972 }
       
   973 
       
   974 bool
       
   975 Encounter::send_response(ProphetTLV::header_result_t hr,
       
   976                          u_int32_t tid)
       
   977 {
       
   978     LOG(LOG_DEBUG,"send_response");
       
   979     local_ribd_.dump(oracle_->core(),__FILE__,__LINE__);
       
   980 
       
   981     ResponseTLV* response = TLVCreator::response(oracle_,
       
   982                                                  remote_offers_,
       
   983                                                  local_response_,
       
   984                                                  local_ribd_);
       
   985 
       
   986     if (response == NULL) return false;
       
   987 
       
   988     // Create outbound TLV
       
   989     ProphetTLV* tlv = NULL;
       
   990     PROPHET_TLV(tlv,hr,tid);
       
   991     if (tlv == NULL) 
       
   992     {
       
   993         delete response;
       
   994         return false;
       
   995     }
       
   996 
       
   997     // Attach the RIBD TLV
       
   998     if (!tlv->add_tlv(response)) 
       
   999     {
       
  1000         delete response;
       
  1001         delete tlv;
       
  1002         return false;
       
  1003     }
       
  1004 
       
  1005     // empty response means state change
       
  1006     bool wait_info = local_response_.empty();
       
  1007 
       
  1008     // Submit TLV as a bundle to the host bundle core
       
  1009     bool ok = send_tlv(tlv);
       
  1010 
       
  1011     if (wait_info && ok)
       
  1012         SET_STATE(WAIT_INFO);
       
  1013 
       
  1014     return ok;
       
  1015 }
       
  1016 
       
  1017 bool
       
  1018 Encounter::send_tlv(ProphetTLV* tlv)
       
  1019 {
       
  1020     // weed out the oddball
       
  1021     if (tlv == NULL) return false;
       
  1022 
       
  1023     LOG(LOG_DEBUG,"send_tlv(%u) with %zu TLVs",tid_,tlv->size());
       
  1024 
       
  1025     // create a new facade bundle with src and dst
       
  1026     // expire after HELLO_DEAD interval seconds
       
  1027     // (convert from 100's of ms)
       
  1028     Bundle* b = oracle_->core()->create_bundle(
       
  1029                        tlv->source(),tlv->destination(),
       
  1030                        oracle_->params()->hello_dead() *
       
  1031                        oracle_->params()->hello_interval() / 10);
       
  1032 
       
  1033     // create a buffer to move data between Prophet and bundle host, with
       
  1034     // some slush factor
       
  1035     u_char* buf = new u_char[tlv->length() + 512];
       
  1036 
       
  1037     size_t len = tlv->serialize(buf,tlv->length() + 512);
       
  1038     bool ok = oracle_->core()->write_bundle(b,buf,len);
       
  1039 
       
  1040     // clean up memory
       
  1041     delete [] buf;
       
  1042     delete tlv;
       
  1043 
       
  1044     if (ok)
       
  1045     {
       
  1046         ok = oracle_->core()->send_bundle(b, next_hop_);
       
  1047         if (ok)
       
  1048         {
       
  1049             // update timestamp
       
  1050             data_sent_ = time(0);
       
  1051         }
       
  1052         else
       
  1053             LOG(LOG_ERR,"failed to send TLV");
       
  1054     }
       
  1055 
       
  1056     return ok;
       
  1057 }
       
  1058 
       
  1059 }; // namespace prophet