NAT64

As the world runs out of usable public IPv4 addresses, clients are increasingly being assigned IPv6 addresses. IPv6-only clients cannot natively communicate with IPv4-only resources. NAT64 is a solution to this problem. In NAT64, an IPv6-only client is NATed to a public IPv4 address in order to access IPv4 services.

In my opinion, NAT64 is much better than CGNAT (NAT444), because the service provider is giving the customer a public IPv6 IP address which allows the customer to host services on the IPv6 internet. With CGNAT, a customer is behind a NAT address so there is no way for them to natively host services to allow others on the public internet to connect to them. NAT64 is similar to doing CGNAT but only when necessary (when accessing an IPv4-only service).

NAT64 is almost always stateful, meaning that multiple IPv6 addresses map to a single IPv4 address. Very rarely would you see stateless NAT64, in which a single IPv6 address maps to a single IPv4 address. But you can configure either stateless or stateful NAT64. If you were to do stateless NAT64, it would make much more sense to just dual-stack the client.

Stateful NAT uses port overloading to allow multiple clients to use the same IPv4 address. This means the router must keep state on each NAT translation that is made, so that the reverse traffic can be mapped back to the IPv6 address based on the IPv4 destination port.

NAT64 requires a DNS64 server. This special DNS server, upon receiveing an AAAA query from a client, will first attempt to locate an AAAA record. If none is available the DNS64 server will obtain the A record. However the IPv6-only client cannot accept the A record, as the client is not IPv4-aware. So the DNS64 server maps the A record to an IPv6 address and returns this as the AAAA answer.

The IPv6 address is constructed by the DNS64 server by using a preconfigured IPv6 prefix, and then mapping the IPv4 address to the last 32 bits of the IPv6 address. The client is not aware that the IPv6 address is actually a special NAT64 address. The client simply delivers traffic to this address as if it were a normal IPv6 destiantion address. The NAT64 router matches destination traffic based on the special NAT64 prefix, and translates the IPv6 destination to the IPv4 address by using the last 32 bits of the IPv6 address as the translated IPv4 address. In this way, the DNS64 server and NAT64 work together to enable the IPv6 client to speak to IPv4-only resources.

The NAT64 prefix used by the DNS64 server and NAT64 router can be configurable, or you can use the well-known prefix of 64:FF9B::/96. You can also configure a /32, /40, /48, /56, or /64 prefix for the NAT64 prefix if desired.

Lab

We will use the following topology to explore NAT64:

Here are the startup configs. IPv4/v6 addressing and routing is configured. I used BGP to exchange v4 and v6 routes between the two “ISPs.” NAT64-Router is AS 200 and R1 is AS 100.

#Client
hostname Client
!
ipv6 unicast-routing
!
int Gi1
 ipv6 address 2001:db8:1::2/64
 no shut
!
ipv6 route ::/0 2001:db8:1::1

#NAT64-Router
hostname NAT64-Router
!
ipv6 unicast-routing
!
int Gi1
 ipv6 address 2001:db8:1::1/64
 no shut
!
int Gi2
 ip address 100.1.1.2 255.255.255.0
 ipv6 address 2001:db8:2::2/64
 no shut
!
router bgp 200
 neighbor 100.1.1.1 remote-as 100
 neighbor 2001:db8:2::1 remote-as 100
 address-family ipv4
  no neighbor 2001:db8:2::1 activate
 address-family ipv6
  neighbor 2001:db8:2::1 activate
  network 2001:db8:1::/64

#R1
hostname R1
!
ipv6 unicast-routing
!
int Gi1
 ip address 100.1.2.2 255.255.255.0
 no shut
!
int Gi2
 ip address 100.1.1.1 255.255.255.0
 ipv6 address 2001:db8:2::1/64
 no shut
!
int Gi3
 ipv6 address 2001:db8:3::1/64
 no shut
!
router bgp 100
 neighbor 100.1.1.2 remote-as 200
 neighbor 2001:db8:2::2 remote-as 200
 address-family ipv4
  no neighbor 2001:db8:2::2 activate
  network 100.1.2.0 mask 255.255.255.0
 address-family ipv6
  neighbor 2001:db8:2::2 activate
  network 2001:db8:3::/64

#IPv4-Server
hostname IPv4-Server
!
int Gi1
 ip address 100.1.2.1 255.255.255.0
 no shut
!
ip route 0.0.0.0 0.0.0.0 100.1.2.2

#IPv6-Server
hostname IPv6-Server
!
ipv6 unicast-routing
!
int Gi1
 ipv6 address 2001:db8:3::2/64
 no shut
!
ipv6 route ::/0 2001:db8:3::1

Currently the client has native accessibility to the IPv6-Server but not the IPv4-Server. Let’s setup NAT64 on the NAT64-Router. We’ll use the prefix 2001:db8:100:100::/96 to match traffic that should be destination NATed to an IPv4 address. In real world this would probably be a prefix that we (the ISP) own, or you would use the well-known prefix 64:FF9B::/96. Notice that a /96 cleanly fits the 32 bits of the IPv4 destination address in the host portion of the IPv6 destination address.

In addition to the v6 to v4 destination NAT, we need to source NAT the client v6 address to a v4 address. The router won’t let us use an existing address that is used by an interface for this, so we will use an address from 100.1.3.0/24 which we will pretend this ISP owns.

#NAT64-Router
int Gi1
 nat64 enable
!
int Gi2
 nat64 enable
!
ipv6 access-list NAT64-SOURCES
 permit ipv6 2001:db8:1::/64 any
!
nat64 prefix stateful 2001:db8:100:100::/96
nat64 v4 pool MY-POOL 100.1.3.1 100.1.3.1
nat64 v6v4 list NAT64-SOURCES pool MY-POOL overload

! Advertise 100.1.3.0/24
ip route 100.1.3.0 255.255.255.0 null0
!
router bgp 200
 address-family ipv4
  network 100.1.3.0 mask 255.255.255.0

Here is a summary of the configuration steps:

  • Enable nat64 on both the inside and outside interfaces with nat64 enable

  • Create an IPv6 ACL to match the source IPv6 addresses that the router will preform NAT64 for.

  • Define the NAT64 prefix. Destination traffic matching this prefix will have the IPv4 address “auto-obtained” from the IPv6 address and used for the destination NAT.

  • Create a pool for the range of v4 addresses to use when source NATing the v6 client. This cannot be an address on the router itself.

  • Associate the ACL with the pool using the command nat64 v6v4 acl pool pool overload

To test this, we need to manually convert the IPv4-Server address to hex, so that we can concatenate it with the NAT64 prefix. Normally you would have a DNS64 do this task automatically, and it would happen when the client does a DNS request for an AAAA record that is for an IPv4-only resource.

100.1.2.1 in hex is 0x64010201. So we will use the destination IPv6 address of 2001:db8:100:100::6401:0201 to test reachability.

Client#ping 2001:db8:100:100::6401:0201
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 2001:DB8:100:100::6401:201, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 2/2/3 ms

As an exercise, fill in the blanks below to test your understanding of the NATs that are taking place:

  • Original Source Address = 2001:db8:1::1

  • Original Destination Address = 2001:db8:100:100::6401:0201

  • Modified Source Address = _______________

  • Modified Destination Address = _______________

The answers are:

  • Modified Source Address = 100.1.3.1

  • Modified Destination Address = 100.1.2.1

Examine the nat64 translations on the NAT64-Router:

NAT64-Router#show nat64 translations 

Proto  Original IPv4         Translated IPv4
       Translated IPv6       Original IPv6 
----------------------------------------------------------------------------

icmp   100.1.2.1:6858        [2001:db8:100:100::6401:201]:6858               
       100.1.3.1:6858        [2001:db8:1::2]:6858                            

Total number of translations: 1

The output of this show command may be a little tricky to read at first. 2001:db8:100:100::6401:201 is the “translated IPv4 address” because the IPv4 address is translated into this IPv6 address by the DNS64 server (typically). The “original IPv4 address” is 100.1.2.1, which the DNS64 server would have obtained from the A record.

The “original IPv6 address” is a little easier to understand - it is the original source IP of the host, in our case 2001:db8:1::2. The “translated IPv6 address” is the IPv4 address that the host is source NATed to, which is 100.1.3.1.

There are some other show commands you can use to verify NAT64:

NAT64-Router#show nat64 prefix stateful global 

Global Stateful Prefix: is valid, 2001:DB8:100:100::/96

IFs Using Global Prefix

   Gi2
   Gi1


NAT64-Router#show nat64 pools 

Pools configured: 1

Protocol HSL ID     Name
   Is Single Range
   Ranges
   VRF Name

IPv4     1          MY-POOL
   TRUE            (100.1.3.1 - 100.1.3.1)
   100.1.3.1 - 100.1.3.1


NAT64-Router#show nat64 statistics | ex 0
NAT64 Statistics

Sessions found: 13
Sessions created: 2
Expired translations: 1
Global Stats:
   Packets translated (IPv4 -> IPv6)
      Stateful: 5
   Packets translated (IPv6 -> IPv4)

Interface Statistics
   GigabitEthernet1 (IPv4 not configured, IPv6 configured):
      Packets translated (IPv4 -> IPv6)
      Packets translated (IPv6 -> IPv4)
   GigabitEthernet2 (IPv4 configured, IPv6 configured):
      Packets translated (IPv4 -> IPv6)
         Stateful: 5
      Packets translated (IPv6 -> IPv4)
Dynamic Mapping Statistics
   v6v4
         pool MY-POOL:
         pool MY-POOL:
Limit Statistics

To drive home the point that IPv6 services still work natively, you can enable telnet on the client and telnet to the client from the IPv6-Server. You would not be able to do this with a client behind CGNAT.

#Client
username cisco password cisco123
enable password cisco123
line vty 0 4
 transport input telnet
 login local


IPv6-Server#telnet 2001:db8:1::2
Trying 2001:DB8:1::2 ... Open


User Access Verification

Username:

For more practice, you can configure NAT64 on R1 so that the IPv6-Server can access the IPv4-Server as well.

Try it on your own first, and then scroll down to see the configuration if you get stuck.

#R1
int Gi3
 nat64 enable
!
int Gi1
 nat64 enable
!
ipv6 access-list NAT64-SOURCES
 permit 2001:db8:3::/64 any
!
nat64 prefix stateful 2001:db8:200:200::/96
! This does not need to be reachable by any other router, since the IPv4-Router is directly connected
nat64 v4 pool MY-POOL 100.3.3.1 100.3.3.1
nat64 v6v4 list NAT64-SOURCES pool MY-POOL overload

#IPv6-Server
IPv6-Server#ping 2001:db8:200:200::6401:0201
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 2001:DB8:200:200::6401:201, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 2/3/10 ms

Conclusion

NAT64 is a stateful mechanism used to allow IPv6-only clients access to IPv4-only resources. This allows a service provider to serve customers without allocating scarce IPv4 address space.

A DNS64 server synthesizes an A record answer into an AAAA answer, concatenating the defined NAT64 prefix with the IPv4 address found in the A record.

The client sends traffic towards this synthesized IPv6 address, which is routed to the NAT64 router. This router source NATs the client to a shared public IPv4 address, and translates the destination IPv6 address to the corresponding IPv4 address. Return traffic is translated in reverse based on the destination port.

NAT64 is a useful IPv6 transition mechanism. As IPv4 is phased out, clients will gradually rely on the DNS64 server less and less. When IPv4 is finally gone, you simply decommision the DNS64 and NAT64 appliances.

Further Reading

https://www.cisco.com/c/en/us/support/docs/ip/network-address-translation-nat/217208-understanding-nat64-and-its-configuratio.html

Last updated