Forum Discussion

kazeem1's avatar
kazeem1
Icon for Nimbostratus rankNimbostratus
Jun 01, 2025

SMPP IRULES that insert destination port in One Vip, as Port in Second Vip pool member

I have a 2-step situation with SMPP traffic.

Aggregator Traffic---F5 (VIP 1) 10.1.1.1:* (eg 5102)

F5 (SNAT IP) 10.1.1.2--- SMS FW 10.1.1.3:10000

SMS FW 10.1.1.3--- F5 (VIP 2) 10.1.1.4:10000

F5 (SNAT IP) 10.1.1.2--- SMSC 10.100.114.129:F5 VIP1 destination port (10.100.114.129:5102)

 

How do i solve this using irules

 

4 Replies

  • There is no way to bind dest port in one VS with the pool member port in another VS
    Unless you somehow inject this info in to the request itself
    If we spoke for http this would be very easy with an http header
    In this case I cannot think something else except injecting dest port in to the payload in VIP1 and then in VIP2 extract/strip the value and use it

    I am not an SMPP expert so I played with AI
    It might not work at all but hope you get the logic.

    you can test it and adjust it

    iRule for VIP 1 (Inject destination port)

    when CLIENT_ACCEPTED {
        # Capture the port this VIP was hit on
        set injected_port [TCP::local_port]
    }
    
    when CLIENT_DATA {
        TCP::collect
    
        set pdu [TCP::payload]
    
        # Extract command_id at offset 4 (should be 0x00000004 for submit_sm)
        binary scan $pdu @4I cmd_id
    
        if { $cmd_id == 4 } {
            # Inject service_type: P<PORT> + NULL terminator
            set tag "P$injected_port"
            set null [binary format c 0]
            set service_type "$tag$null"
    
            # Replace original service_type at offset 16
            set new_pdu "${pdu range 0 15}$service_type${pdu range 16 end}"
    
            TCP::payload replace 0 [TCP::payload length] $new_pdu
    
            log local0. "VIP1: Injected service_type tag $tag into submit_sm"
        }
    
        TCP::release
    }

     

    iRule for VIP 2 (Extract, route, and clean)

    when CLIENT_ACCEPTED {
        # Init variable
        set dyn_port 0
    }
    
    when CLIENT_DATA {
        TCP::collect
    
        set pdu [TCP::payload]
    
        # Check SMPP command_id
        binary scan $pdu @4I cmd_id
    
        if { $cmd_id == 4 } {
            # Read service_type field from byte 16 (C-Octet null-terminated)
            binary scan $pdu @16Z* service_type
    
            # Match and extract port from "P<PORT>" format
            if {[regexp {^P(\d{1,5})$} $service_type match dyn_port]} {
                log local0. "VIP2: Extracted dynamic port: $dyn_port"
    
                # Strip service_type: replace with single null byte
                set null [binary format c 0]
                set tag_len [expr {[string length $service_type] + 1}]
                set stripped_pdu "${pdu range 0 15}$null${pdu range [expr 16 + $tag_len] end}"
    
                TCP::payload replace 0 [TCP::payload length] $stripped_pdu
    
                # Set backend IP and dynamic port
                set backend_ip "10.100.114.129"
                node $backend_ip $dyn_port
    
                log local0. "VIP2: Forwarding to $backend_ip:$dyn_port"
            } else {
                log local0. "VIP2: No valid port tag in service_type"
            }
        }
    
        TCP::release
    }

     

  • Thank you so much. I have played around with the scenario. I also agree with your postulations. My postulation is to add the port into a TLV tag for smpp, and then later pass it through, to be extracted on another irule. I tried the below on VIP 1, AND GOT following logs. I will try your approach and test it. Thanks so much.

    when CLIENT_ACCEPTED {

        set orig_port [TCP::local_port]

        set session_key "[IP::client_addr]:[TCP::client_port]"

        set session_data "port=$orig_port"

        table set $session_key $session_data 300
        set orig_port [TCP::local_port]

        set session_key "[IP::client_addr]:[TCP::client_port]"

        set session_data "port=$orig_port"

        table set $session_key $session_data 300

        log local0. "VIP1: Client connected from [IP::client_addr]:[TCP::client_port] on original port $orig_port"

    }


    when CLIENT_DATA {

        if { [TCP::payload length] < 16 } { return }


        binary scan [TCP::payload] I command_length

        if { [TCP::payload length] < $command_length } { return }


        binary scan [TCP::payload] x4I command_id x8I seq_number

        set min_valid_len 37


        if {

            ($command_id == 0x00000009 || $command_id == 0x00000004)

            && [TCP::payload length] >= $min_valid_len

        } {

            set session_key "[IP::client_addr]:[TCP::client_port]"

            set session_data [table lookup $session_key]


            if { [string length $session_data] == 0 } {

                log local0. "VIP1: No session data found for $session_key"

                return

            }


            set orig_port [lindex [split $session_data "="] 1]


            # Prepare TLV

            set tag 0x1401

            set tlv_len 2

            set value [binary format S $orig_port]

            set tlv [binary format SSa* $tag $tlv_len $value]


            # Append TLV and update command_length

            set payload [TCP::payload]

            set new_len [expr {$command_length + [string length $tlv]}]


            # Replace first 4 bytes (command_length)

            set updated_pdu [binary format I $new_len][string range $payload 4 end]$tlv


            # Inject new payload

            TCP::payload replace 0 $command_length $updated_pdu

            log local0. "VIP1: TLV injected (port=$orig_port). New command_length=$new_len"

        }

    }

     

    when CLIENT_CLOSED {

        log local0. "VIP1: Client [IP::client_addr]:[TCP::client_port] closed connection"

        table delete "[IP::client_addr]:[TCP::client_port]"

    }


    when SERVER_CLOSED {

        log local0. "VIP1: SMS Firewall [IP::server_addr]:[TCP::server_port] closed connection"

    }


    Jun  4 04:36:21 unotelossaka.gloworld.com info tmm[18939]: Rule /Common/smsc1 <CLIENT_DATA>: VIP2: Expected TLV 0x1401 not found for 00000009. Using default port 10000.


    Jun  4 04:36:21 unotelossaka.gloworld.com info tmm[18939]: Rule /Common/smsc1 <CLIENT_DATA>: VIP2: SERVER not yet connected; deferring node assignment


    Jun  4 04:36:33 unotelossaka.gloworld.com info tmm[18939]: Rule /Common/smpp1 <CLIENT_CLOSED>: VIP1: Client 193.135.101.16:16715 closed connection


    Jun  4 04:36:33 unotelossaka.gloworld.com info tmm[18939]: Rule /Common/smpp1 <SERVER_CLOSED>: VIP1: SMS Firewall 10.159.59.132:10000 closed connection


    Jun  4 04:36:33 unotelossaka.gloworld.com info tmm[18939]: Rule /Common/smpp1 <CLIENT_CLOSED>: VIP1: Client 193.135.101.16:56925 closed connection


    Jun  4 04:36:33 unotelossaka.gloworld.com info tmm[18939]: Rule /Common/smpp1 <SERVER_CLOSED>: VIP1: SMS Firewall 10.159.59.132:10000 closed connection


    Jun  4 04:36:43 unotelossaka.gloworld.com info tmm2[18939]: Rule /Common/smpp1 <CLIENT_ACCEPTED>: VIP1: Client connected from 193.135.101.16:45599 on original port 5102


    Jun  4 04:36:43 unotelossaka.gloworld.com info tmm3[18939]: Rule /Common/smpp1 <CLIENT_ACCEPTED>: VIP1: Client connected from 193.135.101.16:29867 on original port 5102


    Jun  4 04:36:45 unotelossaka.gloworld.com info tmm1[18939]: Rule /Common/smsc1 <CLIENT_ACCEPTED>: VIP2: Connection received from SMS Firewall from 10.159.59.132:39024


    Jun  4 04:36:45 unotelossaka.gloworld.com info tmm1[18939]: Rule /Common/smsc1 <CLIENT_DATA>: VIP2: Processing command: 00000009 (Length: 37)


    Jun  4 04:36:45 unotelossaka.gloworld.com info tmm1[18939]: Rule /Common/smsc1 <CLIENT_DATA>: VIP2: Expected TLV 0x1401 not found for 00000009. Using default port 10000.

     

  • So,Injeyan, Thank you for your irule. It is fantstic for submit_sm. before the submit_sm, there is the bind_transceiver which is my primary concern. The service is supposed to be for bind_transceiver (0x00000009) and submit_sm (0x00000004). is there a way to write custom TLV to insert destination ports inside and push it inside pdu?  Thanks

    • Injeyan_Kostas's avatar
      Injeyan_Kostas
      Icon for Cumulonimbus rankCumulonimbus

      Thanks but as said have no idea about SMPP.
      I just found this case interesting, understood a concept that might be able to work, and gave the relevant prompts to AI.
      So i will try to understand how this flow works and might think something.
      In the meantime lets see if there is anyone with more knowledge in this matter.