Stateful translation
ipfw supports in-kernel IPv6/IPv4 network address and protocol transla-
tion. Stateful NAT64 translation allows IPv6-only clients to contact
IPv4 servers using unicast TCP, UDP or ICMP protocols. One or more IPv4
addresses assigned to a stateful NAT64 translator are shared among sev-
eral IPv6-only clients. When stateful NAT64 is used in conjunction with
DNS64, no changes are usually required in the IPv6 client or the IPv4
server. The kernel module ipfw_nat64 should be loaded or kernel should
have options IPFIREWALL_NAT64
to be able use stateful NAT64 translator.
Chapter 6. IPv6 Network Address Translation (IPv6NAT)
Table of Contents
ipfw supports both stateful and stateless IPv6 / IPv4 translation.
From the ipfw(8) man page:
Stateful translation is suitable for deployment at the client side or at the service provider, allowing IPv6-only client hosts to reach remote IPv4-only nodes.
Stateless translation is appropriate when a NAT64 translator is used in front of IPv4-only servers to allow them to be reached by remote IPv6-only clients.
Specific requirements for these translation services are found in a collection of RFCs:
There are a couple of bugs registered for NAT64. See the following: NAT64 https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=255928 (NAT64 issue on 13.0)
The lab examples for all IPv6 / IPv4 translations will use two new virtual machines:
dnshost - this virtual machine runs a configured copy of BIND 9. Some experience with DNS setup with BIND 9 is helpful but not required.
v6only - this virtual machine only uses IPv6. It is not configured for IPv4 addressing at all.
Readers should have a basic understanding of IPv6 and its addressing characteristics. These resources may be helpful:
6.1. Stateful NAT64 (NAT64LSN) With DNS64
NAT64, described in RFC 6146 is one of a number of transition mechanisms that companies can take as they introduce IPv6 into their environment, or move wholesale into IPv6 locally. The idea with NAT64 is to provide a mechanism to allow an IPv6-only host to make a connection to a remote IPv4-only host. This includes the ability to do a DNS lookup on the remote host, and through the features of DNS64 (a companion transition service described in RFC 6147), translate a received IPv4 address into a special IPv6 address that provides a way to connect using the Network Address Translation variant called NAT64.
A logical view of NAT64 and DNS64 is shown in Figure 7:
The process works like this:
An IPv6 only host wants to access a resource from host external1.example.com which only uses IPv4. A DNS lookup for "external1.example.com" is sent to the locally configured DNS64 server. This lookup is for an "AAAA" record for the external1 VM.
The DNS64 server forwards the request to an authoritative server for "example.com".
The authoritative server returns an IPv4 address back to the DNS64 server.
The DNS64 server converts the IPv4 address into an IPv6 address using the transition service described in RFC 6147.
The IPv6 only host, receives the IPv6 address and sends a connection request (SYN) to its local IPV6 router running NAT64.
The NAT64 router converts the IPv6 packet back to IPv4 and forwards the packet to external1.example.com.
The remaining conversions between the IPv6 VM and external1 VM happen in a similar fashion.
In step 4, the DNS64 server converts the IPv4 address into IPv6 by using the "Well Known Prefix" 64:ff9b::
and encapsulating the IPv6 address into the last 4 octets of the address.
In the Figure above, "203.0.113.10" has been converted to "cb00:710a" and added as the last four octets of the new address.
Note that this is one instance of a larger selection of translation algorithms to translate an IPv4 address into an IPv6 address. In our implementation, the DNS64 server and the authoritative server are essentially merged together following the description of "Example of 'an IPv6 Network to the IPv4 Internet' Setup with DNS64 in Stub-Resolver Mode" in Section 7.2 of RFC 6147.
6.1.1. Setting Up for NAT64 / DNS64
To exercise the NAT64 capabilities of ipfw, it is first necessary to restart all lab virtual machines and reconfigure the ipfw lab.
Figure 8 shows the new configuration needed.
On the FreeBSD host system, the appropriate bridge and tap setup is given by this command:
$ sudo /bin/sh mkbr.sh reset bridge0 tap0 tap1 tap7 bridge1 tap4 tap6 tap8
Start up the required virtual machines with:
$ /bin/sh runvm.sh firewall dnshost external1 v6only
As before, configure all interfaces and ensure connectivity of adjacent interfaces. The firewall VM should be set for both IPv4 forwarding and IPv6 forwarding:
# sysctl net.inet.ip.forwarding=1 # sysctl net.inet6.ip6.forwarding=1
The external1 VM’s default route should point to 203.0.113.1 and the v6only host’s default IPv6 route should point to 2001:db8:12::1 as follows:
On external1:
# route add default 203.0.113.50
On v6only:
# route -6 add default 2001:db8:12::50
RFC 5737 describes the use of the 203.0.113.0/24 network for documentation and example purposes. RFC 3849 describes the use of the 2001:db8::/32 network the same purposes. |
6.1.2. Setting Up the dnshost VM
You will have to set up the dnshost VM to provide DNS64 services. ISC’s bind9 (9.18 and above) provides this capability. Setting up bind9, while not trivial, is not impossible. You will have to install the following packages:
bind9 Use the latest supported version. The server running here is using bind 9.18.1
bind-tools Same note as above. The server here uses bind-tools-9.16.27
These packages will install a modest number of dependencies.
If you have downloaded the practice kit, see the tar file dnshost.tar for the bind9 configuration files needed. Use the following command to untar the files:
# tar xvzf dhshost_userlocaletc_namedb.tgz -C /usr/local/etc
This will install all the needed DNS files. Otherwise see the zone files in Appendix E, and try to set up DNS. Note that the files include a stub root zone. This provides a locally complete DNS setup.
Restart the named service with:
# service named restart
There should not be any errors, but if there are, track down and fix.
Test the dnshost configuration with these commands. The first lookup returns the A resource record with an IPv4 address. The second lookup returns the AAAA resource record with the DNS64 configured "Well-known Prefix" 64:ff9b that is used in this section.
root@dnshost:~ # dig @localhost external1.example.com ; <<>> DiG 9.16.27 <<>> @localhost external1.example.com ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61764 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: 96f573e1a62ce4380100000062884cacf9b86d6e9f54b542 (good) ;; QUESTION SECTION: ;external1.example.com. IN A ;; ANSWER SECTION: external1.example.com. 3600 IN A 203.0.113.10 ;; Query time: 58 msec ;; SERVER: ::1#53(::1) ;; WHEN: Fri May 20 22:21:32 EDT 2022 ;; MSG SIZE rcvd: 94 # # root@dnshost:~ # dig @localhost external1.example.com aaaa ; <<>> DiG 9.16.27 <<>> @localhost external1.example.com aaaa ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5865 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: 0ad6c60faab12f7f0100000062884cb0be6e6bd00f7e482f (good) ;; QUESTION SECTION: ;external1.example.com. IN AAAA ;; ANSWER SECTION: external1.example.com. 3600 IN AAAA 64:ff9b::cb00:710a #
The last test is the most important. The dnshost must return the Well-Known Prefix "64:ff9b::" with the coresponding bits for the embedded IPv4 address. If the test does not return this value, reconfigure the DNS service (named.conf and the primary forward zone "example.com") to fix.
Since the v6only machine will only ever request AAAA lookups, the configuration is complete for this section.
We now have to set up the firewall, the IPV6 only host v6only, and the external VM host, external1. Boot all three VMs and test connectivity without any ipfw running on the firewall host.
We now proceed with the installation of NAT64 on the ipfw firewall.
On the firewall VM:
# kldload ipfw The next line loads the NAT64 module. # kldload ipfw_nat64
Configuring NAT64 is similar to configuring NAT. You must create an instance of the NAT64 translator first.
# ipfw nat64lsn foo create prefix4 203.0.112.0/24 allow_private
The use of the "allow_private" keyword is required. The ipfw(8) manual page notes that the NAT64 translator, by defualt, will not handle addresses whose destination matches those listed in RFC 1918. The addressing scheme in this lab uses special purpose addresses as noted in RFC 68901 which are also considered "private addresses" by the ipfw NAT64 translator.
Note that the prefix4 address pool (203.0.112.0/24 above) should not be manually configured as an alias on any interface. These addresses are used internally by ipfw. The only requirement is that they be reserved from deployment elsewhere in the local network so they do not cause a routing conflict with ipfw. This allows for 254 simultaneous NAT64 addresses. If more are needed due to high volume, add another prefix4, or increase the existing prefix4 address space.
Continue configuring the NAT64 / DNS64 translator:
# ipfw add allow log ipv6-icmp from any to any icmp6types 135,136 # ipfw add nat64lsn foo log ip from 2001:db8:12::/64 to 64:ff9b::/96 in # ipfw add nat64lsn foo log ip from any to 203.0.112.0/24 in # ipfw add allow log ip from any to any
and the direct_output sysctl must be set to 1 (not zero):
# sysctl net.inet.ip.fw.nat64_direct_output=1
You can also set the nat64_debug sysctl and the firewall verbose sysctl:
# sysctl net.inet.ip.fw.nat64_debug=1 # sysctl net.inet.ip.fw.verbose=1
See /var/log/security for output.
With these prerequisites completed the following tests on the v6only VM should be successful:
root@v6only# ping6 -c 3 64:ff9b::203.0.113.10 PING6(56=40+8+8 bytes) 2001:db8:12::30 --> 64:ff9b::cb00:710a 16 bytes from 64:ff9b::cb00:710a, icmp_seq=0 hlim=63 time=8.401 ms 16 bytes from 64:ff9b::cb00:710a, icmp_seq=1 hlim=63 time=3.429 ms 16 bytes from 64:ff9b::cb00:710a, icmp_seq=2 hlim=63 time=3.398 ms --- 64:ff9b::203.0.113.10 ping6 statistics --- 3 packets transmitted, 3 packets received, 0.0% packet loss round-trip min/avg/max/std-dev = 3.398/5.076/8.401/2.351 ms
and using lynx to grab the external1.example.com home page should be successful:
[root@v6only ~]# lynx external1.example.com
6.1.3. Setting Up for Stateless NAT64 - NAT64STL
The previous ipfw_nat64 examples used "stateful" address translation. ipfw is also capable of performing "stateless" address translation.
Stateless translation is appropriate when a NAT64 translator is used in front of IPv4-only servers to allow them to be reached by remote IPv6-only clients. Stateful translation is suitable for deployment at the client side or at the service provider, allowing IPv6-only client hosts to reach remote IPv4-only nodes.
Stateless configuration of NAT64 is possible with the same architecture as the previous stateful example. Configuration details however, are different. In the stateless case, ipfw uses two tables for translating addresses in either direction: IPv4 → IPv6 and IPv6 → IPv4. A typical configuration is shown below.
Start fresh # kldunload ipfw_nat64 # kldunload ipfw # kldload ipfw # kldload ipfw_nat64 Create the tables used for ipfw_nat64stl # ipfw table T4to6 create type addr valtype ipv6 # ipfw table T6to4 create type addr valtype ipv4 # ipfw table T4to6 add 203.0.112.1 2001:db8:12::6 # ipfw table T6to4 add 2001:db8:12::6 203.0.112.1 # ipfw nat64stl NAT64 create table4 T4to6 table6 T6to4 allow_private Add rules for ipfw_nat64stl # ipfw add allow log icmp6 from any to any icmp6types 135, 136 # ipfw add nat64stl NAT64 log ip from any to 'table(T4to6)' # ipfw add nat64stl NAT64 log ip6 from 'table(T6to4)' to 64:ff9b::/96 # ipfw add allow log ip from any to any Adjust sysctls # sysctl net.inet.ip.fw.verbose=1 # sysctl net.inet.ip.fw.nat64_debug=1 # sysctl net.inet.ip.fw.nat64_direct_output=1
Only the net.inet.ip.fw.nat64_direct_output sysctl is required.
Use the same tests as in the stateful NAT64 example:
[root@v6only ~]# ping6 -c 3 64:ff9b::203.0.113.10 and [root@v6only ~]# lynx external1.example.com
Both tests should be successful.
It may seem limiting to have to use tables to effect communication for stateless NAT64. However, if you look at the architecture involved, the above statements about stateless translation being appropriate when a NAT64 translator is used in front of IPv4-only servers to allow them to be reached by remote IPv6-only clients makes sense. The entire IPv6 cloud can reach a specified server.
This can be accomplished by, for example, changing the T4to6 and T6to4 tables to read:
# ipfw table T4to6 add 203.0.112.0/31 2001:db8:12::30 # ipfw table T6to4 add 2000:0000:0000::/8 203.0.112.0 # ipfw table T6to4 add 2100:0000:0000::/8 203.0.112.1
The T4to6 table allocates two addresses in the address pool: 203.0.112.0 and 203.0.112.1. These are used separately in the T6to4 table to cover vast ranges of IPv6 address space.
Certainly using just one IPv4 pool address is not going to be sufficient to translate such a large range of IPv6 addresses. The point here is that by carefully constructing the translation pool addresses and the T4to6 and T6to4 address tables, ipfw can manage translation to as many IPv6 addresses as needed.
Note that stateless NAT64 shares the same limitations of stateful NAT64.
We move on to the next, and most important IPv6 / IPv4 translation mechanism NAT64 CLAT.
6.2. XLAT464
ipfw supports 464XLAT (RFC 6877) calling it "XLAT464 CLAT". This transition mechanism provides connectivity for IPv4 edge devices across an IPv6 only network. It does this by combining stateful translation in the core and stateless translation at the edge. 464XLAT only suports IPv4 in the client-server model, so it does not support IPv4 peer-to-peer communication or inbound IPv4 connections.
See diagrams and explanation here: https://www.juniper.net/documentation/us/en/software/junos/interfaces-adaptive-services/topics/topic-map/ipv4-connect-ipv6-464xlat.html#id-464xlat-overview The RFC for this mechanism is more enlightening. See RFC 6877 |
The discussion on Wikipedia1 is somewhat sparse:
464XLAT 464XLAT (RFC 6877) allows clients on IPv6-only networks to access IPv4-only Internet services, such as Skype.[13][14] The client uses a SIIT translator to convert packets from IPv4 to IPv6. These are then sent to a NAT64 translator which translates them from IPv6 back into IPv4 and on to an IPv4-only server. The client translator may be implemented on the client itself or on an intermediate device and is known as the CLAT (Customer-side transLATor). The NAT64 translator, or PLAT (Provider-side transLATor), must be able to reach both the server and the client (through the CLAT). The use of NAT64 limits connections to a client-server model using UDP, TCP, and ICMP.
Figure @@@ shows a diagram for implementing 464XLAT in our lab.
As earlier, shutdown all virtual machines and for this example we will reset all the bridge and tap devices to the new architecture.
This example will require two firewalls. The firewall VM and firewall2 VM will both be used as shown in the diagram. In this example, the firewall VM is the CLAT translator (stateless translation) and the firewall2 VM is the PLAT translator (stateful translation).
To start, set up the bridge and tap interfaces with this command on the FreeBSD host:
$ sudo /bin/sh mkbr.sh reset bridge0 tap0 tap1 tap11 bridge1 tap4 tap6 tap7 tap9 \ bridge2 tap2 tap8 tap10
Start up the virtual machines with:
$ /bin/sh runvm.sh external1 firewall firewall2 dnshost v6only external2
As earlier, configure all interfaces according to the diagram and ensure connectivity with adjacent interfaces.
This example is more complex than past examples. There are a number of additional configuration steps needed as follows:
external1.example.com
On external1.example.com: route add default 192.168.1.1 echo "nameserver 192.168.1.53" > /etc/resolv.conf echo "nameserver 203.0.113.53" >> /etc/resolv.conf
firewall.example.com
On firewall.example.com: /bin/sh /root/bin/bsdclat464.sh echo "nameserver 2001:db8:12::53" > /etc/resolv.conf echo "nameserver 192.168.1.53" >> /etc/resolv.conf route -6 add 2001:db8:bbbb::/96 2001:db8:12::1 sysctl net.inet.ip.forwarding=1 sysctl net.inet6.ip6.forwarding=1 sysctl net.inet.ip.fw.nat64_direct_output=1
firewall2.example.com
On firewall2.example.com /bin/sh /root/bin/bsdplat464.sh route -6 add 2001:db8:aaaa::/96 2001:db8:12::2 echo "nameserver 2001:db8:12::53" > /etc/resolv.conf sysctl net.inet.ip.forwarding=1 sysctl net.inet6.ip6.forwarding=1 sysctl net.inet.ip.fw.nat64_direct_output=1
external2.example.com
On external2.example.com route add default 203.0.113.1 echo "nameserver 203.0.113.53" > /etc/resolv.conf service nginx onestart
dnshost.example.com
On dnshost.example.com: echo "nameserver 127.0.0.1" > /etc/resolv.conf echo "nameserver 2001:db8:12::53" >> /etc/resolv.conf service named onestart route add default 203.0.113.1 route add -net 192.168.1.0/24 192.168.1.1 sysctl net.inet.ip.forwarding=0 sysctl net.inet6.ip6.forwarding=0
v6only.example.com
On v6only.example.com: echo "nameserver 2001:db8:12::53" > /etc/resolv.conf route -6 add 2001:db8:bbbb::/96 2001:db8:12::1 route -6 add 2001:db8:aaaa::/96 2001:db8:12::2
Due to the complex nature of these firewall configurations, a script is available for copy and paste. However, do try to understand the configration details.
Once all the above commands are entered on their respective VMs, test the configuration with the following command:
# ping -c 2 external2.example.com
The snippets below show at each step, how the request was transformed.
The examples below are taken from multiple different invocations of the ping command. However, the data transformations are correct. |
On interface em0 on the firewall VM: root@firewall:~/bin # tcpdump -n -i em0 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on em0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 14:38:07.254114 IP 192.168.1.2 > 203.0.113.20: ICMP echo request, id 46395, seq 0, length 64 14:38:07.256893 IP 203.0.113.20 > 192.168.1.2: ICMP echo reply, id 46395, seq 0, length 64 14:38:08.322597 IP 192.168.1.2 > 203.0.113.20: ICMP echo request, id 46395, seq 1, length 64 14:38:08.326715 IP 203.0.113.20 > 192.168.1.2: ICMP echo reply, id 46395, seq 1, length 64 On interface em1 on the firewall VM: root@firewall:~/bin # tcpdump -n -i em1 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on em1, link-type EN10MB (Ethernet), snapshot length 262144 bytes 14:54:45.140746 IP6 2001:db8:aaaa::c0a8:102 > 2001:db8:bbbb::cb00:7114: ICMP6, echo request, id 38233, seq 0, length 64 14:54:45.142995 IP6 2001:db8:bbbb::cb00:7114 > 2001:db8:aaaa::c0a8:102: ICMP6, echo reply, id 38233, seq 0, length 64 14:54:46.171754 IP6 2001:db8:aaaa::c0a8:102 > 2001:db8:bbbb::cb00:7114: ICMP6, echo request, id 38233, seq 1, length 64 14:54:46.173925 IP6 2001:db8:bbbb::cb00:7114 > 2001:db8:aaaa::c0a8:102: ICMP6, echo reply, id 38233, seq 1, length 64 On interface em0 on the firewall2 VM: root@firewall2:~/bin # tcpdump -n -i em0 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on em0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 14:57:32.519334 IP6 2001:db8:aaaa::c0a8:102 > 2001:db8:bbbb::cb00:7114: ICMP6, echo request, id 17270, seq 0, length 64 14:57:32.529066 IP6 2001:db8:bbbb::cb00:7114 > 2001:db8:aaaa::c0a8:102: ICMP6, echo reply, id 17270, seq 0, length 64 14:57:33.560392 IP6 2001:db8:aaaa::c0a8:102 > 2001:db8:bbbb::cb00:7114: ICMP6, echo request, id 17270, seq 1, length 64 14:57:33.561596 IP6 2001:db8:bbbb::cb00:7114 > 2001:db8:aaaa::c0a8:102: ICMP6, echo reply, id 17270, seq 1, length 64 On interface em1 on the firewall2 VM: root@firewall2:~/bin # tcpdump -n -i em1 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on em1, link-type EN10MB (Ethernet), snapshot length 262144 bytes 14:58:37.139612 IP 203.0.112.22 > 203.0.113.20: ICMP echo request, id 1025, seq 0, length 64 14:58:37.141043 IP 203.0.113.20 > 203.0.112.22: ICMP echo reply, id 1025, seq 0, length 64 14:58:38.187477 IP 203.0.112.22 > 203.0.113.20: ICMP echo request, id 1025, seq 1, length 64 14:58:38.188308 IP 203.0.113.20 > 203.0.112.22: ICMP echo reply, id 1025, seq 1, length 64 On interfae em0 on the external2 VM: root@external2:~ # tcpdump -n -i em0 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on em0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 15:00:44.171439 IP 203.0.112.22 > 203.0.113.20: ICMP echo request, id 1024, seq 0, length 64 15:00:44.172313 IP 203.0.113.20 > 203.0.112.22: ICMP echo reply, id 1024, seq 0, length 64 15:00:45.200883 IP 203.0.112.22 > 203.0.113.20: ICMP echo request, id 1024, seq 1, length 64 15:00:45.201035 IP 203.0.113.20 > 203.0.112.22: ICMP echo reply, id 1024, seq 1, length 64
The firewall log sysctl was reset to log to syslogd(8) and captured these logs:
Logs on the firewall VM: root@firewall:~/bin # cat /var/log/security Dec 2 15:14:04 firewall kernel: ipfw: 150 Eaction nat64clat ICMP:8.0 192.168.1.2 203.0.113.20 in via em0 Dec 2 15:14:04 firewall kernel: ipfw: 150 Eaction nat64clat ICMPv6:129.0 [2001:db8:bbbb::cb00:7114] [2001:db8:aaaa::c0a8:102] in via em1 Dec 2 15:14:04 firewall kernel: ipfw: 150 Eaction nat64clat ICMP:8.0 192.168.1.2 203.0.113.20 in via em0 Dec 2 15:14:05 firewall kernel: ipfw: 150 Eaction nat64clat ICMPv6:129.0 [2001:db8:bbbb::cb00:7114] [2001:db8:aaaa::c0a8:102] in via em1 Dec 2 15:14:08 firewall kernel: ipfw: 100 Accept ICMPv6:135.0 [2001:db8:12::1] [2001:db8:12::2] in via em1 Dec 2 15:14:08 firewall kernel: ipfw: 100 Accept ICMPv6:136.0 [2001:db8:12::2] [2001:db8:12::1] out via em1 Dec 2 15:14:09 firewall kernel: ipfw: 100 Accept ICMPv6:135.0 [2001:db8:12::2] [2001:db8:12::1] out via em1 Dec 2 15:14:09 firewall kernel: ipfw: 100 Accept ICMPv6:136.0 [2001:db8:12::1] [2001:db8:12::2] in via em1 Logs on the firewall2 VM: root@firewall2:~/bin # cat /var/log/security Dec 2 15:10:25 firewall2 kernel: ipfw: 300 Eaction nat64lsn ICMPv6:128.0 [2001:db8:aaaa::c0a8:102] [2001:db8:bbbb::cb00:7114] in via em0 Dec 2 15:10:25 firewall2 kernel: ipfw: 400 Eaction nat64lsn ICMP:0.0 203.0.113.20 203.0.112.22 in via em1 Dec 2 15:10:26 firewall2 kernel: ipfw: 300 Eaction nat64lsn ICMPv6:128.0 [2001:db8:aaaa::c0a8:102] [2001:db8:bbbb::cb00:7114] in via em0 Dec 2 15:10:26 firewall2 kernel: ipfw: 400 Eaction nat64lsn ICMP:0.0 203.0.113.20 203.0.112.22 in via em1 Dec 2 15:10:29 firewall2 kernel: ipfw: 100 Accept ICMPv6:135.0 [2001:db8:12::1] [2001:db8:12::2] out via em0 Dec 2 15:10:29 firewall2 kernel: ipfw: 100 Accept ICMPv6:136.0 [2001:db8:12::2] [2001:db8:12::1] in via em0 Dec 2 15:10:30 firewall2 kernel: ipfw: 100 Accept ICMPv6:135.0 [2001:db8:12::2] [2001:db8:12::1] in via em0 Dec 2 15:10:30 firewall2 kernel: ipfw: 100 Accept ICMPv6:136.0 [2001:db8:12::1] [2001:db8:12::2] out via em0
Finally, a webpage request was made with:
# lynx external2.example.com
as shown below: