Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / transport / PeerConnections.h
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *
5  *    Licensed under the Apache License, Version 2.0 (the "License");
6  *    you may not use this file except in compliance with the License.
7  *    You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *    Unless required by applicable law or agreed to in writing, software
12  *    distributed under the License is distributed on an "AS IS" BASIS,
13  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *    See the License for the specific language governing permissions and
15  *    limitations under the License.
16  */
17 #pragma once
18
19 #include <core/CHIPError.h>
20 #include <support/CodeUtils.h>
21 #include <system/TimeSource.h>
22 #include <transport/AdminPairingTable.h>
23 #include <transport/PeerConnectionState.h>
24
25 namespace chip {
26 namespace Transport {
27
28 // TODO; use 0xffff to match any key id, this is a temporary solution for
29 // InteractionModel, where key id is not obtainable. This will be removed when
30 // InteractionModel is migrated to messaging layer
31 constexpr const uint16_t kAnyKeyId = 0xffff;
32
33 /**
34  * Handles a set of peer connection states.
35  *
36  * Intended for:
37  *   - handle connection active time and expiration
38  *   - allocate and free space for connection states.
39  */
40 template <size_t kMaxConnectionCount, Time::Source kTimeSource = Time::Source::kSystem>
41 class PeerConnections
42 {
43 public:
44     /**
45      * Allocates a new peer connection state state object out of the internal resource pool.
46      *
47      * @param address represents the connection state address
48      * @param state [out] will contain the connection state if one was available. May be null if no return value is desired.
49      *
50      * @note the newly created state will have an 'active' time set based on the current time source.
51      *
52      * @returns CHIP_NO_ERROR if state could be initialized. May fail if maximum connection count
53      *          has been reached (with CHIP_ERROR_NO_MEMORY).
54      */
55     CHECK_RETURN_VALUE
56     CHIP_ERROR CreateNewPeerConnectionState(const PeerAddress & address, PeerConnectionState ** state)
57     {
58         CHIP_ERROR err = CHIP_ERROR_NO_MEMORY;
59
60         if (state)
61         {
62             *state = nullptr;
63         }
64
65         for (size_t i = 0; i < kMaxConnectionCount; i++)
66         {
67             if (!mStates[i].IsInitialized())
68             {
69                 mStates[i] = PeerConnectionState(address);
70                 mStates[i].SetLastActivityTimeMs(mTimeSource.GetCurrentMonotonicTimeMs());
71
72                 if (state)
73                 {
74                     *state = &mStates[i];
75                 }
76
77                 err = CHIP_NO_ERROR;
78                 break;
79             }
80         }
81
82         return err;
83     }
84
85     /**
86      * Allocates a new peer connection state state object out of the internal resource pool.
87      *
88      * @param peerNode represents optional peer Node's ID
89      * @param peerKeyId represents the encryption key ID assigned by peer node
90      * @param localKeyId represents the encryption key ID assigned by local node
91      * @param state [out] will contain the connection state if one was available. May be null if no return value is desired.
92      *
93      * @note the newly created state will have an 'active' time set based on the current time source.
94      *
95      * @returns CHIP_NO_ERROR if state could be initialized. May fail if maximum connection count
96      *          has been reached (with CHIP_ERROR_NO_MEMORY).
97      */
98     CHECK_RETURN_VALUE
99     CHIP_ERROR CreateNewPeerConnectionState(const Optional<NodeId> & peerNode, uint16_t peerKeyId, uint16_t localKeyId,
100                                             PeerConnectionState ** state)
101     {
102         CHIP_ERROR err = CHIP_ERROR_NO_MEMORY;
103
104         if (state)
105         {
106             *state = nullptr;
107         }
108
109         for (size_t i = 0; i < kMaxConnectionCount; i++)
110         {
111             if (!mStates[i].IsInitialized())
112             {
113                 mStates[i] = PeerConnectionState();
114                 mStates[i].SetPeerKeyID(peerKeyId);
115                 mStates[i].SetLocalKeyID(localKeyId);
116                 mStates[i].SetLastActivityTimeMs(mTimeSource.GetCurrentMonotonicTimeMs());
117
118                 if (peerNode.ValueOr(kUndefinedNodeId) != kUndefinedNodeId)
119                 {
120                     mStates[i].SetPeerNodeId(peerNode.Value());
121                 }
122
123                 if (state)
124                 {
125                     *state = &mStates[i];
126                 }
127
128                 err = CHIP_NO_ERROR;
129                 break;
130             }
131         }
132
133         return err;
134     }
135
136     /**
137      * Get a peer connection state given a Peer address.
138      *
139      * @param address is the connection to find (based on address)
140      * @param begin If a member of the pool, will start search from the next item. Can be nullptr to search from start.
141      *
142      * @return the state found, nullptr if not found
143      */
144     CHECK_RETURN_VALUE
145     PeerConnectionState * FindPeerConnectionState(const PeerAddress & address, PeerConnectionState * begin)
146     {
147         PeerConnectionState * state = nullptr;
148         PeerConnectionState * iter  = &mStates[0];
149
150         if (begin >= iter && begin < &mStates[kMaxConnectionCount])
151         {
152             iter = begin + 1;
153         }
154
155         for (; iter < &mStates[kMaxConnectionCount]; iter++)
156         {
157             if (iter->GetPeerAddress() == address)
158             {
159                 state = iter;
160                 break;
161             }
162         }
163         return state;
164     }
165
166     /**
167      * Get a peer connection state given a Node Id.
168      *
169      * @param nodeId is the connection to find (based on nodeId). Note that initial connections
170      *        do not have a node id set. Use this if you know the node id should be set.
171      * @param begin If a member of the pool, will start search from the next item. Can be nullptr to search from start.
172      *
173      * @return the state found, nullptr if not found
174      */
175     CHECK_RETURN_VALUE
176     PeerConnectionState * FindPeerConnectionState(NodeId nodeId, PeerConnectionState * begin)
177     {
178         PeerConnectionState * state = nullptr;
179         PeerConnectionState * iter  = &mStates[0];
180
181         if (begin >= iter && begin < &mStates[kMaxConnectionCount])
182         {
183             iter = begin + 1;
184         }
185
186         for (; iter < &mStates[kMaxConnectionCount]; iter++)
187         {
188             if (!iter->IsInitialized())
189             {
190                 continue;
191             }
192             if (iter->GetPeerNodeId() == nodeId)
193             {
194                 state = iter;
195                 break;
196             }
197         }
198         return state;
199     }
200
201     /**
202      * Get a peer connection state given a Node Id and Peer's Encryption Key Id.
203      *
204      * @param nodeId is the connection to find (based on nodeId). Note that initial connections
205      *        do not have a node id set. Use this if you know the node id should be set.
206      * @param peerKeyId Encryption key ID used by the peer node.
207      * @param begin If a member of the pool, will start search from the next item. Can be nullptr to search from start.
208      *
209      * @return the state found, nullptr if not found
210      */
211     CHECK_RETURN_VALUE
212     PeerConnectionState * FindPeerConnectionState(Optional<NodeId> nodeId, uint16_t peerKeyId, PeerConnectionState * begin)
213     {
214         PeerConnectionState * state = nullptr;
215         PeerConnectionState * iter  = &mStates[0];
216
217         if (begin >= iter && begin < &mStates[kMaxConnectionCount])
218         {
219             iter = begin + 1;
220         }
221
222         for (; iter < &mStates[kMaxConnectionCount]; iter++)
223         {
224             if (!iter->IsInitialized())
225             {
226                 continue;
227             }
228             if (peerKeyId == kAnyKeyId || iter->GetPeerKeyID() == peerKeyId)
229             {
230                 if (nodeId.ValueOr(kUndefinedNodeId) == kUndefinedNodeId || iter->GetPeerNodeId() == kUndefinedNodeId ||
231                     iter->GetPeerNodeId() == nodeId.Value())
232                 {
233                     state = iter;
234                     break;
235                 }
236             }
237         }
238         return state;
239     }
240
241     /**
242      * Get a peer connection state given the local Encryption Key Id.
243      *
244      * @param keyId Encryption key ID assigned by the local node.
245      * @param begin If a member of the pool, will start search from the next item. Can be nullptr to search from start.
246      *
247      * @return the state found, nullptr if not found
248      */
249     CHECK_RETURN_VALUE
250     PeerConnectionState * FindPeerConnectionState(uint16_t keyId, PeerConnectionState * begin)
251     {
252         PeerConnectionState * state = nullptr;
253         PeerConnectionState * iter  = &mStates[0];
254
255         assert(begin == nullptr || (begin >= iter && begin < &mStates[kMaxConnectionCount]));
256
257         if (begin != nullptr)
258         {
259             iter = begin + 1;
260         }
261
262         for (; iter < &mStates[kMaxConnectionCount]; iter++)
263         {
264             if (!iter->IsInitialized())
265             {
266                 continue;
267             }
268
269             if (iter->GetLocalKeyID() == keyId)
270             {
271                 state = iter;
272                 break;
273             }
274         }
275         return state;
276     }
277
278     /**
279      * Get a peer connection state given a Node Id and Peer's Encryption Key Id.
280      *
281      * @param nodeId is the connection to find (based on peer nodeId). Note that initial connections
282      *        do not have a node id set. Use this if you know the node id should be set.
283      * @param localKeyId Encryption key ID used by the local node.
284      * @param begin If a member of the pool, will start search from the next item. Can be nullptr to search from start.
285      *
286      * @return the state found, nullptr if not found
287      */
288     CHECK_RETURN_VALUE
289     PeerConnectionState * FindPeerConnectionStateByLocalKey(Optional<NodeId> nodeId, uint16_t localKeyId,
290                                                             PeerConnectionState * begin)
291     {
292         PeerConnectionState * state = nullptr;
293         PeerConnectionState * iter  = &mStates[0];
294
295         if (begin >= iter && begin < &mStates[kMaxConnectionCount])
296         {
297             iter = begin + 1;
298         }
299
300         for (; iter < &mStates[kMaxConnectionCount]; iter++)
301         {
302             if (!iter->IsInitialized())
303             {
304                 continue;
305             }
306             if (iter->GetLocalKeyID() == localKeyId)
307             {
308                 if (nodeId.ValueOr(kUndefinedNodeId) == kUndefinedNodeId || iter->GetPeerNodeId() == kUndefinedNodeId ||
309                     iter->GetPeerNodeId() == nodeId.Value())
310                 {
311                     state = iter;
312                     break;
313                 }
314             }
315         }
316         return state;
317     }
318
319     /// Convenience method to mark a peer connection state as active
320     void MarkConnectionActive(PeerConnectionState * state)
321     {
322         state->SetLastActivityTimeMs(mTimeSource.GetCurrentMonotonicTimeMs());
323     }
324
325     /// Convenience method to expired a peer connection state and fired the related callback
326     template <typename Callback>
327     void MarkConnectionExpired(PeerConnectionState * state, Callback callback)
328     {
329         callback(*state);
330         *state = PeerConnectionState(PeerAddress::Uninitialized());
331     }
332
333     /**
334      * Iterates through all active connections and expires any connection with an idle time
335      * larger than the given amount.
336      *
337      * Expiring a connection involves callback execution and then clearing the internal state.
338      */
339     template <typename Callback>
340     void ExpireInactiveConnections(uint64_t maxIdleTimeMs, Callback callback)
341     {
342         const uint64_t currentTime = mTimeSource.GetCurrentMonotonicTimeMs();
343
344         for (size_t i = 0; i < kMaxConnectionCount; i++)
345         {
346             if (!mStates[i].GetPeerAddress().IsInitialized())
347             {
348                 continue; // not an active connection
349             }
350
351             uint64_t connectionActiveTime = mStates[i].GetLastActivityTimeMs();
352             if (connectionActiveTime + maxIdleTimeMs >= currentTime)
353             {
354                 continue; // not expired
355             }
356
357             MarkConnectionExpired(&mStates[i], callback);
358         }
359     }
360
361     /// Allows access to the underlying time source used for keeping track of connection active time
362     Time::TimeSource<kTimeSource> & GetTimeSource() { return mTimeSource; }
363
364 private:
365     Time::TimeSource<kTimeSource> mTimeSource;
366     PeerConnectionState mStates[kMaxConnectionCount];
367 };
368
369 } // namespace Transport
370 } // namespace chip