There is one feature available on FortiGate, and I think you should know it, as it modifies a bit what we know about stateful firewalls. In past every packet was treated individually and you had to create policies in both directions. With stateful firewalls we can track connections, and by checking couple of attributes, we can treat them as part of the same session. For example when you initiate connection from a host1 to host2, the returning connection from host2 to host1 will be treated as part of the same connection (session). They have to have the same source/destination and destination/source IPs, port numbers and interfaces.There is an exception from this rule and FortiGate in some specific cases can accept connections on port which was not used in the initial connection. Let me explain how it works on the below example:
The host1 has a default gateway on R1 (10.0.1.2), but you may notice that it is not the optimal path to host2 subnet. When we analyze the packet flow from host2 the path is different:
a) host1 -> R1 -> FG2 -> host2
b) host2 -> FG2 -> host1
So the path is asymmetric. How FortiGate deals with such problems?
Let's start from the case where host2 initiates the SSH connection to the host1. From the FortiGate the optimal path is port3->port1 as 10.0.1.0/24 subnet is directly connected. But what with a reply packet from the host1 which will be sent to R1? FortiGate can deal with such scenario and as long as firewall policy accepts it, both connections will be treated as one session:
As you can see connections from port3->port1 and port2->port3 are part of the same session.
Now let's analyze the case where host1 initiates the SSH connection to host2:
FG2 # get router info routing-table all
Routing table for VRF=0
Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
* - candidate default
S* 0.0.0.0/0 [10/0] via 10.0.2.2, port2
C 10.0.1.0/24 is directly connected, port1
C 10.0.2.0/24 is directly connected, port2
C 10.0.3.0/24 is directly connected, port3
FG2 #
As you can see as long as you have an active route to 10.0.1.2 via port2 (it can be a default gateway) the reply packet goes via non-optimal path.
The host1 has a default gateway on R1 (10.0.1.2), but you may notice that it is not the optimal path to host2 subnet. When we analyze the packet flow from host2 the path is different:
a) host1 -> R1 -> FG2 -> host2
b) host2 -> FG2 -> host1
So the path is asymmetric. How FortiGate deals with such problems?
Let's start from the case where host2 initiates the SSH connection to the host1. From the FortiGate the optimal path is port3->port1 as 10.0.1.0/24 subnet is directly connected. But what with a reply packet from the host1 which will be sent to R1? FortiGate can deal with such scenario and as long as firewall policy accepts it, both connections will be treated as one session:
FG2 # id=20085 trace_id=437 func=print_pkt_detail line=5333 msg="vd-root:0 received a packet(proto=6, 10.0.3.100:42862->10.0.1.100:22)
from port3. flag [S], seq 2695315579, ack 0, win 29200"
id=20085 trace_id=437 func=init_ip_session_common line=5493 msg="allocate a new session-0001e45d"
...
id=20085 trace_id=437 func=init_ip_session_common line=5493 msg="allocate a new session-0001e45d"
...
id=20085 trace_id=437 func=vf_ip_route_input_common line=2590 msg="find a route: flag=04000000
gw-10.0.1.100 via port1"
id=20085 trace_id=437 func=iprope_fwd_check line=710 msg="in-[port3], out-[port1], skb_flags-02000000, vid-0, app_id: 0, url_cat_id: 0"
...
id=20085 trace_id=437 func=iprope_fwd_check line=710 msg="in-[port3], out-[port1], skb_flags-02000000, vid-0, app_id: 0, url_cat_id: 0"
...
id=20085 trace_id=437 func=fw_forward_handler line=749 msg="Allowed by Policy-1:"
id=20085 trace_id=438 func=print_pkt_detail line=5333 msg="vd-root:0 received a packet(proto=6, 10.0.1.100:22->10.0.3.100:42862) from port2. flag [S.], seq 85123908, ack 2695315580, win 28960"
id=20085 trace_id=438 func=resolve_ip_tuple_fast line=5408 msg="Find an existing session, id-0001e45d, reply direction"
id=20085 trace_id=438 func=vf_ip_route_input_common line=2590 msg="find a route: flag=00000000 gw-10.0.3.100 via port3"
id=20085 trace_id=439 func=print_pkt_detail line=5333 msg="vd-root:0 received a packet(proto=6, 10.0.3.100:42862->10.0.1.100:22) from port3. flag [.], seq 2695315580, ack 85123909, win 229"
id=20085 trace_id=439 func=resolve_ip_tuple_fast line=5408 msg="Find an existing session, id-0001e45d, original direction"
id=20085 trace_id=439 func=vf_ip_route_input_common line=2590 msg="find a route: flag=04000000 gw-10.0.1.100 via port1"
id=20085 trace_id=439 func=iprope_fwd_check line=710 msg="in-[port3], out-[port1], skb_flags-02000000, vid-0, app_id: 0, url_cat_id: 0"
id=20085 trace_id=438 func=print_pkt_detail line=5333 msg="vd-root:0 received a packet(proto=6, 10.0.1.100:22->10.0.3.100:42862) from port2. flag [S.], seq 85123908, ack 2695315580, win 28960"
id=20085 trace_id=438 func=resolve_ip_tuple_fast line=5408 msg="Find an existing session, id-0001e45d, reply direction"
id=20085 trace_id=438 func=vf_ip_route_input_common line=2590 msg="find a route: flag=00000000 gw-10.0.3.100 via port3"
id=20085 trace_id=439 func=print_pkt_detail line=5333 msg="vd-root:0 received a packet(proto=6, 10.0.3.100:42862->10.0.1.100:22) from port3. flag [.], seq 2695315580, ack 85123909, win 229"
id=20085 trace_id=439 func=resolve_ip_tuple_fast line=5408 msg="Find an existing session, id-0001e45d, original direction"
id=20085 trace_id=439 func=vf_ip_route_input_common line=2590 msg="find a route: flag=04000000 gw-10.0.1.100 via port1"
id=20085 trace_id=439 func=iprope_fwd_check line=710 msg="in-[port3], out-[port1], skb_flags-02000000, vid-0, app_id: 0, url_cat_id: 0"
As you can see connections from port3->port1 and port2->port3 are part of the same session.
Now let's analyze the case where host1 initiates the SSH connection to host2:
id=20085 trace_id=669 func=print_pkt_detail line=5333
msg="vd-root:0 received a
packet(proto=6, 10.0.1.100:42692->10.0.3.100:22) from port2. flag [S], seq 276815006, ack 0, win 29200"
id=20085 trace_id=669 func=init_ip_session_common line=5493
msg="allocate a new session-00066c8e"
id=20085 trace_id=669 func=iprope_dnat_check line=4921 msg="in-[port2], out-[]"
id=20085 trace_id=669 func=iprope_dnat_check line=4934
msg="result: skb_flags-02000000, vid-0, ret-no-match, act-accept,
flag-00000000"
id=20085 trace_id=669 func=vf_ip_route_input_common
line=2590 msg="find a route:
flag=00000000 gw-10.0.3.100 via port3"
id=20085 trace_id=669 func=iprope_fwd_check line=710
msg="in-[port2], out-[port3],
skb_flags-02000000, vid-0, app_id: 0, url_cat_id: 0"
…
id=20085 trace_id=669 func=fw_forward_handler line=749
msg="Allowed by Policy-1:"
id=20085 trace_id=670 func=print_pkt_detail line=5333
msg="vd-root:0 received a
packet(proto=6, 10.0.3.100:22->10.0.1.100:42692) from port3. flag [S.],
seq 1107817798, ack 276815007, win 28960"
id=20085 trace_id=670 func=resolve_ip_tuple_fast line=5408
msg="Find an existing session,
id-00066c8e, reply direction"
id=20085 trace_id=670 func=vf_ip_route_input_common
line=2590 msg="find a route:
flag=04000000 gw-10.0.2.2 via port2"
id=20085 trace_id=671 func=print_pkt_detail line=5333
msg="vd-root:0 received a
packet(proto=6, 10.0.1.100:42692->10.0.3.100:22) from port2. flag [.],
seq 276815007, ack 1107817799, win 229"
id=20085 trace_id=671 func=resolve_ip_tuple_fast line=5408
msg="Find an existing session,
id-00066c8e, original direction"
FG2 # get router info routing-table all
Routing table for VRF=0
Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
O - OSPF, IA - OSPF inter area
N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
E1 - OSPF external type 1, E2 - OSPF external type 2
i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
* - candidate default
S* 0.0.0.0/0 [10/0] via 10.0.2.2, port2
C 10.0.1.0/24 is directly connected, port1
C 10.0.2.0/24 is directly connected, port2
C 10.0.3.0/24 is directly connected, port3
FG2 #
As you can see as long as you have an active route to 10.0.1.2 via port2 (it can be a default gateway) the reply packet goes via non-optimal path.
Hi and thanks for the article. I'm surprised that in case#1 the Fortigate considers the packet coming back from port2 to be part of the same session "0001e45d". I thought it would just block the packet as the interface is not the same is identified in the session establishment.
ReplyDelete