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