1 /*
2 * Copyright (c) 2019, 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" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /**
29 * @file
30 * This file implements Thread Radio Encapsulation Link (TREL) interface.
31 */
32
33 #include "trel_interface.hpp"
34
35 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
36
37 #include <string.h>
38
39 #include "common/array.hpp"
40 #include "common/as_core_type.hpp"
41 #include "common/code_utils.hpp"
42 #include "common/debug.hpp"
43 #include "common/instance.hpp"
44 #include "common/locator_getters.hpp"
45 #include "common/log.hpp"
46 #include "common/string.hpp"
47 #include "net/dns_types.hpp"
48
49 namespace ot {
50 namespace Trel {
51
52 RegisterLogModule("TrelInterface");
53
54 const char Interface::kTxtRecordExtAddressKey[] = "xa";
55 const char Interface::kTxtRecordExtPanIdKey[] = "xp";
56
Interface(Instance & aInstance)57 Interface::Interface(Instance &aInstance)
58 : InstanceLocator(aInstance)
59 , mInitialized(false)
60 , mEnabled(false)
61 , mFiltered(false)
62 , mRegisterServiceTask(aInstance, HandleRegisterServiceTask)
63 {
64 }
65
Init(void)66 void Interface::Init(void)
67 {
68 OT_ASSERT(!mInitialized);
69
70 mInitialized = true;
71
72 if (mEnabled)
73 {
74 mEnabled = false;
75 Enable();
76 }
77 }
78
Enable(void)79 void Interface::Enable(void)
80 {
81 VerifyOrExit(!mEnabled);
82
83 mEnabled = true;
84 VerifyOrExit(mInitialized);
85
86 otPlatTrelEnable(&GetInstance(), &mUdpPort);
87
88 LogInfo("Enabled interface, local port:%u", mUdpPort);
89 mRegisterServiceTask.Post();
90
91 exit:
92 return;
93 }
94
Disable(void)95 void Interface::Disable(void)
96 {
97 VerifyOrExit(mEnabled);
98
99 mEnabled = false;
100 VerifyOrExit(mInitialized);
101
102 otPlatTrelDisable(&GetInstance());
103 mPeerTable.Clear();
104 LogDebg("Disabled interface");
105
106 exit:
107 return;
108 }
109
HandleExtAddressChange(void)110 void Interface::HandleExtAddressChange(void)
111 {
112 VerifyOrExit(mInitialized && mEnabled);
113 LogDebg("Extended Address changed, re-registering DNS-SD service");
114 mRegisterServiceTask.Post();
115
116 exit:
117 return;
118 }
119
HandleExtPanIdChange(void)120 void Interface::HandleExtPanIdChange(void)
121 {
122 VerifyOrExit(mInitialized && mEnabled);
123 LogDebg("Extended PAN ID changed, re-registering DNS-SD service");
124 mRegisterServiceTask.Post();
125
126 exit:
127 return;
128 }
129
HandleRegisterServiceTask(Tasklet & aTasklet)130 void Interface::HandleRegisterServiceTask(Tasklet &aTasklet)
131 {
132 aTasklet.Get<Interface>().RegisterService();
133 }
134
RegisterService(void)135 void Interface::RegisterService(void)
136 {
137 // TXT data consists of two entries: the length fields, the
138 // "key" string, "=" char, and binary representation of the MAC
139 // or Extended PAN ID values.
140 static constexpr uint8_t kTxtDataSize =
141 /* ExtAddr */ sizeof(uint8_t) + sizeof(kTxtRecordExtAddressKey) - 1 + sizeof(char) + sizeof(Mac::ExtAddress) +
142 /* ExtPanId */ sizeof(uint8_t) + sizeof(kTxtRecordExtPanIdKey) - 1 + sizeof(char) +
143 sizeof(MeshCoP::ExtendedPanId);
144
145 uint8_t txtDataBuffer[kTxtDataSize];
146 MutableData<kWithUint16Length> txtData;
147 Dns::TxtEntry txtEntries[2];
148
149 VerifyOrExit(mInitialized && mEnabled);
150
151 txtEntries[0].Init(kTxtRecordExtAddressKey, Get<Mac::Mac>().GetExtAddress().m8, sizeof(Mac::ExtAddress));
152 txtEntries[1].Init(kTxtRecordExtPanIdKey, Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId().m8,
153 sizeof(MeshCoP::ExtendedPanId));
154
155 txtData.Init(txtDataBuffer, sizeof(txtDataBuffer));
156 SuccessOrAssert(Dns::TxtEntry::AppendEntries(txtEntries, GetArrayLength(txtEntries), txtData));
157
158 LogInfo("Registering DNS-SD service: port:%u, txt:\"%s=%s, %s=%s\"", mUdpPort, kTxtRecordExtAddressKey,
159 Get<Mac::Mac>().GetExtAddress().ToString().AsCString(), kTxtRecordExtPanIdKey,
160 Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId().ToString().AsCString());
161
162 otPlatTrelRegisterService(&GetInstance(), mUdpPort, txtData.GetBytes(), static_cast<uint8_t>(txtData.GetLength()));
163
164 exit:
165 return;
166 }
167
otPlatTrelHandleDiscoveredPeerInfo(otInstance * aInstance,const otPlatTrelPeerInfo * aInfo)168 extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo)
169 {
170 Instance &instance = AsCoreType(aInstance);
171
172 VerifyOrExit(instance.IsInitialized());
173 instance.Get<Interface>().HandleDiscoveredPeerInfo(*static_cast<const Interface::Peer::Info *>(aInfo));
174
175 exit:
176 return;
177 }
178
HandleDiscoveredPeerInfo(const Peer::Info & aInfo)179 void Interface::HandleDiscoveredPeerInfo(const Peer::Info &aInfo)
180 {
181 Peer * entry;
182 Mac::ExtAddress extAddress;
183 MeshCoP::ExtendedPanId extPanId;
184 bool isNew = false;
185
186 VerifyOrExit(mInitialized && mEnabled);
187
188 SuccessOrExit(ParsePeerInfoTxtData(aInfo, extAddress, extPanId));
189
190 VerifyOrExit(extAddress != Get<Mac::Mac>().GetExtAddress());
191
192 if (aInfo.IsRemoved())
193 {
194 entry = mPeerTable.FindMatching(extAddress);
195 VerifyOrExit(entry != nullptr);
196 RemovePeerEntry(*entry);
197 ExitNow();
198 }
199
200 // It is a new entry or an update to an existing entry. First
201 // check whether we have an existing entry that matches the same
202 // socket address, and remove it if it is associated with a
203 // different Extended MAC address. This ensures that we do not
204 // keep stale entries in the peer table.
205
206 entry = mPeerTable.FindMatching(aInfo.GetSockAddr());
207
208 if ((entry != nullptr) && !entry->Matches(extAddress))
209 {
210 RemovePeerEntry(*entry);
211 entry = nullptr;
212 }
213
214 if (entry == nullptr)
215 {
216 entry = mPeerTable.FindMatching(extAddress);
217 }
218
219 if (entry == nullptr)
220 {
221 entry = GetNewPeerEntry();
222 VerifyOrExit(entry != nullptr);
223
224 entry->SetExtAddress(extAddress);
225 isNew = true;
226 }
227
228 if (!isNew)
229 {
230 VerifyOrExit((entry->GetExtPanId() != extPanId) || (entry->GetSockAddr() != aInfo.GetSockAddr()));
231 }
232
233 entry->SetExtPanId(extPanId);
234 entry->SetSockAddr(aInfo.GetSockAddr());
235
236 entry->Log(isNew ? "Added" : "Updated");
237
238 exit:
239 return;
240 }
241
ParsePeerInfoTxtData(const Peer::Info & aInfo,Mac::ExtAddress & aExtAddress,MeshCoP::ExtendedPanId & aExtPanId) const242 Error Interface::ParsePeerInfoTxtData(const Peer::Info & aInfo,
243 Mac::ExtAddress & aExtAddress,
244 MeshCoP::ExtendedPanId &aExtPanId) const
245 {
246 Error error;
247 Dns::TxtEntry entry;
248 Dns::TxtEntry::Iterator iterator;
249 bool parsedExtAddress = false;
250 bool parsedExtPanId = false;
251
252 aExtPanId.Clear();
253
254 iterator.Init(aInfo.GetTxtData(), aInfo.GetTxtLength());
255
256 while ((error = iterator.GetNextEntry(entry)) == kErrorNone)
257 {
258 if (strcmp(entry.mKey, kTxtRecordExtAddressKey) == 0)
259 {
260 VerifyOrExit(!parsedExtAddress, error = kErrorParse);
261 VerifyOrExit(entry.mValueLength == sizeof(Mac::ExtAddress), error = kErrorParse);
262 aExtAddress.Set(entry.mValue);
263 parsedExtAddress = true;
264 }
265 else if (strcmp(entry.mKey, kTxtRecordExtPanIdKey) == 0)
266 {
267 VerifyOrExit(!parsedExtPanId, error = kErrorParse);
268 VerifyOrExit(entry.mValueLength == sizeof(MeshCoP::ExtendedPanId), error = kErrorParse);
269 memcpy(aExtPanId.m8, entry.mValue, sizeof(MeshCoP::ExtendedPanId));
270 parsedExtPanId = true;
271 }
272
273 // Skip over and ignore any unknown keys.
274 }
275
276 VerifyOrExit(error == kErrorNotFound);
277 error = kErrorNone;
278
279 VerifyOrExit(parsedExtAddress && parsedExtPanId, error = kErrorParse);
280
281 exit:
282 return error;
283 }
284
GetNewPeerEntry(void)285 Interface::Peer *Interface::GetNewPeerEntry(void)
286 {
287 Peer *peerEntry;
288
289 peerEntry = mPeerTable.PushBack();
290 VerifyOrExit(peerEntry == nullptr);
291
292 for (Peer &entry : mPeerTable)
293 {
294 if (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId())
295 {
296 ExitNow(peerEntry = &entry);
297 }
298 }
299
300 for (Peer &entry : mPeerTable)
301 {
302 // We skip over any existing entry in neighbor table (even if the
303 // entry is in invalid state).
304
305 if (Get<NeighborTable>().FindNeighbor(entry.GetExtAddress(), Neighbor::kInStateAny) != nullptr)
306 {
307 continue;
308 }
309
310 #if OPENTHREAD_FTD
311 {
312 Mac::Address macAddress;
313
314 macAddress.SetExtended(entry.GetExtAddress());
315
316 if (Get<NeighborTable>().FindRxOnlyNeighborRouter(macAddress) != nullptr)
317 {
318 continue;
319 }
320 }
321 #endif
322
323 ExitNow(peerEntry = &entry);
324 }
325
326 exit:
327 return peerEntry;
328 }
329
RemovePeerEntry(Peer & aEntry)330 void Interface::RemovePeerEntry(Peer &aEntry)
331 {
332 aEntry.Log("Removing");
333
334 // Replace the entry being removed with the last entry (if not the
335 // last one already) and then pop the last entry from array.
336
337 if (&aEntry != mPeerTable.Back())
338 {
339 aEntry = *mPeerTable.Back();
340 }
341
342 mPeerTable.PopBack();
343 }
344
Send(const Packet & aPacket,bool aIsDiscovery)345 Error Interface::Send(const Packet &aPacket, bool aIsDiscovery)
346 {
347 Error error = kErrorNone;
348 Peer *peerEntry;
349
350 VerifyOrExit(mInitialized && mEnabled, error = kErrorAbort);
351 VerifyOrExit(!mFiltered);
352
353 switch (aPacket.GetHeader().GetType())
354 {
355 case Header::kTypeBroadcast:
356 for (Peer &entry : mPeerTable)
357 {
358 if (!aIsDiscovery && (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()))
359 {
360 continue;
361 }
362
363 otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &entry.mSockAddr);
364 }
365 break;
366
367 case Header::kTypeUnicast:
368 case Header::kTypeAck:
369 peerEntry = mPeerTable.FindMatching(aPacket.GetHeader().GetDestination());
370 VerifyOrExit(peerEntry != nullptr, error = kErrorAbort);
371 otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &peerEntry->mSockAddr);
372 break;
373 }
374
375 exit:
376 return error;
377 }
378
otPlatTrelHandleReceived(otInstance * aInstance,uint8_t * aBuffer,uint16_t aLength)379 extern "C" void otPlatTrelHandleReceived(otInstance *aInstance, uint8_t *aBuffer, uint16_t aLength)
380 {
381 Instance &instance = AsCoreType(aInstance);
382
383 VerifyOrExit(instance.IsInitialized());
384 instance.Get<Interface>().HandleReceived(aBuffer, aLength);
385
386 exit:
387 return;
388 }
389
HandleReceived(uint8_t * aBuffer,uint16_t aLength)390 void Interface::HandleReceived(uint8_t *aBuffer, uint16_t aLength)
391 {
392 LogDebg("HandleReceived(aLength:%u)", aLength);
393
394 VerifyOrExit(mInitialized && mEnabled && !mFiltered);
395
396 mRxPacket.Init(aBuffer, aLength);
397 Get<Link>().ProcessReceivedPacket(mRxPacket);
398
399 exit:
400 return;
401 }
402
GetNextPeer(PeerIterator & aIterator) const403 const Interface::Peer *Interface::GetNextPeer(PeerIterator &aIterator) const
404 {
405 const Peer *entry = mPeerTable.At(aIterator);
406
407 if (entry != nullptr)
408 {
409 aIterator++;
410 }
411
412 return entry;
413 }
414
Log(const char * aAction) const415 void Interface::Peer::Log(const char *aAction) const
416 {
417 OT_UNUSED_VARIABLE(aAction);
418
419 LogInfo("%s peer mac:%s, xpan:%s, %s", aAction, GetExtAddress().ToString().AsCString(),
420 GetExtPanId().ToString().AsCString(), GetSockAddr().ToString().AsCString());
421 }
422
423 } // namespace Trel
424 } // namespace ot
425
426 #endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
427