// Copyright (c) 2000 by the University of Southern California // All rights reserved. // // Permission to use, copy, modify, and distribute this software and its // documentation in source and binary forms for non-commercial purposes // and without fee is hereby granted, provided that the above copyright // notice appear in all copies and that both the copyright notice and // this permission notice appear in supporting documentation. and that // any documentation, advertising materials, and other materials related // to such distribution and use acknowledge that the software was // developed by the University of Southern California, Information // Sciences Institute. The name of the University may not be used to // endorse or promote products derived from this software without // specific prior written permission. // // THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about // the suitability of this software for any purpose. THIS SOFTWARE IS // PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, // INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // Other copyrights might apply to parts of this software and are so // noted when applicable. // // $Header: /nfs/jade/vint/CVSROOT/ns-2/mip-reg.cc,v 1.7 2000/09/01 03:04:06 haoboy Exp $ /* * Copyright (c) Sun Microsystems, Inc. 1998 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 Sun Microsystems, Inc. * * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * SUN MICROSYSTEMS MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS * SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is provided "as is" * without express or implied warranty of any kind. * * These notices must be retained in any copies of any part of this software. */ // #ident "@(#)mip-reg.cc 1.4 98/08/30 SMI" /* modified by Thomas Schwabe and Joerg Widmer (ICSI) Sep. 2000 */ #include #include #include #include #include #include #define AGENT_ADS_SIZE 48 #define REG_REQUEST_SIZE 52 #define HANDOFF_RATIO 0.2 /* TS_JCW */ #define OPTIMIZED_HANDOFF /* TS_JCW */ //#define MIP_QUIET int hdr_mip::offset_; static class MIPHeaderClass : public PacketHeaderClass { public: MIPHeaderClass() : PacketHeaderClass("PacketHeader/MIP", sizeof(hdr_mip)) { bind_offset(&hdr_mip::offset_); } } class_miphdr; static class MIPBSAgentClass : public TclClass { public: MIPBSAgentClass() : TclClass("Agent/MIPBS") {} TclObject* create(int, const char*const*) { return (new MIPBSAgent()); } } class_mipbsagent; MIPBSAgent::MIPBSAgent() : Agent(PT_UDP), beacon_(1.0), bcast_target_(0), ragent_(0), timer_(this), seqno_(-1), priority_(0), adlftm_(~0) { bind("adSize_", &size_); bind("ad_lifetime_", &adlftm_); size_ = AGENT_ADS_SIZE; } void MIPBSAgent::recv(Packet* p, Handler *) { Tcl& tcl = Tcl::instance(); char *objname = NULL; NsObject *obj = NULL; hdr_mip *miph = hdr_mip::access(p); hdr_ip *iph = hdr_ip::access(p); hdr_cmn *ch = hdr_cmn::access(p); int nodeaddr = Address::instance().get_nodeaddr(addr()); switch (miph->type_) { case MIPT_REG_REQUEST: if (miph->ha_ == (Address::instance().get_nodeaddr(addr()))){ if (miph->ha_ == miph->coa_) { // back home tcl.evalf("%s clear-reg %d", name_, miph->haddr_); #ifndef MIP_QUIET printf("%f HA %s updated, MH back home, packet %i\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr()), ch->uid()); #endif } else { tcl.evalf("%s encap-route %d %d %lf", name_, miph->haddr_, miph->coa_, miph->lifetime_); #ifndef MIP_QUIET printf("%f HA %s updated (FA %s), packet %i\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr()), Address::instance().print_nodeaddr(miph->coa_), ch->uid()); #endif } iph->dst() = iph->src(); miph->type_ = MIPT_REG_REPLY; miph->priority_ = priority_; // write priority_ of HA } else { iph->daddr() = miph->ha_; iph->dport() = 0; #ifndef MIP_QUIET printf("%f BS %s forwarding reg-request from %s to HA %s, packet %i\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr()), Address::instance().print_nodeaddr(iph->saddr()), Address::instance().print_nodeaddr(miph->ha_), ch->uid()); #endif } iph->saddr() = addr(); iph->sport() = port(); // by now should be back to normal route // if dst is the mobile // also initialise forward counter to 0. otherwise routing // agent is going to think pkt is looping and drop it!! ch->num_forwards() = 0; send(p, 0); break; case MIPT_REG_REPLY: #ifndef MIP_QUIET printf("%f BS %s received reply from BS %s, packet %i\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr()), Address::instance().print_nodeaddr(iph->saddr()), ch->uid()); // JCW #endif assert(miph->coa_ == nodeaddr); tcl.evalf("%s get-link %d %d", name_, nodeaddr, miph->haddr_); // // XXX hacking mobileip. all this should go away // when mobileIP for sun-wired model is no longer reqd. // obj = (NsObject*)tcl.lookup(objname = tcl.result()); if (strlen(objname) == 0) objname = "XXX"; tcl.evalf("%s decap-route %d %s %lf", name_, miph->haddr_, objname, miph->lifetime_); iph->src() = iph->dst(); iph->daddr() = miph->haddr_; iph->dport() = 0; miph->priority_ = priority_; // write priority_ of FA (overwriting HA) if (obj == NULL) obj = ragent_; obj->recv(p, (Handler*)0); break; case MIPT_SOL: tcl.evalf("%s get-link %d %d",name_,nodeaddr,miph->haddr_); send_ads(miph->haddr_, (NsObject*)tcl.lookup(tcl.result())); Packet::free(p); break; default: Packet::free(p); break; } } void MIPBSAgent::timeout(int ) { send_ads(); timer_.resched(beacon_); } int MIPBSAgent::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "beacon-period") == 0) { beacon_ = atof(argv[2]); timer_.resched(Random::uniform(0, beacon_)); return TCL_OK; } if (strcmp(argv[1], "priority") == 0) { // JCW priority_ = atoi(argv[2]); return TCL_OK; } if (strcmp(argv[1], "bcast-target") == 0) { bcast_target_ = (NsObject *)TclObject::lookup(argv[2]); return TCL_OK; } if (strcmp(argv[1], "ragent") == 0) { ragent_ = (NsObject *)TclObject::lookup(argv[2]); return TCL_OK; } } return (Agent::command(argc, argv)); } void MIPBSAgent::send_ads(int dst, NsObject *target) { printf("%f BS send ADS %s\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr())); Packet *p = allocpkt(); hdr_mip *miph = hdr_mip::access(p); hdr_ip *iph = hdr_ip::access(p); miph->haddr_ = miph->ha_ = -1; miph->coa_ = Address::instance().get_nodeaddr(addr()); miph->type_ = MIPT_ADS; miph->lifetime_ = adlftm_; miph->seqno_ = ++seqno_; miph->priority_ = priority_; // JCW if (dst != -1) { iph->daddr() = dst; iph->dport() = 0; } else { hdr_ip *iph = hdr_ip::access(p); hdr_cmn *ch = hdr_cmn::access(p); ch->next_hop_ = IP_BROADCAST; ch->addr_type_ = NS_AF_INET; iph->daddr() = IP_BROADCAST; iph->dport() = 0; } if (target == NULL) { if (bcast_target_) bcast_target_->recv(p, (Handler*) 0); else if (target_) target_->recv(p, (Handler*) 0); else Packet::free(p); // drop; may log in future code } else target->recv(p, (Handler*)0); } void AgtListTimer::expire(Event *) { a_->timeout(MIP_TIMER_AGTLIST); } static class MIPMHAgentClass : public TclClass { public: MIPMHAgentClass() : TclClass("Agent/MIPMH") {} TclObject* create(int, const char*const*) { return (new MIPMHAgent()); } } class_mipmhagent; MIPMHAgent::MIPMHAgent() : Agent(PT_UDP), ha_(-1), coa_(-1), cur_prio_(0), ad_nr(0), beacon_(1.0), force_handoff_(0), bcast_target_(0), agts_(0), rtx_timer_(this), agtlist_timer_(this), seqno_(-1), reglftm_(~0), adlftm_(0.0), node_ (0) { bind("home_agent_", &ha_); bind("rreqSize_", &size_); bind("reg_rtx_", ®_rtx_); bind("reg_lifetime_", ®lftm_); size_ = REG_REQUEST_SIZE; for(int i = 0; i < AD_CACHE_SIZE; ++i) ad_cache[i] = -1; } double MIPMHAgent::distance(MobileNode* t) { double rX, rY, rZ; // loc of receiver double tX, tY, tZ; // loc of xmitter node_->getLoc(&rX, &rY, &rZ); t->getLoc(&tX, &tY, &tZ); return sqrt((rX - tX) * (rX - tX) + (rY - tY) * (rY - tY)); } void MIPMHAgent::recv(Packet* p, Handler *) { Tcl& tcl = Tcl::instance(); hdr_mip *miph = hdr_mip::access(p); //printf("%f MH receive %c\n", Scheduler::instance().clock(), miph->type_); switch (miph->type_) { case MIPT_REG_REPLY: { #ifndef MIP_QUIET hdr_cmn *ch = hdr_cmn::access(p); printf("%f MH %s received reply from BS %s [COA: %s], packet %i\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr()), Address::instance().print_nodeaddr(miph->coa_), Address::instance().print_nodeaddr(coa_), ch->uid()); // JCW #endif if (miph->coa_ != coa_) { int valid = 0; for(int i = 0; i < AD_CACHE_SIZE; ++i) { if (miph->coa_ == ad_cache[i]) { valid = 1; break; } } if (!valid) break; // don't register, coa_ was not found in ad_cache // found new COA -> clear AD cache force_handoff_ = 0; ad_nr = 0; for(int i = 0; i < AD_CACHE_SIZE; ++i) ad_cache[i] = -1; // register new coa coa_ = miph->coa_; cur_prio_ = miph->priority_; if(node_) node_->set_base_stn(coa_); #ifndef MIP_QUIET printf("%f MH %s has new coa (%s)\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr()), Address::instance().print_nodeaddr(coa_)); // JCW #endif } else { if (!force_handoff_) { // refreshed old COA and no handoff needed -> clear AD cache ad_nr = 0; for(int i = 0; i < AD_CACHE_SIZE; ++i) ad_cache[i] = -1; } } adlftm_ = miph->lifetime_; tcl.evalf("%s update-reg %d", name_, coa_); if (rtx_timer_.status() == TIMER_PENDING) rtx_timer_.cancel(); } break; case MIPT_ADS: { #ifndef MIP_QUIET printf("%f MH %s received ADS with coa %s\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(addr()), Address::instance().print_nodeaddr(miph->coa_)); // JCW #endif AgentList **ppagts = &agts_, *ptr; while (*ppagts) { if ((*ppagts)->node_ == miph->coa_) break; ppagts = &(*ppagts)->next_; } if (*ppagts) { // refresh ptr = *ppagts; *ppagts = ptr->next_; } else { // new AD ptr = new AgentList; ptr->node_ = miph->coa_; } // update distance to MH and sending range ptr->priority_ = miph->priority_; ptr->distance_ = distance(p->txinfo_.getNode()); ptr->txpr_ = p->txinfo_.getTxPr(); ptr->expire_time_ = 2 * beacon_ + Scheduler::instance().clock(); ptr->lifetime_ = miph->lifetime_; ptr->next_ = agts_; agts_ = ptr; if (coa_ == -1) { /* send REQUEST if MH has no coa_ */ seqno_++; ad_cache[(++ad_nr) % AD_CACHE_SIZE] = reg(agts_); #ifdef OPTIMIZED_HANDOFF } else if ((ptr->priority_ > cur_prio_) && (ptr->distance_ / ptr->txpr_ <= (1 - HANDOFF_RATIO))) { /* send REQUEST if AD was from "nearby" BS with higher priority */ force_handoff_ = 1; seqno_++; ad_cache[(++ad_nr) % AD_CACHE_SIZE] = reg(agts_); #else } else if (ptr->priority_ > cur_prio_) { /* send REQUEST if AD was from BS with higher priority */ seqno_++; ad_cache[(++ad_nr) % AD_CACHE_SIZE] = reg(agts_); #endif } else if (miph->coa_ == coa_) { /* refresh */ seqno_++; ad_cache[(++ad_nr) % AD_CACHE_SIZE] = reg(agts_); #ifdef OPTIMIZED_HANDOFF if (ptr->distance_ / ptr->txpr_ > (1 - HANDOFF_RATIO)) { /* handoff needed -> also try to register with preferred BS */ force_handoff_ = 1; seqno_++; ad_cache[(++ad_nr) % AD_CACHE_SIZE] = reg(); } #endif } #ifndef MIP_QUIET printf("%f MH agent list [COA %s]: ", Scheduler::instance().clock(), Address::instance().print_nodeaddr(coa_)); ptr = agts_; while (ptr) { if (ptr->node_ == coa_) { printf("[%s-(%.0f/%.0f %i)] ", Address::instance().print_nodeaddr(ptr->node_), ptr->distance_, ptr->txpr_, ptr->priority_); } else { printf("%s-(%.0f/%.0f %i) ", Address::instance().print_nodeaddr(ptr->node_), ptr->distance_, ptr->txpr_, ptr->priority_); } ptr = ptr->next_; } printf("\n"); #endif } break; default: break; } Packet::free(p); } void MIPMHAgent::timeout(int tno) { switch (tno) { case MIP_TIMER_SIMPLE: { printf("%f simple timeout %f [COA %s]\n", Scheduler::instance().clock(), reg_rtx_, Address::instance().print_nodeaddr(coa_)); // JCW AgentList *ptr = agts_; if (ptr) { while (ptr) { if (ptr->node_ == coa_) printf("-->"); printf("%s (%f), ", Address::instance().print_nodeaddr(ptr->node_), ptr->expire_time_); ptr = ptr->next_; } printf("\n"); } else { printf("\t no BSs in range\n"); } reg(); // try to register (preferred) coa } break; case MIP_TIMER_AGTLIST: { double now = Scheduler::instance().clock(); AgentList **ppagts = &agts_, *ptr; int coalost = 0; while (*ppagts) { if ((*ppagts)->expire_time_ < now) { ptr = *ppagts; *ppagts = ptr->next_; if (ptr->node_ == coa_) { // forced switch to new coa_ JCW printf("%f current coa (%s) timed out\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(coa_)); coa_ = -1; coalost = 1; } delete ptr; } else ppagts = &(*ppagts)->next_; } agtlist_timer_.resched(beacon_); if (coalost) { seqno_++; ad_cache[(++ad_nr) % AD_CACHE_SIZE] = reg(); } } break; default: break; } } int MIPMHAgent::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], "beacon-period") == 0) { beacon_ = atof(argv[2]); timeout(MIP_TIMER_AGTLIST); agtlist_timer_.resched(beacon_); rtx_timer_.resched(Random::uniform(0, beacon_)); return TCL_OK; } else if (strcmp(argv[1], "bcast-target") == 0) { bcast_target_ = (NsObject *)TclObject::lookup(argv[2]); return TCL_OK; } else if (strcmp (argv[1], "node") == 0) { node_ = (MobileNode*)TclObject::lookup(argv[2]); if (node_ == 0) { fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1], argv[2]); return TCL_ERROR; } return TCL_OK; } } return (Agent::command(argc, argv)); } AgentList* MIPMHAgent::preferredAgent() { AgentList *ptr = agts_, *pref_agt = 0; int max_prio = -1, near; double min_dist = DBL_MAX; /* return Agent with highest priority (and smallest distance) */ near = 0; while (ptr) { #ifdef OPTIMIZED_HANDOFF if (ptr->distance_ / ptr->txpr_ <= (1 - HANDOFF_RATIO)) { if (!near) { // use "near" BS if only "far" BS found so far max_prio = -1; near = 1; } } else { if (near) { // skip "far" BS if "near" BS possible ptr = ptr->next_; continue; } } if ((ptr->priority_ > max_prio) || (ptr->priority_ == max_prio && ptr->distance_ < min_dist)) { pref_agt = ptr; max_prio = ptr->priority_; min_dist = ptr->distance_; } #else if (ptr->priority_ > max_prio) { pref_agt = ptr; max_prio = ptr->priority_; min_dist = ptr->distance_; } #endif ptr = ptr->next_; } return pref_agt; } int MIPMHAgent::reg() { return reg(preferredAgent()); } int MIPMHAgent::reg(AgentList* agt) { int reg_coa_; rtx_timer_.resched(reg_rtx_); if (agt == 0) { send_sols(); return -1; } reg_coa_ = agt->node_; Tcl& tcl = Tcl::instance(); Packet *p = allocpkt(); hdr_ip *iph = hdr_ip::access(p); iph->daddr() = reg_coa_; iph->dport() = 0; hdr_mip *h = hdr_mip::access(p); h->haddr_ = Address::instance().get_nodeaddr(addr()); h->ha_ = ha_; h->coa_ = reg_coa_; h->type_ = MIPT_REG_REQUEST; h->lifetime_ = min(reglftm_, agt->lifetime_); h->seqno_ = seqno_; hdr_cmn *ch = HDR_CMN (p); ch->next_hop_ = Address::instance().get_nodeaddr(iph->daddr()); #ifndef MIP_QUIET printf("%f MH %s sends request to %s\n", Scheduler::instance().clock(), Address::instance().print_nodeaddr(h->haddr_), Address::instance().print_nodeaddr(reg_coa_)); #endif tcl.evalf("%s get-link %d %d", name_, h->haddr_,reg_coa_); NsObject *target = (NsObject *)tcl.lookup(tcl.result()); if (target != NULL) { ((NsObject *)tcl.lookup(tcl.result()))->recv(p, (Handler*) 0); } else send(p, 0); return reg_coa_; } void MIPMHAgent::send_sols() { Packet *p = allocpkt(); hdr_mip *h = hdr_mip::access(p); h->coa_ = -1; h->haddr_ = Address::instance().get_nodeaddr(addr()); h->ha_ = ha_; h->type_ = MIPT_SOL; h->lifetime_ = reglftm_; h->seqno_ = seqno_; hdr_ip *iph = hdr_ip::access(p); hdr_cmn *ch = hdr_cmn::access(p); ch->next_hop_ = IP_BROADCAST; ch->addr_type_ = NS_AF_INET; iph->daddr() = IP_BROADCAST; iph->dport() = 0; if (bcast_target_) bcast_target_->recv(p, (Handler*) 0); else if (target_) target_->recv(p, (Handler*) 0); else Packet::free(p); // drop; may log in future code }