Technical Forum
Ask questions. Discover Answers.
cancel
Showing results for 
Search instead for 
Did you mean: 

Balancing SMPP traffic based on recipient address

Pavel_70776
Nimbostratus
Nimbostratus
Hello,

 

we have an application sending SMS via SMPP and we need to balance the SMS messages based on recipient address to 2 different SMS Centres. SMS with recipient address starting 420 should go to SMSC1 and the rest of SMS's should go to SMSC2. Note that application creates one TCP session and throught this session sends multiple SMS, one SMS is in one TCP packet or more SMS can be placed in one TCP packet.

 

 

Thanks

 

36 REPLIES 36

Michael_Yates
Nimbostratus
Nimbostratus
Hi Pavel,

 

 

Could you clarify what you are looking for a little more?

 

 

Did you want to direct the traffic to a specific location based on the Client's IP Address or the Destination IP Address?

Pavel_70776
Nimbostratus
Nimbostratus
Hi,

 

I'm looking for routing based on recipient or destination address, in SMPP protocol it means the telephone number. I thing this should require the layer 7 routing as the SMPP protocol is based on pairs of request/response PDUs (protocol data units, or packets) exchanged over OSI layer 4 (TCP session). Is it possible to create an iRule to do the required task?

 

I do not want to direct the traffic based on destination IP address.

 

Nat_Thirasuttakorn
F5 Employee
F5 Employee
Hi Pavel,

 

 

it is possible. however, there are a few tings to note

 

- this requires one new feature called message-based load balancing (which is not available in GUI)

 

- you may need to replay bind message to multiple servers (I assume application create TCP connection and send bind request) I am curious what kind of bind message application use, bind_transmitter?

 

 

what kind of SMPP message you are refering to, submit_sm? what SMPP version? what BIG-IP version you are using?

 

 

it would be great if you can provide packet trace of your SMPP traffic. (if it does not contain any sensitive data)

 

 

Nat

Pavel_70776
Nimbostratus
Nimbostratus
Hi Nat,

 

thanks for reply.

 

Application creates TCP connection and send bind_transiever. I need to route submit_sm messages from one application to more SMSC's. The submit_sm-resp and deliver_sm shall go from SMSC back to appl. Next, appl responses with deliver_sm-resp, which must go back to the SMSC previously sending deliver_sm.

 

We use SMPP v3.4

 

 

Pavel

 

Nat_Thirasuttakorn
F5 Employee
F5 Employee
Hi Pavel,

here is something that should work... (however, I didnt test 🙂 )

the idea is to decode submit_sm to find dest-addr

rewrite sequence number of request that was sent from SMSC (and rewrite it back to its original value in the _resp)

you may need to apply mblb profile to virtual: modify ltm virtual smpp profiles add {mblb}

 
when CLIENT_ACCEPTED {
    set s_seq_idx 1
    set first_bind_resp 1
    set smsc1 10.10.10.1 
    set smsc2 10.10.10.2 
    TCP::collect
}
when CLIENT_DATA {
    while { [TCP::payload length] > 16 } {
        binary scan [TCP::payload] IH8IIa* len oper status seq p
        if { [TCP::payload length] < $len } {
            TCP::collect $len
            return
        }
        switch -glob $oper {
            00000002 {
                set bind_message [TCP::payload $len]
            }
            00000004 {
                 submit_sm
                set p [string range $p [expr [string first \x00 $p]+1] end]
                binary scan $p cca* ston snpi p
                set p [string range $p [expr [string first \x00 $p]+1] end]
                binary scan $p ccA* dton dnpi p
                set dest [getfield $p \x00 1]
                if { $dest starts_with 420 } {
                     use pool or node command
                    pool [LB::server pool] member $smsc1 9000
                } else {
                    pool [LB::server pool] member $smsc2 9000
                }
            }
            8* {
                 rewrite seq
                if { [info exists s_seq_map($seq)] } {
                    set old_seq [lindex $s_seq_map($seq) 0]
                    TCP::payload replace 12 4 [binary format I $s_seq_idx]
                    set addr [lindex $s_seq_map($seq) 1]
                    set port [lindex $s_seq_map($seq) 2]
                     use pool or node command
                    pool [LB::server pool] member $addr $port
                    unset s_seq_map($seq)
                }
            }
        }
        TCP::release $len
        TCP::notify request
    }
    TCP::collect
}
when SERVER_CONNECTED {
    if { $first_bind_resp } {
        TCP::collect
    } else {
        TCP::collect -all
        TCP::respond $bind_message
    }
}
when SERVER_DATA {
    while { [TCP::payload length] > 16 } {
        binary scan [TCP::payload] IH8II len oper status seq
        if { [TCP::payload length] < $len } {
            TCP::collect $len
            return
        }
        switch -glob $oper {
            0* {
                 rewrite seq
                TCP::payload replace 12 4 [binary format I $s_seq_idx]
                set s_seq_map($s_seq_idx) "$seq [IP::remote_addr] [TCP::remote_port]"
                incr s_seq_idx
            }
            80000002 {
                 bind response
                if { $first_bind_resp } {
                    set first_bind_resp 0
                } else {
                    TCP::payload replace 0 [TCP::payload length] ""
                    set len 0
                }
            }
        }
        TCP::release $len
        TCP::notify response
    }
    TCP::collect
}

Nat

Nat_Thirasuttakorn
F5 Employee
F5 Employee
ummm... i think this line

 while { [TCP::payload length] > 16 } {
 

should be

 while { [TCP::payload length] >= 16 } {
 

Pavel_70776
Nimbostratus
Nimbostratus
Hi Nat,

 

I have been playing with the BIG-IP last few days and your iRule works fine. I did only small corrections.

 

Great job! Thanks a lot.

 

Next I have to do some more tests in our environment, mainly with fault tolerance.

 

 

Pavel

Nat_Thirasuttakorn
F5 Employee
F5 Employee
Hi Pavel,

 

 

Thanks for update.

 

Glad to hear that it works.

 

Looking forward to hear more test result.

 

 

Nat

Pavel_70776
Nimbostratus
Nimbostratus
Hi Nat,

 

iRule works fine, tested also for asynchronous SMPP transfer and for more clients.

 

Once again thank you.

 

Pavel

Nat_Thirasuttakorn
F5 Employee
F5 Employee
Hi Pavel,

 

 

This is cool. Thanks for letting me know.

 

I forgot to ask. 🙂

 

Last time you mentioned about a small correction that you fixes the iRule to make it works.

 

Could you let me know what are they?

 

 

Nat

Pavel_70776
Nimbostratus
Nimbostratus
Hi Nat,

 

just small corrections:

 

- we use bind_transiever, oper is 00000009

 

- under CLIENT_DATA statement, switch 8*, the original seq number should be entered, the corrected line is:

 

TCP::payload replace 12 4 [binary format I $old_seq]

 

and next I added some log entries just to understand the iRule and to check the variables.

 

 

Regards

 

Pavel

Nat_Thirasuttakorn
F5 Employee
F5 Employee
Hi Pavel,

 

 

Thanks a lot.

 

 

Regards,

 

Nat

Kiran_Reddy_382
Nimbostratus
Nimbostratus
Hi Nat,

 

 

I just want to configure the same with bit change.

 

 

If client submit message and message comes to Load Balancer and IRule executes that and it has to check the recipient address and take that recipient address connect to database or http check the number is in black list or not if it black list reject that message and send reject response to client if not block listed the processed the message to backed server.

 

 

Can any one help me regarding this script.

 

 

Regards,

 

Kiran Reddy

Nat_Thirasuttakorn
F5 Employee
F5 Employee
Hi Kiran,

 

 

Sorry for late reply.

 

I think in your case. you can use this method

 

https://devcentral.f5.com/wiki/irules.SIDEBAND.ashx

 

I think example (under Solutions section) on that page are pretty good.

 

also, I think using http would be easier than database call (if you mean mysql or the like) 🙂

 

 

Nat

Nat_Thirasuttakorn
F5 Employee
F5 Employee
Hi Kiran,

 

 

Sorry for late reply.

 

I think in your case. you can use this method

 

https://devcentral.f5.com/wiki/irules.SIDEBAND.ashx

 

I think example (under Solutions section) on that page are pretty good.

 

also, I think using http would be easier than database call (if you mean mysql or the like) 🙂

 

 

Nat

Hamdi_Fersi_350
Nimbostratus
Nimbostratus

Hi, I see that the topic is quite old, but it's really saving my day, so thanks. Then I got one more question. I could understand from the iRule shared here, that you are balancing based on recipient address, and that's great, but what happens if the ESME opens only one TCP session towards the BigIP? I mean for my case here, I have 2 SMSCs, and 1 ESME, the iRule balance the traffic towards the 2. But the BIND had been done only once towards one of the SMSCs. What is happening is that, when the second SMSC receives the Submit, it shows, "Invalid Bind status", and that's normal because there is no bind done towards the second SMSC. So my question here is that I'm wondering if there is a way, that when the BIND comes to the LB, it clones it towards the 2 SMSCs. If not, I would need some help to make one ESME open only one BIND towards each SMSC (kind of persistance but on 2 servers not only one). Thanks alot for your help ! Best Regards

 

Nat_Thirasuttakorn
F5 Employee
F5 Employee

Hi Hamdi Fersi,

 

This iRules should replicate the bind... this part of code should do that

 

when SERVER_CONNECTED {
    if { $first_bind_resp } {
        TCP::collect
    } else {
        TCP::collect -all
        TCP::respond $bind_message
    }
}

one thing to note, the very first iRules expect bind_transmitter (hex 0x00000002) in your case, it could be bind_transceiver (hex 0x00000009)?

 

anyway, this part of code record the bind message. you could change it from

 

        switch -glob $oper {
            00000002 {
                set bind_message [TCP::payload $len]
            }

to

 

        switch -glob $oper {
            00000002 -
            00000009 {
                set bind_message [TCP::payload $len]
            }

so it will record the message for both type of bind

 

Nat

 

Hamdi_Fersi_350
Nimbostratus
Nimbostratus

Thanks Nat for your quick feedback. after testing that, I succeeded to get binded in one SMSC. but i never could be bound in both of them (with only one BIND request from the ESME). Do I need a second request from the ESME? The ELSE part of the SERVER_CONNECTED event is never reached. Thanks, BR//

 

Nat_Thirasuttakorn
F5 Employee
F5 Employee

yes, in this iRule, it relies on second request from ESME in order to open connection to more server.

 

we can use trick like adding enquire_link into TCP::payload on CLIENT_DATA it will then be balanced to 2nd server and trigger the bind replay. however, doing this way, we may make sure we drop matching enquire_link_resp coming back from server

 

Nat

 

Hamdi_Fersi_350
Nimbostratus
Nimbostratus

Fixed ! I was sending the traffic to only one SMSC: 8* { rewrite seq if { [info exists s_seq_map($seq)] } { set old_seq [lindex $s_seq_map($seq) 0] TCP::payload replace 12 4 [binary format I $s_seq_idx] set addr [lindex $s_seq_map($seq) 1] set port [lindex $s_seq_map($seq) 2] use pool or node command pool [LB::server pool] member $addr $port unset s_seq_map($seq) } }

 

The thing here is that it's being replicated, but only after ~30seconds (after some Enquire_Link requests received). Thanks again for your help. BR//

 

Hamdi_Fersi_350
Nimbostratus
Nimbostratus

Is that normal that i'm never going into the rewrite block? 8* { rewrite seq if { [info exists s_seq_map($seq)] } { set old_seq [lindex $s_seq_map($seq) 0] TCP::payload replace 12 4 [binary format I $s_seq_idx] set addr [lindex $s_seq_map($seq) 1] set port [lindex $s_seq_map($seq) 2] use pool or node command pool [LB::server pool] member $addr $port unset s_seq_map($seq) } }

 

Neither: 0* { rewrite seq TCP::payload replace 12 4 [binary format I $s_seq_idx] set s_seq_map($s_seq_idx) "$seq [IP::remote_addr] [TCP::remote_port]" incr s_seq_idx }

 

Those rewrite routines are only used when servers (in this case, I believe it is smsc) originate request back to client (through LTM). since there are multiple members in the pool which may send request to the same client, iRule will rewrite sequence number to prevent sequence number conflict. it may be normal if the server never originate any request. this usually depends on type of bind.

sanojmz_239128
Nimbostratus
Nimbostratus

Hi, I am new to Big-F5, I see that many threads in the message heading about SMPP talk about load balancing support based on SMPP protocol.

 

we have problem in hand to route the SMPP PDU based on the "system_id" parameter. I some one can help sharing the iLOG configuration, I would appreciate it.

 

SMPP Protoco:: The system_id parameter is used to identify an ESME or an SMSC at bind time.

 

kazeem_yusuf1
Nimbostratus
Nimbostratus

Hello everyone, i have a similar request. Requests are to hit load balancer ip, to two smpp firewalls to the SMSC.

 

Requests are to hit the loadbalancer each time before getting to any of the smpp firewalls. Which means the loadbalancer is hit thrice.

 

Is there any irule to ensure that once requests hit the loadbalancer, binds are formed with two or three smpp backend servers

 

Joko_Yuliantor3
Historic F5 Account

Hi Kazeem, It is possible to do bind over multiple SMPP backend server and keep them open. This is the concept of MBLB in the previous TMOS version or MRF in the newer TMOS version. However, it is better to do it one by one instead of opening bind all the same time. The iRule above written by Nat (natty76) should do the trick. Try that first and see what the road block.

 

Hello Joko, Thanks for your prompt reply.NATTY76 on this thread, explained that adding enquire_link to TCP::payload under CLIENT_DATA,it will help to trigger bind replay .

 

How can this be configured.

 

ALSO, FOR MY SCENARIO, THERE ARE two separate smpp firewalls/filters, before requests hit the actual SMSC, my current scenari,is to have three separate load balancer instances before final request hits the SMSC.

 

This is my current irule for the first instance,

 

when CLIENT_ACCEPTED { set s_seq_idx 1 set first_bind_resp 1 set mm1 10.199.254.37%5 set mm2 10.199.254.38%5 set mm3 10.199.254.39%5 TCP::collect } when CLIENT_DATA { while { [TCP::payload length] >= 16 } { binary scan [TCP::payload] IH8IIa* len oper status seq p if { [TCP::payload length] < $len } { TCP::collect $len return } switch -glob $oper { 00000002 - 00000009 { set bind_message [TCP::payload $len] } 00000004 { submit_sm set p [string range $p [expr [string first \x00 $p]+1] end] binary scan $p cca* ston snpi p set p [string range $p [expr [string first \x00 $p]+1] end] binary scan $p ccA* dton dnpi p set dest [getfield $p \x00 1] if { $dest starts_with 420 } { use pool or node command pool [LB::server pool] member $mm3 9000 } else { pool [LB::server pool] member $mm1 9000 } elseif { pool [LB::server pool] member $mm2 9000 } } 8* { rewrite seq if { [info exists s_seq_map($seq)] } { set old_seq [lindex $s_seq_map($seq) 0] TCP::payload replace 12 4 [binary format I $s_seq_idx] set addr [lindex $s_seq_map($seq) 1] set port [lindex $s_seq_map($seq) 2] use pool or node command pool [LB::server pool] member $addr $port unset s_seq_map($seq) } } } TCP::release $len TCP::notify request } TCP::collect } when SERVER_CONNECTED { if { $first_bind_resp } { TCP::collect } else { TCP::collect -all TCP::respond $bind_message } } when SERVER_DATA { while { [TCP::payload length] >= 16 } { binary scan [TCP::payload] IH8II len oper status seq if { [TCP::payload length] < $len } { TCP::collect $len return } switch -glob $oper { 0* { rewrite seq TCP::payload replace 12 4 [binary format I $s_seq_idx] set s_seq_map($s_seq_idx) "$seq [IP::remote_addr] [TCP::remote_port]" incr s_seq_idx } 80000002 { bind response if { $first_bind_resp } { set first_bind_resp 0 } else { TCP::payload replace 0 [TCP::payload length] "" set len 0 } } } TCP::release $len TCP::notify response } TCP::collect }

 

Hi Joko, How do i add the mrf profile to the virtual server.

 

Hello Joko, Thanks for the heads up, the smpp binds hit the 1st smpp firewall, when the request is to be forwarded to the second F5 ip, it returns an invalid system id field.

 

How can a system id be included inside an SMPP VIP

 

My topology involves an ESME, two smpp firewalls which communicate via separate F5 vip's To the SMSC.

 

So, my topology looks like this.

 

ESME>>>1ST F5 VIP

 

1ST F5 VIP forwards t0 1st smpp firewalls.

 

1st smpp firewalls are configured to forward to second f5 VIP.

 

1ST SMPP FW>>>>>2ND F5 VIP

 

2nd F5 VIP load balances to 2nd smpp firewalls.

 

2nd f5 vip>>>2nd smpp firewalls.

 

2nd smpp firewalls are configured to forward to third f5 vip.

 

2nd smpp firewalls >>>>> third f5 vip.

 

third f5 vip load balances to SMSC.

 

3rd f5 vip>>>SMSC.

 

The irule used is listed in comment. The bind tranceiver message hits the 1st smpp firewall, and fails on hitting the second F5 VIP, as the second F5 VIP doesn't have the SYSTEM ID parameter configured.

 

How do i configure the system ID on the 2nd and third F5 VIP's.

 

Joko_Yuliantor3
Historic F5 Account

Dear Kazeem,

 

Honestly, I totally forgot the SMPP spec as my last encounter with SMPP is on 2013. So, I can't help much further about the system ID.

 

I am more interested in the architecture. I am not sure why there are multiple firewalls with F5 in between. Anyhow, I believe the iRule should be placed in the 3rd VIP instead of the 1st. It should be plain TCP LB in 1st VIP and 2nd VIP. The 3rd VIP should be the one talking SMPP to the SMPP server and doing the MBLB.

 

Cheers,

 

-joko

 

Thank you Joko, however, i don't understand what you mean by TCP LOAD BALANCING.

 

Can you please explain or give example of TCP load balancing to a pool of servers

 

TCP load balancing means that F5 conduct load-balancing per TCP connection. Once the TCP connection is established to the server-side, the packet from the client will always use that connection to F5 and landed in the same server all the time until the connection is closed. An example would be a VS with standard type and TCP protocol selected, that is TCP LB. It works well for HTTP because short-lived but it does not work well with long-lived connection such as SMPP or DIAMETER. That is the reason on message-based load balancing (MBLB).

 

Hello Joko, Your suggestion on TCP load balancing worked!!!!!!!!!!!

 

I however added mblb profile on cli to the two initial F5 VIP'S. I have noticed something however, the smpp submit_sm works for so long, then after sometime, stops working.

 

If i however bypass, the 3rd F5 VIP (which has SMSC as pool member) and send submit_sm directly to SMSC, i don't get a timeout.

 

What do you think could be responsible?.

 

This is the irule on the 3rd VIP.

 

when CLIENT_ACCEPTED { set s_seq_idx 1 set first_bind_resp 1 set sms_m1 10.206.140.172%5 set sms_m2 10.206.140.173%5 TCP::collect } when CLIENT_DATA { while { [TCP::payload length] >= 16 } { binary scan [TCP::payload] IH8IIa* len oper status seq p if { [TCP::payload length] < $len } { TCP::collect $len return } switch -glob $oper { 00000002 - 00000009 { set bind_message [TCP::payload $len] } 00000004 { submit_sm set p [string range $p [expr [string first \x00 $p]+1] end] binary scan $p cca* ston snpi p set p [string range $p [expr [string first \x00 $p]+1] end] binary scan $p ccA* dton dnpi p set dest [getfield $p \x00 1] if { $dest starts_with 420 } { use pool or node command pool [LB::server pool] member $sms_m2 10000 } else { pool [LB::server pool] member $sms_m1 10000 } } 8* { rewrite seq if { [info exists s_seq_map($seq)] } { set old_seq [lindex $s_seq_map($seq) 0] TCP::payload replace 12 4 [binary format I $old_seq] set addr [lindex $s_seq_map($seq) 1] set port [lindex $s_seq_map($seq) 2] use pool or node command pool [LB::server pool] member $addr $port unset s_seq_map($seq) } } } TCP::release $len TCP::notify request } TCP::collect } when SERVER_CONNECTED { if { $first_bind_resp } { TCP::collect } else { TCP::collect -all TCP::respond $bind_message } } when SERVER_DATA { while { [TCP::payload length] >= 16 } { binary scan [TCP::payload] IH8II len oper status seq if { [TCP::payload length] < $len } { TCP::collect $len return } switch -glob $oper { 0* { rewrite seq TCP::payload replace 12 4 [binary format I $s_seq_idx] set s_seq_map($s_seq_idx) "$seq [IP::remote_addr] [TCP::remote_port]" incr s_seq_idx } 80000002 { bind response if { $first_bind_resp } { set first_bind_resp 0 } else { TCP::payload replace 0 [TCP::payload length] "" set len 0 } } } TCP::release $len TCP::notify response } TCP::collect }

 

etrust_146327
Cirrus
Cirrus

Hi

 

What does "TCP::collect -all" do?

 

I can't find the '-all' flag anywhere in documentation

 

afk_368521
Nimbostratus
Nimbostratus

Hello etrust,

 

Maybe they are like...

 

Available qualifiers:

 

" + " Pass, an IP that matches a mechanism with this qualifier will pass.

 

" - " Fail, an IP that matches a mechanism with this qualifier will fail.

 

" ~ " SoftFail, an IP that matches a mechanism with this qualifier will soft fail.

 

" ? " Neutral, an IP that matches a mechanism with this qualifier will neither pass or fail.

 

But for sure, someone from the F5 team should confirm or deny this assumption.

 

pratiksrivastav
Nimbostratus
Nimbostratus

Need same load balancing but persist with mobile no. dont need specific mobile no. to go on single server.

 

Any help?

marvinjuelz
Nimbostratus
Nimbostratus

Can I kindly have the original irule for the original question?