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