diff --git a/include/openthread/icmp6.h b/include/openthread/icmp6.h index 3a5733c2b..32f8c6a1b 100644 --- a/include/openthread/icmp6.h +++ b/include/openthread/icmp6.h @@ -80,7 +80,8 @@ typedef enum otIcmp6Code OT_ICMP6_CODE_FRAGM_REAS_TIME_EX = 1, ///< Fragment Reassembly Time Exceeded } otIcmp6Code; -#define OT_ICMP6_HEADER_DATA_SIZE 4 ///< Size of an message specific data of ICMPv6 Header. +#define OT_ICMP6_HEADER_DATA_SIZE 4 ///< Size of ICMPv6 Header. +#define OT_ICMP6_ROUTER_ADVERT_MIN_SIZE 16 ///< Size of a Router Advertisement message without any options. /** * @struct otIcmp6Header diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 555b497bf..1abec1077 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (332) +#define OPENTHREAD_API_VERSION (333) /** * @addtogroup api-instance diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index c9251f9b2..e34205f46 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -56,6 +56,8 @@ extern "C" { #define OT_IP6_PREFIX_BITSIZE (OT_IP6_PREFIX_SIZE * 8) ///< Size of an IPv6 prefix (bits) #define OT_IP6_IID_SIZE 8 ///< Size of an IPv6 Interface Identifier (bytes) #define OT_IP6_ADDRESS_SIZE 16 ///< Size of an IPv6 address (bytes) +#define OT_IP6_HEADER_SIZE 40 ///< Size of an IPv6 header (bytes) +#define OT_IP6_HEADER_PROTO_OFFSET 6 ///< Offset of the proto field in the IPv6 header (bytes) /** * @struct otIp6InterfaceIdentifier diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index 455f534ff..3b98fe354 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -146,6 +146,7 @@ extern int #include #include #include +#include #include #include "common/code_utils.hpp" @@ -1021,6 +1022,71 @@ exit: } } +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE +static constexpr uint8_t kIpVersion4 = 4; +static constexpr uint8_t kIpVersion6 = 6; + +static uint8_t getIpVersion(const uint8_t *data) +{ + assert(data != nullptr); + + // Mute compiler warnings. + OT_UNUSED_VARIABLE(kIpVersion4); + OT_UNUSED_VARIABLE(kIpVersion6); + + return (static_cast(data[0]) >> 4) & 0x0F; +} +#endif + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE + +/** + * Returns nullptr if data does not point to a valid ICMPv6 RA message. + * + */ +static const uint8_t *getIcmp6RaMessage(const uint8_t *data, ssize_t length) +{ + const uint8_t *ret = nullptr; + otIcmp6Header icmpHeader; + + VerifyOrExit(length >= OT_IP6_HEADER_SIZE + OT_ICMP6_ROUTER_ADVERT_MIN_SIZE); + VerifyOrExit(getIpVersion(data) == kIpVersion6); + VerifyOrExit(data[OT_IP6_HEADER_PROTO_OFFSET] == OT_IP6_PROTO_ICMP6); + + ret = data + OT_IP6_HEADER_SIZE; + memcpy(&icmpHeader, ret, sizeof(icmpHeader)); + VerifyOrExit(icmpHeader.mType == OT_ICMP6_TYPE_ROUTER_ADVERT, ret = nullptr); + VerifyOrExit(icmpHeader.mCode == 0, ret = nullptr); + +exit: + return ret; +} + +/** + * Returns false if the message is not an ICMPv6 RA message. + * + */ +static otError tryProcessIcmp6RaMessage(otInstance *aInstance, const uint8_t *data, ssize_t length) +{ + otError error = OT_ERROR_NONE; + const uint8_t *ra = getIcmp6RaMessage(data, length); + ssize_t raLength; + + VerifyOrExit(ra != nullptr, error = OT_ERROR_INVALID_ARGS); + +#if OPENTHREAD_POSIX_LOG_TUN_PACKETS + otLogInfoPlat("[netif] RA to BorderRouting (%hu bytes)", static_cast(length)); + otDumpInfoPlat("", data, static_cast(length)); +#endif + + raLength = length + (ra - data); + otPlatBorderRoutingProcessIcmp6Ra(aInstance, ra, raLength); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE + static void processTransmit(otInstance *aInstance) { otMessage *message = nullptr; @@ -1046,13 +1112,20 @@ static void processTransmit(otInstance *aInstance) } #endif +#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE + if (tryProcessIcmp6RaMessage(aInstance, reinterpret_cast(&packet[offset]), rval) == OT_ERROR_NONE) + { + ExitNow(); + } +#endif + { otMessageSettings settings; settings.mLinkSecurityEnabled = (otThreadGetDeviceRole(aInstance) != OT_DEVICE_ROLE_DISABLED); settings.mPriority = OT_MESSAGE_PRIORITY_LOW; #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE - isIp4 = (packet[offset] & 0xf0) == 0x40; + isIp4 = (getIpVersion(reinterpret_cast(&packet[offset])) == kIpVersion4); message = isIp4 ? otIp4NewMessage(aInstance, &settings) : otIp6NewMessage(aInstance, &settings); #else message = otIp6NewMessage(aInstance, &settings);