ipv6: join the solicited-node multicast group of every assigned address - #1191
Open
adamgeorge309 wants to merge 2 commits into
Open
ipv6: join the solicited-node multicast group of every assigned address#1191adamgeorge309 wants to merge 2 commits into
adamgeorge309 wants to merge 2 commits into
Conversation
The function picked the preferred source address by returning from the middle of its scan loop as soon as it met a routable address, and again at the end for the link-local fallback. Both exits also carried the F_IP_ADDRESS notification, so anything that has to run after the address list is settled had to be written twice. The scan now records the routable candidate and breaks, and the choice and the notification happen once, after it. Behaviour is unchanged: the first routable unicast address in sort order still wins, the first link-local one is still the fallback, and an empty address list still returns early without notifying.
An IPv6 interface never joined the solicited-node multicast group of any
address it held. Membership was derived at reception time instead:
Ipv6RoutingTable::isLocalAddress() recomputed the solicited-node address of
every address of every interface and compared it with the destination. That
makes a Duplicate Address Detection answer arrive, but nothing is ever
announced, so a node probed an address while declaring no membership in the
group that carries the answer, and with Multicast Listener Discovery enabled
no report for a ff02::1:ffXX:XXXX group appeared on the wire at all.
RFC 4861 Section 7.2.1 requires the node to join the solicited-node multicast
address corresponding to each address assigned to the interface, and to join
and leave as addresses are added and removed. RFC 4862 Section 5.4.2 requires
the join before the first probe of a tentative address, and says what skipping
it costs: with an MLD-snooping switch "no multicast reception will be
available until the MLD report is sent".
Ipv6InterfaceData joins in assignAddress(), the one place the address list
grows, and leaves in all three places it shrinks: both removeAddress()
overloads and the valid-lifetime sweep inside choosePreferredAddress(), which
is the quiet one and the one stateless autoconfiguration reaches when a prefix
lifetime elapses. The sweep collects the addresses it drops and leaves their
groups only after the list is sorted and preferredAddr is chosen, because
leaving hands control to a Multicast Listener Discovery module that builds and
sends a Done message.
Several unicast addresses map to one solicited-node address -- under stateless
autoconfiguration a link-local and a global address formed from the same
interface identifier always do -- and RFC 4861 Section 7.2.1 forbids leaving
before the last of them is gone. changeMulticastGroupMembership() already
counts joins in numOfExcludeModeSockets, so one join per address and one leave
per address gives that for free. The leave is guarded on actual membership
rather than on the interface flags, so a leave that never had a matching join
cannot drive that count below zero.
The loopback address is excluded: it is not a link address, and its
solicited-node address would collide with that of every unicast address ending
in the same 24 bits.
Reception does not change. The derived path in
matchesSolicitedNodeMulticastAddress() is kept, so isLocalAddress() accepts a
solicited-node datagram exactly as before. Router-side state does grow: a
router with multicast forwarding now records a listener entry per announced
solicited-node group. PIM ignores them, because a scope-2 group is neither
routable nor source-specific.
Fingerprints move only where Multicast Listener Discovery is enabled, because
that is the only place a join becomes a packet. Under MLDv1 --
examples/ipv6/mld MldDemo and examples/pim/{dm,sm}_ipv6 -- each first join of
a group adds an unsolicited Multicast Listener Report. Under MLDv2 --
examples/ipv6/mld MldV2Ssm and examples/pim/ssm_ipv6 -- no packet is added at
all; the group becomes one more multicast address record inside reports that
already existed, so those reports grow. The whole suite gives three failures
before and after, the five configurations above being the only ones whose
recorded values move.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
An IPv6 interface now joins the solicited-node multicast group of every address assigned to
it, and leaves that group when the address goes away. Until now INET never joined such a
group at all: membership was recomputed from the address list at reception time, so a node
sent a Duplicate Address Detection probe while declaring no membership in the group that
carries the answer, and with Multicast Listener Discovery enabled no report for a
ff02::1:ffXX:XXXXgroup ever appeared on the wire.Fixes #1190.
Why
RFC 4861 Section 7.2.1 makes the join mandatory:
RFC 4862 Section 5.4.2 requires it before the first probe of a tentative address, and says
what skipping it costs: with an MLD-snooping switch "no multicast reception will be
available until the MLD report is sent".
How it works
The address list shrinks in three places, and the fix covers all three:
removeAddress(const Ipv6Address&), theremoveAddress(AddressType)overload that MobileIPv6 uses for a care-of address, and the lifetime-expiry sweep inside
choosePreferredAddress()— the quiet one, and the one stateless autoconfiguration reacheswhen a prefix lifetime elapses.
Ipv6InterfaceDatajoins inassignAddress(), the singleplace the list grows.
Several unicast addresses map to one solicited-node address; under stateless
autoconfiguration a link-local and a global address formed from the same interface
identifier always do.
changeMulticastGroupMembership()already counts joins innumOfExcludeModeSockets, so one join per address and one leave per address satisfies RFC4861 Section 7.2.1's "MUST NOT leave [...] until all assigned addresses [...] have been
removed" without new bookkeeping. The leave is guarded on actual membership rather than on
the interface flags, which are read once at join time and again at leave time: a leave with
no matching join would otherwise drive that count below zero and leave the interface a
permanent member of a group it never joined.
The loopback address is excluded. It is not a link address, and its solicited-node address
would collide with that of every unicast address ending in the same 24 bits.
Reading order
Two commits, in order.
ipv6: refactor: give choosePreferredAddress() a single exit— behaviour-preserving. Thefunction returned from the middle of its scan loop, which left nowhere to put work that
has to run after the address list is settled. Verified by the whole fingerprint suite:
three failures and 62 errors, identical to the parent commit.
ipv6: fix: join the solicited-node group of each assigned address— the fix, with thetests and baselines that travel with it (PR-SPLIT-BASELINE).
Read the source of the second alone with
git show <sha> -- . ':!tests'.What it does not change
Reception.
Ipv6RoutingTable::isLocalAddress()keeps its derived path throughmatchesSolicitedNodeMulticastAddress(), which accepted a solicited-node datagram beforeand still does; the new membership only makes a second disjunct in
Ipv6.cc:654true aswell. Making the joined groups the single source of truth for reception would change
behaviour for any address assigned on a path that cannot join, so it is a separate decision.
Router-side state does grow. A router with multicast forwarding now records a listener entry
per announced solicited-node group, visible through
getNumOfReportedMulticastGroups(). PIMbuilds nothing from them:
PimSmfilters on scope and source-specificity, andPimDm::multicastReceiverAdded()is reached but cannot create a route for a scope-2 group.RFC 4862 Section 5.4.2's SHOULD to delay the join by a random interval between 0 and
MAX_RTR_SOLICITATION_DELAYis not implemented; the join is immediate. That SHOULD isconditional — it applies when the probe is the first message after interface
(re)initialization, or when the address came from a multicast Router Advertisement — and the
same section requires the interface to receive and process datagrams for the group during
the delay regardless, which it does. INET's existing
uniform(0, IPv6_MAX_RTR_SOLICITATION_DELAY)atIpv6NeighbourDiscovery.cc:851is addedto the Duplicate Address Detection timeout, the wait for an answer, rather than to either
the probe or the join; that is #1179.
Architectural surface
Ipv6InterfaceDatagains four protected helpers —joinSolicitedNodeMulticastGroup(),leaveSolicitedNodeMulticastGroup(),leaveExpiredSolicitedNodeMulticastGroups()and the shared predicateisSolicitedNodeGroupOwner(). No public API change, no NED parameter, no packet format,no feature descriptor.
(
Ipv6NetworkLayer.hasMld), because that is the only place a join becomes a packet.doc/project/audit/seal-list.mdseals onlycommon/packet/.AV-*orNV-*exception rows.Baselines and tests that move
tests/fingerprint/mipv6-refactoring.csv—examples/ipv6/mldMldDemotests/fingerprint/mipv6-refactoring.csv—examples/ipv6/mldMldV2SsmMldv2MulticastAddressRecordinside reports that already existed, so those reports growtests/fingerprint/examples.csv—examples/pim/dm_ipv6,sm_ipv6Generaltests/fingerprint/examples.csv—examples/pim/ssm_ipv6Generaltests/module/MLDv2_host_ssm.test<dump what="groups"/>lines now also listff02::1:ff00:1tests/module/MLDv2_host_query.testff02::1:ff00:1=IS_EXbesideff3e::1No configuration without Multicast Listener Discovery moved.
tests/module/IPv6_solicited_node_group_join.testis new. It asserts that the hostannounces
ff02::1:ff00:2, the solicited-node group of its link-local address; that thereport precedes the host's own first Duplicate Address Detection probe (RFC 4862 Section
5.4.2); and that its two addresses — the link-local one and the global one formed from the
same interface identifier — run Duplicate Address Detection twice but produce exactly one
report for the group they share, which is RFC 4861 Section 7.2.1's "multiple unicast
addresses may map into the same solicited-node multicast address". It fails on the parent
commit and passes on this one.
The leave side is not covered by a test: no shipped configuration removes an IPv6 address
while another still maps to the same group. The join-side collapse above exercises the same
reference count from the other direction, and
MLDv2_host_ssm.testshows it directly: thetester's interface holds two addresses and dumps a single
ff02::1:ff00:1 Eentry.Tests
Run in a worktree at
origin/master(8ac5675c9c), release mode.Fingerprints — whole suite, both before and after:
origin/masterThe three failures throughout are
examples/ipv6/mipv6HandoverandRouteOptimizationTwoCNsandexamples/ipv6/mipv6roamingRoaming, whose recorded~tNlbvalues are stale on master; they are unchanged here and deliberately not absorbed. Commit 1
moves nothing, which is what makes it a refactor. The five that appear in the third column
are exactly the configurations in the table above, and they are the five whose values this
pull request regenerates. The 62 errors are disabled optional features (
VoIPStream,TcpLwip) and never move.Module tests:
57 TOTAL, 55 PASS, 2 FAIL— the two areMIPv6_tcp_handover.testandIPv6_packet_too_big.test, which fail on pristine master in release mode and pass in debug;they are environmental and pre-existing. The count is one higher than on master because of
the new test.
Protocol tests:
29 TOTAL, 21 PASS, 8 FAIL (expected)— identical to pristine master, no unexpected failure.Gates:
check-naming.sh src/inet/networklayer/ipv6/reports three hits, all pre-existing and infiles this change does not touch (
ipv6/headers,Ipv6ExtHeaderTag.msg,Ipv6ExtensionHeaders.msg).