Forum Discussion

Matthew_McCleme's avatar
Matthew_McCleme
Icon for Nimbostratus rankNimbostratus
Oct 11, 2007

ClamAV iRule

ClamAV has an FTP'esque protocol in that initial connections are made via a control connection, which then requests a stream port to send the actual files to be scanned on. I've written and tested the following iRule, however there might be a few issues with it:


when SERVER_CONNECTED {
    log "server connected"
    TCP::collect
}
when SERVER_DATA {
    log "server data"
    set sdata [TCP::payload]
    if { $sdata contains "PORT" } {
        set ::clamport [regexp -inline {[0-9]+} [TCP::payload]]
        log "I have $::clamport"
        listen {
            proto 6
            timeout 30
            bind [LINK::vlan_id] [peer {IP::local_addr}] $::clamport
            server [LB::server addr] $::clamport
            allow [IP::client_addr]
        }
    }
    TCP::release
    TCP::collect
}

This iRule is attached to an internal VS listening on 3310, SNAT automap, round robin load balancing an no persistance profile.

Now, the main issue I can see is that $::clamport is global(assuming my reading of the TCL manual is right), however listen{} doesn't appear to work with non-global variables. If I simply used $clamport TCL would complain that the variable doesn't exist.

Any suggestions on improving the iRule? I don't think speed is hugely important as the control connection is pretty lightweight however the use of globals makes me nervous and I'm hoping that I've missed something and listen can be made to work with local variables.

2 Replies

  • Updated iRule to get around the fact that ::port is global and to avoid possible stomping of the variable. Although I'm guessing that no one is all that interested in load balancing ClamAV considering how quickly this topic started sinking to the bottom.

    
    when RULE_INIT {
        array set ::clamp { }
    }
    when SERVER_CONNECTED {
        log "server connected"
        TCP::collect 1
    }
    when SERVER_DATA {
        log "server data"
        set sdata [TCP::payload]
        if { $sdata contains "PORT" } {
            set ::clamp([IP::client_addr][TCP::client_port]) [regexp -inline {[0-9]+} [TCP::payload]]
            log "I have $::clamp([IP::client_addr][TCP::client_port])"
            listen {
                proto 6
                timeout 10
                bind [LINK::vlan_id] [peer {IP::local_addr}] $::clamp([IP::client_addr][TCP::client_port])
                server [LB::server addr] $::clamp([IP::client_addr][TCP::client_port])
                allow [IP::client_addr]
            }
        array unset ::clamp([IP::client_addr][TCP::client_port])
        }
        TCP::release
        TCP::collect 1
    }
    • MattB_MA_170307's avatar
      MattB_MA_170307
      Icon for Nimbostratus rankNimbostratus

      FWIW: We're standing up a pool of ClamAV containers to check incoming SMTP services post-decryption, and this iRule worked flawlessly. Thanks very much for taking the time to write it out!