diff --git a/WHATSNEW b/WHATSNEW index 03828369a2a..c566b77986f 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -293,6 +293,32 @@ Notable backward incompatible changes are the following: negotiation. The MacQosWithBlockAck example fingerprints are updated; the QoS and NonQos showcase fingerprint controls remain unchanged. +17. A home agent routes a decapsulated datagram as arriving from the tunnel + + An IPv6 node that decapsulates an IPv6-in-IPv6 datagram now routes the + inner datagram as arriving on the tunnel interface instead of the + physical interface the outer datagram came in on. A Mobile IPv6 home + agent no longer sends an ICMPv6 Redirect for each reverse-tunneled + datagram it forwards back out of the interface it arrived on; before, + Mobile IPv6 scenarios with bidirectional tunneling and the default + sendRedirects = true sent one per such datagram. Upper layers still see + the physical interface in the InterfaceInd tag. Fingerprints of the + Mobile IPv6 examples change. + +18. A mobile node holds home-address traffic until its reverse tunnel exists + + A Mobile IPv6 mobile node that had just moved to a foreign network + discarded the packets it sent from its home address until the home agent + acknowledged its registration ("Using HoA instead of CoA... dropping + datagram"). It now holds them, at most 100 per home agent, and sends them + through the reverse tunnel once the tunnel exists; held packets are dropped + with a packetDropped signal if the registration does not complete. A held + Home Test Init is not retransmitted while it waits, so a late + acknowledgement does not make return routability run twice. In + examples/ipv6/mipv6roaming this recovers a Home Test Init and two echo + replies in run 0. Fingerprints of the Handover and Roaming configurations + change. + Notable backward compatible changes are the following: 1. IEEE 802.11 per-station rate statistics @@ -347,6 +373,12 @@ Notable backward compatible changes are the following: ResidenceTimeTag and, with it, the FlowTag and the PacketEventTag of the packet. Those two tags now survive the measurement. +6. Finding an IPv6 tunnel interface by its endpoints + + Ipv6RoutingTable::findTunnelNetworkInterface() returns the tunnel interface + configured with a given entry and exit point, and Ipv6Tunnel now exposes + those endpoints with getSource() and getDestination(). + INET-4.7 (July 2026) — feature release -------------------------------------- diff --git a/src/inet/networklayer/ipv6/Ipv6.cc b/src/inet/networklayer/ipv6/Ipv6.cc index f3f518f1616..2b4c0e8d83c 100644 --- a/src/inet/networklayer/ipv6/Ipv6.cc +++ b/src/inet/networklayer/ipv6/Ipv6.cc @@ -848,6 +848,38 @@ void Ipv6::localDeliverFinish(Packet *packet) // datagram is re-processed as if received from the network, so it is routed // or forwarded -- and seen by the netfilter pre-routing hooks -- normally. // The L3AddressInd left by decapsulate() carries the tunnel (outer) source. + // + // The inner datagram did not arrive on the physical interface the outer one + // came in on: it arrived through the tunnel, which RFC 2473 Section 3 models + // as a link of its own. Route it as arriving from the tunnel interface, so + // that the RFC 4861 Section 8.2 Redirect condition in routePacket() compares + // against the interface the datagram really came from. For an incoming + // datagram the tunnel's entry point is the outer destination and its exit + // point the outer source, so the endpoints are looked up the other way round. + // A node that decapsulates without having a tunnel interface for these + // endpoints (e.g. the far end of a unidirectional tunnel) keeps the physical + // interface, as before. + // + // Only the routing decision is re-attributed; the InterfaceInd tag keeps + // naming the physical interface, because upper layers read it to find the + // link the datagram came in on -- Mipv6, for instance, looks up that + // interface's Mipv6InterfaceData, which a tunnel interface does not have. + // For the same reason a pre-routing hook that QUEUEs the datagram loses the + // re-attribution: reinjectQueuedDatagram() recovers fromIE from that tag. + // + // Unicast only. A multicast inner datagram keeps the physical interface, + // because routeMulticastPacket() reads the arrival interface's + // Ipv6InterfaceData for its group membership test and matches it against the + // multicast route's incoming interface, and a tunnel interface has neither. + // Redirects are never sent for multicast (RFC 4861 Section 8.2), so nothing + // this commit repairs is lost by leaving that path alone. + const auto& inner = packet->peekAtFront(); + if (!inner->getDestAddress().isMulticast()) { + if (auto *tunnelIE = rt->findTunnelNetworkInterface(localAddress, remoteAddress)) { + fromIE = tunnelIE; + EV_INFO << "Routing the decapsulated datagram as arriving on " << fromIE->getInterfaceName() << "\n"; + } + } packet->removeTagIfPresent(); auto verdict = datagramPreRoutingHook(packet); if (verdict == INetfilter::IHook::ACCEPT) diff --git a/src/inet/networklayer/ipv6/Ipv6RoutingTable.cc b/src/inet/networklayer/ipv6/Ipv6RoutingTable.cc index 0f1996eb21d..b51d98b7052 100644 --- a/src/inet/networklayer/ipv6/Ipv6RoutingTable.cc +++ b/src/inet/networklayer/ipv6/Ipv6RoutingTable.cc @@ -17,6 +17,7 @@ #include "inet/networklayer/contract/IInterfaceTable.h" #include "inet/networklayer/ipv6/Ipv6InterfaceData.h" #include "inet/networklayer/ipv6/Mipv6InterfaceData.h" +#include "inet/networklayer/ipv6tunneling/Ipv6Tunnel.h" namespace inet { @@ -443,6 +444,21 @@ void Ipv6RoutingTable::deleteTunnelNetworkInterface(NetworkInterface *networkInt ift->deleteInterface(networkInterface); } +NetworkInterface *Ipv6RoutingTable::findTunnelNetworkInterface(const Ipv6Address& source, const Ipv6Address& destination) const +{ + Enter_Method("findTunnelNetworkInterface(%s, %s)=?", source.str().c_str(), destination.str().c_str()); + + for (int i = 0; i < ift->getNumInterfaces(); i++) { + NetworkInterface *ie = ift->getInterface(i); + for (cModule::SubmoduleIterator it(ie); !it.end(); ++it) { + auto *tunnel = dynamic_cast(*it); + if (tunnel != nullptr && tunnel->getSource() == source && tunnel->getDestination() == destination) + return ie; + } + } + return nullptr; +} + NetworkInterface *Ipv6RoutingTable::getInterfaceByAddress(const Ipv6Address& addr) const { Enter_Method("getInterfaceByAddress(%s)=?", addr.str().c_str()); diff --git a/src/inet/networklayer/ipv6/Ipv6RoutingTable.h b/src/inet/networklayer/ipv6/Ipv6RoutingTable.h index f6010abe1bb..7b951f3236c 100644 --- a/src/inet/networklayer/ipv6/Ipv6RoutingTable.h +++ b/src/inet/networklayer/ipv6/Ipv6RoutingTable.h @@ -301,6 +301,21 @@ class INET_API Ipv6RoutingTable : public SimpleModule, public IRoutingTable, pro */ virtual void deleteTunnelNetworkInterface(NetworkInterface *networkInterface); + /** + * Returns the ~Ipv6TunnelInterface configured with the given tunnel entry + * and exit points, or nullptr if this node has no such tunnel. Used to + * identify the tunnel a received IPv6-in-IPv6 datagram arrived through: + * for an incoming datagram, source is its outer destination address and + * destination its outer source address. + * + * Endpoints do not identify a tunnel uniquely -- several tunnels may share + * an entry and exit pair and differ only in what is routed onto them (MIPv6 + * split tunnels, for instance). They all represent the same pair of tunnel + * endpoints, so for the purpose of naming the link a datagram arrived on any + * of them will do, and the first match is returned. + */ + virtual NetworkInterface *findTunnelNetworkInterface(const Ipv6Address& source, const Ipv6Address& destination) const; + /** * Adds a default route for a host. This method requires the RA's source * address and the router expiry time plus the simTime(). diff --git a/src/inet/networklayer/ipv6tunneling/Ipv6Tunnel.h b/src/inet/networklayer/ipv6tunneling/Ipv6Tunnel.h index 75f19bb28fa..316c51138eb 100644 --- a/src/inet/networklayer/ipv6tunneling/Ipv6Tunnel.h +++ b/src/inet/networklayer/ipv6tunneling/Ipv6Tunnel.h @@ -28,6 +28,12 @@ namespace inet { */ class INET_API Ipv6Tunnel : public LayeredProtocolBase { + public: + /** The tunnel entry point: the outer source address this tunnel encapsulates with. */ + const Ipv6Address& getSource() const { return source; } + /** The tunnel exit point: the outer destination address this tunnel encapsulates toward. */ + const Ipv6Address& getDestination() const { return destination; } + protected: opp_component_ptr networkInterface; diff --git a/src/inet/networklayer/mipv6/Mipv6.cc b/src/inet/networklayer/mipv6/Mipv6.cc index c70d39b322b..d04cabb01dc 100644 --- a/src/inet/networklayer/mipv6/Mipv6.cc +++ b/src/inet/networklayer/mipv6/Mipv6.cc @@ -126,6 +126,7 @@ void Mipv6::initialize(int stage) // the Home Address Option (MN->CN) on route-optimized outgoing traffic // (replacing the old T2RH/HA_OPT pseudo-tunnels). ipv6->registerHook(0, this); + networkProtocol = ipv6; // Register for Protocol::mobileipv6 so the dispatcher delivers mobility messages to us registerProtocol(Protocol::mobileipv6, gate("toIPv6"), gate("fromIPv6")); @@ -1124,6 +1125,8 @@ void Mipv6::processBAMessage(Packet *inPacket, const PtrremoveBinding(baSource); + // this registration is over, so nothing will release datagrams held for it + dropHeldDatagrams(baSource); // remove all timers related to this BA address removeTimerEntries(baSource, ifTag->getInterfaceId()); } @@ -1142,6 +1145,9 @@ void Mipv6::processBAMessage(Packet *inPacket, const PtrcareOfAddress, entry->destAddress); + // the reverse tunnel now exists: send whatever was held for it + releaseHeldDatagrams(entry->destAddress); + /**11.5.1 After updating its home registration, the mobile node then updates associated mobility bindings in correspondent nodes @@ -1430,6 +1436,20 @@ void Mipv6::sendTestInit(cMessage *msg) MAY record the same information in multiple Binding Update List entries.*/ + // RFC 6275 Section 11.8 retransmits a Test Init only when no response arrived within + // the retransmission interval. A Home Test Init still held by + // holdUntilReverseTunnelExists() has not been sent, so no response can be missing + // yet: sending another one now would release both at the Binding Acknowledgement and + // run return routability twice. Check again one interval later; + // releaseHeldDatagrams() restarts the timer when the held one is sent. + if (dynamicPtrCast(tiIfEntry->testInitMsg) && isHomeTestInitHeld(tiIfEntry->dest)) { + EV_INFO << "The previous HoTI to " << tiIfEntry->dest << " is still held until the reverse " + << "tunnel exists; not retransmitting it\n"; + tiIfEntry->nextScheduledTime = simTime() + tiIfEntry->ackTimeout; + scheduleAt(tiIfEntry->nextScheduledTime, msg); + return; + } + // retrieve the cookie from the Test Init message if (auto homeTestInit = dynamicPtrCast(tiIfEntry->testInitMsg)) { // moved the following two lines to here @@ -2076,9 +2096,161 @@ INetfilter::IHook::Result Mipv6::datagramLocalOutHook(Packet *datagram) // a mobile node's own home-address-sourced traffic goes out the reverse tunnel requestTunnelOutputInterface(datagram); + + if (holdUntilReverseTunnelExists(datagram)) + return QUEUE; + return ACCEPT; } +bool Mipv6::holdUntilReverseTunnelExists(Packet *datagram) +{ + if (!rt6->isMobileNode()) + return false; + + const auto& ipv6Header = datagram->peekAtFront(); + if (datagram->findTag() != nullptr) + return false; // an output interface (the reverse tunnel, or another one) is already pinned + + /* RFC 6275 Section 11.3.1: + "If a binding exists, the mobile node SHOULD send the packets directly to + the correspondent node. Otherwise, if a binding does not exist, the mobile + node MUST use reverse tunneling." + The standard licenses no third treatment, discarding least of all. The reverse + tunnel is created only when the home agent's Binding Acknowledgement arrives, + so between sending a Binding Update for a new care-of address and receiving + that acknowledgement requestTunnelOutputInterface() finds no tunnel to pin. + The datagram would then be routed onto the foreign link, where + Ipv6::resolveMACAddressAndSendPacket() discards it because a home address is + not a topologically correct source there. Hold it until the tunnel exists. */ + for (int i = 0; i < ift->getNumInterfaces(); i++) { + NetworkInterface *ie = ift->getInterface(i); + auto mipv6Data = ie->findProtocolData(); + if (mipv6Data == nullptr || mipv6Data->getMNHomeAddress() != ipv6Header->getSrcAddress()) + continue; + + // Hold only what the drop guard would actually discard. That guard fires on an + // interface that has a care-of address, i.e. one the node is away from home on; + // a home-address-sourced datagram leaving an interface that has none is + // topologically correct and must be sent, not held. + auto ipv6Data = ie->findProtocolData(); + if (ipv6Data == nullptr || ipv6Data->getGlobalAddress(Ipv6InterfaceData::CoA).isUnspecified()) + continue; + + // Hold only while a home registration is actually in flight -- otherwise + // nothing would ever release the datagram, and holding it would replace a + // visible drop with a silent leak. + const Ipv6Address& homeAgentAddress = mipv6Data->getHomeAgentAddress(); + BindingUpdateList::BindingUpdateListEntry *entry = bul->lookup(homeAgentAddress); + if (entry == nullptr || entry->BAck) + continue; + + // A Binding Update that is never acknowledged is retransmitted indefinitely and + // its binding update list entry never expires, so cap what one registration may + // accumulate and discard the oldest beyond it. + if (heldDatagrams.count(homeAgentAddress) >= MAX_HELD_DATAGRAMS_PER_HOME_AGENT) { + auto oldest = heldDatagrams.lower_bound(homeAgentAddress); + EV_WARN << "Already holding " << MAX_HELD_DATAGRAMS_PER_HOME_AGENT << " datagrams for home " + << "agent " << homeAgentAddress << "; dropping the oldest\n"; + dropHeldDatagram(oldest->second, QUEUE_OVERFLOW); + heldDatagrams.erase(oldest); + } + + EV_INFO << "No reverse tunnel to home agent " << homeAgentAddress << " yet; holding the " + << "datagram from " << ipv6Header->getSrcAddress() << " to " + << ipv6Header->getDestAddress() << " until the registration completes\n"; + heldDatagrams.insert({ homeAgentAddress, datagram }); + return true; + } + return false; +} + +std::vector Mipv6::takeHeldDatagrams(const Ipv6Address& homeAgentAddress) +{ + // Detach the datagrams from the map before doing anything with them: reinjecting + // one re-enters the IPv6 module synchronously, and anything it holds in turn would + // otherwise be erased by a range erase performed afterwards. + auto lower = heldDatagrams.lower_bound(homeAgentAddress); + auto upper = heldDatagrams.upper_bound(homeAgentAddress); + std::vector datagrams; + for (auto it = lower; it != upper; ++it) + datagrams.push_back(it->second); + heldDatagrams.erase(lower, upper); + return datagrams; +} + +void Mipv6::releaseHeldDatagrams(const Ipv6Address& homeAgentAddress) +{ + for (Packet *datagram : takeHeldDatagrams(homeAgentAddress)) { + EV_INFO << "Reverse tunnel to home agent " << homeAgentAddress << " is up; sending the held " + << "datagram " << datagram->getName() << "\n"; + // The local-out hook is not re-run on reinjection (reinjectQueuedDatagram() + // resumes at datagramLocalOut()), so pin the tunnel interface here, exactly + // as the hook would have done had the tunnel existed when the datagram was + // first offered. + requestTunnelOutputInterface(datagram); + // A held Home Test Init is transmitted only now, so its retransmission back-off + // starts now (RFC 6275 Section 11.8), not when it was first offered. + if (isHomeTestInit(datagram)) + restartHomeTestInitTimer(datagram->peekAtFront()->getDestAddress()); + networkProtocol->reinjectQueuedDatagram(datagram); + } +} + +bool Mipv6::isHomeTestInit(Packet *datagram) const +{ + const auto& ipv6Header = datagram->peekAtFront(); + if (ipv6Header->getProtocolId() != IP_PROT_IPv6EXT_MOB) + return false; + const auto& mobilityHeader = datagram->peekDataAt(ipv6Header->getChunkLength()); + return mobilityHeader->getMobilityHeaderType() == HOME_TEST_INIT; +} + +bool Mipv6::isHomeTestInitHeld(const Ipv6Address& cnAddress) const +{ + for (const auto& elem : heldDatagrams) + if (isHomeTestInit(elem.second) && elem.second->peekAtFront()->getDestAddress() == cnAddress) + return true; + return false; +} + +void Mipv6::restartHomeTestInitTimer(const Ipv6Address& cnAddress) +{ + for (auto& elem : transmitIfList) { + if (elem.first.type != KEY_HI || elem.first.dest != cnAddress) + continue; + TimerIfEntry *entry = elem.second; + if (entry->timer != nullptr && entry->timer->isScheduled()) { + // the transmission happening now is the first one, so start the back-off + // from the initial interval, as sendTestInit() does after a first send + auto mipv6Data = entry->ifEntry->getProtocolData(); + entry->nextScheduledTime = simTime() + mipv6Data->_getInitialBindAckTimeout(); + entry->ackTimeout = std::min(2 * mipv6Data->_getInitialBindAckTimeout(), mipv6Data->_getMaxBindAckTimeout()); + rescheduleAt(entry->nextScheduledTime, entry->timer); + } + } +} + +void Mipv6::dropHeldDatagrams(const Ipv6Address& homeAgentAddress) +{ + for (Packet *datagram : takeHeldDatagrams(homeAgentAddress)) { + EV_WARN << "Registration with home agent " << homeAgentAddress << " will not complete; " + << "dropping the held datagram " << datagram->getName() << "\n"; + dropHeldDatagram(datagram, OTHER_PACKET_DROP); + } +} + +void Mipv6::dropHeldDatagram(Packet *datagram, PacketDropReason reason) +{ + // The path this replaces counted the loss (Ipv6::numDropped); report it the modern + // way instead, so a held datagram that is discarded is as visible to a statistic or + // a visualizer as one the IPv6 layer drops itself. + PacketDropDetails details; + details.setReason(reason); + emit(packetDroppedSignal, datagram, &details); + networkProtocol->dropQueuedDatagram(datagram); +} + void Mipv6::requestTunnelOutputInterface(Packet *datagram) { // Steer home-address-destined traffic (at the home agent, on the forwarding path) @@ -2788,6 +2960,8 @@ void Mipv6::handleBULExpiry(cMessage *msg) entry->state = BindingUpdateList::NONE; // remove binding bul->removeBinding(bulExpIfEntry->dest); + // the registration will not complete, so nothing will release datagrams held for it + dropHeldDatagrams(bulExpIfEntry->dest); // remove all timers int interfaceID = bulExpIfEntry->ifEntry->getInterfaceId(); @@ -2917,6 +3091,11 @@ void Mipv6::handleStopOperation(LifecycleOperation *operation) interfaceCoAList.clear(); cnList.clear(); + // Ipv6::flush() has freed (or is about to free) every datagram in its netfilter + // queue without telling the hooks, so only forget the borrowed pointers here -- + // dropping them through the IPv6 module would touch memory it already owns. + heldDatagrams.clear(); + tunnels.clear(); noOfNonSplitTunnels = 0; } @@ -2933,6 +3112,11 @@ void Mipv6::handleCrashOperation(LifecycleOperation *operation) interfaceCoAList.clear(); cnList.clear(); + // Ipv6::flush() has freed (or is about to free) every datagram in its netfilter + // queue without telling the hooks, so only forget the borrowed pointers here -- + // dropping them through the IPv6 module would touch memory it already owns. + heldDatagrams.clear(); + tunnels.clear(); noOfNonSplitTunnels = 0; } diff --git a/src/inet/networklayer/mipv6/Mipv6.h b/src/inet/networklayer/mipv6/Mipv6.h index bb871ae9114..ef1c5089ca5 100644 --- a/src/inet/networklayer/mipv6/Mipv6.h +++ b/src/inet/networklayer/mipv6/Mipv6.h @@ -16,6 +16,7 @@ #include #include "inet/common/ModuleRefByPar.h" +#include "inet/common/Simsignals_m.h" #include "inet/common/lifecycle/OperationalBase.h" #include "inet/common/lifecycle/ModuleOperations.h" @@ -71,6 +72,32 @@ class INET_API Mipv6 : public OperationalBase, public IIpv6ExtensionHeaderHandle ModuleRefByPar bc; ModuleRefByPar ipv6nd; + // The IPv6 module this one hooks into (set in initialize()), used to reinject or + // drop the datagrams held by holdUntilReverseTunnelExists(). A plain pointer is + // enough: it is a sibling submodule of the same Ipv6NetworkLayer, so it is created + // before and destroyed after this module, and it is never replaced at run time. + INetfilter *networkProtocol = nullptr; + + // Home-address-sourced datagrams held while the reverse tunnel to the home agent + // does not exist yet, keyed by the home agent whose Binding Acknowledgement they + // are waiting for. + // + // Ownership: the datagrams belong to the IPv6 module's netfilter queue, and these + // are borrowed pointers. The IPv6 module frees that queue in flush() on stop and + // crash without telling the hooks, so this map must be cleared in the same + // lifecycle operations (handleStopOperation/handleCrashOperation) -- a pointer + // left here across a restart would be dereferenced after free by + // releaseHeldDatagrams(). + std::multimap heldDatagrams; + + // Upper bound on the datagrams held for one home agent. A Binding Update that is + // never acknowledged is retransmitted indefinitely (RFC 6275 Section 11.8) and its + // binding update list entry never expires, so the registration alone does not bound + // the hold; without a cap a home agent that stopped answering would make a mobile + // node accumulate every packet it sends for the rest of the run. On overflow the + // oldest held datagram is dropped, with a packetDropped signal. + static const unsigned int MAX_HELD_DATAGRAMS_PER_HOME_AGENT = 100; + // // IP tunnel management (RFC 2473), moved here from the former Ipv6Tunneling // module. A "tunnel" is a dynamically created Ipv6TunnelInterface (built by @@ -488,6 +515,59 @@ class INET_API Mipv6 : public OperationalBase, public IIpv6ExtensionHeaderHandle */ void requestTunnelOutputInterface(Packet *datagram); + /** + * RFC 6275 Section 11.3.1: while away from home and without a binding at the + * correspondent node, a mobile node MUST reverse-tunnel packets that it sources + * from its home address. If such a datagram is offered while the reverse tunnel + * to the home agent does not exist yet -- between sending a Binding Update for a + * new care-of address and receiving the Binding Acknowledgement -- hold it and + * return true, so the local-out hook can QUEUE it instead of letting it be routed + * onto the foreign link (where Ipv6 discards it as topologically incorrect). + * Returns false if the datagram can be sent as it is. + */ + bool holdUntilReverseTunnelExists(Packet *datagram); + + /** + * Reinject the datagrams held by holdUntilReverseTunnelExists() for the given home + * agent, now that the reverse tunnel to it exists. + */ + void releaseHeldDatagrams(const Ipv6Address& homeAgentAddress); + + /** + * Discard the datagrams held by holdUntilReverseTunnelExists() for the given home + * agent, because the registration they were waiting for will not complete. + */ + void dropHeldDatagrams(const Ipv6Address& homeAgentAddress); + + /** + * Detach the datagrams held for the given home agent from heldDatagrams and return + * them, so that a caller can reinject or drop them without the map being mutated + * underneath it by the re-entrant call that reinjection makes. + */ + std::vector takeHeldDatagrams(const Ipv6Address& homeAgentAddress); + + /** + * Discard one held datagram, reporting it with a packetDropped signal first. + */ + void dropHeldDatagram(Packet *datagram, PacketDropReason reason); + + /** + * Returns true if the datagram carries a Home Test Init message. + */ + bool isHomeTestInit(Packet *datagram) const; + + /** + * Returns true if a Home Test Init to the given correspondent node is among the + * datagrams held by holdUntilReverseTunnelExists(), i.e. it has not been sent yet. + */ + bool isHomeTestInitHeld(const Ipv6Address& cnAddress) const; + + /** + * Restart the pending Home Test Init retransmission timer for the given + * correspondent node from the initial retransmission interval, counted from now. + */ + void restartHomeTestInitTimer(const Ipv6Address& cnAddress); + /** * A route-optimization extension-header insertion that the local-out hook * applies to outgoing traffic, replacing a T2RH/HA_OPT pseudo-tunnel. diff --git a/tests/fingerprint/examples.csv b/tests/fingerprint/examples.csv index 9fc76d91942..2fce5b54b71 100644 --- a/tests/fingerprint/examples.csv +++ b/tests/fingerprint/examples.csv @@ -380,9 +380,9 @@ /examples/manetrouting/multiradio/, -f omnetpp.ini -c MultiRadio -r 0, 20s, ec17-5cc2/tplx;55f5-0894/~tNl, PASS, wireless adhoc Ipv4 /examples/manetrouting/multiradio/, -f omnetpp.ini -c SingleRadio -r 0, 20s, 85a0-51b8/tplx;c07a-44e1/~tNl;3aa0-49ed/tyf, PASS, wireless adhoc Ipv4 -/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, dca6-3751/tplx;a44c-17bf/~tNl;96ff-ed7c/~tND;44ef-1a45/tyf, PASS, wireless EthernetMac -/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 1560-028f/tplx;e6db-28c0/~tNl;805b-8a25/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac -/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 342e-5e65/tplx;28ce-974e/~tNl;dcec-ae59/~tND;afae-2b3c/tyf, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, 21f1-ebd1/tplx;18c3-f600/~tNl;1cd9-872d/~tND;44ef-1a45/tyf, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, f2bf-98a6/tplx;e791-2cf4/~tNl;b4c1-7888/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac +/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 5a79-c7e1/tplx;d557-f31d/~tNl;331a-4535/~tND;afae-2b3c/tyf, PASS, wireless EthernetMac /examples/ipv6/pmipv6/, -f omnetpp.ini -c General -r 0, 60s, 081b-769c/tplx;1064-9bd4/~tNl;bd46-d85c/~tND;0277-d784/tyf, PASS, wireless EthernetMac /examples/mobility/, -f omnetpp.ini -c AnsimMobility -r 0, 10000s, 72f8-5c0b/tplx;0000-0000/~tNl;0000-0000/~tND;7dd1-18eb/tyf, PASS, diff --git a/tests/fingerprint/mipv6-refactoring.csv b/tests/fingerprint/mipv6-refactoring.csv index 4e8ebad4ab4..04adfb1b5ae 100644 --- a/tests/fingerprint/mipv6-refactoring.csv +++ b/tests/fingerprint/mipv6-refactoring.csv @@ -8,9 +8,9 @@ # empty-shim step, which kept these fingerprints bit-for-bit identical. # MIPv6 example — the primary test -/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, 8a43-2e1a/~tNlb, PASS, wireless EthernetMac -/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 8a20-d2eb/~tNlb, PASS, wireless EthernetMac -/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, bc7d-f706/~tNlb, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, 23c1-5f4a/~tNlb, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 2c04-f051/~tNlb, PASS, wireless EthernetMac +/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, bf23-11dd/~tNlb, PASS, wireless EthernetMac # IPv6 examples # MLD example — new PASS row; ~tNl locks the MLD Report/Query/Done/MAS-Query packet exchange (traffic+lengths); diff --git a/tests/module/MIPv6_held_home_test_init_not_retransmitted.test b/tests/module/MIPv6_held_home_test_init_not_retransmitted.test new file mode 100644 index 00000000000..ecb8f453dec --- /dev/null +++ b/tests/module/MIPv6_held_home_test_init_not_retransmitted.test @@ -0,0 +1,191 @@ +%description: +A mobile node moves to a foreign network while the correspondent node pings its home +address. The home agent starts tunneling the pings as soon as it accepts the Binding +Update, and the first tunneled ping makes the mobile node start return routability. Its +Home Test Init is sourced from the home address, so it is held until the Binding +Acknowledgement creates the reverse tunnel. + +The home agent sends the acknowledgement of a first registration 1 s after it accepts the +Binding Update (its stand-in for Duplicate Address Detection), and it starts tunneling at +acceptance. The link between Home_Agent and R_2 delays frames that carry a Binding +Acknowledgement by another 0.4 s, so the acknowledgement arrives more than INITIAL_BINDACK_TIMEOUT (1 s) +after the Home Test Init was held: the Home Test Init is held at t = 34.004 s, its +retransmission timer fires at 35.004 s, and the acknowledgement arrives at 35.231 s. + +RFC 6275 Section 11.8 retransmits a Home Test Init only if no response arrives within the +retransmission interval. A held Home Test Init has not been sent, so the test requires that +the retransmission timer does not send a second one: the correspondent node receives one +Home Test Init and one Binding Update, not two of each. + +%#-------------------------------------------------------------------------------------------------------------- +%file: BindingAcknowledgementDelayChannel.cc +#include "inet/common/INETDefs.h" + +namespace inet { + +class BindingAcknowledgementDelayChannel : public cDatarateChannel +{ + public: + explicit BindingAcknowledgementDelayChannel(const char *name = nullptr) : cDatarateChannel(name) {} + + virtual Result processMessage(cMessage *msg, const SendOptions& options, simtime_t t) override + { + Result result = cDatarateChannel::processMessage(msg, options, t); + if (strstr(msg->getName(), "Binding Acknowledgement") != nullptr) + result.delay += par("extraDelay").doubleValue(); + return result; + } +}; + +Define_Channel(BindingAcknowledgementDelayChannel); + +} // namespace inet + +%#-------------------------------------------------------------------------------------------------------------- +%file: test.ned +import inet.networklayer.configurator.ipv6.Ipv6FlatNetworkConfigurator; +import inet.node.ethernet.EthernetSwitch; +import inet.node.ipv6.Router6; +import inet.node.wireless.AccessPoint; +import inet.node.mipv6.CorrespondentNode6; +import inet.node.mipv6.HomeAgent6; +import inet.node.mipv6.WirelessHost6; +import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium; +import inet.test.moduletest.lib.ethernetline; + +channel BindingAcknowledgementDelayChannel extends ned.DatarateChannel +{ + @class(::inet::BindingAcknowledgementDelayChannel); + double extraDelay @unit(s) = default(0s); + delay = 0.1us; + datarate = 100Mbps; +} + +network Mipv6DelayedAckNetwork +{ + parameters: + **.mgmt.numChannels = 5; + submodules: + configurator: Ipv6FlatNetworkConfigurator; + radioMedium: Ieee80211ScalarRadioMedium; + Home_Agent: HomeAgent6; + R_1: Router6; + R_2: Router6; + MN: WirelessHost6; + CN: CorrespondentNode6; + AP_Home: AccessPoint { + @display("p=249,172"); + } + AP_1: AccessPoint { + @display("p=566,172"); + } + switch: EthernetSwitch; + connections allowunconnected: + R_1.ethg++ <--> ethernetline <--> R_2.ethg++; + Home_Agent.ethg++ <--> BindingAcknowledgementDelayChannel { extraDelay = 0.4s; } <--> R_2.ethg++; + CN.ethg++ <--> ethernetline <--> switch.ethg++; + switch.ethg++ <--> ethernetline <--> R_2.ethg++; + AP_Home.ethg++ <--> ethernetline <--> Home_Agent.ethg++; + AP_1.ethg++ <--> ethernetline <--> R_1.ethg++; +} + +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini +[General] +record-vector-results = false +**.mipv6.cmdenv-log-level = trace +**.cmdenv-log-level = off +cmdenv-event-banners = false +ned-path = .;../../../../src;../../lib +network = Mipv6DelayedAckNetwork +sim-time-limit = 60s +cmdenv-express-mode = false +cmdenv-log-prefix = "%C: " +num-rngs = 3 +seed-set = 1 +**.mobility.rng-0 = 2 + +**.neighbourDiscovery.minIntervalBetweenRAs = 0.03s +**.neighbourDiscovery.maxIntervalBetweenRAs = 0.07s +**.neighbourDiscovery.detectL2Movement = false + +*.radioMedium.mediumLimitCache.maxTransmissionPower = 2.0mW +*.radioMedium.mediumLimitCache.minReceptionPower = -82dBm +*.radioMedium.mediumLimitCache.minInterferencePower = -82dBm + +**.wlan*.mgmt.numAuthSteps = 4 +**.AP*.wlan*.mgmt.beaconInterval = 0.1s +**.AP_Home.wlan*.mgmt.ssid = "HOME" +**.AP_Home.wlan*.address = "10:AA:00:00:00:01" +**.AP_Home.eth[0].address = "10:AE:00:00:00:02" +**.AP_1.wlan*.mgmt.ssid = "AP1" +**.AP_1.wlan*.address = "10:AA:00:00:A1:01" +**.AP_1.eth[0].address = "10:AE:00:00:A1:02" + +**.mobility.constraintAreaMinZ = 0m +**.mobility.constraintAreaMaxZ = 0m +**.MN.mobility.typename = "RectangleMobility" +**.MN.mobility.constraintAreaMinX = 180m +**.MN.mobility.constraintAreaMinY = 170m +**.MN.mobility.constraintAreaMaxX = 630m +**.MN.mobility.constraintAreaMaxY = 180m +**.MN.mobility.startPos = 0 +**.MN.mobility.speed = 10mps +**.MN.mobility.updateInterval = 0.1s + +**.MN.numApps = 0 +**.CN.numApps = 1 +**.CN.app[0].typename = "PingApp" +**.CN.app[0].destAddr = "aaaa:0:66:0:8aa:ff:fe00:8" +**.CN.app[0].packetSize = 56B +**.CN.app[0].sendInterval = 0.2s +**.CN.app[0].startTime = 1s +**.CN.app[0].hopLimit = 32 + +**.forwarding = false +**.eth*.duplexMode = true +**.analogModel.ignorePartialInterference = true +**.AP_Home.wlan*.radio.channelNumber = 1 +**.AP_1.wlan*.radio.channelNumber = 2 +**.MN.wlan*.radio.channelNumber = 0 +**.wlan*.agent.activeScan = true +**.wlan*.agent.defaultSsid = "" +**.wlan*.agent.channelsToScan = "1 2" +**.wlan*.agent.probeDelay = 0.1s +**.wlan*.agent.minChannelTime = 0.15s +**.wlan*.agent.maxChannelTime = 0.3s +**.wlan*.agent.authenticationTimeout = 5s +**.wlan*.agent.associationTimeout = 5s +**.wlan*.bitrate = 2Mbps +**.wlan*.mac.dcf.channelAccess.cwMin = 7 +**.radio.transmitter.power = 2.0mW +**.constraintAreaMinX = 0m +**.constraintAreaMinY = 0m +**.constraintAreaMaxX = 850m +**.constraintAreaMaxY = 850m + +%#-------------------------------------------------------------------------------------------------------------- +%subst: /omnetpp::// +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: grep -c "CN.ipv6.mipv6: Message recognised as HOME TEST INIT" test.out > hoti_count.out || true +%postrun-command: grep -c "CN.ipv6.mipv6: Message recognised as BINDING UPDATE" test.out > bu_count.out || true +%postrun-command: grep "undisposed object:" test.out > test_undisposed.out || true +%#-------------------------------------------------------------------------------------------------------------- +%# the retransmission timer of the Home Test Init fired while the Home Test Init was held... +%contains: stdout +is still held until the reverse tunnel exists; not retransmitting it +%#-------------------------------------------------------------------------------------------------------------- +%# ...and the held Home Test Init was sent when the reverse tunnel came up +%contains: stdout +is up; sending the held datagram HoTI +%#-------------------------------------------------------------------------------------------------------------- +%# one Home Test Init and one Binding Update reach the correspondent node +%contains-regex: hoti_count.out +^1$ +%#-------------------------------------------------------------------------------------------------------------- +%contains-regex: bu_count.out +^1$ +%#-------------------------------------------------------------------------------------------------------------- +%not-contains: test_undisposed.out +undisposed object: ( +%#-------------------------------------------------------------------------------------------------------------- diff --git a/tests/module/MIPv6_no_redirect_for_tunneled_traffic.test b/tests/module/MIPv6_no_redirect_for_tunneled_traffic.test new file mode 100644 index 00000000000..9e5b82b4248 --- /dev/null +++ b/tests/module/MIPv6_no_redirect_for_tunneled_traffic.test @@ -0,0 +1,138 @@ +%description: +A mobile node away from home reverse-tunnels its traffic through its home agent, which +decapsulates each datagram and forwards the inner one toward the correspondent node. + +This is a regression test for the arrival interface of a decapsulated datagram. RFC 4861 +Section 8.2 lets a router send a Redirect only when, among other conditions, "the Source +Address field of the packet identifies a neighbor". A reverse-tunneled datagram does not +arrive on a link at all -- it arrives through the tunnel, which RFC 2473 Section 3 models +as a link of its own -- and its source, the mobile node's home address, is not a neighbor +on the physical interface the outer datagram came in on. The inner datagram used to keep +that physical interface, so the home agent, forwarding it back out of the same interface, +believed it was redirecting an on-link neighbor and sent a Redirect to a node that was not +on the link. Because Ipv6.ned defaults sendRedirects to true, a home agent did this without +any configuration whenever it forwarded reverse-tunneled traffic back out of its arrival +interface. + +This configuration leaves sendRedirects at its default, so the home agent would send those +Redirects if the defect were reintroduced. The test asserts that it sends none, and -- so +that the assertion cannot pass merely because no datagram was ever reverse-tunneled -- that +the home agent really does decapsulate tunneled traffic during the run. + +The Redirect check also passes when the Redirect is suppressed for another reason, such as +a check that the source is a neighbor. The test therefore also asserts the arrival interface +itself: the home agent routes the decapsulated datagram as arriving on its tunnel interface +(ip6tun...), not on the physical interface. + +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini +[General] +record-vector-results = false +**.mipv6.cmdenv-log-level = trace +**.ipv6.ipv6.cmdenv-log-level = trace +**.ipv6.neighbourDiscovery.cmdenv-log-level = trace +**.app[*].cmdenv-log-level = trace +**.cmdenv-log-level = off +cmdenv-event-banners = false +ned-path = .;../../../../src;../../lib +network = inet.test.moduletest.lib.Mipv6RoamingNetwork +sim-time-limit = 40s +cmdenv-express-mode = false +cmdenv-log-prefix = "%C: " +num-rngs = 3 +seed-set = 1 +**.mobility.rng-0 = 2 + +*.total_mn = 1 +*.total_cn = 1 + +**.neighbourDiscovery.minIntervalBetweenRAs = 0.03s +**.neighbourDiscovery.maxIntervalBetweenRAs = 0.07s +**.neighbourDiscovery.detectL2Movement = false + +*.radioMedium.mediumLimitCache.maxTransmissionPower = 2.0mW +*.radioMedium.mediumLimitCache.minReceptionPower = -82dBm +*.radioMedium.mediumLimitCache.minInterferencePower = -82dBm + +**.wlan*.mgmt.numAuthSteps = 4 +**.AP*.wlan*.mgmt.beaconInterval = 0.1s +**.AP_Home.wlan*.mgmt.ssid = "HOME" +**.AP_Home.wlan*.address = "10:AA:00:00:00:01" +**.AP_Home.eth[0].address = "10:AE:00:00:00:02" +**.AP_Home.eth[0].duplexMode = true +**.AP_1.wlan*.mgmt.ssid = "AP1" +**.AP_1.wlan*.address = "10:AA:00:00:A1:01" +**.AP_1.eth[0].address = "10:AE:00:00:A1:02" +**.AP_1.eth[0].duplexMode = true +**.AP_2.wlan*.mgmt.ssid = "AP2" +**.AP_2.wlan*.address = "10:AA:00:00:A2:01" +**.AP_2.eth[0].address = "10:AE:00:00:A2:02" +**.AP_2.eth[0].duplexMode = true + +**.mobility.constraintAreaMinZ = 0m +**.mobility.constraintAreaMaxZ = 0m +**.MN[0].mobility.typename = "RectangleMobility" +**.MN[0].mobility.constraintAreaMinX = 180m +**.MN[0].mobility.constraintAreaMinY = 170m +**.MN[0].mobility.constraintAreaMaxX = 950m +**.MN[0].mobility.constraintAreaMaxY = 180m +**.MN[0].mobility.startPos = 0 +**.MN[0].mobility.speed = 10mps +**.MN*.mobility.updateInterval = 0.1s + +# CN pings the MN's home address throughout; the replies must follow the MN across both handovers +**.MN*.numApps = 0 +**.CN*.numApps = 1 +**.CN*.app[*].typename = "PingApp" +**.CN[0].app[0].destAddr = "aaaa:0:66:0:8aa:ff:fe00:b" +**.CN*.app[0].packetSize = 56B +**.CN*.app[0].sendInterval = 0.5s +**.CN[0].app[0].startTime = 1s +**.CN*.app[0].hopLimit = 32 +**.CN*.app[0].printPing = true + +**.forwarding = false +**.eth[*].queue.typename = "EthernetQosQueue" +**.eth[*].queue.dataQueue.typename = "DropTailQueue" +**.eth[*].queue.dataQueue.packetCapacity = 10 +**.eth*.duplexMode = true +**.analogModel.ignorePartialInterference = true +**.AP_Home.wlan*.radio.channelNumber = 1 +**.AP_1.wlan*.radio.channelNumber = 2 +**.AP_2.wlan*.radio.channelNumber = 3 +**.MN*.wlan*.radio.channelNumber = 0 +**.wlan*.agent.activeScan = true +**.wlan*.agent.defaultSsid = "" +**.wlan*.agent.channelsToScan = "1 2 3" +**.wlan*.agent.probeDelay = 0.1s +**.wlan*.agent.minChannelTime = 0.15s +**.wlan*.agent.maxChannelTime = 0.3s +**.wlan*.agent.authenticationTimeout = 5s +**.wlan*.agent.associationTimeout = 5s +**.wlan*.bitrate = 2Mbps +**.wlan*.mac.dcf.channelAccess.cwMin = 7 +**.radio.transmitter.power = 2.0mW +**.constraintAreaMinX = 0m +**.constraintAreaMinY = 0m +**.constraintAreaMaxX = 1050m +**.constraintAreaMaxY = 850m + +%#-------------------------------------------------------------------------------------------------------------- +%subst: /omnetpp::// +%#-------------------------------------------------------------------------------------------------------------- +%# the home agent really does decapsulate reverse-tunneled traffic in this run +%contains: stdout +Mipv6RoamingNetwork.Home_Agent.ipv6.ipv6: Tunnelled IP datagram +%#-------------------------------------------------------------------------------------------------------------- +%# the decapsulated datagram is routed as arriving on the tunnel interface +%contains: stdout +Mipv6RoamingNetwork.Home_Agent.ipv6.ipv6: Routing the decapsulated datagram as arriving on ip6tun +%#-------------------------------------------------------------------------------------------------------------- +%# the defect itself: forwarding a decapsulated datagram must not trigger a Redirect +%not-contains: stdout +Mipv6RoamingNetwork.Home_Agent.ipv6.neighbourDiscovery: Sending ICMPv6 Redirect +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: grep "undisposed object:" test.out > test_undisposed.out || true +%not-contains: test_undisposed.out +undisposed object: ( +%#-------------------------------------------------------------------------------------------------------------- diff --git a/tests/module/MIPv6_reverse_tunnel_pending.test b/tests/module/MIPv6_reverse_tunnel_pending.test new file mode 100644 index 00000000000..3cdaf1b8a4a --- /dev/null +++ b/tests/module/MIPv6_reverse_tunnel_pending.test @@ -0,0 +1,134 @@ +%description: +A mobile node moves to a foreign network and registers its new care-of address with its +home agent. RFC 6275 Section 11.3.1 requires that, without a binding at the correspondent +node, packets the mobile node sources from its home address be reverse-tunneled through +the home agent -- it licenses no other treatment, and discarding least of all. + +This is a regression test for the window between sending the Binding Update and receiving +the Binding Acknowledgement. The reverse tunnel is created only when the acknowledgement +arrives, so during that window nothing pins it as the output interface, and the datagram +used to be routed onto the foreign link, where Ipv6 discarded it as topologically +incorrect ("Using HoA instead of CoA... dropping datagram"). The correspondent node's +echo replies and the first Home Test Init were lost that way once per handover. + +The test asserts that no such datagram is discarded, and -- so that the assertion cannot +pass merely because the window was never entered -- that the hold-and-release path is +actually exercised: at least one datagram is held for the missing reverse tunnel and sent +once the tunnel comes up. + +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini +[General] +record-vector-results = false +**.mipv6.cmdenv-log-level = trace +**.ipv6.ipv6.cmdenv-log-level = trace +**.ipv6.neighbourDiscovery.cmdenv-log-level = trace +**.app[*].cmdenv-log-level = trace +**.cmdenv-log-level = off +cmdenv-event-banners = false +ned-path = .;../../../../src;../../lib +network = inet.test.moduletest.lib.Mipv6RoamingNetwork +sim-time-limit = 40s +cmdenv-express-mode = false +cmdenv-log-prefix = "%C: " +num-rngs = 3 +seed-set = 1 +**.mobility.rng-0 = 2 + +*.total_mn = 1 +*.total_cn = 1 + +**.neighbourDiscovery.minIntervalBetweenRAs = 0.03s +**.neighbourDiscovery.maxIntervalBetweenRAs = 0.07s +**.neighbourDiscovery.detectL2Movement = false + +*.radioMedium.mediumLimitCache.maxTransmissionPower = 2.0mW +*.radioMedium.mediumLimitCache.minReceptionPower = -82dBm +*.radioMedium.mediumLimitCache.minInterferencePower = -82dBm + +**.wlan*.mgmt.numAuthSteps = 4 +**.AP*.wlan*.mgmt.beaconInterval = 0.1s +**.AP_Home.wlan*.mgmt.ssid = "HOME" +**.AP_Home.wlan*.address = "10:AA:00:00:00:01" +**.AP_Home.eth[0].address = "10:AE:00:00:00:02" +**.AP_Home.eth[0].duplexMode = true +**.AP_1.wlan*.mgmt.ssid = "AP1" +**.AP_1.wlan*.address = "10:AA:00:00:A1:01" +**.AP_1.eth[0].address = "10:AE:00:00:A1:02" +**.AP_1.eth[0].duplexMode = true +**.AP_2.wlan*.mgmt.ssid = "AP2" +**.AP_2.wlan*.address = "10:AA:00:00:A2:01" +**.AP_2.eth[0].address = "10:AE:00:00:A2:02" +**.AP_2.eth[0].duplexMode = true + +**.mobility.constraintAreaMinZ = 0m +**.mobility.constraintAreaMaxZ = 0m +**.MN[0].mobility.typename = "RectangleMobility" +**.MN[0].mobility.constraintAreaMinX = 180m +**.MN[0].mobility.constraintAreaMinY = 170m +**.MN[0].mobility.constraintAreaMaxX = 950m +**.MN[0].mobility.constraintAreaMaxY = 180m +**.MN[0].mobility.startPos = 0 +**.MN[0].mobility.speed = 10mps +**.MN*.mobility.updateInterval = 0.1s + +# CN pings the MN's home address throughout; the replies must follow the MN across both handovers +**.MN*.numApps = 0 +**.CN*.numApps = 1 +**.CN*.app[*].typename = "PingApp" +**.CN[0].app[0].destAddr = "aaaa:0:66:0:8aa:ff:fe00:b" +**.CN*.app[0].packetSize = 56B +**.CN*.app[0].sendInterval = 0.5s +**.CN[0].app[0].startTime = 1s +**.CN*.app[0].hopLimit = 32 +**.CN*.app[0].printPing = true + +**.forwarding = false +**.eth[*].queue.typename = "EthernetQosQueue" +**.eth[*].queue.dataQueue.typename = "DropTailQueue" +**.eth[*].queue.dataQueue.packetCapacity = 10 +**.eth*.duplexMode = true +**.analogModel.ignorePartialInterference = true +**.AP_Home.wlan*.radio.channelNumber = 1 +**.AP_1.wlan*.radio.channelNumber = 2 +**.AP_2.wlan*.radio.channelNumber = 3 +**.MN*.wlan*.radio.channelNumber = 0 +**.wlan*.agent.activeScan = true +**.wlan*.agent.defaultSsid = "" +**.wlan*.agent.channelsToScan = "1 2 3" +**.wlan*.agent.probeDelay = 0.1s +**.wlan*.agent.minChannelTime = 0.15s +**.wlan*.agent.maxChannelTime = 0.3s +**.wlan*.agent.authenticationTimeout = 5s +**.wlan*.agent.associationTimeout = 5s +**.wlan*.bitrate = 2Mbps +**.wlan*.mac.dcf.channelAccess.cwMin = 7 +**.radio.transmitter.power = 2.0mW +**.constraintAreaMinX = 0m +**.constraintAreaMinY = 0m +**.constraintAreaMaxX = 1050m +**.constraintAreaMaxY = 850m + +%#-------------------------------------------------------------------------------------------------------------- +%subst: /omnetpp::// +%#-------------------------------------------------------------------------------------------------------------- +%# the mobile node does register, so the window this test is about is really entered +%contains: stdout +Mipv6RoamingNetwork.MN[0].ipv6.mipv6: Binding was accepted. +%#-------------------------------------------------------------------------------------------------------------- +%# a home-address-sourced datagram was offered before the reverse tunnel existed... +%contains: stdout +yet; holding the datagram from +%#-------------------------------------------------------------------------------------------------------------- +%# ...and was sent, not discarded, once the tunnel came up +%contains: stdout +is up; sending the held datagram +%#-------------------------------------------------------------------------------------------------------------- +%# the defect itself: nothing sourced from the home address may be discarded +%not-contains: stdout +Using HoA instead of CoA... dropping datagram +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: grep "undisposed object:" test.out > test_undisposed.out || true +%not-contains: test_undisposed.out +undisposed object: ( +%#--------------------------------------------------------------------------------------------------------------