Skip to content

mipv6: intercept packets for an absent mobile node on its home link - #1268

Open
adamgeorge309 wants to merge 3 commits into
masterfrom
topic/gy/mipv6-home-agent-proxy-nd
Open

adamgeorge309 wants to merge 3 commits into
masterfrom
topic/gy/mipv6-home-agent-proxy-nd

Conversation

@adamgeorge309

@adamgeorge309 adamgeorge309 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

A Mobile IPv6 (MIPv6) home agent now acts as a Neighbor Discovery proxy for an absent mobile node's home address, as RFC 6275 Section 10.4.1 requires, so hosts on the home link reach the mobile node while it is away. INET had no proxy Neighbor Discovery, so the fix needs a new service in Ipv6NeighbourDiscovery before the home agent can use it. That service is a new feature.

Closes #1267

The problem

Ipv6NeighbourDiscovery::processNsPacket() discards a Neighbor Solicitation whose target is not an address of the receiving interface, and the home agent is not a member of the home address's solicited-node multicast group. Off-link traffic reaches the mobile node only because the home agent is also the home link's router. A host on the home link resolves the home address with Neighbor Discovery, gets no answer, and cannot send at all.

examples/ipv6/mipv6 Handover with one StandardHost6 added on the home link, pinging the home address while the mobile node is away (t=25 s to 39 s, run 0; the files are in the issue):

CN[0] (off-link) replies home-link host replies
master 4eb3bb4 116 of 139 0 of 28
this branch 115 of 139 28 of 28

On master the home-link host's address resolution fails at t=28, 31, 34, 37 and 40 s. The one-reply difference for CN[0] is the timing shift described under Verification.

The commits

  1. ipv6: refactor: extracts Ipv6RoutingTable::findOnLinkInterface() from isOnLinkAddress(), which found the interface and discarded it. Behavior-preserving.
  2. icmpv6: add: adds Ipv6NeighbourDiscovery::addProxyAddress() and removeProxyAddress(), proxy service in the sense of RFC 4861 Section 7.2.4. No module calls addProxyAddress() in this commit, and no fingerprint moves.
  3. mipv6: fix: starts the proxy service when the home agent accepts a home registration, multicasts one Neighbor Advertisement (Override set, Router cleared) for a new binding, and stops the service when the binding is de-registered or expires.

Architectural surface

  • New public C++ methods: virtual Ipv6NeighbourDiscovery::addProxyAddress() / removeProxyAddress() and non-virtual Ipv6RoutingTable::findOnLinkInterface(); protected virtual isProxyAddress() / isProxyTargetRouter(). The virtuals follow the class, whose other members are virtual too. All additions; nothing existing changes signature.
  • Packet content: a home agent sends Neighbor Advertisements it did not send before. For a proxied target only: the Override flag of a solicited advertisement is 0, the Router flag of any advertisement describes the proxied node (0 for a mobile node) instead of the sender, and the IPv6 source of an unsolicited advertisement is the interface's preferred address instead of the target. Advertisements for the node's own addresses are unchanged.
  • Log output: new info-level lines when proxy service starts and stops and for each unsolicited Neighbor Advertisement (proxied or not). The new module test matches their text.
  • No NED parameter, message definition or feature descriptor changes. WHATSNEW gains compatible item 6 (commit 2) and incompatible item 17 (commit 3).

Relationship to open pull requests

Proposed landing order: #1245 and #1195 first, then this branch rebased as described. #1247 is independent.

Verification

The new module test tests/module/MIPv6_home_agent_proxy_nd.test puts two hosts on the home link of Mipv6Network in tests/module/lib, a copy of the example network kept for the module tests. It counts ping replies for requests sent while the mobile node is away and registered (EarlyHost from t=36 s, LateHost from its start at t=40 s, both to t=75.5 s), and asserts the multicast advertisement and the withdrawal on return.

EarlyHost (cached the mobile node's address) LateHost (empty cache)
master 0 of 80, test FAILS 0 of 72, "Address Resolution has failed"
this branch 80 of 80, PASS 72 of 72, PASS

Commands, run on master 4eb3bb4 and on this branch unless marked; the builds exit 0:

make -j4 MODE=release
make -j4 MODE=debug                                                    # this branch only
cd tests/fingerprint && ./fingerprinttest -s -F tyf
cd tests/fingerprint && ./fingerprinttest -s -m mipv6 examples.csv mipv6-refactoring.csv
cd tests/module && inet_run_module_tests -m release --no-build -l ERROR
cd tests/module && inet_run_module_tests -m debug --no-build -l ERROR -f MIPv6   # this branch only
cd tests/protocol/ipv6 && inet_run_protocol_tests -m release
cd tests/protocol/nd && inet_run_protocol_tests -m release
doc/project/enforcement/check-architecture.sh src/inet/networklayer
doc/project/enforcement/check-architecture.sh
doc/project/enforcement/check-naming.sh --base origin/master
doc/project/enforcement/check-interfaces.sh
doc/project/enforcement/check-source-seals.sh --base origin/master     # this branch only
doc/project/enforcement/check-commits.sh origin/master..HEAD           # this branch only
doc/project/enforcement/check-classification.sh origin/master..HEAD    # this branch only

Results compared by name against the same commands on unmodified master 4eb3bb4:

  • Fingerprints without the graphical ingredient (-F tyf): master 1711 pass, 0 failures, 63 errors (disabled optional features and the ethernet-nonstandardspeed row). This branch: the same 63 errors, and the 3 rows below move; after re-recording, the -m mipv6 run passes them.
  • Module tests, release: master 346 tests, 46 failures. This branch: the same 46 by name, plus the new test passing.
  • Module tests, debug, -f MIPv6: 12 of 12 pass on this branch, the new test included.
  • tests/protocol/ipv6: 26 of 27 on both (Rfc8200OverlappingFragments fails on master). tests/protocol/nd: 22 of 37 on both, the same 15 failures.
  • check-architecture.sh src/inet/networklayer: PASS (exit 0) on both, identical output.
  • Unscoped check-architecture.sh, check-naming.sh and check-interfaces.sh exit 1 on master and on this branch with byte-identical output; none of their hits is in a file this branch touches. check-source-seals.sh: passed (exit 0), no sealed file touched. check-commits.sh and check-classification.sh: PASS (exit 0).
  • At commit 2 the full fingerprint (-F tyf) and release module suites give master's results by name. Commits 1 and 2 build, and the -m mipv6 run gives master's values at each.

Fingerprint rows re-recorded in commit 3: examples/ipv6/mipv6 Handover and RouteOptimizationTwoCNs and examples/ipv6/mipv6roaming Roaming, in examples.csv (packet-level ingredients tplx, ~tNl, ~tND and the graphical ingredient tyf) and mipv6-refactoring.csv (~tNlb). With only the sendUnsolicitedNa() call removed, every packet-level ingredient of the three rows returns to its recorded value, so the multicast advertisement is the whole cause. The home access point bridges it onto the wireless medium, which shifts IEEE 802.11 timing and everything after it.

The tyf values of the three rows were already stale on master: the -m mipv6 run measures 04da-4143, 2f8e-79f9 and a8a6-63b4 there against the recorded 44ef-1a45, ed3e-17fa and afae-2b3c. With the sendUnsolicitedNa() call removed, tyf returns to exactly these master values too, so commit 3 moves tyf from them to 1ccb-c62a, 38b5-3e5d and 0da4-da9b, recorded from the same run on this branch. tyf is not maintained on master (the suites run with -F tyf), so the stale values get no commit of their own; they are re-recorded with the rest of each cell. The Proxy Mobile IPv6 example row does not move, so its stale tyf (05f8-fee3 against 0277-d784 on master) is left as it is.

The module test checks the proxy service end to end. No test asserts its individual field values (the Router flag, the cleared Override flag of a solicited advertisement, the source address of an unsolicited one), the answer to a Duplicate Address Detection probe, or the withdrawal in stop().

Not addressed here


Devin Review

isOnLinkAddress() scans the interfaces for one whose prefix advertisement
list covers the address, then discards which interface that was. The Mobile
IPv6 home agent needs that interface to intercept packets on the home link:
it joins a multicast group and sends a Neighbor Advertisement there. Return
the interface from findOnLinkInterface() and answer the boolean question
from it. The scan itself is unchanged.

Change: src.networklayer.ipv6.Ipv6RoutingTable | refactor | - | home-agent-proxy-nd
A node that answers Neighbor Solicitations for an address it does not hold
-- a proxy, in the sense of RFC 4861 Section 7.2.4 -- had no way to say so:
processNsPacket() discards every solicitation whose target is not assigned
to the receiving interface. A Mobile IPv6 home agent needs a proxy to
intercept packets for an absent mobile node (RFC 6275 Section 10.4.1).

For a proxied target, the existing unspecified-source path of
processNsPacket() also answers another node's Duplicate Address Detection
probe for the address.

Solicitations for the address go to its solicited-node multicast address. A
node that does not hold the address is not a member of that group, so
Ipv6::routeMulticastPacket() would discard them before Neighbor Discovery
sees them. addProxyAddress() therefore joins the group, and
removeProxyAddress() leaves the group that addProxyAddress() recorded, not
one the caller names: leaving a group that was never joined trips an
assertion in Ipv6InterfaceData, and the caller may no longer know which
interface it named.

Two advertisement fields depend on who owns the target:

- The Router flag describes the target, not the sender, so for a proxied
  address it follows what the caller says the proxied node is. A home agent
  is a router, but the mobile node it proxies for is a host.
- The Override flag of a solicited advertisement is cleared for a proxied
  target, as RFC 4861 Section 7.2.4 says it SHOULD be, so that the owner's
  own advertisement wins over the proxy's. The code quoted that sentence
  above a condition that did not implement it. An unsolicited advertisement
  keeps its set Override flag.

sendUnsolicitedNa() uses the target address as the IPv6 source address.
For a proxied target that is an address the node does not hold, so it now
sources those advertisements from the interface's preferred address, which
RFC 6275 Section 10.4.1 requires for the home agent's advertisement. Each
unsolicited advertisement is now logged at info level, so that a proxied
one can be told apart in the log; nothing logged it before.

When a node stops, Ipv6RoutingTable removes only its routes; the
interfaces keep their Ipv6InterfaceData and its multicast memberships. So
stop() withdraws the proxy records too: one that survived would leave the
restarted node answering for an address nothing stands behind.

No module calls addProxyAddress() yet, so no simulation changes: the full
fingerprint suite (without the graphical tyf ingredient) and the module
suite give the same results as before this commit. The first caller, the
Mobile IPv6 home agent, is tested with its own change.

That module test checks the service end to end: solicitations for the
proxied address are answered, and neighbors that cached another link-layer
address switch to the proxy's. No test asserts the individual field values
(the Router flag, the cleared Override flag of a solicited advertisement,
the source address of an unsolicited one), the answer to a Duplicate Address
Detection probe, or the withdrawal in stop().

Change: src.networklayer.icmpv6.Ipv6NeighbourDiscovery | behavior.add | test whatsnew | home-agent-proxy-nd
A host on the home link could not reach a mobile node that was away, although
the home agent one hop away held a valid binding for it. The host solicited
the home address, nobody answered, and its address resolution failed every
3 s. Only off-link traffic reached the mobile node, because it passes through
the home agent as the home link's router anyway. No shipped example puts a
host on the home link, which hid the defect.

RFC 6275 Section 10.4.1:

  While a node is serving as a home agent for some mobile node, the home
  agent uses IPv6 Neighbor Discovery [18] to intercept unicast packets on
  the home link addressed to the mobile node.  In order to intercept packets
  in this way, the home agent MUST act as a proxy for this mobile node and
  reply to any received Neighbor Solicitations for it.

The same section requires the home agent, when it begins serving, to
multicast a Neighbor Advertisement onto the home link on the mobile node's
behalf, with its own link-layer address, the Router flag cleared and the
Override flag set. Mipv6 did neither.

Register the home address for proxy service when a home registration is
accepted, next to the tunnel that carries the traffic already reaching the
home agent, and withdraw it when the binding is de-registered or expires.
Only a home registration: the standard keys the proxy check on a Binding
Cache entry marked as a home registration. The home link is the interface on
which the home address is on-link; a home agent that serves no prefix
covering the home address logs a warning and does not proxy.

The multicast advertisement goes out only for a binding that did not exist
before. A refreshed binding, or one moved to a new care-of address, changes
nothing on the home link. The advertisement's set Override flag replaces
the mobile node's link-layer address in the home link's neighbor caches at
once.

Reproduce with the new module test against the unfixed source (check out
this commit's parent's src/ with the test file in place, and rebuild):

  cd tests/module && inet_run_module_tests -m release -f MIPv6_home_agent_proxy_nd

It puts two hosts on the home link of tests/module/lib/Mipv6Network and
counts the ping replies for requests sent while the mobile node is away and
registered (from t=36 s or, for the later host, t=40 s, to t=75.5 s).
EarlyHost starts before the mobile node leaves and so caches its link-layer
address; LateHost starts afterwards with an empty cache. Unfixed: EarlyHost
0 of 80 replies, LateHost 0 of 72, and LateHost reports "Address Resolution
has failed". Fixed: 80 of 80 and 72 of 72. The test also asserts the
multicast advertisement and the withdrawal when the mobile node comes home.

The three Mobile IPv6 example fingerprint rows (Handover,
RouteOptimizationTwoCNs, Roaming) move on every packet-level ingredient
(tplx, ~tNl and ~tND in examples.csv, ~tNlb in mipv6-refactoring.csv). The
cause is the multicast advertisement, one per new binding: with only the
sendUnsolicitedNa() call removed, all four ingredients return to their
recorded values. The home access point bridges the advertisement onto the
wireless medium, so IEEE 802.11 timing moves and everything after it shifts.
The graphical tyf values of these rows were already stale on master: a run
there gives 04da-4143, 2f8e-79f9 and a8a6-63b4 against the recorded
44ef-1a45, ed3e-17fa and afae-2b3c. With the sendUnsolicitedNa() call
removed, tyf also returns to exactly these master values, so this commit
moves tyf from them to the new values. tyf is not maintained on master (the
suites run with -F tyf), so the stale values get no commit of their own. The
Proxy Mobile IPv6 example row does not move, so its stale tyf is left as it
is.

Not repaired here: RFC 6275 Section 10.3.1 also requires Duplicate Address
Detection for the home address before the Binding Acknowledgement (#1194), and
Section 10.4.1 requires proxying for the mobile node's link-local address
when the Binding Update sets the Link-Layer Address Compatibility flag.

Change: src.networklayer.mipv6.Mipv6 | behavior.change.fix | fingerprint test whatsnew | home-agent-proxy-nd

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)

Devin Review

Comment on lines +2843 to +2844
if (!existingBinding)
ipv6nd->sendUnsolicitedNa(homeLink, HoA);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Home-link takeover advertisement skipped after registration change

When a home registration replaces a non-home binding, existingBinding suppresses the takeover advertisement. The binding cache also stores non-home registrations. Neighbors retain the absent mobile node's link-layer address and cannot reach it until their caches recover.

Learn more

A home agent accepts a Binding Update without the Home Registration flag and stores it through addOrUpdateBC. existingBinding tests only whether that address exists in the cache, not whether its entry already represents a home registration. When a later home registration arrives for the same address, it creates the proxy but skips the advertisement that replaces the mobile node's old link-layer address in neighbors' caches.

Example: A home-link host caches mobile node M's MAC. The home agent first receives a non-home binding for M's address, then M registers that address as its home address while away. The proxy answers new solicitations, but the host continues sending to M's old MAC.

Recommended fix: Capture the previous entry's getHomeRegistration(HoA) before addOrUpdateBC, and announce when the prior entry was not a home registration. Preserve the existing refresh behavior for an already active home registration.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

return;
}

ipv6nd->addProxyAddress(homeLink, HoA);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Old home link keeps proxying moved address

When a home prefix moves between interfaces, startInterceptingForHomeAddress adds the new proxy without withdrawing the old one. The home agent keeps answering solicitations on the former home link, where its tunnel still intercepts traffic.

Learn more

Proxy registrations are keyed by interface and address in proxyAddresses. findOnLinkInterface can select a different interface after advertised prefixes are changed. A refreshed Binding Update calls addProxyAddress for the new interface, but the previous registration and multicast membership remain; the home agent answers Neighbor Solicitations on both links.

Example: The home agent initially advertises M's home prefix on eth1 and later moves it to eth2. A subsequent Binding Update adds proxying on eth2 while eth1 still answers solicitations for M's address.

Recommended fix: Before changing the home-link proxy, withdraw the previous interface's proxy registration for that address, or make the proxy update operation atomically move the registration to the selected interface. Retain the no-extra-advertisement behavior only when the existing proxy is already on the selected interface.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +2843 to +2844
if (!existingBinding)
ipv6nd->sendUnsolicitedNa(homeLink, HoA);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Takeover advertisement precedes home-address verification

For a new binding, startInterceptingForHomeAddress multicasts the override advertisement before the delayed acknowledgement and any address check. Neighbors can replace a resident node's MAC with the home agent's before it establishes that the address is free.

Learn more

A new home binding takes the existingBinding == false path. processBUMessage delays its Binding Acknowledgement by one second for the intended duplicate-address check, while the new proxy registration and unsolicited Neighbor Advertisement happen immediately. The advertisement sets Override, so a home-link neighbor with an existing cache entry can replace its entry before the address has been checked. The duplicate-address check is not implemented yet, but the new takeover action makes this pre-existing gap affect neighbors.

Example: A second node still owns the requested home address on the home link. The home agent announces its own MAC for that address immediately, and a neighbor replaces its existing MAC mapping before the delayed acknowledgement.

Recommended fix: Defer starting proxy service and sending the takeover advertisement until the new binding's home-link duplicate check has completed successfully. Keep the acceptance and failure paths consistent so a failed check does not leave a proxy or tunnel behind.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mipv6: the home agent does not proxy Neighbor Discovery for an absent mobile node, so hosts on the home link cannot reach it

1 participant