• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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