In this article, we will consider the operation of Zone Based Policy Firewall (ZBF) configured on a Cisco IOS router that is also doing network address translation (NAT). Primarily, what we want to find out is what address (inside local, inside global, outside local, outside global) to use when creating firewall policies. We will also look at the operation of ZBF with NVI-based NAT.

We will use the diagram below for our lab setup:

As shown in the diagram, R2 will be on the trusted (inside) side while R3 will be on the untrusted (outside) side. We will enable ZBF on R1 along with the following NAT rules:

  • Translate R2’s IP address (192.168.12.2) to 192.168.13.2 on the outside.
  • Translate R3’s IP address (192.168.13.3) to 192.168.12.3 on the inside.

CCNA Training – Resources (Intense)

Since we are not concerned with how ZBF itself works, we will restrict our policies to only what is relevant to our discussion. ICMP is one of the easiest things to test with so we will configure ICMP inspection in both directions. Rather than just inspecting all ICMP, we will configure access lists with all the possible combinations of addresses; i.e., before and after NAT. This way, when we generate an ICMP packet, we can determine which access control entry was matched.

The configuration on R1 is as follows:

ip access-list extended IN_TO_OUT_ACL
 permit icmp host 192.168.12.2 host 192.168.13.3
 permit icmp host 192.168.12.2 host 192.168.12.3
 permit icmp host 192.168.13.2 host 192.168.13.3
 permit icmp host 192.168.13.2 host 192.168.12.3
ip access-list extended OUT_TO_IN_ACL
 permit icmp host 192.168.13.3 host 192.168.12.2
 permit icmp host 192.168.13.3 host 192.168.13.2
 permit icmp host 192.168.12.3 host 192.168.12.2
 permit icmp host 192.168.12.3 host 192.168.13.2
!
class-map type inspect match-all IN_TO_OUT_CMAP
 match access-group name IN_TO_OUT_ACL
class-map type inspect match-all OUT_TO_IN_CMAP
 match access-group name OUT_TO_IN_ACL
!
policy-map type inspect IN_TO_OUT_PMAP
 class type inspect IN_TO_OUT_CMAP
  inspect
policy-map type inspect OUT_TO_IN_PMAP
 class type inspect OUT_TO_IN_CMAP
  inspect
!
zone security inside
zone security outside
!
zone-pair security ZP_IN_TO_OUT source inside destination outside
 service-policy type inspect IN_TO_OUT_PMAP
zone-pair security ZP_OUT_TO_IN source outside destination inside
 service-policy type inspect OUT_TO_IN_PMAP
!
interface fa0/0
 ip address 192.168.12.1 255.255.255.0
 ip nat inside
 zone-member security inside
 no ip route-cache
!
interface fa0/1
 ip address 192.168.13.1 255.255.255.0
 ip nat outside
 zone-member security outside
 no ip route-cache
!
ip nat inside source static 192.168.12.2 192.168.13.2
ip nat outside source static 192.168.13.3 192.168.12.3 add-route

Note: I have disabled fast switching (no ip route-cache) on both interfaces so that packets will be process-switched; i.e., we will be able to see packets passing through the router when we turn on debugging.

Inside-to-Outside Traffic Flow

The first test we will do is a ping going from R2 to R3’s outside local address (192.168.12.3).

R2#ping 192.168.12.3 re 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 192.168.12.3, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 132/132/132 ms

We can check the access-list counters on R1 for the ‘IN_TO_OUT_ACL’ to see what entry was matched for this ping packet.

R1#show ip access-lists IN_TO_OUT_ACL
Extended IP access list IN_TO_OUT_ACL
    10 permit icmp host 192.168.12.2 host 192.168.13.3 (1 match)
    20 permit icmp host 192.168.12.2 host 192.168.12.3
    30 permit icmp host 192.168.13.2 host 192.168.13.3
    40 permit icmp host 192.168.13.2 host 192.168.12.3

As shown above, the entry that had the untranslated addresses (i.e., inside local and outside global) was the one that was matched.

I turned on debugging on R1 for IP packets, NAT and ZBF, so let us view these debug messages. We will begin with the debug messages for the ICMP Echo request packet.

Note: Due to the size of the debug output, I will only paste the relevant parts of it.

R1#debug ip packet detail
IP packet debugging is on (detailed)
R1#debug ip nat detailed
IP NAT detailed debugging is on
R1#debug policy-map type inspect detail
Policy-Firewall detailed debugging is on
R1#
*Jun 26 14:13:51.263: IP: s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3, len 100, input feature
...
*Jun 26 14:13:51.291: FIBipv4-packet-proc: route packet from FastEthernet0/0 src 192.168.12.2 dst 192.168.12.3
*Jun 26 14:13:51.295: FIBfwd-proc: Default:192.168.12.3/32 receive entry
*Jun 26 14:13:51.295: FIBipv4-packet-proc: packet routing failed
*Jun 26 14:13:51.295: IP: tableid=0, s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3 (FastEthernet0/1), routed via RIB
*Jun 26 14:13:51.299: NAT: Existing entry found in the global tree,updating it to point to the latest node passed
*Jun 26 14:13:51.303: NAT: i: icmp (192.168.12.2, 2) -> (192.168.12.3, 2) [4]
*Jun 26 14:13:51.303: NAT: s=192.168.12.2->192.168.13.2, d=192.168.12.3 [4]
*Jun 26 14:13:51.303: NAT: s=192.168.13.2, d=192.168.12.3->192.168.13.3 [4]

*Jun 26 14:13:51.307: IP: s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0
R1#/1), len 100, output feature
...
*Jun 26 14:13:51.335: FIREWALL: FW or IPS Feature object found
*Jun 26 14:13
R1#:51.335: FIREWALL: Searching for session in cls 0x6A1ED740 clsgrp 0x10000000, target 0xD8C324, cce clstype 0x28
*Jun 26 14:13:51.339: FIREWALL: Session not found
*Jun 26 14:13:51.343: FIREWALL: Only FW session init FO found
*Jun 26 14:13:51.343: FIREWALL: Creating fso and attaching forward
*Jun 26 14:13:51.347: FIREWALL sis 68DCCC00: SIS_CLOSED
*Jun 26 14:13:51.351: FIREWALL sis 68DCCC00: Pak 0x694A8698 IP: s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0/1), len 80, proto=icmp
*Jun 26 14:13:51.351: FIREWALL sis 68DCCC00: FSO bind success for reve cls 0x6A1ED7A4, clsgrp 0x10000000, target 0xD8C324
*Jun 26 14:13:51.355: FIREWALL sis 68DCCC00:Session Info: fwfo = 0x6A23AA20, saddr(0:192.168.12.2:8) daddr(0:192.168.13.3:0), L4 = ICMP
*Jun 26 14:13:51.363: clstype 0x28, target 0xD8C324, policy id 0x10000000, clsid 0x6A1ED740
*Jun 26 14:13:51.363: clstype 0x28, rev target 0xD8C324, rev policy id 0x10000000, rev clsid 0x6A1ED7A4
*Jun 26 14:13:51.367: FIREWALL sis 6
R1#8DCCC00: L4 result: PASS packet 0x694A8698 (192.168.13.2:0) (192.168.13.3:0) bytes 80
...
*Jun 26 14:13:51.399: IP: s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0/1), len 100, sending full packet
*Jun 26 14:13:51.403: ICMP type=8, code=0

The messages that are in are the ones we will focus on. Because this traffic is flowing from the NAT inside domain to the outside domain, routing (determining egress interface) happens first before NAT rules are applied. The first four debug messages in show this.

At the point of the traffic getting to the ZBF, it has already been translated to [s=192.168.13.2, d=192.168.13.3]. We see this from the first BOLD debug message about “FIREWALL.” However, the next debug message about the firewall in BOLD shows us that the firewall created a session with the real addresses [s=192.168.12.2, d=192.168.13.3].

This explains why the ACE line 10 in the “IN_TO_OUT_ACL” was matched instead of the ACE line 30. The remaining debug messages show that the ICMP Echo reply from R3 was permitted by the firewall.

*Jun 26 14:13:51.435: IP: s=192.168.13.3 (FastEthernet0/1), d=192.168.13.2, len 100, input feature
...
*Jun 26 14:13:51.459: NAT: o: icmp (192.168.13.3, 2) -> (192.168.13.2, 2) [4]
*Jun 26 14:13:51.459: NAT: s=192.168.13.3->192.168.12.3, d=192.168.13.2 [4]
*Jun 26 14:13:51.463: NAT: s=192.168.12.3, d=192.168.13.2->192.168.12.2 [4]
...
*Jun 26 14:13:51.475: FIBipv4-packet-proc: route packet from FastEthernet0/1 src 192.168.12.3 dst 192.168.12.2
*Jun 26 14:13:51.475: FIBfwd-proc: packet routed by adj to FastEthernet0/0 192.168.12.2
*Jun 26 14:
R1#13:51.479: FIBipv4-packet-proc: packet routing succeeded
...
*Jun 26 14:13:51.507: FIREWALL: NEW PAK 69115F28 (0:192.168.13.3) (0:192.168.12.2) icmp
...
*Jun 26 14:13:51.523: FIREWALL sis 68DCCC00: L4 result: PASS packet 0x69115F28 (192.168.12.3:0) (192.168.12.2:0) bytes 80
...
*Jun 26 14:13:51.547: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), g=192.168.12.2, len 100, forward
*Jun 26 14:13:51.551:     ICMP type=0, code=0
*Jun 26 14:13:51.559: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), len 100, sending full packet
*Jun 26 14:13:51.559:     ICMP type=0, code=0

Outside-to-Inside Traffic Flow

Let’s now look at traffic that is initiated from the outside to the inside. I will ping from R3 to R2’s inside global address (192.168.13.2).

R3#ping 192.168.13.2 re 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 192.168.13.2, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 59/59/59 ms

The ‘OUT_TO_IN_ACL’ counters on R1 reveal that this flow also matched the untranslated addresses:

R1#show ip access-lists OUT_TO_IN_ACL
Extended IP access list OUT_TO_IN_ACL
    10 permit icmp host 192.168.13.3 host 192.168.12.2 (1 match)
    20 permit icmp host 192.168.13.3 host 192.168.13.2
    30 permit icmp host 192.168.12.3 host 192.168.12.2
    40 permit icmp host 192.168.12.3 host 192.168.13.2

Again, we will look at the relevant debug output on R1:

R1#
*Jun 26 14:39:17.827: IP: s=192.168.13.3 (FastEthernet0/1), d=192.168.13.2, len 100, input feature
...
*Jun 26 14:39:17.851: NAT: Existing entry found in the global tree,updating it to point to the latest node passed
*Jun 26 14:39:17.851: NAT: o: icmp (192.168.13.3, 1) -> (192.168.13.2, 1) [1]
*Jun 26 14:39:17.855: NAT: s=192.168.13.3->192.168.12.3, d=192.168.13.2 [1]
*Jun 26 14:39:17.855: NAT: s=192.168.12.3, d=192.168.13.2->192.168.12.2 [1]
...
*Jun 26 14:39:17.867: FIBipv4-packet-proc: route packet from FastEthernet0/1 src 192.168.12.3 dst 192.168.12.2
*Jun 26 14:39:17.871: FIBfwd-proc: packet routed by adj to FastEthernet0/0 192.168.12.2
*Jun 26 14:39:17.871: FIBipv4-packet-proc: packet routing succeeded
*Jun 26 14:39:17.875: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), len 100, output feature
...
*Jun 26 14:39:17.903: FIREWALL: FW or IPS Feature object found
*Jun 26 14:39:17.903: FIREWALL: Searching for session in cls 0x6A1ED740 clsgrp 0x10000000, target 0x857AF4, cce clstype 0x28
*Jun 26 14:39:17.907: FIREWALL: Session not found
*Jun 26 14:39:17.911: FIREWALL: Only FW session init FO found
*Jun 26 14:39:17.911: FIREWALL: Creating fso and attaching forward
*Jun 26 14:39:17.915: FIREWALL sis 68DCDD80: SIS_CLOSED
*Jun 26 14:39:17.915: FIREWALL sis 68DCDD80: Pak 0x676BA92C IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), len 80, proto=icmp
*Jun 26 14:39:17.919: FIREWALL sis 68DCDD80: FSO bind success for reve cls 0x6A1ED7A4, clsgrp 0x10000000, target 0x857AF4
*Jun 26 14:39:17.923: FIREWALL sis 68DCDD80:Session Info: fwfo = 0x6A277560, saddr(0:192.168.13.3:8) daddr(0:192.168.12.2:0), L4 = ICMP
…
*Jun 26 14:39:17.935: FIREWALL sis 68DCDD80: L4 result: PASS packet 0x676BA92C (192.168.12.3:0) (192.168.12.2:0) bytes 80
...
*Jun 26 14:39:17.955: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), g=192.168.12.2, len 100, forward
*Jun 26 14:39:17.963:     ICMP type=8, code=0
*Jun 26 14:39:17.967: IP: s=192.168.12.3 (FastEthernet0/1), d=192.168.12.2 (FastEthernet0/0), len 100, sending full packet
*Jun 26 14:39:17.971:     ICMP type=8, code=0
! ####### ICMP Echo Reply #####
*Jun 26 14:39:17.991: IP: s=192.168.12.2 (FastEthernet0/0), d=192.168.12.3, len 100, input feature
...
*Jun 26 14:39:18.075: FIREWALL sis 68DCDD80: L4 result: PASS packet 0x693DD0A0 (192.168.13.2:0) (192.168.13.3:0) bytes 80
...
R1#4:39:18.099: IP: s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0/1), g=192.168.13.3, len 100, forward
*Jun 26 14:39:18.103:     ICMP type=0, code=0
*Jun 26 14:39:18.111: IP: s=192.168.13.2 (FastEthernet0/0), d=192.168.13.3 (FastEthernet0/1), len 100, sending full packet
*Jun 26 14:39:18.115:     ICMP type=0, code=0
R1#

Since this traffic is flowing from outside to inside, it is first translated according to NAT rules before routing is done. We see that when the packet got to the firewall, it has already been translated to [s=192.168.12.3, d=192.168.12.2] but again, the session created was for the real IP addresses [s=192.168.13.3, d=192.168.12.2].

Therefore we can conclude that irrespective of the flow of traffic, when ZBF is used with NAT, the real untranslated addresses (inside local and outside global) should be used in configuring policies.

ZBF and NVI-Based NAT

We configured domain-based NAT (ip nat inside, ip nat outside) on R1 but what happens when we use the NVI-based NAT? Let’s change our configuration and see:

interface fa0/0
interface fa0/0
 no ip nat inside
 ip nat enable
!
interface fa0/1
 no ip nat outside
 ip nat enable
!
no ip nat inside source static 192.168.12.2 192.168.13.2
no ip nat outside source static 192.168.13.3 192.168.12.3 add-route
ip nat source static 192.168.12.2 192.168.13.2
ip nat source static 192.168.13.3 192.168.12.3

When we perform our ping tests from both R2 and R3, they are still successful.

R2#ping 192.168.12.3 re 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 192.168.12.3, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 59/59/59 ms

R3#ping 192.168.13.2 re 1
Type escape sequence to abort.
Sending 1, 100-byte ICMP Echos to 192.168.13.2, timeout is 2 seconds:
!
Success rate is 100 percent (1/1), round-trip min/avg/max = 33/33/33 ms

However, what I have discovered is that this doesn’t work on all IOS versions. I tried it on 12.4T and also on 15.4 and I found that NVI-based NAT does not work when ZBF is configured – the packets will just get dropped. Some have reported that even though it works, once there is a policy configured on the self zone, it stops working.

Summary

In this article, we have looked at the operations of ZBF and NAT configured on the same Cisco IOS router. We concluded that, no matter the direction of traffic flow, the real untranslated addresses should always be used when configuring ZBF policies.

I also highlighted that when NVI-based NAT is configured with ZBF, there may be unexpected results, differing among IOS versions.

I hope you have found this article insightful.

References and Further Reading

  • ZBF, NVI, and self zones: https://learningnetwork.cisco.com/thread/25126
  • Zone-Based Policy Firewall: http://www.cisco.com/c/en/us/td/docs/ios-xml/ios/sec_data_zbf/configuration/12-4t/sec-data-zbf-12-4t-book/sec-zone-pol-fw.html
  • Cisco ZBF – NVI in ‘self’ zone: http://forums.whirlpool.net.au/archive/2210843