Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions WHATSNEW
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,10 @@ Notable bug fixes are the following:

The HT mode factory now includes band and preamble format in its cache key. A 5 GHz request after an equivalent 2.4 GHz request therefore receives the requested band and timing. Previously, that request could receive the earlier mode. Identical complete requests still reuse one object.

2. Address resolution sends no Neighbor Solicitation from a tentative address

Address resolution sent its Neighbor Solicitation from a tentative address, for example under Optimistic Duplicate Address Detection, although RFC 4861 Section 7.2.2 takes the source from the assigned addresses, RFC 4862 does not count a tentative address as assigned, and RFC 4429 forbids an Optimistic address as the source of a Neighbor Solicitation. The solicitation now uses an address that is not tentative, keeping the previous choice when that address is assigned; if the interface has none yet, address resolution starts when Duplicate Address Detection of an address completes. If Duplicate Address Detection finds the link-local address duplicate before any other address is assigned, datagrams to unresolved neighbours now stay queued instead of being returned with an ICMPv6 error. Simulations in which a node sends IPv6 traffic to an unresolved neighbour while every address of the interface is tentative produce different results, for example examples/ipv6/mipv6, examples/ipv6/mipv6roaming and examples/inet/hierarchical99.


INET-4.7 (July 2026) — feature release
--------------------------------------
Expand Down
61 changes: 52 additions & 9 deletions src/inet/networklayer/icmpv6/Ipv6NeighbourDiscovery.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,27 @@ simsignal_t Ipv6NeighbourDiscovery::startDadSignal = registerSignal("startDad");
simsignal_t Ipv6NeighbourDiscovery::dadCompletedSignal = registerSignal("dadCompleted");
simsignal_t Ipv6NeighbourDiscovery::dadFailedSignal = registerSignal("dadFailed");

// RFC 4861 Section 7.2.2: the source of an address-resolution NS is the source of the
// prompting packet if it is assigned to the interface, otherwise any assigned address.
// A tentative address is not assigned (RFC 4862 Section 5.4), and an Optimistic address
// MUST NOT be the source of an NS (RFC 4429 Section 3.2). Returns the unspecified
// address if the interface has no assigned address.
static Ipv6Address chooseNsSourceAddress(const Ipv6InterfaceData *ipv6Data, const Ipv6Address& dgSrcAddr)
{
if (ipv6Data->hasAddress(dgSrcAddr) && !ipv6Data->isTentativeAddress(dgSrcAddr))
return dgSrcAddr;
const Ipv6Address& preferredAddr = ipv6Data->getPreferredAddress();
if (!preferredAddr.isUnspecified() && !ipv6Data->isTentativeAddress(preferredAddr))
return preferredAddr;
const Ipv6Address& linkLocalAddr = ipv6Data->getLinkLocalAddress();
if (!linkLocalAddr.isUnspecified() && !ipv6Data->isTentativeAddress(linkLocalAddr))
return linkLocalAddr;
for (int i = 0; i < ipv6Data->getNumAddresses(); i++)
if (!ipv6Data->isTentativeAddress(i) && ipv6Data->getAddress(i).isUnicast())
return ipv6Data->getAddress(i);
return Ipv6Address::UNSPECIFIED_ADDRESS;
}

Ipv6NeighbourDiscovery::Ipv6NeighbourDiscovery()
: neighbourCache(*this)
{
Expand Down Expand Up @@ -670,15 +691,16 @@ void Ipv6NeighbourDiscovery::initiateAddressResolution(const Ipv6Address& dgSrcA
Ipv6Address nsSrcAddr;

/*If the source address of the packet prompting the solicitation is the
same as one of the addresses assigned to the outgoing interface,*/
if (ie->getProtocolData<Ipv6InterfaceData>()->hasAddress(dgSrcAddr))
/*that address SHOULD be placed in the IP Source Address of the outgoing
solicitation.*/
nsSrcAddr = dgSrcAddr;
else
/*Otherwise, any one of the addresses assigned to the interface
should be used.*/
nsSrcAddr = ie->getProtocolData<Ipv6InterfaceData>()->getPreferredAddress();
same as one of the addresses assigned to the outgoing interface, that
address SHOULD be placed in the IP Source Address of the outgoing
solicitation. Otherwise, any one of the addresses assigned to the
interface should be used.*/
nsSrcAddr = chooseNsSourceAddress(ie->getProtocolData<Ipv6InterfaceData>(), dgSrcAddr);
if (nsSrcAddr.isUnspecified()) {
// makeTentativeAddressPermanent() starts address resolution when an address is assigned
EV_INFO << "No address assigned to the interface yet, address resolution waits for DAD\n";
return;
}
ASSERT(ifID != -1);
// Sending NS on specified interface.
createAndSendNsPacket(nsTargetAddr, nsDestAddr, nsSrcAddr, ie);
Expand Down Expand Up @@ -706,6 +728,19 @@ void Ipv6NeighbourDiscovery::processArTimeout(cMessage *arTimeoutMsg)
EV_INFO << "Max Multicast Solicitation:" << ie->getProtocolData<Ipv6InterfaceData>()->_getMaxMulticastSolicit() << endl;

if (nce->numOfARNSSent < ie->getProtocolData<Ipv6InterfaceData>()->_getMaxMulticastSolicit()) {
// the source chosen for the first NS may have become tentative or been removed since
const auto *ipv6Data = ie->getProtocolData<Ipv6InterfaceData>();
if (!ipv6Data->hasAddress(nce->nsSrcAddr) || ipv6Data->isTentativeAddress(nce->nsSrcAddr)) {
Ipv6Address dgSrcAddr = nce->pendingPackets.empty() ? Ipv6Address::UNSPECIFIED_ADDRESS : nce->pendingPackets.front()->peekAtFront<Ipv6Header>()->getSrcAddress();
nce->nsSrcAddr = chooseNsSourceAddress(ipv6Data, dgSrcAddr);
if (nce->nsSrcAddr.isUnspecified()) {
// makeTentativeAddressPermanent() restarts address resolution
EV_INFO << "No address assigned to the interface any more, address resolution waits for DAD\n";
nce->arTimer = nullptr;
delete arTimeoutMsg;
return;
}
}
EV_INFO << "Sending another Address Resolution NS message" << endl;
Ipv6Address nsDestAddr = nsTargetAddr.formSolicitedNodeMulticastAddress();
createAndSendNsPacket(nsTargetAddr, nsDestAddr, nce->nsSrcAddr, ie);
Expand Down Expand Up @@ -896,6 +931,14 @@ void Ipv6NeighbourDiscovery::makeTentativeAddressPermanent(const Ipv6Address& te

ie->getProtocolDataForUpdate<Ipv6InterfaceData>()->setDadInProgress(false);

// start the address resolution that waited for an assigned address (see initiateAddressResolution())
for (auto& elem : neighbourCache) {
Neighbour& nce = elem.second;
if (elem.first.interfaceID == ie->getInterfaceId() && nce.reachabilityState == Ipv6NeighbourCache::INCOMPLETE
&& nce.arTimer == nullptr && !nce.pendingPackets.empty())
initiateAddressResolution(nce.pendingPackets.front()->peekAtFront<Ipv6Header>()->getSrcAddress(), &nce);
}

if (!tentativeAddr.isLinkLocal()) {
// DAD completed for a global address -- nothing else to do
return;
Expand Down
6 changes: 3 additions & 3 deletions tests/fingerprint/examples.csv
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@

# /examples/inet/hierarchical99/, -f .qtenv.ini -c General -r 0
/examples/inet/hierarchical99/, -f networklayer.ini -c IPv4 -r 0, 10000s, a94d-edae/tplx;1f32-e325/~tNl;b138-ee8b/~tND;66c9-cd73/tyf, PASS, EthernetMac Ipv4
/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, 0001-0108/tplx;3960-479e/~tNl;add1-e848/~tND;2c03-abef/tyf, PASS, EthernetMac
/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, acf6-4224/tplx;af7d-b669/~tNl;710c-34d1/~tND;d96c-23d2/tyf, PASS, EthernetMac
/examples/inet/hierarchical99/, -f networklayer.ini -c Generic -r 0, 10000s, 6ae4-6e75/tplx;2209-80ac/~tNl;93bf-6657/tyf, PASS, EthernetMac
/examples/inet/hierarchical99/, -f networklayer.ini -c Flooding -r 0, 10000s, 7ba7-5b91/tplx;b0f0-411e/~tNl;2b94-8906/tyf, PASS, EthernetMac
/examples/inet/hierarchical99/, -f networklayer.ini -c ProbabilisticBroadcast -r 0, 10000s, 5a54-9779/tplx;1165-a70b/~tNl;760b-abfc/tyf, PASS, EthernetMac
Expand Down Expand Up @@ -380,9 +380,9 @@
/examples/manetrouting/multiradio/, -f omnetpp.ini -c MultiRadio -r 0, 20s, 55c4-44c4/tplx;92b9-416a/~tNl, PASS, wireless adhoc Ipv4
/examples/manetrouting/multiradio/, -f omnetpp.ini -c SingleRadio -r 0, 20s, 5ed8-cf6b/tplx;b9f1-61b0/~tNl;3aa0-49ed/tyf, PASS, wireless adhoc Ipv4

/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, 68ea-9693/tplx;a44c-17bf/~tNl;96ff-ed7c/~tND;44ef-1a45/tyf, PASS, wireless EthernetMac
/examples/ipv6/mipv6/, -f omnetpp.ini -c Handover -r 0, 70s, c073-8d42/tplx;a457-16df/~tNl;3d28-8302/~tND;7491-bcad/tyf, PASS, wireless EthernetMac
/examples/ipv6/mipv6/, -f omnetpp.ini -c RouteOptimizationTwoCNs -r 0, 60s, bacf-986a/tplx;e6db-28c0/~tNl;805b-8a25/~tND;ed3e-17fa/tyf, PASS, wireless EthernetMac
/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, b275-b584/tplx;28ce-974e/~tNl;dcec-ae59/~tND;afae-2b3c/tyf, PASS, wireless EthernetMac
/examples/ipv6/mipv6roaming/, -f omnetpp.ini -c Roaming -r 0, 70s, 8ea9-9430/tplx;9273-d4eb/~tNl;6c79-6458/~tND;f3a3-116d/tyf, PASS, wireless EthernetMac
/examples/ipv6/pmipv6/, -f omnetpp.ini -c General -r 0, 60s, e801-4d1a/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,
Expand Down
2 changes: 1 addition & 1 deletion tests/fingerprint/mipv6-refactoring.csv
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
/examples/ipv6/nclients/, -f omnetpp.ini -c PPP_SCTP -r 0, 100s, 3a7c-924c/~tNlb, PASS,

# IPv6 networking examples
/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, f206-a0e2/~tNlb, PASS, EthernetMac
/examples/inet/hierarchical99/, -f networklayer.ini -c IPv6 -r 0, 10000s, 082b-e145/~tNlb, PASS, EthernetMac
/examples/inet/udpclientserver/, -f omnetpp.ini -c udp_OK_ipv6 -r 0, 10s, 8feb-3936/~tNlb, PASS,
/examples/inet/udpclientserver/, -f omnetpp.ini -c udp_Port_Unav_ipv6 -r 0, 10s, 9661-c1f5/~tNlb, PASS,
/examples/inet/udpclientserver/, -f omnetpp.ini -c udp_Host_Unav_ipv6 -r 0, 10s, 6360-9ed1/~tNlb, PASS,
Expand Down
71 changes: 71 additions & 0 deletions tests/module/IPv6_ND_no_solicitation_from_tentative_address.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
%description:
A host sends no address-resolution Neighbor Solicitation from a tentative address: RFC 4861
Section 7.2.2 takes the source from the addresses assigned to the interface, RFC 4862
Section 5.4 does not count a tentative address as assigned, and RFC 4429 Section 3.2 forbids
an Optimistic address as the source of a Neighbor Solicitation. With no address assigned
yet, address resolution waits for Duplicate Address Detection (DAD) to complete.

Topology: router -- host

The host uses Optimistic DAD and pings the router's link-local address from t=0, so its only
address, the link-local one, is tentative when the first ping needs address resolution. Only
the host's Neighbour Discovery and network-layer dispatcher log. No Neighbor Solicitation
from the host's link-local address to the router's solicited-node address may come before
the host's "DAD completed" line, and one must come after it. The packet printer colours the
fields, so the patterns allow other characters between them.

%#--------------------------------------------------------------------------------------------------------------
%file: test.ned

import inet.networklayer.configurator.ipv6.Ipv6FlatNetworkConfigurator;
import inet.node.ipv6.Router6;
import inet.node.ipv6.StandardHost6;
import ned.DatarateChannel;

network TentativeSolicitationTest
{
types:
channel ethline extends DatarateChannel
{
delay = 0.1us;
datarate = 10Mbps;
}
submodules:
configurator: Ipv6FlatNetworkConfigurator;
router: Router6;
host: StandardHost6;
connections:
router.ethg++ <--> ethline <--> host.ethg++;
}

%#--------------------------------------------------------------------------------------------------------------
%inifile: omnetpp.ini

[General]
network = TentativeSolicitationTest
ned-path = .;../../../../src
sim-time-limit = 3s
record-vector-results = false
cmdenv-express-mode = false

**.neighbourDiscovery.optimisticDad = true
**.host.numApps = 1
**.host.app[0].typename = "PingApp"
**.host.app[0].destAddr = "fe80::8aa:ff:fe00:1"
**.host.app[0].startTime = 0s
**.host.app[0].sendInterval = 0.2s

**.host.ipv6.neighbourDiscovery.cmdenv-log-level = info
**.host.nl.cmdenv-log-level = trace
**.cmdenv-log-level = off

%#--------------------------------------------------------------------------------------------------------------
%contains-regex: stdout
DAD completed for address fe80::8aa:ff:fe00:2 on eth0, address is unique[\s\S]*NeighbourSolicitation[^,]*, [^,]*sourceAddress[^,]*= fe80::8aa:ff:fe00:2, [^,]*destinationAddress[^,]*= ff02::1:ff00:1,
%#--------------------------------------------------------------------------------------------------------------
%not-contains-regex: stdout
NeighbourSolicitation[^,]*, [^,]*sourceAddress[^,]*= fe80::8aa:ff:fe00:2, [^,]*destinationAddress[^,]*= ff02::1:ff00:1,[\s\S]*DAD completed for address fe80::8aa:ff:fe00:2
%#--------------------------------------------------------------------------------------------------------------
%not-contains: stdout
<!> Error
%#--------------------------------------------------------------------------------------------------------------
Loading