1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2007-2021. 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(inet_sockopt_SUITE). 21 22-include_lib("common_test/include/ct.hrl"). 23-include("kernel_test_lib.hrl"). 24 25 26-define(C_GET_IPPROTO_TCP,1). 27-define(C_GET_IPPROTO_IP,2). 28-define(C_GET_SOL_SOCKET,3). 29-define(C_GET_SOL_IP,4). 30 31-define(C_GET_TCP_KEEPIDLE,11). 32-define(C_GET_TCP_LINGER2,12). 33-define(C_GET_TCP_INFO,13). 34-define(C_GET_SO_REUSEADDR,14). 35-define(C_GET_SO_KEEPALIVE,15). 36-define(C_GET_SO_LINGER,16). 37 38-define(C_GET_LINGER_SIZE,21). 39-define(C_GET_TCP_INFO_SIZE,22). 40 41-define(C_GET_OFF_LINGER_L_ONOFF,31). 42-define(C_GET_OFF_LINGER_L_LINGER,32). 43-define(C_GET_OFF_TCPI_SACKED,33). 44-define(C_GET_OFF_TCPI_OPTIONS,34). 45 46-define(C_GET_SIZ_LINGER_L_ONOFF,41). 47-define(C_GET_SIZ_LINGER_L_LINGER,42). 48-define(C_GET_SIZ_TCPI_SACKED,43). 49-define(C_GET_SIZ_TCPI_OPTIONS,44). 50 51-define(C_QUIT,99). 52 53-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 54 init_per_group/2,end_per_group/2, 55 simple/1, loop_all/1, simple_raw/1, simple_raw_getbin/1, 56 multiple_raw/1, multiple_raw_getbin/1, 57 doc_examples_raw/1,doc_examples_raw_getbin/1, 58 large_raw/1,large_raw_getbin/1,combined/1,combined_getbin/1, 59 ipv6_v6only_udp/1, ipv6_v6only_tcp/1, ipv6_v6only_sctp/1, 60 use_ipv6_v6only_udp/1, 61 type_errors/1]). 62 63-export([init_per_testcase/2, end_per_testcase/2]). 64 65 66suite() -> 67 [{ct_hooks,[ts_install_cth]}, 68 {timetrap,{minutes,1}}]. 69 70all() -> 71 [simple, loop_all, simple_raw, simple_raw_getbin, 72 multiple_raw, multiple_raw_getbin, 73 doc_examples_raw, doc_examples_raw_getbin, large_raw, 74 large_raw_getbin, combined, combined_getbin, 75 ipv6_v6only_udp, ipv6_v6only_tcp, ipv6_v6only_sctp, 76 use_ipv6_v6only_udp, 77 type_errors]. 78 79groups() -> 80 []. 81 82init_per_suite(Config) -> 83 Config. 84 85end_per_suite(_Config) -> 86 ok. 87 88init_per_group(_GroupName, Config) -> 89 Config. 90 91end_per_group(_GroupName, Config) -> 92 Config. 93 94 95init_per_testcase(_Func, Config) -> 96 Config. 97 98end_per_testcase(_Func, _Config) -> 99 ok. 100 101%% Test inet:setopt/getopt simple functionality. 102simple(Config) when is_list(Config) -> 103 XOpt = case os:type() of 104 {unix,_} -> [{reuseaddr,true}]; 105 _ -> [] 106 end, 107 Opt = [{nodelay,true}, 108 {keepalive,true},{packet,4}, 109 {active,false}|XOpt], 110 OptTags = [X || {X,_} <- Opt], 111 {S1,S2} = create_socketpair(Opt, Opt), 112 {ok,Opt} = inet:getopts(S1,OptTags), 113 {ok,Opt} = inet:getopts(S2,OptTags), 114 NoPushOpt = case os:type() of 115 {unix, Osname} when Osname =:= linux; Osname =:= freebsd -> {nopush, true}; 116 {_,_} -> {nopush, false} 117 end, 118 COpt = [{X,case X of nodelay -> false;_ -> Y end} || {X,Y} <- [NoPushOpt|Opt]], 119 COptTags = [X || {X,_} <- COpt], 120 inet:setopts(S1,COpt), 121 {ok,COpt} = inet:getopts(S1,COptTags), 122 {ok,Opt} = inet:getopts(S2,OptTags), 123 gen_tcp:close(S1), 124 gen_tcp:close(S2), 125 ok. 126 127%% Loop through all socket options and check that they work. 128loop_all(Config) when is_list(Config) -> 129 ListenFailures = 130 lists:foldr(make_check_fun(listen,1),[],all_listen_options()), 131 ConnectFailures = 132 lists:foldr(make_check_fun(connect,2),[],all_connect_options()), 133 case ListenFailures++ConnectFailures of 134 [] -> 135 ok; 136 Failed -> 137 {comment,lists:flatten( 138 io_lib:format("Non mandatory failed:~w", 139 [Failed]))} 140 end. 141 142 143 144%% Test simple setopt/getopt of raw options. 145simple_raw(Config) when is_list(Config) -> 146 do_simple_raw(Config,false). 147 148%% Test simple setopt/getopt of raw options, with binaries in getopt. 149simple_raw_getbin(Config) when is_list(Config) -> 150 do_simple_raw(Config,true). 151 152do_simple_raw(Config,Binary) when is_list(Config) -> 153 Port = start_helper(Config), 154 SolSocket = ask_helper(Port,?C_GET_SOL_SOCKET), 155 SoKeepAlive = ask_helper(Port,?C_GET_SO_KEEPALIVE), 156 OptionTrue = {raw,SolSocket,SoKeepAlive,<<1:32/native>>}, 157 OptionFalse = {raw,SolSocket,SoKeepAlive,<<0:32/native>>}, 158 {S1,S2} = create_socketpair([OptionTrue],[{keepalive,true}]), 159 {ok,[{keepalive,true}]} = inet:getopts(S1,[keepalive]), 160 {ok,[{keepalive,true}]} = inet:getopts(S2,[keepalive]), 161 {ok,[{raw,SolSocket,SoKeepAlive,X1B}]} = 162 inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), 163 X1 = nintbin2int(X1B), 164 {ok,[{raw,SolSocket,SoKeepAlive,X2B}]} = 165 inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), 166 X2 = nintbin2int(X2B), 167 true = X1 > 0, 168 true = X2 > 0, 169 inet:setopts(S1,[{keepalive,false}]), 170 inet:setopts(S2,[OptionFalse]), 171 {ok,[{keepalive,false}]} = inet:getopts(S1,[keepalive]), 172 {ok,[{keepalive,false}]} = inet:getopts(S2,[keepalive]), 173 {ok,[{raw,SolSocket,SoKeepAlive,Y1B}]} = 174 inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), 175 Y1 = nintbin2int(Y1B), 176 {ok,[{raw,SolSocket,SoKeepAlive,Y2B}]} = 177 inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]), 178 Y2 = nintbin2int(Y2B), 179 true = Y1 == 0, 180 true = Y2 == 0, 181 gen_tcp:close(S1), 182 gen_tcp:close(S2), 183 stop_helper(Port), 184 ok. 185 186nintbin2int(<<Int:32/native>>) -> Int; 187nintbin2int(<<Int:24/native>>) -> Int; 188nintbin2int(<<Int:16/native>>) -> Int; 189nintbin2int(<<Int:8/native>>) -> Int; 190nintbin2int(<<>>) -> 0. 191 192 193 194%% Test setopt/getopt of multiple raw options. 195multiple_raw(Config) when is_list(Config) -> 196 do_multiple_raw(Config,false). 197 198%% Test setopt/getopt of multiple raw options, with binaries in 199%% getopt. 200multiple_raw_getbin(Config) when is_list(Config) -> 201 do_multiple_raw(Config,true). 202 203do_multiple_raw(Config, Binary) -> 204 Port = start_helper(Config), 205 SolSocket = ask_helper(Port, ?C_GET_SOL_SOCKET), 206 SoKeepalive = ask_helper(Port, ?C_GET_SO_KEEPALIVE), 207 SoKeepaliveTrue = {raw,SolSocket,SoKeepalive,<<1:32/native>>}, 208 SoKeepaliveFalse = {raw,SolSocket,SoKeepalive,<<0:32/native>>}, 209 SoReuseaddr = ask_helper(Port, ?C_GET_SO_REUSEADDR), 210 SoReuseaddrTrue = {raw,SolSocket,SoReuseaddr,<<1:32/native>>}, 211 SoReuseaddrFalse = {raw,SolSocket,SoReuseaddr,<<0:32/native>>}, 212 {S1,S2} = 213 create_socketpair( 214 [SoReuseaddrFalse,SoKeepaliveTrue], 215 [SoKeepaliveFalse,SoReuseaddrTrue]), 216 {ok,[{reuseaddr,false},{keepalive,true}]} = 217 inet:getopts(S1, [reuseaddr,keepalive]), 218 {ok, 219 [{raw,SolSocket,SoReuseaddr,S1R1}, 220 {raw,SolSocket,SoKeepalive,S1K1}]} = 221 inet:getopts( 222 S1, 223 [{raw,SolSocket,SoReuseaddr,binarify(4, Binary)}, 224 {raw,SolSocket,SoKeepalive,binarify(4, Binary)}]), 225 true = nintbin2int(S1R1) =:= 0, 226 true = nintbin2int(S1K1) =/= 0, 227 {ok,[{keepalive,false},{reuseaddr,true}]} = 228 inet:getopts(S2, [keepalive,reuseaddr]), 229 {ok, 230 [{raw,SolSocket,SoKeepalive,S2K1}, 231 {raw,SolSocket,SoReuseaddr,S2R1}]} = 232 inet:getopts( 233 S2, 234 [{raw,SolSocket,SoKeepalive,binarify(4, Binary)}, 235 {raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]), 236 true = nintbin2int(S2K1) =:= 0, 237 true = nintbin2int(S2R1) =/= 0, 238 %% 239 ok = inet:setopts( 240 S1, [SoReuseaddrTrue,SoKeepaliveFalse]), 241 ok = inet:setopts( 242 S2, [SoKeepaliveTrue,SoReuseaddrFalse]), 243 {ok, 244 [{raw,SolSocket,SoReuseaddr,S1R2}, 245 {raw,SolSocket,SoKeepalive,S1K2}]} = 246 inet:getopts( 247 S1, 248 [{raw,SolSocket,SoReuseaddr,binarify(4, Binary)}, 249 {raw,SolSocket,SoKeepalive,binarify(4, Binary)}]), 250 true = nintbin2int(S1R2) =/= 0, 251 true = nintbin2int(S1K2) =:= 0, 252 {ok, 253 [{raw,SolSocket,SoKeepalive,S2K2}, 254 {raw,SolSocket,SoReuseaddr,S2R2}]} = 255 inet:getopts( 256 S2, 257 [{raw,SolSocket,SoKeepalive,binarify(4, Binary)}, 258 {raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]), 259 true = nintbin2int(S2K2) =/= 0, 260 true = nintbin2int(S2R2) =:= 0, 261 %% 262 gen_tcp:close(S1), 263 gen_tcp:close(S2), 264 stop_helper(Port), 265 ok. 266 267 268 269%% Test that the example code from the documentation works. 270doc_examples_raw(Config) when is_list(Config) -> 271 do_doc_examples_raw(Config,false). 272 273%% Test that the example code from the documentation works when getopt 274%% uses binaries. 275doc_examples_raw_getbin(Config) when is_list(Config) -> 276 do_doc_examples_raw(Config,true). 277 278do_doc_examples_raw(Config,Binary) when is_list(Config) -> 279 Port = start_helper(Config), 280 Proto = ask_helper(Port,?C_GET_IPPROTO_TCP), 281 TcpInfo = ask_helper(Port,?C_GET_TCP_INFO), 282 TcpInfoSize = ask_helper(Port,?C_GET_TCP_INFO_SIZE), 283 TcpiSackedOffset = ask_helper(Port,?C_GET_OFF_TCPI_SACKED), 284 TcpiOptionsOffset = ask_helper(Port,?C_GET_OFF_TCPI_OPTIONS), 285 TcpiSackedSize = ask_helper(Port,?C_GET_SIZ_TCPI_SACKED), 286 TcpiOptionsSize = ask_helper(Port,?C_GET_SIZ_TCPI_OPTIONS), 287 TcpLinger2 = ask_helper(Port,?C_GET_TCP_LINGER2), 288 stop_helper(Port), 289 case all_ok([Proto,TcpInfo,TcpInfoSize,TcpiSackedOffset, 290 TcpiOptionsOffset,TcpiSackedSize,TcpiOptionsSize, 291 TcpLinger2]) of 292 false -> 293 {skipped,"Does not run on this OS."}; 294 true -> 295 {Sock,I} = create_socketpair([],[]), 296 {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} = 297 inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), 298 NewLinger = OrigLinger div 2, 299 ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, 300 <<NewLinger:32/native>>}]), 301 {ok,[{raw,Proto,TcpLinger2,<<NewLinger:32/native>>}]} = 302 inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), 303 ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, 304 <<OrigLinger:32/native>>}]), 305 {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} = 306 inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), 307 {ok,[{raw,_,_,Info}]} = 308 inet:getopts(Sock,[{raw,Proto,TcpInfo, 309 binarify(TcpInfoSize,Binary)}]), 310 Bit1 = TcpiSackedSize * 8, 311 <<_:TcpiSackedOffset/binary, 312 TcpiSacked:Bit1/native,_/binary>> = 313 Info, 314 0 = TcpiSacked, 315 Bit2 = TcpiOptionsSize * 8, 316 <<_:TcpiOptionsOffset/binary, 317 TcpiOptions:Bit2/native,_/binary>> = 318 Info, 319 true = TcpiOptions =/= 0, 320 gen_tcp:close(Sock), 321 gen_tcp:close(I), 322 ok 323 end. 324 325%% Test structs and large/too large buffers when raw. 326large_raw(Config) when is_list(Config) -> 327 do_large_raw(Config,false). 328 329%% Test structs and large/too large buffers when raw 330%% using binaries to getopts. 331large_raw_getbin(Config) when is_list(Config) -> 332 do_large_raw(Config,true). 333 334do_large_raw(Config,Binary) when is_list(Config) -> 335 Port = start_helper(Config), 336 Proto = ask_helper(Port,?C_GET_SOL_SOCKET), 337 Linger = ask_helper(Port,?C_GET_SO_LINGER), 338 LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE), 339 LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF), 340 LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER), 341 LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF), 342 LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER), 343 stop_helper(Port), 344 case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset, 345 LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of 346 false -> 347 {skipped,"Does not run on this OS."}; 348 true -> 349 {Sock1,Sock2} = create_socketpair([{linger,{true,10}}], 350 [{linger,{false,0}}]), 351 LargeSize = 1024, % Solaris can take up to 1024*9, 352 % linux 1024*63... 353 TooLargeSize = 1024*64, 354 {ok,[{raw,Proto,Linger,Linger1}]} = 355 inet:getopts(Sock1,[{raw,Proto,Linger, 356 binarify(LargeSize,Binary)}]), 357 {ok,[{raw,Proto,Linger,Linger2}]} = 358 inet:getopts(Sock2,[{raw,Proto,Linger, 359 binarify(LingerSize,Binary)}]), 360 true = byte_size(Linger1) =:= LingerSize, 361 LingerLingerBits = LingerLingerSize * 8, 362 LingerOnOffBits = LingerOnOffSize * 8, 363 <<_:LingerLingerOffset/binary, 364 Ling1:LingerLingerBits/native,_/binary>> = Linger1, 365 <<_:LingerOnOffOffset/binary, 366 Off1:LingerOnOffBits/native,_/binary>> = Linger1, 367 <<_:LingerOnOffOffset/binary, 368 Off2:LingerOnOffBits/native,_/binary>> = Linger2, 369 true = Off1 =/= 0, 370 true = Off2 == 0, 371 true = Ling1 == 10, 372 {error,einval} = 373 inet:getopts(Sock1,[{raw,Proto,Linger,TooLargeSize}]), 374 gen_tcp:close(Sock1), 375 gen_tcp:close(Sock2), 376 ok 377 end. 378 379%% Test raw structs combined w/ other options . 380combined(Config) when is_list(Config) -> 381 do_combined(Config,false). 382 383%% Test raw structs combined w/ other options and 384%% binarise in getopts. 385combined_getbin(Config) when is_list(Config) -> 386 do_combined(Config,true). 387 388do_combined(Config,Binary) when is_list(Config) -> 389 Port = start_helper(Config), 390 Proto = ask_helper(Port,?C_GET_SOL_SOCKET), 391 Linger = ask_helper(Port,?C_GET_SO_LINGER), 392 LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE), 393 LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF), 394 LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER), 395 LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF), 396 LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER), 397 stop_helper(Port), 398 case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset, 399 LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of 400 false -> 401 {skipped,"Does not run on this OS."}; 402 true -> 403 LingerLingerBits = LingerLingerSize * 8, 404 LingerOnOffBits = LingerOnOffSize * 8, 405 {LingerOn,LingerOff} = 406 case LingerOnOffOffset < LingerLingerOffset of 407 true -> 408 Pad1 = 409 list_to_binary( 410 lists:duplicate(LingerOnOffOffset, 411 0)), 412 Pad2Siz = 413 LingerLingerOffset - LingerOnOffSize - 414 LingerOnOffOffset, 415 Pad2 = 416 list_to_binary( 417 lists:duplicate(Pad2Siz, 418 0)), 419 Pad3Siz = LingerSize - LingerLingerSize - 420 LingerLingerOffset, 421 Pad3 = list_to_binary( 422 lists:duplicate(Pad3Siz, 423 0)), 424 {<<Pad1/binary,1:LingerOnOffBits/native, 425 Pad2/binary,10:LingerLingerBits/native, 426 Pad3/binary>>, 427 <<Pad1/binary,0:LingerOnOffBits/native, 428 Pad2/binary,0:LingerLingerBits/native, 429 Pad3/binary>>}; 430 false -> 431 Pad1 = 432 list_to_binary( 433 lists:duplicate(LingerLingerOffset, 434 0)), 435 Pad2Siz = 436 LingerOnOffOffset - LingerLingerSize - 437 LingerLingerOffset, 438 Pad2 = 439 list_to_binary( 440 lists:duplicate(Pad2Siz, 441 0)), 442 Pad3Siz = LingerSize - LingerOnOffSize - 443 LingerOnOffOffset, 444 Pad3 = list_to_binary( 445 lists:duplicate(Pad3Siz, 446 0)), 447 {<<Pad1/binary,1:LingerLingerBits/native, 448 Pad2/binary,10:LingerOnOffBits/native, 449 Pad3/binary>>, 450 <<Pad1/binary,0:LingerLingerBits/native, 451 Pad2/binary,0:LingerOnOffBits/native, 452 Pad3/binary>>} 453 end, 454 RawLingerOn = {raw,Proto,Linger,LingerOn}, 455 RawLingerOff = {raw,Proto,Linger,LingerOff}, 456 {Sock1,Sock2} = 457 create_socketpair([{keepalive,true}, 458 RawLingerOn], 459 [{keepalive,false}, 460 RawLingerOff]), 461 {ok,[{raw,Proto,Linger,Linger1},{keepalive,Keep1}]} = 462 inet:getopts(Sock1,[{raw,Proto,Linger, 463 binarify(LingerSize,Binary)},keepalive]), 464 {ok,[{raw,Proto,Linger,Linger2},{keepalive,Keep2}]} = 465 inet:getopts(Sock2,[{raw,Proto,Linger, 466 binarify(LingerSize,Binary)},keepalive]), 467 true = byte_size(Linger1) =:= LingerSize, 468 <<_:LingerLingerOffset/binary, 469 Ling1:LingerLingerBits/native,_/binary>> = Linger1, 470 <<_:LingerOnOffOffset/binary, 471 Off1:LingerOnOffBits/native,_/binary>> = Linger1, 472 <<_:LingerOnOffOffset/binary, 473 Off2:LingerOnOffBits/native,_/binary>> = Linger2, 474 true = Off1 =/= 0, 475 true = Off2 == 0, 476 true = Ling1 == 10, 477 true = Keep1 =:= true, 478 true = Keep2 =:= false, 479 {Sock3,Sock4} = 480 create_socketpair([RawLingerOn,{keepalive,true}], 481 [RawLingerOff,{keepalive,false}]), 482 {ok,[{raw,Proto,Linger,Linger3},{keepalive,Keep3}]} = 483 inet:getopts(Sock3,[{raw,Proto,Linger, 484 binarify(LingerSize,Binary)},keepalive]), 485 {ok,[{raw,Proto,Linger,Linger4},{keepalive,Keep4}]} = 486 inet:getopts(Sock4,[{raw,Proto,Linger, 487 binarify(LingerSize,Binary)},keepalive]), 488 true = byte_size(Linger3) =:= LingerSize, 489 <<_:LingerLingerOffset/binary, 490 Ling3:LingerLingerBits/native,_/binary>> = Linger3, 491 <<_:LingerOnOffOffset/binary, 492 Off3:LingerOnOffBits/native,_/binary>> = Linger3, 493 <<_:LingerOnOffOffset/binary, 494 Off4:LingerOnOffBits/native,_/binary>> = Linger4, 495 true = Off3 =/= 0, 496 true = Off4 == 0, 497 true = Ling3 == 10, 498 true = Keep3 =:= true, 499 true = Keep4 =:= false, 500 {Sock5,Sock6} = 501 create_socketpair([{packet,4},RawLingerOn,{keepalive,true}], 502 [{packet,2},RawLingerOff,{keepalive,false}]), 503 {ok,[{packet,Pack5},{raw,Proto,Linger,Linger5}, 504 {keepalive,Keep5}]} = 505 inet:getopts(Sock5,[packet,{raw,Proto,Linger, 506 binarify(LingerSize,Binary)}, 507 keepalive]), 508 {ok,[{packet,Pack6},{raw,Proto,Linger,Linger6}, 509 {keepalive,Keep6}]} = 510 inet:getopts(Sock6,[packet,{raw,Proto,Linger, 511 binarify(LingerSize,Binary)}, 512 keepalive]), 513 true = byte_size(Linger5) =:= LingerSize, 514 <<_:LingerLingerOffset/binary, 515 Ling5:LingerLingerBits/native,_/binary>> = Linger5, 516 <<_:LingerOnOffOffset/binary, 517 Off5:LingerOnOffBits/native,_/binary>> = Linger5, 518 <<_:LingerOnOffOffset/binary, 519 Off6:LingerOnOffBits/native,_/binary>> = Linger6, 520 true = Off5 =/= 0, 521 true = Off6 == 0, 522 true = Ling5 == 10, 523 true = Keep5 =:= true, 524 true = Keep6 =:= false, 525 true = Pack5 =:= 4, 526 true = Pack6 =:= 2, 527 inet:setopts(Sock6,[{packet,4},RawLingerOn, 528 {keepalive,true}]), 529 {ok,[{packet,Pack7},{raw,Proto,Linger,Linger7}, 530 {keepalive,Keep7}]} = 531 inet:getopts(Sock6,[packet,{raw,Proto,Linger, 532 binarify(LingerSize,Binary)}, 533 keepalive]), 534 <<_:LingerOnOffOffset/binary, 535 Off7:LingerOnOffBits/native,_/binary>> = Linger7, 536 true = Off7 =/= 0, 537 true = Keep7 =:= true, 538 true = Pack7 =:= 4, 539 gen_tcp:close(Sock1), 540 gen_tcp:close(Sock2), 541 gen_tcp:close(Sock3), 542 gen_tcp:close(Sock4), 543 gen_tcp:close(Sock5), 544 gen_tcp:close(Sock6), 545 ok 546 end. 547 548 549 550%% Test socket option ipv6_v6only for UDP. 551ipv6_v6only_udp(Config) when is_list(Config) -> 552 ipv6_v6only(Config, gen_udp). 553 554%% Test socket option ipv6_v6only for TCP. 555ipv6_v6only_tcp(Config) when is_list(Config) -> 556 ipv6_v6only(Config, gen_tcp). 557 558%% Test socket option ipv6_v6only for SCTP. 559ipv6_v6only_sctp(Config) when is_list(Config) -> 560 ipv6_v6only(Config, gen_sctp). 561 562ipv6_v6only(Config, Module) when is_list(Config) -> 563 case ipv6_v6only_open(Module, []) of 564 {ok,S1} -> 565 case inet:getopts(S1, [ipv6_v6only]) of 566 {ok,[{ipv6_v6only,Default}]} 567 when is_boolean(Default) -> 568 ok = 569 ipv6_v6only_close(Module, S1), 570 ipv6_v6only(Config, Module, Default); 571 {ok,[]} -> 572 io:format("Not implemented.~n", []), 573 %% This list of OS:es where the option is 574 %% supposed to be not implemented is just 575 %% a guess, and may grow with time. 576 case {os:type(),os:version()} of 577 {{unix,linux},{2,M,_}} 578 when M =< 4 -> ok 579 end, 580 %% At least this should work 581 {ok,S2} = 582 ipv6_v6only_open( 583 Module, 584 [{ipv6_v6only,true}]), 585 ok = 586 ipv6_v6only_close(Module, S2) 587 end; 588 {error,_} -> 589 {skipped,"Socket type not supported"} 590 end. 591 592ipv6_v6only(Config, Module, Default) when is_list(Config) -> 593 io:format("Default ~w.~n", [Default]), 594 {ok,S1} = 595 ipv6_v6only_open(Module, [{ipv6_v6only,Default}]), 596 {ok,[{ipv6_v6only,Default}]} = 597 inet:getopts(S1, [ipv6_v6only]), 598 ok = 599 ipv6_v6only_close(Module, S1), 600 NotDefault = not Default, 601 case ipv6_v6only_open(Module, [{ipv6_v6only,NotDefault}]) of 602 {ok,S2} -> 603 io:format("Read-write.~n", []), 604 {ok,[{ipv6_v6only,NotDefault}]} = 605 inet:getopts(S2, [ipv6_v6only]), 606 ok; 607 {error,einval} -> 608 io:format("Read-only.~n", []), 609 %% This option is known to be read-only and true 610 %% on Windows and OpenBSD 611 case os:type() of 612 {unix,openbsd} when Default =:= true -> ok; 613 {win32,_} when Default =:= true -> ok 614 end 615 end. 616 617ipv6_v6only_open(Module, Opts) -> 618 Module:case Module of 619 gen_tcp -> listen; 620 _ -> open 621 end(0, [inet6|Opts]). 622 623ipv6_v6only_close(Module, Socket) -> 624 Module:close(Socket). 625 626 627%% Test using socket option ipv6_v6only for UDP. 628use_ipv6_v6only_udp(Config) when is_list(Config) -> 629 ?TC_TRY(use_ipv6_v6only_udp, 630 fun() -> do_use_ipv6_v6only_udp(Config) end). 631 632do_use_ipv6_v6only_udp(Config) -> 633 case gen_udp:open(0, [inet6,{ip,{0,0,0,0,0,0,0,1}}, {ipv6_v6only,true}]) of 634 {ok,S6} -> 635 ?P("IPv6 socket option created - ensure ipv6_v6only"), 636 case inet:getopts(S6, [ipv6_v6only]) of 637 {ok,[{ipv6_v6only,true}]} -> 638 use_ipv6_v6only_udp(Config, S6); 639 {ok, Other} -> 640 ?P("IPv6 socket option created - " 641 "failed ensuring ipv6_v6only"), 642 exit({skip, {getopts, Other}}) 643 end; 644 {error, OReason} -> 645 ?P("failed create IPv6 socket: " 646 "~n ~p", [OReason]), 647 exit({skip, "Socket type not supported"}) 648 end. 649 650use_ipv6_v6only_udp(_Config, S6) -> 651 {ok, Port} = inet:port(S6), 652 ?P("IPv6 socket port number: ~w", [Port]), 653 S4 = case gen_udp:open(Port, [inet]) of 654 {ok, Sock4} -> 655 ?P("IPv4 socket created with port number ~w", [Port]), 656 Sock4; 657 {error, eaddrinuse = Reason} -> 658 ?P("failed create IPv4 socket: ~p => skip", [Reason]), 659 exit({skip, Reason}); 660 {error, Reason} -> 661 ?P("failed create IPv4 socket: " 662 "~n ~p", [Reason]), 663 ct:fail({failed_open, inet, Reason}) 664 end, 665 E6 = " IPv6-echo.", 666 E4 = " IPv4-echo.", 667 {Sender, MRef} = 668 spawn_monitor(fun () -> 669 use_ipv6_v6only_udp_sender(Port, E6, E4) 670 end), 671 use_ipv6_v6only_udp_listener( 672 S6, S4, E6, E4, Sender, MRef). 673 674use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Sender, Mref) -> 675 ?P("await upd message"), 676 receive 677 {udp,S6,IP,P,Data} -> 678 ?P("received (IPv6) upd message"), 679 ok = gen_udp:send(S6, IP, P, [Data|E6]), 680 use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Sender, Mref); 681 {udp,S4,IP,P,Data} -> 682 ?P("received (IPv4) upd message"), 683 ok = gen_udp:send(S4, IP, P, [Data|E4]), 684 use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Sender, Mref); 685 {'DOWN', Mref,_,_, normal} -> 686 ?P("received expected normal down message"), 687 ok; 688 {'DOWN', Mref,_,_, Result} -> 689 %% Since we are linked we will never arrive here 690 ?P("received expected down message: " 691 "~n ~p", [Result]), 692 Result; 693 Other -> 694 ?P("received unexpected message: " 695 "~n ~p", [Other]), 696 exit({failed, {listener_unexpected, Other}}) 697 end. 698 699use_ipv6_v6only_udp_sender(Port, E6, E4) -> 700 D6 = "IPv6-send.", 701 D4 = "IPv4-send.", 702 R6 = D6 ++ E6, 703 R4 = D4 ++ E4, 704 R6 = sndrcv({0,0,0,0,0,0,0,1}, Port, [inet6], D6), 705 R4 = sndrcv({127,0,0,1}, Port, [inet], D4), 706 ok. 707 708sndrcv(Ip, Port, Opts, Data) -> 709 {ok,S} = gen_udp:open(0, Opts), 710 ?P("[~w:~w] ! ~s", [Ip, Port, Data]), 711 ok = gen_udp:send(S, Ip, Port, Data), 712 receive 713 {udp, S, Ip, Port, RecData} -> 714 ?P("[~w:~w] : ~s", [Ip, Port, RecData]), 715 RecData; 716 Other -> 717 ?P("received unexpected message: " 718 "~n ~p", [Other]), 719 exit({failed, {sndrcv_unexpectec, Other}}) 720 end. 721 722 723 724%% Test that raw data requests are not executed for bad types. 725type_errors(Config) when is_list(Config) -> 726 BadSetOptions = 727 [ 728 {raw,x,3,<<1:32>>}, 729 {raw,1,tre,<<1:32>>}, 730 {raw,1,3,ko}, 731 {raw,1,3,5}, 732 {raw,1,3}, 733 {raw,1}, 734 {raw}, 735 {raw,ett}, 736 {raw,ett,tre}, 737 {raw,{true,10}}, 738 {raw,{ett,tre,<<1:32>>}}, 739 {rav,1,3,<<1:32>>}, 740 raw, 741 rav, 742 {linger,banan} 743 ], 744 BadGetOptions = 745 [ 746 {raw,x,3,<<1:32>>}, 747 {raw,1,tre,<<1:32>>}, 748 {raw,1,3,ko}, 749 {raw,1,3,5.1}, 750 {raw,1,3,-3}, 751 {raw,1,3}, 752 {raw,1}, 753 {raw}, 754 {raw,ett}, 755 {raw,ett,tre}, 756 {raw,{true,10}}, 757 {raw,{ett,tre,<<1:32>>}}, 758 {rav,1,3,<<1:32>>}, 759 raw, 760 rav, 761 {linger,banan} 762 ], 763 lists:foreach(fun(Option) -> 764 case 765 catch create_socketpair([Option],[]) of 766 {'EXIT',badarg} -> 767 ok; 768 Unexpected1 -> 769 exit({unexpected, 770 Unexpected1}) 771 end, 772 case 773 catch create_socketpair([],[Option]) of 774 {'EXIT',badarg} -> 775 ok; 776 Unexpected2 -> 777 exit({unexpected, 778 Unexpected2}) 779 end, 780 {Sock1,Sock2} = create_socketpair([],[]), 781 case inet:setopts(Sock1, [Option]) of 782 {error,einval} -> 783 ok; 784 Unexpected3 -> 785 exit({unexpected, 786 Unexpected3}) 787 end, 788 gen_tcp:close(Sock1), 789 gen_tcp:close(Sock2) 790 end,BadSetOptions), 791 {Sock1,Sock2} = create_socketpair([],[]), 792 lists:foreach(fun(Option) -> 793 case inet:getopts(Sock1, [Option]) of 794 {error,einval} -> 795 ok; 796 Unexpected -> 797 exit({unexpected, 798 Unexpected}) 799 end 800 end,BadGetOptions), 801 gen_tcp:close(Sock1), 802 gen_tcp:close(Sock2), 803 ok. 804 805all_ok([]) -> 806 true; 807all_ok([H|T]) when H >= 0 -> 808 all_ok(T); 809all_ok(_) -> 810 false. 811 812 813make_check_fun(Type,Element) -> 814 fun({Name,V1,V2,Mand,Chang},Acc) -> 815 {LO1,CO1} = setelement(Element,{[],[]}, [{Name,V1}]), 816 {LO2,CO2} = setelement(Element,{[],[]}, [{Name,V2}]), 817 {X1,Y1} = create_socketpair(LO1,CO1), 818 {X2,Y2} = create_socketpair(LO2,CO2), 819 S1 = element(Element,{X1,Y1}), 820 S2 = element(Element,{X2,Y2}), 821 {ok,[{Name,R1}]} = inet:getopts(S1,[Name]), 822 {ok,[{Name,R2}]} = inet:getopts(S2,[Name]), 823 NewAcc = 824 case R1 =/= R2 of 825 true -> 826 case Chang of 827 true -> 828 inet:setopts(S1,[{Name,V2}]), 829 {ok,[{Name,R3}]} = 830 inet:getopts(S1,[Name]), 831 case {R3 =/= R1, R3 =:= R2} of 832 {true,true} -> 833 Acc; 834 _ -> 835 case Mand of 836 true -> 837 exit 838 ({failed_sockopt, 839 {change, 840 Name}}); 841 false -> 842 [{change,Name}|Acc] 843 end 844 end; 845 false -> 846 Acc 847 end; 848 false -> 849 case Mand of 850 true -> 851 exit({failed_sockopt, 852 {Type,Name}}); 853 false -> 854 [{Type,Name}|Acc] 855 end 856 end, 857 gen_tcp:close(X1), 858 gen_tcp:close(Y1), 859 gen_tcp:close(X2), 860 gen_tcp:close(Y2), 861 NewAcc 862 end. 863 864%% {OptionName,Value1,Value2,Mandatory,Changeable} 865all_listen_options() -> 866 [{tos,0,1,false,true}, 867 {priority,0,1,false,true}, 868 {reuseaddr,false,true,false,true}, 869 {keepalive,false,true,true,true}, 870 {linger, {false,10}, {true,10},true,true}, 871 {sndbuf,2048,4096,false,true}, 872 {recbuf,2048,4096,false,true}, 873 {nodelay,false,true,true,true}, 874 {header,2,4,true,true}, 875 {active,false,true,true,false}, 876 {packet,2,4,true,true}, 877 {buffer,1000,2000,true,true}, 878 {mode,list,binary,true,true}, 879 {deliver,term,port,true,true}, 880 {exit_on_close, true, false, true, true}, 881 {high_watermark,4096,8192,true,true}, 882 {low_watermark,2048,4096,true,true}, 883 {high_msgq_watermark,4096,8192,true,true}, 884 {low_msgq_watermark,2048,4096,true,true}, 885 {send_timeout,infinity,1000,true,true}, 886 {send_timeout_close,false,true,true,true}, 887 {delay_send,false,true,true,true}, 888 {packet_size,0,4,true,true} 889 ]. 890all_connect_options() -> 891 [{tos,0,1,false,true}, 892 {priority,0,1,false,true}, 893 {reuseaddr,false,true,false,true}, 894 {keepalive,false,true,true,true}, 895 {linger, {false,10}, {true,10},true,true}, 896 {sndbuf,2048,4096,false,true}, 897 {recbuf,2048,4096,false,true}, 898 {nodelay,false,true,true,true}, 899 {header,2,4,true,true}, 900 {active,false,true,true,false}, 901 {packet,2,4,true,true}, 902 {buffer,1000,2000,true,true}, 903 {mode,list,binary,true,true}, 904 {deliver,term,port,true,true}, 905 {exit_on_close, true, false, true, true}, 906 {high_watermark,4096,8192,false,true}, 907 {low_watermark,2048,4096,false,true}, 908 {high_msgq_watermark,4096,8192,true,true}, 909 {low_msgq_watermark,2048,4096,true,true}, 910 {send_timeout,infinity,1000,true,true}, 911 {send_timeout_close,false,true,true,true}, 912 {delay_send,false,true,true,true}, 913 {packet_size,0,4,true,true} 914 ]. 915 916 917create_socketpair(ListenOptions,ConnectOptions) -> 918 {ok,LS}=gen_tcp:listen(0,ListenOptions), 919 {ok,Port}=inet:port(LS), 920 {ok,CS}=gen_tcp:connect(localhost,Port,ConnectOptions), 921 {ok,AS}=gen_tcp:accept(LS), 922 gen_tcp:close(LS), 923 {AS,CS}. 924 925 926start_helper(Config) -> 927 Progname = filename:join(proplists:get_value(data_dir, Config), "sockopt_helper"), 928 Port = open_port({spawn,Progname},[eof,line]), 929 Port. 930 931ask_helper(Port,Code) -> 932 Com = integer_to_list(Code)++"\n", 933 Port ! {self(),{command,Com}}, 934 receive 935 {Port,{data,{eol,Text}}} -> 936 list_to_integer(Text); 937 Other -> 938 exit({error,{unexpected_data_from_helper,Other}}) 939 after 3000 -> 940 exit({error,helper_timeout}) 941 end. 942 943stop_helper(Port) -> 944 catch ask_helper(Port,?C_QUIT), 945 receive 946 {Port,eof} -> 947 Port ! {self(), close}, 948 receive 949 {Port,closed} -> 950 ok 951 after 1000 -> 952 timeout 953 end 954 after 1000 -> 955 timeout 956 end. 957 958binarify(Size,Binary) when Binary =:= true -> 959 <<0:Size/unit:8>>; 960binarify(Size,Binary) when Binary =:= false -> 961 Size. 962