Passive FTP - Preserve Pool Member Ephemeral Port¶
Contributed by: Jason Adams - j.adams@f5.com¶
Description¶
This iRule fills a need to preserve an FTP Servers Passive Data
Channel Port.
F5 Networks has developed an iRule command to address this in v11.5.0+
called FTP::port, but it has yet to come to full fruition, and seems
to have provided more bugs than solutions.
This was tested on v11.6.0, but I fully expect it to work on older
versions of BIG-IP as well.
iRule Source¶
# Created by Jason Adams 2015-02-11 to make compatible with v11.6.0+
# This iRule performs NAT on ftp / ftps in ccc-mode
# It searches for "227 Entering Passive Mode" in data from
# the server and replaces it with the correct IP-address
# so that the client will connect to the correct address
# It will then create a dynamic listener on the specified VLAN
#
# NOTE: Replace the value of static::FTP_DATA_TIMEOUT to the idle timeout (in seconds)
# of your FTP Data Channel
#
# NOTE: Replace the value of static::VLAN to the name of the vlan you want to create
# your dynamic Virtual Server
# Set DEBUG to 1 to get debug-logging of this iRule in /var/log/ltm
when RULE_INIT {
set static::DEBUG 1
set static::FTP_DATA_TIMEOUT 300
set static::VLAN YOUR-VLAN-NAME-HERE
}
when SERVER_CONNECTED {
if { $static::DEBUG } { log local0.debug "FTP connection from [IP::client_addr]:[TCP::client_port]. \
Mapped to [serverside {IP::local_addr}]:[serverside {TCP::local_port}] \
-> [IP::server_addr]:[serverside {TCP::remote_port}]" }
TCP::release
TCP::collect
}
when SERVER_DATA {
# If in debug mode, log payload of received packet
if { $static::DEBUG } { log local0.debug "payload <[TCP::payload]>" }
# check if payload contains the string we want to replace
if { [TCP::payload 50] contains "227 Entering Passive Mode" } {
# If in debug mode, log that the payload matched
if { $static::DEBUG } { log local0.debug "payload matched" }
# use a regular expression to save the ephemeral port $first $second
regexp {[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},([0-9]{1,5}),([0-9]{1,5})} [TCP::payload] all first second
# Set the Virtual Server IP Address as single octet variables
clientside {scan [IP::local_addr] %d.%d.%d.%d ip1 ip2 ip3 ip4}
# If in debug mode, log to ensure we are collecting the proper VS IP Address
if { $static::DEBUG } { log local0.debug "ip=$ip1,$ip2,$ip3,$ip4" }
# Search and replace the IP Address portion of the PASV Command to match the Virtual Server IP Address
regsub {[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3}} [TCP::payload] "$ip1,$ip2,$ip3,$ip4" packetdata
# Replace the TCP Payload with the string above.
TCP::payload replace 0 [TCP::payload length] $packetdata
# If in debug mode, log the new payload.
if { $static::DEBUG } { log local0.debug "changed payload <[TCP::payload]>" }
# Calculate the decimal value of the dynamic ephemeral port
# This will be used to create our dynamic Virtual Server for the FTP Data Connection
set ephemeralport [expr [expr $first * 256] + $second]
# If debugging, log the ephemeral port number.
if { $static::DEBUG } { log local0.debug "ephemeralport=$ephemeralport" }
# Create a dynamic Virtual Server to listen for the FTP Data Connection
listen {
proto 6
timeout $static::FTP_DATA_TIMEOUT
bind $static::VLAN $ip1.$ip2.$ip3.$ip4 $ephemeralport
server [IP::server_addr] $ephemeralport
allow [IP::client_addr]
}
# Release our variables
unset all
unset first
unset second
unset ip1
unset ip2
unset ip3
unset ip4
unset packetdata
}
# release the packet, and collect a new one
TCP::release
TCP::collect
}
The BIG-IP API Reference documentation contains community-contributed content. F5 does not monitor or control community code contributions. We make no guarantees or warranties regarding the available code, and it may contain errors, defects, bugs, inaccuracies, or security vulnerabilities. Your access to and use of any code available in the BIG-IP API reference guides is solely at your own risk.