[routing-manager] initial PD support with platform generated RA (#9050)

This commit adds the following new API:
- otBorderRoutingProcessIcmp6Ra

And related APIs:
- otBorderRoutingDhcp6PdSetEnabled
- otBorderRoutingGetPdOmrPrefix

They are used to support the DHCPv6 PD on Thread Border Routers. On a
typical OS, the DHCPv6 Client of the platform network stack will
request a IPv6 subnet via DHCPv6-PD, then invoke the daemon for
sending router advertisements on the given interface.

With this commit, the RA will be catched by the processTransmit in
netif.cpp, which will then pass the message to
otBorderRoutingAddPrefixByRouterAdvertisement, RoutingManager will
then add the prefix according to the PIO in the router advertisement,
and take care of the lifetime of the prefix added.
This commit is contained in:
Song GUO
2023-06-07 22:52:39 -07:00
committed by GitHub
parent edc1a3fe23
commit 9cb667c01f
14 changed files with 789 additions and 26 deletions
+265 -12
View File
@@ -32,6 +32,7 @@
#include "test_util.hpp"
#include <openthread/thread.h>
#include <openthread/platform/border_routing.h>
#include "border_router/routing_manager.hpp"
#include "common/arg_macros.hpp"
@@ -466,6 +467,13 @@ void LogRouterAdvert(const Icmp6Packet &aPacket)
}
}
void LogRouterAdvert(const uint8_t *aBuffer, size_t aLength)
{
Icmp6Packet packet;
packet.Init(aBuffer, aLength);
LogRouterAdvert(packet);
}
const char *PreferenceToString(int8_t aPreference)
{
const char *str = "";
@@ -491,9 +499,14 @@ const char *PreferenceToString(int8_t aPreference)
return str;
}
void SendRouterAdvert(const Ip6::Address &aAddress, const uint8_t *aBuffer, uint16_t aLength)
{
otPlatInfraIfRecvIcmp6Nd(sInstance, kInfraIfIndex, &aAddress, aBuffer, aLength);
}
void SendRouterAdvert(const Ip6::Address &aAddress, const Icmp6Packet &aPacket)
{
otPlatInfraIfRecvIcmp6Nd(sInstance, kInfraIfIndex, &aAddress, aPacket.GetBytes(), aPacket.GetLength());
SendRouterAdvert(aAddress, aPacket.GetBytes(), aPacket.GetLength());
}
void SendNeighborAdvert(const Ip6::Address &aAddress, const Ip6::Nd::NeighborAdvertMessage &aNaMessage)
@@ -662,21 +675,22 @@ struct DefaultRoute
RoutePreference mPreference;
};
void SendRouterAdvert(const Ip6::Address &aRouterAddress,
const Pio *aPios,
uint16_t aNumPios,
const Rio *aRios,
uint16_t aNumRios,
const DefaultRoute &aDefaultRoute)
template <size_t N>
uint16_t BuildRouterAdvert(uint8_t (&aBuffer)[N],
const Pio *aPios,
uint16_t aNumPios,
const Rio *aRios,
uint16_t aNumRios,
const DefaultRoute &aDefaultRoute)
{
Ip6::Nd::RouterAdvertMessage::Header header;
uint8_t buffer[kMaxRaSize];
uint16_t length;
header.SetRouterLifetime(aDefaultRoute.mLifetime);
header.SetDefaultRouterPreference(aDefaultRoute.mPreference);
{
Ip6::Nd::RouterAdvertMessage raMsg(header, buffer);
Ip6::Nd::RouterAdvertMessage raMsg(header, aBuffer);
for (; aNumPios > 0; aPios++, aNumPios--)
{
@@ -689,10 +703,25 @@ void SendRouterAdvert(const Ip6::Address &aRouterAddress,
SuccessOrQuit(raMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
}
SendRouterAdvert(aRouterAddress, raMsg.GetAsPacket());
Log("Sending RA from router %s", aRouterAddress.ToString().AsCString());
LogRouterAdvert(raMsg.GetAsPacket());
length = raMsg.GetAsPacket().GetLength();
}
return length;
}
void SendRouterAdvert(const Ip6::Address &aRouterAddress,
const Pio *aPios,
uint16_t aNumPios,
const Rio *aRios,
uint16_t aNumRios,
const DefaultRoute &aDefaultRoute)
{
uint8_t buffer[kMaxRaSize];
uint16_t length = BuildRouterAdvert(buffer, aPios, aNumPios, aRios, aNumRios, aDefaultRoute);
SendRouterAdvert(aRouterAddress, buffer, length);
Log("Sending RA from router %s", aRouterAddress.ToString().AsCString());
LogRouterAdvert(buffer, length);
}
template <uint16_t kNumPios, uint16_t kNumRios>
@@ -725,6 +754,17 @@ void SendRouterAdvert(const Ip6::Address &aRouterAddress, const DefaultRoute &aD
SendRouterAdvert(aRouterAddress, nullptr, 0, nullptr, 0, aDefaultRoute);
}
template <uint16_t kNumPios> void SendRouterAdvertToBorderRoutingProcessIcmp6Ra(const Pio (&aPios)[kNumPios])
{
uint8_t buffer[kMaxRaSize];
uint16_t length =
BuildRouterAdvert(buffer, aPios, kNumPios, nullptr, 0, DefaultRoute(0, NetworkData::kRoutePreferenceMedium));
otPlatBorderRoutingProcessIcmp6Ra(sInstance, buffer, length);
Log("Passing RA to otPlatBorderRoutingProcessIcmp6Ra");
LogRouterAdvert(buffer, length);
}
struct OnLinkPrefix : public Pio
{
OnLinkPrefix(const Ip6::Prefix &aPrefix,
@@ -3118,6 +3158,216 @@ void TestNat64PrefixSelection(void)
}
#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
void VerifyPdOmrPrefix(const Ip6::Prefix &aPrefix)
{
otBorderRoutingPrefixTableEntry platformPrefixInfo;
VerifyOrQuit(otBorderRoutingGetPdOmrPrefix(sInstance, &platformPrefixInfo) == OT_ERROR_NONE);
VerifyOrQuit(AsCoreType(&platformPrefixInfo.mPrefix) == aPrefix);
}
void VerifyNoPdOmrPrefix()
{
otBorderRoutingPrefixTableEntry platformPrefixInfo;
VerifyOrQuit(otBorderRoutingGetPdOmrPrefix(sInstance, &platformPrefixInfo) == OT_ERROR_NOT_FOUND);
}
void TestBorderRoutingProcessPlatfromGeneratedNd(void)
{
Ip6::Prefix localOmr;
Log("--------------------------------------------------------------------------------------------");
Log("TestBorderRoutingProcessPlatfromGeneratedNd");
InitTest(/* aEnableBorderRouting */ true);
otBorderRoutingDhcp6PdSetEnabled(sInstance, true);
{
SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOmrPrefix(localOmr));
}
// 0. Reject invalid RA.
Log("0. Invalid RA message.");
{
{
const uint8_t testInvalidRaMessage[] = {
0x86, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
otPlatBorderRoutingProcessIcmp6Ra(sInstance, testInvalidRaMessage, sizeof(testInvalidRaMessage));
VerifyNoPdOmrPrefix();
}
{
const uint8_t testInvalidRaMessage[] = {
0x87, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
otPlatBorderRoutingProcessIcmp6Ra(sInstance, testInvalidRaMessage, sizeof(testInvalidRaMessage));
VerifyNoPdOmrPrefix();
}
{
const uint8_t testRaMessageWithInvalidPrefix[] = {
0x86, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x04, 0x41, 0xc0, 0x00, 0x00, 0x10, 0xe1, 0x00, 0x00, 0x04, 0xd2, 0x00, 0x00, 0x00, 0x00,
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
otPlatBorderRoutingProcessIcmp6Ra(sInstance, testRaMessageWithInvalidPrefix,
sizeof(testRaMessageWithInvalidPrefix));
VerifyNoPdOmrPrefix();
}
}
// 1. Publish a prefix, and wait until it expired.
Log("1. Simple RA message.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:dead:beef::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(400000);
// Deprecated prefixes will be removed.
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 1.1. Publish a prefix, and wait until it expired.
// Multiple prefixes are advertised, only the smallest one will be used.
Log("1.1. RA message with multiple prefixes.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:dead:beef::", 64);
Ip6::Prefix ulaRaPrefix = PrefixFromString("fd01:db8:deaf:beef::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(ulaRaPrefix, kValidLitime * 2, kPreferredLifetime * 2),
Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(400000);
// Deprecated prefixes will be removed.
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 2. Publish a prefix, and renew it before it expired.
Log("2. Renew prefix lifetime.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:1:2::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
AdvanceTime(400000);
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 3. Publish a prefix, and publish another prefix to replace it (with goodbye ra).
Log("3. Update prefix.");
{
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:1:2::", 64);
Ip6::Prefix newRaPrefix = PrefixFromString("2001:db8:3:4::", 64);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(raPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
sExpectedRios.Clear();
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1000000);
VerifyPdOmrPrefix(raPrefix);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra(
{Pio(raPrefix, 0, 0), Pio(newRaPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(newRaPrefix);
AdvanceTime(1000000);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyPdOmrPrefix(newRaPrefix);
AdvanceTime(1000000);
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
// 4. Short prefix will be extended to /64.
Log("Short prefix");
{
// The prefix will be padded to a /64 prefix.
Ip6::Prefix raPrefix = PrefixFromString("2001:db8:cafe:0::", 64);
Ip6::Prefix realRaPrefix;
realRaPrefix.Set(raPrefix.GetBytes(), 48);
SendRouterAdvertToBorderRoutingProcessIcmp6Ra({Pio(realRaPrefix, kValidLitime, kPreferredLifetime)});
sExpectedRios.Add(raPrefix);
AdvanceTime(10000);
VerifyPdOmrPrefix(raPrefix);
VerifyOrQuit(sExpectedRios.SawAll());
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(1500000);
sExpectedRios.Clear();
VerifyPdOmrPrefix(raPrefix);
VerifyOmrPrefixInNetData(raPrefix, /* aDefaultRoute */ false);
AdvanceTime(400000);
// Deprecated prefixes will be removed.
VerifyNoPdOmrPrefix();
VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false);
}
Log("End of TestBorderRoutingProcessPlatfromGeneratedNd");
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
int main(void)
@@ -3143,6 +3393,9 @@ int main(void)
#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
TestNat64PrefixSelection();
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
TestBorderRoutingProcessPlatfromGeneratedNd();
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE
printf("All tests passed\n");
#else