diff options
Diffstat (limited to 'lib/kernel/test/gen_sctp_SUITE.erl')
-rw-r--r-- | lib/kernel/test/gen_sctp_SUITE.erl | 678 |
1 files changed, 533 insertions, 145 deletions
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index 65183d83cc..a77e9cb856 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -21,6 +21,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/inet_sctp.hrl"). +-include("kernel_test_lib.hrl"). %%-compile(export_all). @@ -41,7 +42,8 @@ peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1, buffers/1, names_unihoming_ipv4/1, names_unihoming_ipv6/1, - names_multihoming_ipv4/1, names_multihoming_ipv6/1]). + names_multihoming_ipv4/1, names_multihoming_ipv6/1, + recv_close/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -56,10 +58,25 @@ all() -> {group,G}]. groups() -> - [{smoke,[],[basic,basic_stream]}, - {old_solaris,[],[skip_old_solaris]}, - {extensive,[], - [api_open_close, api_listen, api_connect_init, + [ + {smoke, [], smoke_cases()}, + {old_solaris, [], old_solaris_cases()}, + {extensive, [], extensive_cases()} + ]. + +smoke_cases() -> + [ + basic, + basic_stream + ]. + +old_solaris_cases() -> + [ + skip_old_solaris + ]. + +extensive_cases() -> + [api_open_close, api_listen, api_connect_init, api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6, open_multihoming_ipv4_socket, open_unihoming_ipv6_socket, @@ -68,7 +85,8 @@ groups() -> xfer_stream_min, peeloff_active_once, peeloff_active_true, peeloff_active_n, buffers, names_unihoming_ipv4, names_unihoming_ipv6, - names_multihoming_ipv4, names_multihoming_ipv6]}]. + names_multihoming_ipv4, names_multihoming_ipv6, + recv_close]. init_per_suite(_Config) -> case gen_sctp:open() of @@ -161,8 +179,8 @@ xfer_min(Config) when is_list(Config) -> case log_ok(gen_sctp:recv(Sb, infinity)) of {Loopback, Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], + [#sctp_sndrcvinfo{stream = Stream, + assoc_id = SbAssocId}], Data} -> ok; Event1 -> case recv_event(Event1) of @@ -539,20 +557,6 @@ getopt(S, Opt, Param) -> setopt(S, Opt, Val) -> inet:setopts(S, [{Opt,Val}]). -log_ok(X) -> log(ok(X)). - -ok({ok,X}) -> X. - -err([], Result) -> - erlang:error(Result); -err([Reason|_], {error,Reason}) -> - ok; -err([_|Reasons], Result) -> - err(Reasons, Result). - -log(X) -> - io:format("LOG[~w]: ~p~n", [self(),X]), - X. flush() -> receive @@ -696,24 +700,24 @@ api_connect_init(Config) when is_list(Config) -> ok = gen_sctp:close(Sb), ok. -recv_event({Addr,Port,[],#sctp_assoc_change{}=AssocChange}) -> - {Addr,Port,AssocChange}; -recv_event({Addr,Port, - [#sctp_sndrcvinfo{assoc_id=Assoc}], - #sctp_assoc_change{assoc_id=Assoc}=AssocChange}) -> - {Addr,Port,AssocChange}; -recv_event({Addr,Port,[],#sctp_paddr_change{}=PaddrChange}) -> - {Addr,Port,PaddrChange}; +recv_event({Addr, Port, [], #sctp_assoc_change{} = AssocChange}) -> + {Addr, Port, AssocChange}; recv_event({Addr,Port, - [#sctp_sndrcvinfo{assoc_id=Assoc}], - #sctp_paddr_change{assoc_id=Assoc}=PaddrChange}) -> - {Addr,Port,PaddrChange}; -recv_event({Addr,Port,[],#sctp_shutdown_event{}=ShutdownEvent}) -> - {Addr,Port,ShutdownEvent}; -recv_event({Addr,Port, - [#sctp_sndrcvinfo{assoc_id=Assoc}], - #sctp_shutdown_event{assoc_id=Assoc}=ShutdownEvent}) -> - {Addr,Port,ShutdownEvent}. + [#sctp_sndrcvinfo{assoc_id = Assoc}], + #sctp_assoc_change{assoc_id = Assoc} = AssocChange}) -> + {Addr, Port, AssocChange}; +recv_event({Addr, Port, [], #sctp_paddr_change{} = PaddrChange}) -> + {Addr, Port, PaddrChange}; +recv_event({Addr, Port, + [#sctp_sndrcvinfo{assoc_id = Assoc}], + #sctp_paddr_change{assoc_id = Assoc} = PaddrChange}) -> + {Addr, Port, PaddrChange}; +recv_event({Addr, Port, [], #sctp_shutdown_event{} = ShutdownEvent}) -> + {Addr, Port, ShutdownEvent}; +recv_event({Addr, Port, + [#sctp_sndrcvinfo{assoc_id = Assoc}], + #sctp_shutdown_event{assoc_id = Assoc} = ShutdownEvent}) -> + {Addr, Port, ShutdownEvent}. %% Test socket options. api_opts(Config) when is_list(Config) -> @@ -730,72 +734,165 @@ api_opts(Config) when is_list(Config) -> ok = inet:setopts(S, [{sndbuf,Sndbuf}]), ok = inet:setopts(S, [{recbuf,Recbuf}]), case inet:getopts(S, [sndbuf]) of - {ok,[{sndbuf,SB}]} when SB >= Sndbuf -> ok + {ok, [{sndbuf,SB}]} when SB >= Sndbuf -> ok end, case inet:getopts(S, [recbuf]) of - {ok,[{recbuf,RB}]} when RB >= Recbuf -> ok + {ok, [{recbuf, RB}]} when (RB >= Recbuf) -> ok end. +%% What is this *actually* supposed to test? implicit_inet6(Config) when is_list(Config) -> - Hostname = log_ok(inet:gethostname()), + ?TC_TRY(implicit_inet6, fun() -> do_implicit_inet6(Config) end). + +do_implicit_inet6(_Config) -> + ?P("begin"), + %% First + ?P("try create server socket (1)"), case gen_sctp:open(0, [inet6]) of - {ok,S1} -> - case inet:getaddr(Hostname, inet6) of - {ok,Host} -> - Loopback = {0,0,0,0,0,0,0,1}, - io:format("~s ~p~n", ["Loopback",Loopback]), - implicit_inet6(S1, Loopback), - ok = gen_sctp:close(S1), - %% - Localhost = - log_ok(inet:getaddr("localhost", inet6)), - io:format("~s ~p~n", ["localhost",Localhost]), - S2 = - log_ok(gen_sctp:open(0, [{ip,Localhost}])), - implicit_inet6(S2, Localhost), - ok = gen_sctp:close(S2), - %% - io:format("~s ~p~n", [Hostname,Host]), - S3 = - log_ok(gen_sctp:open(0, [{ifaddr,Host}])), - implicit_inet6(S3, Host), - ok = gen_sctp:close(S1); - {error,eafnosupport} -> - ok = gen_sctp:close(S1), - {skip,"Can not look up IPv6 address"} - end; - _ -> - {skip,"IPv6 not supported"} + {ok, S1} -> + Loopback = {0,0,0,0,0,0,0,1}, + ?P("*** ~s: ~p ***", ["Loopback", Loopback]), + implicit_inet6(S1, Loopback), + ok = gen_sctp:close(S1), + + %% Second + ?P("try create server socket (2)"), + Localhost = log_ok(inet:getaddr("localhost", inet6)), + S2 = log_ok(gen_sctp:open(0, [{ip,Localhost}])), + ?P("*** ~s: ~p ***", ["localhost", Localhost]), + implicit_inet6(S2, Localhost), + ok = gen_sctp:close(S2), + + %% Third + ?P("try create server socket (3)"), + Hostname = log_ok(inet:gethostname()), + Addr = case inet:getaddr(Hostname, inet6) of + {ok, A} -> + A; + {error, eafnosupport} -> + ok = gen_sctp:close(S1), + ?SKIPT("Can not look up IPv6 address") + end, + S3 = log_ok(gen_sctp:open(0, [{ifaddr, Addr}])), + ?P("*** ~s: ~p ***", [Hostname, Addr]), + implicit_inet6(S3, Addr), + ok = gen_sctp:close(S1), + ?P("done"), + ok; + {error, eaddrnotavail = Reason} -> + ?SKIPT(open_failed_str(Reason)); + _ -> + {skip, "IPv6 not supported"} end. + implicit_inet6(S1, Addr) -> + ?P("make (server) listen socket"), ok = gen_sctp:listen(S1, true), - P1 = log_ok(inet:port(S1)), - S2 = log_ok(gen_sctp:open(0, [inet6])), - P2 = log_ok(inet:port(S2)), - #sctp_assoc_change{state=comm_up} = - log_ok(gen_sctp:connect(S2, Addr, P1, [])), - case recv_event(log_ok(gen_sctp:recv(S1))) of - {Addr,P2,#sctp_assoc_change{state=comm_up}} -> - ok; - {Addr,P2,#sctp_paddr_change{state=addr_confirmed, - addr={Addr,P2}, - error=0}} -> - {Addr,P2,#sctp_assoc_change{state=comm_up}} = - recv_event(log_ok(gen_sctp:recv(S1))) + ServerPortNo = log_ok(inet:port(S1)), + ?P("try create (client) socket"), + S2 = case gen_sctp:open(0, [inet6, {ifaddr, Addr}]) of + {ok, Sock} -> + ?P("client socket created: ~p", [Sock]), + Sock; + {error, eaddrnotavail = Reason} -> + ?P("could not create (client) socket with ifaddr: " + "~n ~p", [Addr]), + ?SKIPT(open_failed_str(Reason)) end, + {ClientAddr, ClientPortNo} = log_ok(inet:sockname(S2)), + ?P("try connect" + "~n from (connector): ~p, ~p (~p)" + "~n to: ~p, ~p", + [ClientAddr, ClientPortNo, S2, Addr, ServerPortNo]), + #sctp_assoc_change{state = comm_up} = + log_ok(gen_sctp:connect(S2, Addr, ServerPortNo, [])), + ?P("connect success: await events"), + implicit_inet6_await_ac_comm_up(S1, ClientAddr, ClientPortNo), + ?P("verify server sockname"), case log_ok(inet:sockname(S1)) of - {Addr,P1} -> ok; - {{0,0,0,0,0,0,0,0},P1} -> ok + {Addr, ServerPortNo} -> ok; + {{0,0,0,0,0,0,0,0}, ServerPortNo} -> ok end, + ?P("verify client sockname"), case log_ok(inet:sockname(S2)) of - {Addr,P2} -> ok; - {{0,0,0,0,0,0,0,0},P2} -> ok + {Addr, ClientPortNo} -> ok; + {{0,0,0,0,0,0,0,0}, ClientPortNo} -> ok end, - ok = gen_sctp:close(S2). + ?P("client client socket"), + ok = gen_sctp:close(S2), + ?P("verification complete"), + ok. + + +implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo) -> + {_OsFam, OsName} = os:type(), + implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName). + +implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName) -> + case recv_event(log_ok(gen_sctp:recv(Sock))) of + {Addr, PortNo, #sctp_assoc_change{state = comm_up}} -> + ?P("received assoc-change:comm-up event => done"), + ok; + {Addr, PortNo, #sctp_paddr_change{state = addr_confirmed, + addr = {Addr, PortNo}, + error = 0}} -> + ?P("received paddr-change:addr-confirmed event - " + "try recv assoc-change:comm-up"), + implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName); + + {Addr2, PortNo2, #sctp_assoc_change{state = comm_up}} + when (OsName =:= freebsd) -> + ?P("Expected (assoc-change:comm-up) event from unexpected address: " + "~n Unexpected Address: ~p, ~p" + "~n Expected Address: ~p, ~p" + "~n => RETRY" + "~n", [Addr2, PortNo2, Addr, PortNo]), + implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName); + {Addr2, PortNo2, #sctp_paddr_change{state = addr_confirmed}} + when (OsName =:= freebsd) -> + ?P("Expected paddr-change:addr-confirmed event from " + "UNEXPECTED ADDRESS: " + "~n UNEXPECTED Address: ~p, ~p" + "~n Expected Address: ~p, ~p" + "~n => RETRY" + "~n", [Addr2, PortNo2, Addr, PortNo]), + implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName); + + {Addr2, PortNo2, #sctp_assoc_change{state = comm_up} = CX} = UNEX -> + ?P("Expected (assoc-change:comm-up) event from UNEXPECTED ADDRESS: " + "~n UNEXPECTED Address: ~p, ~p" + "~n Expected Address: ~p, ~p" + "~n Assoc Change: ~p" + "~n", [Addr2, PortNo2, Addr, PortNo, CX]), + exit({unexpected_event, UNEX}); + + {AX, PX, #sctp_paddr_change{state = addr_confirmed} = CX} = UNEX -> + ?P("Expected paddr-change:addr-confirmed event from " + "UNEXPECTED ADDRESS: " + "~n UNEXPECTED Address: ~p, ~p" + "~n Expected Address: ~p, ~p" + "~n PAddr Change: ~p" + "~n", [AX, PX, Addr, PortNo, CX]), + exit({unexpected_event, UNEX}); + + {AX, PX, CX} = UNEX -> + ?P("UNEXPECTED EVENT: " + "~n ~p" + "~n UNEXPECTED ADDRESS: ~p, ~p" + "~n Expected Address: ~p, ~p" + "~n", [CX, AX, PX, Addr, PortNo]), + exit({unexpected_event, UNEX}) + end. -%% Verify {active,N} socket management. + +%% Verify {active, N} socket management. +%% This is difficult to do since we do not just receive data messages. +%% Also, how do we know that sctp behaves the same way on all platforms? active_n(Config) when is_list(Config) -> + ?TC_TRY(active_n, fun() -> do_active_n(Config) end). + +do_active_n(_Config) -> N = 3, S1 = ok(gen_sctp:open([{active,N}])), [{active,N}] = ok(inet:getopts(S1, [active])), @@ -851,33 +948,8 @@ active_n(Config) when is_list(Config) -> S2 = ok(gen_sctp:open(0, [{active,false}])), Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])), ok = inet:setopts(S1, [{active,N}]), - [{active,N}] = ok(inet:getopts(S1, [active])), - LoopFun = fun(Count, Count, _Fn) -> - receive - {sctp_passive,S1} -> - ok - after - 5000 -> - exit({error,timeout}) - end; - (I, Count, Fn) -> - Msg = list_to_binary("message "++integer_to_list(I)), - ok = gen_sctp:send(S2, Assoc, 0, Msg), - receive - {sctp,S1,_,_,{[SR],Msg}} when is_record(SR, sctp_sndrcvinfo) -> - Fn(I+1, Count, Fn); - {sctp,S1,_,_,_} -> - %% ignore non-data messages - ok = inet:setopts(S1, [{active,1}]), - Fn(I, Count, Fn); - Other -> - exit({unexpected, Other}) - after - 5000 -> - exit({error,timeout}) - end - end, - ok = LoopFun(1, N, LoopFun), + active_n_flush_connect_msgs(S1), + active_n_send_loop(N, S2, Assoc, S1), S3 = ok(gen_sctp:open([{active,0}])), receive {sctp_passive,S3} -> @@ -891,6 +963,122 @@ active_n(Config) when is_list(Config) -> ok = gen_sctp:close(S1), ok. + +%% There is no way to know how many addresses this host has, +%% and if there is "too many" (more then N = 3), then the +%% socket may already be passive. In this case the send- +%% loop will fail. +%% So, if we get a passive-message here, we just give up (=skip). +active_n_flush_connect_msgs(Sock) -> + %% This seems only to be needed on certain platforms + active_n_flush_connect_msgs(os:type(), Sock). + +active_n_flush_connect_msgs(_, Sock) -> + do_active_n_flush_connect_msgs(Sock). +%% active_n_flush_connect_msgs({unix, freebsd}, Sock) -> +%% do_active_n_flush_connect_msgs(Sock); +%% active_n_flush_connect_msgs(_, _) -> +%% ok. + +do_active_n_flush_connect_msgs(Sock) -> + receive + {sctp_passive, Sock} -> + ?P("connect-flush-loop -> premature passive"), + ?SKIPT("Too many addresses (premature passive)"); + + {sctp, Sock, + _FromIP, _FromPort, + {[], #sctp_assoc_change{state = comm_up}}} -> + ?P("connect-flush-loop -> " + "connect message discard - assoc change : comm-up"), + ok = inet:setopts(Sock, [{active, 1}]), + do_active_n_flush_connect_msgs(Sock); + + {sctp, Sock, + _FromIP, _FromPort, + {[], #sctp_paddr_change{state = addr_confirmed, + addr = Addr, + error = Error, + assoc_id = AID}}} -> + ?P("connect-flush-loop -> " + "connect message discard - paddr change : addr-confirmed:" + "~n Addr: ~p" + "~n Error: ~p" + "~n AssocID: ~p", [Addr, Error, AID]), + ok = inet:setopts(Sock, [{active, 1}]), + do_active_n_flush_connect_msgs(Sock) + + after 5000 -> + ok + end. + +active_n_send_loop(Count, SrcSock, SndAssoc, DstSock) -> + active_n_send_loop(0, Count, SrcSock, SndAssoc, DstSock). + +active_n_send_loop(Count, Count, _SndSock, _SndAssoc, RcvSock) -> + ?P("send-loop -> we are done - wait for passive"), + receive + {sctp_passive, RcvSock} -> + ?P("received passive"), + ok + after + 5000 -> + ?P("UNEXPECTED TIMEOUT: " + "~n Message Queue: ~p" + "~n Active: ~p", + [process_info(self(), messages), + inet:getopts(RcvSock, [active])]), + exit({error, timeout}) + end; + +active_n_send_loop(Sent, Count, SndSock, SndAssoc, RcvSock) -> + Msg = list_to_binary("message " ++ integer_to_list(Sent+1)), + ?P("send-loop(~w,~w) -> send message (on ~p)", [Sent, Count, SndSock]), + ok = gen_sctp:send(SndSock, SndAssoc, 0, Msg), + receive + {sctp, RcvSock, FromIP, FromPort, {[SR], Msg}} + when is_record(SR, sctp_sndrcvinfo) -> + ?P("send-loop(~w,~w) -> " + "recv (expected) data message (on ~p):" + "~n Msg: ~p" + "~n From: ~p, ~p", + [Sent, Count, + RcvSock, Msg, FromIP, FromPort]), + active_n_send_loop(Sent+1, Count, SndSock, SndAssoc, RcvSock); + + {sctp, RcvSock, _FromIP, _FromPort, {_AncData, _Data}} -> + %% ignore non-data messages + %% we should not get any here because of the flush loop, + %% but just in case... + ?P("send-loop(~w,~w) -> " + "ignore non-data messages (on ~p):" + "~n From: ~p:~p" + "~n AncData: ~p" + "~n Data: ~p", + [Sent, Count, + RcvSock, _FromIP, _FromPort, _AncData, _Data]), + + %% It may be too late to update here, + %% the socket may already have gone passive + %% and generated a passive message! + + ok = inet:setopts(RcvSock, [{active, 1}]), + + active_n_send_loop(Sent, Count, SndSock, SndAssoc, RcvSock); + + Other -> + ?P("send-loop(~w,~w) -> " + "UNEXPECTED: " + "~n Other: ~p" + "~n Send Sock: ~p" + "~n Recv Sock: ~p", [Sent, Count, + Other, SndSock, RcvSock]), + exit({unexpected, Other}) + after + 5000 -> + exit({error,timeout}) + end. + %% Hello world stream socket. basic_stream(Config) when is_list(Config) -> {ok,S} = gen_sctp:open([{type,stream}]), @@ -903,6 +1091,7 @@ basic_stream(Config) when is_list(Config) -> %% Minimal data transfer. xfer_stream_min(Config) when is_list(Config) -> + {_, OSName} = os:type(), Stream = 0, Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, Loopback = {127,0,0,1}, @@ -961,33 +1150,92 @@ xfer_stream_min(Config) when is_list(Config) -> case log_ok(gen_sctp:recv(Sb, infinity)) of {Loopback, Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} -> ok; + [#sctp_sndrcvinfo{stream = Stream, + assoc_id = SbAssocId}], + Data} -> + ?P("[1] received expected data with ancillary data => done"), + ok; + {Loopback, - Pa,[], - #sctp_paddr_change{addr = {Loopback,_}, - state = addr_available, - error = 0, + Pa, + [], + #sctp_paddr_change{addr = {Loopback,_}, + state = addr_available, + error = 0, assoc_id = SbAssocId}} -> + ?P("[2] received paddr change => recv again"), + Res2 = log_ok(gen_sctp:recv(Sb, infinity)), + ?P("[2] recv ok => " + "~n ~p", [Res2]), {Loopback, Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} = log_ok(gen_sctp:recv(Sb, infinity)); + [#sctp_sndrcvinfo{stream = Stream, + assoc_id = SbAssocId}], + Data} = Res2, + ?P("[2] received expected data with ancillary data => done"), + Res2; + {Loopback, Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - #sctp_paddr_change{addr = {Loopback,_}, - state = addr_confirmed, - error = 0, + [#sctp_sndrcvinfo{stream = Stream, + assoc_id = SbAssocId}], + #sctp_paddr_change{addr = {Loopback,_}, + state = addr_confirmed, + error = 0, assoc_id = SbAssocId}} -> + ?P("[3] received paddr change with ancillary data => recv again"), + Res3 = log_ok(gen_sctp:recv(Sb, infinity)), + ?P("[3] recv ok => " + "~n ~p", [Res3]), {Loopback, Pa, - [#sctp_sndrcvinfo{stream=Stream, - assoc_id=SbAssocId}], - Data} = log_ok(gen_sctp:recv(Sb, infinity)) + [#sctp_sndrcvinfo{stream = Stream, + assoc_id = SbAssocId}], + Data} = Res3, + ?P("[3] received expected data with ancillary data => done"), + Res3; + + %% It seems that on FreeBSD (for instance) we don't get any + %% AncData with this. + {Loopback, + Pa, + [], + #sctp_paddr_change{addr = {Loopback,_}, + state = addr_confirmed, + error = 0, + assoc_id = SbAssocId}} when (OSName =:= freebsd) -> + ?P("[4] received paddr change without ancillary data => " + "recv again"), + Res4 = log_ok(gen_sctp:recv(Sb, infinity)), + ?P("[4] recv ok => " + "~n ~p", [Res4]), + {Loopback, + Pa, + [#sctp_sndrcvinfo{stream = Stream, + assoc_id = SbAssocId}], + Data} = Res4, + ?P("[4] received expected data with ancillary data => done"), + Res4; + + {FromIPX, FromPortX, AncDataX, DataX} = Other1 -> + ?P("UNEXPECTED: " + "~n FromIP: ~p" + "~n FromPort: ~p" + "~n AncData: ~p" + "~n Data: ~p" + "~nwhen" + "~n Loopback: ~p" + "~n Pa: ~p", + [FromIPX, FromPortX, AncDataX, DataX, Loopback, Pa]), + exit({unexpected, Other1}); + Other2 -> + ?P("UNEXPECTED: " + "~n Other: ~p" + "~nwhen" + "~n Loopback: ~p" + "~n Pa: ~p", + [Other2, Loopback, Pa]), + exit({unexpected, Other2}) end, ok = do_from_other_process( @@ -1394,11 +1642,12 @@ get_addrs_by_family(Family, NumAddrs) -> end; Os -> Reason = if Family =:= inet_and_inet6 -> - f("Mixing ipv4 and ipv6 addresses for multihoming " - " has not been verified on ~p", [Os]); + ?F("Mixing ipv4 and ipv6 addresses for " + " multihoming has not been verified on ~p", + [Os]); true -> - f("Multihoming for ~p has not been verified on ~p", - [Family, Os]) + ?F("Multihoming for ~p has not been verified " + "on ~p", [Family, Os]) end, {error, Reason} end. @@ -1407,19 +1656,19 @@ get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet; Family =:= inet6 -> case inet:getaddr(localhost, Family) of {error,eafnosupport} -> - {skip, f("No support for ~p", Family)}; + {skip, ?F("No support for ~p", Family)}; {ok, _} -> IfAddrs = ok(inet:getifaddrs()), case filter_addrs_by_family(IfAddrs, Family) of Addrs when length(Addrs) >= NumAddrs -> {ok, lists:sublist(Addrs, NumAddrs)}; [] -> - {error, f("Need ~p ~p address(es) found none~n", - [NumAddrs, Family])}; + {error, ?F("Need ~p ~p address(es) found none~n", + [NumAddrs, Family])}; Addrs -> {error, - f("Need ~p ~p address(es) found only ~p: ~p~n", - [NumAddrs, Family, length(Addrs), Addrs])} + ?F("Need ~p ~p address(es) found only ~p: ~p~n", + [NumAddrs, Family, length(Addrs), Addrs])} end end; get_addrs_by_family_aux(inet_and_inet6, NumAddrs) -> @@ -1451,9 +1700,6 @@ ipv4_map_addrs(InetAddrs) -> {0, 0, 0, 0, 0, 16#ffff, AB, CD} end || {A,B,C,D} <- InetAddrs]. -f(F, A) -> - lists:flatten(io_lib:format(F, A)). - do_open_and_connect(ServerAddresses, AddressToConnectTo) -> Fun = fun (_, _, _, _, _, _) -> ok end, do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun). @@ -1511,6 +1757,111 @@ recv_comm_up_eventually(S) -> recv_comm_up_eventually(S) end. + +%% +recv_close(Config) when is_list(Config) -> + ?P("create server socket (and listen)"), + {ok, S} = gen_sctp:open(), + gen_sctp:listen(S, true), + {ok, SPort} = inet:port(S), + + ?P("create client socket (and connect)"), + {ok, C} = gen_sctp:open(), + {ok, _} = gen_sctp:connect(C, localhost, SPort, []), + + TC = self(), + RECV = fun() -> + ?P("try setup recv(s)"), + ok = recv_close_setup_recv(S), + ?P("announce ready"), + TC ! {self(), ready}, + ?P("try data recv"), + Res = gen_sctp:recv(S), + ?P("recv res: " + "~n ~p", [Res]), + exit(Res) + end, + ?P("spawn reader - then await reader ready"), + {Pid, MRef} = spawn_monitor(RECV), + receive + {'DOWN', MRef, process, Pid, PreReason} -> + %% Make sure it does not die for some other reason... + ?P("unexpected reader termination:" + "~n ~p", [PreReason]), + (catch gen_sctp:close(S)), + (catch gen_sctp:close(C)), + ?line ct:fail("Unexpected pre close from reader (~p): ~p", + [Pid, PreReason]); + {Pid, ready} -> + ?P("reader ready"), + ok + after 30000 -> % Just in case... + %% This is **extreme**, but there is no way to know + %% how long it will take to iterate through all the + %% addresses of a host... + ?P("reader ready timeout"), + (catch gen_sctp:close(S)), + (catch gen_sctp:close(C)), + ?line ct:fail("Unexpected pre close timeout (~p)", [Pid]) + end, + + ?P("\"ensure\" reader reading..."), + receive + Any -> + ?P("Received unexpected message: " + "~n ~p", [Any]), + (catch gen_sctp:close(S)), + (catch gen_sctp:close(C)), + ?line ct:fail("Unexpected message: ~p", [Any]) + after 5000 -> + ok + end, + + ?P("close server socket"), + ok = gen_sctp:close(S), + ?P("await reader termination"), + receive + {'DOWN', MRef, process, Pid, {error, closed}} -> + ?P("expected reader termination result"), + (catch gen_sctp:close(C)), + ok; + {'DOWN', MRef, process, Pid, PostReason} -> + ?P("unexpected reader termination: " + "~n ~p", [PostReason]), + (catch gen_sctp:close(C)), + ?line ct:fail("Unexpected post close from reader (~p): ~p", + [Pid, PostReason]) + after 5000 -> + ?P("unexpected reader termination timeout"), + demonitor(MRef, [flush]), + (catch gen_sctp:close(C)), + exit(Pid, kill), + ?line ct:fail("Reader (~p) termination timeout", [Pid]) + end, + ?P("close client socket"), + (catch gen_sctp:close(C)), + ?P("done"), + ok. + + +recv_close_setup_recv(S) -> + recv_close_setup_recv(S, 1). + +recv_close_setup_recv(S, N) -> + ?P("try setup recv ~w", [N]), + case gen_sctp:recv(S, 5000) of + {ok, {Addr, + Port, + _AncData, + Data}} when is_tuple(Addr) andalso is_integer(Port) -> + ?P("setup recv ~w: " + "~n ~p", [N, Data]), + recv_close_setup_recv(S, N+1); + {error, timeout} -> + ok + end. + + %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% socket gen_server ultra light @@ -1743,5 +2094,42 @@ match_unless_solaris(A, B) -> _ -> A = B end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + timestamp() -> erlang:monotonic_time(). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +log_ok(X) -> + log(ok(X)). + +ok({ok, X}) -> + X; +ok({error, X}) -> + ?P("ERROR: ~p", [X]), + ?line ct:fail({unexpected_error, X}); +ok(X) -> + ?P("UNEXPECTED: ~p", [X]), + ?line ct:fail({unexpected, X}). + + +log(X) -> + ?P("LOG: ~p", [X]), + X. + +err([], Result) -> + erlang:error(Result); +err([Reason|_], {error,Reason}) -> + ok; +err([_|Reasons], Result) -> + err(Reasons, Result). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +open_failed_str(Reason) -> + ?F("Open failed: ~w", [Reason]). + |