1 /* 2 * Copyright (c) 2018, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions for the BorderAgent role. 32 */ 33 34 #ifndef BORDER_AGENT_HPP_ 35 #define BORDER_AGENT_HPP_ 36 37 #include "openthread-core-config.h" 38 39 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 40 41 #include <openthread/border_agent.h> 42 43 #include "common/as_core_type.hpp" 44 #include "common/heap_allocatable.hpp" 45 #include "common/locator.hpp" 46 #include "common/non_copyable.hpp" 47 #include "common/notifier.hpp" 48 #include "common/owned_ptr.hpp" 49 #include "common/tasklet.hpp" 50 #include "meshcop/dataset.hpp" 51 #include "meshcop/secure_transport.hpp" 52 #include "net/udp6.hpp" 53 #include "thread/tmf.hpp" 54 #include "thread/uri_paths.hpp" 55 56 namespace ot { 57 58 namespace MeshCoP { 59 60 #if !OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE 61 #error "Border Agent feature requires `OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE`" 62 #endif 63 64 class BorderAgent : public InstanceLocator, private NonCopyable 65 { 66 friend class ot::Notifier; 67 friend class Tmf::Agent; 68 69 public: 70 /** 71 * Minimum length of the ephemeral key string. 72 */ 73 static constexpr uint16_t kMinEphemeralKeyLength = OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH; 74 75 /** 76 * Maximum length of the ephemeral key string. 77 */ 78 static constexpr uint16_t kMaxEphemeralKeyLength = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH; 79 80 /** 81 * Default ephemeral key timeout interval in milliseconds. 82 */ 83 static constexpr uint32_t kDefaultEphemeralKeyTimeout = OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT; 84 85 /** 86 * Maximum ephemeral key timeout interval in milliseconds. 87 */ 88 static constexpr uint32_t kMaxEphemeralKeyTimeout = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT; 89 90 typedef otBorderAgentId Id; ///< Border Agent ID. 91 typedef otBorderAgentCounters Counters; ///< Border Agent Counters. 92 93 /** 94 * Defines the Border Agent state. 95 */ 96 enum State : uint8_t 97 { 98 kStateStopped, ///< Stopped/disabled. 99 kStateStarted, ///< Started and listening for connections. 100 kStateConnected, ///< Connected to an external commissioner candidate, petition pending. 101 kStateAccepted, ///< Connected to and accepted an external commissioner. 102 }; 103 104 /** 105 * Initializes the `BorderAgent` object. 106 * 107 * @param[in] aInstance A reference to the OpenThread instance. 108 */ 109 explicit BorderAgent(Instance &aInstance); 110 111 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 112 /** 113 * Gets the randomly generated Border Agent ID. 114 * 115 * The ID is saved in persistent storage and survives reboots. The typical use case of the ID is to 116 * be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this 117 * Border Router/Agent device. 118 * 119 * @param[out] aId Reference to return the Border Agent ID. 120 * 121 * @retval kErrorNone If successfully retrieved the Border Agent ID. 122 * @retval ... If failed to retrieve the Border Agent ID. 123 */ 124 Error GetId(Id &aId); 125 126 /** 127 * Sets the Border Agent ID. 128 * 129 * The Border Agent ID will be saved in persistent storage and survive reboots. It's required 130 * to set the ID only once after factory reset. If the ID has never been set by calling this 131 * method, a random ID will be generated and returned when `GetId()` is called. 132 * 133 * @param[out] aId specifies the Border Agent ID. 134 * 135 * @retval kErrorNone If successfully set the Border Agent ID. 136 * @retval ... If failed to set the Border Agent ID. 137 */ 138 Error SetId(const Id &aId); 139 #endif 140 141 /** 142 * Gets the UDP port of this service. 143 * 144 * @returns UDP port number. 145 */ 146 uint16_t GetUdpPort(void) const; 147 148 /** 149 * Gets the state of the Border Agent service. 150 * 151 * @returns The state of the Border Agent service. 152 */ GetState(void) const153 State GetState(void) const { return mState; } 154 155 /** 156 * Disconnects the Border Agent from any active secure sessions. 157 * 158 * If Border Agent is connected to a commissioner candidate with ephemeral key, calling this API 159 * will cause the ephemeral key to be cleared after the session is disconnected. 160 * 161 * The Border Agent state may not change immediately upon calling this method, the state will be 162 * updated when the connection update is notified by `HandleConnected()`. 163 */ 164 void Disconnect(void); 165 166 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 167 /** 168 * Sets the ephemeral key for a given timeout duration. 169 * 170 * The ephemeral key can be set when the Border Agent is already running and is not currently connected to any 171 * external commissioner (i.e., it is in `kStateStarted` state). To terminate active commissioner sessions, 172 * use the `Disconnect()` function. 173 * 174 * The given @p aKeyString is directly used as the ephemeral PSK (excluding the trailing null `\0` character). Its 175 * length must be between `kMinEphemeralKeyLength` and `kMaxEphemeralKeyLength`, inclusive. 176 * 177 * Setting the ephemeral key again before a previously set one is timed out will replace the previous one and will 178 * reset the timeout. 179 * 180 * During the timeout interval, the ephemeral key can be used only once by an external commissioner to establish a 181 * connection. After the commissioner disconnects, the ephemeral key is cleared, and the Border Agent reverts to 182 * using PSKc. If the timeout expires while a commissioner is still connected, the session will be terminated, and 183 * the Border Agent will cease using the ephemeral key and revert to PSKc. 184 * 185 * @param[in] aKeyString The ephemeral key. 186 * @param[in] aTimeout The timeout duration in milliseconds to use the ephemeral key. 187 * If zero, the default `kDefaultEphemeralKeyTimeout` value will be used. 188 * If the timeout value is larger than `kMaxEphemeralKeyTimeout`, the max value will be 189 * used instead. 190 * @param[in] aUdpPort The UDP port to use with ephemeral key. If UDP port is zero, an ephemeral port will be 191 * used. `GetUdpPort()` will return the current UDP port being used. 192 * 193 * @retval kErrorNone Successfully set the ephemeral key. 194 * @retval kErrorInvalidState Agent is not running or connected to external commissioner. 195 * @retval kErrorInvalidArgs The given @p aKeyString is not valid. 196 * @retval kErrorFailed Failed to set the key (e.g., could not bind to UDP port). 197 */ 198 Error SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort); 199 200 /** 201 * Cancels the ephemeral key in use if any. 202 * 203 * Can be used to cancel a previously set ephemeral key before it times out. If the Border Agent is not running or 204 * there is no ephemeral key in use, calling this function has no effect. 205 * 206 * If a commissioner is connected using the ephemeral key and is currently active, calling this method does not 207 * change its state. In this case the `IsEphemeralKeyActive()` will continue to return `true` until the commissioner 208 * disconnects, or the ephemeral key timeout expires. To terminate active commissioner sessions, use the 209 * `Disconnect()` function. 210 */ 211 void ClearEphemeralKey(void); 212 213 /** 214 * Indicates whether or not an ephemeral key is currently active. 215 * 216 * @retval TRUE An ephemeral key is active. 217 * @retval FALSE No ephemeral key is active. 218 */ IsEphemeralKeyActive(void) const219 bool IsEphemeralKeyActive(void) const { return mUsingEphemeralKey; } 220 221 /** 222 * Callback function pointer to notify when there is any changes related to use of ephemeral key by Border Agent. 223 */ 224 typedef otBorderAgentEphemeralKeyCallback EphemeralKeyCallback; 225 SetEphemeralKeyCallback(EphemeralKeyCallback aCallback,void * aContext)226 void SetEphemeralKeyCallback(EphemeralKeyCallback aCallback, void *aContext) 227 { 228 mEphemeralKeyCallback.Set(aCallback, aContext); 229 } 230 231 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 232 233 /** 234 * Gets the set of border agent counters. 235 * 236 * @returns The border agent counters. 237 */ GetCounters(void)238 const Counters &GetCounters(void) { return mCounters; } 239 240 private: 241 static_assert(kMaxEphemeralKeyLength <= Dtls::Transport::kPskMaxLength, 242 "Max ephemeral key length is larger than max PSK len"); 243 244 static constexpr uint16_t kUdpPort = OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT; 245 static constexpr uint32_t kKeepAliveTimeout = 50 * 1000; // Timeout to reject a commissioner (in msec) 246 247 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 248 static constexpr uint16_t kMaxEphemeralKeyConnectionAttempts = 10; 249 #endif 250 251 class CoapDtlsSession : public Coap::SecureSession, public Heap::Allocatable<CoapDtlsSession> 252 { 253 friend Heap::Allocatable<CoapDtlsSession>; 254 255 private: CoapDtlsSession(Instance & aInstance,Dtls::Transport & aDtlsTransport)256 CoapDtlsSession(Instance &aInstance, Dtls::Transport &aDtlsTransport) 257 : Coap::SecureSession(aInstance, aDtlsTransport) 258 { 259 SetResourceHandler(&HandleResource); 260 } 261 262 static bool HandleResource(CoapBase &aCoapBase, 263 const char *aUriPath, 264 Coap::Message &aMessage, 265 const Ip6::MessageInfo &aMessageInfo); 266 bool HandleResource(const char *aUriPath, Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 267 }; 268 269 class ForwardContext : public InstanceLocatorInit, public Heap::Allocatable<ForwardContext> 270 { 271 public: 272 Error Init(Instance &aInstance, const Coap::Message &aMessage, bool aPetition, bool aSeparate); IsPetition(void) const273 bool IsPetition(void) const { return mPetition; } GetMessageId(void) const274 uint16_t GetMessageId(void) const { return mMessageId; } 275 Error ToHeader(Coap::Message &aMessage, uint8_t aCode) const; 276 277 private: 278 uint16_t mMessageId; // The CoAP Message ID of the original request. 279 bool mPetition : 1; // Whether the forwarding request is leader petition. 280 bool mSeparate : 1; // Whether the original request expects separate response. 281 uint8_t mTokenLength : 4; // The CoAP Token Length of the original request. 282 uint8_t mType : 2; // The CoAP Type of the original request. 283 uint8_t mToken[Coap::Message::kMaxTokenLength]; // The CoAP Token of the original request. 284 }; 285 Start(void)286 void Start(void) { IgnoreError(Start(kUdpPort)); } 287 Error Start(uint16_t aUdpPort); 288 Error Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength); 289 void Stop(void); 290 void HandleNotifierEvents(Events aEvents); 291 void HandleTimeout(void); 292 Error ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri); 293 Error ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage); 294 void SendErrorMessage(const ForwardContext &aForwardContext, Error aError); 295 void SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError); 296 void HandleTmfCommissionerKeepAlive(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 297 void HandleTmfRelayTx(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 298 void HandleTmfProxyTx(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 299 void HandleTmfDatasetGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri); 300 301 template <Uri kUri> void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 302 303 static SecureSession *HandleAcceptSession(void *aContext, const Ip6::MessageInfo &aMessageInfo); 304 CoapDtlsSession *HandleAcceptSession(void); 305 static void HandleRemoveSession(void *aContext, SecureSession &aSesssion); 306 void HandleRemoveSession(SecureSession &aSesssion); 307 308 static void HandleConnected(Dtls::Session::ConnectEvent aEvent, void *aContext); 309 void HandleConnected(Dtls::Session::ConnectEvent aEvent); 310 static void HandleCoapResponse(void *aContext, 311 otMessage *aMessage, 312 const otMessageInfo *aMessageInfo, 313 otError aResult); 314 void HandleCoapResponse(const ForwardContext &aForwardContext, const Coap::Message *aResponse, Error aResult); 315 static bool HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo); 316 bool HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 317 318 static Coap::Message::Code CoapCodeFromError(Error aError); 319 320 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 321 void RestartAfterRemovingEphemeralKey(void); 322 void HandleEphemeralKeyTimeout(void); 323 void InvokeEphemeralKeyCallback(void); 324 static void HandleDtlsTransportClosed(void *aContext); 325 void HandleDtlsTransportClosed(void); 326 #endif 327 328 using TimeoutTimer = TimerMilliIn<BorderAgent, &BorderAgent::HandleTimeout>; 329 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 330 using EphemeralKeyTimer = TimerMilliIn<BorderAgent, &BorderAgent::HandleEphemeralKeyTimeout>; 331 using EphemeralKeyTask = TaskletIn<BorderAgent, &BorderAgent::InvokeEphemeralKeyCallback>; 332 #endif 333 334 State mState; 335 Ip6::Udp::Receiver mUdpReceiver; 336 Ip6::Netif::UnicastAddress mCommissionerAloc; 337 TimeoutTimer mTimer; 338 Dtls::Transport mDtlsTransport; 339 CoapDtlsSession *mCoapDtlsSession; 340 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 341 Id mId; 342 bool mIdInitialized; 343 #endif 344 #if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 345 bool mUsingEphemeralKey : 1; 346 bool mDidConnectWithEphemeralKey : 1; 347 uint16_t mOldUdpPort; 348 EphemeralKeyTimer mEphemeralKeyTimer; 349 EphemeralKeyTask mEphemeralKeyTask; 350 Callback<EphemeralKeyCallback> mEphemeralKeyCallback; 351 #endif 352 Counters mCounters; 353 }; 354 355 DeclareTmfHandler(BorderAgent, kUriRelayRx); 356 357 } // namespace MeshCoP 358 359 DefineCoreType(otBorderAgentId, MeshCoP::BorderAgent::Id); 360 361 } // namespace ot 362 363 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 364 365 #endif // BORDER_AGENT_HPP_ 366