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