diff options
author | Micael Karlberg <[email protected]> | 2019-07-10 16:17:49 +0200 |
---|---|---|
committer | Micael Karlberg <[email protected]> | 2019-07-17 12:12:38 +0200 |
commit | ab78a0e3c0655efe5a8aab3569aca0859811dd4a (patch) | |
tree | b44f895c074e67b51ed3b70826a2e56da7b389c2 | |
parent | 151c6bd2c9a374f3ac5dfd940993caa679cf5c17 (diff) | |
download | otp-ab78a0e3c0655efe5a8aab3569aca0859811dd4a.tar.gz otp-ab78a0e3c0655efe5a8aab3569aca0859811dd4a.tar.bz2 otp-ab78a0e3c0655efe5a8aab3569aca0859811dd4a.zip |
[esock] Add test case for socket option bindtodevice
Add test case for the socket option bindtodevice. Also make
correction in the nif code. Used the wrong option (broadcast).
Also added more info to doc.
Note sure if this test case actually works. Set and get "works",
but the value returned by get is always "". So either we should
extend the test to made sure the bind actually has the expected
effect or... Something for a rainy day maybe...
OTP-15904
-rw-r--r-- | erts/doc/src/socket_usage.xml | 4 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_nif.c | 2 | ||||
-rw-r--r-- | erts/emulator/test/socket_SUITE.erl | 267 | ||||
-rw-r--r-- | erts/preloaded/src/socket.erl | 4 |
4 files changed, 272 insertions, 5 deletions
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml index 77c606b1c1..2d0865d45f 100644 --- a/erts/doc/src/socket_usage.xml +++ b/erts/doc/src/socket_usage.xml @@ -208,7 +208,9 @@ <cell>string()</cell> <cell>yes</cell> <cell>yes</cell> - <cell>none</cell> + <cell>Before Linux 3.8, this socket option could be set, but not get. + Only works for some socket types (e.g. <c>inet</c>). + If empty value is set, the binding is removed.</cell> </row> <row> <cell>broadcast</cell> diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 211f21cb40..362e584bd3 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -11919,7 +11919,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ngetopt_lvl_sock_bindtodevice -> entry with\r\n") ); - return ngetopt_str_opt(env, descP, SOL_SOCKET, SO_BROADCAST, IFNAMSIZ+1); + return ngetopt_str_opt(env, descP, SOL_SOCKET, SO_BINDTODEVICE, IFNAMSIZ+1); } #endif diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index 9bda605b02..83dc396a4d 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -123,6 +123,7 @@ api_opt_simple_otp_controlling_process/1, api_opt_sock_acceptconn/1, api_opt_sock_acceptfilter/1, + api_opt_sock_bindtodevice/1, api_opt_ip_add_drop_membership/1, %% *** API Operation Timeout *** @@ -783,7 +784,8 @@ api_options_otp_cases() -> api_options_socket_cases() -> [ api_opt_sock_acceptconn, - api_opt_sock_acceptfilter + api_opt_sock_acceptfilter, + api_opt_sock_bindtodevice ]. api_options_ip_cases() -> @@ -8816,6 +8818,266 @@ api_opt_sock_acceptfilter(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Tests the socket option bindtodevice. +%% It has not always been possible to 'get' this option +%% (atleast on linux). + +api_opt_sock_bindtodevice(suite) -> + []; +api_opt_sock_bindtodevice(doc) -> + []; +api_opt_sock_bindtodevice(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_sock_bindtodevice, + fun() -> has_sock_bindtodevice_support() end, + fun() -> api_opt_sock_bindtodevice() end). + + +api_opt_sock_bindtodevice() -> + Opt = bindtodevice, + Set = fun(S, Val) -> + socket:setopt(S, socket, Opt, Val) + end, + Get = fun(S) -> + socket:getopt(S, socket, Opt) + end, + + TesterSeq = + [ + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + case which_local_host_info(Domain) of + {ok, {Name, _, Addr}} -> + ?SEV_IPRINT("local host info (~p): " + "~n Name: ~p" + "~n Addr: ~p", + [Domain, Name, Addr]), + LSA = #{family => Domain, + addr => Addr}, + {ok, State#{dev => Name, + local_sa => LSA}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create UDP socket 1", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{usock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create UDP socket 2", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{usock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create TCP socket 1", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{tsock1 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "create TCP socket 2", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{tsock2 => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "[get] verify UDP socket 1 (before bindtodevice)", + cmd => fun(#{usock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify UDP socket 2 (before bind)", + cmd => fun(#{usock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 1 (before bindtodevice)", + cmd => fun(#{tsock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 2 (before bind)", + cmd => fun(#{tsock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "Bind UDP socket 1 to device", + cmd => fun(#{usock1 := Sock, dev := Dev} = _State) -> + case Set(Sock, Dev) of + ok -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "Bind UDP socket 2 to local address", + cmd => fun(#{usock2 := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "Bind TCP socket 1 to device", + cmd => fun(#{tsock1 := Sock, dev := Dev} = _State) -> + case Set(Sock, Dev) of + ok -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "Bind TCP socket 2 to local address", + cmd => fun(#{tsock2 := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("Expected Success"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "[get] verify UDP socket 1 (after bindtodevice)", + cmd => fun(#{usock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify UDP socket 2 (after bind)", + cmd => fun(#{usock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 1 (after bindtodevice)", + cmd => fun(#{tsock1 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + #{desc => "[get] verify TCP socket 2 (after bind)", + cmd => fun(#{tsock2 := Sock} = _State) -> + case Get(Sock) of + {ok, Dev} -> + ?SEV_IPRINT("Expected Success: ~p", [Dev]), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]), + ERROR + end + end}, + + ?SEV_SLEEP(?SECS(1)), + + %% *** Termination *** + #{desc => "close UDP socket 1", + cmd => fun(#{usock1 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(usock1, State)} + end}, + #{desc => "close UDP socket 2", + cmd => fun(#{usock2 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(usock2, State)} + end}, + #{desc => "close TCP socket 1", + cmd => fun(#{tsock1 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(tsock1, State)} + end}, + #{desc => "close TCP socket 2", + cmd => fun(#{tsock2 := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(tsock2, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("get multicast address"), + Domain = inet, + + i("start tester evaluator"), + InitState = #{domain => Domain}, + Tester = ?SEV_START("tester", TesterSeq, InitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% Tests that the add_mambership and drop_membership ip options work. %% We create one server and two clients. The server only send messages, %% the clients only receives messages. @@ -27554,6 +27816,9 @@ has_support_ip_multicast() -> has_support_sock_acceptconn() -> has_support_socket_option_sock(acceptconn). +has_support_sock_bindtodevice() -> + has_support_socket_option_sock(bindtodevice). + has_support_ip_add_membership() -> has_support_socket_option_ip(add_membership). diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index b80ef13e1e..016b479c4d 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -3381,8 +3381,8 @@ enc_sockopt_key(socket = L, acceptconn = Opt, Dir, _D, _T, _P) -> not_supported({L, Opt, Dir}); enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -%% Before linux 3.8, this socket option could be set. -%% Maximum size of buffer for name: IFNAMSZIZ +%% Before linux 3.8, this socket option could be set but not get. +%% Maximum size of buffer for name: IFNAMSIZ %% So, we let the implementation decide. enc_sockopt_key(socket = _L, bindtodevice = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_SOCK_BINDTODEVICE; |