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