1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(gen_sctp_SUITE).
21
22-include_lib("common_test/include/ct.hrl").
23-include_lib("kernel/include/inet_sctp.hrl").
24
25%%-compile(export_all).
26
27-export([all/0, suite/0,groups/0,
28	 init_per_suite/1,end_per_suite/1,
29	 init_per_group/2,end_per_group/2,
30	 init_per_testcase/2, end_per_testcase/2]).
31-export(
32   [skip_old_solaris/1,
33    basic/1,
34    api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
35    xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
36    open_multihoming_ipv4_socket/1,
37    open_unihoming_ipv6_socket/1,
38    open_multihoming_ipv6_socket/1,
39    open_multihoming_ipv4_and_ipv6_socket/1,
40    basic_stream/1, xfer_stream_min/1, active_n/1,
41    peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1,
42    buffers/1,
43    names_unihoming_ipv4/1, names_unihoming_ipv6/1,
44    names_multihoming_ipv4/1, names_multihoming_ipv6/1]).
45
46suite() ->
47    [{ct_hooks,[ts_install_cth]},
48     {timetrap,{minutes,1}}].
49
50all() ->
51    G = case is_old_solaris() of
52	    true -> old_solaris;
53	    false -> extensive
54	end,
55    [{group,smoke},
56     {group,G}].
57
58groups() ->
59    [{smoke,[],[basic,basic_stream]},
60     {old_solaris,[],[skip_old_solaris]},
61     {extensive,[],
62      [api_open_close, api_listen, api_connect_init,
63       api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
64       open_multihoming_ipv4_socket,
65       open_unihoming_ipv6_socket,
66       open_multihoming_ipv6_socket,
67       open_multihoming_ipv4_and_ipv6_socket, active_n,
68       xfer_stream_min, peeloff_active_once,
69       peeloff_active_true, peeloff_active_n, buffers,
70       names_unihoming_ipv4, names_unihoming_ipv6,
71       names_multihoming_ipv4, names_multihoming_ipv6]}].
72
73init_per_suite(_Config) ->
74    case gen_sctp:open() of
75	{ok,Socket} ->
76	    gen_sctp:close(Socket),
77	    [];
78	{error,Error}
79	  when Error =:= eprotonosupport;
80	       Error =:= esocktnosupport ->
81	    {skip,"SCTP not supported on this machine"}
82    end.
83
84end_per_suite(_Config) ->
85    ok.
86
87init_per_group(_GroupName, Config) ->
88    Config.
89
90end_per_group(_GroupName, Config) ->
91    Config.
92
93
94init_per_testcase(_Func, Config) ->
95    Config.
96
97end_per_testcase(_Func, _Config) ->
98    ok.
99
100
101-define(LOGVAR(Var), begin io:format(??Var" = ~p~n", [Var]) end).
102
103is_old_solaris() ->
104    os:type() =:= {unix,sunos} andalso os:version() < {5,12,0}.
105
106skip_old_solaris(_Config) ->
107    {skip,"Unreliable test cases and/or implementation on old Solaris"}.
108
109%% Hello world.
110basic(Config) when is_list(Config) ->
111    {ok,S} = gen_sctp:open(),
112    ok = gen_sctp:close(S),
113    ok.
114
115%% Minimal data transfer.
116xfer_min(Config) when is_list(Config) ->
117    Stream = 0,
118    Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
119    Loopback = {127,0,0,1},
120    StatOpts =
121	[recv_avg,recv_cnt,recv_max,recv_oct,
122	 send_avg,send_cnt,send_max,send_oct],
123    {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
124    {ok,SbStat1} = inet:getstat(Sb, StatOpts),
125    {ok,Pb} = inet:port(Sb),
126    ok = gen_sctp:listen(Sb, true),
127
128    {ok,Sa} = gen_sctp:open(),
129    {ok,Pa} = inet:port(Sa),
130    {ok,#sctp_assoc_change{state=comm_up,
131			   error=0,
132			   outbound_streams=SaOutboundStreams,
133			   inbound_streams=SaInboundStreams,
134			   assoc_id=SaAssocId}=SaAssocChange} =
135	gen_sctp:connect(Sa, Loopback, Pb, []),
136    {SbAssocId,SaOutboundStreams,SaInboundStreams} =
137	case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
138	    {Loopback,Pa,
139	     #sctp_assoc_change{state=comm_up,
140				error=0,
141				outbound_streams=SbOutboundStreams,
142				inbound_streams=SbInboundStreams,
143				assoc_id=AssocId}} ->
144		{AssocId,SbInboundStreams,SbOutboundStreams};
145	    {Loopback,Pa,
146	     #sctp_paddr_change{state=addr_confirmed,
147				addr={Loopback,Pa},
148				error=0,
149				assoc_id=AssocId}} ->
150		{Loopback,Pa,
151		 #sctp_assoc_change{state=comm_up,
152				    error=0,
153				    outbound_streams=SbOutboundStreams,
154				    inbound_streams=SbInboundStreams,
155				    assoc_id=AssocId}} =
156		    recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
157		{AssocId,SbInboundStreams,SbOutboundStreams}
158	end,
159
160    ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
161    case log_ok(gen_sctp:recv(Sb, infinity)) of
162	{Loopback,
163	 Pa,
164	 [#sctp_sndrcvinfo{stream=Stream,
165			   assoc_id=SbAssocId}],
166	 Data} -> ok;
167	Event1 ->
168	    case recv_event(Event1) of
169		{Loopback,Pa,
170		 #sctp_paddr_change{addr = {Loopback,_},
171				    state = State,
172				    error = 0,
173				    assoc_id = SbAssocId}}
174		  when State =:= addr_available;
175		       State =:= addr_confirmed ->
176		    {Loopback,
177		     Pa,
178		     [#sctp_sndrcvinfo{stream=Stream,
179				       assoc_id=SbAssocId}],
180		     Data} = log_ok(gen_sctp:recv(Sb, infinity))
181	    end
182    end,
183    ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
184    case log_ok(gen_sctp:recv(Sa, infinity)) of
185	{Loopback,Pb,
186	 [#sctp_sndrcvinfo{stream=Stream,
187			   assoc_id=SaAssocId}],
188	 Data} ->
189	    ok;
190	Event2 ->
191	    {Loopback,Pb,
192	     #sctp_paddr_change{addr={_,Pb},
193				state=addr_confirmed,
194				error=0,
195				assoc_id=SaAssocId}} =
196		recv_event(Event2),
197	    {Loopback,
198	     Pb,
199	     [#sctp_sndrcvinfo{stream=Stream,
200			       assoc_id=SaAssocId}],
201	     Data} =
202		log_ok(gen_sctp:recv(Sa, infinity))
203    end,
204    %%
205    ok = gen_sctp:eof(Sa, SaAssocChange),
206    {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} =
207	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
208    {Loopback,Pb,
209     #sctp_assoc_change{state=shutdown_comp,
210			error=0,
211			assoc_id=SaAssocId}} =
212	recv_event(log_ok(gen_sctp:recv(Sa, infinity))),
213    {Loopback,Pa,
214     #sctp_assoc_change{state=shutdown_comp,
215			error=0,
216			assoc_id=SbAssocId}} =
217	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
218    ok = gen_sctp:close(Sa),
219    {ok,SbStat2} = inet:getstat(Sb, StatOpts),
220    [] = filter_stat_eq(SbStat1, SbStat2),
221    ok = gen_sctp:close(Sb),
222
223    receive
224	Msg -> ct:fail({received,Msg})
225    after 17 -> ok
226    end,
227    ok.
228
229filter_stat_eq([], []) ->
230    [];
231filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) ->
232    if
233	Val1 == Val2 ->
234	    [Stat|filter_stat_eq(SbStat1, SbStat2)];
235	true ->
236	    filter_stat_eq(SbStat1, SbStat2)
237    end.
238
239
240
241%% Minimal data transfer in active mode.
242xfer_active(Config) when is_list(Config) ->
243    Timeout = 2000,
244    Stream = 0,
245    Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
246    Loopback = {127,0,0,1},
247    {ok,Sb} = gen_sctp:open([{active,true}]),
248    {ok,Pb} = inet:port(Sb),
249    ok = gen_sctp:listen(Sb, true),
250
251    {ok,Sa} = gen_sctp:open([{active,true}]),
252    {ok,Pa} = inet:port(Sa),
253    ok = gen_sctp:connect_init(Sa, Loopback, Pb, []),
254    #sctp_assoc_change{state=comm_up,
255		       error=0,
256		       outbound_streams=SaOutboundStreams,
257		       inbound_streams=SaInboundStreams,
258		       assoc_id=SaAssocId} = SaAssocChange =
259	recv_assoc_change(Sa, Loopback, Pb, Timeout),
260    io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, "
261	      "SaOutboundStreams=~p, SaInboundStreams=~p~n",
262	      [Sa,Pa,Sb,Pb,SaAssocId,
263	       SaOutboundStreams,SaInboundStreams]),
264    #sctp_assoc_change{state=comm_up,
265		       error=0,
266		       outbound_streams=SbOutboundStreams,
267		       inbound_streams=SbInboundStreams,
268		       assoc_id=SbAssocId} =
269	recv_assoc_change(Sb, Loopback, Pa, Timeout),
270    SbOutboundStreams = SaInboundStreams,
271    SbInboundStreams = SaOutboundStreams,
272    io:format("SbAssocId=~p~n", [SbAssocId]),
273
274    case recv_paddr_change(Sa, Loopback, Pb, 314) of
275	#sctp_paddr_change{state=addr_confirmed,
276			   addr={_,Pb},
277			   error=0,
278			   assoc_id=SaAssocId} -> ok;
279	#sctp_paddr_change{state=addr_available,
280			   addr={_,Pb},
281			   error=0,
282			   assoc_id=SaAssocId} -> ok;
283	timeout -> ok
284    end,
285    case recv_paddr_change(Sb, Loopback, Pa, 314) of
286	#sctp_paddr_change{state=addr_confirmed,
287			   addr={Loopback,Pa},
288			   error=0,
289			   assoc_id=SbAssocId} -> ok;
290	#sctp_paddr_change{state=addr_available,
291			   addr={Loopback,P},
292			   error=0,
293			   assoc_id=SbAssocId} ->
294	    match_unless_solaris(Pa, P);
295	timeout -> ok
296    end,
297    [] = flush(),
298
299    ok =
300	do_from_other_process(
301	  fun () -> gen_sctp:send(Sa, SaAssocId, 0, Data) end),
302    receive
303	{sctp,Sb,Loopback,Pa,
304	 {[#sctp_sndrcvinfo{stream=Stream,
305			    assoc_id=SbAssocId}],
306	  Data}} -> ok
307    after Timeout ->
308	    ct:fail({timeout,flush()})
309    end,
310    ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
311    receive
312	{sctp,Sa,Loopback,Pb,
313	 {[#sctp_sndrcvinfo{stream=Stream,
314			    assoc_id=SaAssocId}],
315	  Data}} -> ok
316    after Timeout ->
317	    ct:fail({timeout,flush()})
318    end,
319    %%
320    ok = gen_sctp:abort(Sa, SaAssocChange),
321    case recv_assoc_change(Sb, Loopback, Pa, Timeout) of
322	#sctp_assoc_change{state=comm_lost,
323			   assoc_id=SbAssocId} -> ok;
324	timeout ->
325	    ct:fail({timeout,flush()})
326    end,
327    ok = gen_sctp:close(Sb),
328    case recv_assoc_change(Sa, Loopback, Pb, Timeout) of
329	#sctp_assoc_change{state=comm_lost,
330			   assoc_id=SaAssocId} -> ok;
331	timeout ->
332	    io:format("timeout waiting for comm_lost on Sa~n"),
333	    match_unless_solaris(ok, {timeout,flush()})
334    end,
335    receive
336	{sctp_error,Sa,enotconn} -> ok % Solaris
337    after 17 -> ok
338    end,
339    ok = gen_sctp:close(Sa),
340    %%
341    receive
342	Msg -> ct:fail({unexpected,[Msg]++flush()})
343    after 17 -> ok
344    end,
345    ok.
346
347recv_assoc_change(S, Addr, Port, Timeout) ->
348    receive
349	{sctp,S,Addr,Port,{[], #sctp_assoc_change{}=AssocChange}} ->
350	    AssocChange;
351	{sctp,S,Addr,Port,
352	 {[#sctp_sndrcvinfo{assoc_id=AssocId}],
353	  #sctp_assoc_change{assoc_id=AssocId}=AssocChange}} ->
354	    AssocChange
355    after Timeout ->
356	    timeout
357    end.
358
359recv_paddr_change(S, Addr, Port, Timeout) ->
360    receive
361	{sctp,S,Addr,Port,{[], #sctp_paddr_change{}=PaddrChange}} ->
362	    PaddrChange;
363	{sctp,S,Addr,Port,
364	 {[#sctp_sndrcvinfo{assoc_id=AssocId}],
365	  #sctp_paddr_change{assoc_id=AssocId}=PaddrChange}} ->
366	    PaddrChange
367    after Timeout ->
368	    timeout
369    end.
370
371%% Test that #sctp_sndrcvinfo{} parameters set on a socket
372%% are used by gen_sctp:send/4.
373def_sndrcvinfo(Config) when is_list(Config) ->
374    Loopback = {127,0,0,1},
375    Data = <<"What goes up, must come down.">>,
376    %%
377    S1 =
378	log_ok(gen_sctp:open(
379		 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
380    ?LOGVAR(S1),
381    P1 =
382	log_ok(inet:port(S1)),
383    ?LOGVAR(P1),
384    #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} =
385	getopt(S1, sctp_default_send_param),
386    ok =
387	gen_sctp:listen(S1, true),
388    %%
389    S2 =
390	log_ok(gen_sctp:open()),
391    ?LOGVAR(S2),
392    P2 =
393	log_ok(inet:port(S2)),
394    ?LOGVAR(P2),
395    #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} =
396	getopt(S2, sctp_default_send_param),
397    %%
398    #sctp_assoc_change{
399       state=comm_up,
400       error=0,
401       assoc_id=S2AssocId} = S2AssocChange =
402	log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
403    ?LOGVAR(S2AssocChange),
404    S1AssocId =
405	case recv_event(log_ok(gen_sctp:recv(S1))) of
406	    {Loopback,P2,
407	     #sctp_assoc_change{
408		state=comm_up,
409		error=0,
410		assoc_id=AssocId}} ->
411		AssocId;
412	    {Loopback,P2,
413	     #sctp_paddr_change{
414		state=addr_confirmed,
415		error=0,
416		assoc_id=AssocId}} ->
417		{Loopback,P2,
418		 #sctp_assoc_change{
419		    state=comm_up,
420		    error=0,
421		    assoc_id=AssocId}} =
422		    recv_event(log_ok(gen_sctp:recv(S1))),
423		AssocId
424	end,
425    ?LOGVAR(S1AssocId),
426
427    #sctp_sndrcvinfo{
428       ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
429	getopt(
430	  S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
431    #sctp_sndrcvinfo{
432       ppid=0, context=0, timetolive=0} = %, assoc_id=S2AssocId} =
433	getopt(
434	  S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}),
435    %%
436    ok =
437	gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>),
438    case log_ok(gen_sctp:recv(S2)) of
439	{Loopback,P1,
440	 [#sctp_sndrcvinfo{
441	     stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
442	 <<"1: ",Data/binary>>} -> ok;
443	Event1 ->
444	    {Loopback,P1,
445	     #sctp_paddr_change{state=addr_confirmed,
446				addr={_,P1},
447				error=0,
448				assoc_id=S2AssocId}} =
449		recv_event(Event1),
450	    {Loopback,P1,
451	     [#sctp_sndrcvinfo{
452		 stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
453	     <<"1: ",Data/binary>>} =
454		log_ok(gen_sctp:recv(S2))
455    end,
456    %%
457    ok =
458	setopt(
459	  S1, sctp_default_send_param, #sctp_sndrcvinfo{ppid=18}),
460    ok =
461	setopt(
462	  S1, sctp_default_send_param,
463	  #sctp_sndrcvinfo{ppid=19, assoc_id=S1AssocId}),
464    #sctp_sndrcvinfo{
465       ppid=18, context=0, timetolive=0, assoc_id=0} =
466	getopt(S1, sctp_default_send_param),
467    #sctp_sndrcvinfo{
468       ppid=19, context=0, timetolive=0, assoc_id=S1AssocId} =
469	getopt(
470	  S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
471    %%
472    ok =
473	gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>),
474    case log_ok(gen_sctp:recv(S2)) of
475	{Loopback,P1,
476	 [#sctp_sndrcvinfo{
477	     stream=0, ppid=19, context=0, assoc_id=S2AssocId}],
478	 <<"2: ",Data/binary>>} -> ok
479    end,
480    ok =
481	gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>),
482    case log_ok(gen_sctp:recv(S1)) of
483	{Loopback,P2,
484	 [#sctp_sndrcvinfo{
485	     stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
486	 <<"3: ",Data/binary>>} -> ok;
487	Event2 ->
488	    case recv_event(Event2) of
489		{Loopback,P2,
490		 #sctp_paddr_change{
491		    addr={Loopback,_},
492		    state=State,
493		    error=0, assoc_id=S1AssocId}}
494		  when State =:= addr_available;
495		       State =:= addr_confirmed ->
496		    case log_ok(gen_sctp:recv(S1)) of
497			{Loopback,P2,
498			 [#sctp_sndrcvinfo{
499			     stream=1, ppid=0, context=0,
500			     assoc_id=S1AssocId}],
501			 <<"3: ",Data/binary>>} -> ok
502		    end
503	    end
504    end,
505    ok =
506	do_from_other_process(
507	  fun () ->
508		  gen_sctp:send(
509		    S2,
510		    #sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId},
511		    <<"4: ",Data/binary>>)
512	  end),
513    case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
514	{Loopback,P2,
515	 [#sctp_sndrcvinfo{
516	     stream=0, ppid=20, context=0, assoc_id=S1AssocId}],
517	 <<"4: ",Data/binary>>} -> ok
518    end,
519    %%
520    ok =
521	gen_sctp:close(S1),
522    ok =
523	gen_sctp:close(S2),
524    receive
525	Msg ->
526	    ct:fail({received,Msg})
527    after 17 -> ok
528    end,
529    ok.
530
531getopt(S, Opt) ->
532    {ok,[{Opt,Val}]} = inet:getopts(S, [Opt]),
533    Val.
534
535getopt(S, Opt, Param) ->
536    {ok,[{Opt,Val}]} = inet:getopts(S, [{Opt,Param}]),
537    Val.
538
539setopt(S, Opt, Val) ->
540    inet:setopts(S, [{Opt,Val}]).
541
542log_ok(X) -> log(ok(X)).
543
544ok({ok,X}) -> X.
545
546err([], Result) ->
547    erlang:error(Result);
548err([Reason|_], {error,Reason}) ->
549    ok;
550err([_|Reasons], Result) ->
551    err(Reasons, Result).
552
553log(X) ->
554    io:format("LOG[~w]: ~p~n", [self(),X]),
555    X.
556
557flush() ->
558    receive
559	Msg ->
560	    [Msg|flush()]
561    after 17 ->
562	    []
563    end.
564
565%% Test the API function open/1,2 and close/1.
566api_open_close(Config) when is_list(Config) ->
567    {ok,S1} = gen_sctp:open(0),
568    {ok,P}  = inet:port(S1),
569    ok      = gen_sctp:close(S1),
570
571    {ok,S2} = gen_sctp:open(P),
572    {ok,P}  = inet:port(S2),
573    ok      = gen_sctp:close(S2),
574
575    {ok,S3} = gen_sctp:open([{port,P}]),
576    {ok,P}  = inet:port(S3),
577    ok      = gen_sctp:close(S3),
578
579    {ok,S4} = gen_sctp:open(P, []),
580    {ok,P}  = inet:port(S4),
581    ok      = gen_sctp:close(S4),
582
583    {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]),
584    {ok,P}  = inet:port(S5),
585    ok      = gen_sctp:close(S5),
586
587    ok      = gen_sctp:close(S5),
588
589    try gen_sctp:close(0)
590    catch error:badarg -> ok
591    end,
592
593    try gen_sctp:open({})
594    catch error:badarg -> ok
595    end,
596
597    try gen_sctp:open(-1)
598    catch error:badarg -> ok
599    end,
600
601    try gen_sctp:open(65536)
602    catch error:badarg -> ok
603    end,
604
605    try gen_sctp:open(make_ref(), [])
606    catch error:badarg -> ok
607    end,
608
609    try gen_sctp:open(0, {})
610    catch error:badarg -> ok
611    end,
612
613    try gen_sctp:open(0, [make_ref()])
614    catch error:badarg -> ok
615    end,
616
617    try gen_sctp:open([{invalid_option,0}])
618    catch error:badarg -> ok
619    end,
620
621    try gen_sctp:open(0, [{mode,invalid_mode}])
622    catch error:badarg -> ok
623    end,
624    ok.
625
626%% Test the API function listen/2.
627api_listen(Config) when is_list(Config) ->
628    Localhost = {127,0,0,1},
629
630    try gen_sctp:listen(0, true)
631    catch error:badarg -> ok
632    end,
633
634    {ok,S} = gen_sctp:open(),
635    {ok,Pb} = inet:port(S),
636    try gen_sctp:listen(S, not_allowed_for_listen)
637    catch error:badarg -> ok
638    end,
639    ok = gen_sctp:close(S),
640    {error,closed} = gen_sctp:listen(S, true),
641
642    {ok,Sb} = gen_sctp:open(Pb),
643    {ok,Sa} = gen_sctp:open(),
644    case gen_sctp:connect(Sa, localhost, Pb, []) of
645	{error,econnrefused} ->
646	    {ok,{Localhost,
647		 Pb,[],
648		 #sctp_assoc_change{
649		    state=comm_lost}}} =
650		gen_sctp:recv(Sa, infinity);
651	{error,#sctp_assoc_change{state=cant_assoc}} ->
652	    ok%;
653	    %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
654	    %% 	  ok
655    end,
656    ok = gen_sctp:listen(Sb, true),
657    {ok,#sctp_assoc_change{state=comm_up,
658			   error=0}} =
659	gen_sctp:connect(Sa, localhost, Pb, []),
660    ok = gen_sctp:close(Sa),
661    ok = gen_sctp:close(Sb),
662    ok.
663
664%% Test the API function connect_init/4.
665api_connect_init(Config) when is_list(Config) ->
666    Localhost = {127,0,0,1},
667
668    {ok,S} = gen_sctp:open(),
669    {ok,Pb} = inet:port(S),
670    try gen_sctp:connect_init(S, Localhost, not_allowed_for_port, [])
671    catch error:badarg -> ok
672    end,
673    try gen_sctp:connect_init(S, Localhost, 12345, not_allowed_for_opts)
674    catch error:badarg -> ok
675    end,
676    ok = gen_sctp:close(S),
677    {error,closed} = gen_sctp:connect_init(S, Localhost, 12345, []),
678
679    {ok,Sb} = gen_sctp:open(Pb),
680    {ok,Sa} = gen_sctp:open(),
681    case gen_sctp:connect_init(Sa, localhost, Pb, []) of
682	{error,econnrefused} ->
683	    {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} =
684		recv_event(log_ok(gen_sctp:recv(Sa, infinity)));
685	ok ->
686	    {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} =
687		recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
688    end,
689    ok = gen_sctp:listen(Sb, true),
690    case gen_sctp:connect_init(Sa, localhost, Pb, []) of
691	ok ->
692	    {Localhost,Pb,#sctp_assoc_change{state=comm_up}} =
693		recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
694    end,
695    ok = gen_sctp:close(Sa),
696    ok = gen_sctp:close(Sb),
697    ok.
698
699recv_event({Addr,Port,[],#sctp_assoc_change{}=AssocChange}) ->
700    {Addr,Port,AssocChange};
701recv_event({Addr,Port,
702	    [#sctp_sndrcvinfo{assoc_id=Assoc}],
703	    #sctp_assoc_change{assoc_id=Assoc}=AssocChange}) ->
704    {Addr,Port,AssocChange};
705recv_event({Addr,Port,[],#sctp_paddr_change{}=PaddrChange}) ->
706    {Addr,Port,PaddrChange};
707recv_event({Addr,Port,
708	    [#sctp_sndrcvinfo{assoc_id=Assoc}],
709	    #sctp_paddr_change{assoc_id=Assoc}=PaddrChange}) ->
710    {Addr,Port,PaddrChange};
711recv_event({Addr,Port,[],#sctp_shutdown_event{}=ShutdownEvent}) ->
712    {Addr,Port,ShutdownEvent};
713recv_event({Addr,Port,
714	    [#sctp_sndrcvinfo{assoc_id=Assoc}],
715	    #sctp_shutdown_event{assoc_id=Assoc}=ShutdownEvent}) ->
716    {Addr,Port,ShutdownEvent}.
717
718%% Test socket options.
719api_opts(Config) when is_list(Config) ->
720    Sndbuf = 32768,
721    Recbuf = 65536,
722    {ok,S} = gen_sctp:open(0),
723    OSType = os:type(),
724    case {inet:setopts(S, [{linger,{true,2}}]),OSType} of
725	{ok,_} ->
726	    ok;
727	{{error,einval},{unix,sunos}} ->
728	    ok
729    end,
730    ok = inet:setopts(S, [{sndbuf,Sndbuf}]),
731    ok = inet:setopts(S, [{recbuf,Recbuf}]),
732    case inet:getopts(S, [sndbuf]) of
733	{ok,[{sndbuf,SB}]} when SB >= Sndbuf -> ok
734    end,
735    case inet:getopts(S, [recbuf]) of
736	{ok,[{recbuf,RB}]} when RB >= Recbuf -> ok
737    end.
738
739implicit_inet6(Config) when is_list(Config) ->
740    Hostname = log_ok(inet:gethostname()),
741    case gen_sctp:open(0, [inet6]) of
742	{ok,S1} ->
743	    case inet:getaddr(Hostname, inet6) of
744		{ok,Host} ->
745		    Loopback = {0,0,0,0,0,0,0,1},
746		    io:format("~s ~p~n", ["Loopback",Loopback]),
747		    implicit_inet6(S1, Loopback),
748		    ok = gen_sctp:close(S1),
749		    %%
750		    Localhost =
751			log_ok(inet:getaddr("localhost", inet6)),
752		    io:format("~s ~p~n", ["localhost",Localhost]),
753		    S2 =
754			log_ok(gen_sctp:open(0, [{ip,Localhost}])),
755		    implicit_inet6(S2, Localhost),
756		    ok = gen_sctp:close(S2),
757		    %%
758		    io:format("~s ~p~n", [Hostname,Host]),
759		    S3 =
760			log_ok(gen_sctp:open(0, [{ifaddr,Host}])),
761		    implicit_inet6(S3, Host),
762		    ok = gen_sctp:close(S1);
763		{error,eafnosupport} ->
764		    ok = gen_sctp:close(S1),
765		    {skip,"Can not look up IPv6 address"}
766	    end;
767	_ ->
768	    {skip,"IPv6 not supported"}
769    end.
770
771implicit_inet6(S1, Addr) ->
772    ok = gen_sctp:listen(S1, true),
773    P1 = log_ok(inet:port(S1)),
774    S2 = log_ok(gen_sctp:open(0, [inet6])),
775    P2 = log_ok(inet:port(S2)),
776    #sctp_assoc_change{state=comm_up} =
777	log_ok(gen_sctp:connect(S2, Addr, P1, [])),
778    case recv_event(log_ok(gen_sctp:recv(S1))) of
779	{Addr,P2,#sctp_assoc_change{state=comm_up}} ->
780	    ok;
781	{Addr,P2,#sctp_paddr_change{state=addr_confirmed,
782				    addr={Addr,P2},
783				    error=0}} ->
784	    {Addr,P2,#sctp_assoc_change{state=comm_up}} =
785		recv_event(log_ok(gen_sctp:recv(S1)))
786    end,
787    case log_ok(inet:sockname(S1)) of
788	{Addr,P1} -> ok;
789	{{0,0,0,0,0,0,0,0},P1} -> ok
790    end,
791    case log_ok(inet:sockname(S2)) of
792	{Addr,P2} -> ok;
793	{{0,0,0,0,0,0,0,0},P2} -> ok
794    end,
795    ok = gen_sctp:close(S2).
796
797%% Verify {active,N} socket management.
798active_n(Config) when is_list(Config) ->
799    N = 3,
800    S1 = ok(gen_sctp:open([{active,N}])),
801    [{active,N}] = ok(inet:getopts(S1, [active])),
802    ok = inet:setopts(S1, [{active,-N}]),
803    receive
804        {sctp_passive, S1} -> ok
805    after
806        5000 ->
807            exit({error,sctp_passive_failure})
808    end,
809    [{active,false}] = ok(inet:getopts(S1, [active])),
810    ok = inet:setopts(S1, [{active,0}]),
811    receive
812        {sctp_passive, S1} -> ok
813    after
814        5000 ->
815            exit({error,sctp_passive_failure})
816    end,
817    ok = inet:setopts(S1, [{active,32767}]),
818    {error,einval} = inet:setopts(S1, [{active,1}]),
819    {error,einval} = inet:setopts(S1, [{active,-32769}]),
820    ok = inet:setopts(S1, [{active,-32768}]),
821    receive
822        {sctp_passive, S1} -> ok
823    after
824        5000 ->
825            exit({error,sctp_passive_failure})
826    end,
827    [{active,false}] = ok(inet:getopts(S1, [active])),
828    ok = inet:setopts(S1, [{active,N}]),
829    ok = inet:setopts(S1, [{active,true}]),
830    [{active,true}] = ok(inet:getopts(S1, [active])),
831    receive
832        _ -> exit({error,active_n})
833    after
834        0 ->
835            ok
836    end,
837    ok = inet:setopts(S1, [{active,N}]),
838    ok = inet:setopts(S1, [{active,once}]),
839    [{active,once}] = ok(inet:getopts(S1, [active])),
840    receive
841        _ -> exit({error,active_n})
842    after
843        0 ->
844            ok
845    end,
846    {error,einval} = inet:setopts(S1, [{active,32768}]),
847    ok = inet:setopts(S1, [{active,false}]),
848    [{active,false}] = ok(inet:getopts(S1, [active])),
849    ok = gen_sctp:listen(S1, true),
850    S1Port = ok(inet:port(S1)),
851    S2 = ok(gen_sctp:open(0, [{active,false}])),
852    Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])),
853    ok = inet:setopts(S1, [{active,N}]),
854    [{active,N}] = ok(inet:getopts(S1, [active])),
855    LoopFun = fun(Count, Count, _Fn) ->
856		      receive
857			  {sctp_passive,S1} ->
858			      ok
859		      after
860			  5000 ->
861			      exit({error,timeout})
862		      end;
863		 (I, Count, Fn) ->
864		      Msg = list_to_binary("message "++integer_to_list(I)),
865		      ok = gen_sctp:send(S2, Assoc, 0, Msg),
866		      receive
867			  {sctp,S1,_,_,{[SR],Msg}} when is_record(SR, sctp_sndrcvinfo) ->
868			      Fn(I+1, Count, Fn);
869			  {sctp,S1,_,_,_} ->
870			      %% ignore non-data messages
871			      ok = inet:setopts(S1, [{active,1}]),
872			      Fn(I, Count, Fn);
873			  Other ->
874			      exit({unexpected, Other})
875		      after
876			  5000 ->
877			      exit({error,timeout})
878		      end
879	      end,
880    ok = LoopFun(1, N, LoopFun),
881    S3 = ok(gen_sctp:open([{active,0}])),
882    receive
883        {sctp_passive,S3} ->
884            [{active,false}] = ok(inet:getopts(S3, [active]))
885    after
886        5000 ->
887            exit({error,udp_passive})
888    end,
889    ok = gen_sctp:close(S3),
890    ok = gen_sctp:close(S2),
891    ok = gen_sctp:close(S1),
892    ok.
893
894%% Hello world stream socket.
895basic_stream(Config) when is_list(Config) ->
896    {ok,S} = gen_sctp:open([{type,stream}]),
897    ok = gen_sctp:listen(S, true),
898    ok =
899	do_from_other_process(
900	  fun () -> gen_sctp:listen(S, 10) end),
901    ok = gen_sctp:close(S),
902    ok.
903
904%% Minimal data transfer.
905xfer_stream_min(Config) when is_list(Config) ->
906    Stream = 0,
907    Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
908    Loopback = {127,0,0,1},
909    {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
910    ?LOGVAR(Sb),
911    {ok,Pb} = inet:port(Sb),
912    ?LOGVAR(Pb),
913    ok = gen_sctp:listen(Sb, true),
914
915    {ok,Sa} = gen_sctp:open([{type,stream}]),
916    ?LOGVAR(Sa),
917    {ok,Pa} = inet:port(Sa),
918    ?LOGVAR(Pa),
919    #sctp_assoc_change{state=comm_up,
920		       error=0,
921		       outbound_streams=SaOutboundStreams,
922		       inbound_streams=SaInboundStreams,
923		       assoc_id=SaAssocId_X} =
924	log_ok(gen_sctp:connect(Sa, Loopback, Pb, [])),
925    ?LOGVAR(SaAssocId_X),
926    [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] =
927	log_ok(inet:getopts(Sa, [{sctp_get_peer_addr_info,
928				  #sctp_paddrinfo{address={Loopback,Pb}}}])),
929    ?LOGVAR(SaAssocId),
930    match_unless_solaris(SaAssocId_X, SaAssocId),
931
932    {SbOutboundStreams,SbInboundStreams,SbAssocId} =
933	case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
934	    {Loopback,Pa,
935	     #sctp_assoc_change{state=comm_up,
936				error=0,
937				outbound_streams=OS,
938				inbound_streams=IS,
939				assoc_id=AI}} ->
940		{OS,IS,AI};
941	    {Loopback,Pa,
942	     #sctp_paddr_change{state=addr_confirmed,
943				addr={Loopback,Pa},
944				error=0,
945				assoc_id=AI}} ->
946		{Loopback,Pa,
947		 #sctp_assoc_change{state=comm_up,
948				    error=0,
949				    outbound_streams=OS,
950				    inbound_streams=IS,
951				    assoc_id=AI}} =
952		    recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
953		{OS,IS,AI}
954	end,
955    ?LOGVAR(SbAssocId),
956    SaOutboundStreams = SbInboundStreams,
957    ?LOGVAR(SaOutboundStreams),
958    SbOutboundStreams = SaInboundStreams,
959    ?LOGVAR(SbOutboundStreams),
960    ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
961    case log_ok(gen_sctp:recv(Sb, infinity)) of
962	{Loopback,
963	 Pa,
964	 [#sctp_sndrcvinfo{stream=Stream,
965			   assoc_id=SbAssocId}],
966	 Data} -> ok;
967	{Loopback,
968	 Pa,[],
969	 #sctp_paddr_change{addr = {Loopback,_},
970			    state = addr_available,
971			    error = 0,
972			    assoc_id = SbAssocId}} ->
973	    {Loopback,
974	     Pa,
975	     [#sctp_sndrcvinfo{stream=Stream,
976			       assoc_id=SbAssocId}],
977	     Data} = log_ok(gen_sctp:recv(Sb, infinity));
978	{Loopback,
979	 Pa,
980	 [#sctp_sndrcvinfo{stream=Stream,
981			   assoc_id=SbAssocId}],
982	 #sctp_paddr_change{addr = {Loopback,_},
983			    state = addr_confirmed,
984			    error = 0,
985			    assoc_id = SbAssocId}} ->
986	    {Loopback,
987	     Pa,
988	     [#sctp_sndrcvinfo{stream=Stream,
989			       assoc_id=SbAssocId}],
990	     Data} = log_ok(gen_sctp:recv(Sb, infinity))
991    end,
992    ok =
993	do_from_other_process(
994	  fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end),
995    case log_ok(gen_sctp:recv(Sa, infinity)) of
996	{Loopback,Pb,
997	 [#sctp_sndrcvinfo{stream=Stream,
998			   assoc_id=SaAssocId}],
999	 Data} -> ok;
1000	Event1 ->
1001	    {Loopback,Pb,
1002	     #sctp_paddr_change{state=addr_confirmed,
1003				addr={_,Pb},
1004				error=0,
1005				assoc_id=SaAssocId}} =
1006		recv_event(Event1),
1007	    {Loopback,Pb,
1008	     [#sctp_sndrcvinfo{stream=Stream,
1009			       assoc_id=SaAssocId}],
1010	     Data} =
1011		log_ok(gen_sctp:recv(Sa, infinity))
1012    end,
1013    ok = gen_sctp:close(Sa),
1014    {Loopback,Pa,
1015     #sctp_shutdown_event{assoc_id=SbAssocId}} =
1016	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
1017    {Loopback,Pa,
1018     #sctp_assoc_change{state=shutdown_comp,
1019			error=0,
1020			assoc_id=SbAssocId}} =
1021	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
1022    ok = gen_sctp:close(Sb),
1023
1024    receive
1025	Msg -> ct:fail({received,Msg})
1026    after 17 -> ok
1027    end,
1028    ok.
1029
1030
1031
1032do_from_other_process(Fun) ->
1033    Parent = self(),
1034    Ref = make_ref(),
1035    Child =
1036	spawn(fun () ->
1037		      try Fun() of
1038			  Result ->
1039			      Parent ! {Ref,Result}
1040		      catch
1041			  Class:Reason:Stacktrace ->
1042			      Parent ! {Ref,Class,Reason,Stacktrace}
1043		      end
1044	      end),
1045    Mref = erlang:monitor(process, Child),
1046    receive
1047	{Ref,Result} ->
1048	    receive {'DOWN',Mref,_,_,_} -> Result end;
1049	{Ref,Class,Reason,Stacktrace} ->
1050	    receive {'DOWN',Mref,_,_,_} ->
1051		    erlang:raise(Class, Reason, Stacktrace)
1052	    end;
1053	{'DOWN',Mref,_,_,Reason} ->
1054	    erlang:exit(Reason)
1055    end.
1056
1057
1058%% Peel off an SCTP stream socket ({active,once}).
1059
1060peeloff_active_once(Config) ->
1061    peeloff(Config, [{active,once}]).
1062
1063%% Peel off an SCTP stream socket ({active,true}).
1064
1065peeloff_active_true(Config) ->
1066    peeloff(Config, [{active,true}]).
1067
1068%% Peel off an SCTP stream socket ({active,N}).
1069
1070peeloff_active_n(Config) ->
1071    peeloff(Config, [{active,1}]).
1072
1073peeloff(Config, SockOpts) when is_list(Config) ->
1074    Addr = {127,0,0,1},
1075    Stream = 0,
1076    Timeout = 333,
1077    StartTime = timestamp(),
1078    S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
1079    ?LOGVAR(S1),
1080    P1 = socket_call(S1, get_port),
1081    ?LOGVAR(P1),
1082    Socket1 = socket_call(S1, get_socket),
1083    ?LOGVAR(Socket1),
1084    socket_call(S1, {listen,true}),
1085    S2 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
1086    ?LOGVAR(S2),
1087    P2 = socket_call(S2, get_port),
1088    ?LOGVAR(P2),
1089    Socket2 = socket_call(S2, get_socket),
1090    ?LOGVAR(Socket2),
1091    %%
1092    socket_call(S2, {connect_init,Addr,P1,[]}),
1093    S2Ai =
1094	receive
1095	    {S2,{Addr,P1,
1096		 #sctp_assoc_change{
1097		    state=comm_up,
1098		    assoc_id=AssocId2}}} -> AssocId2
1099	after Timeout ->
1100		socket_bailout([S1,S2], StartTime)
1101	end,
1102    ?LOGVAR(S2Ai),
1103    S1Ai =
1104	receive
1105	    {S1,{Addr,P2,
1106		 #sctp_assoc_change{
1107		    state=comm_up,
1108		    assoc_id=AssocId1}}} -> AssocId1
1109	after Timeout ->
1110		socket_bailout([S1,S2], StartTime)
1111	end,
1112    ?LOGVAR(S1Ai),
1113    %%
1114    socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}),
1115    receive
1116	{S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
1117    after Timeout ->
1118	    socket_bailout([S1,S2], StartTime)
1119    end,
1120    socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
1121    receive
1122	{S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
1123    after Timeout ->
1124	    socket_bailout([S1,S2], StartTime)
1125    end,
1126    %%
1127    S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
1128    ?LOGVAR(S3),
1129    P3_X = socket_call(S3, get_port),
1130    ?LOGVAR(P3_X),
1131    P3 = case P3_X of 0 -> P1; _ -> P3_X end,
1132    [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] =
1133	socket_call(S3,
1134		    {getopts,[{sctp_get_peer_addr_info,
1135			       #sctp_paddrinfo{address={Addr,P2}}}]}),
1136    %%S3Ai = S1Ai,
1137    ?LOGVAR(S3Ai),
1138    %%
1139    socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}),
1140    receive
1141	{S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
1142    after Timeout ->
1143	    socket_bailout([S1,S2,S3], StartTime)
1144    end,
1145    socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
1146    receive
1147	{S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
1148    after Timeout ->
1149	    socket_bailout([S1,S2,S3], StartTime)
1150    end,
1151    %%
1152    inet:i(sctp),
1153    socket_close_verbose(S1, StartTime),
1154    socket_close_verbose(S2, StartTime),
1155    receive
1156	{S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
1157	    match_unless_solaris(S3Ai, S3Ai_X)
1158    after Timeout ->
1159	    socket_bailout([S3], StartTime)
1160    end,
1161    receive
1162	{S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
1163					assoc_id=S3Ai}}} -> ok
1164    after Timeout ->
1165	    socket_bailout([S3], StartTime)
1166    end,
1167    socket_close_verbose(S3, StartTime),
1168    [] = flush(),
1169    ok.
1170
1171
1172
1173%% Check sndbuf and recbuf behaviour.
1174buffers(Config) when is_list(Config) ->
1175    Limit = 4096,
1176    Addr = {127,0,0,1},
1177    Stream = 1,
1178    Timeout = 3333,
1179    StartTime = timestamp(),
1180    S1 = socket_open([{ip,Addr}], Timeout),
1181    ?LOGVAR(S1),
1182    P1 = socket_call(S1, get_port),
1183    ?LOGVAR(P1),
1184    ok = socket_call(S1, {listen,true}),
1185    S2 = socket_open([{ip,Addr}], Timeout),
1186    ?LOGVAR(S2),
1187    P2 = socket_call(S2, get_port),
1188    ?LOGVAR(P2),
1189    %%
1190    socket_call(S2, {connect_init,Addr,P1,[]}),
1191    S2Ai =
1192	receive
1193	    {S2,{Addr,P1,
1194		 #sctp_assoc_change{
1195		    state=comm_up,
1196		    assoc_id=AssocId2}}} -> AssocId2
1197	after Timeout ->
1198		socket_bailout([S1,S2], StartTime)
1199	end,
1200    S1Ai =
1201	receive
1202	    {S1,{Addr,P2,
1203		 #sctp_assoc_change{
1204		    state=comm_up,
1205		    assoc_id=AssocId1}}} -> AssocId1
1206	after Timeout ->
1207		socket_bailout([S1,S2], StartTime)
1208	end,
1209    %%
1210    socket_call(S1, {setopts,[{recbuf,Limit}]}),
1211    Recbuf =
1212	case socket_call(S1, {getopts,[recbuf]}) of
1213	    [{recbuf,RB1}] when RB1 >= Limit -> RB1
1214	end,
1215    Data = mk_data(Recbuf+Limit),
1216    socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}),
1217    socket_call(S2, {send,S2Ai,Stream,Data}),
1218    receive
1219	{S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
1220    after Timeout ->
1221	    socket_bailout([S1,S2], StartTime)
1222    end,
1223    %%
1224    socket_close_verbose(S1, StartTime),
1225    receive
1226	{S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
1227    after Timeout ->
1228	    socket_bailout([S2], StartTime)
1229    end,
1230    receive
1231	{S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
1232					assoc_id=S2Ai}}} -> ok
1233    after Timeout ->
1234	    socket_bailout([S2], StartTime)
1235    end,
1236    socket_close_verbose(S2, StartTime),
1237    [] = flush(),
1238    ok.
1239
1240mk_data(Bytes) ->
1241    mk_data(0, Bytes, <<>>).
1242%%
1243mk_data(N, Bytes, Bin) when N < Bytes ->
1244    mk_data(N+4, Bytes, <<Bin/binary,N:32>>);
1245mk_data(_, _, Bin) ->
1246    Bin.
1247
1248
1249
1250%% Test opening a multihoming ipv4 socket.
1251open_multihoming_ipv4_socket(Config) when is_list(Config) ->
1252    case get_addrs_by_family(inet, 2) of
1253	{ok, [Addr1, Addr2]} ->
1254	    do_open_and_connect([Addr1, Addr2], Addr1);
1255	{error, Reason} ->
1256	    {skip, Reason}
1257    end.
1258
1259%% This test is mostly aimed to indicate whether host has a
1260%% non-working ipv6 setup.  Test opening a unihoming (non-multihoming)
1261%% ipv6 socket.
1262open_unihoming_ipv6_socket(Config) when is_list(Config) ->
1263    case get_addrs_by_family(inet6, 1) of
1264	{ok, [Addr]} ->
1265	    do_open_and_connect([Addr], Addr);
1266	{error, Reason} ->
1267	    {skip, Reason}
1268    end.
1269
1270
1271%% Test opening a multihoming ipv6 socket.
1272open_multihoming_ipv6_socket(Config) when is_list(Config) ->
1273    case get_addrs_by_family(inet6, 2) of
1274	{ok, [Addr1, Addr2]} ->
1275	    do_open_and_connect([Addr1, Addr2], Addr1);
1276	{error, Reason} ->
1277	    {skip, Reason}
1278    end.
1279
1280%% Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses.
1281open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) ->
1282    case get_addrs_by_family(inet_and_inet6, 2) of
1283	{ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} ->
1284	    %% Connect to the first address to test bind
1285	    do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2],
1286				InetAddr1),
1287	    do_open_and_connect([Inet6Addr1, InetAddr1],
1288				Inet6Addr1),
1289
1290	    %% Connect an address, not the first,
1291	    %% to test sctp_bindx
1292	    do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
1293				Inet6Addr2),
1294	    do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
1295				InetAddr1);
1296	{error, Reason} ->
1297	    {skip, Reason}
1298    end.
1299
1300%% Test inet:socknames/peernames on unihoming IPv4 sockets.
1301names_unihoming_ipv4(Config) when is_list(Config) ->
1302    do_names(Config, inet, 1).
1303
1304%% Test inet:socknames/peernames on unihoming IPv6 sockets.
1305names_unihoming_ipv6(Config) when is_list(Config) ->
1306    do_names(Config, inet6, 1).
1307
1308%% Test inet:socknames/peernames on multihoming IPv4 sockets.
1309names_multihoming_ipv4(Config) when is_list(Config) ->
1310    do_names(Config, inet, 2).
1311
1312%% Test inet:socknames/peernames on multihoming IPv6 sockets.
1313names_multihoming_ipv6(Config) when is_list(Config) ->
1314    do_names(Config, inet6, 2).
1315
1316
1317
1318do_names(_, FamilySpec, AddressCount) ->
1319    Fun =
1320	fun (ServerSocket, _, ServerAssoc, ClientSocket, _, ClientAssoc) ->
1321		ServerSocknamesNoassoc =
1322		    lists:sort(ok(inet:socknames(ServerSocket))),
1323		?LOGVAR(ServerSocknamesNoassoc),
1324		ServerSocknames =
1325		    lists:sort(ok(inet:socknames(ServerSocket, ServerAssoc))),
1326		?LOGVAR(ServerSocknames),
1327		[_|_] =
1328		    ordsets:intersection
1329		      (ServerSocknamesNoassoc, ServerSocknames),
1330		ClientSocknamesNoassoc =
1331		    lists:sort(ok(inet:socknames(ClientSocket))),
1332		?LOGVAR(ClientSocknamesNoassoc),
1333		ClientSocknames =
1334		    lists:sort(ok(inet:socknames(ClientSocket, ClientAssoc))),
1335		?LOGVAR(ClientSocknames),
1336		[_|_] =
1337		    ordsets:intersection
1338		      (ClientSocknamesNoassoc, ClientSocknames),
1339		err([einval,enotconn], inet:peernames(ServerSocket)),
1340		ServerPeernames =
1341		    lists:sort(ok(inet:peernames(ServerSocket, ServerAssoc))),
1342		?LOGVAR(ServerPeernames),
1343		err([einval,enotconn], inet:peernames(ClientSocket)),
1344		ClientPeernames =
1345		    lists:sort(ok(inet:peernames(ClientSocket, ClientAssoc))),
1346		?LOGVAR(ClientPeernames),
1347		ServerSocknames = ClientPeernames,
1348		ClientSocknames = ServerPeernames,
1349		{ok,Socket} =
1350		    gen_sctp:peeloff(ServerSocket, ServerAssoc),
1351		SocknamesNoassoc =
1352		    lists:sort(ok(inet:socknames(Socket))),
1353		?LOGVAR(SocknamesNoassoc),
1354		Socknames =
1355		    lists:sort(ok(inet:socknames(Socket, ServerAssoc))),
1356		?LOGVAR(Socknames),
1357		true =
1358		    ordsets:is_subset(SocknamesNoassoc, Socknames),
1359		Peernames =
1360		    lists:sort(ok(inet:peernames(Socket, ServerAssoc))),
1361		?LOGVAR(Peernames),
1362		ok = gen_sctp:close(Socket),
1363		Socknames = ClientPeernames,
1364		ClientSocknames = Peernames,
1365		ok
1366	end,
1367    case get_addrs_by_family(FamilySpec, AddressCount) of
1368	{ok, Addresses} when length(Addresses) =:= AddressCount ->
1369	    do_open_and_connect(Addresses, hd(Addresses), Fun);
1370	{error, Reason} ->
1371	    {skip, Reason}
1372    end.
1373
1374
1375
1376get_addrs_by_family(Family, NumAddrs) ->
1377    case os:type() of
1378	{unix,linux} ->
1379	    get_addrs_by_family_aux(Family, NumAddrs);
1380	{unix,freebsd} ->
1381	    get_addrs_by_family_aux(Family, NumAddrs);
1382	{unix,sunos} ->
1383	    case get_addrs_by_family_aux(Family, NumAddrs) of
1384		{ok, [InetAddrs, Inet6Addrs]} when Family =:= inet_and_inet6 ->
1385		    %% Man page for sctp_bindx on Solaris says: "If sock is an
1386		    %% Internet Protocol Version 6 (IPv6) socket, addrs should
1387		    %% be an array of sockaddr_in6 structures containing IPv6
1388		    %% or IPv4-mapped IPv6 addresses."
1389		    {ok, [ipv4_map_addrs(InetAddrs), Inet6Addrs]};
1390		{ok, Addrs} ->
1391		    {ok, Addrs};
1392		{error, Reason} ->
1393		    {error, Reason}
1394	    end;
1395	Os ->
1396	    Reason = if Family =:= inet_and_inet6 ->
1397			     f("Mixing ipv4 and ipv6 addresses for multihoming "
1398			       " has not been verified on ~p", [Os]);
1399			true ->
1400			     f("Multihoming for ~p has not been verified on ~p",
1401			       [Family, Os])
1402		     end,
1403	    {error, Reason}
1404    end.
1405
1406get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet;
1407					       Family =:= inet6 ->
1408    case inet:getaddr(localhost, Family) of
1409	{error,eafnosupport} ->
1410	    {skip, f("No support for ~p", Family)};
1411	{ok, _} ->
1412	    IfAddrs = ok(inet:getifaddrs()),
1413	    case filter_addrs_by_family(IfAddrs, Family) of
1414		Addrs when length(Addrs) >= NumAddrs ->
1415		    {ok, lists:sublist(Addrs, NumAddrs)};
1416		[] ->
1417		    {error, f("Need ~p ~p address(es) found none~n",
1418			      [NumAddrs, Family])};
1419		Addrs ->
1420		    {error,
1421		     f("Need ~p ~p address(es) found only ~p: ~p~n",
1422		       [NumAddrs, Family, length(Addrs), Addrs])}
1423	    end
1424    end;
1425get_addrs_by_family_aux(inet_and_inet6, NumAddrs) ->
1426    catch {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of
1427		    {ok, Addrs}     -> Addrs;
1428		    {error, Reason} -> throw({error, Reason})
1429		end || Family <- [inet, inet6]]}.
1430
1431filter_addrs_by_family(IfAddrs, Family) ->
1432    lists:flatten([[Addr || {addr, Addr} <- Info,
1433			    is_good_addr(Addr, Family)]
1434		   || {_IfName, Info} <- IfAddrs]).
1435
1436is_good_addr(Addr, inet) when tuple_size(Addr) =:= 4 ->
1437    true;
1438is_good_addr({0,0,0,0,0,16#ffff,_,_}, inet6) ->
1439    false; %% ipv4 mapped
1440is_good_addr({16#fe80,_,_,_,_,_,_,_}, inet6) ->
1441    false; %% link-local
1442is_good_addr(Addr, inet6) when tuple_size(Addr) =:= 8 ->
1443    true;
1444is_good_addr(_Addr, _Family) ->
1445    false.
1446
1447ipv4_map_addrs(InetAddrs) ->
1448    [begin
1449	 <<AB:16>> = <<A,B>>,
1450	 <<CD:16>> = <<C,D>>,
1451	 {0, 0, 0, 0, 0, 16#ffff, AB, CD}
1452     end || {A,B,C,D} <- InetAddrs].
1453
1454f(F, A) ->
1455    lists:flatten(io_lib:format(F, A)).
1456
1457do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
1458    Fun = fun (_, _, _, _, _, _) -> ok end,
1459    do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun).
1460%%
1461do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) ->
1462    ServerFamily = get_family_by_addrs(ServerAddresses),
1463    io:format("Serving ~p addresses: ~p~n",
1464	      [ServerFamily, ServerAddresses]),
1465    S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++
1466			      [ServerFamily])),
1467    ok = gen_sctp:listen(S1, true),
1468    P1 = ok(inet:port(S1)),
1469    ClientFamily = get_family_by_addr(AddressToConnectTo),
1470    io:format("Connecting to ~p ~p~n",
1471	      [ClientFamily, AddressToConnectTo]),
1472    ClientOpts =
1473	[ClientFamily |
1474	 case ClientFamily of
1475	     inet6 ->
1476		 [{ipv6_v6only,true}];
1477	     _ ->
1478		 []
1479	 end],
1480    S2 = ok(gen_sctp:open(0, ClientOpts)),
1481    log(open),
1482    %% Verify client can connect
1483    #sctp_assoc_change{state=comm_up} = S2Assoc =
1484	ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])),
1485    log(comm_up),
1486    %% verify server side also receives comm_up from client
1487    S1Assoc = recv_comm_up_eventually(S1),
1488    Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc),
1489    ok = gen_sctp:close(S2),
1490    ok = gen_sctp:close(S1),
1491    Result.
1492
1493%% If at least one of the addresses is an ipv6 address, return inet6, else inet.
1494get_family_by_addrs(Addresses) ->
1495    case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of
1496	[inet, inet6] -> inet6;
1497	[inet]        -> inet;
1498	[inet6]       -> inet6
1499    end.
1500
1501get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet;
1502get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6.
1503
1504recv_comm_up_eventually(S) ->
1505    case ok(gen_sctp:recv(S)) of
1506	{_Addr, _Port, _Info,
1507	 #sctp_assoc_change{state=comm_up} = Assoc} ->
1508	    Assoc;
1509	{_Addr, _Port, _Info, _OtherSctpMsg} = Msg ->
1510	    log({unexpected,Msg}),
1511	    recv_comm_up_eventually(S)
1512    end.
1513
1514%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1515%%% socket gen_server ultra light
1516
1517socket_open(SockOpts0, Timeout) ->
1518    SockOpts =
1519	case lists:keyfind(active,1,SockOpts0) of
1520	    false ->
1521		[{active,once}|SockOpts0];
1522	    _ ->
1523		SockOpts0
1524	end,
1525    Opts = [{type,seqpacket},binary|SockOpts],
1526    Starter =
1527	fun () ->
1528		{ok,Socket} =
1529		    gen_sctp:open(Opts),
1530		Socket
1531	end,
1532    s_start(Starter, Timeout).
1533
1534socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
1535    Opts = [binary|SocketOpts],
1536    Starter =
1537	fun () ->
1538		{ok,NewSocket} =
1539		    gen_sctp:peeloff(Socket, AssocId),
1540		ok = inet:setopts(NewSocket, Opts),
1541		NewSocket
1542	end,
1543    s_start(Starter, Timeout).
1544
1545socket_close_verbose(S, StartTime) ->
1546    History = socket_history(socket_close(S), StartTime),
1547    io:format("socket_close ~p:~n    ~p.~n", [S,History]),
1548    History.
1549
1550socket_close(S) ->
1551    s_req(S, close).
1552
1553socket_call(S, Request) ->
1554    s_req(S, {Request}).
1555
1556%% socket_get(S, Key) ->
1557%%     s_req(S, {get,Key}).
1558
1559socket_bailout([S|Ss], StartTime) ->
1560    History = socket_history(socket_close(S), StartTime),
1561    io:format("bailout ~p:~n    ~p.~n", [S,History]),
1562    socket_bailout(Ss, StartTime);
1563socket_bailout([], _) ->
1564    io:format("flush: ~p.~n", [flush()]),
1565    ct:fail(socket_bailout).
1566
1567socket_history({State,Flush}, StartTime) ->
1568    {lists:keysort(
1569       2,
1570       lists:flatten(
1571	 [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals]
1572	  || {Key,Vals} <- gb_trees:to_list(State)])),
1573     Flush}.
1574
1575s_handler(Socket) ->
1576    fun ({listen,Listen}) ->
1577	    ok = gen_sctp:listen(Socket, Listen);
1578	(get_port) ->
1579	    ok(inet:port(Socket));
1580	(get_socket) ->
1581	    Socket;
1582	({connect_init,ConAddr,ConPort,ConOpts}) ->
1583	    ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts);
1584	({send,AssocId,Stream,Data}) ->
1585	    ok = gen_sctp:send(Socket, AssocId, Stream, Data);
1586	({send,OtherSocket,AssocId,Stream,Data}) ->
1587	    ok = gen_sctp:send(OtherSocket, AssocId, Stream, Data);
1588	({setopts,Opts}) ->
1589	    ok = inet:setopts(Socket, Opts);
1590	({getopts,Optnames}) ->
1591	    ok(inet:getopts(Socket, Optnames))
1592    end.
1593
1594s_req(S, Req) ->
1595    Mref = erlang:monitor(process, S),
1596    S ! {self(),Mref,Req},
1597    receive
1598	{'DOWN',Mref,_,_,Error} ->
1599	    exit(Error);
1600	{S,Mref,Reply} ->
1601	    erlang:demonitor(Mref, [flush]),
1602	    Reply
1603    end.
1604
1605s_start(Starter, Timeout) ->
1606    Parent = self(),
1607    Owner =
1608	spawn_link(
1609	  fun () ->
1610		  s_start(Starter(), Timeout, Parent)
1611	  end),
1612    Owner.
1613
1614s_start(Socket, Timeout, Parent) ->
1615    Handler = s_handler(Socket),
1616    try
1617	s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty())
1618    catch
1619	Class:Reason:Stacktrace ->
1620	    io:format(?MODULE_STRING":socket exception ~w:~w at~n"
1621		      "~p.~n", [Class,Reason,Stacktrace]),
1622	    erlang:raise(Class, Reason, Stacktrace)
1623    end.
1624
1625s_loop(Socket, Timeout, Parent, Handler, State) ->
1626    receive
1627	{Parent,Ref,close} -> % socket_close()
1628	    erlang:send_after(Timeout, self(), {Parent,Ref,exit}),
1629	    s_loop(Socket, Timeout, Parent, Handler, State);
1630	{Parent,Ref,exit} ->
1631	    ok = gen_sctp:close(Socket),
1632	    Key = exit,
1633	    NewState = gb_push(Key, Socket, State),
1634	    Parent ! {self(),Ref,{NewState,flush()}};
1635	{Parent,Ref,{Msg}} ->
1636	    Result = Handler(Msg),
1637	    Key = req,
1638	    NewState = gb_push(Key, {Msg,Result}, State),
1639	    Parent ! {self(),Ref,Result},
1640	    s_loop(Socket, Timeout, Parent, Handler, NewState);
1641	%% {Parent,Ref,{get,Key}} ->
1642	%%     Parent ! {self(),Ref,gb_get(Key, State)},
1643	%%     s_loop(Socket, Timeout, Parent, Handler, State);
1644	{sctp,Socket,Addr,Port,
1645	 {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
1646	  when not is_tuple(Data) ->
1647	    case gb_get({assoc_change,AssocId}, State) of
1648		[{Addr,Port,
1649		  #sctp_assoc_change{
1650		     state=comm_up,
1651		     inbound_streams=Is}}|_]
1652		  when 0 =< Stream, Stream < Is-> ok;
1653		[] -> ok
1654	    end,
1655	    Key = {msg,AssocId,Stream},
1656	    NewState = gb_push(Key, {Addr,Port,SRI,Data}, State),
1657	    Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
1658	    again(Socket),
1659	    s_loop(Socket, Timeout, Parent, Handler, NewState);
1660	{sctp,Socket,Addr,Port,
1661	 {SRI,#sctp_assoc_change{assoc_id=AssocId,state=St}=SAC}} ->
1662	    case SRI of
1663		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
1664		[] -> ok
1665	    end,
1666	    Key = {assoc_change,AssocId},
1667	    case {gb_get(Key, State),St} of
1668		{[],_} -> ok;
1669		{[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
1670		  when St =:= comm_lost; St =:= shutdown_comp -> ok
1671	    end,
1672	    NewState = gb_push(Key, {Addr,Port,SAC}, State),
1673	    Parent ! {self(),{Addr,Port,SAC}},
1674	    again(Socket),
1675	    s_loop(Socket, Timeout, Parent, Handler, NewState);
1676	{sctp,Socket,Addr,Port,
1677	 {SRI,#sctp_paddr_change{assoc_id=AssocId,
1678				 addr={_,P},
1679				 state=St}=SPC}} ->
1680	    match_unless_solaris(Port, P),
1681	    case SRI of
1682		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
1683		[] -> ok
1684	    end,
1685	    case {gb_get({assoc_change,AssocId}, State),St} of
1686		{[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
1687		  when St =:= addr_available;
1688		       St =:= addr_confirmed -> ok;
1689		{[],addr_confirmed} -> ok
1690	    end,
1691	    Key = {paddr_change,AssocId},
1692	    NewState = gb_push(Key, {Addr,Port,SPC}, State),
1693	    again(Socket),
1694	    s_loop(Socket, Timeout, Parent, Handler, NewState);
1695	{sctp,Socket,Addr,Port,
1696	 {SRI,#sctp_shutdown_event{assoc_id=AssocId}=SSE}} ->
1697	    case SRI of
1698		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
1699		[] -> ok
1700	    end,
1701	    case gb_get({assoc_change,AssocId}, State) of
1702		[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok;
1703		[] -> ok
1704	    end,
1705	    Key = {shutdown_event,AssocId},
1706	    NewState = gb_push(Key, {Addr,Port}, State),
1707	    Parent ! {self(), {Addr,Port,SSE}},
1708	    again(Socket),
1709	    s_loop(Socket, Timeout, Parent, Handler, NewState);
1710	Unexpected ->
1711	    erlang:error({unexpected,Unexpected})
1712    end.
1713
1714again(Socket) ->
1715    receive
1716	{sctp_passive,Socket} ->
1717	    [{active, false}] = ok(inet:getopts(Socket, [active])),
1718	    ok = inet:setopts(Socket,[{active,1}])
1719    after 0 ->
1720	    ok = inet:setopts(Socket, [{active,once}])
1721    end.
1722
1723gb_push(Key, Val, GBT) ->
1724    TS = timestamp(),
1725    case gb_trees:lookup(Key, GBT) of
1726	none ->
1727	    gb_trees:insert(Key, [{TS,Val}], GBT);
1728	{value,V} ->
1729	    gb_trees:update(Key, [{TS,Val}|V], GBT)
1730    end.
1731
1732gb_get(Key, GBT) ->
1733    case gb_trees:lookup(Key, GBT) of
1734	none ->
1735	    [];
1736	{value,V} ->
1737	    [Val || {_TS,Val} <- V]
1738    end.
1739
1740match_unless_solaris(A, B) ->
1741    case os:type() of
1742	{unix,sunos} -> B;
1743	_ -> A = B
1744    end.
1745
1746timestamp() ->
1747    erlang:monotonic_time().
1748