• 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 "instance/instance.hpp"
38 
39 namespace ot {
40 namespace Trel {
41 
42 RegisterLogModule("TrelInterface");
43 
44 const char Interface::kTxtRecordExtAddressKey[] = "xa";
45 const char Interface::kTxtRecordExtPanIdKey[]   = "xp";
46 
Interface(Instance & aInstance)47 Interface::Interface(Instance &aInstance)
48     : InstanceLocator(aInstance)
49     , mInitialized(false)
50     , mEnabled(false)
51     , mFiltered(false)
52     , mRegisterServiceTask(aInstance)
53 {
54 }
55 
Init(void)56 void Interface::Init(void)
57 {
58     OT_ASSERT(!mInitialized);
59 
60     mInitialized = true;
61 
62     if (mEnabled)
63     {
64         mEnabled = false;
65         Enable();
66     }
67 }
68 
SetEnabled(bool aEnable)69 void Interface::SetEnabled(bool aEnable)
70 {
71     if (aEnable)
72     {
73         Enable();
74     }
75     else
76     {
77         Disable();
78     }
79 }
80 
Enable(void)81 void Interface::Enable(void)
82 {
83     VerifyOrExit(!mEnabled);
84 
85     mEnabled = true;
86     VerifyOrExit(mInitialized);
87 
88     otPlatTrelEnable(&GetInstance(), &mUdpPort);
89 
90     LogInfo("Enabled interface, local port:%u", mUdpPort);
91     mRegisterServiceTask.Post();
92 
93 exit:
94     return;
95 }
96 
Disable(void)97 void Interface::Disable(void)
98 {
99     VerifyOrExit(mEnabled);
100 
101     mEnabled = false;
102     VerifyOrExit(mInitialized);
103 
104     otPlatTrelDisable(&GetInstance());
105     mPeerTable.Clear();
106     LogDebg("Disabled interface");
107 
108 exit:
109     return;
110 }
111 
FindPeer(const Mac::ExtAddress & aExtAddress)112 Interface::Peer *Interface::FindPeer(const Mac::ExtAddress &aExtAddress)
113 {
114     return mPeerTable.FindMatching(aExtAddress);
115 }
116 
NotifyPeerSocketAddressDifference(const Ip6::SockAddr & aPeerSockAddr,const Ip6::SockAddr & aRxSockAddr)117 void Interface::NotifyPeerSocketAddressDifference(const Ip6::SockAddr &aPeerSockAddr, const Ip6::SockAddr &aRxSockAddr)
118 {
119     otPlatTrelNotifyPeerSocketAddressDifference(&GetInstance(), &aPeerSockAddr, &aRxSockAddr);
120 }
121 
HandleExtAddressChange(void)122 void Interface::HandleExtAddressChange(void)
123 {
124     VerifyOrExit(mInitialized && mEnabled);
125     LogDebg("Extended Address changed, re-registering DNS-SD service");
126     mRegisterServiceTask.Post();
127 
128 exit:
129     return;
130 }
131 
HandleExtPanIdChange(void)132 void Interface::HandleExtPanIdChange(void)
133 {
134     VerifyOrExit(mInitialized && mEnabled);
135     LogDebg("Extended PAN ID changed, re-registering DNS-SD service");
136     mRegisterServiceTask.Post();
137 
138 exit:
139     return;
140 }
141 
RegisterService(void)142 void Interface::RegisterService(void)
143 {
144     // TXT data consists of two entries: the length fields, the
145     // "key" string, "=" char, and binary representation of the MAC
146     // or Extended PAN ID values.
147     static constexpr uint8_t kTxtDataSize =
148         /* ExtAddr  */ sizeof(uint8_t) + sizeof(kTxtRecordExtAddressKey) - 1 + sizeof(char) + sizeof(Mac::ExtAddress) +
149         /* ExtPanId */ sizeof(uint8_t) + sizeof(kTxtRecordExtPanIdKey) - 1 + sizeof(char) +
150         sizeof(MeshCoP::ExtendedPanId);
151 
152     uint8_t                        txtDataBuffer[kTxtDataSize];
153     MutableData<kWithUint16Length> txtData;
154     Dns::TxtEntry                  txtEntries[2];
155 
156     VerifyOrExit(mInitialized && mEnabled);
157 
158     txtEntries[0].Init(kTxtRecordExtAddressKey, Get<Mac::Mac>().GetExtAddress().m8, sizeof(Mac::ExtAddress));
159     txtEntries[1].Init(kTxtRecordExtPanIdKey, Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId().m8,
160                        sizeof(MeshCoP::ExtendedPanId));
161 
162     txtData.Init(txtDataBuffer, sizeof(txtDataBuffer));
163     SuccessOrAssert(Dns::TxtEntry::AppendEntries(txtEntries, GetArrayLength(txtEntries), txtData));
164 
165     LogInfo("Registering DNS-SD service: port:%u, txt:\"%s=%s, %s=%s\"", mUdpPort, kTxtRecordExtAddressKey,
166             Get<Mac::Mac>().GetExtAddress().ToString().AsCString(), kTxtRecordExtPanIdKey,
167             Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId().ToString().AsCString());
168 
169     otPlatTrelRegisterService(&GetInstance(), mUdpPort, txtData.GetBytes(), static_cast<uint8_t>(txtData.GetLength()));
170 
171 exit:
172     return;
173 }
174 
otPlatTrelHandleDiscoveredPeerInfo(otInstance * aInstance,const otPlatTrelPeerInfo * aInfo)175 extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const otPlatTrelPeerInfo *aInfo)
176 {
177     Instance &instance = AsCoreType(aInstance);
178 
179     VerifyOrExit(instance.IsInitialized());
180     instance.Get<Interface>().HandleDiscoveredPeerInfo(*static_cast<const Interface::Peer::Info *>(aInfo));
181 
182 exit:
183     return;
184 }
185 
HandleDiscoveredPeerInfo(const Peer::Info & aInfo)186 void Interface::HandleDiscoveredPeerInfo(const Peer::Info &aInfo)
187 {
188     Peer                  *entry;
189     Mac::ExtAddress        extAddress;
190     MeshCoP::ExtendedPanId extPanId;
191     bool                   isNew = false;
192 
193     VerifyOrExit(mInitialized && mEnabled);
194 
195     SuccessOrExit(ParsePeerInfoTxtData(aInfo, extAddress, extPanId));
196 
197     VerifyOrExit(extAddress != Get<Mac::Mac>().GetExtAddress());
198 
199     if (aInfo.IsRemoved())
200     {
201         entry = FindPeer(extAddress);
202         VerifyOrExit(entry != nullptr);
203         RemovePeerEntry(*entry);
204         ExitNow();
205     }
206 
207     // It is a new entry or an update to an existing entry. First
208     // check whether we have an existing entry that matches the same
209     // socket address, and remove it if it is associated with a
210     // different Extended MAC address. This ensures that we do not
211     // keep stale entries in the peer table.
212 
213     entry = mPeerTable.FindMatching(aInfo.GetSockAddr());
214 
215     if ((entry != nullptr) && !entry->Matches(extAddress))
216     {
217         RemovePeerEntry(*entry);
218         entry = nullptr;
219     }
220 
221     if (entry == nullptr)
222     {
223         entry = mPeerTable.FindMatching(extAddress);
224     }
225 
226     if (entry == nullptr)
227     {
228         entry = GetNewPeerEntry();
229         VerifyOrExit(entry != nullptr);
230 
231         entry->SetExtAddress(extAddress);
232         isNew = true;
233     }
234 
235     if (!isNew)
236     {
237         VerifyOrExit((entry->GetExtPanId() != extPanId) || (entry->GetSockAddr() != aInfo.GetSockAddr()));
238     }
239 
240     entry->SetExtPanId(extPanId);
241     entry->SetSockAddr(aInfo.GetSockAddr());
242 
243     entry->Log(isNew ? "Added" : "Updated");
244 
245 exit:
246     return;
247 }
248 
ParsePeerInfoTxtData(const Peer::Info & aInfo,Mac::ExtAddress & aExtAddress,MeshCoP::ExtendedPanId & aExtPanId) const249 Error Interface::ParsePeerInfoTxtData(const Peer::Info       &aInfo,
250                                       Mac::ExtAddress        &aExtAddress,
251                                       MeshCoP::ExtendedPanId &aExtPanId) const
252 {
253     Error                   error;
254     Dns::TxtEntry           entry;
255     Dns::TxtEntry::Iterator iterator;
256     bool                    parsedExtAddress = false;
257     bool                    parsedExtPanId   = false;
258 
259     aExtPanId.Clear();
260 
261     iterator.Init(aInfo.GetTxtData(), aInfo.GetTxtLength());
262 
263     while ((error = iterator.GetNextEntry(entry)) == kErrorNone)
264     {
265         // If the TXT data happens to have entries with key longer
266         // than `kMaxIterKeyLength`, `mKey` would be `nullptr` and full
267         // entry would be placed in `mValue`. We skip over such
268         // entries.
269         if (entry.mKey == nullptr)
270         {
271             continue;
272         }
273 
274         if (StringMatch(entry.mKey, kTxtRecordExtAddressKey))
275         {
276             VerifyOrExit(!parsedExtAddress, error = kErrorParse);
277             VerifyOrExit(entry.mValueLength == sizeof(Mac::ExtAddress), error = kErrorParse);
278             aExtAddress.Set(entry.mValue);
279             parsedExtAddress = true;
280         }
281         else if (StringMatch(entry.mKey, kTxtRecordExtPanIdKey))
282         {
283             VerifyOrExit(!parsedExtPanId, error = kErrorParse);
284             VerifyOrExit(entry.mValueLength == sizeof(MeshCoP::ExtendedPanId), error = kErrorParse);
285             memcpy(aExtPanId.m8, entry.mValue, sizeof(MeshCoP::ExtendedPanId));
286             parsedExtPanId = true;
287         }
288 
289         // Skip over and ignore any unknown keys.
290     }
291 
292     VerifyOrExit(error == kErrorNotFound);
293     error = kErrorNone;
294 
295     VerifyOrExit(parsedExtAddress && parsedExtPanId, error = kErrorParse);
296 
297 exit:
298     return error;
299 }
300 
GetNewPeerEntry(void)301 Interface::Peer *Interface::GetNewPeerEntry(void)
302 {
303     Peer *peerEntry;
304 
305     peerEntry = mPeerTable.PushBack();
306     VerifyOrExit(peerEntry == nullptr);
307 
308     for (Peer &entry : mPeerTable)
309     {
310         if (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId())
311         {
312             ExitNow(peerEntry = &entry);
313         }
314     }
315 
316     for (Peer &entry : mPeerTable)
317     {
318         // We skip over any existing entry in neighbor table (even if the
319         // entry is in invalid state).
320 
321         if (Get<NeighborTable>().FindNeighbor(entry.GetExtAddress(), Neighbor::kInStateAny) != nullptr)
322         {
323             continue;
324         }
325 
326 #if OPENTHREAD_FTD
327         if (Get<NeighborTable>().FindRxOnlyNeighborRouter(entry.GetExtAddress()) != nullptr)
328         {
329             continue;
330         }
331 #endif
332 
333         ExitNow(peerEntry = &entry);
334     }
335 
336 exit:
337     return peerEntry;
338 }
339 
RemovePeerEntry(Peer & aEntry)340 void Interface::RemovePeerEntry(Peer &aEntry)
341 {
342     aEntry.Log("Removing");
343 
344     // Replace the entry being removed with the last entry (if not the
345     // last one already) and then pop the last entry from array.
346 
347     if (&aEntry != mPeerTable.Back())
348     {
349         aEntry = *mPeerTable.Back();
350     }
351 
352     mPeerTable.PopBack();
353 }
354 
GetCounters(void) const355 const Counters *Interface::GetCounters(void) const { return otPlatTrelGetCounters(&GetInstance()); }
356 
ResetCounters(void)357 void Interface::ResetCounters(void) { otPlatTrelResetCounters(&GetInstance()); }
358 
Send(const Packet & aPacket,bool aIsDiscovery)359 Error Interface::Send(const Packet &aPacket, bool aIsDiscovery)
360 {
361     Error error = kErrorNone;
362     Peer *peerEntry;
363 
364     VerifyOrExit(mInitialized && mEnabled, error = kErrorAbort);
365     VerifyOrExit(!mFiltered);
366 
367     switch (aPacket.GetHeader().GetType())
368     {
369     case Header::kTypeBroadcast:
370         for (Peer &entry : mPeerTable)
371         {
372             if (!aIsDiscovery && (entry.GetExtPanId() != Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()))
373             {
374                 continue;
375             }
376 
377             otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &entry.mSockAddr);
378         }
379         break;
380 
381     case Header::kTypeUnicast:
382     case Header::kTypeAck:
383         peerEntry = mPeerTable.FindMatching(aPacket.GetHeader().GetDestination());
384         VerifyOrExit(peerEntry != nullptr, error = kErrorAbort);
385         otPlatTrelSend(&GetInstance(), aPacket.GetBuffer(), aPacket.GetLength(), &peerEntry->mSockAddr);
386         break;
387     }
388 
389 exit:
390     return error;
391 }
392 
otPlatTrelHandleReceived(otInstance * aInstance,uint8_t * aBuffer,uint16_t aLength,const otSockAddr * aSenderAddress)393 extern "C" void otPlatTrelHandleReceived(otInstance       *aInstance,
394                                          uint8_t          *aBuffer,
395                                          uint16_t          aLength,
396                                          const otSockAddr *aSenderAddress)
397 {
398     Instance &instance = AsCoreType(aInstance);
399 
400     VerifyOrExit(instance.IsInitialized());
401     instance.Get<Interface>().HandleReceived(aBuffer, aLength, AsCoreType(aSenderAddress));
402 
403 exit:
404     return;
405 }
406 
HandleReceived(uint8_t * aBuffer,uint16_t aLength,const Ip6::SockAddr & aSenderAddr)407 void Interface::HandleReceived(uint8_t *aBuffer, uint16_t aLength, const Ip6::SockAddr &aSenderAddr)
408 {
409     LogDebg("HandleReceived(aLength:%u)", aLength);
410 
411     VerifyOrExit(mInitialized && mEnabled && !mFiltered);
412 
413     mRxPacket.Init(aBuffer, aLength);
414     Get<Link>().ProcessReceivedPacket(mRxPacket, aSenderAddr);
415 
416 exit:
417     return;
418 }
419 
GetNextPeer(PeerIterator & aIterator) const420 const Interface::Peer *Interface::GetNextPeer(PeerIterator &aIterator) const
421 {
422     const Peer *entry = mPeerTable.At(aIterator);
423 
424     if (entry != nullptr)
425     {
426         aIterator++;
427     }
428 
429     return entry;
430 }
431 
Log(const char * aAction) const432 void Interface::Peer::Log(const char *aAction) const
433 {
434     OT_UNUSED_VARIABLE(aAction);
435 
436     LogInfo("%s peer mac:%s, xpan:%s, %s", aAction, GetExtAddress().ToString().AsCString(),
437             GetExtPanId().ToString().AsCString(), GetSockAddr().ToString().AsCString());
438 }
439 
440 } // namespace Trel
441 } // namespace ot
442 
443 #endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
444