osmo-epdg/src/aaa_diameter_swx_cb.erl

216 lines
8.5 KiB
Erlang

%%
%% The diameter application callback module configured by client.erl.
%%
-module(aaa_diameter_swx_cb).
-include_lib("diameter/include/diameter.hrl").
-include_lib("diameter_3gpp_ts29_273_swx.hrl").
-include("conv.hrl").
%% diameter callbacks
-export([peer_up/3, peer_down/3, pick_peer/4, pick_peer/5, prepare_request/3, prepare_request/4,
prepare_retransmit/3, prepare_retransmit/4,
handle_request/3,
handle_answer/4, handle_answer/5, handle_error/4, handle_error/5]).
%% peer_up/3
peer_up(_SvcName, Peer, State) ->
lager:info("Peer up: ~p~n", [Peer]),
State.
%% peer_down/3
peer_down(_SvcName, Peer, State) ->
lager:info("Peer down: ~p~n", [Peer]),
State.
%% pick_peer/4
pick_peer([Peer | _], _, _SvcName, _State) ->
{ok, Peer}.
pick_peer([Peer | _], _, _SvcName, _State, _ExtraPars) ->
{ok, Peer}.
%% prepare_request/3
prepare_request(#diameter_packet{msg = [ T | Avps ]}, _, {_, Caps})
when is_list(Avps) ->
#diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps,
{send,
[T,
{'Origin-Host', OH},
{'Origin-Realm', OR},
{'Destination-Host', [DH]},
{'Destination-Realm', DR}
| Avps]}.
% TODO: is there a simple way to capture all the following requests?
prepare_request(#diameter_packet{msg = Req}, _, {_, Caps}, _ExtraPars)
when is_record(Req, 'MAR') ->
#diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps,
Msg = Req#'MAR'{'Origin-Host' = OH,
'Origin-Realm' = OR,
'Destination-Host' = [DH],
'Destination-Realm' = DR},
{send, Msg};
%% prepare_request/4
prepare_request(#diameter_packet{msg = Req}, _, {_, Caps}, _ExtraPars)
when is_record(Req, 'SAR') ->
#diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps,
Msg = Req#'SAR'{'Origin-Host' = OH,
'Origin-Realm' = OR,
'Destination-Host' = [DH],
'Destination-Realm' = DR},
lager:debug("SWx prepare_request: ~p~n", [Msg]),
{send, Msg}.
%% prepare_retransmit/3
prepare_retransmit(Packet, SvcName, Peer) ->
prepare_request(Packet, SvcName, Peer).
%% prepare_retransmit/4
prepare_retransmit(Packet, SvcName, Peer, ExtraPars) ->
prepare_request(Packet, SvcName, Peer, ExtraPars).
%% handle_request/3
%% 3GPP TS 29.273 8.2.2.4 Network Initiated De-Registration by HSS Procedure
handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) when is_record(Req, 'RTR') ->
lager:info("SWx Rx RTR from ~p: ~p~n", [Caps, Req]),
#diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} = Caps,
#'RTR'{'Session-Id' = SessionId,
'Vendor-Specific-Application-Id' = VendorAppId,
'Auth-Session-State' = AuthSessState,
'User-Name' = Imsi,
'Deregistration-Reason' = _DeregReason} = Req,
case aaa_ue_fsm:get_pid_by_imsi(Imsi) of
Pid when is_pid(Pid) ->
aaa_ue_fsm:stop(Pid),
Res = 2001, %% Success
ERes = [];
undefined ->
Res = [],
%% TS 29.229 6.2.2.1 DIAMETER_ERROR_USER_UNKNOWN
ERes = #'Experimental-Result'{'Vendor-Id' = ?VENDOR_ID_3GPP, 'Experimental-Result-Code' = 5001}
end,
Resp = #'RTA'{'Session-Id' = SessionId,
'Vendor-Specific-Application-Id' = VendorAppId,
'Result-Code' = Res,
'Experimental-Result' = ERes,
'Auth-Session-State' = AuthSessState,
'Origin-Host' = OH,
'Origin-Realm' = OR},
lager:info("SWx Tx to ~p: ~p~n", [Caps, Resp]),
{reply, Resp};
handle_request(Msg, _SvcName, Peer) ->
lager:error("SWx Rx unexpected msg from ~p: ~p~n", [Peer, Msg]),
erlang:error({unexpected, ?MODULE, ?LINE}).
%% handle_answer/4
handle_answer(#diameter_packet{msg = Msg, errors = Errors}, _Request, _SvcName, Peer, ReqPid) when is_record(Msg, 'MAA') ->
lager:info("SWx Rx MAA ~p: ~p/ Errors ~p ~n", [Peer, Msg, Errors]),
#'MAA'{'Result-Code' = ResultCodeOpt,
'Experimental-Result' = ExperimentalResultOpt} = Msg,
DiaRC = parse_epdg_dia_rc(ResultCodeOpt, ExperimentalResultOpt),
case dia_rc_success(DiaRC) of
ok ->
#'MAA'{'SIP-Auth-Data-Item' = SipAuthTuples} = Msg,
AuthTuples = lists:map(fun dia_sip2epdg_auth_tuple/1, SipAuthTuples),
aaa_ue_fsm:ev_rx_swx_maa(ReqPid, {ok, AuthTuples});
_ ->
aaa_ue_fsm:ev_rx_swx_maa(ReqPid, {error, DiaRC})
end,
{ok, Msg};
handle_answer(#diameter_packet{msg = Msg, errors = Errors}, Request, _SvcName, Peer, ReqPid) when is_record(Msg, 'SAA') ->
lager:info("SWx Rx SAA ~p: ~p/ Errors ~p ~n", [Peer, Msg, Errors]),
% Recover fields from originating request:
#'SAR'{'Server-Assignment-Type' = SAType} = Request,
% Retrieve fields from answer:
#'SAA'{'Result-Code' = ResultCodeOpt,
'Experimental-Result' = ExperimentalResultOpt} = Msg,
DiaRC = parse_epdg_dia_rc(ResultCodeOpt, ExperimentalResultOpt),
case dia_rc_success(DiaRC) of
ok ->
#'SAA'{'Non-3GPP-User-Data' = N3UA} = Msg,
PGWAddresses = parse_pgw_addr_from_N3UA(N3UA),
case PGWAddresses of
undefined -> ResInfo = #{};
_ -> ResInfo = maps:put(pgw_address_list, PGWAddresses, #{})
end,
aaa_ue_fsm:ev_rx_swx_saa(ReqPid, {ok, SAType, ResInfo});
_ ->
aaa_ue_fsm:ev_rx_swx_saa(ReqPid, {error, SAType, DiaRC})
end,
{ok, Msg}.
handle_answer(#diameter_packet{msg = Msg, errors = []}, _Request, _SvcName, Peer) ->
lager:info("SWx Rx ~p: ~p~n", [Peer, Msg]),
{ok, Msg};
handle_answer(#diameter_packet{msg = Msg, errors = Errors}, _Request, _SvcName, Peer) ->
lager:info("SWx Rx ~p: ~p / Errors ~p ~n", [Peer, Msg, Errors]),
{error, Errors}.
%% handle_error/4
handle_error(Reason, Request, _SvcName, _Peer) when is_list(Request) ->
lager:error("SWx error: ~p~n", [Reason]),
{error, Reason};
handle_error(Reason, _Request, _SvcName, _Peer) ->
lager:error("SWx error: ~p~n", [Reason]),
{error, Reason}.
%% handle_error/5
handle_error(Reason, _Request, _SvcName, _Peer, ExtraPars) ->
lager:error("SWx error: ~p, ExtraPars: ~p~n", [Reason, ExtraPars]),
{error, Reason}.
%% ------------------------------------------------------------------
%% Internal Function Definitions
%% ------------------------------------------------------------------
dia_rc_success(#epdg_dia_rc{result_code = 2001}) -> ok;
dia_rc_success(#epdg_dia_rc{result_code = 2002}) -> ok;
dia_rc_success(_) -> invalid_result_code.
parse_epdg_dia_rc([], []) ->
#epdg_dia_rc{vendor_id = undefined, result_code = 2001 };
parse_epdg_dia_rc([ResultCode], []) ->
#epdg_dia_rc{vendor_id = undefined, result_code = ResultCode };
parse_epdg_dia_rc([], [ExpResultCode]) ->
#'Experimental-Result'{'Vendor-Id' = VendorId, 'Experimental-Result-Code' = ERC} = ExpResultCode,
#epdg_dia_rc{vendor_id = VendorId, result_code = ERC };
parse_epdg_dia_rc([ResultCode], [_ExpResultCode]) ->
parse_epdg_dia_rc([ResultCode], []).
dia_sip2epdg_auth_tuple(#'SIP-Auth-Data-Item'{'SIP-Authenticate' = [Authenticate],
'SIP-Authorization' = [Authorization],
'Confidentiality-Key' = [CKey],
'Integrity-Key' = [IKey]}) ->
lager:info("dia_sip2gsup: auth ~p authz ~p ~n", [Authenticate, Authorization]),
lager:info(" rand ~p autn ~p ~n", [lists:sublist(Authenticate, 1, 16), lists:sublist(Authenticate, 17, 16)]),
#epdg_auth_tuple{
rand = list_to_binary(lists:sublist(Authenticate, 1, 16)),
autn = list_to_binary(lists:sublist(Authenticate, 17, 16)),
res = list_to_binary(Authorization),
ik = list_to_binary(IKey),
ck =list_to_binary(CKey)
}.
parse_pgw_addr_from_MIP6_Agent_Info([]) ->
undefined;
parse_pgw_addr_from_MIP6_Agent_Info([AgentInfo]) ->
#'MIP6-Agent-Info'{'MIP-Home-Agent-Address' = AgentAddrOpt} = AgentInfo,
case AgentAddrOpt of
[] -> undefined;
Res -> Res
end.
parse_pgw_addr_from_APN_Configuration([]) ->
undefined;
parse_pgw_addr_from_APN_Configuration([Head | Tail] = _ApnConfigs) ->
#'APN-Configuration'{'MIP6-Agent-Info' = AgentInfoOpt} = Head,
case parse_pgw_addr_from_MIP6_Agent_Info(AgentInfoOpt) of
undefined -> parse_pgw_addr_from_APN_Configuration(Tail);
Res -> Res
end.
parse_pgw_addr_from_N3UA([]) ->
undefined;
parse_pgw_addr_from_N3UA([N3UA]) ->
#'Non-3GPP-User-Data'{'APN-Configuration' = ApnConfigs} = N3UA,
parse_pgw_addr_from_APN_Configuration(ApnConfigs).