Passive FTP - Preserve Pool Member Ephemeral Port

Contributed by: Jason Adams -


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

    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}]" }

    # 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


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.