Forum Discussion
Wolfgang_Blau_7
Nimbostratus
Jun 11, 2008Check protocol in iRule
I'm using the F5 LTM to intercept HTTP traffic. Sometimes clients "mis-use" port 80 for other protocols like SSH, FTP, etc and I've written an iRule to verify the connection contains HTTP traffic:
rule http_check {
when RULE_INIT {
set ::http_cmds [list \
OPTIONS \
GET \
HEAD \
POST \
PUT \
DELETE \
TRACE \
CONNECT \
]
}
when HTTP_REQUEST {
if { [matchclass [HTTP::method] equals $::http_cmds]} {
pool transparent_pool
}
else {
forward
}
}
The virtual server looks like
virtual http_redir {
destination any:http
mirror enable
ip protocol tcp
translate service disable
profile http tcp
persist source_addr
pool transparent_pool
rule http_check
vlans xxx enable
}
The iRule works fine for HTTP traffic but fails for e.g. SSH. When running SSH over port 80 the client hangs and the iRule is not fired. A tcpdump towards the client shows the TCP handshake but nothing on the server side. I've the impression that LTM is waiting for client data but that won't happen w/. SSH as the server sends data first.
Any recommendations on how to solve this?
Thanks.
- hoolio
Cirrostratus
The HTTP_REQUEST event is triggered when the HTTP headers in the request are parsed. If the request doesn't contain valid HTTP, the event won't be triggered. Events following HTTP_REQUEST will not be triggered either.when RULE_INIT { Log debug to /var/log/ltm? 1=yes, 0=no. set ::http_debug 1 } when CLIENT_ACCEPTED { if {$::http_debug}{ log local0. "[IP::client_addr]:[TCP::client_port]: Collecting data"} Trigger the collection of at least 15 bytes of data. TCP::collect 15 } when CLIENT_DATA { Log the collected data. if {$::http_debug}{ log local0. "[IP::client_addr]:[TCP::client_port]: collected payload ([TCP::payload length]): [TCP::payload]"} Check if the collected payload doesn't contain HTTP. if { not ([TCP::payload] contains "HTTP") } { Disable the HTTP profile as this doesn't appear to be HTTP. HTTP::disable Forward request? Else, it will be sent to the VIP's default pool. if {$::http_debug}{ log local0. "[IP::client_addr]:[TCP::client_port]: Releasing. We collected this much data: [TCP::release]"} Stop processing the rule for this connection. return } } when HTTP_REQUEST { This event will only be triggered if the HTTP profile is enabled if {$::http_debug}{ log local0. "[IP::client_addr]:[TCP::client_port]: request was parsed as HTTP"} Perform validation of HTTP method? }
- Mainsail_45878
Nimbostratus
What happens if the client uses a protocol, such as ssh, to the virtual server where the client doesn't send any data until it has received some data from the server? Will the connection not hang as the CLIENT_DATA event will never be triggered? - Wolfgang_Blau_7
Nimbostratus
Aaron, - hoolio
Cirrostratus
I agree it would be a kludge to let the first connection hang and then know to disable HTTP for the subsequent connections. I think it would technically be possible to do using the steps you've outlined. You can get the destination IP address (ie, what IP the client requested) using IP::local_addr in the clientside context. The server IP address doesn't exist in the CLIENT_ACCEPTED event as the server side connection hasn't been established yet. So running 'serverside {IP::remote_addr} will result in a TCL error. You could use the session table (Click here) to record the client and destination IP's with a timeout value. If a matching session table exists in CLIENT_ACCEPTED, then disable HTTP. If it doesn't exist, then add a session table entry in CLIENT_ACCEPTED. In HTTP_REQUEST, you would remove the session table entry as you know the client-server IP pair is HTTP. - Hamish
Cirrocumulus
I still think you'll hit a chicken & egg problem there... Or even a why bother, depending on how you look at it, and exactly WHY you want to do this. - rob_carr
Cirrocumulus
Posted By hoolio on 06/13/2008 2:58 AM
- spark_86682Historic F5 AccountHow to have your client and server too:
when CLIENT_ACCEPTED { TCP::collect 20 0 } when SERVER_CONNECTED { TCP::collect 20 } when CLIENT_DATA { log local0. "Got client data first" event disable all } when SERVER_DATA { log local0. "Got server data first" event disable all }
when CLIENT_ACCEPTED { TCP::collect 8 0 set server_hold 0 log local0. "Buildup -- collecting data" } when CLIENT_DATA { Since we're here, the clientside sent data first log local0. "Got client data" Extract the HTTP command, if any. Find the first space in the text, and trim off any characters after it. If there's no space, $spcpos will be -1, so [string range] will return the empty string. set spcpos [string first " " [TCP::payload 8]] set command [string range [TCP::payload 8] 0 $spcpos] Now see if this is a recognized HTTP command if { [matchclass $command equals ::http_cmds] } { log local0. "HTTP traffic -- command $command" if { $server_hold eq 1 } { serverside { TCP::release } } event SERVER_CONNECTED disable event SERVER_DATA disable return } log local0. "Not HTTP -- staying in passthru" event disable all } when SERVER_CONNECTED { log local0. "Server connection established" TCP::collect set server_hold 1 } when SERVER_DATA { log local0. "Banner protocol -- staying in passthru" clientside { TCP::release } TCP::release event disable all }
Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects