/* -*- Mode:C++; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */ /* * Copyright (c) 1996-1997 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "@(#) $Header: /nfs/jade/vint/CVSROOT/ns-2/common/ivs.cc,v 1.16 2000/09/01 03:04:05 haoboy Exp $ (LBL)"; #endif #include #include #include "message.h" #include "trace.h" #include "agent.h" /* ivs data packet; ctrl packets are sent back as "messages" */ struct hdr_ivs { double ts_; /* timestamp sent at source */ u_int8_t S_; u_int8_t R_; u_int8_t state_; u_int8_t rshft_; u_int8_t kshft_; u_int16_t key_; double maxrtt_; int seqno_; static int offset_; inline static int& offset() { return offset_; } inline static hdr_ivs* access(Packet* p) { return (hdr_ivs*) p->access(offset_); } /* per-field member functions */ double& ts() { return (ts_); } u_int8_t& S() { return (S_); } u_int8_t& R() { return (R_); } u_int8_t& state() { return (state_); } u_int8_t& rshft() { return (rshft_); } u_int8_t& kshft() { return (kshft_); } u_int16_t& key() { return (key_); } double& maxrtt() { return (maxrtt_); } int& seqno() { return (seqno_); } }; int hdr_ivs::offset_; static class IvsHeaderClass : public PacketHeaderClass { public: IvsHeaderClass() : PacketHeaderClass("PacketHeader/IVS", sizeof(hdr_ivs)) { bind_offset(&hdr_ivs::offset_); } } class_ivshdr; class IvsSource : public Agent { public: IvsSource(); protected: void reset(); void recv(Packet *pkt, Handler*); void sendpkt(); int S_; int R_; int state_; #define ST_U 0 #define ST_L 1 #define ST_C 2 int rttShift_; int keyShift_; int key_; double maxrtt_; }; struct Mc_Hole { int start; int end; double time; Mc_Hole* next; }; class IvsReceiver : public Agent { public: IvsReceiver(); int command(int argc, const char*const* argv); protected: void recv(Packet *pkt, Handler*); void update_ipg(double now); int lossMeter(double timeDiff, u_int32_t seq, double maxrtt); void upcall_respond(double ts, int matchS); void upcall_rtt_solicit(double ts, int rshift); int state_; u_int32_t nextSeq_; double timeMean_; double timeVar_; double ipg_; /* interpkt gap (estimator) */ Mc_Hole* head_; Mc_Hole* tail_; double lastPktTime_; int ignoreR_; double lastTime_; /* last time a resp pkt sent */ int key_; }; static class IvsSourceClass : public TclClass { public: IvsSourceClass() : TclClass("Agent/IVS/Source") {} TclObject* create(int, const char*const*) { return (new IvsSource()); } } class_ivs_source; static class IvsReceiverClass : public TclClass { public: IvsReceiverClass() : TclClass("Agent/IVS/Receiver") {} TclObject* create(int, const char*const*) { return (new IvsReceiver()); } } class_ivs_receiver; IvsSource::IvsSource() : Agent(PT_MESSAGE), S_(0), R_(0), state_(ST_U), rttShift_(0), keyShift_(0), key_(0), maxrtt_(0) { bind("S_", &S_); bind("R_", &R_); bind("state_", &state_); bind("rttShift_", &rttShift_); bind("keyShift_", &keyShift_); bind("key_", &key_); bind("maxrtt_", &maxrtt_); } void IvsSource::reset() { } /* * main reception path - should only see acks, otherwise the * network connections are misconfigured */ void IvsSource::recv(Packet* pkt, Handler*) { char wrk[128];/*XXX*/ Tcl& tcl = Tcl::instance(); hdr_msg *q = hdr_msg::access(pkt); sprintf(wrk, "%s handle {%s}", name(), q->msg()); tcl.eval(wrk); Packet::free(pkt); } #ifdef notdef void IvsSource::probe_timeout() { rndStart_ = now; if (keyShift_ == 15) { if (key_ == 0) { if (solicitedResponses_ == 0) estReceivers_ = 0; /* * Got through a round without being LOADED. * increase send rate. */ if (state_ == ST_U) increase(); /* Reset keys et al */ S_ = 1; state_ = ST_U; /*XXX*/ setRttSolicit(mcstate); solicitedResponses_ = 0; keyShift_ = startShift_; /*XXX do all this in tcl? */ setkey(); } else { mcstate->hdr.key = 0; } } else { if (probeTimeout_ > 0) ++keyShift_; } sched(pktTime + 2 * maxrtt_, IVS_TIMER_PROBE); } #endif void IvsSource::sendpkt() { Packet* pkt = allocpkt(); hdr_ivs *p = hdr_ivs::access(pkt); /*fill in ivs fields */ p->ts() = Scheduler::instance().clock(); p->S() = S_; p->R() = R_; p->state() = state_; p->rshft() = rttShift_; p->kshft() = keyShift_; p->key() = key_; p->maxrtt() = maxrtt_; target_->recv(pkt, (Handler *)0); } IvsReceiver::IvsReceiver() : Agent(PT_MESSAGE), state_(ST_U), nextSeq_(0), timeMean_(0.), timeVar_(0.),/*XXX*/ ipg_(0.), head_(0), tail_(0), lastPktTime_(0.), ignoreR_(0), lastTime_(0.), key_(0) { bind("ignoreR_", &ignoreR_); bind("key_", &key_); bind("state_", &state_); bind("packetSize_", &size_); } inline void IvsReceiver::update_ipg(double v) { /* Update the estimated interpacket gap */ ipg_ = (15 * ipg_ + v) / 16; } /* * timestamp comes in milliseconds since start of connection according to * remote clock * now is milliseconds since start of connection * rtt in milliseconds * This congestion meter is not terribly good at figuring out when the net is * loaded, since the loss of a packet over a rtt is a transitory event * Eventually we ought to have a memory thing, that records state once a * maxrtt, with thresholds to decide current state */ int IvsReceiver::lossMeter(double timeDiff, u_int32_t seq, double maxrtt) { /* * The congestion signal is calculated here by measuring the loss in a * given period of packets - if the threshold for lost packets is * passed then signal Congested. If there are no lost packets, * then we are at UNLOADED, else LOADED */ /* if sequence number is next, increase expected number */ double now = Scheduler::instance().clock(); if (nextSeq_ == 0) nextSeq_ = seq + 1; else if (seq == nextSeq_) nextSeq_++; else if (seq > nextSeq_) { #ifdef notdef if (trace_ != 0) { sprintf(trace_->buffer(), "d %g %d", lastPktTime_, seq - nextSeq_); trace_->dump(); } #endif /* This is definitely a hole */ Mc_Hole* hole = new Mc_Hole; hole->time = now; hole->start = nextSeq_; hole->end = seq - 1; hole->next = 0; /* Now add it to the list */ if (head_ == NULL) { head_ = hole; tail_ = hole; } else { tail_->next = hole; tail_ = hole; } nextSeq_ = seq + 1; } else { /* XXX can't happen in current ns simulations */ fprintf(stderr, "ns: ivs rcvr: seq number went backward\n"); abort(); } /* update the calculation of the variance in the rtt */ /* get the time averaged mean of the difference */ if (timeMean_ == 0) timeMean_ = timeDiff; else timeMean_ = (7 * timeMean_ + timeDiff) / 8; timeDiff -= timeMean_; if (timeDiff < 0) timeDiff = -timeDiff; timeVar_ = (7 * timeVar_ + timeDiff) / 8; int lostPkts = 0; /* * Check down the list of holes, discarding those that before * now-rttvar-rtt, counting those that fall within * now-rttvar to now-rttvar-rtt */ if (head_ == 0) return (ST_U); Mc_Hole *cur = head_, *prev = NULL; double validEnd = now - 2 * timeVar_; double validStart = validEnd - maxrtt; /* for each hole, if it is older than required, dump it */ /* If it is valid, add the size to the loss count */ /* Go to the next hole */ while (cur != NULL) { if (cur->time < validStart) { if (prev == NULL) head_ = cur->next; else prev->next = cur->next; delete cur; if (prev == NULL) cur = head_; else cur = prev->next; } else { if (cur->time < validEnd) lostPkts += cur->end - cur->start + 1; prev = cur; cur = cur->next; } } /* * Update the moving average calculation of the number of holes, if * nowMs is another rtt away */ double pps = (ipg_ != 0) ? maxrtt / ipg_ : 0.; /*XXX*/ #ifdef notdef if (trace_ != 0) { double now = Scheduler::instance().clock(); sprintf(trace_->buffer(), "%.17g %g", now, (double)lostPkts / pps); trace_->dump(); } #endif /*XXX*/ #define LOSSCONGTH 15 #define LOSSLOADTH 5 /* If the rtt is smaller than the ipg, set the thresholds to 0,1,2 */ if ((pps * LOSSCONGTH) / 100 < 2) pps = 200 / LOSSCONGTH; if (lostPkts <= (LOSSLOADTH * pps) / 100) return (ST_U); else if (lostPkts <= (LOSSCONGTH * pps) / 100) return (ST_L); else return (ST_C); } void IvsReceiver::recv(Packet* pkt, Handler*) { hdr_ivs *p = hdr_ivs::access(pkt); double now = Scheduler::instance().clock(); if (lastPktTime_ == 0.) { lastPktTime_ = now; Packet::free(pkt); return; } update_ipg(now - lastPktTime_); double ts = p->ts(); int prevState = state_; state_ = lossMeter(now - ts, p->seqno(), p->maxrtt()); lastPktTime_ = now; /* If soliciting rtt */ if (p->R() && !ignoreR_) /* upcall into tcl */ upcall_rtt_solicit(ts, p->rshft()); /* * send a response if we're congested and its over an rtt since * we last sent one OR * any response is solicited to estimate size and we match the key OR * we're LOADED and we match the key and its over an rtt since we last * sent a response */ if (now - lastTime_ < p->maxrtt() && state_ <= prevState) { Packet::free(pkt); return; } int shift = p->kshft(); int match; if (p->key() == 0) match = 1; else match = (key_ >> shift) == (p->key() >> shift); int matchS = match ? p->S() : 0; if (state_ == ST_C || matchS || (match && state_ == ST_L)) { upcall_respond(ts, matchS); lastTime_ = now; } Packet::free(pkt); } void IvsReceiver::upcall_respond(double ts, int matchS) { Tcl::instance().evalf("%s respond %.17g %d", name(), ts, matchS); } void IvsReceiver::upcall_rtt_solicit(double ts, int rshift) { Tcl::instance().evalf("%s solicit-rtt %.17g %d", name(), ts, rshift); } int IvsReceiver::command(int argc, const char*const* argv) { Tcl& tcl = Tcl::instance(); if (argc == 3) { if (strcmp(argv[1], "send") == 0) { Packet* pkt = allocpkt(); hdr_msg* p = hdr_msg::access(pkt); const char* s = argv[2]; int n = strlen(s); if (n >= p->maxmsg()) { tcl.result("message too big"); Packet::free(pkt); return (TCL_ERROR); } strcpy(p->msg(), s); target_->recv(pkt, (Handler*)0); return (TCL_OK); } } return (Agent::command(argc, argv)); }