1 /*
2  *  Copyright (c) 2022, 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 implementation for the NAT64 translator.
32  */
33 
34 #include "nat64_translator.hpp"
35 
36 #if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
37 
38 #include "instance/instance.hpp"
39 
40 namespace ot {
41 namespace Nat64 {
42 
43 RegisterLogModule("Nat64");
44 
StateToString(State aState)45 const char *StateToString(State aState)
46 {
47     static const char *const kStateString[] = {
48         "Disabled",
49         "NotRunning",
50         "Idle",
51         "Active",
52     };
53 
54     struct EnumCheck
55     {
56         InitEnumValidatorCounter();
57         ValidateNextEnum(kStateDisabled);
58         ValidateNextEnum(kStateNotRunning);
59         ValidateNextEnum(kStateIdle);
60         ValidateNextEnum(kStateActive);
61     };
62 
63     return kStateString[aState];
64 }
65 
Translator(Instance & aInstance)66 Translator::Translator(Instance &aInstance)
67     : InstanceLocator(aInstance)
68     , mState(State::kStateDisabled)
69     , mMappingExpirerTimer(aInstance)
70 {
71     Random::NonCrypto::Fill(mNextMappingId);
72 
73     mNat64Prefix.Clear();
74     mIp4Cidr.Clear();
75     mMappingExpirerTimer.Start(kAddressMappingIdleTimeoutMsec);
76 }
77 
NewIp4Message(const Message::Settings & aSettings)78 Message *Translator::NewIp4Message(const Message::Settings &aSettings)
79 {
80     Message *message = Get<Ip6::Ip6>().NewMessage(sizeof(Ip6::Header) - sizeof(Ip4::Header), aSettings);
81 
82     if (message != nullptr)
83     {
84         message->SetType(Message::kTypeIp4);
85     }
86 
87     return message;
88 }
89 
SendMessage(Message & aMessage)90 Error Translator::SendMessage(Message &aMessage)
91 {
92     bool   freed  = false;
93     Error  error  = kErrorDrop;
94     Result result = TranslateToIp6(aMessage);
95 
96     VerifyOrExit(result == kForward);
97 
98     error = Get<Ip6::Ip6>().SendRaw(OwnedPtr<Message>(&aMessage).PassOwnership());
99     freed = true;
100 
101 exit:
102     if (!freed)
103     {
104         aMessage.Free();
105     }
106 
107     return error;
108 }
109 
TranslateFromIp6(Message & aMessage)110 Translator::Result Translator::TranslateFromIp6(Message &aMessage)
111 {
112     Result                res        = kDrop;
113     ErrorCounters::Reason dropReason = ErrorCounters::kUnknown;
114     Ip6::Header           ip6Header;
115     Ip4::Header           ip4Header;
116     AddressMapping       *mapping = nullptr;
117 
118     if (mIp4Cidr.mLength == 0 || !mNat64Prefix.IsValidNat64())
119     {
120         ExitNow(res = kNotTranslated);
121     }
122 
123     // ParseFrom will do basic checks for the message, including the message length and IP protocol version.
124     if (ip6Header.ParseFrom(aMessage) != kErrorNone)
125     {
126         LogWarn("outgoing datagram is not a valid IPv6 datagram, drop");
127         dropReason = ErrorCounters::Reason::kIllegalPacket;
128         ExitNow(res = kDrop);
129     }
130 
131     if (!ip6Header.GetDestination().MatchesPrefix(mNat64Prefix))
132     {
133         ExitNow(res = kNotTranslated);
134     }
135 
136     mapping = FindOrAllocateMapping(ip6Header.GetSource());
137     if (mapping == nullptr)
138     {
139         LogWarn("failed to get a mapping for %s (mapping pool full?)", ip6Header.GetSource().ToString().AsCString());
140         dropReason = ErrorCounters::Reason::kNoMapping;
141         ExitNow(res = kDrop);
142     }
143 
144     aMessage.RemoveHeader(sizeof(Ip6::Header));
145 
146     ip4Header.Clear();
147     ip4Header.InitVersionIhl();
148     ip4Header.SetSource(mapping->mIp4);
149     ip4Header.GetDestination().ExtractFromIp6Address(mNat64Prefix.mLength, ip6Header.GetDestination());
150     ip4Header.SetTtl(ip6Header.GetHopLimit());
151     ip4Header.SetIdentification(0);
152 
153     switch (ip6Header.GetNextHeader())
154     {
155     case Ip6::kProtoUdp:
156         ip4Header.SetProtocol(Ip4::kProtoUdp);
157         res = kForward;
158         break;
159     case Ip6::kProtoTcp:
160         ip4Header.SetProtocol(Ip4::kProtoTcp);
161         res = kForward;
162         break;
163     case Ip6::kProtoIcmp6:
164         ip4Header.SetProtocol(Ip4::kProtoIcmp);
165         SuccessOrExit(TranslateIcmp6(aMessage));
166         res = kForward;
167         break;
168     default:
169         dropReason = ErrorCounters::Reason::kUnsupportedProto;
170         ExitNow(res = kDrop);
171     }
172 
173     // res here must be kForward based on the switch above.
174     // TODO: Implement the logic for replying ICMP messages.
175     ip4Header.SetTotalLength(sizeof(Ip4::Header) + aMessage.GetLength() - aMessage.GetOffset());
176     Checksum::UpdateMessageChecksum(aMessage, ip4Header.GetSource(), ip4Header.GetDestination(),
177                                     ip4Header.GetProtocol());
178     Checksum::UpdateIp4HeaderChecksum(ip4Header);
179     if (aMessage.Prepend(ip4Header) != kErrorNone)
180     {
181         // This should never happen since the IPv4 header is shorter than the IPv6 header.
182         LogCrit("failed to prepend IPv4 head to translated message");
183         ExitNow(res = kDrop);
184     }
185     aMessage.SetType(Message::kTypeIp4);
186     mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength());
187     mapping->mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength());
188 
189 exit:
190     if (res == Result::kDrop)
191     {
192         mErrorCounters.Count6To4(dropReason);
193     }
194     return res;
195 }
196 
TranslateToIp6(Message & aMessage)197 Translator::Result Translator::TranslateToIp6(Message &aMessage)
198 {
199     Result                res        = Result::kDrop;
200     ErrorCounters::Reason dropReason = ErrorCounters::kUnknown;
201     Ip6::Header           ip6Header;
202     Ip4::Header           ip4Header;
203     AddressMapping       *mapping = nullptr;
204 
205     // Ip6::Header::ParseFrom may return an error value when the incoming message is an IPv4 datagram.
206     // If the message is already an IPv6 datagram, forward it directly.
207     VerifyOrExit(ip6Header.ParseFrom(aMessage) != kErrorNone, res = kNotTranslated);
208 
209     if (mIp4Cidr.mLength == 0)
210     {
211         // The NAT64 translation is bypassed (will be handled externally)
212         LogWarn("incoming message is an IPv4 datagram but no IPv4 CIDR for NAT64 configured, drop");
213         ExitNow(res = kForward);
214     }
215 
216     if (!mNat64Prefix.IsValidNat64())
217     {
218         LogWarn("incoming message is an IPv4 datagram but no NAT64 prefix configured, drop");
219         ExitNow(res = kDrop);
220     }
221 
222     if (ip4Header.ParseFrom(aMessage) != kErrorNone)
223     {
224         LogWarn("incoming message is neither IPv4 nor an IPv6 datagram, drop");
225         dropReason = ErrorCounters::Reason::kIllegalPacket;
226         ExitNow(res = kDrop);
227     }
228 
229     mapping = FindMapping(ip4Header.GetDestination());
230     if (mapping == nullptr)
231     {
232         LogWarn("no mapping found for the IPv4 address");
233         dropReason = ErrorCounters::Reason::kNoMapping;
234         ExitNow(res = kDrop);
235     }
236 
237     aMessage.RemoveHeader(sizeof(Ip4::Header));
238 
239     ip6Header.Clear();
240     ip6Header.InitVersionTrafficClassFlow();
241     ip6Header.GetSource().SynthesizeFromIp4Address(mNat64Prefix, ip4Header.GetSource());
242     ip6Header.SetDestination(mapping->mIp6);
243     ip6Header.SetFlow(0);
244     ip6Header.SetHopLimit(ip4Header.GetTtl());
245 
246     // Note: TCP and UDP are the same for both IPv4 and IPv6 except for the checksum calculation, we will update the
247     // checksum in the payload later. However, we need to translate ICMPv6 messages to ICMP messages in IPv4.
248     switch (ip4Header.GetProtocol())
249     {
250     case Ip4::kProtoUdp:
251         ip6Header.SetNextHeader(Ip6::kProtoUdp);
252         res = kForward;
253         break;
254     case Ip4::kProtoTcp:
255         ip6Header.SetNextHeader(Ip6::kProtoTcp);
256         res = kForward;
257         break;
258     case Ip4::kProtoIcmp:
259         ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
260         SuccessOrExit(TranslateIcmp4(aMessage));
261         res = kForward;
262         break;
263     default:
264         dropReason = ErrorCounters::Reason::kUnsupportedProto;
265         ExitNow(res = kDrop);
266     }
267 
268     // res here must be kForward based on the switch above.
269     // TODO: Implement the logic for replying ICMP datagrams.
270     ip6Header.SetPayloadLength(aMessage.GetLength() - aMessage.GetOffset());
271     Checksum::UpdateMessageChecksum(aMessage, ip6Header.GetSource(), ip6Header.GetDestination(),
272                                     ip6Header.GetNextHeader());
273     if (aMessage.Prepend(ip6Header) != kErrorNone)
274     {
275         // This might happen when the platform failed to reserve enough space before the original IPv4 datagram.
276         LogWarn("failed to prepend IPv6 head to translated message");
277         ExitNow(res = kDrop);
278     }
279     aMessage.SetType(Message::kTypeIp6);
280     mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header));
281     mapping->mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header));
282 
283 exit:
284     if (res == Result::kDrop)
285     {
286         mErrorCounters.Count4To6(dropReason);
287     }
288 
289     return res;
290 }
291 
ToString(void) const292 Translator::AddressMapping::InfoString Translator::AddressMapping::ToString(void) const
293 {
294     InfoString string;
295 
296     string.Append("%s -> %s", mIp6.ToString().AsCString(), mIp4.ToString().AsCString());
297 
298     return string;
299 }
300 
CopyTo(otNat64AddressMapping & aMapping,TimeMilli aNow) const301 void Translator::AddressMapping::CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const
302 {
303     aMapping.mId       = mId;
304     aMapping.mIp4      = mIp4;
305     aMapping.mIp6      = mIp6;
306     aMapping.mCounters = mCounters;
307 
308     // We are removing expired mappings lazily, and an expired mapping might become active again before actually
309     // removed. Report the mapping to be "just expired" to avoid confusion.
310     if (mExpiry < aNow)
311     {
312         aMapping.mRemainingTimeMs = 0;
313     }
314     else
315     {
316         aMapping.mRemainingTimeMs = mExpiry - aNow;
317     }
318 }
319 
ReleaseMapping(AddressMapping & aMapping)320 void Translator::ReleaseMapping(AddressMapping &aMapping)
321 {
322     IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4));
323     mAddressMappingPool.Free(aMapping);
324     LogInfo("mapping removed: %s", aMapping.ToString().AsCString());
325 }
326 
ReleaseMappings(LinkedList<AddressMapping> & aMappings)327 uint16_t Translator::ReleaseMappings(LinkedList<AddressMapping> &aMappings)
328 {
329     uint16_t numRemoved = 0;
330 
331     for (AddressMapping *mapping = aMappings.Pop(); mapping != nullptr; mapping = aMappings.Pop())
332     {
333         numRemoved++;
334         ReleaseMapping(*mapping);
335     }
336 
337     return numRemoved;
338 }
339 
ReleaseExpiredMappings(void)340 uint16_t Translator::ReleaseExpiredMappings(void)
341 {
342     LinkedList<AddressMapping> idleMappings;
343 
344     mActiveAddressMappings.RemoveAllMatching(idleMappings, TimerMilli::GetNow());
345 
346     return ReleaseMappings(idleMappings);
347 }
348 
AllocateMapping(const Ip6::Address & aIp6Addr)349 Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Address &aIp6Addr)
350 {
351     AddressMapping *mapping = nullptr;
352 
353     // The address pool will be no larger than the mapping pool, so checking the address pool is enough.
354     if (mIp4AddressPool.IsEmpty())
355     {
356         // ReleaseExpiredMappings returns the number of mappings removed.
357         VerifyOrExit(ReleaseExpiredMappings() > 0);
358     }
359 
360     mapping = mAddressMappingPool.Allocate();
361     // We should get a valid item since address pool is no larger than the mapping pool, and the address pool is not
362     // empty.
363     VerifyOrExit(mapping != nullptr);
364 
365     mActiveAddressMappings.Push(*mapping);
366     mapping->mId = ++mNextMappingId;
367     mapping->mCounters.Clear();
368     mapping->mIp6 = aIp6Addr;
369     // PopBack must return a valid address since it is not empty.
370     mapping->mIp4 = *mIp4AddressPool.PopBack();
371     mapping->Touch(TimerMilli::GetNow());
372     LogInfo("mapping created: %s", mapping->ToString().AsCString());
373 
374 exit:
375     return mapping;
376 }
377 
FindOrAllocateMapping(const Ip6::Address & aIp6Addr)378 Translator::AddressMapping *Translator::FindOrAllocateMapping(const Ip6::Address &aIp6Addr)
379 {
380     AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp6Addr);
381 
382     // Exit if we found a valid mapping.
383     VerifyOrExit(mapping == nullptr);
384 
385     mapping = AllocateMapping(aIp6Addr);
386 
387 exit:
388     return mapping;
389 }
390 
FindMapping(const Ip4::Address & aIp4Addr)391 Translator::AddressMapping *Translator::FindMapping(const Ip4::Address &aIp4Addr)
392 {
393     AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp4Addr);
394 
395     if (mapping != nullptr)
396     {
397         mapping->Touch(TimerMilli::GetNow());
398     }
399     return mapping;
400 }
401 
TranslateIcmp4(Message & aMessage)402 Error Translator::TranslateIcmp4(Message &aMessage)
403 {
404     Error             err = kErrorNone;
405     Ip4::Icmp::Header icmp4Header;
406     Ip6::Icmp::Header icmp6Header;
407 
408     // TODO: Implement the translation of other ICMP messages.
409 
410     // Note: The caller consumed the IP header, so the ICMP header is at offset 0.
411     SuccessOrExit(err = aMessage.Read(0, icmp4Header));
412     switch (icmp4Header.GetType())
413     {
414     case Ip4::Icmp::Header::Type::kTypeEchoReply:
415     {
416         // The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as
417         // ICMP6 header and set the message type.
418         SuccessOrExit(err = aMessage.Read(0, icmp6Header));
419         icmp6Header.SetType(Ip6::Icmp::Header::Type::kTypeEchoReply);
420         aMessage.Write(0, icmp6Header);
421         break;
422     }
423     default:
424         err = kErrorInvalidArgs;
425         break;
426     }
427 
428 exit:
429     return err;
430 }
431 
TranslateIcmp6(Message & aMessage)432 Error Translator::TranslateIcmp6(Message &aMessage)
433 {
434     Error             err = kErrorNone;
435     Ip4::Icmp::Header icmp4Header;
436     Ip6::Icmp::Header icmp6Header;
437 
438     // TODO: Implement the translation of other ICMP messages.
439 
440     // Note: The caller have consumed the IP header, so the ICMP header is at offset 0.
441     SuccessOrExit(err = aMessage.Read(0, icmp6Header));
442     switch (icmp6Header.GetType())
443     {
444     case Ip6::Icmp::Header::Type::kTypeEchoRequest:
445     {
446         // The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as
447         // ICMP6 header and set the message type.
448         SuccessOrExit(err = aMessage.Read(0, icmp4Header));
449         icmp4Header.SetType(Ip4::Icmp::Header::Type::kTypeEchoRequest);
450         aMessage.Write(0, icmp4Header);
451         break;
452     }
453     default:
454         err = kErrorInvalidArgs;
455         break;
456     }
457 
458 exit:
459     return err;
460 }
461 
SetIp4Cidr(const Ip4::Cidr & aCidr)462 Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr)
463 {
464     Error err = kErrorNone;
465 
466     uint32_t numberOfHosts;
467     uint32_t hostIdBegin;
468 
469     VerifyOrExit(aCidr.mLength > 0 && aCidr.mLength <= 32, err = kErrorInvalidArgs);
470 
471     VerifyOrExit(mIp4Cidr != aCidr);
472 
473     // Avoid using the 0s and 1s in the host id of an address, but what if the user provides us with /32 or /31
474     // addresses?
475     if (aCidr.mLength == 32)
476     {
477         hostIdBegin   = 0;
478         numberOfHosts = 1;
479     }
480     else if (aCidr.mLength == 31)
481     {
482         hostIdBegin   = 0;
483         numberOfHosts = 2;
484     }
485     else
486     {
487         hostIdBegin   = 1;
488         numberOfHosts = static_cast<uint32_t>((1 << (Ip4::Address::kSize * 8 - aCidr.mLength)) - 2);
489     }
490     numberOfHosts = OT_MIN(numberOfHosts, kAddressMappingPoolSize);
491 
492     mAddressMappingPool.FreeAll();
493     mActiveAddressMappings.Clear();
494     mIp4AddressPool.Clear();
495 
496     for (uint32_t i = 0; i < numberOfHosts; i++)
497     {
498         Ip4::Address addr;
499 
500         addr.SynthesizeFromCidrAndHost(aCidr, i + hostIdBegin);
501         IgnoreError(mIp4AddressPool.PushBack(addr));
502     }
503 
504     LogInfo("IPv4 CIDR for NAT64: %s (actual address pool: %s - %s, %lu addresses)", aCidr.ToString().AsCString(),
505             mIp4AddressPool.Front()->ToString().AsCString(), mIp4AddressPool.Back()->ToString().AsCString(),
506             ToUlong(numberOfHosts));
507     mIp4Cidr = aCidr;
508 
509     UpdateState();
510 
511     // Notify the platform when the CIDR is changed.
512     Get<Notifier>().Signal(kEventNat64TranslatorStateChanged);
513 
514 exit:
515     return err;
516 }
517 
ClearIp4Cidr(void)518 void Translator::ClearIp4Cidr(void)
519 {
520     mIp4Cidr.Clear();
521     mAddressMappingPool.FreeAll();
522     mActiveAddressMappings.Clear();
523     mIp4AddressPool.Clear();
524 
525     UpdateState();
526 }
527 
SetNat64Prefix(const Ip6::Prefix & aNat64Prefix)528 void Translator::SetNat64Prefix(const Ip6::Prefix &aNat64Prefix)
529 {
530     if (aNat64Prefix.GetLength() == 0)
531     {
532         ClearNat64Prefix();
533     }
534     else if (mNat64Prefix != aNat64Prefix)
535     {
536         LogInfo("IPv6 Prefix for NAT64 updated to %s", aNat64Prefix.ToString().AsCString());
537         mNat64Prefix = aNat64Prefix;
538         UpdateState();
539     }
540 }
541 
ClearNat64Prefix(void)542 void Translator::ClearNat64Prefix(void)
543 {
544     VerifyOrExit(mNat64Prefix.GetLength() != 0);
545     mNat64Prefix.Clear();
546     LogInfo("IPv6 Prefix for NAT64 cleared");
547     UpdateState();
548 
549 exit:
550     return;
551 }
552 
HandleMappingExpirerTimer(void)553 void Translator::HandleMappingExpirerTimer(void)
554 {
555     uint16_t numReleased = ReleaseExpiredMappings();
556 
557     LogInfo("Released %u expired mappings", numReleased);
558 
559     mMappingExpirerTimer.Start(kAddressMappingIdleTimeoutMsec);
560 
561     OT_UNUSED_VARIABLE(numReleased);
562 }
563 
InitAddressMappingIterator(AddressMappingIterator & aIterator)564 void Translator::InitAddressMappingIterator(AddressMappingIterator &aIterator)
565 {
566     aIterator.mPtr = mActiveAddressMappings.GetHead();
567 }
568 
GetNextAddressMapping(AddressMappingIterator & aIterator,otNat64AddressMapping & aMapping)569 Error Translator::GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping)
570 {
571     Error           err  = kErrorNotFound;
572     TimeMilli       now  = TimerMilli::GetNow();
573     AddressMapping *item = static_cast<AddressMapping *>(aIterator.mPtr);
574 
575     VerifyOrExit(item != nullptr);
576 
577     item->CopyTo(aMapping, now);
578     aIterator.mPtr = item->GetNext();
579     err            = kErrorNone;
580 
581 exit:
582     return err;
583 }
584 
GetIp4Cidr(Ip4::Cidr & aCidr)585 Error Translator::GetIp4Cidr(Ip4::Cidr &aCidr)
586 {
587     Error err = kErrorNone;
588 
589     VerifyOrExit(mIp4Cidr.mLength > 0, err = kErrorNotFound);
590     aCidr = mIp4Cidr;
591 
592 exit:
593     return err;
594 }
595 
GetIp6Prefix(Ip6::Prefix & aPrefix)596 Error Translator::GetIp6Prefix(Ip6::Prefix &aPrefix)
597 {
598     Error err = kErrorNone;
599 
600     VerifyOrExit(mNat64Prefix.mLength > 0, err = kErrorNotFound);
601     aPrefix = mNat64Prefix;
602 
603 exit:
604     return err;
605 }
606 
Count6To4Packet(uint8_t aProtocol,uint64_t aPacketSize)607 void Translator::ProtocolCounters::Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize)
608 {
609     switch (aProtocol)
610     {
611     case Ip6::kProtoUdp:
612         mUdp.m6To4Packets++;
613         mUdp.m6To4Bytes += aPacketSize;
614         break;
615     case Ip6::kProtoTcp:
616         mTcp.m6To4Packets++;
617         mTcp.m6To4Bytes += aPacketSize;
618         break;
619     case Ip6::kProtoIcmp6:
620         mIcmp.m6To4Packets++;
621         mIcmp.m6To4Bytes += aPacketSize;
622         break;
623     }
624 
625     mTotal.m6To4Packets++;
626     mTotal.m6To4Bytes += aPacketSize;
627 }
628 
Count4To6Packet(uint8_t aProtocol,uint64_t aPacketSize)629 void Translator::ProtocolCounters::Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize)
630 {
631     switch (aProtocol)
632     {
633     case Ip4::kProtoUdp:
634         mUdp.m4To6Packets++;
635         mUdp.m4To6Bytes += aPacketSize;
636         break;
637     case Ip4::kProtoTcp:
638         mTcp.m4To6Packets++;
639         mTcp.m4To6Bytes += aPacketSize;
640         break;
641     case Ip4::kProtoIcmp:
642         mIcmp.m4To6Packets++;
643         mIcmp.m4To6Bytes += aPacketSize;
644         break;
645     }
646 
647     mTotal.m4To6Packets++;
648     mTotal.m4To6Bytes += aPacketSize;
649 }
650 
UpdateState(void)651 void Translator::UpdateState(void)
652 {
653     State newState;
654 
655     if (mEnabled)
656     {
657         if (mIp4Cidr.mLength > 0 && mNat64Prefix.IsValidNat64())
658         {
659             newState = kStateActive;
660         }
661         else
662         {
663             newState = kStateNotRunning;
664         }
665     }
666     else
667     {
668         newState = kStateDisabled;
669     }
670 
671     SuccessOrExit(Get<Notifier>().Update(mState, newState, kEventNat64TranslatorStateChanged));
672     LogInfo("NAT64 translator is now %s", StateToString(mState));
673 
674 exit:
675     return;
676 }
677 
SetEnabled(bool aEnabled)678 void Translator::SetEnabled(bool aEnabled)
679 {
680     VerifyOrExit(mEnabled != aEnabled);
681     mEnabled = aEnabled;
682 
683     if (!aEnabled)
684     {
685         ReleaseMappings(mActiveAddressMappings);
686     }
687 
688     UpdateState();
689 
690 exit:
691     return;
692 }
693 
694 } // namespace Nat64
695 } // namespace ot
696 
697 #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
698