Forum Discussion

Chris_Day_10331's avatar
Chris_Day_10331
Icon for Nimbostratus rankNimbostratus
Oct 04, 2005

iRule for Server Testing

Good morning,

 

 

Please see attached doc.

 

 

Essentially, what we are trying to achieve is to be able to validate that a single server behind a VS is responding correctly. Ideally, we could rewrite both the request and the response to make the client "think" that it is receiving content from the URL to which they connected, when in fact it has been cloaked to the server and back to the client via header rewrite.

 

 

I'm assuming this must be possible, but have not seen this done before. Perhaps there are some other alternatives on how this could be done differently?

 

 

I could not attach the word document ("Invalid file type or exceeds max size(60kb)" even though the file is 28k) but here is the guts of the content:

 

 

RE: BIG-IP 1500

 

SN: bip071410s / bip071401s

 

Version: 9.0.5

 

 

Environment:

 

 

- 4 web servers: web001, web002, web003, web004

 

- front-ended by single VS on BIG-IP

 

 

Requirements:

 

 

1) Each web server runs multiple sites via HTTP/1.1 host headers:

 

 

companyA.pnimedia.com

 

companyB.pnimedia.com

 

companyC.pnimedia.com

 

 

2) Sites are accessed via virtual server on BIG-IP from front end and back end (via VPN)

 

 

3) We would like to be able to test (connect and verify) each web server independently, ideally through iRules processing.

 

 

Desired functionality:

 

 

http://companyA.test.pnimedia.com/web001

 

- use node web001 AND rewrite request/response to companyA.pnimedia.com

 

http://companyB.test.pnimedia.com/web003

 

- use node web003 AND rewrite request/response to companyB.pnimedia.com

 

 

Ideally, if the above could be specified in some sort of matrix/which statements like this:

 

 

web001 = 10.10.10.101

 

web003 = 10.10.10.113 (etc.)

 

 

I appreciate any comments, recommendations and/or sample iRules!

 

 

Thanks,

 

Chris
  • Chris, I moved your post over to the iRules forum. Next time, if you could post iRules questions over there that would help you get a better response.

     

     

    Also, the forums weren't configured to allow uploads of .doc files. I've modified that so you should be able to post your attachment (and I've bumped the size limit up to 100k).

     

     

    Now, on to your question:

     

     

    As for monitoring, iRules does not have the ability to test an application directly in the code. You will have to rely on either external monitors (if your errors are detected via HTTP content or network connectivity).

     

     

    As for having iRules do some manipulation of the request uri then that has been covered many times here on the forum. Here's a sample on how you can build 2 classes, one for the host mapping and another for the node mapping.

     

     

    *** String data groups ***
    class host_mapping {
      companyA.test.pnimedia.com companyA.pnimedia.com
      companyB.test.pnimedia.com companyB.pnimedia.com
      companyC.test.pnimedia.com companyC.pnimedia.com
    }
    class node_mapping {
      web001 10.10.10.1
      web002 10.10.10.2
      web003 10.10.10.3
    }
    *** Begin iRule *** 
    when HTTP_REQUEST {
       Search for host mapping in the lookup list
      set new_host [findclass [HTTP::host] $::host_mapping " "]
      if { "" ne $new_host } {
         if mapping is found, replace the Host header
        log local0. "Replacing host from [HTTP::host] to $new_host"
        HTTP::header replace "Host" $new_host"
         Pull out the node name as the first element of the uri
         ie. /web001/foo/bar -> web001
        set node_name [lindex [split [HTTP::uri] "/"] 1]
        if { "" ne $node_name } {
           Now look for the node address in the lookup list
          log local0. "Searching for node: $node_name"
          set node_addr [findclass $node_name $::node_mapping " "]
          if { "" ne $node_addr } {
            log local0. "Routing to node: $node_addr " 
            node $node_addr
          } else {
            log local0. "Didn't find node '$node_name' in lookup class"
          }
        } else {
          log local0. "No node name passed in URI"
        }
      } else {
        log local0. "Didn't find [HTTP::host] in lookup class"
      }
    }

     

     

    There may be more elegant ways to extract the node out of the uri without making a list. I'll leave it to you to figure that optimization out.

     

     

    Good luck!

     

     

    -Joe
  • Hey Joe,

     

     

    Thanks a bunch! That is great! One problem I get trying to compile this rule under 9.0.5 is:

     

     

    01070151:3: Rule [PNI_TestProcessor] error:

     

    line 3: [undefined procedure: class] [class host_mapping {

     

    companyA.test.pnimedia.com companyA.pnimedia.com

     

    companyB.test.pnimedia.com companyB.pnimedia.com

     

    companyC.test.pnimedia.com companyC.pnimedia.com

     

    }]

     

     

    Since I am relatively new to this, do you know if I am doing something wrong? I did not see any feature enhancements since 9.0.5 that seem to apply.

     

     

    Also, if you have any whiz bang ideas on how to extract the node out of the URI I am all ears and will owe you a beer or 12.

     

     

    Thanks,

     

    Chris

     

     

    FYI - I am an ex-F5 SE (worked with Vilnis) now working with a reseller (Pro-Data) up in Western Canada.
  • Colin_Walker_12's avatar
    Colin_Walker_12
    Historic F5 Account
    Oh, and about the node name out of the URI, Joe's rule example does that already.

    This section here is the part that performs the lookup:

    
    ...
        Pull out the node name as the first element of the uri
         ie. /web001/foo/bar -> web001
        set node_name [lindex [split [HTTP::uri] "/"] 1]
        if { "" ne $node_name } {
           Now look for the node address in the lookup list
          log local0. "Searching for node: $node_name"
          set node_addr [findclass $node_name $::node_mapping " "]
          if { "" ne $node_addr } {
            log local0. "Routing to node: $node_addr " 
            node $node_addr
    ...

    It does a lookup in this class to relate the "web001" or whatever the hostname is in the URI, to the actual IP of the node:

    
    class node_mapping {
      web001 10.10.10.1
      web002 10.10.10.2
      web003 10.10.10.3
    }

    So, for a request with web001 in the URI, it will be forwarded to 10.10.10.1.

    Hopefully that helps clarify a bit.

    -Colin
  • Hi Guys,

    Thanks for replying - this is truly a big help. I now have a better understanding of how this all works and have been analyzing the results captured with tcpdump. Here is what I am seeing:

    If I issue the following (via telnet):

    telnet blacks.test.pnimedia.com
    GET / HTTP/1.1
    HOST: blacks.pnimedia.com

    Everything works fine and I get the expected content.

    *However*, via the rule I have created according to your posts everything seems to be correct. Is it possible that the HTTP/1.1 header is not being included in the request down to the node? I am seeing this (as expected) in the 'ltm' logs:

    Rule PNI_TestProcessor : Replacing host from blacks.test.pnimedia.com to blacks.pnimedia.com
    Rule PNI_TestProcessor : Searching for node: web002
    Rule PNI_TestProcessor : Routing to node: 10.10.20.51 

    Which is correct!

    Tcpdumps, however, reveal a different story. The external dump captured was as follows:

    tcpdump -w /var/tmp/external.dump -ni external port 80 and host 64.85.47.204

    The internal dump captured was as follows:

    tcpdump -w /var/tmp/internal.dump -ni internal port 80 and host 64.85.47.204

    Both of these are attached, but as a summary it looks like request is malformed.

    For reference, here is what is in the rule "PNI_TestProcessor" which I have attached to the VS in question:

    
    when HTTP_REQUEST {
     Search for host mapping in the lookup list
    set new_host [findclass [HTTP::host] $::host_mapping " "]
    if { "" ne $new_host } {
     if mapping is found, replace the Host header
    log local0. "Replacing host from [HTTP::host] to $new_host"
    HTTP::header replace "Host" $new_host"
     Pull out the node name as the first element of the uri
     ie. /web001/foo/bar -> web001
    set node_name [lindex [split [HTTP::uri] "/"] 1]
    if { "" ne $node_name } {
     Now look for the node address in the lookup list
    log local0. "Searching for node: $node_name"
    set node_addr [findclass $node_name $::node_mapping " "]
    if { "" ne $node_addr } {
    log local0. "Routing to node: $node_addr " 
    node $node_addr
    } else {
    log local0. "Didn't find node '$node_name' in lookup class"
    }
    } else {
    log local0. "No node name passed in URI"
    }
    } else {
    log local0. "Didn't find [HTTP::host] in lookup class"
    }
    }

    Do you guys have any ideas on this?
  • It doesn't look like your dumps were added. I've just added ".txt" as a valid attachment extension to the forums so if you could repost them with a .txt extension that would be great.

     

     

    Also, in looking at your post, you are telnetting to the host but are not specifying port 80 so telnet should be defaulting to the default telnet port of 23 which shouldn't route to your web server. You said you received the correct response so I'm a bit confused how you did.

     

     

    Also, if you could specify why you believe the request is invalid even though it returned a correct response that would help us out.

     

     

    -Joe
  • My bad, the "80" for TCP/80 was on the end of that telnet string. I'll save you the output of that command ;-) I believe that the problem might be that this part:

     

     

    GET / HTTP/1.1

     

    HOST: blacks.pnimedia.com

     

     

    ...is not getting passed through to the host. Please see attached dumps, now as .txt.

     

     

    Cheers,

     

    Chris
  • In looking at your traces, they contain the following:

    13:45:35.179821 64-85-47-204.ip.van.radiant.net.46721 
      > 10.10.10.100.http: P 1:421(420) ack 1 win 5840 
       (DF)
    0x0000   4500 01d8 6996 4000 3906 51fb 4055 2fcc        E...i.@.9.Q.@U/.
    0x0010   0a0a 0a64 b681 0050 db6d cd96 3ea3 4800        ...d...P.m..>.H.
    0x0020   8018 16d0 bf12 0000 0101 080a 01a4 06bc        ................
    0x0030   af8c 12ea 4745 5420 2f77 6562 3030 322f        ....GET./web002/
    0x0040   6465 6661 756c 742e 6173 7078 2048 5454        default.aspx.HTT
    0x0050   502f                                           P/
    13:45:35.180389 10.10.10.100.http 
      > 64-85-47-204.ip.van.radiant.net.46721: P 1:169(168) ack 421 win 4800 
       (DF)
    0x0000   4500 00dc e46b 4000 ff06 1221 0a0a 0a64        E....k@....!...d
    0x0010   4055 2fcc 0050 b681 3ea3 4800 db6d cf3a        @U/..P..>.H..m.:
    0x0020   8018 12c0 60d4 0000 0101 080a af8c 1300        ....`...........
    0x0030   01a4 06bc 4854 5450 2f31 2e31 2034 3030        ....HTTP/1.1.400
    0x0040   2042 6164 2052 6571 7565 7374 0d0a 436f        .Bad.Request..Co
    0x0050   6e74                                           nt

    This shows a request for "GET /web002/default.aspx HTTP/" and a server response of "HTTP/1.1 400 Bad Request".

    This doesn't seem to line up with the commands you were issuing from telnet. Are you sure this is the trace from that session?

    -Joe
  • Hi Joe,

    You are correct. This was the trace from an actual request from IE, not the raw telnet GET request I illustrated above. The telnet command I did was only to verify that if indeed I requested the REAL header (blacks.pnimedia.com) the request would work through that VS. Have a look at these samples, which you can try (this site is publically available):

    Using a masked hostname (blacks.test.pnimedia.com):

    
    telnet blacks.test.pnimedia.com 80
    GET /web002/default.aspx HTTP/1.1
    Host: blacks.test.pnimedia.com
    HTTP/1.1 400 Bad Request
    Content-Type: text/html
    Date: Wed, 05 Oct 2005 18:49:39 GMT
    Connection: close
    Content-Length: 39Bad Request (Invalid Hostname)

    Using a real hostname (blacks.pnimedia.com):

    - A HTTP 302 is returned, which is fine with me!

    
    telnet blacks.test.pnimedia.com 80
    GET /web002/default.aspx HTTP/1.1
    Host: blacks.pnimedia.com
    HTTP/1.1 302 Found
    Date: Wed, 05 Oct 2005 18:50:34 GMT
    Server: Microsoft-IIS/6.0
    X-Powered-By: ASP.NET
    X-AspNet-Version: 1.1.4322
    Transfer-Encoding: chunked
    Location: /error.aspx
    Cache-Control: private
    Content-Type: text/html; charset=utf-8
    80Object movedObject moved to .
  • My bad, In my original code I posted I had an extra quote at the end of the HTTP::header replace command:

     

     

    HTTP::header replace "Host" $new_host"

     

     

    This will basically add a \" to the $new_host value which obviously isn't what you want. Try changing the line to this:

     

     

    HTTP::header replace "Host" $new_host

     

     

    and you should be all set.

     

     

    -Joe