Forum Discussion

Original_D_2018's avatar
Original_D_2018
Icon for Nimbostratus rankNimbostratus
Aug 06, 2015

iRule to create TPDU header

I have a project to grab the source IP address from a client connection and create a fixed length TPDU header to send to the backend server. I need some help to accomplish this: These are my beginning thoughts for the iRule, but I am not sure how to send it to the selected server or what format the data should be in string, hexadecimal, etc... when CLIENT_ACCEPTED { set CLIENT_IP [IP::client_addr] TCP::collect } when CLIENT_DATA { set tcppkt [TCP::payload] set CLIENT_PORT [TCP::client_port] set tpdu_constant "IP36 CR"

 

$tpdu_constant $CLIENT_IP:$CLIENT_PORT $tcppkt

 

unset CLIENT_IP unset tcppkt unset CLIENT_PORT unset tpdu_constant }

 

11 Replies

  • You are heading in the right direction. A few small things right off the bat:

    1. Generally you don't need to set IP::client_addr, TCP::client_port and TCP::payload to a variable. For Tcl, it is faster not to do so (particularly for the payload);
    2. You don't need to unset variables. Variables are scoped to the connection (unless they are declared global -- but don't do that -- or in the static:: namespace) so they will be freed when the connection closes.

    With those quibbles out of the way: what is the server expecting? Does it want 32-bits (the IP) followed by 16 bits (the port) followed by the original data, or does it want the IP address as text, the literal colon, then the port, as text? Also, should this data be inserted at the start of the TCP stream (that's easy) or with each TCP segment (that's difficult, maybe impossible)?

    • Original_D_2018's avatar
      Original_D_2018
      Icon for Nimbostratus rankNimbostratus
      Thanks, 1. The data is expected at the start, in other words it is a header with the original TCP payload as its data 2. It is a protocol but for some reason I have not found its specific rfc/spec except for what I will list below. So I can't say which format the server is expecting the data as yet. I am hoping a TPDU expert can help me. The TPDU messages have the following structure: Bytes 0 - 3The string literal"IP36" Bytes 4 - 5 An operation code Bytes 6 - 29An IPv4 identifier constant "00000000000000000000FFFF" Bytes 30 - 37A representation of an IP address Bytes 38 - 39A representation of a port Balance of available bytesMessage data (Optional) Where the field values are defined as follows: •Operation code: This is used to indicate the type of activity being communicated, for example, a connection has been established to the remote entity. •IP address and port: This is set to the remote address of the source for which the notification is being sent. For example, for a message received, this will contain the remote address of the source entity connected to a SAP from which the message was routed. •Message data: If present, this will generally contain a transactional message received from the source entity.
    • Original_D_2018's avatar
      Original_D_2018
      Icon for Nimbostratus rankNimbostratus
      Also what is the best event to send the data to the server? And what command/reserve word to use to send the data to the selected server?
  • Because the BIG-IP is a normally a full-proxy, for the load-balancing function, the client establishes a connection to the BIG-IP, then an LB decision is made, then the server connection is established. For each incoming segment from the client, data may be collected, modified, then forwarded toward the server. So the trick here is to grab the data from the first segment after the three way handshake and insert your header, followed by whatever data was in that segment. Thereafter, you may allow the flow to continue without alteration. The event where you may manipulate data from the client-side (before sending it to the server) is CLIENT_DATA, which is triggered when TCP::collect is invoked. In this case, it makes sense to do so in the CLIENT_ACCEPTED event, just as you had.

    So, this should achieve what you're after:

     

    when CLIENT_ACCEPTED {
        TCP::collect
    }
    
    when CLIENT_DATA {
        TCP::payload replace 0 0 [binary format "a*SIIIIIIIc4S" "IP36" 0x01 0x0 0x0 0x0 0x0 0x0 0x0000ffff 0x0 [split [IP::client_addr] .] [TCP::client_port]]
        TCP::release
    }
    

     

    The 0x01 is the "operation code". I used the value of 1 because I don't know what you wish that to be. I also assume that the IPv4 address is high-order padded (it's 64 bits, so I assume the first 32-bits are zero followed by the 32-bit IPv4 address). Finally, I assume all values should be encoded in network byte-order.

    • Original_D_2018's avatar
      Original_D_2018
      Icon for Nimbostratus rankNimbostratus
      I will definitely do some testing after I return from the F5 Agility Conference here in D.C. Question, what is "a*SIIIIIIIc4S"?
    • Original_D_2018's avatar
      Original_D_2018
      Icon for Nimbostratus rankNimbostratus
      Also I found this: http://ntrg.cs.tcd.ie/undergrad/4ba2/transport/5.nq.10.html Thanks a million for the help! I will use Wireshark to see if it picks up the TPDU protocol format as a pre-test.
    • Original_D_2018's avatar
      Original_D_2018
      Icon for Nimbostratus rankNimbostratus
      As for the operational code I found: http://www.banalyzer.de/ban/HTML/P_ISO/Eng/P_iso93.html.
  • The binary format method takes a template that describes how you want to pack the variables that follow the template string. These have meaning as follows:

    • a* - take the first parameter (the literal "IP36") and convert all of the letters in 8-bit ASCII encoded values;
    • S - take the second parameter (since it is the second item in the template) -- which is the operational code -- and convert it to a 16-bit unsigned integer using big-endian byte order (which is the same as network byte order);
    • IIIIII - each of these is a 32-bit unsigned integer in big-endian byte order. In your specification, there is a byte stream 0x00000000 00000000 00000000 00000000 00000000 0000ffff (24 bytes total, or 6 x 4 bytes);
    • I - again, a 32-bit unsigned integer in big-endian byte order, this is the leading 4 bytes of the address field (which, by your layout, is 8 bytes, so I assume the first 4 bytes are zeroes);
    • c4 - the c is a one byte unsigned integer. c4 means "assume a list of four elements, and convert each element into an 8-bit integer". The split does exactly this: it splits the textual representation of the client IP (as dotted-quad) into a list of four elements, where each element is one byte of the IP address is descending (and big-endian) order;
    • S - again, an unsigned 16-bit integer, it is passed the value of the client source port.

    This might be of some value:

    • Original_D_2018's avatar
      Original_D_2018
      Icon for Nimbostratus rankNimbostratus
      That is a awesome explanation, I will read the article. So you gave the operational code as "1". Comparing these articles: http://ntrg.cs.tcd.ie/undergrad/4ba2/transport/5.nq.10.html & http://www.banalyzer.de/ban/HTML/P_ISO/Eng/P_iso93.html. It is a 4 bit code but then it says the code for a request is: CR - connection request.......... x x x x x 1110 xxxx xxxx (bits 4 to 1): used to signal the CDT; set to 0000 in classes 0 and 1. I am lost with what they're saying?!? For a customer request should I put "0x1110" to represent CR?
  • Well, assuming that it is ISO 8073, there is an RFC (rfc905 from 1984!) and it relates to the original OSI specification for connection-oriented services (making it a cousin of TCP):

     

    Beyond that, the RFC is long and not terribly well organized (it is lifted directly from the ISO specification, apparently), so for better-or-worse, you'll have to try and sort through it. You might consider engaging F5 Professional Services, a for-pay consulting arm of F5. They should be able to take the time to work through protocol details with you and craft an appropriate iRule based on the specification.