Forum Discussion

henrik_k's avatar
henrik_k
Icon for Altostratus rankAltostratus
Sep 03, 2024

TCP::collect and large TLS v1.3 client hello packets

Is anyone using iRules successfully to parse SNI names from the new TLS 1.3 hybridized Kyber client hello packets? The problem is the these packets are larger than MTU(?) size, around ~1800 bytes. No...
  • henrik_k's avatar
    Sep 04, 2024

    Here is working cleaned up example code for everyone. TLS parsing itself is based on some old forum code which I cleaned up, all credits there.

    when CLIENT_ACCEPTED {
      set collect_count 0
      set tls_servername ""
      TCP::collect
    }
    
    when CLIENT_DATA {
      # Collect max 2 extra packets
      if { $collect_count > 2 } {
        reject; event disable all; return
      }
    
      # Parse first packet?
      if { not $collect_count } {
        if { [binary scan [TCP::payload] cSSc tls_xacttype tls_version tls_recordlen tls_action] != 4 } {
          reject; event disable all; return
        }
        # Only allow TLSv1.2 = 771, TLSv1.3 = 769
        if { $tls_xacttype ne "22" or ($tls_version ne "769" and $tls_version ne "771") or $tls_action ne "1" } {
          reject; event disable all; return
        }
        set record_offset 43
        if { not [binary scan [TCP::payload] @${record_offset}c tls_sessidlen] } {
          reject; event disable all; return
        }
        incr record_offset [expr {1 + $tls_sessidlen}]
        if { not [binary scan [TCP::payload] @${record_offset}S tls_ciphlen] } {
          reject; event disable all; return
        }
        incr record_offset [expr {2 + $tls_ciphlen}]
        if { not [binary scan [TCP::payload] @${record_offset}c tls_complen] } {
          reject; event disable all; return
        }
        incr record_offset [expr {1 + $tls_complen}]
        if { not [binary scan [TCP::payload] @${record_offset}S tls_extenlen] } {
          reject; event disable all; return
        }
        incr record_offset 2
      }
    
      # Collect more if TLS extensions data not fully received
      if { $tls_extenlen > [TCP::payload length] - $record_offset } {
        incr collect_count
        return
      }
    
      if { not [binary scan [TCP::payload] @${record_offset}a* tls_extensions] } {
        reject; event disable all; return
      }
      for { set x 0 } { $x < $tls_extenlen } { incr x 4 } {
        if { [binary scan $tls_extensions @${x}SS etype elen] != 2 } {
          reject; event disable all; return
        }
        if { $etype == "00" } {
          if { [binary scan $tls_extensions @[expr {$x + 9}]A[expr {$elen - 5}] tls_servername] } {
            break
          }
        }
        incr x $elen
      }
    
      # Choose pool, unknown will be rejected
      switch -- [string tolower $tls_servername] {
        "host1.example.com" { pool host1.example.com }
        "host2.example.com" { pool host2.example.com }
        default { reject; event disable all; return }
      }
    
      TCP::release
    }