Repository navigation
ipv6: hold a datagram with a tentative source address, whoever chose it - #1297
adamgeorge309 wants to merge 1 commit into
Conversation
Ipv6::fragmentPostRouting() holds a datagram whose source address is still tentative, that is, still under Duplicate Address Detection (DAD), in pendingDadQueue until DAD completes. The check sat inside the branch that fills in an unspecified source address, so it ran only when Ipv6 chose the source itself. A datagram whose sender had set the source address left with the tentative address. Icmpv6 is such a sender: the reply to an Echo Request sent to a multicast address takes its source from getPreferredAddress() (Icmpv6.cc:252-258). During link-local DAD that is the only, tentative, address; during global DAD choosePreferredAddress() prefers the tentative global address. RFC (Request for Comments) 4862 (Internet Protocol version 6 (IPv6) Stateless Address Autoconfiguration) Section 5.4: "A tentative address is not considered 'assigned to an interface' in the traditional sense", and a node that uses an address in parallel with DAD "will erroneously process traffic intended for the other node". No RFC 4862 sentence forbids a tentative source outright. The same check, with the same exception for Optimistic DAD (RFC 4429), now also runs on a source address that the sender set. It is in Ipv6 rather than in each sender: it then covers every sender, and during link-local DAD a node has no other source address to choose. Neighbor Discovery messages are left out: RFC 4861 gives each of them its own source address rules, Ipv6NeighbourDiscovery chooses the source itself, and holding them would send a router's Router Advertisements from its DAD period all at once when DAD completes. An interface without Ipv6InterfaceData, such as a tunnel interface that Mobile IPv6 creates at run time, has no tentative addresses and is not checked. makeTentativeAddressPermanent() emits dadCompletedSignal (Ipv6NeighbourDiscovery.cc:890) before its loop makes the other addresses of the interface permanent (919-925), so a held datagram whose source only that loop makes permanent stays held until the next DAD completes; the shipped Mobile IPv6 examples do not reach that case. tests/module/IPv6_tentative_source_echo_reply.test reproduces it: a router pings ff02::1 every 0.2 s while a host runs DAD. Before this commit the host's Echo Replies reach the router with a tentative source from t=2.1061 s (link-local, DAD complete at t=2.1506 s) and from t=3.2004 s (global, DAD complete at t=4.5349 s). With it they arrive from t=2.1507 s and from t=4.5350 s. Change: src.networklayer.ipv6.Ipv6 | behavior.change.fix | test whatsnew
There was a problem hiding this comment.
Devin Review found 2 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| auto ipv6Data = ie->findProtocolData<Ipv6InterfaceData>(); | ||
| if (ipv6Data != nullptr && ipv6Data->isTentativeAddress(ipv6Header->getSrcAddress()) && !ipv6Data->isOptimisticDad()) { |
There was a problem hiding this comment.
🟡 Cross-interface tentative source escapes DAD
When a sender selects a tentative source on another interface, isTentativeAddress checks only the outgoing interface. The datagram leaves before DAD completes on the source interface.
Learn more
An explicit source address is accepted when it belongs to any interface on the node, not necessarily the egress interface. handleMessageFromHL checks the address through the routing table, while routePacket chooses the output independently. The new check inspects only the output interface's address list. If another interface owns the tentative source, that lookup is false and the packet goes onto the wire before DAD completes.
Example: A host has a tentative source address on eth0 and a route to the destination through eth1. An application binds to eth0's address. The output check sees no such address on eth1 and sends the packet during eth0's DAD.
Recommended fix: Resolve the explicitly supplied source to its owning interface and check its DAD state there, while retaining the outgoing interface and next-hop decision for transmission. Verify how the DAD completion and failure callbacks match that owning interface before queuing cross-interface packets.
Was this helpful? React with 👍 or 👎 to provide feedback.
| if (ipv6Data != nullptr && ipv6Data->isTentativeAddress(ipv6Header->getSrcAddress()) && !ipv6Data->isOptimisticDad()) { | ||
| EV_INFO << "Source address is tentative - enqueueing datagram for later resubmission." << endl; | ||
| ScheduledDatagram *sDgram = new ScheduledDatagram(packet, ipv6Header.get(), ie, nextHopAddr, fromHL); | ||
| take(sDgram); | ||
| pendingDadQueue.push_back(sDgram); | ||
| return; |
There was a problem hiding this comment.
🟡 Handover datagrams remain queued indefinitely
During a handover, pendingDadQueue holds explicit sources other than the link-local address. DAD completion signals before clearing their tentative flags, leaving those datagrams queued without another signal.
Learn more
On handover, processRaPrefixInfoForAddrAutoConf marks every existing interface address tentative and starts link-local DAD. This branch can now enqueue a packet with an explicitly chosen global source. makeTentativeAddressPermanent emits dadCompletedSignal just after clearing the link-local flag, before clearing the other addresses' flags. The queue listener leaves those packets queued. Subsequent flag changes emit no DAD completion, and removal of the old care-of address emits no DAD failure, so affected packets stay queued until shutdown.
Example: During handover, a sender uses an existing global source while its flag is tentative. The packet enters the DAD queue. Link-local DAD ends, the queue listener skips it, then handover clears or removes the global address without notifying the queue; the packet never leaves.
Recommended fix: Coordinate the handover address changes and queue notification: complete or remove all affected addresses before notifying the IPv6 queue, and explicitly drop queued packets for addresses removed during handover. Add a test where an explicit global-source packet is queued during handover and verify its eventual disposition.
Was this helpful? React with 👍 or 👎 to provide feedback.
Ipv6held a datagram whose source address was tentative, that is, still under Duplicate Address Detection (DAD), only whenIpv6had chosen the source address itself. A datagram whose sender set the source address, such as an ICMPv6 (Internet Control Message Protocol for Internet Protocol version 6) Echo Reply to a multicast Echo Request, left with the tentative address. RFC (Request for Comments) 4862 Section 5.4 does not count a tentative address as assigned, and the new module test shows such replies on master. The same check now applies to a source address that the sender set, except in Neighbor Discovery messages, whose sourcesIpv6NeighbourDiscoverychooses under RFC 4861 (Neighbor Discovery for IP version 6). Results change only where a node sends such a datagram during DAD; no fingerprint moves. Risk:makeTentativeAddressPermanent()signals the end of DAD before it makes the other addresses of the interface permanent, so a datagram held for one of those addresses stays held until the next DAD (see Not addressed here); the shipped Mobile IPv6 examples do not reach that case.Closes #1295
The problem
The check in
Ipv6::fragmentPostRouting()(src/inet/networklayer/ipv6/Ipv6.cc:1019-1029on master) sat inside the branch that fills in an unspecified source address. RFC 4862 (IPv6 Stateless Address Autoconfiguration) Section 5.4 has no sentence that forbids a tentative source outright; the rule follows from the address not being assigned.tests/module/IPv6_tentative_source_echo_reply.test: a router pings ff02::1 every 0.2 s from t=0 while a host runs DAD.The fix
The check keeps its exception for Optimistic DAD (RFC 4429). It runs in
Ipv6rather than in each sender because it then covers every sender, and because during link-local DAD a node has no other source address to choose. Two cases are left out:Ipv6InterfaceData, such as a tunnel interface that Mobile IPv6 creates at run time. It has no tentative addresses.A held datagram is released as before, by the
dadCompletedSignalhandler inIpv6::receiveSignal(), and dropped by thedadFailedSignalhandler if its source address is removed. A datagram whose sourceIpv6fills in is handled exactly as on master.Architectural surface
Behavior of
Ipv6: when a locally originated datagram with an explicit source address leaves during DAD. A file-local helperisNeighbourDiscoveryMessage()inIpv6.cc; no change to theIpv6class. No NED (Network Description) parameter, packet format or feature descriptor changes.Verification
Release build (
make MODE=release):IPv6_tentative_source_echo_replyfails on master (the host logs no "Source address is tentative") and passes on this branch. The existingIPv6_traffic_during_DAD,IPv6_DAD_successandIPv6_DAD_failurepass on both. The same holds in a debug build (make MODE=debug,inet_run_module_tests -m debug ...).Full suites, release, on master 93210f4 and on this branch:
examples/ipv6/nclientsETHandPPPinmipv6-refactoring.csv; the errors are rows whose optional features are disabled in this build. No fingerprint moves.tcp_*,ConvolutionalCoder12/34,EtherHost_lifecycle,ExternalProcess_3,Ieee80211BitDomain/SymbolDomain,Ieee8021d-Rstp/Stp,IPv6_packet_too_big,MIPv6_tcp_handover,PacketGate_1,UDPSocket_1).tests/protocol/ipv626 of 27 pass on both (Rfc8200OverlappingFragmentsfails on both);tests/protocol/nd22 of 37 pass on both, with the same 15 failures.doc/project/enforcement/check-commits.sh,check-classification.shandcheck-architecture.sh src/inet/networklayer/ipv6: PASS. Statistical tests andcheck-cpp.shwere not run.Not addressed here
Ipv6NeighbourDiscovery.cc:674-677). RFC 4861 Section 7.2.2 allows that only for an address "assigned to the outgoing interface", and a tentative address is not assigned; otherwise "any one of the addresses assigned to the interface should be used". On this branch, too, each Echo Reply in the test first goes to address resolution, whose solicitation reaches the router with the tentative source at t=2.1048 s and at t=3.2002 s; only then is the reply itself held. A router also sends Router Advertisements while its link-local address is tentative. Both belong toIpv6NeighbourDiscovery, which should choose another source or wait.Ipv6NeighbourDiscovery::makeTentativeAddressPermanent()emitsdadCompletedSignalbefore its loop makes the other addresses of the interface permanent (Ipv6NeighbourDiscovery.cc:890and919-925). A datagram whose source only that loop makes permanent stays held until the next DAD on that node completes, or to the end of the simulation. A mobile node meets this at a handover, where all its addresses are marked tentative. This branch does not add such datagrams in the shipped Mobile IPv6 examples: a datagram that a mobile node sources from its home address there is either dropped first by the existing home-address check inIpv6::resolveMACAddressAndSendPacket()or sent through a tunnel interface, which is not checked, and their fingerprints do not move.Landing order
fragmentPostRouting().git merge-treemergesIpv6.cccleanly: ipv6: take a global source when the outgoing interface is link-local only #1256 changes the lines that pick the source, this branch the lines after them. With both, a source that ipv6: take a global source when the outgoing interface is link-local only #1256 borrows from another interface is not an address of the outgoing interface and is not held, and ipv6: take a global source when the outgoing interface is link-local only #1256 already skips tentative candidates.git merge-treeagainst ipv6: purge the destination cache when a route is removed or changed or an interface is deleted #1228, ipv6: repeat the Hop-by-Hop and Routing headers in every fragment #1232, ipv6: a tunnel exit point survives a hook that drops the inner datagram #1252, ipv6: take a global source when the outgoing interface is link-local only #1256 and networklayer: carry ECN through an IPv6-in-IPv6 tunnel #1264 conflicts otherwise only inWHATSNEW, where each adds an item with the same number. The pull request that lands later renumbers its item.