NPTv6 protocol for IPv6 address translation example
FortiOS adds partial support of the Network Prefix Translation (NPTv6) protocol in RFC6296 for IPv6 address translation, ensuring end-to-end connectivity, address independence, and 1:1 address mapping. It allows the use of private IPv6 addresses internally while translating them to globally routable IPv6 addresses when communicating with external networks. It operates at the prefix level by translating the network, prefix portion of an IPv6 address while leaving the host information unchanged. This enhances network scalability and facilitates efficient IPv6 network management.
A new NPTv6 type IP pool is introduced which allows an internal prefix of a IPv6 address to be translated to an external prefix. This enables administrators the freedom to use any internal prefix, but efficiently translate these to the prefix provided by their provider that can be routed globally.
To configure NPTv6 in the CLI:
config firewall ippool6 edit <IPv6 IP pool name> set type nptv6 set internal-prefix <internal NPTv6 prefix length> set external-prefix <external NPTv6 prefix length> next end
To configure NPTv6 in the GUI:
-
Go to Policy & Objects > IP Pools.
-
Go to the IPv6 IP Pool tab.
-
Click Create new.
-
Configure the IP pool:
-
Enter a Name.
-
Set the Type to NPTv6.
-
Enter the Internal prefix and External prefix.
-
Click OK.
-
How NPTv6 works
NPTv6 works on the principle that network translation is accomplished using a stateless approach, and that it is checksum-neutral at the transport layer. For instance, at the TCP layer, TCP checksums should remain unchanged even after the IP address has been translated.
In brief, TCP checksum is calculated based on a pseudo-header that includes the source and destination IP address. When the source address is translated, it needs to be done in a way that offsets the change to keep the checksum unchanged. This can be accomplished with the following rules:
-
For an IPv6 with prefix length of 48-bit or shorter, a 16-bit adjustment can be made on the 4th word (4th hextat) of the IPv6 address which is the subnet field.
-
For an IPv6 with prefix length greater than 48-bit, a 16-bit adjustment can be made on the 5th, 6th, 7th or 8th word of the IPv6 address.
For example, to translate from an internal prefix of fc00:1::/48 to an external prefix of 2003:db8:1::/48, an “adjustment” of 0xce45 has to be made in the 5th word, by adding 0xce45 to the 16-bit subnet field.
Therefore, the internal address fc00:1::1/48 becomes 2003:db8:1:ce45::1/48.
For more information, see section RFC6296 Section 3 – NPTv6 Algorithmic Specification.
Example
In the following example, an NPTv6 pool will be created and applied to the firewall policy. The packet IPv6 address translation will then be verifeid.
To configure the NPTv6 IP pool:
-
Configure the NPTv6 IP pool and prefix length:
The internal and external prefix must be the same length.
config firewall ippool6 edit "NPTv6-ippool6-1" set type nptv6 set internal-prefix 2000:10:1:100::/64 set external-prefix 2000:172:16:200::/64 next end
-
Apply the IP pool in the firewall policy:
config firewall policy edit 2 set name "NPTv6_policy6-1" set srcintf "port2" set dstintf "port1" set action accept set srcaddr6 "all" set dstaddr6 "all" set schedule "always" set service "ALL" set logtraffic all set nat enable set ippool enable set poolname6 "NPTv6-ippool6-1" next end
To verify the address translation:
-
Start an ICMP ping from 2000:10:1:100::41 to 2000:172:16:200::155.
-
Identify the 16-bit subnet field adjustment that is calculated according to the algorithm specified by RFC6296:
# diagnose firewall iprope6 list 100004 policy id: 2, group: 00100004, uuid_idx=8162 action: accept, schedule: always cos_fwd=255 cos_rev=255 flag (08050109): log redir nat master use_src pol_stats flag2(00004000): resolve_sso flag3(00000080): best-route shapers: / per_ip= sub_groups: av 00004e20 auth 00000000 split 00000000 misc 00000000 app_list: 0 ips_view: 0 vdom_id: 0 zone_from(1): 8 zone_to(1): 7 address_src(1): all uuid_idx=8045 address_dst(1): all uuid_idx=8045 service(1): [0:0x0:0/(0,65535)->(0,65535)] helper:auto nat(0): nat_64(0): nptv6(1): 2000:10:1:100::/64->2000:172:16:200::/64 adjust=FD88
-
Verify the session table. The translated IP address shows 2000:172:16:200:fd88::41 after applying the external prefix and the adjustment.
# diagnose sys session6 list session6 info: proto=58 proto_state=00 duration=15 expire=45 timeout=0 refresh_dir=both flags=00000000 sockport=0 socktype=0 use=3 origin-shaper= reply-shaper= per_ip_shaper= class_id=0 ha_id=0 policy_dir=0 tunnel=/ vlan_cos=0/0 state=log may_dirty npu statistic(bytes/packets/allow_err): org=1096/2/0 reply=1096/2/0 tuples=2 tx speed(Bps/kbps): 0/0 rx speed(Bps/kbps): 0/0 orgin->sink: org pre->post, reply pre->post dev=8->7/7->8 hook=post dir=org act=snat 2000:10:1:100::41:15308->2000:172:16:200::155:128(2000:172:16:200:fd88::41:15308) hook=pre dir=reply act=dnat 2000:172:16:200::155:15308->2000:172:16:200:fd88::41:129(2000:10:1:100::41:15308) misc=0 policy_id=2 pol_uuid_idx=8162 auth_info=0 chk_client_info=0 vd=0 serial=00000b86 tos=ff/ff ips_view=0 app_list=0 app=0 url_cat=0 rpdb_link_id=00000000 ngfwid=n/a npu_state=0x000c00 ofld-O ofld-R npu info: flag=0x81/0x81, offload=9/9, ips_offload=0/0, epid=128/129, ipid=129/128, vlan=0x0000/0x0000 vlifid=129/128, vtag_in=0x0000/0x0000 in_npu=1/1, out_npu=1/1, fwd_en=0/0, qid=4/7, ha_divert=0/0 total session6: 1
-
Take a sniffer trace. The IP address also shows 2000:172:16:200:fd88::41.
# diagnose sniffer packet any icmp6 4 interfaces=[any] filters=[icmp6] 6.712522 port2 in 2000:10:1:100::41 -> 2000:172:16:200::155: icmp6: echo request seq 1 [flowlabel 0xc6f01] 6.712555 port1 out 2000:172:16:200:fd88::41 -> 2000:172:16:200::155: icmp6: echo request seq 1 [flowlabel 0xc6f01] 6.712706 port1 in 2000:172:16:200::155 -> 2000:172:16:200:fd88::41: icmp6: echo reply seq 1 [flowlabel 0xa0b59] 6.712718 port2 out 2000:172:16:200::155 -> 2000:10:1:100::41: icmp6: echo reply seq 1 [flowlabel 0xa0b59] 7.721168 port2 in 2000:10:1:100::41 -> 2000:172:16:200::155: icmp6: echo request seq 2 [flowlabel 0xc6f01] 7.721181 port1 out 2000:172:16:200:fd88::41 -> 2000:172:16:200::155: icmp6: echo request seq 2 [flowlabel 0xc6f01] 7.721347 port1 in 2000:172:16:200::155 -> 2000:172:16:200:fd88::41: icmp6: echo reply seq 2 [flowlabel 0xa0b59] 7.721355 port2 out 2000:172:16:200::155 -> 2000:10:1:100::41: icmp6: echo reply seq 2 [flowlabel 0xa0b59] ^C 8 packets received by filter 0 packets dropped by kernel
The IPv6 prefix and subnet fields have been translated but the host field remains unchanged.