One of the security features Cisco ASA provides for new connections is to ensure the 3-Way Handshake is completed between two hosts before allowing any further tcp traffic between the two hosts. The 3-Way Handshake is simply exchanging the SYN, SYN-ACK and ACK between two hosts, each sends the relevant packets based if it acts as a sender or a receiver. If the ASA should see a SYN-ACK packet sent by a host to another before seeing the initial SYN packet, the traffic will be dropped. Similar if the ASA should see an ACK packet before seeing the previous two packets SYN and SYN-ACK exchanged between the two hosts. The ASA does this by inspecting each packet and creating a state for each connection. This a nice feature, however, in some legitimate scenarios it might create some issues and preventing the traffic from being delivered between the two hosts. Let’s see what would be an example scenario for this, and how to apply the fix.
In this topology we have two hosts, one is a client and another is a server. The client and the server are located in two different subnets. The client uses the ASA as its default gateway, and the server uses the router that is dividing the two broadcast domains as its default gateway. Please note that on this lab I am using an IOSv to simulate the server with ip routing disabled. ICMP is allowed through the ASA, so the client can ping the server with no restrictions, and the client can also connect to the server on port 80/tcp.
Here are the interested parts of the configuration on all the involved devices:
Now let’s do some basic connectivity check between the client and the server by trying to ping the server and connecting to port 80/tcp, and then inspect the results:
There are no access lists on the ASA or policy restrictions on the server to prevent ICMP traffic from passing through. Let’s look at the ASA logs and see if we get any useful messages that would help us in finding out why pings are failing:
Right, one of the default behaviors of the ASA is to prevent traffic from entering and exiting the same interface without passing through another interface. The fix for this is simple, we just need to apply a command to change this default behavior:
Let’s try again and see if it works:
As we can see, ping is now successful. Now let’s try to connect to the server on port 80/tcp:
It does not seem to be working. Let’s try to use telnet and try to connect directly to port 80/tcp instead of using a browser:
Still does not work. Maybe port 80/tcp is not open on the server?! let’s verify this from the ASA by initiating tcp ping to the server IP address on port 80:
The ASA seems to be able to reach the server on port 80/tcp as we can see. Let’s run some packet capture on the ASA’s inside interface and see if we get anything useful to help us finding out the issue when we try again to connect to port 80/tcp from the client:
As we can see there are some Syn packets and some resets. The flow does not seem to be healthy. Let’s run some packet capture to see if there are any drops on the ASA related to this traffic:
As we can see from the packet capture, the ASA is dropping some packets we are sending from the clients to the server. The reason behind this is because from the ASA perspective those packet are not SYN packets and the first tcp packet is not a SYN. Let’s try to interpret this message by analyzing the traffic flow.
When the client initiates tcp traffic to the server, the client first sends a SYN packet through its default gateway which is the ASA inside interface IP address. This is because the server is located on a different subnet. When the ASA receives this SYN packet it does a lookup on its routing table to see if it has a route to the server. In our case the ASA has a route to the server’s subnet 192.168.1.0/24 via the next hop 192.168.0.112 which is the router interface facing the ASA.
The ASA routes the SYN packet to the router, and the router then routes the packet to the server via its connected interface GigabitEthernet0/1. When the server responds back with a SYN-ACK packet to the client, the server sends the packet to its default gateway, again this is because the client is located on a different subnet.
The router then does a lookup on its routing table to see if it has a route to the client, in our case the router has a route to the client’s subnet via its directly connected interface GigabitEthernet0/0. This means that from the router perspective the traffic destined to the subnet 192.168.0.0/24 will be routed via its directly connected interface GigabitEthernet0/0, so the ASA is not being involved at all in this piece.
When the client receives the SYN-ACK packet from the router, the client finally sends the ACK packet to the server to complete the 3-Way Handshake. This final packet will be sent again through the ASA. Here the ASA drops the packet and reset the connection.
The reason behind this is because the ASA treats this traffic as illegitimate since it did not see the whole 3-Way Handshake flow. The ASA in this case has seen the SYN packet and the ACK packet, but it did not see the SYN-ACK packet the server sent to the client, because as we have seen, that packet was sent directly the router to the client.
You might be wondering why the traffic sent by the client to the server on port 80/tcp fails, but the pings were successful. The reason is because with ICMP traffic there is no 3-Way Handshake at all. Also ICMP echo and echo-reply packets are kind of independent.
One thing worth mentioning here is that in this specific scenario even if we don’t enable the ICMP inspection on the ASA global policy map, pings will still be successful. The reason as we could see is because the ASA in this case does not receive the echo-reply packets as those are sent directly by the router to the client.
This issue could be seen in topologies similar to the one we have on this lab, and also on topologies where legitimate asymmetric routing could happen. In both these cases we need to fix this issue by bypassing this default check by the ASA. The way to fix this is going to be by applying a policy map on the ASA interested interface where the traffic is being routed, which is the inside interface in our case. Let’s apply the required configuration and test again.
This fix will be compound of three objects, an access list to match the traffic we want the TCP State Bypass to be applied, a class map to tie the access list in a policy map, a policy map to define the action and finally we need to apply the policy map on the interested interface which is in our case the ASA inside interface.
Step 1: Create the access list to match traffic sent by the client to the server on port 80/tcp:
Step 2: Create the class map and bind the access list we created:
Step 3: Create the policy map and applying the TCP State Bypass to the class map we created:
Step 4: Apply the policy map we created to the ASA inside interface:
Now that we applied all the required configuration, let’s test again and see if the tcp traffic now is working:
As we can see now the client can successfully connect to the server web page on port 80/tcp.
I hope you enjoyed reading this post, and as always, I would love to hear your feedback. Thanks for reading!