lists.osmocom.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

gerrit-log

Thread Start a new thread
Download
Threads by month
  • ----- 2026 -----
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2025 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
gerrit-log@lists.osmocom.org

July 2025

  • 1 participants
  • 1492 discussions
[S] Change in ...osmo-s1gw[master]: enft_kpi: flush the table on init
by fixeria 21 Jul '25

21 Jul '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40532?usp=email ) ( 2 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: enft_kpi: flush the table on init ...................................................................... enft_kpi: flush the table on init Older versions of nftables, including the 1.0.6 provided by Osmocom, do not support setting the "owner" flag when creating a table via JSON. Ensure that we start with a clean state by deleting the table on init. Change-Id: I96bf4f7b6d5c9104fad0d6f98eda56e7a4e4fa7d Related: SYS#7307 --- M src/enft_kpi.erl 1 file changed, 30 insertions(+), 15 deletions(-) Approvals: Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve laforge: Looks good to me, approved diff --git a/src/enft_kpi.erl b/src/enft_kpi.erl index cc2107a..f8cf126 100644 --- a/src/enft_kpi.erl +++ b/src/enft_kpi.erl @@ -149,20 +149,11 @@ process_flag(trap_exit, true), TName = maps:get(table_name, Cfg, "osmo-s1gw"), Interval = maps:get(interval, Cfg, 3000), - %% ignore (accept) anything but GTPU @ udp/2152 - R1 = [enftables:nft_expr_match_ip_proto("udp", ?OP_NEQ), - enftables:nft_expr_accept()], - R2 = [enftables:nft_expr_match_udp_dport(?GTPU_PORT, ?OP_NEQ), - enftables:nft_expr_accept()], - Cmds = [enftables:nft_cmd_add_table(TName, [<< "owner" >>]), - nft_cmd_add_chain(TName, "gtpu-ul", "prerouting"), - nft_cmd_add_chain(TName, "gtpu-dl", "postrouting"), - enftables:nft_cmd_add_rule(TName, "gtpu-ul", R1), - enftables:nft_cmd_add_rule(TName, "gtpu-dl", R1), - enftables:nft_cmd_add_rule(TName, "gtpu-ul", R2), - enftables:nft_cmd_add_rule(TName, "gtpu-dl", R2) - ], - case nft_exec(Cmds) of + %% flush the table, in case it remained + %% it may not exist, so we ignore the result + nft_flush_table(TName), + %% create and initialize the table + case nft_init_table(TName) of ok -> ?LOG_INFO("NFT table ~p has been initialized", [TName]), spawn_link(fun() -> heartbeat(Interval) end), @@ -317,7 +308,7 @@ ?LOG_NOTICE("Terminating, reason ~p", [Reason]), case Cfg of #{enable := true, table_name := TName} -> - nft_exec([enftables:nft_cmd_del_table(TName)]), %% delete the table + nft_flush_table(TName), ok; _ -> ok %% stub mode end. @@ -518,6 +509,30 @@ end. +-spec nft_flush_table(string()) -> enftables:result(). +nft_flush_table(TName) -> + Cmds = [enftables:nft_cmd_del_table(TName)], + nft_exec(Cmds). + + +-spec nft_init_table(string()) -> enftables:result(). +nft_init_table(TName) -> + %% ignore (accept) anything but GTPU @ udp/2152 + R1 = [enftables:nft_expr_match_ip_proto("udp", ?OP_NEQ), + enftables:nft_expr_accept()], + R2 = [enftables:nft_expr_match_udp_dport(?GTPU_PORT, ?OP_NEQ), + enftables:nft_expr_accept()], + Cmds = [enftables:nft_cmd_add_table(TName, [<< "owner" >>]), + nft_cmd_add_chain(TName, "gtpu-ul", "prerouting"), + nft_cmd_add_chain(TName, "gtpu-dl", "postrouting"), + enftables:nft_cmd_add_rule(TName, "gtpu-ul", R1), + enftables:nft_cmd_add_rule(TName, "gtpu-dl", R1), + enftables:nft_cmd_add_rule(TName, "gtpu-ul", R2), + enftables:nft_cmd_add_rule(TName, "gtpu-dl", R2) + ], + nft_exec(Cmds). + + -spec nft_exec(Cmds) -> enftables:result() when Cmds :: [enftables:nft_cmd()]. nft_exec(Cmds) -> -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40532?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: merged Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I96bf4f7b6d5c9104fad0d6f98eda56e7a4e4fa7d Gerrit-Change-Number: 40532 Gerrit-PatchSet: 3 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[L] Change in ...osmo-s1gw[master]: enft_kpi: support different UL/DL GTP-U addresses
by fixeria 21 Jul '25

21 Jul '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40437?usp=email ) ( 4 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: enft_kpi: support different UL/DL GTP-U addresses ...................................................................... enft_kpi: support different UL/DL GTP-U addresses Change-Id: Icd4a17790062bfcaf2bccb01fa94dcdb65c0872c Related: SYS#7307 --- M src/enft_kpi.erl M src/s1ap_proxy.erl 2 files changed, 156 insertions(+), 113 deletions(-) Approvals: pespin: Looks good to me, but someone else must approve laforge: Looks good to me, approved Jenkins Builder: Verified diff --git a/src/enft_kpi.erl b/src/enft_kpi.erl index 015956c..3c9bdc1 100644 --- a/src/enft_kpi.erl +++ b/src/enft_kpi.erl @@ -72,14 +72,22 @@ -type counters() :: dict:dict(K :: string(), V :: counter()). +-type uldl() :: ul | dl. + +-type uldl_addr() :: {ULDL :: uldl(), + Addr :: string()}. + +-type enb_uldl_state() :: #{uldl => uldl(), + addr => string(), + handle => integer(), + ctr => counter() + }. + -type enb_state() :: #{pid => pid(), - addr => string(), genb_id => string(), mon_ref => reference(), - handle_ul => integer(), - handle_dl => integer(), - ctr_ul => counter(), %% UL counters - ctr_dl => counter() %% DL counters + ul => enb_uldl_state(), %% Uplink data + dl => enb_uldl_state() %% Downlink data }. -type registry() :: dict:dict(K :: pid(), @@ -109,10 +117,11 @@ gen_server:call(?MODULE, {?FUNCTION_NAME, GlobalENBId}). --spec enb_set_addr(Addr) -> ok | {error, term()} - when Addr :: string(). -enb_set_addr(Addr) -> - gen_server:call(?MODULE, {?FUNCTION_NAME, Addr}). +-spec enb_set_addr(ULDLAddr) -> ok | {error, term()} + when ULDLAddr :: uldl_addr(). +enb_set_addr(ULDLAddr) -> + %% TODO: use cast here to avoid blocking + gen_server:call(?MODULE, {?FUNCTION_NAME, ULDLAddr}). -spec enb_unregister() -> ok | {error, term()}. @@ -194,15 +203,30 @@ {reply, ok, S#state{registry = R1}} end; -handle_call({enb_set_addr, Addr}, {Pid, _Ref}, +handle_call({enb_set_addr, {ULDL, Addr}}, {Pid, _Ref}, #state{registry = R} = S0) -> case dict:find(Pid, R) of - {ok, EnbState} -> - {Reply, S1} = enb_set_addr(Addr, EnbState, S0), + %% the given UL/DL Addr is already known + {ok, #{genb_id := GlobalENBId, + ULDL := #{addr := Addr}}} -> + ?LOG_DEBUG("eNB (pid ~p, ~p): ~p address ~p is already known", + [Pid, GlobalENBId, ULDL, Addr]), + {reply, ok, S0}; + %% the given UL/DL Addr differs from stored Addr + {ok, #{genb_id := GlobalENBId, + ULDL := #{addr := OldAddr}}} -> + ?LOG_ERROR("eNB (pid ~p, ~p): ~p address ~p -> ~p change?!?", + [Pid, GlobalENBId, ULDL, OldAddr, Addr]), + {reply, {error, addr_mismatch}, S0}; + %% UL/DL state is missing => create it + {ok, #{genb_id := GlobalENBId} = ES} -> + ?LOG_DEBUG("eNB (pid ~p, ~p): ~p address ~p indicated, " + "creating NFT counters and rules", + [Pid, GlobalENBId, ULDL, Addr]), + {Reply, S1} = enb_set_addr({ULDL, Addr}, ES, S0), {reply, Reply, S1}; error -> - ?LOG_ERROR("eNB @ ~p (pid ~p) is *not* registered", - [Addr, Pid]), + ?LOG_ERROR("eNB (pid ~p) is *not* registered", [Pid]), {reply, {error, not_registered}, S0} end; @@ -303,107 +327,108 @@ enb_add_metrics(GlobalENBId, ul), enb_add_metrics(GlobalENBId, dl). -enb_add_metrics(GlobalENBId, UDL) -> +enb_add_metrics(GlobalENBId, ULDL) -> %% counters may already exist, so catch exceptions here - catch exometer:new(?S1GW_CTR_GTPU_PACKETS(GlobalENBId, UDL), counter), - catch exometer:new(?S1GW_CTR_GTPU_BYTES_UE(GlobalENBId, UDL), counter), - catch exometer:new(?S1GW_CTR_GTPU_BYTES_TOTAL(GlobalENBId, UDL), counter). + catch exometer:new(?S1GW_CTR_GTPU_PACKETS(GlobalENBId, ULDL), counter), + catch exometer:new(?S1GW_CTR_GTPU_BYTES_UE(GlobalENBId, ULDL), counter), + catch exometer:new(?S1GW_CTR_GTPU_BYTES_TOTAL(GlobalENBId, ULDL), counter). --spec enb_set_addr(Addr, ES, S0) -> {Reply, S1} - when Addr :: string(), +-spec enb_set_addr(ULDLAddr, ES, S0) -> {Reply, S1} + when ULDLAddr :: uldl_addr(), ES :: enb_state(), S0 :: #state{}, S1 :: #state{}, Reply :: ok | {error, term()}. -enb_set_addr(Addr, %% given Addr matches stored Addr +enb_set_addr({ULDL, Addr}, #{genb_id := GlobalENBId, - addr := Addr, - pid := Pid}, S) -> - ?LOG_DEBUG("eNB @ ~p (pid ~p, ~p): address is already known", - [Addr, Pid, GlobalENBId]), - {ok, S}; - -enb_set_addr(NewAddr, %% given NewAddr differs from stored Addr - #{genb_id := GlobalENBId, - addr := Addr, - pid := Pid}, S) -> - ?LOG_ERROR("eNB @ ~p (pid ~p, ~p): address (~p) mismatch?!?", - [Addr, Pid, GlobalENBId, NewAddr]), - {{error, addr_mismatch}, S}; - -enb_set_addr(Addr, #{genb_id := GlobalENBId, - pid := Pid} = ES0, + pid := Pid} = ES0, #state{cfg = Cfg, registry = R0} = S) -> - ?LOG_DEBUG("eNB @ ~p (pid ~p, ~p): " - "address indicated, creating NFT rules/counters", - [Addr, Pid, GlobalENBId]), - case enb_add_nft_counters(ES0#{addr => Addr}, Cfg) of - {ok, ES1} -> + case enb_add_nft_counter({ULDL, Addr}, GlobalENBId, Cfg) of + {ok, Handle} -> + ?LOG_INFO("eNB (pid ~p, ~p): NFT rules/counters created for ~p", + [Pid, GlobalENBId, ULDL]), + %% store an updated eNB state to the registry + ES1 = ES0#{ULDL => #{uldl => ULDL, + addr => Addr, + handle => Handle, + ctr => #ctr{}}}, R1 = dict:store(Pid, ES1, R0), - ?LOG_INFO("eNB @ ~p (pid ~p, ~p): " - "NFT rules/counters created (KPI ready)", - [Addr, Pid, GlobalENBId]), {ok, S#state{registry = R1}}; {error, Error} -> - ?LOG_ERROR("eNB @ ~p (pid ~p, ~p): " - "creating NFT rules/counters failed: ~p", - [Addr, Pid, GlobalENBId, Error]), + ?LOG_ERROR("eNB (pid ~p, ~p): creating NFT rules/counters failed: ~p", + [Pid, GlobalENBId, Error]), {{error, Error}, S} end. --spec enb_add_nft_counters(ES0, Cfg) -> {ok, ES1} | {error, term()} - when ES0 :: enb_state(), - ES1 :: enb_state(), - Cfg :: cfg(). -enb_add_nft_counters(#{addr := Addr, - genb_id := GlobalENBId} = ES0, - #{table_name := TName}) -> - RUL = [nft_expr_match_ip_saddr(Addr, ?OP_EQ), - nft_expr_counter("ul-" ++ GlobalENBId)], - RDL = [nft_expr_match_ip_daddr(Addr, ?OP_EQ), - nft_expr_counter("dl-" ++ GlobalENBId)], - Cmds = [nft_cmd_add_counter(TName, "ul-" ++ GlobalENBId), - nft_cmd_add_counter(TName, "dl-" ++ GlobalENBId), - nft_cmd_add_rule(TName, "gtpu-ul", RUL), - nft_cmd_add_rule(TName, "gtpu-dl", RDL) +enb_nft_counter_name(ul, GlobalENBId) -> "ul-" ++ GlobalENBId; +enb_nft_counter_name(dl, GlobalENBId) -> "dl-" ++ GlobalENBId. + +enb_nft_chain_name(ul) -> "gtpu-ul"; +enb_nft_chain_name(dl) -> "gtpu-dl". + + +-spec enb_add_nft_counter(ULDLAddr, GlobalENBId, Cfg) -> {ok, Handle} | {error, term()} + when ULDLAddr :: uldl_addr(), + GlobalENBId :: string(), + Cfg :: cfg(), + Handle :: integer(). +enb_add_nft_counter({ULDL, Addr}, GlobalENBId, + #{table_name := TName}) -> + CtrName = enb_nft_counter_name(ULDL, GlobalENBId), + CName = enb_nft_chain_name(ULDL), + Rule = [nft_expr_match_ip_addr({ULDL, Addr}), + nft_expr_counter(CtrName)], + Cmds = [nft_cmd_add_counter(TName, CtrName), + nft_cmd_add_rule(TName, CName, Rule) ], case nft_exec(Cmds) of ok -> - ULH = nft_chain_last_handle(TName, "gtpu-ul"), - DLH = nft_chain_last_handle(TName, "gtpu-dl"), - %% update the eNB state with new info - ES1 = ES0#{handle_ul => ULH, - handle_dl => DLH, - ctr_ul => #ctr{}, - ctr_dl => #ctr{}}, - {ok, ES1}; + Handle = nft_chain_last_handle(TName, CName), + {ok, Handle}; {error, Error} -> ?LOG_ERROR("~p() failed: ~p", [?FUNCTION_NAME, Error]), {error, Error} end. --spec enb_del_nft_counters(enb_state(), cfg()) -> ok | {error, term()}. -enb_del_nft_counters(#{genb_id := GlobalENBId, - handle_ul := ULH, - handle_dl := DLH}, - #{table_name := TName}) -> - Cmds = [nft_cmd_del_rule(TName, "gtpu-ul", ULH), - nft_cmd_del_rule(TName, "gtpu-dl", DLH), - nft_cmd_del_counter(TName, "ul-" ++ GlobalENBId), - nft_cmd_del_counter(TName, "dl-" ++ GlobalENBId) +-spec enb_del_nft_counters(ES0, Cfg) -> ES1 + when Cfg :: cfg(), + ES0 :: enb_state(), + ES1 :: enb_state(). +enb_del_nft_counters(#{ul := ULS} = ES, Cfg) -> + enb_del_nft_counter(ULS, ES, Cfg), + enb_del_nft_counters(maps:remove(ul, ES), Cfg); + +enb_del_nft_counters(#{dl := DLS} = ES, Cfg) -> + enb_del_nft_counter(DLS, ES, Cfg), + enb_del_nft_counters(maps:remove(dl, ES), Cfg); + +%% no UL/DL state => nothing to delete +enb_del_nft_counters(ES, _Cfg) -> ES. + + +-spec enb_del_nft_counter(S, ES, Cfg) -> ok | {error, term()} + when S :: enb_uldl_state(), + ES :: enb_state(), + Cfg :: cfg(). +enb_del_nft_counter(#{uldl := ULDL, handle := Handle}, + #{genb_id := GlobalENBId, pid := Pid}, + #{table_name := TName}) -> + ?LOG_DEBUG("eNB (pid ~p, ~p): deleting NFT counter for ~p", + [Pid, GlobalENBId, ULDL]), + CtrName = enb_nft_counter_name(ULDL, GlobalENBId), + CName = enb_nft_chain_name(ULDL), + Cmds = [nft_cmd_del_rule(TName, CName, Handle), + nft_cmd_del_counter(TName, CtrName) ], case nft_exec(Cmds) of ok -> ok; {error, Error} -> ?LOG_ERROR("~p() failed: ~p", [?FUNCTION_NAME, Error]), {error, Error} - end; - -%% missing ULH/DLH => nothing to delete -enb_del_nft_counters(_ES, _Cfg) -> ok. + end. %% Parse the given list of NFT counters (result of nft_cmd_list_counters()). @@ -437,51 +462,59 @@ R1 :: registry(). %% (new) registry of eNBs report_nft_counters(Ctrs, R0) -> %% for each registered eNB, look-up and report UL/DL counters - dict:map(fun(_Pid, EnbState) -> enb_report_nft_counters(Ctrs, EnbState) end, R0). + dict:map(fun(_Pid, ES) -> enb_report_nft_counters(Ctrs, ES) end, R0). -spec enb_report_nft_counters(counters(), enb_state()) -> enb_state(). -enb_report_nft_counters(Ctrs, #{genb_id := GlobalENBId, - ctr_ul := ULC, - ctr_dl := DLC} = ES) -> - ?LOG_DEBUG("Reporting NFT counters for eNB ~p", [GlobalENBId]), - ES#{ctr_ul => report_nft_counter(Ctrs, {ul, GlobalENBId}, ULC), - ctr_dl => report_nft_counter(Ctrs, {dl, GlobalENBId}, DLC)}; +enb_report_nft_counters(Ctrs, #{ul := ULS, + dl := DLS} = ES) -> + %% report both UL and DL counters + ES#{ul => enb_report_nft_counters(Ctrs, ES, ULS), + dl => enb_report_nft_counters(Ctrs, ES, DLS)}; -%% missing ULH/DLH => nothing to report +enb_report_nft_counters(Ctrs, #{ul := ULS} = ES) -> + %% report UL counters only + ES#{ul => enb_report_nft_counters(Ctrs, ES, ULS)}; + +enb_report_nft_counters(Ctrs, #{dl := DLS} = ES) -> + %% report DL counters only + ES#{dl => enb_report_nft_counters(Ctrs, ES, DLS)}; + +%% no UL/DL state => nothing to report enb_report_nft_counters(_Ctrs, ES) -> ES. --spec report_nft_counter(Ctrs, {UDL, GlobalENBId}, C0) -> C1 +-spec enb_report_nft_counters(Ctrs, ES, S0) -> S1 when Ctrs :: counters(), - UDL :: ul | dl, - GlobalENBId :: string(), - C0 :: counter(), - C1 :: counter(). -report_nft_counter(Ctrs, {UDL, GlobalENBId}, C0) -> - CtrName = atom_to_list(UDL) ++ "-" ++ GlobalENBId, + ES :: enb_state(), + S0 :: enb_uldl_state(), + S1 :: enb_uldl_state(). +enb_report_nft_counters(Ctrs, + #{genb_id := GlobalENBId, pid := Pid}, + #{uldl := ULDL, ctr := C0} = S) -> + CtrName = enb_nft_counter_name(ULDL, GlobalENBId), case dict:find(CtrName, Ctrs) of {ok, C0} -> %% no diff, nothing to report - ?LOG_DEBUG("NFT counters (~p) for eNB ~p: ~p", - [UDL, GlobalENBId, C0]), - C0; + ?LOG_DEBUG("NFT counters (~p) for eNB (pid ~p, ~p): ~p", + [ULDL, Pid, GlobalENBId, C0]), + S; {ok, C1} -> %% XXX: assuming C1 (new) values >= C0 (cached) values - ?LOG_DEBUG("NFT counters (~p) for eNB ~p: ~p -> ~p", - [UDL, GlobalENBId, C0, C1]), - s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_PACKETS(GlobalENBId, UDL), + ?LOG_DEBUG("NFT counters (~p) for eNB (pid ~p, ~p): ~p -> ~p", + [ULDL, Pid, GlobalENBId, C0, C1]), + s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_PACKETS(GlobalENBId, ULDL), C1#ctr.packets - C0#ctr.packets), - s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_BYTES_UE(GlobalENBId, UDL), + s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_BYTES_UE(GlobalENBId, ULDL), C1#ctr.bytes_ue - C0#ctr.bytes_ue), - s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_BYTES_TOTAL(GlobalENBId, UDL), + s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_BYTES_TOTAL(GlobalENBId, ULDL), C1#ctr.bytes_total - C0#ctr.bytes_total), - C1; + S#{ctr => C1}; error -> %% no counters for this eNB (yet?) - ?LOG_DEBUG("NFT counters (~p) for eNB ~p: nope", - [UDL, GlobalENBId]), - C0 + ?LOG_DEBUG("NFT counters (~p) for eNB (pid ~p, ~p): nope", + [ULDL, Pid, GlobalENBId]), + S end. @@ -591,6 +624,14 @@ nft_expr_match_payload({"udp", "dport"}, Port, Op). +-spec nft_expr_match_ip_addr(uldl_addr()) -> map(). +nft_expr_match_ip_addr({ul, Addr}) -> + nft_expr_match_ip_saddr(Addr, ?OP_EQ); + +nft_expr_match_ip_addr({dl, Addr}) -> + nft_expr_match_ip_daddr(Addr, ?OP_EQ). + + nft_expr_accept() -> #{accept => null}. diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index 6b75945..d2c1b0a 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -691,7 +691,8 @@ 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> %% indicate eNB's address to the enft_kpi module - enft_kpi:enb_set_addr(tla_str(TLA_In)), + enft_kpi:enb_set_addr({ul, tla_str(TLA_In)}), + enft_kpi:enb_set_addr({dl, tla_str(TLA_In)}), %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> @@ -980,7 +981,8 @@ 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> %% indicate eNB's address to the enft_kpi module - enft_kpi:enb_set_addr(tla_str(TLA_In)), + enft_kpi:enb_set_addr({ul, tla_str(TLA_In)}), + enft_kpi:enb_set_addr({dl, tla_str(TLA_In)}), %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40437?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: merged Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: Icd4a17790062bfcaf2bccb01fa94dcdb65c0872c Gerrit-Change-Number: 40437 Gerrit-PatchSet: 5 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[M] Change in ...osmo-s1gw[master]: enft_kpi: make enb_set_addr/1 non-blocking
by fixeria 21 Jul '25

21 Jul '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40438?usp=email ) ( 5 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: enft_kpi: make enb_set_addr/1 non-blocking ...................................................................... enft_kpi: make enb_set_addr/1 non-blocking This function is called each time a new E-RAB is established, while the result, be it success or failure, does not matter to s1ap_proxy. Use gen_server:cast/2 instead of gen_server:call/2 to avoid blocking. Change-Id: Iacd380f66fc0249a6a645293abd5aac9cdba34d5 Related: SYS#7307 --- M src/enft_kpi.erl 1 file changed, 29 insertions(+), 30 deletions(-) Approvals: pespin: Looks good to me, but someone else must approve laforge: Looks good to me, approved Jenkins Builder: Verified diff --git a/src/enft_kpi.erl b/src/enft_kpi.erl index 3c9bdc1..901842b 100644 --- a/src/enft_kpi.erl +++ b/src/enft_kpi.erl @@ -117,11 +117,10 @@ gen_server:call(?MODULE, {?FUNCTION_NAME, GlobalENBId}). --spec enb_set_addr(ULDLAddr) -> ok | {error, term()} +-spec enb_set_addr(ULDLAddr) -> ok when ULDLAddr :: uldl_addr(). enb_set_addr(ULDLAddr) -> - %% TODO: use cast here to avoid blocking - gen_server:call(?MODULE, {?FUNCTION_NAME, ULDLAddr}). + gen_server:cast(?MODULE, {?FUNCTION_NAME, self(), ULDLAddr}). -spec enb_unregister() -> ok | {error, term()}. @@ -203,33 +202,6 @@ {reply, ok, S#state{registry = R1}} end; -handle_call({enb_set_addr, {ULDL, Addr}}, {Pid, _Ref}, - #state{registry = R} = S0) -> - case dict:find(Pid, R) of - %% the given UL/DL Addr is already known - {ok, #{genb_id := GlobalENBId, - ULDL := #{addr := Addr}}} -> - ?LOG_DEBUG("eNB (pid ~p, ~p): ~p address ~p is already known", - [Pid, GlobalENBId, ULDL, Addr]), - {reply, ok, S0}; - %% the given UL/DL Addr differs from stored Addr - {ok, #{genb_id := GlobalENBId, - ULDL := #{addr := OldAddr}}} -> - ?LOG_ERROR("eNB (pid ~p, ~p): ~p address ~p -> ~p change?!?", - [Pid, GlobalENBId, ULDL, OldAddr, Addr]), - {reply, {error, addr_mismatch}, S0}; - %% UL/DL state is missing => create it - {ok, #{genb_id := GlobalENBId} = ES} -> - ?LOG_DEBUG("eNB (pid ~p, ~p): ~p address ~p indicated, " - "creating NFT counters and rules", - [Pid, GlobalENBId, ULDL, Addr]), - {Reply, S1} = enb_set_addr({ULDL, Addr}, ES, S0), - {reply, Reply, S1}; - error -> - ?LOG_ERROR("eNB (pid ~p) is *not* registered", [Pid]), - {reply, {error, not_registered}, S0} - end; - handle_call(enb_unregister, {Pid, _Ref}, #state{cfg = Cfg, registry = R0} = S) -> case dict:find(Pid, R0) of @@ -269,6 +241,33 @@ ?LOG_DEBUG("ignore ~p(): ~p", [?FUNCTION_NAME, Info]), {noreply, S}; +handle_cast({enb_set_addr, Pid, {ULDL, Addr}}, + #state{registry = R} = S0) -> + case dict:find(Pid, R) of + %% the given UL/DL Addr is already known + {ok, #{genb_id := GlobalENBId, + ULDL := #{addr := Addr}}} -> + ?LOG_DEBUG("eNB (pid ~p, ~p): ~p address ~p is already known", + [Pid, GlobalENBId, ULDL, Addr]), + {noreply, S0}; + %% the given UL/DL Addr differs from stored Addr + {ok, #{genb_id := GlobalENBId, + ULDL := #{addr := OldAddr}}} -> + ?LOG_ERROR("eNB (pid ~p, ~p): ~p address ~p -> ~p change?!?", + [Pid, GlobalENBId, ULDL, OldAddr, Addr]), + {noreply, S0}; + %% UL/DL state is missing => create it + {ok, #{genb_id := GlobalENBId} = ES} -> + ?LOG_DEBUG("eNB (pid ~p, ~p): ~p address ~p indicated, " + "creating NFT counters and rules", + [Pid, GlobalENBId, ULDL, Addr]), + {_, S1} = enb_set_addr({ULDL, Addr}, ES, S0), + {noreply, S1}; + error -> + ?LOG_ERROR("eNB (pid ~p) is *not* registered", [Pid]), + {noreply, S0} + end; + handle_cast(report_nft_counters, #state{cfg = #{table_name := TName}, registry = R0} = S) -> -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40438?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: merged Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: Iacd380f66fc0249a6a645293abd5aac9cdba34d5 Gerrit-Change-Number: 40438 Gerrit-PatchSet: 6 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[L] Change in ...osmo-s1gw[master]: enft_kpi: retrieve per-eNB traffic counters
by fixeria 21 Jul '25

21 Jul '25
fixeria has submitted this change. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40281?usp=email ) Change subject: enft_kpi: retrieve per-eNB traffic counters ...................................................................... enft_kpi: retrieve per-eNB traffic counters Change-Id: I498d2854447a2d53d2abddd38652f3e2bbb1fbdd Related: SYS#7307 --- M config/sys.config M debian/control M include/s1gw_metrics.hrl M rebar.config M rebar.lock A src/enft_kpi.erl M src/osmo_s1gw.app.src M src/osmo_s1gw_sup.erl M src/s1ap_proxy.erl M test/s1ap_proxy_test.erl 10 files changed, 684 insertions(+), 1 deletion(-) Approvals: Jenkins Builder: Verified pespin: Looks good to me, but someone else must approve fixeria: Looks good to me, approved diff --git a/config/sys.config b/config/sys.config index d405db8..57cb61c 100644 --- a/config/sys.config +++ b/config/sys.config @@ -19,6 +19,11 @@ %% Optional PFCP Network Instance IEs (omitted if not configured) %% {pfcp_net_inst_core, << 16#09, "core-side" >>}, %% PFCP Network Instance IE value (to core) %% {pfcp_net_inst_access, << 16#0a, "radio-side" >>} %% PFCP Network Instance IE value (to access) +%% +%% Optional NFT KPI configuration +%% {enft_kpi_enable, true}, %% whether to enable the NFT KPI module (default: false) +%% {enft_kpi_table_name, "osmo-s1gw"}, %% the NFT table name to be used by this process +%% {enft_kpi_interval, 3000} %% counter reporting interval (ms) ]}, %% ================================================================================ %% kernel config diff --git a/debian/control b/debian/control index a4279b7..10eddac 100644 --- a/debian/control +++ b/debian/control @@ -3,6 +3,7 @@ Section: net Priority: optional Build-Depends: erlang-nox, + libnftables-dev, rebar3, debhelper (>= 10) Standards-Version: 4.5.0 diff --git a/include/s1gw_metrics.hrl b/include/s1gw_metrics.hrl index 940529b..83ab46d 100644 --- a/include/s1gw_metrics.hrl +++ b/include/s1gw_metrics.hrl @@ -36,6 +36,13 @@ -define(S1GW_CTR_S1AP_PROXY_OUT_PKT_REPLY_ALL, [ctr, s1ap, proxy, out_pkt, reply, all]). -define(S1GW_CTR_S1AP_PROXY_OUT_PKT_REPLY_ERAB_SETUP_RSP, [ctr, s1ap, proxy, out_pkt, reply, erab_setup_rsp]). +%% GTP-U (traffic) related counters +%% NOTE: these counters shall not be listed in ?S1GW_COUNTERS, +%% but created dynamically for each connecting eNB. +-define(S1GW_CTR_GTPU_PACKETS(ENBId, UDL), [ctr, enb, ENBId, gtpu, packets, UDL]). +-define(S1GW_CTR_GTPU_BYTES_UE(ENBId, UDL), [ctr, enb, ENBId, gtpu, bytes, ue, UDL]). +-define(S1GW_CTR_GTPU_BYTES_TOTAL(ENBId, UDL), [ctr, enb, ENBId, gtpu, bytes, total, UDL]). + -define(S1GW_GAUGE_PFCP_ASSOCIATED, [gauge, pfcp, associated]). -define(S1GW_GAUGE_S1AP_ENB_NUM_SCTP_CONNECTIONS, [gauge, s1ap, enb, num_sctp_connections]). -define(S1GW_GAUGE_S1AP_PROXY_UPLINK_PACKETS_QUEUED, [gauge, s1ap, proxy, uplink_packets_queued]). diff --git a/rebar.config b/rebar.config index 0100f17..b69ef47 100644 --- a/rebar.config +++ b/rebar.config @@ -9,6 +9,8 @@ {git, "https://github.com/rlipscombe/logger_color_formatter.git", {tag, "0.5.0"}}}, {pfcplib, {git, "https://github.com/travelping/pfcplib.git", {branch, "master"}}}, + {enftables, + {git, "https://gitea.osmocom.org/erlang/enftables.git", {branch, "master"}}}, {exometer_core, {git, "https://github.com/Feuerlabs/exometer_core.git", {branch, "master"}}}, {exometer_report_statsd, diff --git a/rebar.lock b/rebar.lock index c52d677..c5a90df 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,5 +1,9 @@ {"1.2.0", [{<<"cut">>,{pkg,<<"cut">>,<<"1.0.3">>},1}, + {<<"enftables">>, + {git,"https://gitea.osmocom.org/erlang/enftables.git", + {ref,"be23c8ebac948aa1eea450b368af437d12a7173a"}}, + 0}, {<<"exometer_core">>, {git,"https://github.com/Feuerlabs/exometer_core.git", {ref,"f9c7abc095edc893c9354a3d5f061715de1d9e79"}}, @@ -9,6 +13,10 @@ {ref,"f1c369becb6e57871f1c7b0e491f6c3a302a65ee"}}, 0}, {<<"hut">>,{pkg,<<"hut">>,<<"1.3.0">>},1}, + {<<"jiffy">>, + {git,"https://github.com/davisp/jiffy", + {ref,"50daa80a62a97ffb6dd46ea9cb8ccb4930cbf1ae"}}, + 1}, {<<"logger_color_formatter">>, {git,"https://github.com/rlipscombe/logger_color_formatter.git", {ref,"f1c96f979e6350f8cd787d27fe9ff003cbf3416b"}}, diff --git a/src/enft_kpi.erl b/src/enft_kpi.erl new file mode 100644 index 0000000..015956c --- /dev/null +++ b/src/enft_kpi.erl @@ -0,0 +1,624 @@ +%% Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> +%% Author: Vadim Yanitskiy <vyanitskiy(a)sysmocom.de> +%% +%% All Rights Reserved +%% +%% SPDX-License-Identifier: AGPL-3.0-or-later +%% +%% This program is free software; you can redistribute it and/or modify +%% it under the terms of the GNU Affero General Public License as +%% published by the Free Software Foundation; either version 3 of the +%% License, or (at your option) any later version. +%% +%% This program is distributed in the hope that it will be useful, +%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%% GNU General Public License for more details. +%% +%% You should have received a copy of the GNU Affero General Public License +%% along with this program. If not, see <https://www.gnu.org/licenses/>. +%% +%% Additional Permission under GNU AGPL version 3 section 7: +%% +%% If you modify this Program, or any covered work, by linking or +%% combining it with runtime libraries of Erlang/OTP as released by +%% Ericsson on https://www.erlang.org (or a modified version of these +%% libraries), containing parts covered by the terms of the Erlang Public +%% License (https://www.erlang.org/EPLICENSE) the licensors of this +%% Program grant you additional permission to convey the resulting work +%% without the need to license the runtime libraries of Erlang/OTP under +%% the GNU Affero General Public License. Corresponding Source for a +%% non-source form of such a combination shall include the source code +%% for the parts of the runtime libraries of Erlang/OTP used as well as +%% that of the covered work. + +-module(enft_kpi). +-behaviour(gen_server). + +-export([init/1, + handle_info/2, + handle_call/3, + handle_cast/2, + terminate/2]). +-export([start_link/1, + enb_register/1, + enb_set_addr/1, + enb_unregister/0, + fetch_counters/0, + shutdown/0]). + +-include_lib("kernel/include/logger.hrl"). + +-include("s1gw_metrics.hrl"). + +-define(GTPU_PORT, 2152). + +-define(OP_EQ, "=="). +-define(OP_NEQ, "!="). + + +-type cfg() :: #{enable => boolean(), + table_name => string(), + interval => non_neg_integer() %% like X34 in osmo-hnbgw + }. + +-record(ctr, {packets = 0 :: non_neg_integer(), + bytes_ue = 0 :: non_neg_integer(), + bytes_total = 0 :: non_neg_integer() + }). + +-type counter() :: #ctr{}. + +-type counters() :: dict:dict(K :: string(), + V :: counter()). + +-type enb_state() :: #{pid => pid(), + addr => string(), + genb_id => string(), + mon_ref => reference(), + handle_ul => integer(), + handle_dl => integer(), + ctr_ul => counter(), %% UL counters + ctr_dl => counter() %% DL counters + }. + +-type registry() :: dict:dict(K :: pid(), + V :: enb_state()). + +-record(state, {cfg :: cfg(), + registry :: registry() + }). + +-export_type([cfg/0, + counter/0, + counters/0]). + + +%% ------------------------------------------------------------------ +%% public API +%% ------------------------------------------------------------------ + +-spec start_link(cfg()) -> gen_server:start_ret(). +start_link(Cfg) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [Cfg], []). + + +-spec enb_register(GlobalENBId) -> ok | {error, term()} + when GlobalENBId :: string(). +enb_register(GlobalENBId) -> + gen_server:call(?MODULE, {?FUNCTION_NAME, GlobalENBId}). + + +-spec enb_set_addr(Addr) -> ok | {error, term()} + when Addr :: string(). +enb_set_addr(Addr) -> + gen_server:call(?MODULE, {?FUNCTION_NAME, Addr}). + + +-spec enb_unregister() -> ok | {error, term()}. +enb_unregister() -> + gen_server:call(?MODULE, ?FUNCTION_NAME). + + +-spec fetch_counters() -> ok | {ok, counters()} | {error, term()}. +fetch_counters() -> + gen_server:call(?MODULE, ?FUNCTION_NAME). + + +-spec shutdown() -> ok. +shutdown() -> + gen_server:stop(?MODULE). + + +%% ------------------------------------------------------------------ +%% gen_server API +%% ------------------------------------------------------------------ + +init([#{enable := true} = Cfg]) -> + process_flag(trap_exit, true), + TName = maps:get(table_name, Cfg, "osmo-s1gw"), + Interval = maps:get(interval, Cfg, 3000), + %% ignore (accept) anything but GTPU @ udp/2152 + R1 = [nft_expr_match_ip_proto("udp", ?OP_NEQ), nft_expr_accept()], + R2 = [nft_expr_match_udp_dport(?GTPU_PORT, ?OP_NEQ), nft_expr_accept()], + Cmds = [nft_cmd_add_table(TName), + nft_cmd_add_chain(TName, "gtpu-ul", "prerouting"), + nft_cmd_add_chain(TName, "gtpu-dl", "postrouting"), + nft_cmd_add_rule(TName, "gtpu-ul", R1), + nft_cmd_add_rule(TName, "gtpu-dl", R1), + nft_cmd_add_rule(TName, "gtpu-ul", R2), + nft_cmd_add_rule(TName, "gtpu-dl", R2) + ], + case nft_exec(Cmds) of + ok -> + ?LOG_INFO("NFT table ~p has been initialized", [TName]), + spawn_link(fun() -> heartbeat(Interval) end), + {ok, #state{cfg = Cfg#{table_name => TName, + interval => Interval}, + registry = dict:new()}}; + Error -> + ?LOG_ERROR("NFT table ~p init failed: ~p", [TName, Error]), + {error, Error} + end; + +%% stub mode +init([#{enable := false} = Cfg]) -> + {ok, #state{cfg = Cfg, + registry = dict:new()}}. + + +handle_call(Info, From, + #state{cfg = #{enable := false}} = S) -> + ?LOG_DEBUG("ignore ~p() from ~p: ~p", [?FUNCTION_NAME, From, Info]), + {reply, ok, S}; + +handle_call({enb_register, GlobalENBId}, {Pid, _Ref}, + #state{registry = R0} = S) -> + case dict:find(Pid, R0) of + {ok, _} -> + ?LOG_ERROR("eNB (pid ~p, ~p) is already registered", + [Pid, GlobalENBId]), + {reply, {error, already_registered}, S}; + error -> + %% keep an eye on the process being registered + MonRef = erlang:monitor(process, Pid), + %% add exometer counters + enb_add_metrics(GlobalENBId), + %% create and store an initial eNB state + ES = #{genb_id => GlobalENBId, + mon_ref => MonRef, + pid => Pid}, + R1 = dict:store(Pid, ES, R0), + ?LOG_INFO("eNB (pid ~p, ~p) has been registered", + [Pid, GlobalENBId]), + {reply, ok, S#state{registry = R1}} + end; + +handle_call({enb_set_addr, Addr}, {Pid, _Ref}, + #state{registry = R} = S0) -> + case dict:find(Pid, R) of + {ok, EnbState} -> + {Reply, S1} = enb_set_addr(Addr, EnbState, S0), + {reply, Reply, S1}; + error -> + ?LOG_ERROR("eNB @ ~p (pid ~p) is *not* registered", + [Addr, Pid]), + {reply, {error, not_registered}, S0} + end; + +handle_call(enb_unregister, {Pid, _Ref}, + #state{cfg = Cfg, registry = R0} = S) -> + case dict:find(Pid, R0) of + {ok, #{genb_id := GlobalENBId, + mon_ref := MonRef} = ES} -> + erlang:demonitor(MonRef, [flush]), + enb_del_nft_counters(ES, Cfg), + R1 = dict:erase(Pid, R0), + ?LOG_INFO("eNB (pid ~p, ~p) has been unregistered", + [Pid, GlobalENBId]), + {reply, ok, S#state{registry = R1}}; + error -> + ?LOG_ERROR("eNB (pid ~p) is *not* registered", [Pid]), + {reply, {error, enb_not_registered}, S} + end; + +handle_call(fetch_counters, _From, + #state{cfg = #{table_name := TName}} = S) -> + ?LOG_DEBUG("Fetching NFT counters"), + Cmds = [nft_cmd_list_counters(TName)], + case nft_exec(Cmds) of + {ok, Res} -> + Ctrs = parse_nft_counters(Res), + {reply, {ok, Ctrs}, S}; + {error, Error} -> + ?LOG_ERROR("Failed to fetch NFT counters: ~p", [Error]), + {reply, {error, Error}, S} + end; + +handle_call(Info, From, S) -> + ?LOG_ERROR("unknown ~p() from ~p: ~p", [?FUNCTION_NAME, From, Info]), + {reply, {error, not_implemented}, S}. + + +handle_cast(Info, + #state{cfg = #{enable := false}} = S) -> + ?LOG_DEBUG("ignore ~p(): ~p", [?FUNCTION_NAME, Info]), + {noreply, S}; + +handle_cast(report_nft_counters, + #state{cfg = #{table_name := TName}, + registry = R0} = S) -> + ?LOG_DEBUG("Fetching and reporting NFT counters"), + Cmds = [nft_cmd_list_counters(TName)], + case nft_exec(Cmds) of + {ok, Res} -> + Ctrs = parse_nft_counters(Res), + R1 = report_nft_counters(Ctrs, R0), + {noreply, S#state{registry = R1}}; + {error, Error} -> + ?LOG_ERROR("Failed to fetch NFT counters: ~p", [Error]), + {noreply, S} + end; + +handle_cast(Info, S) -> + ?LOG_ERROR("unknown ~p(): ~p", [?FUNCTION_NAME, Info]), + {noreply, S}. + + +handle_info({'DOWN', _MonRef, process, Pid, Reason}, + #state{cfg = Cfg, registry = R0} = S) -> + ?LOG_INFO("eNB process ~p terminated with reason ~p", [Pid, Reason]), + case dict:find(Pid, R0) of + {ok, ES} -> + enb_del_nft_counters(ES, Cfg), + R1 = dict:erase(Pid, R0), + ?LOG_INFO("eNB (pid ~p) has been unregistered", [Pid]), + {noreply, S#state{registry = R1}}; + error -> + ?LOG_ERROR("eNB (pid ~p) is *not* registered", [Pid]), + {noreply, S} + end; + +handle_info(Info, S) -> + ?LOG_ERROR("unknown ~p(): ~p", [?FUNCTION_NAME, Info]), + {noreply, S}. + + +terminate(Reason, + #state{cfg = Cfg}) -> + ?LOG_NOTICE("Terminating, reason ~p", [Reason]), + case Cfg of + #{enable := true, table_name := TName} -> + nft_exec([nft_cmd_del_table(TName)]), %% delete the table + ok; + _ -> ok %% stub mode + end. + + +%% ------------------------------------------------------------------ +%% private API +%% ------------------------------------------------------------------ + +enb_add_metrics(GlobalENBId) -> + enb_add_metrics(GlobalENBId, ul), + enb_add_metrics(GlobalENBId, dl). + +enb_add_metrics(GlobalENBId, UDL) -> + %% counters may already exist, so catch exceptions here + catch exometer:new(?S1GW_CTR_GTPU_PACKETS(GlobalENBId, UDL), counter), + catch exometer:new(?S1GW_CTR_GTPU_BYTES_UE(GlobalENBId, UDL), counter), + catch exometer:new(?S1GW_CTR_GTPU_BYTES_TOTAL(GlobalENBId, UDL), counter). + + +-spec enb_set_addr(Addr, ES, S0) -> {Reply, S1} + when Addr :: string(), + ES :: enb_state(), + S0 :: #state{}, + S1 :: #state{}, + Reply :: ok | {error, term()}. +enb_set_addr(Addr, %% given Addr matches stored Addr + #{genb_id := GlobalENBId, + addr := Addr, + pid := Pid}, S) -> + ?LOG_DEBUG("eNB @ ~p (pid ~p, ~p): address is already known", + [Addr, Pid, GlobalENBId]), + {ok, S}; + +enb_set_addr(NewAddr, %% given NewAddr differs from stored Addr + #{genb_id := GlobalENBId, + addr := Addr, + pid := Pid}, S) -> + ?LOG_ERROR("eNB @ ~p (pid ~p, ~p): address (~p) mismatch?!?", + [Addr, Pid, GlobalENBId, NewAddr]), + {{error, addr_mismatch}, S}; + +enb_set_addr(Addr, #{genb_id := GlobalENBId, + pid := Pid} = ES0, + #state{cfg = Cfg, registry = R0} = S) -> + ?LOG_DEBUG("eNB @ ~p (pid ~p, ~p): " + "address indicated, creating NFT rules/counters", + [Addr, Pid, GlobalENBId]), + case enb_add_nft_counters(ES0#{addr => Addr}, Cfg) of + {ok, ES1} -> + R1 = dict:store(Pid, ES1, R0), + ?LOG_INFO("eNB @ ~p (pid ~p, ~p): " + "NFT rules/counters created (KPI ready)", + [Addr, Pid, GlobalENBId]), + {ok, S#state{registry = R1}}; + {error, Error} -> + ?LOG_ERROR("eNB @ ~p (pid ~p, ~p): " + "creating NFT rules/counters failed: ~p", + [Addr, Pid, GlobalENBId, Error]), + {{error, Error}, S} + end. + + +-spec enb_add_nft_counters(ES0, Cfg) -> {ok, ES1} | {error, term()} + when ES0 :: enb_state(), + ES1 :: enb_state(), + Cfg :: cfg(). +enb_add_nft_counters(#{addr := Addr, + genb_id := GlobalENBId} = ES0, + #{table_name := TName}) -> + RUL = [nft_expr_match_ip_saddr(Addr, ?OP_EQ), + nft_expr_counter("ul-" ++ GlobalENBId)], + RDL = [nft_expr_match_ip_daddr(Addr, ?OP_EQ), + nft_expr_counter("dl-" ++ GlobalENBId)], + Cmds = [nft_cmd_add_counter(TName, "ul-" ++ GlobalENBId), + nft_cmd_add_counter(TName, "dl-" ++ GlobalENBId), + nft_cmd_add_rule(TName, "gtpu-ul", RUL), + nft_cmd_add_rule(TName, "gtpu-dl", RDL) + ], + case nft_exec(Cmds) of + ok -> + ULH = nft_chain_last_handle(TName, "gtpu-ul"), + DLH = nft_chain_last_handle(TName, "gtpu-dl"), + %% update the eNB state with new info + ES1 = ES0#{handle_ul => ULH, + handle_dl => DLH, + ctr_ul => #ctr{}, + ctr_dl => #ctr{}}, + {ok, ES1}; + {error, Error} -> + ?LOG_ERROR("~p() failed: ~p", [?FUNCTION_NAME, Error]), + {error, Error} + end. + + +-spec enb_del_nft_counters(enb_state(), cfg()) -> ok | {error, term()}. +enb_del_nft_counters(#{genb_id := GlobalENBId, + handle_ul := ULH, + handle_dl := DLH}, + #{table_name := TName}) -> + Cmds = [nft_cmd_del_rule(TName, "gtpu-ul", ULH), + nft_cmd_del_rule(TName, "gtpu-dl", DLH), + nft_cmd_del_counter(TName, "ul-" ++ GlobalENBId), + nft_cmd_del_counter(TName, "dl-" ++ GlobalENBId) + ], + case nft_exec(Cmds) of + ok -> ok; + {error, Error} -> + ?LOG_ERROR("~p() failed: ~p", [?FUNCTION_NAME, Error]), + {error, Error} + end; + +%% missing ULH/DLH => nothing to delete +enb_del_nft_counters(_ES, _Cfg) -> ok. + + +%% Parse the given list of NFT counters (result of nft_cmd_list_counters()). +-spec parse_nft_counters([map()]) -> counters(). +parse_nft_counters(L0) -> + L1 = lists:map(fun parse_nft_counter/1, L0), + dict:from_list(L1). + + +-spec parse_nft_counter(map()) -> {string(), counter()}. +parse_nft_counter(#{<< "counter" >> := #{<< "name" >> := Name, + << "bytes" >> := Bytes, + << "packets" >> := Packets}}) -> + %% Assuming an IP header of 20 bytes, derive the GTP-U payload size: + %% [...] \ \ + %% [ UDP ][ TCP ] | UE payload | nft reports these bytes + %% [ IP ] / | + %% -- payload -- | + %% [ GTP-U 8 bytes ] | \ + %% [ UDP 8 bytes ] | | need to subtract these, 20 + 8 + 8 + %% [ IP 20 bytes ] / / + Ctr = #ctr{packets = Packets, + bytes_ue = Bytes - erlang:min(Bytes, Packets * (20 + 8 + 8)), + bytes_total = Bytes}, + {binary_to_list(Name), Ctr}. + + +-spec report_nft_counters(Ctrs, R0) -> R1 + when Ctrs :: counters(), %% result of parse_nft_counters() + R0 :: registry(), %% (current) registry of eNBs + R1 :: registry(). %% (new) registry of eNBs +report_nft_counters(Ctrs, R0) -> + %% for each registered eNB, look-up and report UL/DL counters + dict:map(fun(_Pid, EnbState) -> enb_report_nft_counters(Ctrs, EnbState) end, R0). + + +-spec enb_report_nft_counters(counters(), enb_state()) -> enb_state(). +enb_report_nft_counters(Ctrs, #{genb_id := GlobalENBId, + ctr_ul := ULC, + ctr_dl := DLC} = ES) -> + ?LOG_DEBUG("Reporting NFT counters for eNB ~p", [GlobalENBId]), + ES#{ctr_ul => report_nft_counter(Ctrs, {ul, GlobalENBId}, ULC), + ctr_dl => report_nft_counter(Ctrs, {dl, GlobalENBId}, DLC)}; + +%% missing ULH/DLH => nothing to report +enb_report_nft_counters(_Ctrs, ES) -> ES. + + +-spec report_nft_counter(Ctrs, {UDL, GlobalENBId}, C0) -> C1 + when Ctrs :: counters(), + UDL :: ul | dl, + GlobalENBId :: string(), + C0 :: counter(), + C1 :: counter(). +report_nft_counter(Ctrs, {UDL, GlobalENBId}, C0) -> + CtrName = atom_to_list(UDL) ++ "-" ++ GlobalENBId, + case dict:find(CtrName, Ctrs) of + {ok, C0} -> + %% no diff, nothing to report + ?LOG_DEBUG("NFT counters (~p) for eNB ~p: ~p", + [UDL, GlobalENBId, C0]), + C0; + {ok, C1} -> + %% XXX: assuming C1 (new) values >= C0 (cached) values + ?LOG_DEBUG("NFT counters (~p) for eNB ~p: ~p -> ~p", + [UDL, GlobalENBId, C0, C1]), + s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_PACKETS(GlobalENBId, UDL), + C1#ctr.packets - C0#ctr.packets), + s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_BYTES_UE(GlobalENBId, UDL), + C1#ctr.bytes_ue - C0#ctr.bytes_ue), + s1gw_metrics:ctr_inc(?S1GW_CTR_GTPU_BYTES_TOTAL(GlobalENBId, UDL), + C1#ctr.bytes_total - C0#ctr.bytes_total), + C1; + error -> + %% no counters for this eNB (yet?) + ?LOG_DEBUG("NFT counters (~p) for eNB ~p: nope", + [UDL, GlobalENBId]), + C0 + end. + + +-spec nft_exec(Cmds) -> enftables:result() + when Cmds :: [enftables:nft_cmd()]. +nft_exec(Cmds) -> + ?LOG_DEBUG("Executing nftables commands: ~p", [Cmds]), + enftables:run_cmd(Cmds, json). + + +nft_chain_last_handle(TName, CName) -> + Cmds = [nft_cmd_list_chain(TName, CName)], + case nft_exec(Cmds) of + {ok, Res} -> + #{<< "rule" >> := Rule} = lists:last(Res), + maps:get(<< "handle" >>, Rule); + Error -> + ?LOG_ERROR("~p() failed: ~p", [?FUNCTION_NAME, Error]), + error + end. + + +nft_cmd_add_table(TName) -> + T = #{family => << "inet" >>, + name => list_to_binary(TName), + flags => [<< "owner" >>] + }, + #{add => #{table => T}}. + + +nft_cmd_del_table(TName) -> + T = #{family => << "inet" >>, + name => list_to_binary(TName) + }, + #{delete => #{table => T}}. + + +nft_cmd_add_chain(TName, CName, Hook) -> + C = #{family => << "inet" >>, + table => list_to_binary(TName), + name => list_to_binary(CName), + type => << "filter" >>, + hook => list_to_binary(Hook), + prio => 0, + policy => << "accept" >> + }, + #{add => #{chain => C}}. + + +nft_cmd_add_rule(TName, CName, Expr) -> + R = #{family => << "inet" >>, + table => list_to_binary(TName), + chain => list_to_binary(CName), + expr => Expr + }, + #{add => #{rule => R}}. + + +nft_cmd_del_rule(TName, CName, Handle) -> + R = #{family => << "inet" >>, + table => list_to_binary(TName), + chain => list_to_binary(CName), + handle => Handle + }, + #{delete => #{rule => R}}. + + +nft_counter(TName, Name) -> + #{family => << "inet" >>, + table => list_to_binary(TName), + name => list_to_binary(Name) + }. + +nft_cmd_add_counter(TName, Name) -> + #{add => #{counter => nft_counter(TName, Name)}}. + +nft_cmd_del_counter(TName, Name) -> + #{delete => #{counter => nft_counter(TName, Name)}}. + + +-spec nft_expr_match_payload({Proto, Field}, Value, Op) -> map() + when Proto :: string(), + Field :: string(), + Value :: term(), + Op :: string(). +nft_expr_match_payload({Proto, Field}, Value, Op) -> + Left = #{payload => #{protocol => list_to_binary(Proto), + field => list_to_binary(Field)}}, + #{match => #{left => Left, + right => Value, + op => list_to_binary(Op)}}. + + +nft_expr_match_ip_proto(Proto, Op) -> + nft_expr_match_payload({"ip", "protocol"}, + list_to_binary(Proto), Op). + +nft_expr_match_ip_saddr(Addr, Op) -> + nft_expr_match_payload({"ip", "saddr"}, + list_to_binary(Addr), Op). + +nft_expr_match_ip_daddr(Addr, Op) -> + nft_expr_match_payload({"ip", "daddr"}, + list_to_binary(Addr), Op). + +nft_expr_match_udp_dport(Port, Op) -> + nft_expr_match_payload({"udp", "dport"}, Port, Op). + + +nft_expr_accept() -> + #{accept => null}. + + +nft_expr_counter(Name) -> + #{counter => list_to_binary(Name)}. + + +nft_cmd_list_chain(TName, CName) -> + C = #{family => << "inet" >>, + table => list_to_binary(TName), + name => list_to_binary(CName) + }, + #{list => #{chain => C}}. + + +nft_cmd_list_counters(TName) -> + T = #{family => << "inet" >>, + name => list_to_binary(TName) + }, + #{list => #{counters => #{table => T}}}. + + +-spec heartbeat(timeout()) -> no_return(). +heartbeat(Tval) -> + timer:sleep(Tval), + gen_server:cast(?MODULE, report_nft_counters), + heartbeat(Tval). %% keep going + + +%% vim:set ts=4 sw=4 et: diff --git a/src/osmo_s1gw.app.src b/src/osmo_s1gw.app.src index 743a5d5..e64fb93 100644 --- a/src/osmo_s1gw.app.src +++ b/src/osmo_s1gw.app.src @@ -9,6 +9,7 @@ stdlib, logger_color_formatter, pfcplib, + enftables, exometer_core, exometer_report_statsd ]}, diff --git a/src/osmo_s1gw_sup.erl b/src/osmo_s1gw_sup.erl index 279ad02..c746d7c 100644 --- a/src/osmo_s1gw_sup.erl +++ b/src/osmo_s1gw_sup.erl @@ -50,6 +50,9 @@ -define(ENV_DEFAULT_MME_REM_PORT, ?S1AP_PORT). -define(ENV_DEFAULT_PFCP_LOC_ADDR, "127.0.1.1"). -define(ENV_DEFAULT_PFCP_REM_ADDR, "127.0.1.2"). +-define(ENV_DEFAULT_ENFT_KPI_ENABLE, false). +-define(ENV_DEFAULT_ENFT_KPI_TABLE_NAME, "osmo-s1gw"). +-define(ENV_DEFAULT_ENFT_KPI_INTERVAL, 3000). %% ------------------------------------------------------------------ %% supervisor API @@ -74,9 +77,14 @@ 5000, worker, [pfcp_peer]}, + EnftKpi = {enft_kpi, {enft_kpi, start_link, [enft_kpi_cfg()]}, + permanent, + 5000, + worker, + [enft_kpi]}, s1gw_metrics:init(), - {ok, {{one_for_one, 5, 10}, [SctpServer, PfcpPeer]}}. + {ok, {{one_for_one, 5, 10}, [SctpServer, PfcpPeer, EnftKpi]}}. %% ------------------------------------------------------------------ @@ -106,4 +114,11 @@ handler => sctp_proxy, priv => client_cfg()}. + +-spec enft_kpi_cfg() -> enft_kpi:cfg(). +enft_kpi_cfg() -> + #{enable => osmo_s1gw:get_env(enft_kpi_enable, ?ENV_DEFAULT_ENFT_KPI_ENABLE), + table_name => osmo_s1gw:get_env(enft_kpi_table_name, ?ENV_DEFAULT_ENFT_KPI_TABLE_NAME), + interval => osmo_s1gw:get_env(enft_kpi_interval, ?ENV_DEFAULT_ENFT_KPI_INTERVAL)}. + %% vim:set ts=4 sw=4 et: diff --git a/src/s1ap_proxy.erl b/src/s1ap_proxy.erl index f2fd722..6b75945 100644 --- a/src/s1ap_proxy.erl +++ b/src/s1ap_proxy.erl @@ -278,6 +278,12 @@ end. +-spec tla_str(binary()) -> string(). +tla_str(TLA0) -> + TLA1 = list_to_tuple(binary_to_list(TLA0)), + inet:ntoa(TLA1). + + %% Encode an S1AP PDU -spec encode_pdu(s1ap_pdu()) -> {ok, binary()} | {error, {asn1, tuple()}}. @@ -354,6 +360,14 @@ C0#'S1SetupRequest'.protocolIEs, S0), {forward, S1}; +%% 9.1.8.5 S1 SETUP RESPONSE +handle_pdu({successfulOutcome, + #'SuccessfulOutcome'{procedureCode = ?'id-S1Setup'}}, S) -> + ?LOG_DEBUG("Processing S1 SETUP RESPONSE"), + ok = enft_kpi:enb_register(genb_id_str(S)), + %% there's nothing to patch in this PDU, so we forward it as-is + {forward, S}; + %% 9.1.3.1 E-RAB SETUP REQUEST handle_pdu({Outcome = initiatingMessage, #'InitiatingMessage'{procedureCode = ?'id-E-RABSetup', @@ -676,6 +690,8 @@ #'E-RABSetupItemBearerSURes'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> + %% indicate eNB's address to the enft_kpi module + enft_kpi:enb_set_addr(tla_str(TLA_In)), %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> @@ -963,6 +979,8 @@ #'E-RABSetupItemCtxtSURes'{'e-RAB-ID' = ERABId, 'transportLayerAddress' = TLA_In, 'gTP-TEID' = << TEID_In:32/big >>} = C0, S) -> + %% indicate eNB's address to the enft_kpi module + enft_kpi:enb_set_addr(tla_str(TLA_In)), %% poke E-RAB FSM case erab_fsm_find(ERABId, S) of {ok, Pid} -> diff --git a/test/s1ap_proxy_test.erl b/test/s1ap_proxy_test.erl index 3a73910..dd1638b 100644 --- a/test/s1ap_proxy_test.erl +++ b/test/s1ap_proxy_test.erl @@ -23,6 +23,7 @@ pfcp_mock:mock_all(), exometer:start(), s1gw_metrics:init(), + enft_kpi:start_link(#{enable => false}), {ok, Pid} = s1ap_proxy:start_link(), #{handler => Pid}. @@ -30,6 +31,7 @@ stop(#{handler := Pid}) -> s1ap_proxy:shutdown(Pid), exometer:stop(), + enft_kpi:shutdown(), pfcp_mock:unmock_all(). -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40281?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: merged Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I498d2854447a2d53d2abddd38652f3e2bbb1fbdd Gerrit-Change-Number: 40281 Gerrit-PatchSet: 12 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
1 0
0 0
[M] Change in ...osmo-s1gw[master]: Rename enft_kpi -> gtpu_kpi
by pespin 21 Jul '25

21 Jul '25
Attention is currently required from: fixeria. pespin has posted comments on this change by fixeria. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726?usp=email ) Change subject: Rename enft_kpi -> gtpu_kpi ...................................................................... Patch Set 2: Code-Review+1 -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: comment Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I8a470d2c40dc4487c4de30b3cceeec333228935b Gerrit-Change-Number: 40726 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Mon, 21 Jul 2025 13:45:48 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1 0
0 0
[L] Change in ...osmo-s1gw[master]: enft_kpi: retrieve per-eNB traffic counters
by pespin 21 Jul '25

21 Jul '25
Attention is currently required from: laforge. pespin has posted comments on this change by fixeria. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40281?usp=email ) Change subject: enft_kpi: retrieve per-eNB traffic counters ...................................................................... Patch Set 12: (1 comment) File config/sys.config: https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40281/comment/405ed4c8_46a4… : PS8, Line 24: %% {enft_kpi_enable, true}, %% whether to enable the NFT KPI module (default: false) > Done, see: https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726. LGTM, please. If by "similar" you mean "nft" instead of "enft", then it sure makes my point valid ;) -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40281?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: comment Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I498d2854447a2d53d2abddd38652f3e2bbb1fbdd Gerrit-Change-Number: 40281 Gerrit-PatchSet: 12 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: laforge <laforge(a)osmocom.org> Gerrit-Comment-Date: Mon, 21 Jul 2025 13:45:33 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: No Comment-In-Reply-To: laforge <laforge(a)osmocom.org> Comment-In-Reply-To: fixeria <vyanitskiy(a)sysmocom.de> Comment-In-Reply-To: pespin <pespin(a)sysmocom.de>
1 0
0 0
[L] Change in ...osmo-s1gw[master]: enft_kpi: retrieve per-eNB traffic counters
by fixeria 21 Jul '25

21 Jul '25
Attention is currently required from: laforge. fixeria has posted comments on this change by fixeria. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40281?usp=email ) Change subject: enft_kpi: retrieve per-eNB traffic counters ...................................................................... Patch Set 12: Code-Review+2 (1 comment) Patchset: PS12: Re-applying CR+1 from @laforge@osmocom.org => CR+2. -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40281?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: comment Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I498d2854447a2d53d2abddd38652f3e2bbb1fbdd Gerrit-Change-Number: 40281 Gerrit-PatchSet: 12 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: laforge <laforge(a)osmocom.org> Gerrit-Comment-Date: Mon, 21 Jul 2025 13:45:18 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: Yes
1 0
0 0
[M] Change in ...osmo-s1gw[master]: Rename enft_kpi -> gtpu_kpi
by fixeria 21 Jul '25

21 Jul '25
Attention is currently required from: pespin. fixeria has posted comments on this change by fixeria. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726?usp=email ) Change subject: Rename enft_kpi -> gtpu_kpi ...................................................................... Patch Set 2: (1 comment) File src/osmo_s1gw_sup.erl: https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726/comment/9e3f0a01_5993… : PS1, Line 80: EnftKpi = {gtpu_kpi, {gtpu_kpi, start_link, [gtpu_kpi_cfg()]}, > GtpuKpi Done -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: comment Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I8a470d2c40dc4487c4de30b3cceeec333228935b Gerrit-Change-Number: 40726 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-CC: pespin <pespin(a)sysmocom.de> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Comment-Date: Mon, 21 Jul 2025 13:44:48 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: No Comment-In-Reply-To: pespin <pespin(a)sysmocom.de>
1 0
0 0
[M] Change in ...osmo-s1gw[master]: Rename enft_kpi -> gtpu_kpi
by fixeria 21 Jul '25

21 Jul '25
Attention is currently required from: fixeria. Hello Jenkins Builder, I'd like you to reexamine a change. Please visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726?usp=email to look at the new patch set (#2). The following approvals got outdated and were removed: Verified+1 by Jenkins Builder Change subject: Rename enft_kpi -> gtpu_kpi ...................................................................... Rename enft_kpi -> gtpu_kpi Change-Id: I8a470d2c40dc4487c4de30b3cceeec333228935b Related: SYS#7307 --- M config/sys.config R src/gtpu_kpi.erl M src/osmo_s1gw_sup.erl M src/s1ap_proxy.erl M test/s1ap_proxy_test.erl 5 files changed, 38 insertions(+), 38 deletions(-) git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/26/40726/2 -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40726?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: newpatchset Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I8a470d2c40dc4487c4de30b3cceeec333228935b Gerrit-Change-Number: 40726 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-CC: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de>
1 0
0 0
[M] Change in ...osmo-s1gw[master]: s1ap_proxy: support using NFT KPI UL/DL address from SCTP
by pespin 21 Jul '25

21 Jul '25
Attention is currently required from: fixeria. pespin has posted comments on this change by fixeria. ( https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40547?usp=email ) Change subject: s1ap_proxy: support using NFT KPI UL/DL address from SCTP ...................................................................... Patch Set 3: Code-Review+1 -- To view, visit https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40547?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email Gerrit-MessageType: comment Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: Iae92a42b0ccb1a4bc34f06d969f5270a304b1d3a Gerrit-Change-Number: 40547 Gerrit-PatchSet: 3 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Mon, 21 Jul 2025 13:43:37 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • ...
  • 150
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.