Reading the IPv6 Configuration Guide (Implementing Traffic Filters and Firewalls for IPv6 Security), I came across a little known fact that seems to be very important when configuring IPv6 access-lists on IOS.
Usually when I configured an IPv4 ACL, I explicitly defined a deny ip any any at the end, which seems like the best practice. But what happens when you do that same thing with IPv6 ACLs.
I’ll be working with this very simple topology:
At first I’ll apply the EXPLICIT-BROKEN-ACL on R2’s F0/0 (towards R1). With that clever name, you guess right that this ACL will break things for us. Later on, I’ll apply the EXPLICI-FIXED-ACL on R2 F0/1 interface (toward R2), which will demonstrate the proper configuration of IPv6 ACLs.
First verify R2 can see both R3 and R1.
R2#sh ipv6 neighbors IPv6 Address Age Link-layer Addr State Interface 2001:12::1 11 c200.13b8.0000 STALE Fa0/0 2001:23::3 11 c202.13b8.0000 STALE Fa0/1 FE80::C002:13FF:FEB8:0 13 c202.13b8.0000 STALE Fa0/1 FE80::C000:13FF:FEB8:0 13 c200.13b8.0000 STALE Fa0/0
R2#ping 2001:12::1 re 2 Type escape sequence to abort. Sending 2, 100-byte ICMP Echos to 2001:12::1, timeout is 2 seconds: !! Success rate is 100 percent (2/2), round-trip min/avg/max = 40/50/60 ms R2#ping 2001:23::3 re 2 Type escape sequence to abort. Sending 2, 100-byte ICMP Echos to 2001:23::3, timeout is 2 seconds: !! Success rate is 100 percent (2/2), round-trip min/avg/max = 24/24/24 ms
Next, I will apply the explicit deny ACL:
R2#conf t Enter configuration commands, one per line. End with CNTL/Z. R2(config)#ipv6 access-list EXPLICIT-BROKEN-ACL R2(config-ipv6-acl)# permit tcp any any R2(config-ipv6-acl)# deny ip any any log R2(config-ipv6-acl)#exit R2(config)#int fa0/0 R2(config-if)#ipv6 traffic-filter EXPLICIT-BROKEN-ACL in %IPV6-6-ACCESSLOGDP: list EXPLICIT-BROKEN-ACL/20 denied icmpv6 FE80::C000:13FF:FEB8:0 -> FE80::C001:13FF:FEB8:0 (135/0), 1 packet %IPV6-6-ACCESSLOGDP: list EXPLICIT-BROKEN-ACL/20 denied icmpv6 FE80::C000:13FF:FEB8:0 -> FE80::C001:13FF:FEB8:0 (136/0), 1 packet
Right after applying the EXPLICIT-BROKEN-ACL, I see two deny syslog messages. These messages are denying traffic from link-local addresses, they are ICMPv6 and looks like they are of type 135 and 136. What is ICMPv6 type 135 and 136? Quick check at IANA’s website tells me that it is ICMPv6 Neighbor Solicitation (NS) and ICMPv6 Neighbor Advertisement (NA). So when you explicitly define a deny any any ACE, you are blocking IPv6 neighbor discovery, which is the ARP of IPv4.
To verify that is the problem, let me telnet to R1 Fa0/1 IP address, this should not work.
RR2#telnet 2001:12::1 Trying 2001:12::1 ... % Connection timed out; remote host not responding %IPV6-6-ACCESSLOGDP: list EXPLICIT-BROKEN-ACL/20 denied icmpv6 2001:12::1 -> 2001:12::2 (136/0), 3 packets %IPV6-6-ACCESSLOGDP: list EXPLICIT-BROKEN-ACL/20 denied icmpv6 FE80::C000:13FF:FEB8:0 -> 2001:12::2 (135/0), 3 packets %IPV6-6-ACCESSLOGDP: list EXPLICIT-BROKEN-ACL/20 denied icmpv6 FE80::C000:13FF:FEB8:0 -> 2001:12::2 (136/0), 3 packets
R2#sh ipv6 neighbor IPv6 Address Age Link-layer Addr State Interface FE80::C002:13FF:FEB8:0 6 c202.13b8.0000 STALE Fa0/1
Just as expected, telnet does not work and you can see denies for IPv6 NA and NS, additionally, resolution for 2001:12::1 is not in the neighbor table. So what is going on? Digging into the Cisco docs this is what I found:
“Each IPv6 ACL contains implicit permit rules to enable IPv6 neighbor discovery. These rules can be overridden by the user by placing a deny ipv6 any any statement within an ACL. The IPv6 neighbor discovery process makes use of the IPv6 network layer service; therefore, by default, IPv6 ACLs implicitly allow IPv6 neighbor discovery packets to be sent and received on an interface. In IPv4, the Address Resolution Protocol (ARP), which is equivalent to the IPv6 neighbor discovery process, makes use of a separate data link layer protocol; therefore, by default, IPv4 ACLs implicitly allow ARP packets to be sent and received on an interface.” source IPv6 Configuration Guide
So this is a Cisco designed feature. If you want to stop IPv6 neighbor discovery, specify an deny any any ACE, without allowing ICMPv6 NS and NA, if you want to allow it then specifically allow ND.
To properly configure this, I’ll use the cleverly named ACL called EXPLICIT-FIXED-ACL and apply it on R2 Fa0/1 interface towards R3.
R2(config)#ipv6 access-list EXPLICIT-FIXED-ACL R2(config-ipv6-acl)# permit tcp any any R2(config-ipv6-acl)# permit icmp any any nd-ns R2(config-ipv6-acl)# permit icmp any any nd-na R2(config-ipv6-acl)# deny ip any any log
R2(config-ipv6-acl)#int fa0/1 R2(config-if)#ipv6 traffic-filter EXPLICIT-FIXED-ACL IN
Telnet should work to R3..
R2#telnet 2001:23::3 Trying 2001:23::3 ... Open User Access Verification Username: kemot-net.com Password: R3>
… and R2 sees R3 as an IPv6 neighbor:
R2#sh ipv6 neig IPv6 Address Age Link-layer Addr State Interface 2001:23::3 0 c202.13b8.0000 REACH Fa0/1 FE80::C002:13FF:FEB8:0 0 c202.13b8.0000 DELAY Fa0/1
Hope this make sense, I could see this being used in a very evil troubleshooting lab yet to be written. If you would like to play around with this scenario, you can download the GNS3 project file IPv6 ACLs.