From 611b6645b2a9fc78d35f621da93385abe2f47167 Mon Sep 17 00:00:00 2001 From: fengweihao Date: Thu, 4 Jun 2020 20:22:31 +0800 Subject: [PATCH] =?UTF-8?q?TSG-1818=20insert=E5=92=8Chijack=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=B3=A8=E5=85=A5=E6=B5=81=E9=87=8F=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/pangu/pangu_pxy.conf | 6 ++ plugin/business/pangu-http/src/pangu_http.cpp | 62 +++++++++++++++++- vendor/CMakeLists.txt | 16 +++++ vendor/ratelimiter-1.1.0-x86_64.tar.gz | Bin 0 -> 18897 bytes 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 vendor/ratelimiter-1.1.0-x86_64.tar.gz diff --git a/conf/pangu/pangu_pxy.conf b/conf/pangu/pangu_pxy.conf index 6f6d8a3..49f2c50 100644 --- a/conf/pangu/pangu_pxy.conf +++ b/conf/pangu/pangu_pxy.conf @@ -50,6 +50,12 @@ log_fsstat_trig=1 log_fsstat_dst_ip=10.4.20.202 log_fsstat_dst_port=8125 +[ratelimit] +enable=0 +token_name=ratelimit +redis_server=192.168.40.137 +redis_port=6379 + [maat] # 0:json 1: redis 2: iris maat_input_mode=1 diff --git a/plugin/business/pangu-http/src/pangu_http.cpp b/plugin/business/pangu-http/src/pangu_http.cpp index c75346a..4197fb7 100644 --- a/plugin/business/pangu-http/src/pangu_http.cpp +++ b/plugin/business/pangu-http/src/pangu_http.cpp @@ -25,6 +25,7 @@ #include #include +#include "ratelimiter.h" #define MAX_SCAN_RESULT 16 #define MAX_EDIT_ZONE_NUM 64 @@ -146,6 +147,9 @@ struct pangu_rt struct event_base* gc_evbase; struct event* gcev; + Ratelimiter_handle_t ratelimiter; + int enable_rate; + int ctrl_compile_idx; int ca_store_reseting; @@ -981,6 +985,40 @@ error_out: return ret; } +Ratelimiter_handle_t ratelimit_handle_create(const char* profile_path, const char* static_section) +{ + int ret=0; + int redis_port = 0, interval_sec = 0; + char redis_server[TFE_STRING_MAX] = {0}; + char token_name[TFE_STRING_MAX] = {0}; + + Ratelimiter_handle_t ratelimit = NULL; + + MESA_load_profile_int_def(profile_path, static_section, "enable", &(g_pangu_rt->enable_rate), 0); + MESA_load_profile_int_def(profile_path, static_section, "redis_port", &(redis_port), 6379); + MESA_load_profile_string_def(profile_path, static_section, "redis_server", redis_server, sizeof(redis_server), ""); + MESA_load_profile_string_def(profile_path, static_section, "token_name", token_name, sizeof(token_name), ""); + + MESA_load_profile_int_def(profile_path, static_section, "interval_sec", &(interval_sec), 1); + + ratelimit=Ratelimiter_create(token_name, g_pangu_rt->local_logger); + + Ratelimiter_set_opt(ratelimit, RATELIMITER_OPT_INTERVAL_SEC, &interval_sec, sizeof(interval_sec)); + Ratelimiter_set_opt(ratelimit, RATELIMITER_OPT_REDIS_IP, redis_server, strlen(redis_server) + 1); + Ratelimiter_set_opt(ratelimit, RATELIMITER_OPT_REDIS_PORT, &redis_port, sizeof(redis_port)); + + ret = Ratelimiter_start(ratelimit); + if (ret < 0) + { + TFE_LOG_ERROR(g_pangu_rt->local_logger, "%s Ratelimiter init failed.", __FUNCTION__); + goto error_out; + } + return ratelimit; +error_out: + Ratelimiter_stop(ratelimit); + return NULL; +} + int pangu_http_init(struct tfe_proxy * proxy) { const char * profile_path = "./conf/pangu/pangu_pxy.conf"; @@ -1002,8 +1040,9 @@ int pangu_http_init(struct tfe_proxy * proxy) } g_pangu_rt->fs_handle = tfe_proxy_get_fs_handle(); - pangu_http_stat_init(g_pangu_rt); + g_pangu_rt->ratelimiter=ratelimit_handle_create(profile_path, "ratelimit"); + pangu_http_stat_init(g_pangu_rt); if(pangu_policy_init(profile_path, "MAAT", "DYNAMIC_MAAT")<0) { goto error_out; @@ -1230,6 +1269,15 @@ static struct manipulate_profile* get_profile_by_id(int profile_table, int profi return result; } +static unsigned long long try_send_by_token(int inject_sz) +{ + if (g_pangu_rt->enable_rate != 1) + { + return 1; + } + return Ratelimiter_customer_factory(g_pangu_rt->ratelimiter, inject_sz); +} + static int pangu_action_weight[__PG_ACTION_MAX] = {0}; void __pangu_action_weight_init() __attribute__((constructor, used)); void __pangu_action_weight_init() @@ -1770,6 +1818,12 @@ static void http_hijack(const struct tfe_http_session * session, enum tfe_http_e ctx->action = PG_ACTION_NONE; return; } + if (try_send_by_token(hijack_size) <= 0) + { + TFE_LOG_ERROR(g_pangu_rt->local_logger, "No token is available to send data, profile_id = %d", param->profile_id); + ctx->action = PG_ACTION_NONE; + return; + } ctx->inject_sz = hijack_size; char cont_len_str[16]; @@ -1869,6 +1923,12 @@ static void http_insert(const struct tfe_stream * stream, const struct tfe_http_ ctx->action = PG_ACTION_NONE; return; } + if (try_send_by_token(ins_ctx->rule->inject_sz) <= 0) + { + TFE_LOG_ERROR(g_pangu_rt->local_logger, "No token is available to send data, profile_id = %d", param->profile_id); + ctx->action = PG_ACTION_NONE; + return; + } ctx->inject_sz = ins_ctx->rule->inject_sz; } else diff --git a/vendor/CMakeLists.txt b/vendor/CMakeLists.txt index 6cc452c..dd2c413 100644 --- a/vendor/CMakeLists.txt +++ b/vendor/CMakeLists.txt @@ -94,6 +94,22 @@ add_dependencies(nghttp2-static nghttp2) set_property(TARGET nghttp2-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libnghttp2.a) set_property(TARGET nghttp2-static APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include;${INSTALL_DIR}/src/nghttp2/lib) +### ratelimiter +ExternalProject_Add(ratelimiter PREFIX ratelimiter + URL ${CMAKE_CURRENT_SOURCE_DIR}/ratelimiter-1.1.0-x86_64.tar.gz + URL_MD5 54cc36713c2aaa8ff378ca342778e57d + CONFIGURE_COMMAND "" + BUILD_COMMAND make + BUILD_IN_SOURCE 1) + +ExternalProject_Get_Property(ratelimiter INSTALL_DIR) +file(MAKE_DIRECTORY ${INSTALL_DIR}/include) + +add_library(ratelimiter-static STATIC IMPORTED GLOBAL) +add_dependencies(ratelimiter-static ratelimiter) +set_property(TARGET ratelimiter-static PROPERTY IMPORTED_LOCATION ${INSTALL_DIR}/lib/libratelimiter.a) +set_property(TARGET ratelimiter-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INSTALL_DIR}/include) + #### GoogleTest ExternalProject_Add(googletest PREFIX googletest URL ${CMAKE_CURRENT_SOURCE_DIR}/googletest-release-1.8.0.tar.gz diff --git a/vendor/ratelimiter-1.1.0-x86_64.tar.gz b/vendor/ratelimiter-1.1.0-x86_64.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..14356503221b1460e211bf2aedb7110fca5765f6 GIT binary patch literal 18897 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 literal 0 HcmV?d00001