diff --git a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc index 9785d3f5522..6e7b55dca8a 100644 --- a/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc +++ b/src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc @@ -897,7 +897,18 @@ void Ipv6NeighbourDiscovery::makeTentativeAddressPermanent(const Ipv6Address& te ie->getProtocolDataForUpdate()->setDadInProgress(false); if (!tentativeAddr.isLinkLocal()) { - // DAD completed for a global address -- nothing else to do + // DAD completed for a global address. When it is the care-of address formed at a + // handover, the mobile node may now register it with its home agent: until this + // point its uniqueness on the visited link was unverified (RFC 4862 Section 5.4). + auto git = dadGlobalList.find(ie->getInterfaceId()); + if (git != dadGlobalList.end() && git->second.addr == tentativeAddr) { + [[maybe_unused]] bool homeNetwork = git->second.hFlag; + dadGlobalList.erase(git); +#ifdef INET_WITH_MIPV6 + if (rt6->isMobileNode() && !homeNetwork) // if we are not in the home network, send BUs + mipv6->initiateMipv6Protocol(ie, tentativeAddr); +#endif + } return; } @@ -908,7 +919,21 @@ void Ipv6NeighbourDiscovery::makeTentativeAddressPermanent(const Ipv6Address& te if (it != dadGlobalList.end()) { DadGlobalEntry& entry = it->second; - ie->getProtocolDataForUpdate()->assignAddress(entry.addr, false, simTime() + entry.validLifetime, + // Clear the tentative flag on the addresses that were already on the interface when + // the Router Advertisement arrived: the link-local address whose DAD just completed, + // and the home address. This has to happen before the address formed from the new + // prefix is added below, because that address has not been verified yet. + for (int i = 0; i < ie->getProtocolData()->getNumAddresses(); i++) { + Ipv6Address addr = ie->getProtocolData()->getAddress(i); + ie->getProtocolDataForUpdate()->permanentlyAssign(addr); + } + + // RFC 4862 Section 5.4 requires DAD on every unicast address, so the care-of address + // formed from the new prefix is assigned as tentative; the loop at the end of this + // function starts DAD for it. The MIPv6 registration is not started here but when + // that DAD completes (see above), so no Binding Update can advertise an address + // whose uniqueness on the visited link is still unverified. + ie->getProtocolDataForUpdate()->assignAddress(entry.addr, true, simTime() + entry.validLifetime, simTime() + entry.preferredLifetime, entry.hFlag); // moved from processRAPrefixInfoForAddrAutoConf() @@ -916,24 +941,9 @@ void Ipv6NeighbourDiscovery::makeTentativeAddressPermanent(const Ipv6Address& te if (!entry.CoA.isUnspecified()) ie->getProtocolDataForUpdate()->removeAddress(entry.CoA); - // set addresses on this interface to tentative=false - for (int i = 0; i < ie->getProtocolData()->getNumAddresses(); i++) { - // TODO improve this code so that only addresses are permanently assigned - // which are formed based on the new prefix from the RA - Ipv6Address addr = ie->getProtocolData()->getAddress(i); - ie->getProtocolDataForUpdate()->permanentlyAssign(addr); - } - -#ifdef INET_WITH_MIPV6 - // if we have MIPv6 protocols on this node we will eventually have to - // call some appropriate methods - if (rt6->isMobileNode()) { - if (entry.hFlag == false) // if we are not in the home network, send BUs - mipv6->initiateMipv6Protocol(ie, tentativeAddr); - } -#endif - - dadGlobalList.erase(it->first); + // The MIPv6 protocol is initiated once the care-of address assigned above has + // passed DAD; the dadGlobalList entry is kept until then. dadHasFailed() drops + // it, so a care-of address that is already in use is never registered. } // Assign global scope addresses to routers. The Ipv6FlatNetworkConfigurator assigns diff --git a/tests/fingerprint/examples.csv b/tests/fingerprint/examples.csv index 283915ab865..06da0dbb820 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, 19b8-20a4/tplx;b378-071d/~tNl;d358-e3bb/~tND;44ef-1a45/tyf, PASS, wireless EthernetMac -/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 3f0c-078d/tplx;04e1-1f03/~tNl;4199-2b15/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac -/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 0798-40c2/tplx;a682-8d0e/~tNl;cdb7-1b34/~tND;afae-2b3c/tyf, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, 6204-6190/tplx;68f4-68c6/~tNl;76af-99d1/~tND;44ef-1a45/tyf, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 9faf-b8a8/tplx;f076-4454/~tNl;582f-fb5e/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac +/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 4ab1-dea8/tplx;9d55-36d4/~tNl;1f90-d7bf/~tND;afae-2b3c/tyf, PASS, wireless EthernetMac /examples/ipv6/pmipv6/, -f omnetpp.ini -c General -r 0, 60s, f614-da0d/tplx;8b55-7191/~tNl;b490-dc09/~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 e7a648648c0..321cdf1f869 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, aa29-a8c5/~tNlb, PASS, wireless EthernetMac -/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, 068b-23aa/~tNlb, PASS, wireless EthernetMac -/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, c8dc-27c2/~tNlb, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, dfed-b54a/~tNlb, PASS, wireless EthernetMac +/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, ab29-ae3f/~tNlb, PASS, wireless EthernetMac +/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 2b0b-fa1a/~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_care_of_address_DAD.test b/tests/module/MIPv6_care_of_address_DAD.test new file mode 100644 index 00000000000..3b2d6f6edd9 --- /dev/null +++ b/tests/module/MIPv6_care_of_address_DAD.test @@ -0,0 +1,134 @@ +%description: +Tests that a care-of address formed at a handover undergoes Duplicate Address +Detection (DAD) before it is used, as required by RFC 4862 Section 5.4, and that +the Mobile Node does not register it with its Home Agent until that DAD passes. + +The Mobile Node boots on its home link (where it has only a link-local address, +so the home address is verified through the single-address path), then moves to +the foreign network. On arrival its interface already holds a link-local address +and the home address, so address autoconfiguration takes the multi-address path. +Before this test was added, that path assigned the care-of address as permanent +and started no DAD for it, and the Binding Update advertised an address whose +uniqueness on the visited link had never been tested. + +%#-------------------------------------------------------------------------------------------------------------- +%inifile: omnetpp.ini +[General] +record-vector-results = false +**.neighbourDiscovery.cmdenv-log-level = trace +**.mipv6.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.Mipv6Network +sim-time-limit = 60s +cmdenv-express-mode = false +cmdenv-log-prefix = "%C: " +num-rngs = 3 +seed-set = 1 +**.mobility.rng-0 = 2 + +# number of MNs and CNs +*.total_mn = 1 +*.total_cn = 1 + +**.neighbourDiscovery.minIntervalBetweenRAs = 0.03s +**.neighbourDiscovery.maxIntervalBetweenRAs = 0.07s +**.neighbourDiscovery.detectL2Movement = false # legacy: detect movement from periodic RAs only + +# channel physical parameters +*.radioMedium.mediumLimitCache.maxTransmissionPower = 2.0mW +*.radioMedium.mediumLimitCache.minReceptionPower = -82dBm +*.radioMedium.mediumLimitCache.minInterferencePower = -82dBm + +# access point +**.wlan*.mgmt.numAuthSteps = 4 + +# ALL APs common parameters +**.AP*.wlan*.mgmt.beaconInterval = 0.1s + +# Access Point parameters +**.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 + +# mobility +**.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 = 630m +**.MN[0].mobility.constraintAreaMaxY = 180m +**.MN[0].mobility.startPos = 0 +**.MN[0].mobility.speed = 10mps +**.MN*.mobility.updateInterval = 0.1s + +# No apps needed - we just test the handover signaling +**.MN*.numApps = 0 +**.CN*.numApps = 0 + +# ip settings +**.forwarding = false + +# Ethernet NIC configuration +**.eth[*].queue.typename = "EthernetQosQueue" +**.eth[*].queue.dataQueue.typename = "DropTailQueue" +**.eth[*].queue.dataQueue.packetCapacity = 10 +**.eth*.duplexMode = true + +# analog model +**.analogModel.ignorePartialInterference = true + +# wireless channels +**.AP_Home.wlan*.radio.channelNumber = 1 +**.AP_1.wlan*.radio.channelNumber = 2 +**.MN*.wlan*.radio.channelNumber = 0 + +# wireless configuration +**.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 + +# nic settings +**.wlan*.bitrate = 2Mbps +**.wlan*.mac.dcf.channelAccess.cwMin = 7 +**.radio.transmitter.power = 2.0mW + +**.constraintAreaMinX = 0m +**.constraintAreaMinY = 0m +**.constraintAreaMaxX = 850m +**.constraintAreaMaxY = 850m + +%#-------------------------------------------------------------------------------------------------------------- +%subst: /omnetpp::// +%#-------------------------------------------------------------------------------------------------------------- +%contains: stdout +Mipv6Network.MN[0].ipv6.neighbourDiscovery: Starting DAD for tentative global address aaaa:1:66:0:8aa:ff:fe00:8 +%#-------------------------------------------------------------------------------------------------------------- +%contains: stdout +Mipv6Network.MN[0].ipv6.neighbourDiscovery: DAD completed for address aaaa:1:66:0:8aa:ff:fe00:8 on wlan0, address is unique +%#-------------------------------------------------------------------------------------------------------------- +%contains: stdout +Mipv6Network.MN[0].ipv6.mipv6: Initiating Mobile Ipv6 protocol... +%#-------------------------------------------------------------------------------------------------------------- +%contains: stdout +Mipv6Network.Home_Agent.ipv6.mipv6: BU validation passed +%#-------------------------------------------------------------------------------------------------------------- +%postrun-command: grep "undisposed object:" test.out > test_undisposed.out || true +%not-contains: test_undisposed.out +undisposed object: ( +%#--------------------------------------------------------------------------------------------------------------