/* * Copyright (c) 2018, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @file * This file includes definitions for the BorderAgent role. */ #ifndef BORDER_AGENT_HPP_ #define BORDER_AGENT_HPP_ #include "openthread-core-config.h" #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE #include #include "common/as_core_type.hpp" #include "common/heap_allocatable.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" #include "common/tasklet.hpp" #include "meshcop/secure_transport.hpp" #include "net/udp6.hpp" #include "thread/tmf.hpp" #include "thread/uri_paths.hpp" namespace ot { namespace MeshCoP { class BorderAgent : public InstanceLocator, private NonCopyable { friend class ot::Notifier; friend class Tmf::Agent; friend class Tmf::SecureAgent; public: /** * Minimum length of the ephemeral key string. * */ static constexpr uint16_t kMinEphemeralKeyLength = OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH; /** * Maximum length of the ephemeral key string. * */ static constexpr uint16_t kMaxEphemeralKeyLength = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH; /** * Default ephemeral key timeout interval in milliseconds. * */ static constexpr uint32_t kDefaultEphemeralKeyTimeout = OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT; /** * Maximum ephemeral key timeout interval in milliseconds. * */ static constexpr uint32_t kMaxEphemeralKeyTimeout = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT; typedef otBorderAgentId Id; ///< Border Agent ID. /** * Defines the Border Agent state. * */ enum State : uint8_t { kStateStopped = OT_BORDER_AGENT_STATE_STOPPED, ///< Border agent is stopped/disabled. kStateStarted = OT_BORDER_AGENT_STATE_STARTED, ///< Border agent is started. kStateActive = OT_BORDER_AGENT_STATE_ACTIVE, ///< Border agent is connected with external commissioner. }; /** * Initializes the `BorderAgent` object. * * @param[in] aInstance A reference to the OpenThread instance. * */ explicit BorderAgent(Instance &aInstance); #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE /** * Gets the randomly generated Border Agent ID. * * The ID is saved in persistent storage and survives reboots. The typical use case of the ID is to * be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this * Border Router/Agent device. * * @param[out] aId Reference to return the Border Agent ID. * * @retval kErrorNone If successfully retrieved the Border Agent ID. * @retval ... If failed to retrieve the Border Agent ID. * */ Error GetId(Id &aId); /** * Sets the Border Agent ID. * * The Border Agent ID will be saved in persistent storage and survive reboots. It's required * to set the ID only once after factory reset. If the ID has never been set by calling this * method, a random ID will be generated and returned when `GetId()` is called. * * @param[out] aId specifies the Border Agent ID. * * @retval kErrorNone If successfully set the Border Agent ID. * @retval ... If failed to set the Border Agent ID. * */ Error SetId(const Id &aId); #endif /** * Gets the UDP port of this service. * * @returns UDP port number. * */ uint16_t GetUdpPort(void) const; /** * Starts the Border Agent service. * */ void Start(void) { IgnoreError(Start(kUdpPort)); } /** * Stops the Border Agent service. * */ void Stop(void); /** * Gets the state of the Border Agent service. * * @returns The state of the Border Agent service. * */ State GetState(void) const { return mState; } #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE /** * Sets the ephemeral key for a given timeout duration. * * The ephemeral key can be set when the Border Agent is already running and is not currently connected to any * external commissioner (i.e., it is in `kStateStarted` state). * * The given @p aKeyString is directly used as the ephemeral PSK (excluding the trailing null `\0` character). Its * length must be between `kMinEphemeralKeyLength` and `kMaxEphemeralKeyLength`, inclusive. * * Setting the ephemeral key again before a previously set one is timed out will replace the previous one and will * reset the timeout. * * While the timeout interval is in effect, the ephemeral key can be used only once by an external commissioner to * connect. Once the commissioner disconnects, the ephemeral key is cleared, and Border Agent reverts to using * PSKc. * * @param[in] aKeyString The ephemeral key. * @param[in] aTimeout The timeout duration in milliseconds to use the ephemeral key. * If zero, the default `kDefaultEphemeralKeyTimeout` value will be used. * If the timeout value is larger than `kMaxEphemeralKeyTimeout`, the max value will be * used instead. * @param[in] aUdpPort The UDP port to use with ephemeral key. If UDP port is zero, an ephemeral port will be * used. `GetUdpPort()` will return the current UDP port being used. * * @retval kErrorNone Successfully set the ephemeral key. * @retval kErrorInvalidState Agent is not running or connected to external commissioner. * @retval kErrorInvalidArgs The given @p aKeyString is not valid. * @retval kErrorFailed Failed to set the key (e.g., could not bind to UDP port). * */ Error SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort); /** * Cancels the ephemeral key in use if any. * * Can be used to cancel a previously set ephemeral key before it times out. If the Border Agent is not running or * there is no ephemeral key in use, calling this function has no effect. * * If a commissioner is connected using the ephemeral key and is currently active, calling this method does not * change its state. In this case the `IsEphemeralKeyActive()` will continue to return `true` until the commissioner * disconnects. * */ void ClearEphemeralKey(void); /** * Indicates whether or not an ephemeral key is currently active. * * @retval TRUE An ephemeral key is active. * @retval FALSE No ephemeral key is active. * */ bool IsEphemeralKeyActive(void) const { return mUsingEphemeralKey; } /** * Callback function pointer to notify when there is any changes related to use of ephemeral key by Border Agent. * * */ typedef otBorderAgentEphemeralKeyCallback EphemeralKeyCallback; void SetEphemeralKeyCallback(EphemeralKeyCallback aCallback, void *aContext) { mEphemeralKeyCallback.Set(aCallback, aContext); } #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE /** * Returns the UDP Proxy port to which the commissioner is currently * bound. * * @returns The current UDP Proxy port or 0 if no Proxy Transmit has been received yet. * */ uint16_t GetUdpProxyPort(void) const { return mUdpProxyPort; } private: static_assert(kMaxEphemeralKeyLength <= SecureTransport::kPskMaxLength, "Max ephemeral key length is larger than max PSK len"); static constexpr uint16_t kUdpPort = OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT; static constexpr uint32_t kKeepAliveTimeout = 50 * 1000; // Timeout to reject a commissioner (in msec) #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE static constexpr uint16_t kMaxEphemeralKeyConnectionAttempts = 10; #endif class ForwardContext : public InstanceLocatorInit, public Heap::Allocatable { public: Error Init(Instance &aInstance, const Coap::Message &aMessage, bool aPetition, bool aSeparate); bool IsPetition(void) const { return mPetition; } uint16_t GetMessageId(void) const { return mMessageId; } Error ToHeader(Coap::Message &aMessage, uint8_t aCode) const; private: uint16_t mMessageId; // The CoAP Message ID of the original request. bool mPetition : 1; // Whether the forwarding request is leader petition. bool mSeparate : 1; // Whether the original request expects separate response. uint8_t mTokenLength : 4; // The CoAP Token Length of the original request. uint8_t mType : 2; // The CoAP Type of the original request. uint8_t mToken[Coap::Message::kMaxTokenLength]; // The CoAP Token of the original request. }; Error Start(uint16_t aUdpPort); Error Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength); void HandleNotifierEvents(Events aEvents); Coap::Message::Code CoapCodeFromError(Error aError); Error SendMessage(Coap::Message &aMessage); void SendErrorMessage(const ForwardContext &aForwardContext, Error aError); void SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError); static void HandleConnected(bool aConnected, void *aContext); void HandleConnected(bool aConnected); template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void HandleTimeout(void); #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE void RestartAfterRemovingEphemeralKey(void); void HandleEphemeralKeyTimeout(void); void InvokeEphemeralKeyCallback(void); static void HandleSecureAgentStopped(void *aContext); void HandleSecureAgentStopped(void); #endif static void HandleCoapResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleCoapResponse(const ForwardContext &aForwardContext, const Coap::Message *aResponse, Error aResult); Error ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri); Error ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage); static bool HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo); bool HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); using TimeoutTimer = TimerMilliIn; #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE using EphemeralKeyTimer = TimerMilliIn; using EphemeralKeyTask = TaskletIn; #endif State mState; uint16_t mUdpProxyPort; Ip6::Udp::Receiver mUdpReceiver; Ip6::Netif::UnicastAddress mCommissionerAloc; TimeoutTimer mTimer; #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE Id mId; bool mIdInitialized; #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE bool mUsingEphemeralKey; uint16_t mOldUdpPort; EphemeralKeyTimer mEphemeralKeyTimer; EphemeralKeyTask mEphemeralKeyTask; Callback mEphemeralKeyCallback; #endif }; DeclareTmfHandler(BorderAgent, kUriRelayRx); DeclareTmfHandler(BorderAgent, kUriCommissionerPetition); DeclareTmfHandler(BorderAgent, kUriCommissionerKeepAlive); DeclareTmfHandler(BorderAgent, kUriRelayTx); DeclareTmfHandler(BorderAgent, kUriCommissionerGet); DeclareTmfHandler(BorderAgent, kUriCommissionerSet); DeclareTmfHandler(BorderAgent, kUriActiveGet); DeclareTmfHandler(BorderAgent, kUriActiveSet); DeclareTmfHandler(BorderAgent, kUriPendingGet); DeclareTmfHandler(BorderAgent, kUriPendingSet); DeclareTmfHandler(BorderAgent, kUriProxyTx); } // namespace MeshCoP DefineMapEnum(otBorderAgentState, MeshCoP::BorderAgent::State); DefineCoreType(otBorderAgentId, MeshCoP::BorderAgent::Id); } // namespace ot #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE #endif // BORDER_AGENT_HPP_