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 <vyanits...@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanits...@sysmocom.de>
Gerrit-Reviewer: laforge <lafo...@osmocom.org>
Gerrit-Reviewer: pespin <pes...@sysmocom.de>

Reply via email to