aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/diameter/test')
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl294
1 files changed, 233 insertions, 61 deletions
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 00b2c2c90e..8c85323222 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -48,12 +48,30 @@
send_nopeer/1,
send_noapp/1,
send_discard/1,
- send_any/1,
- send_all/1,
+ send_any_1/1,
+ send_any_2/1,
+ send_all_1/1,
+ send_all_2/1,
send_timeout/1,
send_error/1,
send_detach/1,
send_encode_error/1,
+ send_destination_1/1,
+ send_destination_2/1,
+ send_destination_3/1,
+ send_destination_4/1,
+ send_destination_5/1,
+ send_destination_6/1,
+ send_bad_option_1/1,
+ send_bad_option_2/1,
+ send_bad_filter_1/1,
+ send_bad_filter_2/1,
+ send_bad_filter_3/1,
+ send_bad_filter_4/1,
+ send_multiple_filters_1/1,
+ send_multiple_filters_2/1,
+ send_multiple_filters_3/1,
+ send_anything/1,
remove_transports/1,
stop_services/1]).
@@ -79,10 +97,11 @@
%% ===========================================================================
-define(ADDR, {127,0,0,1}).
--define(LISTEN_PORT, 3868).
-define(CLIENT, "CLIENT").
-define(SERVER, "SERVER").
+-define(REALM, "erlang.org").
+-define(HOST(Host, Realm), Host ++ [$.|Realm]).
-define(APP_ALIAS, base).
-define(EXTRA, an_extra_argument).
@@ -93,8 +112,8 @@
%% Config for diameter:start_service/2.
-define(SERVICE(Name),
- [{'Origin-Host', Name ++ ".erlang.org"},
- {'Origin-Realm', "erlang.org"},
+ [{'Origin-Host', Name ++ "." ++ ?REALM},
+ {'Origin-Realm', ?REALM},
{'Host-IP-Address', [?ADDR]},
{'Vendor-Id', 12345},
{'Product-Name', "OTP/diameter"},
@@ -112,14 +131,10 @@
{transport_config, [{raddr, ?ADDR},
{rport, PortNr},
{ip, ?ADDR},
- {port, 0}]},
- {watchdog_timer, 6000},
- {reconnect_timer, 1000}]}).
+ {port, 0}]}]}).
-define(LISTEN,
{listen, [{transport_module, diameter_tcp},
- {transport_config, [{ip, ?ADDR}, {port, 0}]},
- {watchdog_timer, 6000},
- {reconnect_timer, 1000}]}).
+ {transport_config, [{ip, ?ADDR}, {port, 0}]}]}).
-define(SUCCESS,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
@@ -135,6 +150,10 @@
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_AVP_UNSUPPORTED').
-define(UNSUPPORTED_VERSION,
?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNSUPPORTED_VERSION').
+-define(REALM_NOT_SERVED,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_REALM_NOT_SERVED').
+-define(UNABLE_TO_DELIVER,
+ ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNABLE_TO_DELIVER').
-define(EVENT_RECORD,
?'DIAMETER_BASE_ACCOUNTING-RECORD-TYPE_EVENT_RECORD').
@@ -173,9 +192,8 @@ init_per_suite(Config) ->
{ok, LRef} = diameter:add_transport(?SERVER, ?LISTEN),
true = diameter:subscribe(?CLIENT),
{ok, CRef} = diameter:add_transport(?CLIENT, ?CONNECT(portnr())),
- #diameter_event{service = ?CLIENT,
- info = {up, CRef, _Peer, _Config, #diameter_packet{}}}
- = receive #diameter_event{service = ?CLIENT} = E -> E
+ {up, CRef, _Peer, _Config, #diameter_packet{}}
+ = receive #diameter_event{service = ?CLIENT, info = I} -> I
after 2000 -> false
end,
true = diameter:unsubscribe(?CLIENT),
@@ -218,15 +236,33 @@ tc() ->
send_nopeer,
send_noapp,
send_discard,
- send_any,
- send_all,
+ send_any_1,
+ send_any_2,
+ send_all_1,
+ send_all_2,
send_timeout,
send_error,
send_detach,
- send_encode_error].
+ send_encode_error,
+ send_destination_1,
+ send_destination_2,
+ send_destination_3,
+ send_destination_4,
+ send_destination_5,
+ send_destination_6,
+ send_bad_option_1,
+ send_bad_option_2,
+ send_bad_filter_1,
+ send_bad_filter_2,
+ send_bad_filter_3,
+ send_bad_filter_4,
+ send_multiple_filters_1,
+ send_multiple_filters_2,
+ send_multiple_filters_3,
+ send_anything].
portnr() ->
- portnr(10).
+ portnr(20).
portnr(N)
when 0 < N ->
@@ -244,9 +280,11 @@ portnr(N)
%% Ensure that result codes have the expected values.
result_codes(_Config) ->
- {2001, 3001, 3004, 3007, 3008, 5001, 5011}
+ {2001, 3001, 3002, 3003, 3004, 3007, 3008, 5001, 5011}
= {?SUCCESS,
?COMMAND_UNSUPPORTED,
+ ?UNABLE_TO_DELIVER,
+ ?REALM_NOT_SERVED,
?TOO_BUSY,
?APPLICATION_UNSUPPORTED,
?INVALID_HDR_BITS,
@@ -349,16 +387,28 @@ send_discard(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
{error, unprepared} = call(Config, Req).
-%% Send something using a filter that doesn't match any peer.
-send_any(Config) ->
+%% Send with a disjunctive filter.
+send_any_1(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
{error, no_connection} = call(Config, Req, [{filter, {any, []}}]).
+send_any_2(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT},
+ {'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
+ #'diameter_base_answer-message'{'Result-Code' = ?UNABLE_TO_DELIVER}
+ = call(Config, Req, [{filter, {any, [host, realm]}}]).
-%% Success with a non-trivial filter.
-send_all(Config) ->
+%% Send with a conjunctive filter.
+send_all_1(Config) ->
Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+ Realm = lists:foldr(fun(C,A) -> [C,A] end, [], ?REALM),
#diameter_base_STA{'Result-Code' = ?SUCCESS}
- = call(Config, Req, [{filter, {all, []}}]).
+ = call(Config, Req, [{filter, {all, [{host, any},
+ {realm, Realm}]}}]).
+send_all_2(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT},
+ {'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
+ {error, no_connection}
+ = call(Config, Req, [{filter, {all, [host, realm]}}]).
%% Timeout before the server manages an answer.
send_timeout(Config) ->
@@ -389,17 +439,113 @@ send_detach(Config) ->
send_encode_error(Config) ->
{error, encode} = call(Config, ['STR']). %% No Termination-Cause
+%% Send with filtering and expect success.
+send_destination_1(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT},
+ {'Destination-Host', [?HOST(?SERVER, ?REALM)]}],
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(Config, Req, [{filter, {all, [host, realm]}}]).
+send_destination_2(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(Config, Req, [{filter, {all, [host, realm]}}]).
+
+%% Send with filtering on and expect failure when specifying an
+%% unknown host or realm.
+send_destination_3(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT},
+ {'Destination-Realm', "unknown.org"}],
+ {error, no_connection}
+ = call(Config, Req, [{filter, {all, [host, realm]}}]).
+send_destination_4(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT},
+ {'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
+ {error, no_connection}
+ = call(Config, Req, [{filter, {all, [host, realm]}}]).
+
+%% Send without filtering and expect an error answer when specifying
+%% an unknown host or realm.
+send_destination_5(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT},
+ {'Destination-Realm', "unknown.org"}],
+ #'diameter_base_answer-message'{'Result-Code' = ?REALM_NOT_SERVED}
+ = call(Config, Req).
+send_destination_6(Config) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT},
+ {'Destination-Host', [?HOST(?SERVER, "unknown.org")]}],
+ #'diameter_base_answer-message'{'Result-Code' = ?UNABLE_TO_DELIVER}
+ = call(Config, Req).
+
+%% Specify an invalid option and expect failure.
+send_bad_option_1(Config) ->
+ send_bad_option(Config, x).
+send_bad_option_2(Config) ->
+ send_bad_option(Config, {extra, false}).
+
+send_bad_option(Config, Opt) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+ try call(Config, Req, [Opt]) of
+ T -> erlang:error({?MODULE, ?LINE, T})
+ catch
+ error: _ -> ok
+ end.
+
+%% Specify an invalid filter and expect no matching peers.
+send_bad_filter_1(Config) ->
+ send_bad_filter(Config, {all, none}).
+send_bad_filter_2(Config) ->
+ send_bad_filter(Config, {host, x}).
+send_bad_filter_3(Config) ->
+ send_bad_filter(Config, {eval, fun() -> true end}).
+send_bad_filter_4(Config) ->
+ send_bad_filter(Config, {eval, {?MODULE, not_exported, []}}).
+
+send_bad_filter(Config, F) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+ {error, no_connection} = call(Config, Req, [{filter, F}]).
+
+%% Specify multiple filter options and expect them be conjunctive.
+send_multiple_filters_1(Config) ->
+ Fun = fun(#diameter_caps{}) -> true end,
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = send_multiple_filters(Config, [host, {eval, Fun}]).
+send_multiple_filters_2(Config) ->
+ E = {erlang, is_tuple, []},
+ {error, no_connection}
+ = send_multiple_filters(Config, [realm, {neg, {eval, E}}]).
+send_multiple_filters_3(Config) ->
+ E1 = [fun(#diameter_caps{}, ok) -> true end, ok],
+ E2 = {erlang, is_tuple, []},
+ E3 = {erlang, is_record, [diameter_caps]},
+ E4 = [{erlang, is_record, []}, diameter_caps],
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = send_multiple_filters(Config, [{eval, E} || E <- [E1,E2,E3,E4]]).
+
+send_multiple_filters(Config, Fs) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+ call(Config, Req, [{filter, F} || F <- Fs]).
+
+%% Ensure that we can pass a request in any form to diameter:call/4,
+%% only the return value from the prepare_request callback being
+%% significant.
+send_anything(Config) ->
+ #diameter_base_STA{'Result-Code' = ?SUCCESS}
+ = call(Config, anything).
+
+%% Remove the client transport and expect the server transport to
+%% go down.
remove_transports(Config) ->
{LRef, CRef} = proplists:get_value(transports, Config),
true = diameter:subscribe(?SERVER),
ok = diameter:remove_transport(?CLIENT, CRef),
{down, LRef, _, _}
= receive #diameter_event{service = ?SERVER, info = I} -> I
- after 5000 -> false
+ after 2000 -> false
end.
stop_services(_Config) ->
- {ok, ok} = {diameter:stop_service(?CLIENT), diameter:stop_service(?SERVER)}.
+ {ok, ok} = {diameter:stop_service(?CLIENT),
+ diameter:stop_service(?SERVER)}.
%% ===========================================================================
@@ -414,16 +560,27 @@ call(Config, Req, Opts) ->
msg(Req, Enc),
[{extra, [Name]} | Opts]).
-msg(L, list) ->
+msg([_|_] = L, list) ->
L;
msg([H|T], record) ->
- ?DICT:'#new-'(?DICT:msg2rec(H), T).
+ ?DICT:'#new-'(?DICT:msg2rec(H), T);
+msg(T, _) ->
+ T.
-set(L, Vs)
- when is_list(L) ->
- L ++ Vs;
+%% Set only values that aren't already.
+set([H|T], Vs) ->
+ [H | Vs ++ T];
set(Rec, Vs) ->
- ?DICT:'#set-'(Vs, Rec).
+ lists:foldl(fun({F,_} = FV, A) -> set(?DICT:'#get-'(F, A), FV, A) end,
+ Rec,
+ Vs).
+
+set(E, FV, Rec)
+ when E == undefined;
+ E == [] ->
+ ?DICT:'#set-'(FV, Rec);
+set(_, _, Rec) ->
+ Rec.
%% ===========================================================================
%% diameter callbacks
@@ -452,8 +609,11 @@ pick_peer([Peer], _, ?CLIENT, _State, send_detach, {_,_}) ->
%% prepare_request/4/5
+prepare_request(_Pkt, ?CLIENT, {_Ref, _Caps}, send_discard) ->
+ {discard, unprepared};
+
prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) ->
- prepare(Pkt, Caps, Name).
+ {send, prepare(Pkt, Caps, Name)}.
prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, _) ->
{send, prepare(Pkt, Caps)}.
@@ -463,64 +623,65 @@ prepare(Pkt, Caps, send_unsupported) ->
#diameter_packet{bin = <<H:5/binary, _CmdCode:3/binary, T/binary>>}
= E
= diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}),
- {send, E#diameter_packet{bin = <<H/binary, 42:24/integer, T/binary>>}};
+ E#diameter_packet{bin = <<H/binary, 42:24/integer, T/binary>>};
prepare(Pkt, Caps, send_unsupported_app) ->
Req = prepare(Pkt, Caps),
#diameter_packet{bin = <<H:8/binary, _ApplId:4/binary, T/binary>>}
= E
= diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req}),
- {send, E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>}};
+ E#diameter_packet{bin = <<H/binary, 42:32/integer, T/binary>>};
-prepare(Pkg, Caps, send_error_bit) ->
- #diameter_packet{header = Hdr} = Pkg,
- {send, Pkg#diameter_packet{header = Hdr#diameter_header{is_error = true},
- msg = prepare(Pkg, Caps)}};
+prepare(Pkt, Caps, send_error_bit) ->
+ #diameter_packet{header = Hdr} = Pkt,
+ Pkt#diameter_packet{header = Hdr#diameter_header{is_error = true},
+ msg = prepare(Pkt, Caps)};
-prepare(Pkg, Caps, send_unsupported_version) ->
- #diameter_packet{header = Hdr} = Pkg,
- {send, Pkg#diameter_packet{header = Hdr#diameter_header{version = 42},
- msg = prepare(Pkg, Caps)}};
+prepare(Pkt, Caps, send_unsupported_version) ->
+ #diameter_packet{header = Hdr} = Pkt,
+ Pkt#diameter_packet{header = Hdr#diameter_header{version = 42},
+ msg = prepare(Pkt, Caps)};
-prepare(_Pkg, _Caps, send_discard) ->
- {discard, unprepared};
+prepare(Pkt, Caps, send_anything) ->
+ Req = ['STR', {'Termination-Cause', ?LOGOUT}],
+ prepare(Pkt#diameter_packet{msg = Req}, Caps);
-prepare(Pkg, Caps, _Name) ->
- {send, prepare(Pkg, Caps)}.
+prepare(Pkt, Caps, _Name) ->
+ prepare(Pkt, Caps).
prepare(#diameter_packet{msg = Req}, Caps)
when is_record(Req, diameter_base_ACR);
'ACR' == hd(Req) ->
- #diameter_caps{origin_host = {OH, _},
+ #diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
+ {'Origin-Host', OH},
{'Origin-Realm', OR},
{'Destination-Realm', DR}]);
prepare(#diameter_packet{msg = Req}, Caps)
when is_record(Req, diameter_base_ASR);
'ASR' == hd(Req) ->
- #diameter_caps{origin_host = {OH, DH},
+ #diameter_caps{origin_host = {OH, DH},
origin_realm = {OR, DR}}
= Caps,
set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
+ {'Origin-Host', OH},
{'Origin-Realm', OR},
- {'Destination-Host', DH},
+ {'Destination-Host', DH},
{'Destination-Realm', DR},
{'Auth-Application-Id', ?APP_ID}]);
prepare(#diameter_packet{msg = Req}, Caps)
when is_record(Req, diameter_base_STR);
'STR' == hd(Req) ->
- #diameter_caps{origin_host = {OH, _},
+ #diameter_caps{origin_host = {OH, _},
origin_realm = {OR, DR}}
= Caps,
set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
+ {'Origin-Host', OH},
{'Origin-Realm', OR},
{'Destination-Realm', DR},
{'Auth-Application-Id', ?APP_ID}]);
@@ -532,9 +693,9 @@ prepare(#diameter_packet{msg = Req}, Caps)
origin_realm = {OR, DR}}
= Caps,
set(Req, [{'Session-Id', diameter:session_id(OH)},
- {'Origin-Host', OH},
+ {'Origin-Host', OH},
{'Origin-Realm', OR},
- {'Destination-Host', DH},
+ {'Destination-Host', DH},
{'Destination-Realm', DR},
{'Auth-Application-Id', ?APP_ID}]).
@@ -590,8 +751,22 @@ request(#diameter_packet{msg = #diameter_base_ASR{'Session-Id' = SId,
'Origin-Realm' = OR,
'AVP' = Avps}};
-request(#diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = ?LOGOUT,
- 'Session-Id' = SId}},
+request(#diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = T}},
+ _Caps)
+ when T /= ?LOGOUT ->
+ discard;
+
+request(#diameter_packet{msg = #diameter_base_STR{'Destination-Realm'= R}},
+ #diameter_caps{origin_realm = {OR, _}})
+ when R /= undefined, R /= OR ->
+ {protocol_error, ?REALM_NOT_SERVED};
+
+request(#diameter_packet{msg = #diameter_base_STR{'Destination-Host'= [H]}},
+ #diameter_caps{origin_host = {OH, _}})
+ when H /= OH ->
+ {protocol_error, ?UNABLE_TO_DELIVER};
+
+request(#diameter_packet{msg = #diameter_base_STR{'Session-Id' = SId}},
#diameter_caps{origin_host = {OH, _},
origin_realm = {OR, _}}) ->
{reply, #diameter_base_STA{'Result-Code' = ?SUCCESS,
@@ -599,9 +774,6 @@ request(#diameter_packet{msg = #diameter_base_STR{'Termination-Cause' = ?LOGOUT,
'Origin-Host' = OH,
'Origin-Realm' = OR}};
-request(#diameter_packet{msg = #diameter_base_STR{}}, _Caps) ->
- discard;
-
request(#diameter_packet{msg = #diameter_base_RAR{}}, _Caps) ->
receive after 2000 -> ok end,
{protocol_error, ?TOO_BUSY}.