From a1fe7c8de4b6ced9191398e71f9461b92124ccf3 Mon Sep 17 00:00:00 2001 From: Moises Silva Date: Wed, 28 Jul 2010 11:35:39 -0400 Subject: [PATCH] freetdm: call rate limit --- libs/freetdm/mod_freetdm/mod_freetdm.c | 76 +++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 6233feffbd..c7262242ef 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -38,6 +38,7 @@ #define __FUNCTION__ __SWITCH_FUNC__ #endif +#define FREETDM_LIMIT_REALM "__freetdm" #define FREETDM_VAR_PREFIX "freetdm_" #define FREETDM_VAR_PREFIX_LEN 8 @@ -55,6 +56,11 @@ typedef enum { ANALOG_OPTION_CALL_SWAP = (1 << 1) } analog_option_t; +typedef enum { + FTDM_LIMIT_RESET_ON_TIMEOUT = 0, + FTDM_LIMIT_RESET_ON_ANSWER = 1 +} limit_reset_event_t; + typedef enum { TFLAG_IO = (1 << 0), TFLAG_DTMF = (1 << 1), @@ -119,6 +125,10 @@ struct span_config { char hold_music[256]; char type[256]; analog_option_t analog_options; + const char *limit_backend; + int limit_calls; + int limit_seconds; + limit_reset_event_t limit_reset_event; chan_pvt_t pvts[FTDM_MAX_CHANNELS_SPAN]; }; @@ -1292,6 +1302,9 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi switch_caller_profile_t *caller_profile; switch_channel_t *channel = switch_core_session_get_channel(*new_session); + span_id = ftdm_channel_get_span_id(ftdmchan); + chan_id = ftdm_channel_get_id(ftdmchan); + switch_core_session_add_stream(*new_session, NULL); if ((tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t))) != 0) { tech_init(tech_pvt, *new_session, ftdmchan); @@ -1302,12 +1315,12 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi goto fail; } - snprintf(name, sizeof(name), "FreeTDM/%u:%u/%s", ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan), dest); + snprintf(name, sizeof(name), "FreeTDM/%u:%u/%s", span_id, chan_id, dest); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Connect outbound channel %s\n", name); switch_channel_set_name(channel, name); switch_channel_set_variable(channel, "freetdm_span_name", ftdm_channel_get_span_name(ftdmchan)); - switch_channel_set_variable_printf(channel, "freetdm_span_number", "%d", ftdm_channel_get_span_id(ftdmchan)); - switch_channel_set_variable_printf(channel, "freetdm_chan_number", "%d", ftdm_channel_get_id(ftdmchan)); + switch_channel_set_variable_printf(channel, "freetdm_span_number", "%d", span_id); + switch_channel_set_variable_printf(channel, "freetdm_chan_number", "%d", chan_id); ftdm_channel_set_caller_data(ftdmchan, &caller_data); caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); caller_profile->destination_number = switch_core_strdup(caller_profile->pool, switch_str_nil(dest_num)); @@ -1325,6 +1338,18 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi } + if (SPAN_CONFIG[span_id].limit_calls) { + char spanresource[512]; + snprintf(spanresource, sizeof(spanresource), "span_%s_%s", ftdm_channel_get_span_name(ftdmchan), caller_data.dnis.digits); + ftdm_log(FTDM_LOG_DEBUG, "Adding rate limit resource on channel %d:%d (%s/%s/%d/%d)\n", span_id, chan_id, FREETDM_LIMIT_REALM, + spanresource, SPAN_CONFIG[span_id].limit_calls, SPAN_CONFIG[span_id].limit_seconds); + if (switch_limit_incr("hash", *new_session, FREETDM_LIMIT_REALM, spanresource, SPAN_CONFIG[span_id].limit_calls, SPAN_CONFIG[span_id].limit_seconds) != SWITCH_STATUS_SUCCESS) { + switch_core_session_destroy(new_session); + cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; + goto fail; + } + } + if ((status = ftdm_channel_call_place(ftdmchan)) != FTDM_SUCCESS) { if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); @@ -1516,6 +1541,18 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal) } } break; + case FTDM_SIGEVENT_UP: + { + /* clear any rate limit resource for this span */ + char spanresource[512]; + if (SPAN_CONFIG[spanid].limit_reset_event == FTDM_LIMIT_RESET_ON_ANSWER && SPAN_CONFIG[spanid].limit_calls) { + ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(sigmsg->channel); + snprintf(spanresource, sizeof(spanresource), "span_%s_%s", ftdm_channel_get_span_name(sigmsg->channel), caller_data->dnis.digits); + ftdm_log(FTDM_LOG_DEBUG, "Clearing rate limit resource on channel %d:%d (%s/%s)\n", spanid, chanid, FREETDM_LIMIT_REALM, spanresource); + switch_limit_interval_reset("hash", FREETDM_LIMIT_REALM, spanresource); + } + return FTDM_SUCCESS; + } default: return FTDM_SUCCESS; break; @@ -2344,7 +2381,7 @@ static switch_status_t load_config(void) } } - if ((spans = switch_xml_child(cfg, "sangoma_isdn_spans"))) { + if ((spans = switch_xml_child(cfg, "sangoma_pri_spans")) || (spans = switch_xml_child(cfg, "sangoma_bri_spans"))) { for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { ftdm_status_t zstatus = FTDM_FAIL; const char *context = "default"; @@ -2362,11 +2399,6 @@ static switch_status_t load_config(void) continue; } - if (!configname) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sangoma isdn span missing required attribute, skipping ...\n"); - continue; - } - if (name) { zstatus = ftdm_span_find_by_name(name, &span); } else { @@ -2399,6 +2431,10 @@ static switch_status_t load_config(void) } } + /* some defaults first */ + SPAN_CONFIG[span_id].limit_backend = "hash"; + SPAN_CONFIG[span_id].limit_reset_event = FTDM_LIMIT_RESET_ON_TIMEOUT; + for (param = switch_xml_child(myspan, "param"); param; param = param->next) { char *var = (char *) switch_xml_attr_soft(param, "name"); char *val = (char *) switch_xml_attr_soft(param, "value"); @@ -2412,6 +2448,28 @@ static switch_status_t load_config(void) context = val; } else if (!strcasecmp(var, "dialplan")) { dialplan = val; + } else if (!strcasecmp(var, "call_limit_backend")) { + SPAN_CONFIG[span_id].limit_backend = val; + ftdm_log(FTDM_LOG_DEBUG, "Using limit backend %s for span %d\n", SPAN_CONFIG[span_id].limit_backend, span_id); + } else if (!strcasecmp(var, "call_limit_rate")) { + int calls; + int seconds; + if (sscanf(val, "%d/%d", &calls, &seconds) != 2) { + ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var); + } else { + if (calls < 1 || seconds < 1) { + ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, minimum call limit must be 1 per second\n", var); + } else { + SPAN_CONFIG[span_id].limit_calls = calls; + SPAN_CONFIG[span_id].limit_seconds = seconds; + } + } + } else if (!strcasecmp(var, "call_limit_reset_event")) { + if (!strcasecmp(val, "answer")) { + SPAN_CONFIG[span_id].limit_reset_event = FTDM_LIMIT_RESET_ON_ANSWER; + } else { + ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, only accepted event is 'answer'\n", var); + } } else { spanparameters[paramindex].var = var; spanparameters[paramindex].val = val;