• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::Headers          ip6Headers;
115     Ip4::Header           ip4Header;
116     uint16_t              srcPortOrId = 0;
117     AddressMapping       *mapping     = nullptr;
118 
119     if (mIp4Cidr.mLength == 0 || !mNat64Prefix.IsValidNat64())
120     {
121         ExitNow(res = kNotTranslated);
122     }
123 
124     // ParseFrom will do basic checks for the message, including the message length and IP protocol version.
125     if (ip6Headers.ParseFrom(aMessage) != kErrorNone)
126     {
127         LogWarn("outgoing datagram is not a valid IPv6 datagram, drop");
128         dropReason = ErrorCounters::Reason::kIllegalPacket;
129         ExitNow(res = kDrop);
130     }
131 
132     if (!ip6Headers.GetDestinationAddress().MatchesPrefix(mNat64Prefix))
133     {
134         ExitNow(res = kNotTranslated);
135     }
136 
137     mapping = FindOrAllocateMapping(ip6Headers);
138     if (mapping == nullptr)
139     {
140         LogWarn("failed to get a mapping for %s (mapping pool full?)",
141                 ip6Headers.GetSourceAddress().ToString().AsCString());
142         dropReason = ErrorCounters::Reason::kNoMapping;
143         ExitNow(res = kDrop);
144     }
145 
146 #if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
147     srcPortOrId = mapping->mTranslatedPortOrId;
148 #else
149     srcPortOrId           = ip6Headers.IsIcmp6() ? ip6Headers.GetIcmpHeader().GetId() : ip6Headers.GetSourcePort();
150 #endif
151 
152     aMessage.RemoveHeader(sizeof(Ip6::Header));
153 
154     ip4Header.Clear();
155     ip4Header.InitVersionIhl();
156     ip4Header.SetSource(mapping->mIp4);
157     ip4Header.GetDestination().ExtractFromIp6Address(mNat64Prefix.mLength, ip6Headers.GetDestinationAddress());
158     ip4Header.SetTtl(ip6Headers.GetIpHopLimit());
159     ip4Header.SetIdentification(0);
160 
161     switch (ip6Headers.GetIpProto())
162     {
163     // The IP header is consumed , so the next header is at offset 0.
164     case Ip6::kProtoUdp:
165         ip4Header.SetProtocol(Ip4::kProtoUdp);
166         ip6Headers.SetSourcePort(srcPortOrId);
167         aMessage.Write(0, ip6Headers.GetUdpHeader());
168         res = kForward;
169         break;
170     case Ip6::kProtoTcp:
171         ip4Header.SetProtocol(Ip4::kProtoTcp);
172         ip6Headers.SetSourcePort(srcPortOrId);
173         aMessage.Write(0, ip6Headers.GetTcpHeader());
174         res = kForward;
175         break;
176     case Ip6::kProtoIcmp6:
177         ip4Header.SetProtocol(Ip4::kProtoIcmp);
178         SuccessOrExit(TranslateIcmp6(aMessage, srcPortOrId));
179         res = kForward;
180         break;
181     default:
182         dropReason = ErrorCounters::Reason::kUnsupportedProto;
183         ExitNow(res = kDrop);
184     }
185 
186     // res here must be kForward based on the switch above.
187     // TODO: Implement the logic for replying ICMP messages.
188     ip4Header.SetTotalLength(sizeof(Ip4::Header) + aMessage.GetLength() - aMessage.GetOffset());
189     Checksum::UpdateMessageChecksum(aMessage, ip4Header.GetSource(), ip4Header.GetDestination(),
190                                     ip4Header.GetProtocol());
191     Checksum::UpdateIp4HeaderChecksum(ip4Header);
192     if (aMessage.Prepend(ip4Header) != kErrorNone)
193     {
194         // This should never happen since the IPv4 header is shorter than the IPv6 header.
195         LogCrit("failed to prepend IPv4 head to translated message");
196         ExitNow(res = kDrop);
197     }
198     aMessage.SetType(Message::kTypeIp4);
199     mCounters.Count6To4Packet(ip6Headers.GetIpProto(), ip6Headers.GetIpLength());
200     mapping->mCounters.Count6To4Packet(ip6Headers.GetIpProto(), ip6Headers.GetIpLength());
201 
202 exit:
203     if (res == Result::kDrop)
204     {
205         mErrorCounters.Count6To4(dropReason);
206     }
207     return res;
208 }
209 
TranslateToIp6(Message & aMessage)210 Translator::Result Translator::TranslateToIp6(Message &aMessage)
211 {
212     Result                res        = Result::kDrop;
213     ErrorCounters::Reason dropReason = ErrorCounters::kUnknown;
214     Ip6::Header           ip6Header;
215     Ip4::Headers          ip4Headers;
216     uint16_t              dstPortOrId = 0;
217     AddressMapping       *mapping     = nullptr;
218 
219     // Ip6::Header::ParseFrom may return an error value when the incoming message is an IPv4 datagram.
220     // If the message is already an IPv6 datagram, forward it directly.
221     VerifyOrExit(ip6Header.ParseFrom(aMessage) != kErrorNone, res = kNotTranslated);
222 
223     if (mIp4Cidr.mLength == 0)
224     {
225         // The NAT64 translation is bypassed (will be handled externally)
226         LogWarn("incoming message is an IPv4 datagram but no IPv4 CIDR for NAT64 configured, drop");
227         ExitNow(res = kForward);
228     }
229 
230     if (!mNat64Prefix.IsValidNat64())
231     {
232         LogWarn("incoming message is an IPv4 datagram but no NAT64 prefix configured, drop");
233         ExitNow(res = kDrop);
234     }
235 
236     if (ip4Headers.ParseFrom(aMessage) != kErrorNone)
237     {
238         LogWarn("incoming message is neither IPv4 nor an IPv6 datagram, drop");
239         dropReason = ErrorCounters::Reason::kIllegalPacket;
240         ExitNow(res = kDrop);
241     }
242 
243     mapping = FindMapping(ip4Headers);
244 
245     if (mapping == nullptr)
246     {
247         LogWarn("no mapping found for the IPv4 address");
248         dropReason = ErrorCounters::Reason::kNoMapping;
249         ExitNow(res = kDrop);
250     }
251 
252 #if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
253     dstPortOrId = mapping->mSrcPortOrId;
254 #else
255     dstPortOrId           = ip4Headers.IsIcmp4() ? ip4Headers.GetIcmpHeader().GetId() : ip4Headers.GetDestinationPort();
256 #endif
257 
258     aMessage.RemoveHeader(sizeof(Ip4::Header));
259 
260     ip6Header.Clear();
261     ip6Header.InitVersionTrafficClassFlow();
262     ip6Header.GetSource().SynthesizeFromIp4Address(mNat64Prefix, ip4Headers.GetSourceAddress());
263     ip6Header.SetDestination(mapping->mIp6);
264     ip6Header.SetFlow(0);
265     ip6Header.SetHopLimit(ip4Headers.GetIpTtl());
266 
267     // Note: TCP and UDP are the same for both IPv4 and IPv6 except for the checksum calculation, we will update the
268     // checksum in the payload later. However, we need to translate ICMPv6 messages to ICMP messages in IPv4.
269     switch (ip4Headers.GetIpProto())
270     {
271     // The IP header is consumed , so the next header is at offset 0.
272     case Ip4::kProtoUdp:
273         ip6Header.SetNextHeader(Ip6::kProtoUdp);
274         ip4Headers.SetDestinationPort(dstPortOrId);
275         aMessage.Write(0, ip4Headers.GetUdpHeader());
276         res = kForward;
277         break;
278     case Ip4::kProtoTcp:
279         ip6Header.SetNextHeader(Ip6::kProtoTcp);
280         ip4Headers.SetDestinationPort(dstPortOrId);
281         aMessage.Write(0, ip4Headers.GetTcpHeader());
282         res = kForward;
283         break;
284     case Ip4::kProtoIcmp:
285         ip6Header.SetNextHeader(Ip6::kProtoIcmp6);
286         SuccessOrExit(TranslateIcmp4(aMessage, dstPortOrId));
287         res = kForward;
288         break;
289     default:
290         dropReason = ErrorCounters::Reason::kUnsupportedProto;
291         ExitNow(res = kDrop);
292     }
293 
294     // res here must be kForward based on the switch above.
295     // TODO: Implement the logic for replying ICMP datagrams.
296     ip6Header.SetPayloadLength(aMessage.GetLength() - aMessage.GetOffset());
297     Checksum::UpdateMessageChecksum(aMessage, ip6Header.GetSource(), ip6Header.GetDestination(),
298                                     ip6Header.GetNextHeader());
299     if (aMessage.Prepend(ip6Header) != kErrorNone)
300     {
301         // This might happen when the platform failed to reserve enough space before the original IPv4 datagram.
302         LogWarn("failed to prepend IPv6 head to translated message");
303         ExitNow(res = kDrop);
304     }
305     aMessage.SetType(Message::kTypeIp6);
306     mCounters.Count4To6Packet(ip4Headers.GetIpProto(), ip4Headers.GetIpLength() - sizeof(Ip4::Header));
307     mapping->mCounters.Count4To6Packet(ip4Headers.GetIpProto(), ip4Headers.GetIpLength() - sizeof(Ip4::Header));
308 
309 exit:
310     if (res == Result::kDrop)
311     {
312         mErrorCounters.Count4To6(dropReason);
313     }
314 
315     return res;
316 }
317 
ToString(void) const318 Translator::AddressMapping::InfoString Translator::AddressMapping::ToString(void) const
319 {
320     InfoString string;
321 
322     string.Append("%s -> %s", mIp6.ToString().AsCString(), mIp4.ToString().AsCString());
323 
324     return string;
325 }
326 
CopyTo(otNat64AddressMapping & aMapping,TimeMilli aNow) const327 void Translator::AddressMapping::CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const
328 {
329     aMapping.mId                 = mId;
330     aMapping.mIp4                = mIp4;
331     aMapping.mIp6                = mIp6;
332     aMapping.mSrcPortOrId        = mSrcPortOrId;
333     aMapping.mTranslatedPortOrId = mTranslatedPortOrId;
334     aMapping.mCounters           = mCounters;
335 
336     // We are removing expired mappings lazily, and an expired mapping might become active again before actually
337     // removed. Report the mapping to be "just expired" to avoid confusion.
338     if (mExpiry < aNow)
339     {
340         aMapping.mRemainingTimeMs = 0;
341     }
342     else
343     {
344         aMapping.mRemainingTimeMs = mExpiry - aNow;
345     }
346 }
347 
ReleaseMapping(AddressMapping & aMapping)348 void Translator::ReleaseMapping(AddressMapping &aMapping)
349 {
350     if (mIp4Cidr.mLength <= kMaxCidrLenForValidAddrPool)
351     {
352         // IPv4 addresses are allocated from the pool only when the pool size is above a minimum value.
353         // Otherwise use just the first address from the list and we are not removing it from the array.
354         IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4));
355     }
356     mAddressMappingPool.Free(aMapping);
357     LogInfo("mapping removed: %s", aMapping.ToString().AsCString());
358 }
359 
ReleaseMappings(LinkedList<AddressMapping> & aMappings)360 uint16_t Translator::ReleaseMappings(LinkedList<AddressMapping> &aMappings)
361 {
362     uint16_t numRemoved = 0;
363 
364     for (AddressMapping *mapping = aMappings.Pop(); mapping != nullptr; mapping = aMappings.Pop())
365     {
366         numRemoved++;
367         ReleaseMapping(*mapping);
368     }
369 
370     return numRemoved;
371 }
372 
ReleaseExpiredMappings(void)373 uint16_t Translator::ReleaseExpiredMappings(void)
374 {
375     LinkedList<AddressMapping> idleMappings;
376 
377     mActiveAddressMappings.RemoveAllMatching(idleMappings, TimerMilli::GetNow());
378 
379     return ReleaseMappings(idleMappings);
380 }
381 #if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
AllocateSourcePort(uint16_t aSrcPort)382 uint16_t Translator::AllocateSourcePort(uint16_t aSrcPort)
383 {
384     // The translated port is randomly allocated from the range of dynamic or private ports (RFC 7605 section 4).
385     // In this way, we will not pick a random port that could be a well-known port preventing an unknown situation on
386     // the receiver side.
387     uint16_t retPort;
388 
389     do
390     {
391         retPort = Random::NonCrypto::GetUint16InRange(kTranslationPortRangeStart, kTranslationPortRangeEnd);
392         // The NAT64 SHOULD preserve the port parity (odd/even), as per Section 4.2.2 of [RFC4787]).
393         // Determine if original and allocated port have different parity
394         if (((aSrcPort ^ retPort) & 1) == 1)
395         {
396             retPort++;
397         }
398     } while (mActiveAddressMappings.ContainsMatching(retPort));
399 
400     return retPort;
401 }
402 #endif
403 
AllocateMapping(const Ip6::Headers & aIp6Headers)404 Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Headers &aIp6Headers)
405 {
406     AddressMapping *mapping = nullptr;
407     Ip4::Address    ip4Addr;
408 
409     // The NAT64 translator can work in 2 ways, either with a single IPv4 address or a larger pool of addresses. There
410     // is also the corner case where the address pool is generated from a big CIDR length and the number of available
411     // IPv4 addresses is not big enough to apply a 1 to 1 translation from IPv6 to IPv4 address. When operating in the
412     // first case, there is no need to manage the address pool and all active mappings will use 1 single address (or the
413     // limited number alternatively). If a larger pool is available each active mapping will use a separate IPv4
414     // address.
415     if (mIp4Cidr.mLength > kMaxCidrLenForValidAddrPool)
416     {
417         // TODO: add logic to cycle between available IPv4 addresses
418         ip4Addr = *mIp4AddressPool.At(0);
419     }
420     else
421     {
422         if (mIp4AddressPool.IsEmpty())
423         {
424             // ReleaseExpiredMappings returns the number of mappings removed.
425             VerifyOrExit(ReleaseExpiredMappings() > 0);
426         }
427         ip4Addr = *mIp4AddressPool.PopBack();
428     }
429 
430     mapping = mAddressMappingPool.Allocate();
431     // We should get a valid item, there is enough space in the mapping pool. Otherwise return null and fail the
432     // translation.
433     VerifyOrExit(mapping != nullptr);
434 
435     mActiveAddressMappings.Push(*mapping);
436     mapping->mCounters.Clear();
437     mapping->mId  = ++mNextMappingId;
438     mapping->mIp6 = aIp6Headers.GetSourceAddress();
439     mapping->mIp4 = ip4Addr;
440 #if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
441     mapping->mSrcPortOrId = aIp6Headers.IsIcmp6() ? aIp6Headers.GetIcmpHeader().GetId() : aIp6Headers.GetSourcePort();
442     // Allocate a unique source port or ICMP Id
443     mapping->mTranslatedPortOrId = AllocateSourcePort(mapping->mSrcPortOrId);
444 #else
445     mapping->mSrcPortOrId = 0;
446     mapping->mTranslatedPortOrId = 0;
447 #endif
448     mapping->Touch(TimerMilli::GetNow(), aIp6Headers.GetIpProto());
449     LogInfo("mapping created: %s", mapping->ToString().AsCString());
450 
451 exit:
452     return mapping;
453 }
454 
FindOrAllocateMapping(const Ip6::Headers & aIp6Headers)455 Translator::AddressMapping *Translator::FindOrAllocateMapping(const Ip6::Headers &aIp6Headers)
456 {
457 #if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
458     uint16_t srcPortOrId    = aIp6Headers.IsIcmp6() ? aIp6Headers.GetIcmpHeader().GetId() : aIp6Headers.GetSourcePort();
459     AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp6Headers.GetSourceAddress(), srcPortOrId);
460 #else
461     AddressMapping *mapping      = mActiveAddressMappings.FindMatching(aIp6Headers.GetSourceAddress());
462 #endif
463 
464     // Exit if we found a valid mapping.
465     VerifyOrExit(mapping == nullptr);
466 
467     mapping = AllocateMapping(aIp6Headers);
468 
469 exit:
470     return mapping;
471 }
472 
FindMapping(const Ip4::Headers & aIp4Headers)473 Translator::AddressMapping *Translator::FindMapping(const Ip4::Headers &aIp4Headers)
474 {
475     uint16_t dstPortOrId =
476         aIp4Headers.IsIcmp4() ? aIp4Headers.GetIcmpHeader().GetId() : aIp4Headers.GetDestinationPort();
477 
478 #if OPENTHREAD_CONFIG_NAT64_PORT_TRANSLATION_ENABLE
479     AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp4Headers.GetDestinationAddress(), dstPortOrId);
480 #else
481     AddressMapping *mapping      = mActiveAddressMappings.FindMatching(aIp4Headers.GetDestinationAddress());
482     OT_UNUSED_VARIABLE(dstPortOrId);
483 #endif
484 
485     if (mapping != nullptr)
486     {
487         mapping->Touch(TimerMilli::GetNow(), aIp4Headers.GetIpProto());
488     }
489     return mapping;
490 }
491 
Touch(TimeMilli aNow,uint8_t aProtocol)492 void Translator::AddressMapping::Touch(TimeMilli aNow, uint8_t aProtocol)
493 {
494     if ((aProtocol == Ip6::kProtoIcmp6) || (aProtocol == Ip4::kProtoIcmp))
495     {
496         mExpiry = aNow + kAddressMappingIcmpIdleTimeoutMsec;
497     }
498     else
499     {
500         mExpiry = aNow + kAddressMappingIdleTimeoutMsec;
501     }
502 }
503 
TranslateIcmp4(Message & aMessage,uint16_t aOriginalId)504 Error Translator::TranslateIcmp4(Message &aMessage, uint16_t aOriginalId)
505 {
506     Error             err = kErrorNone;
507     Ip4::Icmp::Header icmp4Header;
508     Ip6::Icmp::Header icmp6Header;
509 
510     // TODO: Implement the translation of other ICMP messages.
511 
512     // Note: The caller consumed the IP header, so the ICMP header is at offset 0.
513     SuccessOrExit(err = aMessage.Read(0, icmp4Header));
514     switch (icmp4Header.GetType())
515     {
516     case Ip4::Icmp::Header::Type::kTypeEchoReply:
517     {
518         // The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as
519         // ICMP6 header and set the message type.
520         SuccessOrExit(err = aMessage.Read(0, icmp6Header));
521         icmp6Header.SetType(Ip6::Icmp::Header::Type::kTypeEchoReply);
522         icmp6Header.SetId(aOriginalId);
523         aMessage.Write(0, icmp6Header);
524         break;
525     }
526     default:
527         err = kErrorInvalidArgs;
528         break;
529     }
530 
531 exit:
532     return err;
533 }
534 
TranslateIcmp6(Message & aMessage,uint16_t aTranslatedId)535 Error Translator::TranslateIcmp6(Message &aMessage, uint16_t aTranslatedId)
536 {
537     Error             err = kErrorNone;
538     Ip4::Icmp::Header icmp4Header;
539     Ip6::Icmp::Header icmp6Header;
540 
541     // TODO: Implement the translation of other ICMP messages.
542 
543     // Note: The caller have consumed the IP header, so the ICMP header is at offset 0.
544     SuccessOrExit(err = aMessage.Read(0, icmp6Header));
545     switch (icmp6Header.GetType())
546     {
547     case Ip6::Icmp::Header::Type::kTypeEchoRequest:
548     {
549         // The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as
550         // ICMP6 header and set the message type.
551         SuccessOrExit(err = aMessage.Read(0, icmp4Header));
552         icmp4Header.SetType(Ip4::Icmp::Header::Type::kTypeEchoRequest);
553         icmp4Header.SetId(aTranslatedId);
554         aMessage.Write(0, icmp4Header);
555         break;
556     }
557     default:
558         err = kErrorInvalidArgs;
559         break;
560     }
561 
562 exit:
563     return err;
564 }
565 
SetIp4Cidr(const Ip4::Cidr & aCidr)566 Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr)
567 {
568     Error err = kErrorNone;
569 
570     uint32_t numberOfHosts;
571     uint32_t hostIdBegin;
572 
573     VerifyOrExit(aCidr.mLength > 0 && aCidr.mLength <= 32, err = kErrorInvalidArgs);
574 
575     VerifyOrExit(mIp4Cidr != aCidr);
576 
577     // Avoid using the 0s and 1s in the host id of an address, but what if the user provides us with /32 or /31
578     // addresses?
579     if (aCidr.mLength == 32)
580     {
581         hostIdBegin   = 0;
582         numberOfHosts = 1;
583     }
584     else if (aCidr.mLength == 31)
585     {
586         hostIdBegin   = 0;
587         numberOfHosts = 2;
588     }
589     else
590     {
591         hostIdBegin   = 1;
592         numberOfHosts = static_cast<uint32_t>((1 << (Ip4::Address::kSize * 8 - aCidr.mLength)) - 2);
593     }
594     numberOfHosts = OT_MIN(numberOfHosts, kAddressMappingPoolSize);
595 
596     mAddressMappingPool.FreeAll();
597     mActiveAddressMappings.Clear();
598     mIp4AddressPool.Clear();
599 
600     for (uint32_t i = 0; i < numberOfHosts; i++)
601     {
602         Ip4::Address addr;
603 
604         addr.SynthesizeFromCidrAndHost(aCidr, i + hostIdBegin);
605         IgnoreError(mIp4AddressPool.PushBack(addr));
606     }
607 
608     LogInfo("IPv4 CIDR for NAT64: %s (actual address pool: %s - %s, %lu addresses)", aCidr.ToString().AsCString(),
609             mIp4AddressPool.Front()->ToString().AsCString(), mIp4AddressPool.Back()->ToString().AsCString(),
610             ToUlong(numberOfHosts));
611     mIp4Cidr = aCidr;
612 
613     UpdateState();
614 
615     // Notify the platform when the CIDR is changed.
616     Get<Notifier>().Signal(kEventNat64TranslatorStateChanged);
617 
618 exit:
619     return err;
620 }
621 
ClearIp4Cidr(void)622 void Translator::ClearIp4Cidr(void)
623 {
624     mIp4Cidr.Clear();
625     mAddressMappingPool.FreeAll();
626     mActiveAddressMappings.Clear();
627     mIp4AddressPool.Clear();
628 
629     UpdateState();
630 }
631 
SetNat64Prefix(const Ip6::Prefix & aNat64Prefix)632 void Translator::SetNat64Prefix(const Ip6::Prefix &aNat64Prefix)
633 {
634     if (aNat64Prefix.GetLength() == 0)
635     {
636         ClearNat64Prefix();
637     }
638     else if (mNat64Prefix != aNat64Prefix)
639     {
640         LogInfo("IPv6 Prefix for NAT64 updated to %s", aNat64Prefix.ToString().AsCString());
641         mNat64Prefix = aNat64Prefix;
642         UpdateState();
643     }
644 }
645 
ClearNat64Prefix(void)646 void Translator::ClearNat64Prefix(void)
647 {
648     VerifyOrExit(mNat64Prefix.GetLength() != 0);
649     mNat64Prefix.Clear();
650     LogInfo("IPv6 Prefix for NAT64 cleared");
651     UpdateState();
652 
653 exit:
654     return;
655 }
656 
HandleMappingExpirerTimer(void)657 void Translator::HandleMappingExpirerTimer(void)
658 {
659     uint16_t numReleased = ReleaseExpiredMappings();
660 
661     LogInfo("Released %u expired mappings", numReleased);
662 
663     mMappingExpirerTimer.Start(Min(kAddressMappingIcmpIdleTimeoutMsec, kAddressMappingIdleTimeoutMsec));
664 
665     OT_UNUSED_VARIABLE(numReleased);
666 }
667 
InitAddressMappingIterator(AddressMappingIterator & aIterator)668 void Translator::InitAddressMappingIterator(AddressMappingIterator &aIterator)
669 {
670     aIterator.mPtr = mActiveAddressMappings.GetHead();
671 }
672 
GetNextAddressMapping(AddressMappingIterator & aIterator,otNat64AddressMapping & aMapping)673 Error Translator::GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping)
674 {
675     Error           err  = kErrorNotFound;
676     TimeMilli       now  = TimerMilli::GetNow();
677     AddressMapping *item = static_cast<AddressMapping *>(aIterator.mPtr);
678 
679     VerifyOrExit(item != nullptr);
680 
681     item->CopyTo(aMapping, now);
682     aIterator.mPtr = item->GetNext();
683     err            = kErrorNone;
684 
685 exit:
686     return err;
687 }
688 
GetIp4Cidr(Ip4::Cidr & aCidr)689 Error Translator::GetIp4Cidr(Ip4::Cidr &aCidr)
690 {
691     Error err = kErrorNone;
692 
693     VerifyOrExit(mIp4Cidr.mLength > 0, err = kErrorNotFound);
694     aCidr = mIp4Cidr;
695 
696 exit:
697     return err;
698 }
699 
GetIp6Prefix(Ip6::Prefix & aPrefix)700 Error Translator::GetIp6Prefix(Ip6::Prefix &aPrefix)
701 {
702     Error err = kErrorNone;
703 
704     VerifyOrExit(mNat64Prefix.mLength > 0, err = kErrorNotFound);
705     aPrefix = mNat64Prefix;
706 
707 exit:
708     return err;
709 }
710 
Count6To4Packet(uint8_t aProtocol,uint64_t aPacketSize)711 void Translator::ProtocolCounters::Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize)
712 {
713     switch (aProtocol)
714     {
715     case Ip6::kProtoUdp:
716         mUdp.m6To4Packets++;
717         mUdp.m6To4Bytes += aPacketSize;
718         break;
719     case Ip6::kProtoTcp:
720         mTcp.m6To4Packets++;
721         mTcp.m6To4Bytes += aPacketSize;
722         break;
723     case Ip6::kProtoIcmp6:
724         mIcmp.m6To4Packets++;
725         mIcmp.m6To4Bytes += aPacketSize;
726         break;
727     }
728 
729     mTotal.m6To4Packets++;
730     mTotal.m6To4Bytes += aPacketSize;
731 }
732 
Count4To6Packet(uint8_t aProtocol,uint64_t aPacketSize)733 void Translator::ProtocolCounters::Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize)
734 {
735     switch (aProtocol)
736     {
737     case Ip4::kProtoUdp:
738         mUdp.m4To6Packets++;
739         mUdp.m4To6Bytes += aPacketSize;
740         break;
741     case Ip4::kProtoTcp:
742         mTcp.m4To6Packets++;
743         mTcp.m4To6Bytes += aPacketSize;
744         break;
745     case Ip4::kProtoIcmp:
746         mIcmp.m4To6Packets++;
747         mIcmp.m4To6Bytes += aPacketSize;
748         break;
749     }
750 
751     mTotal.m4To6Packets++;
752     mTotal.m4To6Bytes += aPacketSize;
753 }
754 
UpdateState(void)755 void Translator::UpdateState(void)
756 {
757     State newState;
758 
759     if (mEnabled)
760     {
761         if (mIp4Cidr.mLength > 0 && mNat64Prefix.IsValidNat64())
762         {
763             newState = kStateActive;
764         }
765         else
766         {
767             newState = kStateNotRunning;
768         }
769     }
770     else
771     {
772         newState = kStateDisabled;
773     }
774 
775     SuccessOrExit(Get<Notifier>().Update(mState, newState, kEventNat64TranslatorStateChanged));
776     LogInfo("NAT64 translator is now %s", StateToString(mState));
777 
778 exit:
779     return;
780 }
781 
SetEnabled(bool aEnabled)782 void Translator::SetEnabled(bool aEnabled)
783 {
784     VerifyOrExit(mEnabled != aEnabled);
785     mEnabled = aEnabled;
786 
787     if (!aEnabled)
788     {
789         ReleaseMappings(mActiveAddressMappings);
790     }
791 
792     UpdateState();
793 
794 exit:
795     return;
796 }
797 
798 } // namespace Nat64
799 } // namespace ot
800 
801 #endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
802