Forum Discussion

Kevin_Jones_505's avatar
Kevin_Jones_505
Historic F5 Account
Oct 26, 2004

TCP::payload - How do I get data from the TCP data field?

I'm trying to write an iRule that reads the TCP data rather than HTTP data. I'm building it off an HTTP data iRule that works. The first one works as expected.

 

 

when HTTP_REQUEST {

 

if {[findstr [HTTP::uri] "user=" 5] == "me" } {

 

pool pool1

 

}

 

else {

 

pool pool2

 

}

 

}

 

 

The second doesn't seem to get any data; the TCP::payload seems to be a null.

 

 

when CLIENT_ACCEPTED {

 

if {[TCP::local_port] == 80} {

 

TCP::collect 75

 

log local0. "Test1[findstr [TCP::payload] "user=" 5]End"

 

}

 

}

 

when CLIENT_DATA {

 

if {[findstr [TCP::payload] "user=" 5] == "me"} {

 

pool pool1

 

}

 

else {

 

pool pool2

 

}

 

}

 

 

When I run this, the ltm log shows .. Test1End, infering the TCP::payload is null. I've tried many other combinations, but no luck so far. Any advice?

 

 

thanks ...

7 Replies

  • You are very close. Let's take a look at your code piece by piece...

     

     

    when CLIENT_ACCEPTED { 
       if {[TCP::local_port] == 80} { 
         TCP::collect 75 
         log local0. "Text1[findstr [TCP::payload] "user=" 5]End" 
       } 
     }

     

     

    The CLIENT_ACCEPTED event is triggered when the client establishes a connection. At this point there is no data to be processed (we'll there could be but it is not guaranteed). You must call TCP::collect which you are doing for 75 bytes. The resulting payload is not available until the CLIENT_DATA event is triggered. This will occur when the 75 bytes have been received (or portion of if the size is smaller than 75). At this point you will have access to the TCP::payload data.

     

     

    when CLIENT_DATA { 
       log local0. "Text1[findstr [TCP::payload] "user=" 5]End" 
       if {[findstr [TCP::payload] "user=" 5] == "me"} { 
         pool pool1 
       } 
       else { 
         pool pool2 
       } 
     }

     

     

    Note that if you need additional data, you can call TCP::collect from within the CLIENT_DATA event and that will trigger a subsequent CLIENT_DATA event when the additional data is called. When you access TCP::payload at this point, the content will be the total accumulated payload.

     

     

    Hope this helps.

     

     

    -Joe
  • unRuleY_95363's avatar
    unRuleY_95363
    Historic F5 Account
    It needs to be pointed out that there is currently a known issue in v9.0.1 (CR41716) that will prevent making a load-balancing decision in the CLIENT_DATA event from working correctly.

     

     

    The problem is that TCP::collect doesn't properly hold the internal accepted event from reaching the proxy. This results in a load-balancing decision as soon as the TCP connection is established and *before* any data has actually been received. After the load-balancing decision has occurred and the server-side has been connected, selecting a new pool will have no effect.

     

     

    There is possibly a very crazy way to make it work that involves using the LB_SELECTED and USER_RESPONSE events, as well as the TCP::notify and LB::reselect commands. However, I instead highly recommend getting the impending v9.0.2 release over doing this kind of thing (you're welcome to experiment).

     

     

  • Hello,

     

     

    I used your advices, and all works fin, but... I'm trying to achieve same functionality with ssl client profile enabled. When using the iRule above, the encrypted data is collected which causes the balancing decision impossible.

     

    I wonder if anyone knows what should be done to collect decrypted payload from the ssl traffic which is terminated by ltm.

     

     

    BR.

     

    w.
  • I was able to get the above example working with a TCP, but also ran into the same issue as described by wmazanek. Is the LB selection possible with an iRule on a SSL_TCP virtual server?
  • spark_86682's avatar
    spark_86682
    Historic F5 Account
    There's no direct way yet (it's coming, though), so the best you can do is an indirect way. If you're on v9.4.0 or above, you can use the virtual command to direct the decrypted traffic to a standard TCP virtual server where you can then use the usual TCP:: commands to inspect content and make load-balancing decisions. To elaborate, the virtual server that the client connects to will have a clientssl profile, and a very simple iRule that looks like:

     
     when CLIENT_ACCEPTED { 
       virtual internal_virtual 
     } 
     

    so that the decrypted traffic will be accessible on the "internal_virtual" server. Then, you create that new virtual server (here, named "internal_virtual") and you can just inspect the plaintext content in the usual TCP way:

     
     when CLIENT_ACCEPTED { 
       TCP::collect 100 
     } 
     when CLIENT_DATA { 
       set payload [TCP::payload] 
       if { $payload contains "magic" } { 
         pool magic_pool 
       } else { 
         pool default_pool 
       } 
     } 
     

    If you're on some version before v9.4.0, then the only way to do it is an ugly hack that just simulates the above. If this is the situation you're in, and you can't upgrade, post back and I'll try to write up the instructions, because they're long and ugly.
  • Hi Spark,

     

     

    I'm running version 9.3.0 Build 178.5 so no virtual command to send the session off to another virtual server.

     

    Can I trouble you to post the long & ugly hack ?

     

     

    Cheers,

     

     

    Steve