pmipv6: refuse a proxy binding update older than the accepted one - #1243
adamgeorge309 wants to merge 1 commit into
Conversation
A Proxy Mobile IPv6 (PMIPv6, RFC 5213) local mobility anchor (LMA) must be able to tell which of two Proxy Binding Updates (PBU) for one mobile node was sent later. RFC 5213 Section 5.5 names the consequence of not telling: a message from the gateway the node was previously anchored at, "delivered out of order, resulting in incorrectly updating the mobile node's Binding Cache entry". Section 9.3 makes the timestamp scheme of Section 5.5 the default (TimestampBasedApproachInUse = 1), with the sequence-number scheme as the alternative. Pmipv6 applied neither. The mobile access gateway (MAG) wrote a zero Timestamp option, with the comment "ordered by sequence number in this model", and the LMA stored the sequence number without comparing it. The LMA accepted every registration in arrival order. Now the MAG writes its clock into the Timestamp option in the RFC 5213 Section 8.8 layout (48 bits of seconds, 16 bits of 1/65536 s). The clock is simulation time, counted from 0 rather than from the 1970 epoch that Section 8.8 names: the ordering needs one clock that all nodes share, and simulation time is such a clock. The LMA keeps the timestamp of the last accepted PBU in the binding cache entry and refuses a registration whose timestamp is not greater: an older one with TIMESTAMP_LOWER_THAN_PREV_ACCEPTED (Section 5.5, rule 8), an equal one with TIMESTAMP_MISMATCH (rules 6 and 9). An acceptance echoes the timestamp, a rejection carries the LMA's clock (rules 7 to 9), and a rejection carries lifetime 0: RFC 6275 Section 6.1.8 leaves the lifetime of a rejection undefined, and 0 grants nothing. Deregistrations are not compared by timestamp: they keep the existing rule that only the serving MAG may deregister a node (Section 5.3.5), which already refuses a deregistration from the MAG a node has left. The sequence-number scheme, which Section 9.3 selects with TimestampBasedApproachInUse = 0, is not implemented, and no parameter selects it: Section 5.5 allows it only when a MAG can learn the last sequence number another MAG sent, and each MAG here keeps its own counter. The check against the LMA's clock and TimestampValidityWindow is not implemented either, because all nodes read the same simulation clock. To reproduce, copy the new module test into a master checkout and run it: cd tests/module inet_run_module_tests -m release -f PMIPv6_pbu_ordering The mobile node attaches to mag1 at 0.82 s and to mag2 at 3.49 s, and mag2's PBU reaches the LMA at once. mag1's backhaul link has a 1.4 s delay: mag1's PBU waits one address resolution round trip (2.8 s) and then travels 1.4 s, so it reaches the LMA at 5.02 s. On master the LMA logs "LMA handover: re-pointing prefix 2001:db8:1::/64 toward MAG 2001:db8:0:1::2" at 5.02 s, and the correspondent node's PingApp reports "sent: 33 received: 3". With this commit the LMA logs "LMA rejecting Proxy Binding Update for MN 'mn1@example.com' from MAG 2001:db8:0:1::2: its timestamp is older than the last one accepted" and PingApp reports "sent: 33 received: 32". examples/ipv6/pmipv6 behaves as before: the same 31117 events, 105 echo requests sent and 102 answered, and identical scalars. Its fingerprint that hashes packet bytes (~tND) moves from bd46-d85c to bdf6-52b5, because the Timestamp option of each PBU and Proxy Binding Acknowledgement is no longer zero. The fingerprint of event times, module paths, message lengths and module-supplied extra data (tplx) and the one of event times, node paths and message lengths of packets between nodes (~tNl) do not move. Change: src.pmipv6.Pmipv6 | behavior.change.fix | fingerprint test whatsnew
There was a problem hiding this comment.
Devin Review found 3 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| else if (it != bindingCache.end() | ||
| && pbu->getTimestampValue() <= it->second.timestamp) |
There was a problem hiding this comment.
🔴 Delayed deregistration erases a newer binding
When processProxyBindingUpdate receives a delayed deregistration after a newer registration from the same MAG, it erases the newer binding. The lifetime-zero branch bypasses the timestamp comparison but still matches the serving MAG. Downlink traffic loses its route.
Learn more
The LMA accepts a registration by saving its timestamp in the binding cache. A deregistration enters the earlier lifetime-zero branch, which tests only whether its source address equals the serving MAG. If that deregistration was delayed behind a newer registration from the same MAG, it deletes the new route and entry. The new timestamp check does not protect this ordering even though both messages carry timestamps.
Example: MAG A sends a deregistration at 10 s, then registers the same node again at 11 s. If the 11 s PBU arrives first, the LMA accepts it. When the delayed 10 s deregistration arrives, the LMA removes the 11 s binding instead of preserving it.
Recommended fix: Apply timestamp ordering before both deregistration and registration handling in processProxyBindingUpdate. Preserve the accepted timestamp through the lifetime-zero transition as necessary, and add a test that reverses delivery order for deregistration and re-registration from one MAG.
Was this helpful? React with 👍 or 👎 to provide feedback.
| else if (it != bindingCache.end() | ||
| && pbu->getTimestampValue() <= it->second.timestamp) |
There was a problem hiding this comment.
🔴 Delayed update revives a deregistered binding
When an old registration arrives after deregistration, bindingCache.find returns no entry and accepts the old timestamp. Deregistration erased the only saved timestamp. The LMA routes the departed node's traffic back to the old MAG.
Learn more
The LMA stores the most recent accepted timestamp only inside a binding cache entry. The lifetime-zero path in processProxyBindingUpdate erases that entry. When an earlier registration arrives afterward, the new check sees no entry, accepts the outdated PBU, and installs a downlink route despite the node having detached.
Example: A registration sent by MAG A at 5 s waits on a slow link. MAG B registers the node at 6 s and deregisters it at 7 s. When A's 5 s registration reaches the LMA at 8 s, it revives a binding to A instead of being rejected as older than the last accepted update.
Recommended fix: Retain the last accepted timestamp for a mobile node after removing its route, for at least the period when delayed PBUs can arrive. Compare future registrations against this history, and test an old PBU arriving after successful deregistration.
Was this helpful? React with 👍 or 👎 to provide feedback.
| uint64_t fraction = (uint64_t)((seconds - (double)wholeSeconds) * 65536.0); | ||
| return (wholeSeconds << 16) | (fraction & 0xFFFF); |
There was a problem hiding this comment.
🟡 Sub-tick handovers lose their registration
When two MAGs send PBUs within one 1/65536-second tick, timestampOf gives both updates the same value. The LMA rejects the later update as equal; the new MAG neither installs a route nor retries.
Learn more
The wire-format timestamp has 16 fractional bits, so this conversion discards finer simulation-time differences. The LMA's strict comparison in processProxyBindingUpdate rejects equal values, while processProxyBindingAcknowledgement simply returns on rejection. Two distinct attachment events can therefore leave the later MAG without a binding even though it sent the later update.
Example: A PBU sent at 10.000001 s and another sent at 10.000002 s both encode as 655360. Once the LMA accepts the first, it rejects the second with TIMESTAMP_MISMATCH; the later attachment gets no registration.
Recommended fix: Define handling for distinct PBUs within one timestamp tick, such as a retry with a strictly later timestamp and bounded backoff. Cover a handover occurring within one tick in a focused module test.
Was this helpful? React with 👍 or 👎 to provide feedback.
The Proxy Mobile IPv6 (PMIPv6, RFC 5213) local mobility anchor (LMA) now refuses a Proxy Binding Update (PBU) whose timestamp is older than that of the last one it accepted for the mobile node, as RFC 5213 Section 5.5 requires. Before, it accepted PBUs in arrival order: a PBU from the mobile access gateway (MAG) the node had left, delayed on that MAG's backhaul, re-pointed the node's home network prefix back to that MAG.
Closes #1242
The problem
src/inet/networklayer/pmipv6/Pmipv6.ccon master::376— the MAG writespbu->setTimestampValue(0); // ordered by sequence number in this model;:233— the LMA stores the sequence number in the binding cache entry, and nothing reads it back;:208–:236— the registration branch re-points the prefix for any PBU, whatever its age.RFC 5213 Section 5.5 names the consequence of an older message from the previous MAG "delivered out of order, resulting in incorrectly updating the mobile node's Binding Cache entry and creating a routing state for tunneling the mobile node's traffic to the previous mobile access gateway." Section 9.3 makes the timestamp scheme the default (
TimestampBasedApproachInUse= 1).tests/module/PMIPv6_pbu_ordering.test: the mobile node attaches to mag1 at 0.82 s and to mag2 at 3.49 s. mag1's backhaul link has a 1.4 s delay: its PBU waits one address resolution round trip (2.8 s) and then travels 1.4 s, so it reaches the LMA at 5.02 s, after mag2's. The correspondent node pings the mobile node every 0.5 s from 4 s.LMA handover: re-pointing prefix 2001:db8:1::/64 toward MAG 2001:db8:0:1::2TIMESTAMP_LOWER_THAN_PREV_ACCEPTEDThe fix
TIMESTAMP_LOWER_THAN_PREV_ACCEPTED(Section 5.5, rule 8); an equal one fails the "greater than" test of rule 6 and is refused withTIMESTAMP_MISMATCH(rule 9). Both status codes were declared inMobilityHeader.msgand not used.Architectural surface
TIMESTAMP_LOWER_THAN_PREV_ACCEPTEDorTIMESTAMP_MISMATCH, lifetime 0 and the LMA's clock. No message field or length changes; one comment inMobilityHeader.msgchanges.Pmipv6::BindingCacheEntrygains the fieldtimestamp. No public or virtual function changes.Pmipv6.neddocumentation comment gains one paragraph on the ordering.Verification
Each suite was run on unmodified master (
49e1fa0945) and with this commit, on a release build (make MODE=releasein the root, then intests/module/libandtests/serializer/lib); the test commands pass--no-build.tests/module: inet_run_module_tests -m release --no-build -l ERROR -f MIPv6(matchesPMIPv6_*too)MIPv6_tcp_handoverfailsPMIPv6_pbu_orderingpasses,MIPv6_tcp_handoverfailstests/fingerprint: ./fingerprinttest -s -F tyf~tNDupdatetests/serializer: inet_run_serializer_tests -m release --no-build -l ERRORserializer_chunk_roundtripfails with 60 failed casesinet_run_statistical_tests -m release -w examples/ipv6/pmipv6(statistics repository9ab4c26a45)doc/project/enforcement/check-commits.sh origin/master..HEAD,check-classification.sh origin/master..HEAD,check-source-seals.sh --base origin/masterdoc/project/enforcement/check-naming.sh --base origin/master.nedand.msgfiles have no naming candidatesdoc/project/enforcement/check-architecture.sh,check-interfaces.shThe commit was also built in debug mode, with
make MODE=debug -j4at the repository root and intests/module/lib, exit 0, with no compiler warning in the changed files. On that buildcd tests/module && inet_run_module_tests -m debug --no-build -l ERROR -f 'MIPv6|PMIPv6'passes 12 of 12, exit 0: the newPMIPv6_pbu_orderingand the elevenMIPv6_*tests,MIPv6_tcp_handoverincluded.The master row of the first table comes from the network,
config.xml,movement.xmlandomnetpp.iniofPMIPv6_pbu_ordering.test, extracted into a directory and run against the master library withopp_run -l <master>/src/INET -n .:<master>/src -u Cmdenv. The 62 fingerprint errors are configurations whose optional features are not built (VoipStreamSender,TcpLwip,Z3GateScheduleConfigurator, the OpenSceneGraph visualizer showcases).MIPv6_tcp_handoverandserializer_chunk_roundtripfail identically on unmodified master, so they are pre-existing.examples/ipv6/pmipv6behaves as before: 31117 events, 105 echo requests sent and 102 answered, identical scalars. Its fingerprint that hashes packet bytes (~tND) moves frombd46-d85ctobdf6-52b5because the Timestamp option is no longer zero. The fingerprint of event times, module paths, message lengths and module-supplied extra data (tplx) and the one of event times, node paths and message lengths of packets between nodes (~tNl) do not move. The one that hashes display strings and canvas figures (tyf) fails on unmodified master (actual05f8-fee3, recorded0277-d784), gives the same actual value with this commit, and is left as it is.Not addressed here
TimestampBasedApproachInUse= 0. It is allowed only when a MAG can learn the last sequence number another MAG sent for the node (rule 3 of the sequence-number approach), and each MAG here keeps its own counter.TimestampValidityWindow. It detects clock skew between machines; all nodes read one simulation clock, soTIMESTAMP_MISMATCHis sent only for an equal timestamp.TIMESTAMP_MISMATCH. That is the resolution of the Section 8.8 format.