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