Forum Discussion
JRahm
Admin
Mar 01, 2005Terminal Server Persistence
I have msrdp persistence working without a rule, but only within a single vip. Globally, I don't have persistence to the client, so a client could potentially be assigned to the wrong vip, and even though session directory sends the client the routing token, and the client sends this routing token to the BigIP, it is being ignored if the client hits the wrong vip. Here's what the cookie looks like in hex and ascii:
0040 00 43 6f 6f 6b 69 65 3a 20 6d 73 74 73 3d 38 31 .Cookie: msts=81
0050 33 39 36 34 30 34 32 2e 31 35 36 32 39 2e 30 30 3964042.15629.00
0060 30 30 0d 0a 00..
The 813964042 is the server IP (dec->hex(reverse pairs)->dec(by octet)) and the 15629 is the tcp port (dec->hex(reverse pairs)->dec)
If I can't persist across virtuals with the msrdp cookie with the gui, then I'd like to build a rule that will persist across virtuals. I don't want only one vip because I will have aprox. 750 servers, and I'd like to pool these by rack.
Is there a command within iRules to return a node from the msrdp cookie? I saw in the config guide that there is one for BEA WebLogic and I didn't know if there was one for MS Terminal Services since the BigIP has the ability to do this already. Thanks.
18 Replies
- unRuleY_95363Historic F5 AccountHmm, I'm not quite sure I fully understand your vip issue. When MSRDP persistence is configured on a vip, it searches for the embedded cookie. Upon finding it, it will then use that node as long as it is a valid node within the current pool.
So I have a couple of question? Do you have msrdp persistence configured on the "wrong vip"? Does the "wrong vip" have a pool with the same WTS server nodes in it? - JRahm
Admin
For clarity,
Vip 1->Pool 1->Server 1
Vip 2->Pool 2->Server 2
MSRDP persistence with session directory is configured to persist across services, pools, and vips.
1. Login to Vip 1, which creates Terminal Server Session on Server 1.
2. Close the window, but remain logged in
3. Login to Vip 2, credentials supplied to Server 2, but server 2 checks session directory and sends routing token to client, which then redirects back to Vip 2. BigIP sends back to Server 2, instead of to Server 1 as expected.
If I uncheck session directory in the persistence and send the username credentials with my session request, the persistence works fine across vips. Since this doesn't work with the routing token, I was hoping I could do something with a rule. - unRuleY_95363Historic F5 AccountOk, two things:
First, the "Has Session Directory" setting is now ignored (I just realized this is only true in 9.0.5 which is pending release) by the BigIP because the session actually sends different cookies depending on whether the servers have been configured to use the session directory.
We now switch behavior depending on which cookie is present. Cookie: msts= is what you see when the servers are using a session directory and Cookie: mstshash= is what you see when the servers are not using a session directory.
When the msrdp persistence detects the session directory cookie, it does not actually use any stored persistence on the BigIP. It merely checks the node to see if it's a member of the pool and directly selects it if it is. Since your vip2 is not using the same pool, this fails and it is re-load balance to server2 within pool2.
To fix this, you have several choices (or potentially more if your creative)...
Option a) Change your servers to not use a session directory, thus causing the Cookie switch to mstshash which the BigIP will store in it's persistence table and would be available "across virtuals".
Option b) Install a rule that check if the node is really in pool1 and uses that instead (I will give you an example of this at the bottom).
Option c) Merge vip1/vip2 and instead use a rule to dynamically make the selection that differentiates the two vips (say a larger pool vs. a smaller pool). Here is where I'm not clear on why you have vip1 and vip2 and so I can't guess at why you would first get connected to vip1 and then later to vip2.
The rule that you could use to replace msrdp persistence and instead directly use the node regardless of the pool would look something like this:rule msrdp_sessdir_uie { when CLIENT_ACCEPTED { TCP::collect } when CLIENT_DATA { if { [TCP::payload length] < 25 } { TCP::collect return } binary scan [TCP::payload] x11a* msrdp if { [string equal -nocase -length 12 $msrdp "cookie: msts"] } { set msrdp [string range $msrdp 12 end] set len [string first "\n" $msrdp] if { $len == -1 } { Didnt get whole cookie collect more TCP::collect return } if { $msrdp starts_with "=" } { Session directory - extract node/port if { [scan $msrdp "=%u.%u" node port] != 2] } Did not find node/port, get more TCP::collect return } set node [ntohl $node] set port [ntohs $port] log "DEBUG: overriding to $node:$port" Note: the node command does not check the pool node $node $port } elseif { $msrdp starts_with "hash=" } { No session directory - username used instead if { $len > 5 } { incr len -1 set record [string range $msrdp 5 $len] log "adding persistence record - $record" persist uie $record 300 } else { log "No username - not persisting" } } } else { log "Cookie not found" } TCP::release } }
Note: this rule has not been validated or tested in any way and is provided solely as an example. - JRahm
Admin
Wow, iRules are much more powerful that I anticipated. Concerning the options:
1) We don't have global persistence (hopefully in a year or so), so using the password would only work if all of our connections were sent to site 1. We are diverse on the WAN, but both sites have access to all the servers on the MAN.
2) I will test this. This seems like a viable option.
3) If I only have 1 VIP, can I still pool resources independently behind a single VIP?
EX- VIP 1
--> Pool 1 (48 members)
--> Pool 2 (48 members)
...
...
--> Pool 15 (48 members)
The GUI only lets me select one pool. Are you saying that with a rule I could load-balance by pool, then by member?
Thanks for your help. I think I have a lot to learn about rules.
Jason - JRahm
Admin
I corrected some syntax so that the rule would be accepted, but I'm not sure if I broke the rule in doing so. I don't get any connection when I don't send user credentials (thus preventing the mstshash= but I can login once I do supply the username. Also, the correct syntax from the documentation is [TCP::payload_length], but this gives me an error:
line 6:[undefined procedure: TCP::payload_length][TCP::payload_length]
If I change the expression from
when CLIENT_DATA {
if { [TCP::payload_length] < 25 } {
TCP::collect
return
}
to
when CLIENT_DATA {
if { [TCP::payload length] < 25 } {
TCP::collect
return
}
It doesn't error on this, but it also doesn't work. The syntax is looking for a size following TCP::payload, so I'm not sure what it is evaluating at this point. - rapmaster_c_127Historic F5 AccountTry TCP::offset instead?
- rapmaster_c_127Historic F5 AccountOr actually, come to think of it, you want to collect at least 25 bytes, right? In that case TCP::collect 25 ought to do it for you. TCP::offset should give you the amount currently collected, but there should be no need for this if you specify the length to collect.
- JRahm
Admin
OK, I commented out the if statement with TCP::payload_length and used TCP::collect 25 instead. This is working, and I see the log entries.
As the cookie is scanned:
Cookie: msts=813964042.15629.0000^M
After the cookie is processed:
Mar 2 12:46:48 :Rule msrdp_session_uie :DEBUG: overriding to 169575472:3389
169575472 when converted to hex and split into octets is the correct IP for the server where my session is.
The BigIP receives the cookie, but never initiates a connection to the correct server. Is there something special I need to do with the node command? Currently, the line looks like this:
set node [ntohl $node]
set port [ntohs $port]
log "DEBUG: overriding to $node:$port"
Note: the node command does not check the pool
use node $node $port
In tcpdump, after the client has received the routing token, establishes a new tcp session with the BigIP, and sends the routing token, the only packet the client receives in return is a reset, and, based on relative timestamps, no packets are sent from BigIP to either the server that redirected or the server where the client's original session is.
The only other line after the node command is TCP::release. Is there a command I'm missing?
Jason - unRuleY_95363Historic F5 AccountOoops, sorry. I forgot to format the $node variable into an IP address that is compatible with the node command. You should be seeing an error in /var/log/ltm.
So, replace the "set node [ntohl $node]" line with something like:binary scan [binary format I $node] c1c1c1c1 ip1 ip2 ip3 ip4 set ip1 [expr { ($ip1 + 0x100) % 0x100 }] set ip2 [expr { ($ip2 + 0x100) % 0x100 }] set ip3 [expr { ($ip3 + 0x100) % 0x100 }] set ip4 [expr { ($ip4 + 0x100) % 0x100 }] set node [format "%u.%u.%u.%u" $ip1 $ip2 $ip3 $ip4]
Of couse, depending on how many different nodes you have, it may be quicker to initialize an array with the integer/ip as name/value pairs.
An example of this approach would look like:when RULE_INIT { array set ::my_nodes { 169575472 10.27.132.48 813964042 48.132.27.10 } } when CLIENT_DATA { ... if { not [info exists $::my_nodes($node)] } { log "Unrecognized Terminal Server Node: $node" reject return } use node $::my_nodes($node) $port ... }
The first approach is probably the safest as you don't have to keep the ::my_nodes array up-to-date with the current set of actual terminal servers.
Hope this helps. - JRahm
Admin
Concerning performance, this rule will only fire on session initialization, correct? Thanks.
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)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