| @ -0,0 +1,249 @@ | |||
| ######## DoS prevention module ######## | |||
| modparam("htable", "timer_interval", 10) | |||
| modparam("htable", "htable", "rps=>size=8;initval=0;autoexpire=60") | |||
| modparam("htable", "htable", "rpm=>size=8;initval=0;autoexpire=180") | |||
| modparam("htable", "htable", "tps=>size=8;initval=0;autoexpire=60") | |||
| modparam("htable", "htable", "tpm=>size=8;initval=0;autoexpire=180") | |||
| modparam("htable", "htable", "rate_limits=>initval=-1;autoexpire=60") # initval = -1 means that record is expired and we need an update from DB | |||
| #!trydef RATE_LIMIT_MESSAGE "Over rate Limit" | |||
| route[DOS_PREVENTION_REPLY] { | |||
| setflag(FLAG_IS_REPLY); | |||
| route(DOS_PREVENTION); | |||
| } | |||
| route[DOS_PREVENTION] { | |||
| # Initially we do not want to get data | |||
| $var(with-realm-request) = "false"; | |||
| $var(with-realm-total) = "false"; | |||
| $var(with-device-request) = "false"; | |||
| $var(with-device-total) = "false"; | |||
| $var(method-key) = "Method"; | |||
| $var(method-value) = "\"TOTAL\""; | |||
| # SIP methods INVITE and REGISTER have personal counters | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| $var(lrpm_realm) = $fd+"/"+$rm+"/min"; | |||
| $var(lrps_realm) = $fd+"/"+$rm+"/sec"; | |||
| $var(lrpm_device) = $fU+"@"+$fd+"/"+$rm+"/min"; | |||
| $var(lrps_device) = $fU+"@"+$fd+"/"+$rm+"/sec"; | |||
| $var(method-value) = "\"" + $rm + "\""; | |||
| } | |||
| # For BYE method we use REALM from To SIP header | |||
| if (is_method("BYE")) { | |||
| xlog("L_WARNING","$ci|RL-realm log| Fixup for BYE method with IP in from URI: use to-domain"); | |||
| $var(ltpm_realm) = $td+"/TOTAL/min"; | |||
| $var(ltps_realm) = $td+"/TOTAL/sec"; | |||
| $var(ltpm_device) = $fU+"@"+$td+"/TOTAL/min"; | |||
| $var(ltps_device) = $fU+"@"+$td+"/TOTAL/sec"; | |||
| $var(entity) = $td; | |||
| } else { | |||
| $var(ltpm_realm) = $fd+"/TOTAL/min"; | |||
| $var(ltps_realm) = $fd+"/TOTAL/sec"; | |||
| $var(ltpm_device) = $fU+"@"+$fd+"/TOTAL/min"; | |||
| $var(ltps_device) = $fU+"@"+$fd+"/TOTAL/sec"; | |||
| $var(entity) = $fd; | |||
| } | |||
| # REALM check | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| if ($sht(rate_limits=>$var(lrpm_realm)) == -1 | |||
| || $sht(rate_limits=>$var(lrps_realm)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-realm log| Can't find HASHed rate for $var(entity) with $rm method"); | |||
| $var(with-realm-request) = "true"; | |||
| } | |||
| } | |||
| if ($sht(rate_limits=>$var(ltpm_realm)) == -1 | |||
| || $sht(rate_limits=>$var(ltps_realm)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-realm log| Can't find HASHed rate for $var(entity) with $rm method"); | |||
| $var(with-realm-total) = "true"; | |||
| } | |||
| if (not_empty("$fU")) { | |||
| if (is_method("BYE")) { | |||
| xlog("L_WARNING","$ci|RL-realm log| Fixup for BYE method with IP in from URI: use to-domain"); | |||
| $var(entity) = $fU+"@"+$td; | |||
| } else { | |||
| $var(entity) = $fU+"@"+$fd; | |||
| } | |||
| #DEVICE check | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| if ($sht(rate_limits=>$var(lrpm_device)) == -1 | |||
| || $sht(rate_limits=>$var(lrps_device)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-device log| Can't find HASHed rate for $var(entity) with $rm method"); | |||
| $var(with-device-request) = "true"; | |||
| } | |||
| } | |||
| if ($sht(rate_limits=>$var(ltpm_device)) == -1 || $sht(rate_limits=>$var(ltps_device)) == -1) { | |||
| xlog("L_INFO", "$ci|RL-device log| Can't find HASHed rate for $var(entity) with $rm method"); | |||
| $var(with-device-total) = "true"; | |||
| } | |||
| } | |||
| if ((is_method("INVITE") || is_method("REGISTER")) | |||
| && (($var(with-device-request) == "true" && $var(with-device-total) == "true") | |||
| || ($var(with-realm-request) == "true" && $var(with-realm-total) == "true"))) { | |||
| $var(method-key) = "Method-List"; | |||
| $var(method-value) = "[\"" + $rm + "\", \"TOTAL\"]"; | |||
| } | |||
| if ( $var(with-device-request) == "true" | |||
| || $var(with-device-total) == "true" | |||
| || $var(with-realm-request) == "true" | |||
| || $var(with-realm-total) == "true" ) { | |||
| avp_printf("$avp(s:query-request)", "{\"Entity\" : \"$var(entity)\", \"$var(method-key)\" : $var(method-value), \"Event-Category\" : \"rate_limit\", \"Event-Name\" : \"query\", \"With-Realm\" : $var(with-realm-request)}"); | |||
| xlog("L_INFO", "$ci|RL log| Query: $avp(s:query-request)"); | |||
| if (kazoo_query("frontier", "sbc_config", $avp(s:query-request), "$var(amqp_result)")) { | |||
| xlog("L_INFO", "$ci|RL log| Response: $var(amqp_result)"); | |||
| kazoo_json($var(amqp_result), "Realm.Minute." + $rm, "$var(realm-min)"); | |||
| kazoo_json($var(amqp_result), "Realm.Second." + $rm, "$var(realm-sec)"); | |||
| kazoo_json($var(amqp_result), "Realm.Minute.TOTAL", "$var(realm-min-total)"); | |||
| kazoo_json($var(amqp_result), "Realm.Second.TOTAL", "$var(realm-sec-total)"); | |||
| kazoo_json($var(amqp_result), "Device.Minute." + $rm, "$var(device-min)"); | |||
| kazoo_json($var(amqp_result), "Device.Second." + $rm, "$var(device-sec)"); | |||
| kazoo_json($var(amqp_result), "Device.Minute.TOTAL", "$var(device-min-total)"); | |||
| kazoo_json($var(amqp_result), "Device.Second.TOTAL", "$var(device-sec-total)"); | |||
| if ( not_empty("$var(realm-min)") ) { | |||
| $sht(rate_limits=>$var(lrpm_realm)) = $(var(realm-min){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(lrpm_realm)=$sht(rate_limits=>$var(lrpm_realm))"); | |||
| } | |||
| if ( not_empty("$var(realm-sec)") ) { | |||
| $sht(rate_limits=>$var(lrps_realm)) = $(var(realm-sec){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(lrps_realm)=$sht(rate_limits=>$var(lrps_realm))"); | |||
| } | |||
| if ( not_empty("$var(realm-min-total)") ) { | |||
| $sht(rate_limits=>$var(ltpm_realm)) = $(var(realm-min-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(ltpm_realm)=$sht(rate_limits=>$var(ltpm_realm))"); | |||
| } | |||
| if ( not_empty("$var(realm-sec-total)") ) { | |||
| $sht(rate_limits=>$var(ltps_realm)) = $(var(realm-sec-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-realm log| $rm DB=>HASH for $var(ltps_realm)=$sht(rate_limits=>$var(ltps_realm))"); | |||
| } | |||
| if ( not_empty("$var(device-min)") ) { | |||
| $sht(rate_limits=>$var(lrpm_device)) = $(var(device-min){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(lrpm_device)=$sht(rate_limits=>$var(lrpm_device))"); | |||
| } | |||
| if ( not_empty("$var(device-sec)") ) { | |||
| $sht(rate_limits=>$var(lrps_device)) = $(var(device-sec){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(lrps_device)=$sht(rate_limits=>$var(lrps_device))"); | |||
| } | |||
| if ( not_empty("$var(device-min-total)") ) { | |||
| $sht(rate_limits=>$var(ltpm_device)) = $(var(device-min-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(ltpm_device)=$sht(rate_limits=>$var(ltpm_device))"); | |||
| } | |||
| if ( not_empty("$var(device-sec-total)") ) { | |||
| $sht(rate_limits=>$var(ltps_device)) = $(var(device-sec-total){s.int}); | |||
| xlog("L_INFO", "$ci|RL-device log| $rm DB=>HASH for $var(ltps_device)=$sht(rate_limits=>$var(ltps_device))"); | |||
| } | |||
| } else { | |||
| xlog("L_ERROR", "$ci|RL log| $rm DB unreachable for entity: $var(entity)"); | |||
| return; | |||
| } | |||
| } | |||
| if (is_method("BYE")) { | |||
| xlog("L_WARNING","$ci|RL-device log| Fixup for BYE method with IP in from URI: use to-domain"); | |||
| $var(entity) = $td; | |||
| } else { | |||
| $var(entity) = $fd; | |||
| } | |||
| $var(entity-type) = "realm"; | |||
| if (is_method("INVITE") || is_method("REGISTER")) { | |||
| $var(lrpm) = $sht(rate_limits=>$var(lrpm_realm)); | |||
| $var(lrps) = $sht(rate_limits=>$var(lrps_realm)); | |||
| } | |||
| $var(ltpm) = $sht(rate_limits=>$var(ltpm_realm)); | |||
| $var(ltps) = $sht(rate_limits=>$var(ltps_realm)); | |||
| route(DO_DOS_PREVENTION); | |||
| if ( not_empty("$fU") ) { | |||
| if (is_method("BYE")) { | |||
| $var(entity) = $fU+"@"+$td; | |||
| xlog("L_WARNING","$ci|RL-device log| Fixup for BYE method with IP in from URI: use to-domain"); | |||
| } else { | |||
| $var(entity) = $fU+"@"+$fd; | |||
| } | |||
| $var(entity-type) = "device"; | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| $var(lrpm) = $sht(rate_limits=>$var(lrpm_device)); | |||
| $var(lrps) = $sht(rate_limits=>$var(lrps_device)); | |||
| } | |||
| $var(ltpm) = $sht(rate_limits=>$var(ltpm_device)); | |||
| $var(ltps) = $sht(rate_limits=>$var(ltps_device)); | |||
| route(DO_DOS_PREVENTION); | |||
| } | |||
| } | |||
| # This route do counting and decide either to ACCEPT or DECLINE packet | |||
| route[DO_DOS_PREVENTION] { | |||
| # Personal counters for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| $var(rpm) = $var(entity)+":"+$rm+":min:"+$timef(%Y/%m/%d_%H_%M_00); | |||
| $var(rps) = $var(entity)+":"+$rm+":sec:"+$timef(%Y/%m/%d_%H_%M_%S); | |||
| } | |||
| # Commmon counters for ALL packet including INVITE and REGISTER | |||
| $var(tpm) = $var(entity)+":TOTAL:min:"+$timef(%Y/%m/%d_%H_%M_00); | |||
| $var(tps) = $var(entity)+":TOTAL:sec:"+$timef(%Y/%m/%d_%H_%M_%S); | |||
| # Personal debug for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(rpm) = $var(lrpm)/$sht(rpm=>$var(rpm))"); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(rps) = $var(lrps)/$sht(rps=>$var(rps))"); | |||
| } | |||
| # Commmon debug for ALL packet including INVITE and REGISTER | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(tpm) = $var(ltpm)/$sht(tpm=>$var(tpm))"); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| L/C for $var(tps) = $var(ltps)/$sht(tps=>$var(tps))"); | |||
| # Personal increment for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| $sht(rpm=>$var(rpm)) = $shtinc(rpm=>$var(rpm)); | |||
| $sht(rps=>$var(rps)) = $shtinc(rps=>$var(rps)); | |||
| } | |||
| # Commmon increment for ALL packet including INVITE and REGISTER | |||
| $sht(tpm=>$var(tpm)) = $shtinc(tpm=>$var(tpm)); | |||
| $sht(tps=>$var(tps)) = $shtinc(tps=>$var(tps)); | |||
| # Personal checks for INVITE and REGISTER | |||
| if ((is_method("INVITE") || is_method("REGISTER"))) { | |||
| if ($sht(rps=>$var(rps)) > $var(lrps)) { | |||
| sl_send_reply("603", RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of $rm $var(rps) rate limits: $sht(rps=>$var(rps)) > $var(lrps))"); | |||
| exit; | |||
| } | |||
| if ($sht(rpm=>$var(rpm)) > $var(lrpm)) { | |||
| sl_send_reply("603", RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of $rm $var(rpm) rate limits: $sht(rpm=>$var(rpm)) > $var(lrpm))"); | |||
| exit; | |||
| } | |||
| } | |||
| # Commmon checks for ALL packet including INVITE and REGISTER | |||
| if ($sht(tps=>$var(tps)) > $var(ltps)) { | |||
| # if (isflagset(FLAG_IS_REPLY)) { | |||
| # xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm::$rs $rr) $var(tps) rate limits: $sht(tps=>$var(tps)) > $var(ltps))"); | |||
| # } else { | |||
| sl_send_reply("603", RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm) $var(tps) rate limits: $sht(tps=>$var(tps)) > $var(ltps))"); | |||
| # } | |||
| exit; | |||
| } | |||
| if ($sht(tpm=>$var(tpm)) > $var(ltpm)) { | |||
| # if (isflagset(FLAG_IS_REPLY)) { | |||
| # xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm::$rs $rr) $var(tpm) rate limits: $sht(tpm=>$var(tpm)) > $var(ltpm))"); | |||
| # } else { | |||
| sl_send_reply("603", RATE_LIMIT_MESSAGE); | |||
| xlog("L_INFO", "$ci|RL-$var(entity-type) log| Out of TOTAL($rm) $var(tpm) rate limits: $sht(tpm=>$var(tpm)) > $var(ltpm))"); | |||
| # } | |||
| exit; | |||
| } | |||
| } | |||