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