Forum Discussion

TJ_Vreugdenhil's avatar
Mar 12, 2015

iRule v11 to send user to correct DC database based on HTML header

We have two DC's with one LTM and GTM in each DC. All VIP's have clientSSL profiles and no serverssl profiles. A user points to www.example.com and is DNS load balanced to either DC1 or DC2 based on Round Robin Priority Groups.

iRule Requirement: When a user comes into DC1 and hits the DC1 webserver. The database might actually live in DC2. In that case, the DC1 webserver will send the F5 an HTML header letting it know the database resides in DC2. Once the F5 receives this, it will redirect across an internal MPLS to DC2 LTM VIP and forward the request to the DC2 webserver. The same is true if a user comes in on the DC2 webserver and needs to reach the DC1 database server.

Do you think something like the following would work? I want to make sure of course that we don't get cert errors. Would sending a Cookie from the server work better then sending a http header? Would there be any problems with regular persistence using this?

 

when HTTP_RESPONSE {
    Check incoming packets from DC1 application database server for "DC2-ID" header
  if { [HTTP::header] contains "DC2-ID"} {
       If "DC2-ID" header exists, redirect to DC2 VIP IP address
      HTTP::redirect "https://x.x.x.x[HTTP::uri]"
   }
}

 

Thanks!

6 Replies

  • More simply, I just need to check for a header (like an ID or text) in a HTTP RESPONSE and redirect the user to another LTM VIP in a separate DC, hopefully by IP address, because both LTM VIP's have the same URL/cert. The purpose of this is to be able to have active/active DC's without having both sets of database servers at each site.
  • When a user comes into DC1 and hits the DC1 webserver. The database might actually live in DC2. In that case, the DC1 webserver will send the F5 an HTML header letting it know the database resides in DC2. Once the F5 receives this, it will redirect across an internal MPLS to DC2 LTM VIP and forward the request to the DC2 webserver.

     

    in case you do not want to use redirect, you may try HTTP::retry. take a look at example in its wiki.

     

    HTTP::retry

     

    https://devcentral.f5.com/wiki/iRules.HTTP__retry.ashx

     

  • Ok, I have come up with three different options for testing. I am mainly interested in Option 2 as I would believe it would be the most efficient, but I would like to verify the logic is ok.For Option 2, do I have to put in a variable for the default pool? Thanks a bunch for your help!

     

    Option 1

     

    when HTTP_RESPONSE {
        Check incoming packets from DC1 application database for "DC2-ID" header
      if { [HTTP::header] contains "DC2-ID"} {
           If "DC2-ID" header exists, redirect to DC2 VIP IP address
          HTTP::redirect "https://x.x.x.x[HTTP::uri]"
       }
    }

    Option 2

     

    If a HTTP::redirect is coming from LTM1 VIP to LTM2 VIP, would it come in as a HTTP_REQUEST or HTTP_RESPONSE? And do you think the defualt 302 (Found - non-cacheable) HTTP:redirect code will be better then using "HTTP::respond 301 Location" (Moved Permanently)

     

    Apply iRule on DC1 LTM VIP's
    
     One can save a WAN round trip by following a redirect at the LTM and responding to the 
     original client request with the result of the redirect
    when HTTP_REQUEST {
    
        Save the host header value
       set host [HTTP::host]
    }
    when HTTP_RESPONSE {
        Check if response is a redirect based on the status code, (301, 302, 303, 305, or 307)
       HTTP::is_redirect is fixed in 11.5.1 HF5
       if { [HTTP::is_redirect] } {
           Now generate a GET request to the new location
          HTTP::retry -reset "GET [HTTP::header location] HTTP/1.1\r\nHost: $host\r\n\r\n"
       }
        Check incoming packets from DC1 application database for "DC2-ID" header
      elseif { [HTTP::header] contains "DC2-ID"} {
           If "DC2-ID" header exists, redirect to DC2 VIP IP address
          HTTP::redirect "https://x.x.x.x[HTTP::uri]"
          log local0. "DC2-ID header found and request was redirected to DC2 VIP."
       }
    }

    Option 3

     

    The following iRule should be possible if we copy all pools from LTM1 to LTM2 and vis versa. Since we are using OTV to extend Layer 2, the same LTM will be able to reach pools at either DC. However, I imagine that this would not be as efficient as sending the entire HTTP request to the other LTM VIP in the other DC because connections from LTM1 VIP would have to cross the MPLS to get to the 2nd pool, instead of redirecting the entire request to the LTM2 VIP in that DC.

     

     Apply iRule on DC1 LTM VIP's 
     Retry DC2-ID header responses to a second pool
    when CLIENT_ACCEPTED {
    
         Track whether this is a retried request
        set retried 0
    
         Save the name of the VS default pool
        set default_pool [LB::server pool]
    
         Track whether we have the headers for this request
        set request_headers ""
    }
    
    when HTTP_REQUEST {
    
         Select the VS default pool by default
        pool $default_pool
    
    
         Check if this is a retried request
        if {$retried == 0}{
    
             Only save request headers if it is a GET request
             We do not want to retry requests with payloads
            if {[HTTP::method] eq "GET"}{
    
                 Save the HTTP request headers
                set request_headers [HTTP::request]
                log local0. "HTTP request: $request_headers"
    
            } else {
    
                 Null the headers to make sure we only retry GETs
                set request_headers ""
            }
        } else {
             Select the other pool for the retry
            pool other_pool
        }
    }
    
    when HTTP_RESPONSE {
    
         Check if we recieved the DC2-ID header from the default pool
        if { [HTTP::header] contains "DC2-ID" && $request_headers ne "" && [LB::server pool] eq $default_pool } {
    
             Track that we are retrying this request
            set retried 1
            log local0. "DC2-ID header found. Retrying to 'other pool'."
    
            HTTP::retry $request_headers
        } else {
             Track that we are retrying this request
            set retried 0
        }
    }
  • HTTP::redirect "[HTTP::uri]"

     

    HTTP::uri is not valid in HTTP_RESPONSE. you may have to save it to variable in HTTP_REQUEST and use the variable in HTTP_RESPONSE.

     

    HTTP::retry -reset "GET [HTTP::header location] HTTP/1.1\r\nHost: $host\r\n\r\n"

     

    you may need to check whether protocol (e.g. and hostname are included in HTTP::header location. if yes, you have to extract only uri part here.

     

    If a HTTP::redirect is coming from LTM1 VIP to LTM2 VIP, would it come in as a HTTP_REQUEST or HTTP_RESPONSE?

     

    you mean LTM2, don't you? if yes, i understand it will trigger HTTP_REQUEST on LTM2.

     

    do you think the defualt 302 (Found - non-cacheable) HTTP:redirect code will be better then using "HTTP::respond 301 Location" (Moved Permanently)

     

    i am not sure about this.

     

    For Option 2, do I have to put in a variable for the default pool?

     

    you are not using pool command in option2, are you?

     

  • I don't think I need to use the HTTP::retry if the HTTP::respond or HTTP::redirect is sending the request to a completely new LTM. And HTTP::retry is not a valid event in HTTP_REQUEST anyways.

    My main concern is using the HTTP::respond or HTTP::redirect and trying to send it via IP and carry and try to carry the URL info to the other DC LTM. The same URL's exist on both LTM's and we can't mess with adjusting domain names or changing SSL certificates to include SAN's, etc.

    Do you think this would work ok?

    HTTP::redirect https://2.2.2.2[getfield [$host] ":" 1][$httpuri]

    Since I am trying to put the actual VIP IP address in the HTTP::redirect, would there be a way to strip it out once it reaches the other DC LTM? And also not have the client see it? Any examples? Thank you! Thank you!

    Option 2

     

    Apply iRule on DC1 LTM VIP's
    when CLIENT_ACCEPTED {
          set default_pool [LB::server pool]
    }
    when HTTP_REQUEST {
      Check if response is a redirect from the DC2 LTM based on the status code, (301, 302, 303, 305, or 307) and also if it contains it's own header, "DC1-ID"; then send the request to the default pool on DC1 LTM
       if { ([HTTP::is_redirect]) && { ([HTTP::header] contains "DC1-ID"}) } {
          pool $default_pool
          Exit iRule
          return
       }
        Save the host header value
       set host [HTTP::host]
        Save the URI
       set httpuri [string tolower [HTTP::uri]]
    }
    when HTTP_RESPONSE {
        Check incoming packets from DC1 application database for "DC2-ID" header
      if { [HTTP::header] contains "DC2-ID"} {
           If "DC2-ID" header exists, redirect to DC2 LTM VIP IP address. 2.2.2.2 represent the DC2 LTM VIP. 
           HTTP::respond 302 Location https://2.2.2.2$htturi
          HTTP::redirect https://2.2.2.2[getfield [$host] ":" 1][$httpuri]
          log local0. "DC2-ID header found and request was redirected to DC2 VIP - requested Host: [HTTP::header host] --- URI: $httpuri" 
          return
       }
    }
    

     

  • HTTP::redirect https://2.2.2.2[getfield [$host] ":" 1][$httpuri]

    i do not think you do need host part.

     

    HTTP::redirect "https://2.2.2.2$httpuri"
    

     

    additionally, i think it may be better to not use string tolower for uri in case server's uri is case sensitive.

    Since I am trying to put the actual VIP IP address in the HTTP::redirect, would there be a way to strip it out once it reaches the other DC LTM? And also not have the client see it?

    that is why i think HTTP::retry could be a good candidate because bigip does retry to another bigip by itself. anyway, i understand you want to use internet bandwidth rather than mpls one.