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