FF_ Firewall_iRule

Problem this snippet solves:

I wrote this iRule since I needed a way to handle ACL-based firewalling on LTM. The code relies on a datagroup that holds the ACLs. The iRule itself needs to be applied to an LTM "IP Forwarding" Virtual Server, listening on 0.0.0.0/0.0.0.0 all protocols and on all VLANs that have to secured by this "ACL-based Firewall".

Code :

#
# FF Firewall iRule for TMOS v10
# 2011 Fabrizio Fiorucci 
#
# Release history:
# 20110217 - 0.1 - Initial release
# 20110218 - 0.2 - Improved datagroup lookup speed, full support for subnets (src and dest) and wildcard ports and for TCP/UDP/ICMP
#
# This iRule relies on a String datagroup called "ACL_Datagroup" that contains a list of allowed ACLs
# Datagroup entries format is:
# DSTIP DSTPORT PROTOCOL/ICMP_TYPE := SRCIP SRCPORT
#
# Examples:
# 85.18.58.110 110 6 := 172.30.30.0/24 0
# 85.18.58.110 22 17 := 172.30.30.40 0
# 93.62.218.238 8 1  := 172.30.30.40 0 <-- If protocol is 1 (ICMP) dstport is the ICMP type (8 for echo request)
#
# Protocol numbers taken from /etc/protocols (1=ICMP, 6=TCP, 17=UDP)
#
# Sample Datagroup is:
#
# class ACL_Datagroup {
#  {
#   "65.61.115.222 80 6" { "192.168.1.0/24 0" }
#   "65.61.115.222 443 6" { "192.168.1.1 0" }
#   "8.8.8.8 8 1" { "192.168.0.0/16 0" }
#   "8.8.8.8 53 17" { "192.168.0.0/16 53" }
#  }
# }
#
# That defines the following ACLs:
# allow from 192.168.1.0/24 (all src ports) to 65.61.115.222 TCP/80
# allow from 192.168.1.1/32 (all src ports) to 65.61.115.222 TCP/443
# allow from 192.168.0.0/16 (all src ports) to 8.8.8.8 ICMP/Echo Request (ICMP type 8)
# allow from 192.168.0.0/16 (src port 53/UDP) to 8.8.8.8 UDP/53
#

when CLIENT_ACCEPTED {

set fw_debug 1
set dropRequest 1
set src_ip [IP::client_addr]
set src_port [TCP::client_port]
set dst_ip [IP::local_addr]
set dst_port [TCP::local_port]
set request_protocol [IP::protocol]
set dumpRequest "\[$src_ip\:$src_port\] -> \[$dst_ip\:$dst_port\] protocol $request_protocol"

if { $fw_debug } { log local0. "Checking request: $dumpRequest" }

# Looks up destination IP, destination_port/icmp_type and protocol (x.x.x.x port proto)
set ACLDstLookup "$dst_ip $dst_port $request_protocol"
set ACLMatchedList [class get ACL_Datagroup $ACLDstLookup]

if { $fw_debug } { log local0. "Destination $ACLDstLookup found [llength $ACLMatchedList] matches" }

if { [llength $ACLMatchedList] == 0 } {

if { $fw_debug } { log local0. "No ACL found for $dumpRequest - trying for wildcard dstport" }

set ACLMatchedList [class get ACL_Datagroup "$dst_ip 0"]

if { $fw_debug } { log local0. "Destination $ACLDstLookup found [llength $ACLMatchedList] wildcard dstsport matches" }

if { [llength $ACLMatchedList] == 0 } {
if { $fw_debug } { log local0. "No ACL found for $dumpRequest with wildcard dstport" }
discard
}
}

# ACL List is built like { DSTIP DSTPORT } { SRCIP SRCPORT }
# thus we need to skip all items in odd-numbered positions
foreach thisACL $ACLMatchedList {
if { $thisACL equals $ACLDstLookup } {
if { $fw_debug } { log local0. "Skipping odd-numbered item $thisACL" }
} else {
if { $fw_debug } { log local0. "Checking ACL $thisACL" }

set thisACLSrcIP [lindex $thisACL 0]
set thisACLSrcPort [lindex $thisACL 1]

if { [IP::addr $src_ip equals $thisACLSrcIP] || [IP::addr 0.0.0.0 equals $thisACLSrcIP] } {
if { $fw_debug } { log local0. "ACL Source IP Matches" }
if { $src_port == $thisACLSrcPort || $thisACLSrcPort == 0 } {
if { $fw_debug } { log local0. "ACL Source Port Matches, forwarding $dumpRequest" }
set dropRequest 0

# Match found, exits foreach loop
break
} else {
if { $fw_debug } { log local0. "ACL Source Port doesn't match, dropping" }
}
} else {
if { $fw_debug } { log local0. "ACL Source IP doesn't match, dropping" }
}
}
}

if { $dropRequest == 1 } {
if { $fw_debug } { log local0. "Dropping $dumpRequest" }
discard
} else {
if { $fw_debug } { log local0. "Forwarding $dumpRequest" }
forward
}
}
Published Mar 17, 2015
Version 1.0
No CommentsBe the first to comment