Greetings A specific Kannel behavior is required after receiving a throttling error:

  1. For 60 seconds, stop sending any messages.

This feature was realized by editing the source file smsc_smpp.c:
#define SMPP_THROTTLING_SLEEP_TIME 60

  1. After a timeout, send not the next message from the queue, but the message for which the error was received.

That is, if you send messages "1", "2", "3", "4", "5", and get the message "3" throttling error, then Kannel will send message "4" after timeout, then "5" , and only then "3".

It is necessary that after receiving the throttling error Kannel sent "3", then "4", then "5". So that the message "3" rose at the beginning of the queue, and not at the end.

When searching Google, many similar problems were found, but there is not a single working solution. For example:

    2 answers 2

    The problem was solved as follows:

     I did the following patch to smsc/smsc_smpp.c in "case submit_sm_resp:" section from line 1609: change *** if (pdu->u.submit_sm_resp.command_status == SMPP_ESME_RTHROTTLED) time(&(smpp->throttling_err_time)); else smpp->throttling_err_time = 0; bb_smscconn_send_failed(smpp->conn, msg, reason, octstr_format("0x%08lx/%s", pdu->u.submit_sm_resp.command_status, smpp_error_to_string(pdu->u.submit_sm_resp.command_status))); *** to *** if (pdu->u.submit_sm_resp.command_status == SMPP_ESME_RTHROTTLED) { time(&(smpp->throttling_err_time)); /* Put the message back into the SMPP queue */ gw_prioqueue_produce(smpp->msgs_to_send, msg); } else { smpp->throttling_err_time = 0; bb_smscconn_send_failed(smpp->conn, msg, reason, octstr_format("0x%08lx/%s", pdu->u.submit_sm_resp.command_status, smpp_error_to_string(pdu->u.submit_sm_resp.command_status))); } *** and in sms.c I have changed the function sms_priority_compare() to reverse time sorting order (for some reason it was LIFO): if (msg1->sms.time > msg2->sms.time) ret = -1; else if (msg1->sms.time < msg2->sms.time) ret = 1; -------------- next part -------------- 

    SMPP Throttling error processing

    But this change does not affect the order in which parts of the composite message are sent. To fix this, you need to add a comparison by sms.id in the sms_priority_compare file in sms.id . When dividing a message into parts in sms_split this ID is generated using uuid_generate(part->sms.id) . The final sms_priority_compare function looks like this:

     int sms_priority_compare(const void *a, const void *b) { int ret; Msg *msg1 = (Msg*)a, *msg2 = (Msg*)b; gw_assert(msg_type(msg1) == sms); gw_assert(msg_type(msg2) == sms); if (msg1->sms.priority > msg2->sms.priority) ret = 1; else if (msg1->sms.priority < msg2->sms.priority) ret = -1; else { if (msg1->sms.time > msg2->sms.time) ret = -1; else if (msg1->sms.time < msg2->sms.time) ret = 1; else { if (msg1->sms.id > msg2->sms.id) ret = -1; else if (msg1->sms.id < msg2->sms.id) ret = 1; else ret = 0; } } return ret; } 

      The solution in my answer dated 11/11/2016 was working until there was a message with the error Message Queue Full error. Upon request from the SMS center, when receiving an MQF error, the following message should be sent.

      Total requirements :

      • Throttling Error - 60 seconds timeout, rewind message with error
      • Message Queue Full - no timeout, re-send the next message from the queue. The message with the error is placed at the end of the queue

      Solution :

      1. As written in the question, the timeout duration is regulated using SMPP_THROTTLING_SLEEP_TIME in smsc_smpp.c
      2. In the sms.c file in the sms_priority_compare function, sms_priority_compare change the ordering by time. We also add the ordering by id for compound messages:

         if (msg1->sms.time > msg2->sms.time) ret = 1; else if (msg1->sms.time < msg2->sms.time) ret = -1; 

        change to

         if (msg1->sms.time > msg2->sms.time) ret = -1; else if (msg1->sms.time < msg2->sms.time) ret = 1; else { if (msg1->sms.id > msg2->sms.id) ret = -1; else if (msg1->sms.id < msg2->sms.id) ret = 1; else ret = 0; } 
      3. By default, for any errors, Kannel places the message at the end of the queue. To track TE separately, in the smsc/smsc_smpp.c change the function smpp_status_to_smscconn_failure_reason :

         static long smpp_status_to_smscconn_failure_reason(long status) { switch(status) { case SMPP_ESME_RMSGQFUL: case SMPP_ESME_RTHROTTLED: case SMPP_ESME_RX_T_APPN: case SMPP_ESME_RSYSERR: return SMSCCONN_FAILED_TEMPORARILY; break; default: return SMSCCONN_FAILED_REJECTED; } } 

        on

         static long smpp_status_to_smscconn_failure_reason(long status) { switch(status) { case SMPP_ESME_RMSGQFUL: case SMPP_ESME_RX_T_APPN: case SMPP_ESME_RSYSERR: return SMSCCONN_FAILED_TEMPORARILY; break; case SMPP_ESME_RTHROTTLED: return SMPP_ESME_RTHROTTLED; break; default: return SMSCCONN_FAILED_REJECTED; } } 
      4. When receiving an MQF change the message time. Otherwise, when the TE is received, messages that received the MQF and moved to the end of the queue will be moved to the top of the queue. In the smsc/smsc_smpp.c in the handle_pdu function in the case submit_sm_resp: block, case submit_sm_resp: change:

         if (pdu->u.submit_sm_resp.command_status == SMPP_ESME_RTHROTTLED) time(&(smpp->throttling_err_time)); else smpp->throttling_err_time = 0; bb_smscconn_send_failed(smpp->conn, msg, reason, octstr_format("0x%08lx/%s", pdu->u.submit_sm_resp.command_status, smpp_error_to_string(pdu->u.submit_sm_resp.command_status))); 

        on

         if (pdu->u.submit_sm_resp.command_status == SMPP_ESME_RTHROTTLED) time(&(smpp->throttling_err_time)); else smpp->throttling_err_time = 0; if (pdu->u.submit_sm_resp.command_status == SMPP_ESME_RMSGQFUL) time(&(msg->sms.time)); bb_smscconn_send_failed(smpp->conn, msg, reason, octstr_format("0x%08lx/%s", pdu->u.submit_sm_resp.command_status, smpp_error_to_string(pdu->u.submit_sm_resp.command_status))); 
      5. When a TE error is received, we put the message at the top of the queue. To do this, in the bb_smscconn.c file in the bb_smscconn_send_failed function bb_smscconn_send_failed change:

         case SMSCCONN_FAILED_TEMPORARILY: ... gwlist_produce(outgoing_sms, sms); break; case SMSCCONN_FAILED_SHUTDOWN: gwlist_produce(outgoing_sms, sms); break; 

        on

         case SMSCCONN_FAILED_TEMPORARILY: ... gwlist_produce(outgoing_sms, sms); break; case SMPP_ESME_RTHROTTLED: gwlist_insert(outgoing_sms, 0, sms); break; case SMSCCONN_FAILED_SHUTDOWN: gwlist_produce(outgoing_sms, sms); break; 
      6. Similarly, you need to change the function handle_split :

         case SMSCCONN_FAILED_TEMPORARILY: ... gwlist_produce(outgoing_sms, msg); break; case SMSCCONN_FAILED_DISCARDED: 

        on

         case SMSCCONN_FAILED_TEMPORARILY: ... gwlist_produce(outgoing_sms, msg); break; case SMPP_ESME_RTHROTTLED: gwlist_insert(outgoing_sms, 0, msg); break; case SMSCCONN_FAILED_DISCARDED: