aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2017-04-10 15:28:43 +0200
committerAnders Svensson <[email protected]>2017-06-11 16:30:37 +0200
commit618acfe5dcb0aa3d8ec0101704cd8fc774ac6c90 (patch)
treed85e23ff6cb82501d25d926198d0fa3490cfbe21 /lib
parentc02abb8185bc9c33b93d13cd720539e5bf5e2fad (diff)
downloadotp-618acfe5dcb0aa3d8ec0101704cd8fc774ac6c90.tar.gz
otp-618acfe5dcb0aa3d8ec0101704cd8fc774ac6c90.tar.bz2
otp-618acfe5dcb0aa3d8ec0101704cd8fc774ac6c90.zip
Deal with SCTP association id quirk on Solaris
In particular, that the association id received in messages on a one-to-one socket after peeloff may be different from the id received on the listen socket at comm_up. This seems odd, since it's then not possible to send until the id is discovered by reception of an SCTP message containing it, but it's unclear if this is a bug or a feature, or if it's specific to certain platforms. Treat it as a feature in this commit, and get the association id as mentioned, an incoming CER being expected before anything is sent. Commit da3e5d67 has more history.
Diffstat (limited to 'lib')
-rw-r--r--lib/diameter/src/transport/diameter_sctp.erl22
-rw-r--r--lib/diameter/test/diameter_gen_sctp_SUITE.erl58
2 files changed, 55 insertions, 25 deletions
diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl
index f9feb68874..2059a0e029 100644
--- a/lib/diameter/src/transport/diameter_sctp.erl
+++ b/lib/diameter/src/transport/diameter_sctp.erl
@@ -88,7 +88,9 @@
%% {RAs, RP, Errors}
| connect,
socket :: gen_sctp:sctp_socket() | undefined,
- assoc_id :: gen_sctp:assoc_id(), %% association identifier
+ assoc_id :: gen_sctp:assoc_id() %% association identifier
+ | undefined
+ | true,
peer :: {[inet:ip_address()], uint()} %% {RAs, RP}
| undefined,
streams :: {uint(), uint()} %% {InStream, OutStream} counts
@@ -676,7 +678,9 @@ recv({_, #sctp_assoc_change{state = comm_up,
= S) ->
Ref = getr(?REF_KEY),
publish(T, Ref, Id, Sock),
- up(S#transport{assoc_id = Id,
+ %% Deal with different association id after peeloff on Solaris by
+ %% taking the id from the first reception.
+ up(S#transport{assoc_id = T == accept orelse Id,
streams = {IS, OS}});
%% ... or not: try the next address.
@@ -691,16 +695,24 @@ recv({_, #sctp_assoc_change{} = E},
recv({_, #sctp_assoc_change{}}, _) ->
stop;
+%% First inbound on an accepting transport.
+recv({[#sctp_sndrcvinfo{assoc_id = Id}], _Bin}
+ = T,
+ #transport{assoc_id = true}
+ = S) ->
+ recv(T, S#transport{assoc_id = Id});
+
%% Inbound Diameter message.
-recv({[#sctp_sndrcvinfo{stream = Id}], Bin}, #transport{parent = Pid})
+recv({[#sctp_sndrcvinfo{stream = Id}], Bin}, #transport{parent = Pid} = S)
when is_binary(Bin) ->
diameter_peer:recv(Pid, #diameter_packet{transport_data = {stream, Id},
bin = Bin}),
- ok;
+ S;
recv({_, #sctp_shutdown_event{assoc_id = A}},
#transport{assoc_id = Id})
- when A == Id;
+ when Id;
+ A == Id;
A == 0 ->
stop;
diff --git a/lib/diameter/test/diameter_gen_sctp_SUITE.erl b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
index 457d9a0a85..d76d2bdbd3 100644
--- a/lib/diameter/test/diameter_gen_sctp_SUITE.erl
+++ b/lib/diameter/test/diameter_gen_sctp_SUITE.erl
@@ -228,33 +228,49 @@ accept_loop(Sock, MRef) ->
%% Server process that answers incoming messages as long as the parent
%% lives.
-assoc(MRef, Id, OS)
+assoc(MRef, _Id, OS)
when is_reference(MRef) ->
{peeloff, Sock} = receive T -> T end,
- recv_loop(Sock, Id, sender(Sock, Id, OS), MRef).
+ recv_loop(Sock, false, sender(Sock, false, OS), MRef).
%% recv_loop/4
recv_loop(Sock, Id, Pid, MRef) ->
ok = inet:setopts(Sock, [{active, once}]),
- receive
- ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = I}], B})
- when is_binary(B) ->
- T2 = diameter_lib:now(),
- I = Id, %% assert
- <<?MAGIC, Bin/binary>> = B, %% assert
- {[_,_,_,Sz] = L, Bytes} = unmark(Bin),
- Sz = size(Bin) - Bytes, %% assert
- <<_:Bytes/binary, Body:Sz/binary>> = Bin,
- send(Pid, [T2|L], Body), %% answer
- recv_loop(Sock, Id, Pid, MRef);
- ?SCTP(Sock, _) ->
- recv_loop(Sock, Id, Pid, MRef);
- {'DOWN', MRef, process, _, Reason} ->
- Reason;
- T ->
- error(T)
- end.
+ recv(Sock, Id, Pid, MRef, receive T -> T end).
+
+%% recv/5
+
+%% Association id can change on a peeloff socket on some versions of
+%% Solaris.
+recv(Sock,
+ false,
+ Pid,
+ MRef,
+ ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = Id}], _})
+ = T) ->
+ Pid ! {assoc_id, Id},
+ recv(Sock, Id, Pid, MRef, T);
+
+recv(Sock, Id, Pid, MRef, ?SCTP(Sock, {[#sctp_sndrcvinfo{assoc_id = I}], B}))
+ when is_binary(B) ->
+ T2 = diameter_lib:now(),
+ Id = I, %% assert
+ <<?MAGIC, Bin/binary>> = B, %% assert
+ {[_,_,_,Sz] = L, Bytes} = unmark(Bin),
+ Sz = size(Bin) - Bytes, %% assert
+ <<_:Bytes/binary, Body:Sz/binary>> = Bin,
+ send(Pid, [T2|L], Body), %% answer
+ recv_loop(Sock, Id, Pid, MRef);
+
+recv(Sock, Id, Pid, MRef, ?SCTP(Sock, _)) ->
+ recv_loop(Sock, Id, Pid, MRef);
+
+recv(_, _, _, MRef, {'DOWN', MRef, process, _, Reason}) ->
+ Reason;
+
+recv(_, _, _, _, T) ->
+ error(T).
%% send/3
@@ -273,6 +289,8 @@ sender(Sock, Id, OS) ->
send_loop(Sock, Id, OS, N, MRef) ->
receive
+ {assoc_id, I} ->
+ send_loop(Sock, I, OS, N, MRef);
{send, L, Body} ->
Stream = N rem OS,
ok = send(Sock, Id, Stream, mark(Body, [N, Stream | L])),