1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2007-2020. 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-include("kernel_test_lib.hrl"). 25 26%%-compile(export_all). 27 28-export([all/0, suite/0,groups/0, 29 init_per_suite/1,end_per_suite/1, 30 init_per_group/2,end_per_group/2, 31 init_per_testcase/2, end_per_testcase/2]). 32-export( 33 [skip_old_solaris/1, 34 basic/1, 35 api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1, 36 xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1, 37 open_multihoming_ipv4_socket/1, 38 open_unihoming_ipv6_socket/1, 39 open_multihoming_ipv6_socket/1, 40 open_multihoming_ipv4_and_ipv6_socket/1, 41 basic_stream/1, xfer_stream_min/1, active_n/1, 42 peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1, 43 buffers/1, 44 names_unihoming_ipv4/1, names_unihoming_ipv6/1, 45 names_multihoming_ipv4/1, names_multihoming_ipv6/1, 46 recv_close/1]). 47 48suite() -> 49 [{ct_hooks,[ts_install_cth]}, 50 {timetrap,{minutes,1}}]. 51 52all() -> 53 G = case is_old_solaris() of 54 true -> old_solaris; 55 false -> extensive 56 end, 57 [{group,smoke}, 58 {group,G}]. 59 60groups() -> 61 [ 62 {smoke, [], smoke_cases()}, 63 {old_solaris, [], old_solaris_cases()}, 64 {extensive, [], extensive_cases()} 65 ]. 66 67smoke_cases() -> 68 [ 69 basic, 70 basic_stream 71 ]. 72 73old_solaris_cases() -> 74 [ 75 skip_old_solaris 76 ]. 77 78extensive_cases() -> 79 [api_open_close, api_listen, api_connect_init, 80 api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6, 81 open_multihoming_ipv4_socket, 82 open_unihoming_ipv6_socket, 83 open_multihoming_ipv6_socket, 84 open_multihoming_ipv4_and_ipv6_socket, active_n, 85 xfer_stream_min, peeloff_active_once, 86 peeloff_active_true, peeloff_active_n, buffers, 87 names_unihoming_ipv4, names_unihoming_ipv6, 88 names_multihoming_ipv4, names_multihoming_ipv6, 89 recv_close]. 90 91init_per_suite(_Config) -> 92 case gen_sctp:open() of 93 {ok,Socket} -> 94 gen_sctp:close(Socket), 95 []; 96 {error,Error} 97 when Error =:= eprotonosupport; 98 Error =:= esocktnosupport -> 99 {skip,"SCTP not supported on this machine"} 100 end. 101 102end_per_suite(_Config) -> 103 ok. 104 105init_per_group(_GroupName, Config) -> 106 Config. 107 108end_per_group(_GroupName, Config) -> 109 Config. 110 111 112init_per_testcase(_Func, Config) -> 113 Config. 114 115end_per_testcase(_Func, _Config) -> 116 ok. 117 118 119-define(LOGVAR(Var), begin io:format(??Var" = ~p~n", [Var]) end). 120 121is_old_solaris() -> 122 os:type() =:= {unix,sunos} andalso os:version() < {5,12,0}. 123 124skip_old_solaris(_Config) -> 125 {skip,"Unreliable test cases and/or implementation on old Solaris"}. 126 127%% Hello world. 128basic(Config) when is_list(Config) -> 129 {ok,S} = gen_sctp:open(), 130 ok = gen_sctp:close(S), 131 ok. 132 133%% Minimal data transfer. 134xfer_min(Config) when is_list(Config) -> 135 Stream = 0, 136 Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, 137 Loopback = {127,0,0,1}, 138 StatOpts = 139 [recv_avg,recv_cnt,recv_max,recv_oct, 140 send_avg,send_cnt,send_max,send_oct], 141 {ok,Sb} = gen_sctp:open([{type,seqpacket}]), 142 {ok,SbStat1} = inet:getstat(Sb, StatOpts), 143 {ok,Pb} = inet:port(Sb), 144 ok = gen_sctp:listen(Sb, true), 145 146 {ok,Sa} = gen_sctp:open(), 147 {ok,Pa} = inet:port(Sa), 148 {ok,#sctp_assoc_change{state=comm_up, 149 error=0, 150 outbound_streams=SaOutboundStreams, 151 inbound_streams=SaInboundStreams, 152 assoc_id=SaAssocId}=SaAssocChange} = 153 gen_sctp:connect(Sa, Loopback, Pb, []), 154 {SbAssocId,SaOutboundStreams,SaInboundStreams} = 155 case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of 156 {Loopback,Pa, 157 #sctp_assoc_change{state=comm_up, 158 error=0, 159 outbound_streams=SbOutboundStreams, 160 inbound_streams=SbInboundStreams, 161 assoc_id=AssocId}} -> 162 {AssocId,SbInboundStreams,SbOutboundStreams}; 163 {Loopback,Pa, 164 #sctp_paddr_change{state=addr_confirmed, 165 addr={Loopback,Pa}, 166 error=0, 167 assoc_id=AssocId}} -> 168 {Loopback,Pa, 169 #sctp_assoc_change{state=comm_up, 170 error=0, 171 outbound_streams=SbOutboundStreams, 172 inbound_streams=SbInboundStreams, 173 assoc_id=AssocId}} = 174 recv_event(log_ok(gen_sctp:recv(Sb, infinity))), 175 {AssocId,SbInboundStreams,SbOutboundStreams} 176 end, 177 178 ok = gen_sctp:send(Sa, SaAssocId, 0, Data), 179 case log_ok(gen_sctp:recv(Sb, infinity)) of 180 {Loopback, 181 Pa, 182 [#sctp_sndrcvinfo{stream = Stream, 183 assoc_id = SbAssocId}], 184 Data} -> ok; 185 Event1 -> 186 case recv_event(Event1) of 187 {Loopback,Pa, 188 #sctp_paddr_change{addr = {Loopback,_}, 189 state = State, 190 error = 0, 191 assoc_id = SbAssocId}} 192 when State =:= addr_available; 193 State =:= addr_confirmed -> 194 {Loopback, 195 Pa, 196 [#sctp_sndrcvinfo{stream=Stream, 197 assoc_id=SbAssocId}], 198 Data} = log_ok(gen_sctp:recv(Sb, infinity)) 199 end 200 end, 201 ok = gen_sctp:send(Sb, SbAssocId, 0, Data), 202 case log_ok(gen_sctp:recv(Sa, infinity)) of 203 {Loopback,Pb, 204 [#sctp_sndrcvinfo{stream=Stream, 205 assoc_id=SaAssocId}], 206 Data} -> 207 ok; 208 Event2 -> 209 {Loopback,Pb, 210 #sctp_paddr_change{addr={_,Pb}, 211 state=addr_confirmed, 212 error=0, 213 assoc_id=SaAssocId}} = 214 recv_event(Event2), 215 {Loopback, 216 Pb, 217 [#sctp_sndrcvinfo{stream=Stream, 218 assoc_id=SaAssocId}], 219 Data} = 220 log_ok(gen_sctp:recv(Sa, infinity)) 221 end, 222 %% 223 ok = gen_sctp:eof(Sa, SaAssocChange), 224 {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} = 225 recv_event(log_ok(gen_sctp:recv(Sb, infinity))), 226 {Loopback,Pb, 227 #sctp_assoc_change{state=shutdown_comp, 228 error=0, 229 assoc_id=SaAssocId}} = 230 recv_event(log_ok(gen_sctp:recv(Sa, infinity))), 231 {Loopback,Pa, 232 #sctp_assoc_change{state=shutdown_comp, 233 error=0, 234 assoc_id=SbAssocId}} = 235 recv_event(log_ok(gen_sctp:recv(Sb, infinity))), 236 ok = gen_sctp:close(Sa), 237 {ok,SbStat2} = inet:getstat(Sb, StatOpts), 238 [] = filter_stat_eq(SbStat1, SbStat2), 239 ok = gen_sctp:close(Sb), 240 241 receive 242 Msg -> ct:fail({received,Msg}) 243 after 17 -> ok 244 end, 245 ok. 246 247filter_stat_eq([], []) -> 248 []; 249filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) -> 250 if 251 Val1 == Val2 -> 252 [Stat|filter_stat_eq(SbStat1, SbStat2)]; 253 true -> 254 filter_stat_eq(SbStat1, SbStat2) 255 end. 256 257 258 259%% Minimal data transfer in active mode. 260xfer_active(Config) when is_list(Config) -> 261 Timeout = 2000, 262 Stream = 0, 263 Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, 264 Loopback = {127,0,0,1}, 265 {ok,Sb} = gen_sctp:open([{active,true}]), 266 {ok,Pb} = inet:port(Sb), 267 ok = gen_sctp:listen(Sb, true), 268 269 {ok,Sa} = gen_sctp:open([{active,true}]), 270 {ok,Pa} = inet:port(Sa), 271 ok = gen_sctp:connect_init(Sa, Loopback, Pb, []), 272 #sctp_assoc_change{state=comm_up, 273 error=0, 274 outbound_streams=SaOutboundStreams, 275 inbound_streams=SaInboundStreams, 276 assoc_id=SaAssocId} = SaAssocChange = 277 recv_assoc_change(Sa, Loopback, Pb, Timeout), 278 io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, " 279 "SaOutboundStreams=~p, SaInboundStreams=~p~n", 280 [Sa,Pa,Sb,Pb,SaAssocId, 281 SaOutboundStreams,SaInboundStreams]), 282 #sctp_assoc_change{state=comm_up, 283 error=0, 284 outbound_streams=SbOutboundStreams, 285 inbound_streams=SbInboundStreams, 286 assoc_id=SbAssocId} = 287 recv_assoc_change(Sb, Loopback, Pa, Timeout), 288 SbOutboundStreams = SaInboundStreams, 289 SbInboundStreams = SaOutboundStreams, 290 io:format("SbAssocId=~p~n", [SbAssocId]), 291 292 case recv_paddr_change(Sa, Loopback, Pb, 314) of 293 #sctp_paddr_change{state=addr_confirmed, 294 addr={_,Pb}, 295 error=0, 296 assoc_id=SaAssocId} -> ok; 297 #sctp_paddr_change{state=addr_available, 298 addr={_,Pb}, 299 error=0, 300 assoc_id=SaAssocId} -> ok; 301 timeout -> ok 302 end, 303 case recv_paddr_change(Sb, Loopback, Pa, 314) of 304 #sctp_paddr_change{state=addr_confirmed, 305 addr={Loopback,Pa}, 306 error=0, 307 assoc_id=SbAssocId} -> ok; 308 #sctp_paddr_change{state=addr_available, 309 addr={Loopback,P}, 310 error=0, 311 assoc_id=SbAssocId} -> 312 match_unless_solaris(Pa, P); 313 timeout -> ok 314 end, 315 [] = flush(), 316 317 ok = 318 do_from_other_process( 319 fun () -> gen_sctp:send(Sa, SaAssocId, 0, Data) end), 320 receive 321 {sctp,Sb,Loopback,Pa, 322 {[#sctp_sndrcvinfo{stream=Stream, 323 assoc_id=SbAssocId}], 324 Data}} -> ok 325 after Timeout -> 326 ct:fail({timeout,flush()}) 327 end, 328 ok = gen_sctp:send(Sb, SbAssocId, 0, Data), 329 receive 330 {sctp,Sa,Loopback,Pb, 331 {[#sctp_sndrcvinfo{stream=Stream, 332 assoc_id=SaAssocId}], 333 Data}} -> ok 334 after Timeout -> 335 ct:fail({timeout,flush()}) 336 end, 337 %% 338 ok = gen_sctp:abort(Sa, SaAssocChange), 339 case recv_assoc_change(Sb, Loopback, Pa, Timeout) of 340 #sctp_assoc_change{state=comm_lost, 341 assoc_id=SbAssocId} -> ok; 342 timeout -> 343 ct:fail({timeout,flush()}) 344 end, 345 ok = gen_sctp:close(Sb), 346 case recv_assoc_change(Sa, Loopback, Pb, Timeout) of 347 #sctp_assoc_change{state=comm_lost, 348 assoc_id=SaAssocId} -> ok; 349 timeout -> 350 io:format("timeout waiting for comm_lost on Sa~n"), 351 match_unless_solaris(ok, {timeout,flush()}) 352 end, 353 receive 354 {sctp_error,Sa,enotconn} -> ok % Solaris 355 after 17 -> ok 356 end, 357 ok = gen_sctp:close(Sa), 358 %% 359 receive 360 Msg -> ct:fail({unexpected,[Msg]++flush()}) 361 after 17 -> ok 362 end, 363 ok. 364 365recv_assoc_change(S, Addr, Port, Timeout) -> 366 receive 367 {sctp,S,Addr,Port,{[], #sctp_assoc_change{}=AssocChange}} -> 368 AssocChange; 369 {sctp,S,Addr,Port, 370 {[#sctp_sndrcvinfo{assoc_id=AssocId}], 371 #sctp_assoc_change{assoc_id=AssocId}=AssocChange}} -> 372 AssocChange 373 after Timeout -> 374 timeout 375 end. 376 377recv_paddr_change(S, Addr, Port, Timeout) -> 378 receive 379 {sctp,S,Addr,Port,{[], #sctp_paddr_change{}=PaddrChange}} -> 380 PaddrChange; 381 {sctp,S,Addr,Port, 382 {[#sctp_sndrcvinfo{assoc_id=AssocId}], 383 #sctp_paddr_change{assoc_id=AssocId}=PaddrChange}} -> 384 PaddrChange 385 after Timeout -> 386 timeout 387 end. 388 389%% Test that #sctp_sndrcvinfo{} parameters set on a socket 390%% are used by gen_sctp:send/4. 391def_sndrcvinfo(Config) when is_list(Config) -> 392 Loopback = {127,0,0,1}, 393 Data = <<"What goes up, must come down.">>, 394 %% 395 S1 = 396 log_ok(gen_sctp:open( 397 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])), 398 ?LOGVAR(S1), 399 P1 = 400 log_ok(inet:port(S1)), 401 ?LOGVAR(P1), 402 #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} = 403 getopt(S1, sctp_default_send_param), 404 ok = 405 gen_sctp:listen(S1, true), 406 %% 407 S2 = 408 log_ok(gen_sctp:open()), 409 ?LOGVAR(S2), 410 P2 = 411 log_ok(inet:port(S2)), 412 ?LOGVAR(P2), 413 #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} = 414 getopt(S2, sctp_default_send_param), 415 %% 416 #sctp_assoc_change{ 417 state=comm_up, 418 error=0, 419 assoc_id=S2AssocId} = S2AssocChange = 420 log_ok(gen_sctp:connect(S2, Loopback, P1, [])), 421 ?LOGVAR(S2AssocChange), 422 S1AssocId = 423 case recv_event(log_ok(gen_sctp:recv(S1))) of 424 {Loopback,P2, 425 #sctp_assoc_change{ 426 state=comm_up, 427 error=0, 428 assoc_id=AssocId}} -> 429 AssocId; 430 {Loopback,P2, 431 #sctp_paddr_change{ 432 state=addr_confirmed, 433 error=0, 434 assoc_id=AssocId}} -> 435 {Loopback,P2, 436 #sctp_assoc_change{ 437 state=comm_up, 438 error=0, 439 assoc_id=AssocId}} = 440 recv_event(log_ok(gen_sctp:recv(S1))), 441 AssocId 442 end, 443 ?LOGVAR(S1AssocId), 444 445 #sctp_sndrcvinfo{ 446 ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} = 447 getopt( 448 S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}), 449 #sctp_sndrcvinfo{ 450 ppid=0, context=0, timetolive=0} = %, assoc_id=S2AssocId} = 451 getopt( 452 S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}), 453 %% 454 ok = 455 gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>), 456 case log_ok(gen_sctp:recv(S2)) of 457 {Loopback,P1, 458 [#sctp_sndrcvinfo{ 459 stream=1, ppid=17, context=0, assoc_id=S2AssocId}], 460 <<"1: ",Data/binary>>} -> ok; 461 Event1 -> 462 {Loopback,P1, 463 #sctp_paddr_change{state=addr_confirmed, 464 addr={_,P1}, 465 error=0, 466 assoc_id=S2AssocId}} = 467 recv_event(Event1), 468 {Loopback,P1, 469 [#sctp_sndrcvinfo{ 470 stream=1, ppid=17, context=0, assoc_id=S2AssocId}], 471 <<"1: ",Data/binary>>} = 472 log_ok(gen_sctp:recv(S2)) 473 end, 474 %% 475 ok = 476 setopt( 477 S1, sctp_default_send_param, #sctp_sndrcvinfo{ppid=18}), 478 ok = 479 setopt( 480 S1, sctp_default_send_param, 481 #sctp_sndrcvinfo{ppid=19, assoc_id=S1AssocId}), 482 #sctp_sndrcvinfo{ 483 ppid=18, context=0, timetolive=0, assoc_id=0} = 484 getopt(S1, sctp_default_send_param), 485 #sctp_sndrcvinfo{ 486 ppid=19, context=0, timetolive=0, assoc_id=S1AssocId} = 487 getopt( 488 S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}), 489 %% 490 ok = 491 gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>), 492 case log_ok(gen_sctp:recv(S2)) of 493 {Loopback,P1, 494 [#sctp_sndrcvinfo{ 495 stream=0, ppid=19, context=0, assoc_id=S2AssocId}], 496 <<"2: ",Data/binary>>} -> ok 497 end, 498 ok = 499 gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>), 500 case log_ok(gen_sctp:recv(S1)) of 501 {Loopback,P2, 502 [#sctp_sndrcvinfo{ 503 stream=1, ppid=0, context=0, assoc_id=S1AssocId}], 504 <<"3: ",Data/binary>>} -> ok; 505 Event2 -> 506 case recv_event(Event2) of 507 {Loopback,P2, 508 #sctp_paddr_change{ 509 addr={Loopback,_}, 510 state=State, 511 error=0, assoc_id=S1AssocId}} 512 when State =:= addr_available; 513 State =:= addr_confirmed -> 514 case log_ok(gen_sctp:recv(S1)) of 515 {Loopback,P2, 516 [#sctp_sndrcvinfo{ 517 stream=1, ppid=0, context=0, 518 assoc_id=S1AssocId}], 519 <<"3: ",Data/binary>>} -> ok 520 end 521 end 522 end, 523 ok = 524 do_from_other_process( 525 fun () -> 526 gen_sctp:send( 527 S2, 528 #sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId}, 529 <<"4: ",Data/binary>>) 530 end), 531 case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of 532 {Loopback,P2, 533 [#sctp_sndrcvinfo{ 534 stream=0, ppid=20, context=0, assoc_id=S1AssocId}], 535 <<"4: ",Data/binary>>} -> ok 536 end, 537 %% 538 ok = 539 gen_sctp:close(S1), 540 ok = 541 gen_sctp:close(S2), 542 receive 543 Msg -> 544 ct:fail({received,Msg}) 545 after 17 -> ok 546 end, 547 ok. 548 549getopt(S, Opt) -> 550 {ok,[{Opt,Val}]} = inet:getopts(S, [Opt]), 551 Val. 552 553getopt(S, Opt, Param) -> 554 {ok,[{Opt,Val}]} = inet:getopts(S, [{Opt,Param}]), 555 Val. 556 557setopt(S, Opt, Val) -> 558 inet:setopts(S, [{Opt,Val}]). 559 560 561flush() -> 562 receive 563 Msg -> 564 [Msg|flush()] 565 after 17 -> 566 [] 567 end. 568 569%% Test the API function open/1,2 and close/1. 570api_open_close(Config) when is_list(Config) -> 571 {ok,S1} = gen_sctp:open(0), 572 {ok,P} = inet:port(S1), 573 ok = gen_sctp:close(S1), 574 575 {ok,S2} = gen_sctp:open(P), 576 {ok,P} = inet:port(S2), 577 ok = gen_sctp:close(S2), 578 579 {ok,S3} = gen_sctp:open([{port,P}]), 580 {ok,P} = inet:port(S3), 581 ok = gen_sctp:close(S3), 582 583 {ok,S4} = gen_sctp:open(P, []), 584 {ok,P} = inet:port(S4), 585 ok = gen_sctp:close(S4), 586 587 {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]), 588 {ok,P} = inet:port(S5), 589 ok = gen_sctp:close(S5), 590 591 ok = gen_sctp:close(S5), 592 593 try gen_sctp:close(0) 594 catch error:badarg -> ok 595 end, 596 597 try gen_sctp:open({}) 598 catch error:badarg -> ok 599 end, 600 601 try gen_sctp:open(-1) 602 catch error:badarg -> ok 603 end, 604 605 try gen_sctp:open(65536) 606 catch error:badarg -> ok 607 end, 608 609 try gen_sctp:open(make_ref(), []) 610 catch error:badarg -> ok 611 end, 612 613 try gen_sctp:open(0, {}) 614 catch error:badarg -> ok 615 end, 616 617 try gen_sctp:open(0, [make_ref()]) 618 catch error:badarg -> ok 619 end, 620 621 try gen_sctp:open([{invalid_option,0}]) 622 catch error:badarg -> ok 623 end, 624 625 try gen_sctp:open(0, [{mode,invalid_mode}]) 626 catch error:badarg -> ok 627 end, 628 ok. 629 630%% Test the API function listen/2. 631api_listen(Config) when is_list(Config) -> 632 Localhost = {127,0,0,1}, 633 634 try gen_sctp:listen(0, true) 635 catch error:badarg -> ok 636 end, 637 638 {ok,S} = gen_sctp:open(), 639 {ok,Pb} = inet:port(S), 640 try gen_sctp:listen(S, not_allowed_for_listen) 641 catch error:badarg -> ok 642 end, 643 ok = gen_sctp:close(S), 644 {error,closed} = gen_sctp:listen(S, true), 645 646 {ok,Sb} = gen_sctp:open(Pb), 647 {ok,Sa} = gen_sctp:open(), 648 case gen_sctp:connect(Sa, localhost, Pb, []) of 649 {error,econnrefused} -> 650 {ok,{Localhost, 651 Pb,[], 652 #sctp_assoc_change{ 653 state=comm_lost}}} = 654 gen_sctp:recv(Sa, infinity); 655 {error,#sctp_assoc_change{state=cant_assoc}} -> 656 ok%; 657 %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} -> 658 %% ok 659 end, 660 ok = gen_sctp:listen(Sb, true), 661 {ok,#sctp_assoc_change{state=comm_up, 662 error=0}} = 663 gen_sctp:connect(Sa, localhost, Pb, []), 664 ok = gen_sctp:close(Sa), 665 ok = gen_sctp:close(Sb), 666 ok. 667 668%% Test the API function connect_init/4. 669api_connect_init(Config) when is_list(Config) -> 670 Localhost = {127,0,0,1}, 671 672 {ok,S} = gen_sctp:open(), 673 {ok,Pb} = inet:port(S), 674 try gen_sctp:connect_init(S, Localhost, not_allowed_for_port, []) 675 catch error:badarg -> ok 676 end, 677 try gen_sctp:connect_init(S, Localhost, 12345, not_allowed_for_opts) 678 catch error:badarg -> ok 679 end, 680 ok = gen_sctp:close(S), 681 {error,closed} = gen_sctp:connect_init(S, Localhost, 12345, []), 682 683 {ok,Sb} = gen_sctp:open(Pb), 684 {ok,Sa} = gen_sctp:open(), 685 case gen_sctp:connect_init(Sa, localhost, Pb, []) of 686 {error,econnrefused} -> 687 {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} = 688 recv_event(log_ok(gen_sctp:recv(Sa, infinity))); 689 ok -> 690 {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} = 691 recv_event(log_ok(gen_sctp:recv(Sa, infinity))) 692 end, 693 ok = gen_sctp:listen(Sb, true), 694 case gen_sctp:connect_init(Sa, localhost, Pb, []) of 695 ok -> 696 {Localhost,Pb,#sctp_assoc_change{state=comm_up}} = 697 recv_event(log_ok(gen_sctp:recv(Sa, infinity))) 698 end, 699 ok = gen_sctp:close(Sa), 700 ok = gen_sctp:close(Sb), 701 ok. 702 703recv_event({Addr, Port, [], #sctp_assoc_change{} = AssocChange}) -> 704 {Addr, Port, AssocChange}; 705recv_event({Addr,Port, 706 [#sctp_sndrcvinfo{assoc_id = Assoc}], 707 #sctp_assoc_change{assoc_id = Assoc} = AssocChange}) -> 708 {Addr, Port, AssocChange}; 709recv_event({Addr, Port, [], #sctp_paddr_change{} = PaddrChange}) -> 710 {Addr, Port, PaddrChange}; 711recv_event({Addr, Port, 712 [#sctp_sndrcvinfo{assoc_id = Assoc}], 713 #sctp_paddr_change{assoc_id = Assoc} = PaddrChange}) -> 714 {Addr, Port, PaddrChange}; 715recv_event({Addr, Port, [], #sctp_shutdown_event{} = ShutdownEvent}) -> 716 {Addr, Port, ShutdownEvent}; 717recv_event({Addr, Port, 718 [#sctp_sndrcvinfo{assoc_id = Assoc}], 719 #sctp_shutdown_event{assoc_id = Assoc} = ShutdownEvent}) -> 720 {Addr, Port, ShutdownEvent}. 721 722%% Test socket options. 723api_opts(Config) when is_list(Config) -> 724 Sndbuf = 32768, 725 Recbuf = 65536, 726 {ok,S} = gen_sctp:open(0), 727 OSType = os:type(), 728 case {inet:setopts(S, [{linger,{true,2}}]),OSType} of 729 {ok,_} -> 730 ok; 731 {{error,einval},{unix,sunos}} -> 732 ok 733 end, 734 ok = inet:setopts(S, [{sndbuf,Sndbuf}]), 735 ok = inet:setopts(S, [{recbuf,Recbuf}]), 736 case inet:getopts(S, [sndbuf]) of 737 {ok, [{sndbuf,SB}]} when SB >= Sndbuf -> ok 738 end, 739 case inet:getopts(S, [recbuf]) of 740 {ok, [{recbuf, RB}]} when (RB >= Recbuf) -> ok 741 end. 742 743%% What is this *actually* supposed to test? 744implicit_inet6(Config) when is_list(Config) -> 745 ?TC_TRY(implicit_inet6, fun() -> do_implicit_inet6(Config) end). 746 747do_implicit_inet6(_Config) -> 748 ?P("begin"), 749 %% First 750 ?P("try create server socket (1)"), 751 case gen_sctp:open(0, [inet6]) of 752 {ok, S1} -> 753 Loopback = {0,0,0,0,0,0,0,1}, 754 ?P("*** ~s: ~p ***", ["Loopback", Loopback]), 755 implicit_inet6(S1, Loopback), 756 ok = gen_sctp:close(S1), 757 758 %% Second 759 ?P("try create server socket (2)"), 760 Localhost = 761 case inet:getaddr("localhost", inet6) of 762 {ok, LH} -> 763 LH; 764 {error, nxdomain = Reason_getaddr} -> 765 ?SKIPT(Reason_getaddr); 766 {error, Reason_getaddr} -> 767 ?line ct:fail({unexpected, Reason_getaddr}) 768 end, 769 S2 = case gen_sctp:open(0, [{ip, Localhost}]) of 770 {ok, S} -> 771 S; 772 {error, nxdomain = Reason_open} -> 773 ?SKIPT(Reason_open); 774 {error, Reason_open} -> 775 ?line ct:fail({unexpected, Reason_open}) 776 end, 777 778 ?P("*** ~s: ~p ***", ["localhost", Localhost]), 779 implicit_inet6(S2, Localhost), 780 ok = gen_sctp:close(S2), 781 782 %% Third 783 ?P("try create server socket (3)"), 784 Hostname = log_ok(inet:gethostname()), 785 Addr = case inet:getaddr(Hostname, inet6) of 786 {ok, A} -> 787 A; 788 {error, eafnosupport} -> 789 ok = gen_sctp:close(S1), 790 ?SKIPT("Can not look up IPv6 address") 791 end, 792 S3 = log_ok(gen_sctp:open(0, [{ifaddr, Addr}])), 793 ?P("*** ~s: ~p ***", [Hostname, Addr]), 794 implicit_inet6(S3, Addr), 795 ok = gen_sctp:close(S1), 796 ?P("done"), 797 ok; 798 {error, eaddrnotavail = Reason} -> 799 ?SKIPT(open_failed_str(Reason)); 800 _ -> 801 {skip, "IPv6 not supported"} 802 end. 803 804 805implicit_inet6(S1, Addr) -> 806 ?P("make (server) listen socket"), 807 ok = gen_sctp:listen(S1, true), 808 ServerPortNo = log_ok(inet:port(S1)), 809 ?P("try create (client) socket"), 810 S2 = case gen_sctp:open(0, [inet6, {ifaddr, Addr}]) of 811 {ok, Sock} -> 812 ?P("client socket created: ~p", [Sock]), 813 Sock; 814 {error, eaddrnotavail = Reason} -> 815 ?P("could not create (client) socket with ifaddr: " 816 "~n ~p", [Addr]), 817 ?SKIPT(open_failed_str(Reason)) 818 end, 819 {ClientAddr, ClientPortNo} = log_ok(inet:sockname(S2)), 820 ?P("try connect" 821 "~n from (connector): ~p, ~p (~p)" 822 "~n to: ~p, ~p", 823 [ClientAddr, ClientPortNo, S2, Addr, ServerPortNo]), 824 #sctp_assoc_change{state = comm_up} = 825 log_ok(gen_sctp:connect(S2, Addr, ServerPortNo, [])), 826 ?P("connect success: await events"), 827 implicit_inet6_await_ac_comm_up(S1, ClientAddr, ClientPortNo), 828 ?P("verify server sockname"), 829 case log_ok(inet:sockname(S1)) of 830 {Addr, ServerPortNo} -> ok; 831 {{0,0,0,0,0,0,0,0}, ServerPortNo} -> ok 832 end, 833 ?P("verify client sockname"), 834 case log_ok(inet:sockname(S2)) of 835 {Addr, ClientPortNo} -> ok; 836 {{0,0,0,0,0,0,0,0}, ClientPortNo} -> ok 837 end, 838 ?P("client client socket"), 839 ok = gen_sctp:close(S2), 840 ?P("verification complete"), 841 ok. 842 843 844implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo) -> 845 {_OsFam, OsName} = os:type(), 846 implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName). 847 848implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName) -> 849 case recv_event(log_ok(gen_sctp:recv(Sock))) of 850 {Addr, PortNo, #sctp_assoc_change{state = comm_up}} -> 851 ?P("received assoc-change:comm-up event => done"), 852 ok; 853 {Addr, PortNo, #sctp_paddr_change{state = addr_confirmed, 854 addr = {Addr, PortNo}, 855 error = 0}} -> 856 ?P("received paddr-change:addr-confirmed event - " 857 "try recv assoc-change:comm-up"), 858 implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName); 859 860 {Addr2, PortNo2, #sctp_assoc_change{state = comm_up}} 861 when (OsName =:= freebsd) -> 862 ?P("Expected (assoc-change:comm-up) event from unexpected address: " 863 "~n Unexpected Address: ~p, ~p" 864 "~n Expected Address: ~p, ~p" 865 "~n => RETRY" 866 "~n", [Addr2, PortNo2, Addr, PortNo]), 867 implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName); 868 {Addr2, PortNo2, #sctp_paddr_change{state = addr_confirmed}} 869 when (OsName =:= freebsd) -> 870 ?P("Expected paddr-change:addr-confirmed event from " 871 "UNEXPECTED ADDRESS: " 872 "~n UNEXPECTED Address: ~p, ~p" 873 "~n Expected Address: ~p, ~p" 874 "~n => RETRY" 875 "~n", [Addr2, PortNo2, Addr, PortNo]), 876 implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName); 877 878 {Addr2, PortNo2, #sctp_assoc_change{state = comm_up} = CX} = UNEX -> 879 ?P("Expected (assoc-change:comm-up) event from UNEXPECTED ADDRESS: " 880 "~n UNEXPECTED Address: ~p, ~p" 881 "~n Expected Address: ~p, ~p" 882 "~n Assoc Change: ~p" 883 "~n", [Addr2, PortNo2, Addr, PortNo, CX]), 884 exit({unexpected_event, UNEX}); 885 886 {AX, PX, #sctp_paddr_change{state = addr_confirmed} = CX} = UNEX -> 887 ?P("Expected paddr-change:addr-confirmed event from " 888 "UNEXPECTED ADDRESS: " 889 "~n UNEXPECTED Address: ~p, ~p" 890 "~n Expected Address: ~p, ~p" 891 "~n PAddr Change: ~p" 892 "~n", [AX, PX, Addr, PortNo, CX]), 893 exit({unexpected_event, UNEX}); 894 895 {AX, PX, CX} = UNEX -> 896 ?P("UNEXPECTED EVENT: " 897 "~n ~p" 898 "~n UNEXPECTED ADDRESS: ~p, ~p" 899 "~n Expected Address: ~p, ~p" 900 "~n", [CX, AX, PX, Addr, PortNo]), 901 exit({unexpected_event, UNEX}) 902 end. 903 904 905%% Verify {active, N} socket management. 906%% This is difficult to do since we do not just receive data messages. 907%% Also, how do we know that sctp behaves the same way on all platforms? 908active_n(Config) when is_list(Config) -> 909 ?TC_TRY(active_n, fun() -> do_active_n(Config) end). 910 911do_active_n(_Config) -> 912 N = 3, 913 S1 = ok(gen_sctp:open([{active,N}])), 914 [{active,N}] = ok(inet:getopts(S1, [active])), 915 ok = inet:setopts(S1, [{active,-N}]), 916 receive 917 {sctp_passive, S1} -> ok 918 after 919 5000 -> 920 exit({error,sctp_passive_failure}) 921 end, 922 [{active,false}] = ok(inet:getopts(S1, [active])), 923 ok = inet:setopts(S1, [{active,0}]), 924 receive 925 {sctp_passive, S1} -> ok 926 after 927 5000 -> 928 exit({error,sctp_passive_failure}) 929 end, 930 ok = inet:setopts(S1, [{active,32767}]), 931 {error,einval} = inet:setopts(S1, [{active,1}]), 932 {error,einval} = inet:setopts(S1, [{active,-32769}]), 933 ok = inet:setopts(S1, [{active,-32768}]), 934 receive 935 {sctp_passive, S1} -> ok 936 after 937 5000 -> 938 exit({error,sctp_passive_failure}) 939 end, 940 [{active,false}] = ok(inet:getopts(S1, [active])), 941 ok = inet:setopts(S1, [{active,N}]), 942 ok = inet:setopts(S1, [{active,true}]), 943 [{active,true}] = ok(inet:getopts(S1, [active])), 944 receive 945 _ -> exit({error,active_n}) 946 after 947 0 -> 948 ok 949 end, 950 ok = inet:setopts(S1, [{active,N}]), 951 ok = inet:setopts(S1, [{active,once}]), 952 [{active,once}] = ok(inet:getopts(S1, [active])), 953 receive 954 _ -> exit({error,active_n}) 955 after 956 0 -> 957 ok 958 end, 959 {error,einval} = inet:setopts(S1, [{active,32768}]), 960 ok = inet:setopts(S1, [{active,false}]), 961 [{active,false}] = ok(inet:getopts(S1, [active])), 962 ok = gen_sctp:listen(S1, true), 963 S1Port = ok(inet:port(S1)), 964 S2 = ok(gen_sctp:open(0, [{active,false}])), 965 Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])), 966 ok = inet:setopts(S1, [{active,N}]), 967 active_n_flush_connect_msgs(S1), 968 active_n_send_loop(N, S2, Assoc, S1), 969 S3 = ok(gen_sctp:open([{active,0}])), 970 receive 971 {sctp_passive,S3} -> 972 [{active,false}] = ok(inet:getopts(S3, [active])) 973 after 974 5000 -> 975 exit({error,udp_passive}) 976 end, 977 ok = gen_sctp:close(S3), 978 ok = gen_sctp:close(S2), 979 ok = gen_sctp:close(S1), 980 ok. 981 982 983%% There is no way to know how many addresses this host has, 984%% and if there is "too many" (more then N = 3), then the 985%% socket may already be passive. In this case the send- 986%% loop will fail. 987%% So, if we get a passive-message here, we just give up (=skip). 988active_n_flush_connect_msgs(Sock) -> 989 %% This seems only to be needed on certain platforms 990 active_n_flush_connect_msgs(os:type(), Sock). 991 992active_n_flush_connect_msgs(_, Sock) -> 993 do_active_n_flush_connect_msgs(Sock). 994%% active_n_flush_connect_msgs({unix, freebsd}, Sock) -> 995%% do_active_n_flush_connect_msgs(Sock); 996%% active_n_flush_connect_msgs(_, _) -> 997%% ok. 998 999do_active_n_flush_connect_msgs(Sock) -> 1000 receive 1001 {sctp_passive, Sock} -> 1002 ?P("connect-flush-loop -> premature passive"), 1003 ?SKIPT("Too many addresses (premature passive)"); 1004 1005 {sctp, Sock, 1006 _FromIP, _FromPort, 1007 {[], #sctp_assoc_change{state = comm_up}}} -> 1008 ?P("connect-flush-loop -> " 1009 "connect message discard - assoc change : comm-up"), 1010 ok = inet:setopts(Sock, [{active, 1}]), 1011 do_active_n_flush_connect_msgs(Sock); 1012 1013 {sctp, Sock, 1014 _FromIP, _FromPort, 1015 {[], #sctp_paddr_change{state = addr_confirmed, 1016 addr = Addr, 1017 error = Error, 1018 assoc_id = AID}}} -> 1019 ?P("connect-flush-loop -> " 1020 "connect message discard - paddr change : addr-confirmed:" 1021 "~n Addr: ~p" 1022 "~n Error: ~p" 1023 "~n AssocID: ~p", [Addr, Error, AID]), 1024 ok = inet:setopts(Sock, [{active, 1}]), 1025 do_active_n_flush_connect_msgs(Sock) 1026 1027 after 5000 -> 1028 ok 1029 end. 1030 1031active_n_send_loop(Count, SrcSock, SndAssoc, DstSock) -> 1032 active_n_send_loop(0, Count, SrcSock, SndAssoc, DstSock). 1033 1034active_n_send_loop(Count, Count, _SndSock, _SndAssoc, RcvSock) -> 1035 ?P("send-loop -> we are done - wait for passive"), 1036 receive 1037 {sctp_passive, RcvSock} -> 1038 ?P("received passive"), 1039 ok 1040 after 1041 5000 -> 1042 ?P("UNEXPECTED TIMEOUT: " 1043 "~n Message Queue: ~p" 1044 "~n Active: ~p", 1045 [process_info(self(), messages), 1046 inet:getopts(RcvSock, [active])]), 1047 exit({error, timeout}) 1048 end; 1049 1050active_n_send_loop(Sent, Count, SndSock, SndAssoc, RcvSock) -> 1051 Msg = list_to_binary("message " ++ integer_to_list(Sent+1)), 1052 ?P("send-loop(~w,~w) -> send message (on ~p)", [Sent, Count, SndSock]), 1053 ok = gen_sctp:send(SndSock, SndAssoc, 0, Msg), 1054 receive 1055 {sctp, RcvSock, FromIP, FromPort, {[SR], Msg}} 1056 when is_record(SR, sctp_sndrcvinfo) -> 1057 ?P("send-loop(~w,~w) -> " 1058 "recv (expected) data message (on ~p):" 1059 "~n Msg: ~p" 1060 "~n From: ~p, ~p", 1061 [Sent, Count, 1062 RcvSock, Msg, FromIP, FromPort]), 1063 active_n_send_loop(Sent+1, Count, SndSock, SndAssoc, RcvSock); 1064 1065 {sctp, RcvSock, _FromIP, _FromPort, {_AncData, _Data}} -> 1066 %% ignore non-data messages 1067 %% we should not get any here because of the flush loop, 1068 %% but just in case... 1069 ?P("send-loop(~w,~w) -> " 1070 "ignore non-data messages (on ~p):" 1071 "~n From: ~p:~p" 1072 "~n AncData: ~p" 1073 "~n Data: ~p", 1074 [Sent, Count, 1075 RcvSock, _FromIP, _FromPort, _AncData, _Data]), 1076 1077 %% It may be too late to update here, 1078 %% the socket may already have gone passive 1079 %% and generated a passive message! 1080 1081 ok = inet:setopts(RcvSock, [{active, 1}]), 1082 1083 active_n_send_loop(Sent, Count, SndSock, SndAssoc, RcvSock); 1084 1085 Other -> 1086 ?P("send-loop(~w,~w) -> " 1087 "UNEXPECTED: " 1088 "~n Other: ~p" 1089 "~n Send Sock: ~p" 1090 "~n Recv Sock: ~p", [Sent, Count, 1091 Other, SndSock, RcvSock]), 1092 exit({unexpected, Other}) 1093 after 1094 5000 -> 1095 exit({error,timeout}) 1096 end. 1097 1098%% Hello world stream socket. 1099basic_stream(Config) when is_list(Config) -> 1100 {ok,S} = gen_sctp:open([{type,stream}]), 1101 ok = gen_sctp:listen(S, true), 1102 ok = 1103 do_from_other_process( 1104 fun () -> gen_sctp:listen(S, 10) end), 1105 ok = gen_sctp:close(S), 1106 ok. 1107 1108%% Minimal data transfer. 1109xfer_stream_min(Config) when is_list(Config) -> 1110 {_, OSName} = os:type(), 1111 Stream = 0, 1112 Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, 1113 Loopback = {127,0,0,1}, 1114 {ok,Sb} = gen_sctp:open([{type,seqpacket}]), 1115 ?LOGVAR(Sb), 1116 {ok,Pb} = inet:port(Sb), 1117 ?LOGVAR(Pb), 1118 ok = gen_sctp:listen(Sb, true), 1119 1120 {ok,Sa} = gen_sctp:open([{type,stream}]), 1121 ?LOGVAR(Sa), 1122 {ok,Pa} = inet:port(Sa), 1123 ?LOGVAR(Pa), 1124 #sctp_assoc_change{state=comm_up, 1125 error=0, 1126 outbound_streams=SaOutboundStreams, 1127 inbound_streams=SaInboundStreams, 1128 assoc_id=SaAssocId_X} = 1129 log_ok(gen_sctp:connect(Sa, Loopback, Pb, [])), 1130 ?LOGVAR(SaAssocId_X), 1131 [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] = 1132 log_ok(inet:getopts(Sa, [{sctp_get_peer_addr_info, 1133 #sctp_paddrinfo{address={Loopback,Pb}}}])), 1134 ?LOGVAR(SaAssocId), 1135 match_unless_solaris(SaAssocId_X, SaAssocId), 1136 1137 {SbOutboundStreams,SbInboundStreams,SbAssocId} = 1138 case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of 1139 {Loopback,Pa, 1140 #sctp_assoc_change{state=comm_up, 1141 error=0, 1142 outbound_streams=OS, 1143 inbound_streams=IS, 1144 assoc_id=AI}} -> 1145 {OS,IS,AI}; 1146 {Loopback,Pa, 1147 #sctp_paddr_change{state=addr_confirmed, 1148 addr={Loopback,Pa}, 1149 error=0, 1150 assoc_id=AI}} -> 1151 {Loopback,Pa, 1152 #sctp_assoc_change{state=comm_up, 1153 error=0, 1154 outbound_streams=OS, 1155 inbound_streams=IS, 1156 assoc_id=AI}} = 1157 recv_event(log_ok(gen_sctp:recv(Sb, infinity))), 1158 {OS,IS,AI} 1159 end, 1160 ?LOGVAR(SbAssocId), 1161 SaOutboundStreams = SbInboundStreams, 1162 ?LOGVAR(SaOutboundStreams), 1163 SbOutboundStreams = SaInboundStreams, 1164 ?LOGVAR(SbOutboundStreams), 1165 ok = gen_sctp:send(Sa, SaAssocId, 0, Data), 1166 case log_ok(gen_sctp:recv(Sb, infinity)) of 1167 {Loopback, 1168 Pa, 1169 [#sctp_sndrcvinfo{stream = Stream, 1170 assoc_id = SbAssocId}], 1171 Data} -> 1172 ?P("[1] received expected data with ancillary data => done"), 1173 ok; 1174 1175 {Loopback, 1176 Pa, 1177 [], 1178 #sctp_paddr_change{addr = {Loopback,_}, 1179 state = addr_available, 1180 error = 0, 1181 assoc_id = SbAssocId}} -> 1182 ?P("[2] received paddr change => recv again"), 1183 Res2 = log_ok(gen_sctp:recv(Sb, infinity)), 1184 ?P("[2] recv ok => " 1185 "~n ~p", [Res2]), 1186 {Loopback, 1187 Pa, 1188 [#sctp_sndrcvinfo{stream = Stream, 1189 assoc_id = SbAssocId}], 1190 Data} = Res2, 1191 ?P("[2] received expected data with ancillary data => done"), 1192 Res2; 1193 1194 {Loopback, 1195 Pa, 1196 [#sctp_sndrcvinfo{stream = Stream, 1197 assoc_id = SbAssocId}], 1198 #sctp_paddr_change{addr = {Loopback,_}, 1199 state = addr_confirmed, 1200 error = 0, 1201 assoc_id = SbAssocId}} -> 1202 ?P("[3] received paddr change with ancillary data => recv again"), 1203 Res3 = log_ok(gen_sctp:recv(Sb, infinity)), 1204 ?P("[3] recv ok => " 1205 "~n ~p", [Res3]), 1206 {Loopback, 1207 Pa, 1208 [#sctp_sndrcvinfo{stream = Stream, 1209 assoc_id = SbAssocId}], 1210 Data} = Res3, 1211 ?P("[3] received expected data with ancillary data => done"), 1212 Res3; 1213 1214 %% It seems that on FreeBSD (for instance) we don't get any 1215 %% AncData with this. 1216 {Loopback, 1217 Pa, 1218 [], 1219 #sctp_paddr_change{addr = {Loopback,_}, 1220 state = addr_confirmed, 1221 error = 0, 1222 assoc_id = SbAssocId}} when (OSName =:= freebsd) -> 1223 ?P("[4] received paddr change without ancillary data => " 1224 "recv again"), 1225 Res4 = log_ok(gen_sctp:recv(Sb, infinity)), 1226 ?P("[4] recv ok => " 1227 "~n ~p", [Res4]), 1228 {Loopback, 1229 Pa, 1230 [#sctp_sndrcvinfo{stream = Stream, 1231 assoc_id = SbAssocId}], 1232 Data} = Res4, 1233 ?P("[4] received expected data with ancillary data => done"), 1234 Res4; 1235 1236 {FromIPX, FromPortX, AncDataX, DataX} = Other1 -> 1237 ?P("UNEXPECTED: " 1238 "~n FromIP: ~p" 1239 "~n FromPort: ~p" 1240 "~n AncData: ~p" 1241 "~n Data: ~p" 1242 "~nwhen" 1243 "~n Loopback: ~p" 1244 "~n Pa: ~p", 1245 [FromIPX, FromPortX, AncDataX, DataX, Loopback, Pa]), 1246 exit({unexpected, Other1}); 1247 Other2 -> 1248 ?P("UNEXPECTED: " 1249 "~n Other: ~p" 1250 "~nwhen" 1251 "~n Loopback: ~p" 1252 "~n Pa: ~p", 1253 [Other2, Loopback, Pa]), 1254 exit({unexpected, Other2}) 1255 end, 1256 ok = 1257 do_from_other_process( 1258 fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end), 1259 case log_ok(gen_sctp:recv(Sa, infinity)) of 1260 {Loopback,Pb, 1261 [#sctp_sndrcvinfo{stream=Stream, 1262 assoc_id=SaAssocId}], 1263 Data} -> ok; 1264 Event1 -> 1265 {Loopback,Pb, 1266 #sctp_paddr_change{state=addr_confirmed, 1267 addr={_,Pb}, 1268 error=0, 1269 assoc_id=SaAssocId}} = 1270 recv_event(Event1), 1271 {Loopback,Pb, 1272 [#sctp_sndrcvinfo{stream=Stream, 1273 assoc_id=SaAssocId}], 1274 Data} = 1275 log_ok(gen_sctp:recv(Sa, infinity)) 1276 end, 1277 ok = gen_sctp:close(Sa), 1278 {Loopback,Pa, 1279 #sctp_shutdown_event{assoc_id=SbAssocId}} = 1280 recv_event(log_ok(gen_sctp:recv(Sb, infinity))), 1281 {Loopback,Pa, 1282 #sctp_assoc_change{state=shutdown_comp, 1283 error=0, 1284 assoc_id=SbAssocId}} = 1285 recv_event(log_ok(gen_sctp:recv(Sb, infinity))), 1286 ok = gen_sctp:close(Sb), 1287 1288 receive 1289 Msg -> ct:fail({received,Msg}) 1290 after 17 -> ok 1291 end, 1292 ok. 1293 1294 1295 1296do_from_other_process(Fun) -> 1297 Parent = self(), 1298 Ref = make_ref(), 1299 Child = 1300 spawn(fun () -> 1301 try Fun() of 1302 Result -> 1303 Parent ! {Ref,Result} 1304 catch 1305 Class:Reason:Stacktrace -> 1306 Parent ! {Ref,Class,Reason,Stacktrace} 1307 end 1308 end), 1309 Mref = erlang:monitor(process, Child), 1310 receive 1311 {Ref,Result} -> 1312 receive {'DOWN',Mref,_,_,_} -> Result end; 1313 {Ref,Class,Reason,Stacktrace} -> 1314 receive {'DOWN',Mref,_,_,_} -> 1315 erlang:raise(Class, Reason, Stacktrace) 1316 end; 1317 {'DOWN',Mref,_,_,Reason} -> 1318 erlang:exit(Reason) 1319 end. 1320 1321 1322%% Peel off an SCTP stream socket ({active,once}). 1323 1324peeloff_active_once(Config) -> 1325 peeloff(Config, [{active,once}]). 1326 1327%% Peel off an SCTP stream socket ({active,true}). 1328 1329peeloff_active_true(Config) -> 1330 peeloff(Config, [{active,true}]). 1331 1332%% Peel off an SCTP stream socket ({active,N}). 1333 1334peeloff_active_n(Config) -> 1335 peeloff(Config, [{active,1}]). 1336 1337peeloff(Config, SockOpts) when is_list(Config) -> 1338 Addr = {127,0,0,1}, 1339 Stream = 0, 1340 Timeout = 333, 1341 StartTime = timestamp(), 1342 S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), 1343 ?LOGVAR(S1), 1344 P1 = socket_call(S1, get_port), 1345 ?LOGVAR(P1), 1346 Socket1 = socket_call(S1, get_socket), 1347 ?LOGVAR(Socket1), 1348 socket_call(S1, {listen,true}), 1349 S2 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), 1350 ?LOGVAR(S2), 1351 P2 = socket_call(S2, get_port), 1352 ?LOGVAR(P2), 1353 Socket2 = socket_call(S2, get_socket), 1354 ?LOGVAR(Socket2), 1355 %% 1356 socket_call(S2, {connect_init,Addr,P1,[]}), 1357 S2Ai = 1358 receive 1359 {S2,{Addr,P1, 1360 #sctp_assoc_change{ 1361 state=comm_up, 1362 assoc_id=AssocId2}}} -> AssocId2 1363 after Timeout -> 1364 socket_bailout([S1,S2], StartTime) 1365 end, 1366 ?LOGVAR(S2Ai), 1367 S1Ai = 1368 receive 1369 {S1,{Addr,P2, 1370 #sctp_assoc_change{ 1371 state=comm_up, 1372 assoc_id=AssocId1}}} -> AssocId1 1373 after Timeout -> 1374 socket_bailout([S1,S2], StartTime) 1375 end, 1376 ?LOGVAR(S1Ai), 1377 %% 1378 socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}), 1379 receive 1380 {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok 1381 after Timeout -> 1382 socket_bailout([S1,S2], StartTime) 1383 end, 1384 socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}), 1385 receive 1386 {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok 1387 after Timeout -> 1388 socket_bailout([S1,S2], StartTime) 1389 end, 1390 %% 1391 S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout), 1392 ?LOGVAR(S3), 1393 P3_X = socket_call(S3, get_port), 1394 ?LOGVAR(P3_X), 1395 P3 = case P3_X of 0 -> P1; _ -> P3_X end, 1396 [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] = 1397 socket_call(S3, 1398 {getopts,[{sctp_get_peer_addr_info, 1399 #sctp_paddrinfo{address={Addr,P2}}}]}), 1400 %%S3Ai = S1Ai, 1401 ?LOGVAR(S3Ai), 1402 %% 1403 socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}), 1404 receive 1405 {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok 1406 after Timeout -> 1407 socket_bailout([S1,S2,S3], StartTime) 1408 end, 1409 socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}), 1410 receive 1411 {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok 1412 after Timeout -> 1413 socket_bailout([S1,S2,S3], StartTime) 1414 end, 1415 %% 1416 inet:i(sctp), 1417 socket_close_verbose(S1, StartTime), 1418 socket_close_verbose(S2, StartTime), 1419 receive 1420 {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} -> 1421 match_unless_solaris(S3Ai, S3Ai_X) 1422 after Timeout -> 1423 socket_bailout([S3], StartTime) 1424 end, 1425 receive 1426 {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp, 1427 assoc_id=S3Ai}}} -> ok 1428 after Timeout -> 1429 socket_bailout([S3], StartTime) 1430 end, 1431 socket_close_verbose(S3, StartTime), 1432 [] = flush(), 1433 ok. 1434 1435 1436 1437%% Check sndbuf and recbuf behaviour. 1438buffers(Config) when is_list(Config) -> 1439 Limit = 4096, 1440 Addr = {127,0,0,1}, 1441 Stream = 1, 1442 Timeout = 3333, 1443 StartTime = timestamp(), 1444 S1 = socket_open([{ip,Addr}], Timeout), 1445 ?LOGVAR(S1), 1446 P1 = socket_call(S1, get_port), 1447 ?LOGVAR(P1), 1448 ok = socket_call(S1, {listen,true}), 1449 S2 = socket_open([{ip,Addr}], Timeout), 1450 ?LOGVAR(S2), 1451 P2 = socket_call(S2, get_port), 1452 ?LOGVAR(P2), 1453 %% 1454 socket_call(S2, {connect_init,Addr,P1,[]}), 1455 S2Ai = 1456 receive 1457 {S2,{Addr,P1, 1458 #sctp_assoc_change{ 1459 state=comm_up, 1460 assoc_id=AssocId2}}} -> AssocId2 1461 after Timeout -> 1462 socket_bailout([S1,S2], StartTime) 1463 end, 1464 S1Ai = 1465 receive 1466 {S1,{Addr,P2, 1467 #sctp_assoc_change{ 1468 state=comm_up, 1469 assoc_id=AssocId1}}} -> AssocId1 1470 after Timeout -> 1471 socket_bailout([S1,S2], StartTime) 1472 end, 1473 %% 1474 socket_call(S1, {setopts,[{recbuf,Limit}]}), 1475 Recbuf = 1476 case socket_call(S1, {getopts,[recbuf]}) of 1477 [{recbuf,RB1}] when RB1 >= Limit -> RB1 1478 end, 1479 Data = mk_data(Recbuf+Limit), 1480 socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}), 1481 socket_call(S2, {send,S2Ai,Stream,Data}), 1482 receive 1483 {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok 1484 after Timeout -> 1485 socket_bailout([S1,S2], StartTime) 1486 end, 1487 %% 1488 socket_close_verbose(S1, StartTime), 1489 receive 1490 {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok 1491 after Timeout -> 1492 socket_bailout([S2], StartTime) 1493 end, 1494 receive 1495 {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp, 1496 assoc_id=S2Ai}}} -> ok 1497 after Timeout -> 1498 socket_bailout([S2], StartTime) 1499 end, 1500 socket_close_verbose(S2, StartTime), 1501 [] = flush(), 1502 ok. 1503 1504mk_data(Bytes) -> 1505 mk_data(0, Bytes, <<>>). 1506%% 1507mk_data(N, Bytes, Bin) when N < Bytes -> 1508 mk_data(N+4, Bytes, <<Bin/binary,N:32>>); 1509mk_data(_, _, Bin) -> 1510 Bin. 1511 1512 1513 1514%% Test opening a multihoming ipv4 socket. 1515open_multihoming_ipv4_socket(Config) when is_list(Config) -> 1516 ?P("get addrs by family (inet)"), 1517 case get_addrs_by_family(inet, 2) of 1518 {ok, [Addr1, Addr2]} -> 1519 ?P("got addrs: " 1520 "~n Addr1: ~p" 1521 "~n Addr2: ~p", [Addr1, Addr2]), 1522 do_open_and_connect([Addr1, Addr2], Addr1); 1523 {error, Reason} -> 1524 ?P("failed get addrs: " 1525 "~n ~p", [Reason]), 1526 {skip, Reason} 1527 end. 1528 1529%% This test is mostly aimed to indicate whether host has a 1530%% non-working ipv6 setup. Test opening a unihoming (non-multihoming) 1531%% ipv6 socket. 1532open_unihoming_ipv6_socket(Config) when is_list(Config) -> 1533 case get_addrs_by_family(inet6, 1) of 1534 {ok, [Addr]} -> 1535 do_open_and_connect([Addr], Addr); 1536 {error, Reason} -> 1537 {skip, Reason} 1538 end. 1539 1540 1541%% Test opening a multihoming ipv6 socket. 1542open_multihoming_ipv6_socket(Config) when is_list(Config) -> 1543 case get_addrs_by_family(inet6, 2) of 1544 {ok, [Addr1, Addr2]} -> 1545 do_open_and_connect([Addr1, Addr2], Addr1); 1546 {error, Reason} -> 1547 {skip, Reason} 1548 end. 1549 1550%% Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses. 1551open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) -> 1552 case get_addrs_by_family(inet_and_inet6, 2) of 1553 {ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} -> 1554 %% Connect to the first address to test bind 1555 do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2], 1556 InetAddr1), 1557 do_open_and_connect([Inet6Addr1, InetAddr1], 1558 Inet6Addr1), 1559 1560 %% Connect an address, not the first, 1561 %% to test sctp_bindx 1562 do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], 1563 Inet6Addr2), 1564 do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1], 1565 InetAddr1); 1566 {error, Reason} -> 1567 {skip, Reason} 1568 end. 1569 1570%% Test inet:socknames/peernames on unihoming IPv4 sockets. 1571names_unihoming_ipv4(Config) when is_list(Config) -> 1572 do_names(Config, inet, 1). 1573 1574%% Test inet:socknames/peernames on unihoming IPv6 sockets. 1575names_unihoming_ipv6(Config) when is_list(Config) -> 1576 do_names(Config, inet6, 1). 1577 1578%% Test inet:socknames/peernames on multihoming IPv4 sockets. 1579names_multihoming_ipv4(Config) when is_list(Config) -> 1580 do_names(Config, inet, 2). 1581 1582%% Test inet:socknames/peernames on multihoming IPv6 sockets. 1583names_multihoming_ipv6(Config) when is_list(Config) -> 1584 do_names(Config, inet6, 2). 1585 1586 1587 1588do_names(_, FamilySpec, AddressCount) -> 1589 Fun = 1590 fun (ServerSocket, _, ServerAssoc, ClientSocket, _, ClientAssoc) -> 1591 ServerSocknamesNoassoc = 1592 lists:sort(ok(inet:socknames(ServerSocket))), 1593 ?LOGVAR(ServerSocknamesNoassoc), 1594 ServerSocknames = 1595 lists:sort(ok(inet:socknames(ServerSocket, ServerAssoc))), 1596 ?LOGVAR(ServerSocknames), 1597 [_|_] = 1598 ordsets:intersection 1599 (ServerSocknamesNoassoc, ServerSocknames), 1600 ClientSocknamesNoassoc = 1601 lists:sort(ok(inet:socknames(ClientSocket))), 1602 ?LOGVAR(ClientSocknamesNoassoc), 1603 ClientSocknames = 1604 lists:sort(ok(inet:socknames(ClientSocket, ClientAssoc))), 1605 ?LOGVAR(ClientSocknames), 1606 [_|_] = 1607 ordsets:intersection 1608 (ClientSocknamesNoassoc, ClientSocknames), 1609 err([einval,enotconn], inet:peernames(ServerSocket)), 1610 ServerPeernames = 1611 lists:sort(ok(inet:peernames(ServerSocket, ServerAssoc))), 1612 ?LOGVAR(ServerPeernames), 1613 err([einval,enotconn], inet:peernames(ClientSocket)), 1614 ClientPeernames = 1615 lists:sort(ok(inet:peernames(ClientSocket, ClientAssoc))), 1616 ?LOGVAR(ClientPeernames), 1617 ServerSocknames = ClientPeernames, 1618 ClientSocknames = ServerPeernames, 1619 {ok,Socket} = 1620 gen_sctp:peeloff(ServerSocket, ServerAssoc), 1621 SocknamesNoassoc = 1622 lists:sort(ok(inet:socknames(Socket))), 1623 ?LOGVAR(SocknamesNoassoc), 1624 Socknames = 1625 lists:sort(ok(inet:socknames(Socket, ServerAssoc))), 1626 ?LOGVAR(Socknames), 1627 true = 1628 ordsets:is_subset(SocknamesNoassoc, Socknames), 1629 Peernames = 1630 lists:sort(ok(inet:peernames(Socket, ServerAssoc))), 1631 ?LOGVAR(Peernames), 1632 ok = gen_sctp:close(Socket), 1633 Socknames = ClientPeernames, 1634 ClientSocknames = Peernames, 1635 ok 1636 end, 1637 case get_addrs_by_family(FamilySpec, AddressCount) of 1638 {ok, Addresses} when length(Addresses) =:= AddressCount -> 1639 do_open_and_connect(Addresses, hd(Addresses), Fun); 1640 {error, Reason} -> 1641 {skip, Reason} 1642 end. 1643 1644 1645 1646get_addrs_by_family(Family, NumAddrs) -> 1647 case os:type() of 1648 {unix,linux} -> 1649 get_addrs_by_family_aux(Family, NumAddrs); 1650 {unix,freebsd} -> 1651 get_addrs_by_family_aux(Family, NumAddrs); 1652 {unix,sunos} -> 1653 case get_addrs_by_family_aux(Family, NumAddrs) of 1654 {ok, [InetAddrs, Inet6Addrs]} when Family =:= inet_and_inet6 -> 1655 %% Man page for sctp_bindx on Solaris says: "If sock is an 1656 %% Internet Protocol Version 6 (IPv6) socket, addrs should 1657 %% be an array of sockaddr_in6 structures containing IPv6 1658 %% or IPv4-mapped IPv6 addresses." 1659 {ok, [ipv4_map_addrs(InetAddrs), Inet6Addrs]}; 1660 {ok, Addrs} -> 1661 {ok, Addrs}; 1662 {error, Reason} -> 1663 {error, Reason} 1664 end; 1665 Os -> 1666 Reason = if Family =:= inet_and_inet6 -> 1667 ?F("Mixing ipv4 and ipv6 addresses for " 1668 " multihoming has not been verified on ~p", 1669 [Os]); 1670 true -> 1671 ?F("Multihoming for ~p has not been verified " 1672 "on ~p", [Family, Os]) 1673 end, 1674 {error, Reason} 1675 end. 1676 1677get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet; 1678 Family =:= inet6 -> 1679 case inet:getaddr(localhost, Family) of 1680 {error, eafnosupport = Reason} -> 1681 ?P("failed get (~w) addrs for localhost: ~p", [Family, Reason]), 1682 {error, ?F("No support for ~p (~p)", [Family, Reason])}; 1683 {error, nxdomain = Reason} -> 1684 ?P("failed get (~w) addrs for localhost: ~p", [Family, Reason]), 1685 {error, ?F("No support for ~p (~p)", [Family, Reason])}; 1686 {ok, _} -> 1687 ?P("got addr for localhost (ignored)"), 1688 IfAddrs = ok(inet:getifaddrs()), 1689 ?P("IfAddrs: ~p", [IfAddrs]), 1690 case filter_addrs_by_family(IfAddrs, Family) of 1691 Addrs when length(Addrs) >= NumAddrs -> 1692 {ok, lists:sublist(Addrs, NumAddrs)}; 1693 [] -> 1694 {error, ?F("Need ~p ~p address(es) found none~n", 1695 [NumAddrs, Family])}; 1696 Addrs -> 1697 {error, 1698 ?F("Need ~p ~p address(es) found only ~p: ~p~n", 1699 [NumAddrs, Family, length(Addrs), Addrs])} 1700 end 1701 end; 1702get_addrs_by_family_aux(inet_and_inet6, NumAddrs) -> 1703 try {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of 1704 {ok, Addrs} -> Addrs; 1705 {error, Reason} -> throw({error, Reason}) 1706 end || Family <- [inet, inet6]]} 1707 catch 1708 throw:{error, _} = ERROR -> 1709 ERROR 1710 end. 1711 1712filter_addrs_by_family(IfAddrs, Family) -> 1713 lists:flatten([[Addr || {addr, Addr} <- Info, 1714 is_good_addr(Addr, Family)] 1715 || {_IfName, Info} <- IfAddrs]). 1716 1717is_good_addr(Addr, inet) when tuple_size(Addr) =:= 4 -> 1718 true; 1719is_good_addr({0,0,0,0,0,16#ffff,_,_}, inet6) -> 1720 false; %% ipv4 mapped 1721is_good_addr({16#fe80,_,_,_,_,_,_,_}, inet6) -> 1722 false; %% link-local 1723is_good_addr(Addr, inet6) when tuple_size(Addr) =:= 8 -> 1724 true; 1725is_good_addr(_Addr, _Family) -> 1726 false. 1727 1728ipv4_map_addrs(InetAddrs) -> 1729 [begin 1730 <<AB:16>> = <<A,B>>, 1731 <<CD:16>> = <<C,D>>, 1732 {0, 0, 0, 0, 0, 16#ffff, AB, CD} 1733 end || {A,B,C,D} <- InetAddrs]. 1734 1735do_open_and_connect(ServerAddresses, AddressToConnectTo) -> 1736 Fun = fun (_, _, _, _, _, _) -> ok end, 1737 do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun). 1738%% 1739do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) -> 1740 {ServerFamily, ServerOpts} = get_family_by_addrs(ServerAddresses), 1741 io:format("Serving ~p addresses: ~p~n", 1742 [ServerFamily, ServerAddresses]), 1743 S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++ 1744 [ServerFamily|ServerOpts])), 1745 ok = gen_sctp:listen(S1, true), 1746 P1 = ok(inet:port(S1)), 1747 ClientFamily = get_family_by_addr(AddressToConnectTo), 1748 io:format("Connecting to ~p ~p~n", 1749 [ClientFamily, AddressToConnectTo]), 1750 ClientOpts = 1751 [ClientFamily | 1752 case ClientFamily of 1753 inet6 -> 1754 [{ipv6_v6only,true}]; 1755 _ -> 1756 [] 1757 end], 1758 S2 = ok(gen_sctp:open(0, ClientOpts)), 1759 log(open), 1760 %% Verify client can connect 1761 #sctp_assoc_change{state=comm_up} = S2Assoc = 1762 ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])), 1763 log(comm_up), 1764 %% verify server side also receives comm_up from client 1765 S1Assoc = recv_comm_up_eventually(S1), 1766 Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc), 1767 ok = gen_sctp:close(S2), 1768 ok = gen_sctp:close(S1), 1769 Result. 1770 1771%% If at least one of the addresses is an ipv6 address, return inet6, else inet. 1772get_family_by_addrs(Addresses) -> 1773 case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of 1774 [inet, inet6] -> {inet6, [{ipv6_v6only, false}]}; 1775 [inet] -> {inet, []}; 1776 [inet6] -> {inet6, []} 1777 end. 1778 1779get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet; 1780get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6. 1781 1782recv_comm_up_eventually(S) -> 1783 case ok(gen_sctp:recv(S)) of 1784 {_Addr, _Port, _Info, 1785 #sctp_assoc_change{state=comm_up} = Assoc} -> 1786 Assoc; 1787 {_Addr, _Port, _Info, _OtherSctpMsg} = Msg -> 1788 log({unexpected,Msg}), 1789 recv_comm_up_eventually(S) 1790 end. 1791 1792 1793%% 1794recv_close(Config) when is_list(Config) -> 1795 ?P("create server socket (and listen)"), 1796 {ok, S} = gen_sctp:open(), 1797 gen_sctp:listen(S, true), 1798 {ok, SPort} = inet:port(S), 1799 1800 ?P("create client socket (and connect)"), 1801 {ok, C} = gen_sctp:open(), 1802 {ok, _} = gen_sctp:connect(C, localhost, SPort, []), 1803 1804 TC = self(), 1805 RECV = fun() -> 1806 ?P("try setup recv(s)"), 1807 ok = recv_close_setup_recv(S), 1808 ?P("announce ready"), 1809 TC ! {self(), ready}, 1810 ?P("try data recv"), 1811 Res = gen_sctp:recv(S), 1812 ?P("recv res: " 1813 "~n ~p", [Res]), 1814 exit(Res) 1815 end, 1816 ?P("spawn reader - then await reader ready"), 1817 {Pid, MRef} = spawn_monitor(RECV), 1818 receive 1819 {'DOWN', MRef, process, Pid, PreReason} -> 1820 %% Make sure it does not die for some other reason... 1821 ?P("unexpected reader termination:" 1822 "~n ~p", [PreReason]), 1823 (catch gen_sctp:close(S)), 1824 (catch gen_sctp:close(C)), 1825 ?line ct:fail("Unexpected pre close from reader (~p): ~p", 1826 [Pid, PreReason]); 1827 {Pid, ready} -> 1828 ?P("reader ready"), 1829 ok 1830 after 30000 -> % Just in case... 1831 %% This is **extreme**, but there is no way to know 1832 %% how long it will take to iterate through all the 1833 %% addresses of a host... 1834 ?P("reader ready timeout"), 1835 (catch gen_sctp:close(S)), 1836 (catch gen_sctp:close(C)), 1837 ?line ct:fail("Unexpected pre close timeout (~p)", [Pid]) 1838 end, 1839 1840 ?P("\"ensure\" reader reading..."), 1841 receive 1842 Any -> 1843 ?P("Received unexpected message: " 1844 "~n ~p", [Any]), 1845 (catch gen_sctp:close(S)), 1846 (catch gen_sctp:close(C)), 1847 ?line ct:fail("Unexpected message: ~p", [Any]) 1848 after 5000 -> 1849 ok 1850 end, 1851 1852 ?P("close server socket"), 1853 ok = gen_sctp:close(S), 1854 ?P("await reader termination"), 1855 receive 1856 {'DOWN', MRef, process, Pid, {error, closed}} -> 1857 ?P("expected reader termination result"), 1858 (catch gen_sctp:close(C)), 1859 ok; 1860 {'DOWN', MRef, process, Pid, PostReason} -> 1861 ?P("unexpected reader termination: " 1862 "~n ~p", [PostReason]), 1863 (catch gen_sctp:close(C)), 1864 ?line ct:fail("Unexpected post close from reader (~p): ~p", 1865 [Pid, PostReason]) 1866 after 5000 -> 1867 ?P("unexpected reader termination timeout"), 1868 demonitor(MRef, [flush]), 1869 (catch gen_sctp:close(C)), 1870 exit(Pid, kill), 1871 ?line ct:fail("Reader (~p) termination timeout", [Pid]) 1872 end, 1873 ?P("close client socket"), 1874 (catch gen_sctp:close(C)), 1875 ?P("done"), 1876 ok. 1877 1878 1879recv_close_setup_recv(S) -> 1880 recv_close_setup_recv(S, 1). 1881 1882recv_close_setup_recv(S, N) -> 1883 ?P("try setup recv ~w", [N]), 1884 case gen_sctp:recv(S, 5000) of 1885 {ok, {Addr, 1886 Port, 1887 _AncData, 1888 Data}} when is_tuple(Addr) andalso is_integer(Port) -> 1889 ?P("setup recv ~w: " 1890 "~n ~p", [N, Data]), 1891 recv_close_setup_recv(S, N+1); 1892 {error, timeout} -> 1893 ok 1894 end. 1895 1896 1897%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1898%%% socket gen_server ultra light 1899 1900socket_open(SockOpts0, Timeout) -> 1901 SockOpts = 1902 case lists:keyfind(active,1,SockOpts0) of 1903 false -> 1904 [{active,once}|SockOpts0]; 1905 _ -> 1906 SockOpts0 1907 end, 1908 Opts = [{type,seqpacket},binary|SockOpts], 1909 Starter = 1910 fun () -> 1911 {ok,Socket} = 1912 gen_sctp:open(Opts), 1913 Socket 1914 end, 1915 s_start(Starter, Timeout). 1916 1917socket_peeloff(Socket, AssocId, SocketOpts, Timeout) -> 1918 Opts = [binary|SocketOpts], 1919 Starter = 1920 fun () -> 1921 {ok,NewSocket} = 1922 gen_sctp:peeloff(Socket, AssocId), 1923 ok = inet:setopts(NewSocket, Opts), 1924 NewSocket 1925 end, 1926 s_start(Starter, Timeout). 1927 1928socket_close_verbose(S, StartTime) -> 1929 History = socket_history(socket_close(S), StartTime), 1930 io:format("socket_close ~p:~n ~p.~n", [S,History]), 1931 History. 1932 1933socket_close(S) -> 1934 s_req(S, close). 1935 1936socket_call(S, Request) -> 1937 s_req(S, {Request}). 1938 1939%% socket_get(S, Key) -> 1940%% s_req(S, {get,Key}). 1941 1942socket_bailout([S|Ss], StartTime) -> 1943 History = socket_history(socket_close(S), StartTime), 1944 io:format("bailout ~p:~n ~p.~n", [S,History]), 1945 socket_bailout(Ss, StartTime); 1946socket_bailout([], _) -> 1947 io:format("flush: ~p.~n", [flush()]), 1948 ct:fail(socket_bailout). 1949 1950socket_history({State,Flush}, StartTime) -> 1951 {lists:keysort( 1952 2, 1953 lists:flatten( 1954 [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals] 1955 || {Key,Vals} <- gb_trees:to_list(State)])), 1956 Flush}. 1957 1958s_handler(Socket) -> 1959 fun ({listen,Listen}) -> 1960 ok = gen_sctp:listen(Socket, Listen); 1961 (get_port) -> 1962 ok(inet:port(Socket)); 1963 (get_socket) -> 1964 Socket; 1965 ({connect_init,ConAddr,ConPort,ConOpts}) -> 1966 ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts); 1967 ({send,AssocId,Stream,Data}) -> 1968 ok = gen_sctp:send(Socket, AssocId, Stream, Data); 1969 ({send,OtherSocket,AssocId,Stream,Data}) -> 1970 ok = gen_sctp:send(OtherSocket, AssocId, Stream, Data); 1971 ({setopts,Opts}) -> 1972 ok = inet:setopts(Socket, Opts); 1973 ({getopts,Optnames}) -> 1974 ok(inet:getopts(Socket, Optnames)) 1975 end. 1976 1977s_req(S, Req) -> 1978 Mref = erlang:monitor(process, S), 1979 S ! {self(),Mref,Req}, 1980 receive 1981 {'DOWN',Mref,_,_,Error} -> 1982 exit(Error); 1983 {S,Mref,Reply} -> 1984 erlang:demonitor(Mref, [flush]), 1985 Reply 1986 end. 1987 1988s_start(Starter, Timeout) -> 1989 Parent = self(), 1990 Owner = 1991 spawn_link( 1992 fun () -> 1993 s_start(Starter(), Timeout, Parent) 1994 end), 1995 Owner. 1996 1997s_start(Socket, Timeout, Parent) -> 1998 Handler = s_handler(Socket), 1999 try 2000 s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty()) 2001 catch 2002 Class:Reason:Stacktrace -> 2003 io:format(?MODULE_STRING":socket exception ~w:~w at~n" 2004 "~p.~n", [Class,Reason,Stacktrace]), 2005 erlang:raise(Class, Reason, Stacktrace) 2006 end. 2007 2008s_loop(Socket, Timeout, Parent, Handler, State) -> 2009 receive 2010 {Parent,Ref,close} -> % socket_close() 2011 erlang:send_after(Timeout, self(), {Parent,Ref,exit}), 2012 s_loop(Socket, Timeout, Parent, Handler, State); 2013 {Parent,Ref,exit} -> 2014 ok = gen_sctp:close(Socket), 2015 Key = exit, 2016 NewState = gb_push(Key, Socket, State), 2017 Parent ! {self(),Ref,{NewState,flush()}}; 2018 {Parent,Ref,{Msg}} -> 2019 Result = Handler(Msg), 2020 Key = req, 2021 NewState = gb_push(Key, {Msg,Result}, State), 2022 Parent ! {self(),Ref,Result}, 2023 s_loop(Socket, Timeout, Parent, Handler, NewState); 2024 %% {Parent,Ref,{get,Key}} -> 2025 %% Parent ! {self(),Ref,gb_get(Key, State)}, 2026 %% s_loop(Socket, Timeout, Parent, Handler, State); 2027 {sctp,Socket,Addr,Port, 2028 {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}} 2029 when not is_tuple(Data) -> 2030 case gb_get({assoc_change,AssocId}, State) of 2031 [{Addr,Port, 2032 #sctp_assoc_change{ 2033 state=comm_up, 2034 inbound_streams=Is}}|_] 2035 when 0 =< Stream, Stream < Is-> ok; 2036 [] -> ok 2037 end, 2038 Key = {msg,AssocId,Stream}, 2039 NewState = gb_push(Key, {Addr,Port,SRI,Data}, State), 2040 Parent ! {self(),{Addr,Port,AssocId,Stream,Data}}, 2041 again(Socket), 2042 s_loop(Socket, Timeout, Parent, Handler, NewState); 2043 {sctp,Socket,Addr,Port, 2044 {SRI,#sctp_assoc_change{assoc_id=AssocId,state=St}=SAC}} -> 2045 case SRI of 2046 [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok; 2047 [] -> ok 2048 end, 2049 Key = {assoc_change,AssocId}, 2050 case {gb_get(Key, State),St} of 2051 {[],_} -> ok; 2052 {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} 2053 when St =:= comm_lost; St =:= shutdown_comp -> ok 2054 end, 2055 NewState = gb_push(Key, {Addr,Port,SAC}, State), 2056 Parent ! {self(),{Addr,Port,SAC}}, 2057 again(Socket), 2058 s_loop(Socket, Timeout, Parent, Handler, NewState); 2059 {sctp,Socket,Addr,Port, 2060 {SRI,#sctp_paddr_change{assoc_id=AssocId, 2061 addr={_,P}, 2062 state=St}=SPC}} -> 2063 match_unless_solaris(Port, P), 2064 case SRI of 2065 [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok; 2066 [] -> ok 2067 end, 2068 case {gb_get({assoc_change,AssocId}, State),St} of 2069 {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} 2070 when St =:= addr_available; 2071 St =:= addr_confirmed -> ok; 2072 {[],addr_confirmed} -> ok 2073 end, 2074 Key = {paddr_change,AssocId}, 2075 NewState = gb_push(Key, {Addr,Port,SPC}, State), 2076 again(Socket), 2077 s_loop(Socket, Timeout, Parent, Handler, NewState); 2078 {sctp,Socket,Addr,Port, 2079 {SRI,#sctp_shutdown_event{assoc_id=AssocId}=SSE}} -> 2080 case SRI of 2081 [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok; 2082 [] -> ok 2083 end, 2084 case gb_get({assoc_change,AssocId}, State) of 2085 [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok; 2086 [] -> ok 2087 end, 2088 Key = {shutdown_event,AssocId}, 2089 NewState = gb_push(Key, {Addr,Port}, State), 2090 Parent ! {self(), {Addr,Port,SSE}}, 2091 again(Socket), 2092 s_loop(Socket, Timeout, Parent, Handler, NewState); 2093 Unexpected -> 2094 erlang:error({unexpected,Unexpected}) 2095 end. 2096 2097again(Socket) -> 2098 receive 2099 {sctp_passive,Socket} -> 2100 [{active, false}] = ok(inet:getopts(Socket, [active])), 2101 ok = inet:setopts(Socket,[{active,1}]) 2102 after 0 -> 2103 ok = inet:setopts(Socket, [{active,once}]) 2104 end. 2105 2106gb_push(Key, Val, GBT) -> 2107 TS = timestamp(), 2108 case gb_trees:lookup(Key, GBT) of 2109 none -> 2110 gb_trees:insert(Key, [{TS,Val}], GBT); 2111 {value,V} -> 2112 gb_trees:update(Key, [{TS,Val}|V], GBT) 2113 end. 2114 2115gb_get(Key, GBT) -> 2116 case gb_trees:lookup(Key, GBT) of 2117 none -> 2118 []; 2119 {value,V} -> 2120 [Val || {_TS,Val} <- V] 2121 end. 2122 2123match_unless_solaris(A, B) -> 2124 case os:type() of 2125 {unix,sunos} -> B; 2126 _ -> A = B 2127 end. 2128 2129 2130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2131 2132timestamp() -> 2133 erlang:monotonic_time(). 2134 2135 2136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2137 2138log_ok(X) -> 2139 log(ok(X)). 2140 2141ok({ok, X}) -> 2142 X; 2143ok({error, X}) -> 2144 ?P("ERROR: ~p", [X]), 2145 ?line ct:fail({unexpected_error, X}); 2146ok(X) -> 2147 ?P("UNEXPECTED: ~p", [X]), 2148 ?line ct:fail({unexpected, X}). 2149 2150 2151log(X) -> 2152 ?P("LOG: ~p", [X]), 2153 X. 2154 2155err([], Result) -> 2156 erlang:error(Result); 2157err([Reason|_], {error,Reason}) -> 2158 ok; 2159err([_|Reasons], Result) -> 2160 err(Reasons, Result). 2161 2162 2163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2164 2165open_failed_str(Reason) -> 2166 ?F("Open failed: ~w", [Reason]). 2167 2168