|
37 | 37 |
|
38 | 38 | #include <openthread/openthread-system.h> |
39 | 39 |
|
| 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 | + |
40 | 46 | #include "host/async_task.hpp" |
41 | 47 | #include "lib/spinel/spinel_driver.hpp" |
42 | 48 |
|
@@ -141,6 +147,10 @@ void NcpHost::Init(void) |
141 | 147 | #else |
142 | 148 | mNcpSpinel.SrpServerSetEnabled(/* aEnabled */ true); |
143 | 149 | #endif |
| 150 | +#endif |
| 151 | + |
| 152 | +#if OTBR_ENABLE_DHCP6_PD && OTBR_ENABLE_BORDER_ROUTING |
| 153 | + mNcpSpinel.BorderRoutingSetDhcp6PdEnabled(true); |
144 | 154 | #endif |
145 | 155 | mIsInitialized = true; |
146 | 156 | } |
@@ -431,5 +441,111 @@ otbrError NcpHost::HandleIcmp6Nd(uint32_t aInfraIfIndex, |
431 | 441 | return mNcpSpinel.HandleIcmp6Nd(aInfraIfIndex, aIp6Address, aData, aDataLen); |
432 | 442 | } |
433 | 443 |
|
| 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 (optionLength == 0 || remainingLen < optionSize) |
| 485 | + { |
| 486 | + break; // Invalid option, stop parsing |
| 487 | + } |
| 488 | + |
| 489 | + // Process Prefix Information Option (Type 3) |
| 490 | + if (optionType == 3 && optionLength == 4) // 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 | + |
434 | 550 | } // namespace Host |
435 | 551 | } // namespace otbr |
0 commit comments