Summary
Mipv6::createBUTimer() implements the RFC 6275 Section 11.8 rule that selects the initial
retransmission timer for a Binding Update (BU), but the branch that would select
InitialBindackTimeoutFirstReg is unreachable. A Mobile Node's (MN) first home registration
therefore always starts its exponential backoff at INITIAL_BINDACK_TIMEOUT (1 s) instead of
InitialBindackTimeoutFirstReg (1.5 s by RFC default).
What the standard requires
RFC 6275 Section 11.8, Retransmissions and Rate Limiting:
o If the mobile node is sending a Binding Update and does not have
an existing binding at the home agent, it SHOULD use
InitialBindackTimeoutFirstReg (see Section 13) as a value for the
initial retransmission timer. This long retransmission interval
will allow the home agent to complete the Duplicate Address
Detection procedure mandated in this case, as detailed in
Section 11.7.1.
o Otherwise, the mobile node should use the specified value of
INITIAL_BINDACK_TIMEOUT for the initial retransmission timer.
RFC 3775 Section 11.8 is identical, so this has applied for as long as the model has existed.
Where it goes wrong
src/inet/networklayer/mipv6/Mipv6.cc. The two-argument createBUTimer() overload calls
BindingUpdateList::fetch(), which is a lookup-or-create: it inserts a Binding Update List
(BUL) entry when none exists, purely so that it can set the entry's state field. It then calls
the four-argument overload, which tests whether a BUL entry exists:
void Mipv6::createBUTimer(const Ipv6Address& buDest, NetworkInterface *ie)
{
cancelTimerIfEntry(buDest, ie->getInterfaceId(), KEY_BUL_EXP);
BindingUpdateList::BindingUpdateListEntry *bulEntry = bul->fetch(buDest); // <-- creates the entry
ASSERT(bulEntry != nullptr);
if (bulEntry->state != BindingUpdateList::DEREGISTER)
bulEntry->state = BindingUpdateList::REGISTER;
if (buDest == ie->getProtocolData<Mipv6InterfaceData>()->getHomeAgentAddress())
createBUTimer(buDest, ie, (uint)par("maxHaBindingLifeTime").doubleValueInUnit("s"), true);
...
}
void Mipv6::createBUTimer(const Ipv6Address& buDest, NetworkInterface *ie, const uint lifeTime,
bool homeRegistration)
{
...
if (!bul->isInBindingUpdateList(buDest)) // <-- always false by now
buIfEntry->ackTimeout = ie->getProtocolData<Mipv6InterfaceData>()->_getInitialBindAckTimeoutFirst();
else
buIfEntry->ackTimeout = ie->getProtocolData<Mipv6InterfaceData>()->_getInitialBindAckTimeout();
...
}
fetch() populates the map three statements before the predicate reads it, so
_getInitialBindAckTimeoutFirst() is never called on any code path.
How to reproduce
Change MIPv6_INITIAL_BINDACK_TIMEOUT_FIRST in
src/inet/networklayer/ipv6/Mipv6InterfaceData.h to an obviously distinct value such as 7,
rebuild, and run:
cd examples/ipv6/mipv6
inet -u Cmdenv -c Handover --cmdenv-log-level=WARN '--**.mipv6.cmdenv-log-level=TRACE'
The logged backoff is unchanged:
21.750073506141 Sending periodic BU message at time: 21.750073506141 seconds.
21.750073506141 Present Sent Time: 21.750073506141, Present TimeOut: 2
21.750073506141 Next Sent Time: 22.750073506141
Present TimeOut: 2 is INITIAL_BINDACK_TIMEOUT (1 s) after the one doubling that
sendPeriodicBU() applies, i.e. the first-registration value was not used.
Two further problems with the predicate
Both surface only once the branch is reachable again, so they should be fixed in the same change.
-
It tests the wrong thing. The RFC condition is "does not have an existing binding at the
home agent". A Binding Update List (BUL) entry only records that a Binding Update (BU) was
attempted; it does not mean a binding exists. BindingUpdateList::hasActiveBinding() — an
acknowledged binding whose expiry still lies in the future — is the predicate the RFC describes.
-
It is not restricted to home registrations. The longer timer exists solely to cover the Home
Agent's (HA) Duplicate Address Detection (DAD), which only a home registration triggers. The test
is not gated on the homeRegistration argument, so once revived it would also stretch the initial
retransmission timer for Binding Updates (BUs) sent to Correspondent Nodes (CNs), where RFC 6275
Section 11.8 calls for INITIAL_BINDACK_TIMEOUT.
Observable effect
Together with the wrong default value tracked in #1133, this makes every first home registration
in examples/ipv6/mipv6 cost two Binding Updates (BUs) instead of one. The Home Agent (HA) models
Duplicate Address Detection (DAD) as a 1 s delay before the Binding Acknowledgement (BA)
(Mipv6.cc, sendTime = existingBinding ? 0 : 1), which matches DupAddrDetectTransmits ×
RetransTimer = 1 s. Measured in -c Handover:
| Event |
Time (s) |
| Binding Update (BU) #1 sent by Mobile Node (MN) |
21.750074 |
| Binding Update (BU) #1 received by Home Agent (HA) |
21.750737 |
| Mobile Node (MN) retransmission timer expires (+1.000 s) |
22.750074 |
| Binding Update (BU) #2 sent |
22.750074 |
| Binding Acknowledgement (BA) for Binding Update (BU) #1 arrives |
22.752424 |
| Binding Acknowledgement (BA) for Binding Update (BU) #2 arrives |
22.753396 |
The Binding Acknowledgement (BA) loses the race by 2.35 ms. The RFC's 1.5x factor exists precisely
to give that margin, and the model currently has none.
Summary
Mipv6::createBUTimer()implements the RFC 6275 Section 11.8 rule that selects the initialretransmission timer for a Binding Update (BU), but the branch that would select
InitialBindackTimeoutFirstRegis unreachable. A Mobile Node's (MN) first home registrationtherefore always starts its exponential backoff at
INITIAL_BINDACK_TIMEOUT(1 s) instead ofInitialBindackTimeoutFirstReg(1.5 s by RFC default).What the standard requires
RFC 6275 Section 11.8, Retransmissions and Rate Limiting:
RFC 3775 Section 11.8 is identical, so this has applied for as long as the model has existed.
Where it goes wrong
src/inet/networklayer/mipv6/Mipv6.cc. The two-argumentcreateBUTimer()overload callsBindingUpdateList::fetch(), which is a lookup-or-create: it inserts a Binding Update List(BUL) entry when none exists, purely so that it can set the entry's
statefield. It then callsthe four-argument overload, which tests whether a BUL entry exists:
fetch()populates the map three statements before the predicate reads it, so_getInitialBindAckTimeoutFirst()is never called on any code path.How to reproduce
Change
MIPv6_INITIAL_BINDACK_TIMEOUT_FIRSTinsrc/inet/networklayer/ipv6/Mipv6InterfaceData.hto an obviously distinct value such as7,rebuild, and run:
The logged backoff is unchanged:
Present TimeOut: 2isINITIAL_BINDACK_TIMEOUT(1 s) after the one doubling thatsendPeriodicBU()applies, i.e. the first-registration value was not used.Two further problems with the predicate
Both surface only once the branch is reachable again, so they should be fixed in the same change.
It tests the wrong thing. The RFC condition is "does not have an existing binding at the
home agent". A Binding Update List (BUL) entry only records that a Binding Update (BU) was
attempted; it does not mean a binding exists.
BindingUpdateList::hasActiveBinding()— anacknowledged binding whose expiry still lies in the future — is the predicate the RFC describes.
It is not restricted to home registrations. The longer timer exists solely to cover the Home
Agent's (HA) Duplicate Address Detection (DAD), which only a home registration triggers. The test
is not gated on the
homeRegistrationargument, so once revived it would also stretch the initialretransmission timer for Binding Updates (BUs) sent to Correspondent Nodes (CNs), where RFC 6275
Section 11.8 calls for
INITIAL_BINDACK_TIMEOUT.Observable effect
Together with the wrong default value tracked in #1133, this makes every first home registration
in
examples/ipv6/mipv6cost two Binding Updates (BUs) instead of one. The Home Agent (HA) modelsDuplicate Address Detection (DAD) as a 1 s delay before the Binding Acknowledgement (BA)
(
Mipv6.cc,sendTime = existingBinding ? 0 : 1), which matchesDupAddrDetectTransmits×RetransTimer= 1 s. Measured in-c Handover:The Binding Acknowledgement (BA) loses the race by 2.35 ms. The RFC's 1.5x factor exists precisely
to give that margin, and the model currently has none.