Skip to content

Commit dd954b8

Browse files
silabs-thanhNsuveshpratapa
authored andcommitted
[ncp] Enable DHCPv6 PD support on the NCP
1 parent ab0c135 commit dd954b8

File tree

6 files changed

+204
-0
lines changed

6 files changed

+204
-0
lines changed

src/host/ncp_host.cpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737

3838
#include <openthread/openthread-system.h>
3939

40+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
41+
#include <openthread/border_routing.h>
42+
#include <openthread/icmp6.h>
43+
#include <openthread/ip6.h>
44+
#endif
45+
4046
#include "host/async_task.hpp"
4147
#include "lib/spinel/spinel_driver.hpp"
4248

@@ -141,6 +147,10 @@ void NcpHost::Init(void)
141147
#else
142148
mNcpSpinel.SrpServerSetEnabled(/* aEnabled */ true);
143149
#endif
150+
#endif
151+
152+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
153+
mNcpSpinel.BorderRoutingSetDhcp6PdEnabled(true);
144154
#endif
145155
mIsInitialized = true;
146156
}
@@ -431,5 +441,111 @@ otbrError NcpHost::HandleIcmp6Nd(uint32_t aInfraIfIndex,
431441
return mNcpSpinel.HandleIcmp6Nd(aInfraIfIndex, aIp6Address, aData, aDataLen);
432442
}
433443

444+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
445+
otbrError NcpHost::TryProcessIcmp6RaMessage(const uint8_t *aData, uint16_t aLength)
446+
{
447+
otbrError error = OTBR_ERROR_NOT_FOUND;
448+
const uint8_t *ra = nullptr;
449+
ssize_t raLength;
450+
const uint8_t *optionPtr;
451+
ssize_t remainingLen;
452+
bool foundPio = false;
453+
454+
// Check if data is a valid IPv6 packet with ICMPv6 RA message
455+
VerifyOrExit(aData != nullptr && aLength >= OT_IP6_HEADER_SIZE + OT_ICMP6_ROUTER_ADVERT_MIN_SIZE,
456+
error = OTBR_ERROR_INVALID_ARGS);
457+
458+
// Check IPv6 version (first 4 bits should be 6)
459+
VerifyOrExit((aData[0] >> 4) == 6, error = OTBR_ERROR_INVALID_ARGS);
460+
461+
// Check if protocol is ICMPv6 (0x3A = 58)
462+
VerifyOrExit(aData[OT_IP6_HEADER_PROTO_OFFSET] == OT_IP6_PROTO_ICMP6, error = OTBR_ERROR_INVALID_ARGS);
463+
464+
// Get pointer to ICMPv6 header (after IPv6 header)
465+
ra = aData + OT_IP6_HEADER_SIZE;
466+
raLength = aLength - OT_IP6_HEADER_SIZE;
467+
468+
// Check if it's a Router Advertisement message (Type 134, Code 0)
469+
VerifyOrExit(raLength >= OT_ICMP6_ROUTER_ADVERT_MIN_SIZE, error = OTBR_ERROR_INVALID_ARGS);
470+
VerifyOrExit(ra[0] == OT_ICMP6_TYPE_ROUTER_ADVERT && ra[1] == 0, error = OTBR_ERROR_INVALID_ARGS);
471+
472+
// Parse options to find Prefix Information Options (PIO)
473+
// Options start after RA header (offset 16 from ICMPv6 header start)
474+
optionPtr = ra + 16;
475+
remainingLen = raLength - 16;
476+
477+
while (remainingLen >= 8) // Minimum option size is 8 bytes
478+
{
479+
uint8_t optionType = optionPtr[0];
480+
uint8_t optionLength = optionPtr[1]; // Length in units of 8 bytes
481+
uint16_t optionSize = optionLength * 8;
482+
483+
// Check if we have enough data for this option
484+
if (remainingLen < optionSize)
485+
{
486+
break; // Invalid option, stop parsing
487+
}
488+
489+
// Process Prefix Information Option (Type 3)
490+
if (optionType == 3 && optionSize >= 32) // PIO is 32 bytes
491+
{
492+
// PIO structure:
493+
// Bytes 0-1: Type (3) and Length (4 = 32 bytes)
494+
// Byte 2: Prefix Length
495+
// Byte 3: Flags (L|A|Reserved1)
496+
// Bytes 4-7: Valid Lifetime
497+
// Bytes 8-11: Preferred Lifetime
498+
// Bytes 12-15: Reserved2
499+
// Bytes 16-31: Prefix (16 bytes)
500+
501+
otBorderRoutingPrefixTableEntry pioEntry;
502+
memset(&pioEntry, 0, sizeof(pioEntry));
503+
504+
// Extract prefix (16 bytes starting at offset 16)
505+
memcpy(pioEntry.mPrefix.mPrefix.mFields.m8, &optionPtr[16], OT_IP6_ADDRESS_SIZE);
506+
// Extract prefix length
507+
pioEntry.mPrefix.mLength = optionPtr[2];
508+
// Extract lifetimes (big-endian)
509+
pioEntry.mValidLifetime = (static_cast<uint32_t>(optionPtr[4]) << 24) |
510+
(static_cast<uint32_t>(optionPtr[5]) << 16) |
511+
(static_cast<uint32_t>(optionPtr[6]) << 8) | static_cast<uint32_t>(optionPtr[7]);
512+
pioEntry.mPreferredLifetime = (static_cast<uint32_t>(optionPtr[8]) << 24) |
513+
(static_cast<uint32_t>(optionPtr[9]) << 16) |
514+
(static_cast<uint32_t>(optionPtr[10]) << 8) | static_cast<uint32_t>(optionPtr[11]);
515+
516+
// Logging the PIO information
517+
Ip6Address prefixAddr(pioEntry.mPrefix.mPrefix);
518+
otbrLogInfo("PIO Entry: prefix=%s/%u, validLifetime=%u, preferredLifetime=%u",
519+
prefixAddr.ToString().c_str(), pioEntry.mPrefix.mLength,
520+
pioEntry.mValidLifetime, pioEntry.mPreferredLifetime);
521+
522+
// Process this PIO by sending it to NCP
523+
otError pioError = mNcpSpinel.BorderRoutingProcessDhcp6PdPrefix(&pioEntry);
524+
if (pioError != OT_ERROR_NONE)
525+
{
526+
otbrLogWarning("Failed to process PIO from RA message, %s", otThreadErrorToString(pioError));
527+
}
528+
else
529+
{
530+
foundPio = true;
531+
}
532+
}
533+
534+
// Move to next option
535+
optionPtr += optionSize;
536+
remainingLen -= optionSize;
537+
}
538+
539+
// Return success if we found at least one PIO
540+
if (foundPio)
541+
{
542+
error = OTBR_ERROR_NONE;
543+
}
544+
545+
exit:
546+
return error;
547+
}
548+
#endif // OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
549+
434550
} // namespace Host
435551
} // namespace otbr

src/host/ncp_host.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,9 @@ class NcpHost : public MainloopProcessor,
176176
const Ip6Address &aIp6Address,
177177
const uint8_t *aData,
178178
uint16_t aDataLen) override;
179+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
180+
otbrError TryProcessIcmp6RaMessage(const uint8_t *aData, uint16_t aLength) override;
181+
#endif
179182

180183
bool mIsInitialized;
181184
ot::Spinel::SpinelDriver &mSpinelDriver;

src/host/ncp_spinel.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,5 +1514,46 @@ otDeviceRole NcpSpinel::SpinelRoleToDeviceRole(spinel_net_role_t aRole)
15141514
return role;
15151515
}
15161516

1517+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
1518+
void NcpSpinel::BorderRoutingSetDhcp6PdEnabled(bool aEnabled)
1519+
{
1520+
otError error;
1521+
EncodingFunc encodingFunc = [aEnabled](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteBool(aEnabled); };
1522+
1523+
error = SetProperty(SPINEL_PROP_BORDER_ROUTER_DHCP6_PD_ENABLE, encodingFunc);
1524+
if (error != OT_ERROR_NONE)
1525+
{
1526+
otbrLogWarning("Failed to call BorderRoutingSetDhcp6PdEnabled, %s", otThreadErrorToString(error));
1527+
}
1528+
}
1529+
1530+
otError NcpSpinel::BorderRoutingProcessDhcp6PdPrefix(const otBorderRoutingPrefixTableEntry *aPrefixInfo)
1531+
{
1532+
otError error = OT_ERROR_NONE;
1533+
EncodingFunc encodingFunc = [aPrefixInfo](ot::Spinel::Encoder &aEncoder) {
1534+
otError error = OT_ERROR_NONE;
1535+
1536+
// Write prefix and prefix length
1537+
SuccessOrExit(error = aEncoder.WriteIp6Address(aPrefixInfo->mPrefix.mPrefix));
1538+
SuccessOrExit(error = aEncoder.WriteUint8(aPrefixInfo->mPrefix.mLength));
1539+
1540+
// Write valid and preferred lifetimes
1541+
SuccessOrExit(error = aEncoder.WriteUint32(aPrefixInfo->mValidLifetime));
1542+
SuccessOrExit(error = aEncoder.WriteUint32(aPrefixInfo->mPreferredLifetime));
1543+
1544+
exit:
1545+
return error;
1546+
};
1547+
1548+
error = SetProperty(SPINEL_PROP_BORDER_ROUTER_DHCP6_PD_PREFIX, encodingFunc);
1549+
if (error != OT_ERROR_NONE)
1550+
{
1551+
otbrLogWarning("Failed to send DHCP6 PD prefix to NCP, %s", otThreadErrorToString(error));
1552+
}
1553+
1554+
return error;
1555+
}
1556+
#endif // OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
1557+
15171558
} // namespace Host
15181559
} // namespace otbr

src/host/ncp_spinel.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060
#include "host/posix/netif.hpp"
6161
#include "mdns/mdns.hpp"
6262

63+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
64+
#include <openthread/border_routing.h>
65+
#endif
66+
6367
namespace otbr {
6468
namespace Host {
6569

@@ -467,6 +471,25 @@ class NcpSpinel : public CliDaemon::Dependencies
467471
void DeactivateEphemeralKey(bool aRetainActiveSession, AsyncTaskPtr aAsyncTask);
468472
#endif // OTBR_ENABLE_EPSKC
469473

474+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
475+
/**
476+
* This method enables/disables the DHCP6 PD on NCP.
477+
*
478+
* @param[in] aEnabled A boolean to enable/disable the DHCP6 PD.
479+
*/
480+
void BorderRoutingSetDhcp6PdEnabled(bool aEnabled);
481+
482+
/**
483+
* This method processes a DHCP6 PD prefix by sending it to the NCP via SPINEL.
484+
*
485+
* @param[in] aPrefixInfo A pointer to the prefix information structure containing all fields.
486+
*
487+
* @retval OT_ERROR_NONE The prefix was sent successfully.
488+
* @retval OT_ERROR_FAILED Failed to encode or send the SPINEL command.
489+
*/
490+
otError BorderRoutingProcessDhcp6PdPrefix(const otBorderRoutingPrefixTableEntry *aPrefixInfo);
491+
#endif
492+
470493
private:
471494
using FailureHandler = std::function<void(otError)>;
472495

src/host/posix/netif.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ otbrError Netif::Dependencies::Ip6MulAddrUpdateSubscription(const otIp6Address &
6868
return OTBR_ERROR_NONE;
6969
}
7070

71+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
72+
otbrError Netif::Dependencies::TryProcessIcmp6RaMessage(const uint8_t *aData, uint16_t aLength)
73+
{
74+
OTBR_UNUSED_VARIABLE(aData);
75+
OTBR_UNUSED_VARIABLE(aLength);
76+
77+
return OTBR_ERROR_NOT_FOUND;
78+
}
79+
#endif
80+
7181
OT_TOOL_PACKED_BEGIN
7282
struct Mldv2Header
7383
{
@@ -279,6 +289,14 @@ void Netif::ProcessIp6Send(void)
279289
rval = read(mTunFd, packet, sizeof(packet));
280290
VerifyOrExit(rval > 0, error = OTBR_ERROR_ERRNO);
281291

292+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
293+
// Try to process as RA message for DHCP6 PD prefix processing
294+
if (mDeps.TryProcessIcmp6RaMessage(packet, static_cast<uint16_t>(rval)) == OTBR_ERROR_NONE)
295+
{
296+
ExitNow();
297+
}
298+
#endif
299+
282300
otbrLogInfo("Send packet (%hu bytes)", static_cast<uint16_t>(rval));
283301

284302
error = mDeps.Ip6Send(packet, rval);

src/host/posix/netif.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ class Netif : public MainloopProcessor, private NonCopyable
5757

5858
virtual otbrError Ip6Send(const uint8_t *aData, uint16_t aLength);
5959
virtual otbrError Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded);
60+
#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING
61+
virtual otbrError TryProcessIcmp6RaMessage(const uint8_t *aData, uint16_t aLength);
62+
#endif
6063
};
6164

6265
Netif(const std::string &aInterfaceName, Dependencies &aDependencies);

0 commit comments

Comments
 (0)