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