SOCKS5 SSL Persistence
Problem this snippet solves: Much requested 2005 iRule contest winner (thanks Adam!) The judges said: "This iRule addresses application persistence challenges associated with a proprietary service. By handling SOCKS and SSL protocol negotiation through binary rewrites, this iRule not only reduces server outages and increases customer satisfaction, it is a great example of the unique, far-reaching power of iRules and TMOS." The iRule first responds with the SOCKS 5 handshake so that it can get the next packet and persist based on the session identifier. When load-balancing the request, it proxies the relevant portion of the client’s initial handshake and removes the servers response to the handshake since we already spoofed that to the client earlier, and finally adds a persistence entry for the session id. Code : #Written by Adam Kramer (akramer@netifice.com) for Netifice Corporation #July, 2005 when CLIENT_ACCEPTED { TCP::collect 2 } when CLIENT_DATA { # read initial socks handshake – # the version number, and the number of auth methods supported binary scan [TCP::payload] cc socksver numauthmethods if { $socksver != 5 } { log local0. "Got non-socks connection from client [IP::remote_addr]" reject return } # set offset to the beginning of the second packet (SSL negotiation) set offset [expr {2 + $numauthmethods}] if { [TCP::payload length] == $offset } { #only respond if exactly the right amount of data was sent TCP::respond [binary format H2H2 05 86] TCP::collect [expr {$offset + 1}] return } # more data than the offset, this means we got the first packet of the SSL negotiation if { [TCP::payload length] > $offset} { # 4 bytes is the length of the SOCKS SSL header, 1 byte gets to the SSL version field # another 41 bytes past that is the session length, immediately following is the session # binary scan gracefully handles the string being too short, # so we can safely read all 3 values here binary scan [TCP::payload] "x[expr {$offset + 5}]cx41ch32" sslversion sessionlength hexid if { $sslversion != 3 } { log local0. "Received wrong SSL version in header from client [IP::remote_addr]" reject return } if { $sessionlength == 0 } { # this is a new connection, allow normal server selection return } else { persist universal $hexid return } } # this should never happen, but a bad client might do it if { [TCP::payload length] < $offset } { TCP::collect $offset return } } when SERVER_CONNECTED { # send current full payload from client to server, we need server's ssl hello # also delete client payload - replace returns the replaced characters, # doing both in one shot saves 50,000 cycles TCP::respond [clientside {TCP::payload replace 0 [TCP::payload length] ""}] # 5 bytes should do it, only 2 bytes to the first socks handshake TCP::collect 5 } when SERVER_DATA { # remove initial protocol negotiation since we already did that with client TCP::payload replace 0 2 "" # 4 bytes for socks ssl header, 44 for offset of session id binary scan [TCP::payload] "x48h32" hexid # need to add a session state for the case where the client didn't send a session ID persist add universal $hexid }5.2KViews0likes0CommentsWeblogic JSessionID Persistence
Problem this snippet solves: Contributed by: unRuleY, Summarized by: deb Note: The previous version of this iRule contained escaped newlines following the session command, which in versions 10.0 - 10.2.0 causes TMM to core as documented in CR135937 / SOL11427. This was fixed in 10.2.1. See this related Codeshare example for details on how to take advantage of session replication on the WebLogic servers with targeted node failover in an iRule. Provides persistence on the jsessionid value found in either the URI or a cookie. When a request is received, the iRule first looks for a "jsessionid" cookie, and if not found, for a "jsessionid" parameter in the requested URI. If either is found, a persistence record is created if it doesn't already exist, or followed if it does. If neither is found, the request is load balanced according to the load balancing method applied to the virtual server and persisted based on the client's IP address. In order to ensure the second and subsequent requests follow the first, LTM must create a persistence record indicating the pool member to which the first request was load balanced. If the server is setting the jsessionid in a cookie, the persistence key value may be extracted from the server response to create the persistence record. If the server is setting the jsessionid in the URLs, source address persistence with a short timeout is recommended to track the original destination until the jsessionid is sent. How to use this snippet: To ensure a new persistence record is followed when a request is re-load balanced in a client-side Keep-Alive connection, apply a OneConnect profile to the virtual server. The iRule assumes the jsessionid is in upper case when used as a cookie name. If this isn't the case, please update the example. To persist on jsessionid, create the iRule below and create a custom Universal persistence profile, with Match Across Services enabled, that uses the iRule. Then use this custom Universal persistence profile as the Default Persistence profile on your Virtual Server. Applying a Fallback Persistence profile of type Source Address Affinity with a host mask and a short timeout (the default source_addr persistence profile will do the trick) to your Virtual Server is also recommended. Attention, if you are running firmware 11.0 - 11.2.1 and enabled "Match Across Services"! There is a bug inside. SOL14061 This iRule requires LTM v10. or higher. Code : when HTTP_REQUEST { # Log details for the request set log_prefix "[IP::client_addr]:[TCP::client_port]" log local0. "$log_prefix: Request to [HTTP::uri] with cookie: [HTTP::cookie value JSESSIONID]" # Check if there is a JSESSIONID cookie if { [HTTP::cookie "JSESSIONID"] ne "" }{ # Persist off of the cookie value with a timeout of 1 hour (3600 seconds) persist uie [string tolower [HTTP::cookie "JSESSIONID"]] 3600 # Log that we're using the cookie value for persistence and the persistence key if it exists. log local0. "$log_prefix: Used persistence record from cookie. Existing key? [persist lookup uie [string tolower [HTTP::cookie "JSESSIONID"]]]" } else { # Parse the jsessionid from the path. The jsessionid, when included in the URI, is in the path, # not the query string: /path/to/file.ext;jsessionid=1234?param=value set jsess [findstr [string tolower [HTTP::path]] "jsessionid=" 11] # Use the jsessionid from the path for persisting with a timeout of 1 hour (3600 seconds) if { $jsess != "" } { persist uie $jsess 3600 # Log that we're using the path jessionid for persistence and the persistence key if it exists. log local0. "$log_prefix: Used persistence record from path: [persist lookup uie $jsess]" } } } when HTTP_RESPONSE { # Check if there is a jsessionid cookie in the response if { [HTTP::cookie "JSESSIONID"] ne "" }{ # Persist off of the cookie value with a timeout of 1 hour (3600 seconds) persist add uie [string tolower [HTTP::cookie "JSESSIONID"]] 3600 log local0. "$log_prefix: Added persistence record from cookie: [persist lookup uie [string tolower [HTTP::cookie "JSESSIONID"]]]" } }4.7KViews1like7CommentsSingle Node Persistence
Problem this snippet solves: A really slick & reliable way to stick to one and only one server in a pool. Requirement: Direct traffic to only a single node in a pool at a time. Initially, traffic should always go to node A. If Node A fails, then traffic will go to Node B. When Node A comes back online, traffic should continue to go to Node B. When Node B fails, then the traffic should go to Node A. To send traffic to only 1 pool member at a time, you can use an iRule and Universal Persistence to set a single persistence record that applies to all connections. Create a virtual server. Create a pool with the real servers in it. Create an iRule like this: Create a Persistence profile of type Universal which uses the iRule you just created. Set the timeout high enough so it will never expire under typical traffic conditions. In the virtual server definition, apply pool as the default pool, and the new persistence profile as the default persistence profile (both on the virtual server "resources" screen). The first connection will create a single universal persistence record with a key of "1". All subsequent connections will look up persistence using "1" as the key, resulting in truly universal persistence for all connections. (Use 1 or any constant value. 0 will have the same affect as using 1. One of my customers uses "persist uie TCP__local_port" When one node fails, the other is persisted to by all comers. When the 2nd node fails, the 1st again becomes the preferred node for all, ad infinitum. Doesn't offer the capability of manual resume after failure, or true designation of a "primary" and "secondary" instance (sometimes required for db applications), but it sure does solve the problem of "only use one node at a time, I don't care which one, please" (You can use priority to gravitate towards the top of a list...) Note: Priority-based load balancing with or without dynamic persistence doesn't quite address this requirement. Priority load balancing allows you to set a preferred server to which traffic should return once it recovers. With just Priority, and with dynamic persistence of any kind enabled, when a higher priority nodes come back up after failing, you will see traffic distributed across multiple pool members until old connections/sessions die off. With just Priority and no persistence, existing sessions will break once the preferred node again becomes available. Code : rule PriorityFailover { when CLIENT_ACCEPTED { persist uie 1 } }3.4KViews0likes25CommentsWeblogic JSessionID Persistence for Session Replication
Problem this snippet solves: Persists HTTP requests on the primary and secondary server values found in the JSESSIONID cookie when the WebLogic servers implement session replication across two servers. The actual JSESSIONID session ID is not used for peristence. The server-specific token is used to create one persistence record per pool member. This should use less LTM memory than the standard JSESSIONID perstistence iRule which creates one persistence record per client session. See this article for details on the WebLogic behavior when replicating application session replication: Load Balancers and the WebLogic Session Cookie When an HTTP request is received, the iRule looks for a JESSSIONID cookie. If found, the primary and secondary server ID hashes are parsed from the JSESSIONID cookie. If a persistence record is found for the primary server ID, it is used to persist the client request. If there isn't a primary server ID persistence record, the secondary server ID is checked for a persistence record. If neither is found or the JSESSIONID cookie is null, the request is load balanced according to the load balancing method applied to the virtual server. In order to ensure the second and subsequent requests follow the first, LTM must create a persistence record indicating the pool member to which the first request was load balanced. If the server is setting the jsessionid in a cookie, the persistence key value may be extracted from the server response to create the persistence record. If the server is setting the jsessionid in the URLs, source address persistence with a short timeout is recommended to track the original destination until the jsessionid is sent. Please post any questions or issues to the iRules forum. This version of the iRule has not been tested in production, so I'm keen to find out about any issues you may encounter. Thanks, Aaron How to use this snippet: To ensure a new persistence record is followed when a request is re-load balanced in a client-side Keep-Alive connection, apply a OneConnect profile to the virtual server. The iRule assumes the JSESSIONID cookie is in upper case when used as a cookie name. If this isn't the case, please update the example. To persist on jsessionid, create the iRule below and create a custom Universal persistence profile. Then use this custom Universal persistence profile as the Default Persistence profile on your Virtual Server. Applying a Fallback Persistence profile of type Source Address Affinity with a host mask and a short timeout (the default source_addr persistence profile will do the trick) to your virtual server is also recommended. Once done testing, comment out the LBSELECTED, LBFAILED and SERVER_CONNECTED events as they're there just for debug logging. This iRule requires LTM v10. or higher. Code : # jsessionid persistence with primary and secondary server fallback # # Uses Universal Inspection Engine (UIE) persistence to select the primary server. # If the primary is down, then the secondary server is selected. # # Cookie format from http://download.oracle.com/docs/cd/E11035_01/wls100/cluster/load_balancing.html#wp1028843 # Assumes a cookie format of: # sessionid!primary_server_id!secondary_server_id # # Use UIE persistence based on the first server ID in the application's jsessionid cookie. # If that fails, then try the second server ID. # Check server responses to determine the secondary server ID's hash token and save the mappings in the session table # when RULE_INIT { # Log debug to /var/log/ltm? 1=yes, 0=no set static::j_debug 1 # LTM persistence timeout set static::persist_timeout 3600 # Java sessiond ID cookie name set static::j_cookie "JSESSIONID" } when HTTP_REQUEST { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: [HTTP::method] to [HTTP::host][HTTP::uri]\ cookie: [HTTP::cookie $static::j_cookie]"} # Check if cookie has a value if {[HTTP::cookie $static::j_cookie] ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Scanning cookie: [HTTP::cookie $static::j_cookie]"} # Parse the three ! delineated fields from the cookie if {[scan [HTTP::cookie $static::j_cookie] {%[^!]!%[^!]!%[^!]} session pri_server_hash sec_server_hash] == 3}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie] pri: $pri_server_hash; sec: $sec_server_hash;\ pri UIE: [persist lookup uie $pri_server_hash]; sec UIE: [persist lookup uie $sec_server_hash]"} # Persist off of the primary server token if it is present, not a null value and has a current persistence record if {$pri_server_hash ne "" and $pri_server_hash ne "NONE" and [persist lookup uie $pri_server_hash] ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Using existing primary UIE persistence"} persist uie $pri_server_hash $static::persist_timeout } elseif {$sec_server_hash ne "" and $sec_server_hash ne "NONE" and [persist lookup uie $sec_server_hash] ne ""}{ # Persist off of the secondary server token if it is present, not a null value and has a current persistence record if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Using existing secondary UIE persistence"} persist uie $sec_server_hash $static::persist_timeout } else { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No existing persistence record"} } } } } # Debug logging only. Remove this event after testing. when LB_SELECTED { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Selected server [LB::server]"} } # Debug logging only. Remove this event after testing. when LB_FAILED { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: LB failure for selected server [LB::server]"} } # Debug logging only. Remove this event after testing. when SERVER_CONNECTED { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Connected server [IP::server_addr]:[TCP::server_port]"} } when HTTP_RESPONSE { # Check if cookie has a value if {[HTTP::cookie $static::j_cookie] ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie]"} # Parse the second field of the cookie value. # The current server always sets its hash as the primary server hash in the second field of the cookie. set pri_server_hash [getfield [HTTP::cookie $static::j_cookie] "!" 2] if {$pri_server_hash ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Adding: persist add uie $pri_server_hash $static::persist_timeout"} # Add a persistence record for the primary server persist add uie $pri_server_hash $static::persist_timeout if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie] set by [IP::server_addr]:[TCP::server_port],\ UIE: [persist lookup uie $pri_server_hash]"} } } }2.5KViews0likes2CommentsXFF Universal Persistence iRule
Problem this snippet solves: Simple iRule to read the XFF header on an incoming HTTP Request and use a Universal Persistence ID. Orginal iRule found to have an issue with multiple IP addresses in the XFF header for changed to only pass the first XFF IP. I have updated the iRule line to account for systems where multiple 'X-Forwarded-For' headers have been added. persist uie [lindex [ split [HTTP::header X-Forwarded-For] "," ] 0] to persist uie [lindex [ split [lindex [HTTP::header values X-Forwarded-For] 0] "," ] 0] thanks to the advice from Yann Desmarest. This could also be done with the 'getfield' command see Yann's comments below. How to use this snippet: Create iRule using following code (mine is named 'persist_xff_uie') Create Universal Persistence Profile with iRule set to 'persist_xff_uie' (or what ever name you assign to the iRule) Assign Universal Persistence Profile to Virtual Server (ensure virtual server has HTTP profile assigned) Code : # Name: persist_xff_uie # # To be used with UIE Persistence Profile # # Checks HTTP Request for 'X-Forwarded-For' header and if exists takes the first 'X-Forwarded-For' IP address as sets as # Persist identifier. # If the 'X-Forwarded-For' header does not exist then the client IP address is set as Persist identifier. when HTTP_REQUEST { if {[HTTP::header X-Forwarded-For] != ""} then { persist uie [lindex [ split [lindex [HTTP::header values X-Forwarded-For] 0] "," ] 0] } else { persist uie [IP::client_addr] } } Tested this on version: 11.52.3KViews1like7CommentsHow to generate the persistence cookie with an iRule
Problem this snippet solves: When you configure a cookie persistence profile to use the HTTP Cookie Insert or HTTP Cookie Rewrite method, the BIG-IP system inserts a cookie into the HTTP response. The cookie value contains the encoded IP address and port of the destination server. Exemple of a cookie value : 1677787402.36895.0000 (See SOL6917 for more information about this topic) Let's assume that you want your pool member to receive a copy of this cookie value in an HTTP header. Because for example you want your application to forge an url where the cookie value is in a GET parameter. (NOTE : I cannot modify the behavior of the application, I can only play with headers) Retrieving the cookie value is pretty easy with iRule : [HTTP::cookie value $cookie_name] But you'll notice that there is a little issue with this feature: when you are a new visitor, the persistence cookie is inserted in the HTTP response ... Meaning that for the very first hit made by the visitor, there will be NO cookie value to retrieve ... In my scenario it was an issue to miss this cookie value on the first hit, so I had to come up with a solution to forge the cookie value based on pool member IP and port when the persistence cookie is missing. I chose to adapt the code found here and there (thanks !) EDIT : Well I figured out that if you are not using a default route-domain the persistence cookie value will be different (see https://support.f5.com/csp/article/K6917 ) Here is the alternative code bloc to use IPv4 non-default route domains: set ADDR "[format %02x $a][format %02x $b][format %02x $c][format %02x $d]" set PORT [LB::server port] set COOKIE "rd2o00000000000000000000ffff${ADDR}o${PORT}" How to use this snippet: To summarize what the iRule does : if the persistence cookie doesn't exist (most likely because it's the very first hit), then calculate it from member IP and PORT (it obviously has to be after the "When LB_SELECTED" statement) ; else just read the existing cookie. You can set the $cookie_name parameter manually, or let the iRule identify it Code : when LB_SELECTED { #set cookie_name SERVERID # following function could determine persistence cookie name being used if not manually set by the previous line if {not [info exists cookie_name]} { if { [set cookie_name [PROFILE::persist mode cookie cookie_name]] eq "" } { set cookie_name "BIGipServer[getfield [LB::server pool] "/" 3]" } #Default cookie name requires the getfield "/" 3 purge otherwise it's /Common/pool_name } if { [set COOKIE [HTTP::cookie value $cookie_name]] == "" } { scan [LB::server addr] {%d.%d.%d.%d} a b c d set ADDR [expr { $a + $b * 256 + $c * 65536 + $d * 16777216 }] set PORT [ntohs [LB::server port]] set COOKIE "${ADDR}.${PORT}.0000" ## Following bloc must be used instead if you are using non-default route domains, see K6917 #set ADDR "[format %02x $a][format %02x $b][format %02x $c][format %02x $d]" #set PORT [LB::server port] #set COOKIE "rd2o00000000000000000000ffff${ADDR}o${PORT}" ######### unset a b c d ADDR PORT #log local0. "$cookie_name = $COOKIE created for [HTTP::uri]" } else { #log local0. "$cookie_name = $COOKIE already exists for [HTTP::uri]" } HTTP::header insert X-F5-persist $COOKIE } Tested this on version: 11.52.3KViews2likes1CommentPersistence Cookie Logging
Problem this snippet solves: Cookie Persistence Logging Adapted and Simplified for TMOS 10.0.0 or greater cookie format. Extracts the cookie value from a persistence cookie inserted by LTM's "cookie insert" persistence method, logs cookie, logs virtual iRule is applied to, logs virtual's default pool iRule is applied to, decodes cookie to get pool member ip and destination port also including the requesting client's ip address and source port. If no cookie is found that's logged along with the virtual irule is applied to, virtual's default pool irule is applied to and pool member assignment ip and destination port. Useful for troubleshooting cookie persistence. Explained Further - On the LB_SELECTED event automatically grabs the Virtual Name the iRule is applied to, Virtual Default Pool Name the iRule is applied to. Determines what the Cookies Name should be by Default and sets this information to varibles. Executes the guts TCL Script to query if a cookie exists & decodes the cookie if so while logging to via syslog-ng to /var/log/ltm; if no cookie exists logs that information also to /var/log/ltm. Credit: deb for the idea and a very small portion of this code sol6917: Overview of BIG-IP persistence cookie encoding How to use this snippet: This iRule requires LTM v10. or higher. Example Logging Entries Sep 28 11:43:14 local/tmm1 info tmm1[4875]: Rule persist_cookie_logger : Request from client: 172.20.66.224%10:53566 contains no persistence cookie on vip https-vip-12; request was assigned to pool https-pool-12 and member 10.11.101.211%10:443 Sep 28 11:47:10 local/tmm1 info tmm1[4875]: Rule persist_cookie_logger : Request from client: 172.20.66.224%10:53618 contains persistence cookie rd10o00000000000000000000ffff0a0b65d3o443 referencing vip https-vip-12; pool https-pool-12; member 10.11.101.211:443 Code : # Cookie Persistance Logging Adapted and Simplified for TMOS Version 10.0.0 Cookies # - RJ Wilke when LB_SELECTED { # grab Virtual Server Name Servicing this Request set Virtual [virtual name] # grab Pool (Default Pool for the Virtual Server) set Pool [LB::server pool] # grab Version TMOS 10.0.0 or greater Cookie (includes Route Domain) set Cookie BIGipServer$Pool # if {[HTTP::cookie exists $Cookie]}{ scan [HTTP::cookie $Cookie] {%*[^\f]%*4x%2x%2x%2x%2xo%i} a b c d e set Ip $a.$b.$c.$d set Port $e # log results out to /var/log/ltm log local0.info "Request from client: \ [IP::remote_addr]:[TCP::remote_port] contains persistence cookie [HTTP::cookie $Cookie]\ referencing vip $Virtual; pool $Pool; member $Ip:$Port" } else { log local0.info "Request from client: [IP::remote_addr]:[TCP::remote_port] contains\ no persistence cookie on vip $Virtual; request was assigned to pool $Pool and member\ [LB::server addr]:[LB::server port]" } } Tested this on version: 10.01.6KViews0likes5CommentsASP Session ID Persistence
Problem this snippet solves: Persist on ASP SessionID cookie value or PID. Some ASP applications set a session cookie named "ASPSESSIONIDxxx" where "xxx" completes the 20 character alpha string dynamically generated from the process ID of the IIS instance. The cookie name will be constant for the life of the instance and unique to that instance, and will be used for all cookies set by that instance. When an instance is restarted, the PID changes and consequently the dynamic string changes, and the connection must be re-load balanced. Other ASP applications use a session cookie named "ASP.NET_SessionId". This cookie name does not change for these applications. Persistence can be based off of the cookie name as a whole, or just the dynamic part of the string. There are a few advantages to using the PID string instead of the cookie value: the cookie name changes less frequently compared with the cookie value you don't have to worry about the client changing the cookie value you only have one persistence record per web server instance instead of one per client The 2 examples below demonstrate both solutions, along with a few variations for implementing the desired persistence: One uses "session" commands and direct node selection with LB_FAILED recovery logic, the other uses "persist" commands which manage node selection and recovery more automatically. Code : ### iRule source with persist command ### when RULE_INIT { set ::debug 0 } when HTTP_REQUEST { set cli [IP::remote_addr]:[TCP::remote_port] set SessionId [HTTP::cookie ASP.NET_SessionId] if {$::debug}{log local0. "Client: $cli Request SessionId: >$SessionId<"} if { $SessionId ne "" } { persist uie $SessionId 1800 } } when LB_SELECTED { if {$::debug}{log local0. "Client: $cli LB to: [LB::server addr]"} } when HTTP_RESPONSE { set SessionId [HTTP::cookie ASP.NET_SessionId] if {$::debug}{log local0. "Client: $cli Response SessionId: >$SessionId<"} if { $SessionId ne "" }{ persist add uie $SessionId 1800 } } ### iRule source with session command ### when RULE_INIT { set ::debug 0 } when HTTP_RESPONSE { set sessionID "" set sessionID [findstr [HTTP::cookie names] "ASPSESSIONID" 12 20]] set persistTo [session lookup uie $sessionID] if { $persistTo equals "" } { session add uie $sessionID [IP::server_addr] 1800 if {$::debug}{log local0. "added server entry $persistTo for aspsessionID $sessionID"} } else { if {$::debug}{log local0. "existing server entry $persistTo for aspsessionID $sessionID"} } } when HTTP_REQUEST { set sessionID "" set sessionID [findstr [HTTP::cookie names] "ASPSESSIONID" 12 20]] set persistTo [session lookup uie $sessionID] if { $persistTo equals "" } { pool [LB::server pool] if {$::debug}{log local0. "No aspsession ID, load balancing the connection."} } else { pool [LB::server pool] member $persistTo if {$::debug}{log local0. "aspsessionID $sessionID sent to $persistTo"} } } when LB_FAILED { # If no servers are active, redirect to sorry page elsewhere if {[active_members [LB::server pool]] == 0}{ HTTP::redirect http:://sorry.domain.com/NoServersAvail.html if {$::debug}{log local0. "No servers available. Redirecting to sorry server."} } else { LB::detach LB::reselect [LB::server pool] if {$::debug}{log local0. "LB failed, re-load balancing to pool."} } } Tested this on version: 9.01.3KViews0likes1CommentPersistence Cookie Logger
Problem this snippet solves: Extracts the cookie value from a persistence cookie inserted by LTM's "cookie insert" persistence method, decodes the node IP and port and logs them along with the requesting client's IP address. (If no cookie is found, that's logged as well.) Useful for troubleshooting cookie persistence. Only use this rule for v9. It still needs to be udpated for CMP compliance with v10/v11. Edit the RULE_INIT section to reflect the actual name of the cookie inserted, which defaults to BIGipServer . Refer to SOL6917: Overview of BIG-IP LTM cookie encoding for the cookie persistence profile for cookie encoding details. Code : rule persistence_cookie_logger { when RULE_INIT { # see AskF5 SOL6917 for cookie encoding details: # https://support.f5.com/kb/en-us/solutions/public/6000/900/sol6917.html set ::myCookieName BIGipServerXXXX set ::debug 0 } when HTTP_REQUEST { # grab encoded cookie value & parse into relevant pieces if {[HTTP::cookie exists $::myCookieName]}{ scan [HTTP::cookie $::myCookieName] {%[^\.].%d.%d} myIpE myPortE unused if {$::debug != 0}{log local0. "myIpD=$myIpE myPortE=$myPortE unused=$unused"} # calculate IP # %08x format strings forces padding w/leading 0's to 8 char string set myIpH [format %08x $myIpE] if {$::debug != 0}{log local0. "myIpH=$myIpH"} set myIpD1 [expr 0x[substr $myIpH 6 2]] set myIpD2 [expr 0x[substr $myIpH 4 2]] set myIpD3 [expr 0x[substr $myIpH 2 2]] set myIpD4 [expr 0x[substr $myIpH 0 2]] set myIpD "$myIpD1.$myIpD2.$myIpD3.$myIpD4" if {$::debug != 0}{log local0. "myIpD=$myIpD"} # calculate port set myPortH [format %04x $myPortE] if {$::debug != 0}{log local0. "myPortH=$myPortH"} set myPortD [string trimleft [expr 0x[substr $myPortH 2 2][substr $myPortH 0 2]] 0] if {$::debug != 0}{log local0. "myPortD=$myPortD"} # log results log local0. "Request from client: \ [IP::remote_addr]:[TCP::remote_port] contains persistence cookie \ referencing pool member $myIpD:$myPortD" } else { log local0. "Request from client: [IP::remote_addr]:[TCP::remote_port] \ contains NO persistence cookie" } } } # And the inverse process of encoding an IP address and port into the LTM cookie format in an iRule (from UnRuley's post here): # https://devcentral.f5.com/s/Default.aspx?tabid=53&forumid=5&tpage=1&view=topic&postid=9756 # Generate a persistence cookie for a sample IP:port when HTTP_RESPONSE { set addr "10.10.10.10:80" scan $addr "%u.%u.%u.%u:%u" a b c d e set cookie "[format %u [expr {($d<<24)|($c<<16)|($b<<8)|$a}]].[expr {256*$e}].0000" HTTP::cookie insert name "BIGipServerFP_pool" value $cookie path "/" } # Sample Logs # New request with no persistence cookie: Rule persistence_cookie_logger_rule : 10.1.0.1:62606: Request for 10.1.0.15/ contains no persistence cookie Rule persistence_cookie_logger_rule : 10.1.0.1:62606: New member selected: ubuntu_external_http_pool 10.1.0.101 80 Rule persistence_cookie_logger_rule : 10.1.0.1:62606: New member connected: 10.1.0.101:80 Rule persistence_cookie_logger_rule : 10.1.0.1:62606: Persistence cookie in Set-Cookie header(s): {BIGipServerubuntu_external_http_pool=1694499082.20480.0000; path=/} # Request with a valid persistence cookie: Rule persistence_cookie_logger_rule : 10.1.0.1:62894: Request for 10.1.0.15/ contains persistence cookie 1694499082.20480.0000 referencing pool member 10.1.0.101:80 Rule persistence_cookie_logger_rule : 10.1.0.1:62894: Persistence member 10.1.0.101:80 selected successfully Rule persistence_cookie_logger_rule : 10.1.0.1:62894: Persistence member 10.1.0.101:80 connected to successfully # Request with a persistence cookie for a downed pool member: Rule persistence_cookie_logger_rule : 10.1.0.1:62932: Request for 10.1.0.15/ contains persistence cookie 1677721866.20480.0000 referencing pool member 10.1.0.100:80 Rule persistence_cookie_logger_rule : 10.1.0.1:62932: Persistence member 10.1.0.100:80 is not the selected member ubuntu_external_http_pool 10.1.0.101 80. State of pool member from persistence cookie: down Rule persistence_cookie_logger_rule : 10.1.0.1:62932: Persistence member 10.1.0.100:80 is not the connected member 10.1.0.101:80. State of pool member from persistence cookie: down Rule persistence_cookie_logger_rule : 10.1.0.1:62932: Persistence cookie in Set-Cookie header(s): {BIGipServerubuntu_external_http_pool=1694499082.20480.0000; path=/}1.2KViews0likes1CommentManual cookie persistence
Problem this snippet solves: I had an app admin ask me for a way to temporarily force node assignment for his session, for testing purposes, in an app where we already use cookie persistence. We wanted a particular URL request to force the node assignment, then have all subsequent requests (regardless of URL) use that same pool member. I would have thought "pool mypool member 10.2.3.4 80" and "persist cookie insert" would do the trick, but no. We were able to assign the first request (with special URL) to the right app server, but unable to update the "BIGipServermy_pool" cookie with the new node assignment, so subsequent requests were sent to the wrong node. As of version 9.3 LTM does not honor cookie persistence settings during forced node assignment. Thus, I give you manual cookie persistence... Code : when RULE_INIT { set cookie_name "my_node_forced" } when HTTP_REQUEST { # Get node id from URL ending in ?node=server1, ?node=server2, etc. set node_forced [findstr [HTTP::uri] "?node=" 6] # Or from previous cookie set has_cookie [HTTP::cookie exists $::cookie_name] if { $node_forced eq "" and $has_cookie } { set node_forced [HTTP::cookie value $::cookie_name] } # Map node id to IP and port found in a class # Entries should be strings of the form # if { $node_forced ne "" } { set node_data [findclass $node_forced $::my_nodes " "] if { $node_data ne "" } { set node_ip [getfield $node_data " " 1] set node_port [getfield $node_data " " 2] pool my_pool member $node_ip $node_port } else { set node_forced "" } } } when HTTP_RESPONSE { if { $node_forced ne "" } { # Add a cookie to continue forcing node assignment HTTP::cookie insert name $::cookie_name value $node_forced path "/" } elseif { $has_cookie } { # Delete cookie and resume load balancing (with built-in persistence) HTTP::cookie insert name $::cookie_name value "" path "/" HTTP::cookie expires $::cookie_name 0 absolute } }627Views0likes0Comments