FraserK_151071
Jun 02, 2015Nimbostratus
iRule catching HTTP_REQUEST made to other Virtual Server
I'm experiencing a problem with apparently conflicting LTM iRules.
I have two Virtual Servers set up (let's name one
VS_TEST
and the other VS_PREP
). Each has a different iRule applied to it (iRule_TEST
and iRule_PREP
). These iRules perform the same function - they intercept incoming HTTP requests, extract some data, and then forward the data to an application running on the corresponding Pool (POOL_TEST
, POOL_PREP
) in the form of a HTTP GET
. The application returns either Allow
or Deny
, informing the iRule whether to allow the request to pass through, or to reject it. Each Pool has only one node.
Normally these iRules behave correctly. A request made to
VS_TEST
will be handled by iRule_TEST
and send information to the application running on the single node in POOL_TEST
.
There is a second type of request made to the Virtual Servers, let's call these password requests as they retrieve a password that is randomly generated by the server. I need to intercept the response by the sever and extract the password, and then send it to the same application as before. I add
HTTP_RESPONSE
and HTTP_RESPONSE_DATA
events to the iRules.
However, when I add
HTTP_RESPONSE
and HTTP_RESPONSE_DATA
events to both iRules, there is a conflict which depends on the order the iRules are updated.
For example, if I update
iRule_TEST
first, followed by iRule_PREP
:
- Requests made to
are handled byVS_TEST
iRule_TEST
sends the data of the request to the single node iniRule_TEST
!POOL_PREP
- Requests made to
are handled byVS_PREP
and the data of the request is sent to the single node iniRule_PREP
, as expected.POOL_PREP
How is this possible when both
POOL_TEST
and the IP:port of its corresponding node are explicitly mentioned in iRule_TEST
? The exact opposite happens if I update iRule_TEST
first.
iRule_TEST
when RULE_INIT {
set ip:port of destination node (specific to TEST)
set static::serveripport "192.168.10.80:80"
}
when HTTP_REQUEST {
if {([HTTP::query] starts_with "message=")} {
This is a request we want to intercept
log local0. "Raw request: [HTTP::query]"
Extract the actual message
regexp {(message\=)(.*)} [HTTP::query] -> garbage query
Connect to node. Use catch to handle errors. Check if return value is not null.
if {[catch {connect -timeout 1000 -idle 30 -status conn_status $static::serveripport} conn_id] == 0 && $conn_id ne ""} {
Send TCP payload to application
set data "GET /Service.svc/checkmessage?message=$query"
set send_info [send -timeout 1000 -status send_status $conn_id $data]
Receive reply from application
set recv_info [recv -timeout 1000 -status recv_status $conn_id]
Allow or deny request based on application response
if {$recv_info contains "Allow"} {
pool POOL_TEST
} elseif {$recv_info contains "Deny"} {
reject
}
Tidy up
close $conn_id
} else {
reject
}
}
}
Update below
when HTTP_RESPONSE {
Collect all 200 responses
if {[HTTP::status == 200} {
set content_length [HTTP::header "Content-Length"]
HTTP::collect $content_length
}
}
when HTTP_RESPONSE_DATA {
if {[catch {binary scan [HTTP::payload] H* payload_hex} error] ne 0} {
log local0. "Error whilst binary scanning response: $error"
} else {
if {some hex string matches} {
collect password from response and set to $password
Connect to node. Use catch to handle errors. Check if return value is not null.
if {[catch {connect -timeout 1000 -idle 30 -status conn_status $static::serveripport} conn_id] == 0 && $conn_id ne ""} {
Send TCP payload to application
set data "GET /Service.svc/submitresponse?password=$password"
set send_info [send -timeout 1000 -status send_status $conn_id $data]
Tidy up
close $conn_id
}
}
HTTP::release
}
iRule_PREP
is identical, save for references to POOL_TEST
and the static::serveripport
address.set static::serveripport "192.168.10.80:80"
The above variable is global, static, and is evaluated each time the irule is updated, or whenever tmm starts (See Rule_Init ).
I hope that you haven't used the same name in both of your irules?