Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[erts, kernel] reuseaddr/reuseport/exclusiveaddruse support/fixes #6522

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
346 changes: 308 additions & 38 deletions erts/emulator/drivers/common/inet_drv.c

Large diffs are not rendered by default.

Binary file modified erts/preloaded/ebin/prim_inet.beam
Binary file not shown.
2 changes: 1 addition & 1 deletion erts/preloaded/src/erts.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
{registered, []},
{applications, []},
{env, []},
{runtime_dependencies, ["stdlib-4.1", "kernel-8.5", "sasl-3.3"]}
{runtime_dependencies, ["stdlib-4.1", "kernel-@OTP-18344@", "sasl-3.3"]}
]}.

%% vim: ft=erlang
9 changes: 9 additions & 0 deletions erts/preloaded/src/prim_inet.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,9 @@ is_sockopt_val(Opt, Val) ->
%% Socket options processing: Encoding option NAMES:
%%
enc_opt(reuseaddr) -> ?INET_OPT_REUSEADDR;
enc_opt(reuseport) -> ?INET_OPT_REUSEPORT;
enc_opt(reuseport_lb) -> ?INET_OPT_REUSEPORT_LB;
enc_opt(exclusiveaddruse) -> ?INET_OPT_EXCLUSIVEADDRUSE;
enc_opt(keepalive) -> ?INET_OPT_KEEPALIVE;
enc_opt(dontroute) -> ?INET_OPT_DONTROUTE;
enc_opt(linger) -> ?INET_OPT_LINGER;
Expand Down Expand Up @@ -1581,6 +1584,9 @@ enc_opt(sctp_get_peer_addr_info) -> ?SCTP_OPT_GET_PEER_ADDR_INFO.
%% Decoding option NAMES:
%%
dec_opt(?INET_OPT_REUSEADDR) -> reuseaddr;
dec_opt(?INET_OPT_REUSEPORT) -> reuseport;
dec_opt(?INET_OPT_REUSEPORT_LB) -> reuseport_lb;
dec_opt(?INET_OPT_EXCLUSIVEADDRUSE) -> exclusiveaddruse;
dec_opt(?INET_OPT_KEEPALIVE) -> keepalive;
dec_opt(?INET_OPT_DONTROUTE) -> dontroute;
dec_opt(?INET_OPT_LINGER) -> linger;
Expand Down Expand Up @@ -1664,6 +1670,9 @@ type_opt(_, Opt) ->
%% Types of option values, by option name:
%%
type_opt_1(reuseaddr) -> bool;
type_opt_1(reuseport) -> bool;
type_opt_1(reuseport_lb) -> bool;
type_opt_1(exclusiveaddruse) -> bool;
type_opt_1(keepalive) -> bool;
type_opt_1(dontroute) -> bool;
type_opt_1(linger) -> {bool,int};
Expand Down
110 changes: 101 additions & 9 deletions lib/kernel/doc/src/inet.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,22 @@ get_tcpi_sacked(Sock) ->
<seemfa marker="gen_tcp#shutdown/2"><c>gen_tcp:shutdown/2</c></seemfa>
to shut down the write side.</p>
</item>
<tag><c>{exclusiveaddruse, Boolean}</c>
<marker id="option-exclusiveaddruse"/></tag>
<item>
<p>
Enables/disables exclusive address/port usage on Windows. That
is, by enabling this option you can prevent other sockets from
binding to the same address/port. By default this option is
disabled. That is, other sockets may use the same address/port
by setting <seeerl marker="#option-reuseaddr"><c>{reuseaddr,
true}</c></seeerl> in combination with
<seeerl marker="#option-reuseport"><c>{reuseport,
true}</c></seeerl> unless <c>{exclusiveaddruse, true}</c>
has been set on <c><anno>Socket</anno></c>. On non-Windows
systems this option is silently ignored.
</p>
</item>
<tag><c>{header, Size}</c></tag>
<item>
<p>This option is only meaningful if option <c>binary</c>
Expand Down Expand Up @@ -1566,18 +1582,94 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code>
option.
</p>
</item>
<tag><c>{reuseaddr, Boolean}</c></tag>
<tag><c>{reuseaddr, Boolean}</c><marker id="option-reuseaddr"/></tag>
<item>
<p>
Allows or disallows reuse of local address. By default, reuse
is disallowed.
</p>
<note>
<p>
On windows <c>{reuseaddr, true}</c> will have no effect unless
also <seeerl marker="#option-reuseport"><c>{reuseport,
true}</c></seeerl> is set. If both are set, the
rickard-green marked this conversation as resolved.
Show resolved Hide resolved
<c>SO_REUSEADDR</c> Windows socket option will be enabled.
This since setting <c>SO_REUSEADDR</c> on Windows more or less
has the same behavior as setting both <c>SO_REUSEADDR</c> and
<c>SO_REUSEPORT</c> on BSD. This behavior was introduced as
of OTP @OTP-18324@.
</p>
<p>
Between OTP 25.0 and up to the predecessor of OTP @OTP-18324@,
the <c>SO_REUSEADDR</c> option was set on Windows if
<c>{reuseaddr, true}</c> was set. Prior to OTP 25.0, the
<c>{reuseaddr, true}</c> option on Windows was silently
ignored.
</p>
<p>
See also the
<seeerl marker="#option-exclusiveaddruse"><c>exclusiveaddruse</c></seeerl>
option.
</p>
</note>
</item>
<tag><c>{reuseport, Boolean}</c><marker id="option-reuseport"/></tag>
<item>
<p>
Allows or disallows reuse of local port which <i>may or may not</i>
have load balancing depending on the underlying OS. By default,
reuse is disallowed. See also
<seeerl marker="#option-reuseport_lb"><c>reuseport_lb</c></seeerl>.
</p>
<note>
<p>
On windows <c>{reuseport, true}</c> will have no effect unless
also <seeerl marker="#option-reuseaddr"><c>{reuseaddr,
true}</c></seeerl> is set. If both are set, the
<c>SO_REUSEADDR</c> Windows socket option will be enabled.
This since setting <c>SO_REUSEADDR</c> on Windows more or less
has the same behavior as setting both <c>SO_REUSEADDR</c> and
<c>SO_REUSEPORT</c> on BSD. The <c>reuseport</c> option was
introduced as of OTP @OTP-18324@.
</p>
<p>
See also the
<seeerl marker="#option-exclusiveaddruse"><c>exclusiveaddruse</c></seeerl>
option.
</p>
</note>
<note>
<p>
<c>reuseport</c> <i>may or may not</i> be the same underlying
option as
<seeerl marker="#option-reuseport_lb"><c>reuseport_lb</c></seeerl>
depending on the underlying OS. They, for example, are on Linux.
When they are the same underlying option, operating on both may
cause them to interact in surprising ways. For example,
by enabling <c>reuseport</c> and then disabling
<c>reuseport_lb</c> both will end up being disabled.
</p>
</note>
</item>
<tag><c>{reuseport_lb, Boolean}</c><marker id="option-reuseport_lb"/></tag>
<item>
<p>
Allows or disallows local reuse of address. By
default, reuse is disallowed.
Allows or disallows reuse of local port <i>with</i> load balancing.
By default, reuse is disallowed. See also
<seeerl marker="#option-reuseport"><c>reuseport</c></seeerl>.
</p>
<note><p>
On Windows this option will be ignored unless
<c><anno>Socket</anno></c> is an UDP socket. This since the
behavior of <c>reuseaddr</c> is very different on Windows
compared to other system.
</p></note>
<note>
<p>
<c>reuseport_lb</c> <i>may or may not</i> be the same underlying
option as
<seeerl marker="#option-reuseport"><c>reuseport</c></seeerl>
depending on the underlying OS. They, for example, are on Linux.
When they are the same underlying option, operating on both may
cause them to interact in surprising ways. For example,
by enabling <c>reuseport_lb</c> and then disabling
<c>reuseport</c> both will end up being disabled.
</p>
</note>
</item>
<tag><c>{send_timeout, Integer}</c></tag>
<item>
Expand Down
6 changes: 6 additions & 0 deletions lib/kernel/src/gen_sctp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@
{buffer, non_neg_integer()} |
{debug, boolean()} |
{dontroute, boolean()} |
{exclusiveaddruse, boolean()} |
{high_msgq_watermark, pos_integer()} |
{linger, {boolean(), non_neg_integer()}} |
{low_msgq_watermark, pos_integer()} |
{mode, list | binary} | list | binary |
{priority, non_neg_integer()} |
{recbuf, non_neg_integer()} |
{reuseaddr, boolean()} |
{reuseport, boolean()} |
{reuseport_lb, boolean()} |
{ipv6_v6only, boolean()} |
{sndbuf, non_neg_integer()} |
{sctp_autoclose, non_neg_integer()} |
Expand All @@ -84,13 +87,16 @@
buffer |
debug |
dontroute |
exclusiveaddruse |
high_msgq_watermark |
linger |
low_msgq_watermark |
mode |
priority |
recbuf |
reuseaddr |
reuseport |
reuseport_lb |
ipv6_v6only |
sctp_autoclose |
sctp_disable_fragments |
Expand Down
6 changes: 6 additions & 0 deletions lib/kernel/src/gen_tcp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
{deliver, port | term} |
{dontroute, boolean()} |
{exit_on_close, boolean()} |
{exclusiveaddruse, boolean()} |
{header, non_neg_integer()} |
{high_msgq_watermark, pos_integer()} |
{high_watermark, non_neg_integer()} |
Expand All @@ -63,6 +64,8 @@
ValueBin :: binary()} |
{recbuf, non_neg_integer()} |
{reuseaddr, boolean()} |
{reuseport, boolean()} |
{reuseport_lb, boolean()} |
{send_timeout, non_neg_integer() | infinity} |
{send_timeout_close, boolean()} |
{show_econnreset, boolean()} |
Expand All @@ -84,6 +87,7 @@
deliver |
dontroute |
exit_on_close |
exclusiveaddruse |
header |
high_msgq_watermark |
high_watermark |
Expand All @@ -103,6 +107,8 @@
(ValueBin :: binary())} |
recbuf |
reuseaddr |
reuseport |
reuseport_lb |
send_timeout |
send_timeout_close |
show_econnreset |
Expand Down
6 changes: 6 additions & 0 deletions lib/kernel/src/gen_udp.erl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
{deliver, port | term} |
{dontroute, boolean()} |
{drop_membership, membership()} |
{exclusiveaddruse, boolean()} |
{header, non_neg_integer()} |
{high_msgq_watermark, pos_integer()} |
{low_msgq_watermark, pos_integer()} |
Expand All @@ -53,6 +54,8 @@
{read_packets, non_neg_integer()} |
{recbuf, non_neg_integer()} |
{reuseaddr, boolean()} |
{reuseport, boolean()} |
{reuseport_lb, boolean()} |
{sndbuf, non_neg_integer()} |
{tos, non_neg_integer()} |
{tclass, non_neg_integer()} |
Expand All @@ -68,6 +71,7 @@
debug |
deliver |
dontroute |
exclusiveaddruse |
header |
high_msgq_watermark |
low_msgq_watermark |
Expand All @@ -84,6 +88,8 @@
read_packets |
recbuf |
reuseaddr |
reuseport |
reuseport_lb |
sndbuf |
tos |
tclass |
Expand Down
10 changes: 5 additions & 5 deletions lib/kernel/src/inet.erl
Original file line number Diff line number Diff line change
Expand Up @@ -953,9 +953,9 @@ stats() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
connect_options() ->
[debug,
tos, tclass, priority, reuseaddr, keepalive, linger, nodelay,
sndbuf, recbuf,
recvtos, recvtclass, ttl, recvttl,
tos, tclass, priority, reuseaddr, reuseport, reuseport_lb,
exclusiveaddruse, keepalive,
linger, nodelay, sndbuf, recbuf, recvtos, recvtclass, ttl, recvttl,
header, active, packet, packet_size, buffer, mode, deliver, line_delimiter,
exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
low_msgq_watermark, send_timeout, send_timeout_close, delay_send, raw,
Expand Down Expand Up @@ -1044,8 +1044,8 @@ con_add(Name, Val, #connect_opts{} = R, Opts, AllOpts) ->
listen_options() ->
[debug,
tos, tclass,
priority, reuseaddr, keepalive, linger, sndbuf, recbuf, nodelay,
recvtos, recvtclass, ttl, recvttl,
priority, reuseaddr, reuseport, reuseport_lb, exclusiveaddruse, keepalive,
linger, sndbuf, recbuf, nodelay, recvtos, recvtclass, ttl, recvttl,
header, active, packet, buffer, mode, deliver, backlog, ipv6_v6only,
exit_on_close, high_watermark, low_watermark, high_msgq_watermark,
low_msgq_watermark, send_timeout, send_timeout_close, delay_send,
Expand Down
3 changes: 3 additions & 0 deletions lib/kernel/src/inet_int.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@
-define(UDP_OPT_ADD_MEMBERSHIP, 14).
-define(UDP_OPT_DROP_MEMBERSHIP, 15).
-define(INET_OPT_IPV6_V6ONLY, 16).
-define(INET_OPT_REUSEPORT, 17).
-define(INET_OPT_REUSEPORT_LB, 18).
-define(INET_OPT_EXCLUSIVEADDRUSE, 19).
% "Local" options: codes start from 20:
-define(INET_LOPT_BUFFER, 20).
-define(INET_LOPT_HEADER, 21).
Expand Down
2 changes: 1 addition & 1 deletion lib/kernel/src/kernel.app.src
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
{shell_docs_ansi,auto}
]},
{mod, {kernel, []}},
{runtime_dependencies, ["erts-@OTP-18248:OTP-18324@", "stdlib-@OTP-17932@",
{runtime_dependencies, ["erts-@OTP-18248:OTP-18324:OTP-18344@", "stdlib-@OTP-17932@",
"sasl-3.0", "crypto-5.0"]}
]
}.
54 changes: 52 additions & 2 deletions lib/kernel/test/inet_sockopt_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -863,9 +863,14 @@ make_check_fun(Type,Element) ->

%% {OptionName,Value1,Value2,Mandatory,Changeable}
all_listen_options() ->
OsType = os:type(),
OsVersion = os:version(),
[{tos,0,1,false,true},
{priority,0,1,false,true},
{reuseaddr,false,true,false,true},
{reuseaddr,false,true,mandatory_reuseaddr(OsType,OsVersion),true},
{reuseport,false,true,mandatory_reuseport(OsType,OsVersion),true},
{reuseport_lb,false,true,mandatory_reuseport_lb(OsType,OsVersion),true},
{exclusiveaddruse,false,true,mandatory_exclusiveaddruse(OsType,OsVersion),true},
{keepalive,false,true,true,true},
{linger, {false,10}, {true,10},true,true},
{sndbuf,2048,4096,false,true},
Expand All @@ -888,9 +893,14 @@ all_listen_options() ->
{packet_size,0,4,true,true}
].
all_connect_options() ->
OsType = os:type(),
OsVersion = os:version(),
[{tos,0,1,false,true},
{priority,0,1,false,true},
{reuseaddr,false,true,false,true},
{reuseaddr,false,true,mandatory_reuseaddr(OsType,OsVersion),true},
{reuseport,false,true,mandatory_reuseport(OsType,OsVersion),true},
{reuseport_lb,false,true,mandatory_reuseport_lb(OsType,OsVersion),true},
{exclusiveaddruse,false,true,mandatory_exclusiveaddruse(OsType,OsVersion),true},
{keepalive,false,true,true,true},
{linger, {false,10}, {true,10},true,true},
{sndbuf,2048,4096,false,true},
Expand All @@ -914,6 +924,46 @@ all_connect_options() ->
].


%% Mandatory on a lot of system other than those listed below. Please add more...
mandatory_reuseaddr({unix, linux}, _OsVersion) ->
true;
mandatory_reuseaddr({unix, freebsd}, _OsVersion) ->
true;
mandatory_reuseaddr({unix, darwin}, _OsVersion) ->
true;
mandatory_reuseaddr({win32, _}, _OsVersion) ->
true; %% reuseaddr and reuseport are emulated by the inet-driver
mandatory_reuseaddr(_OsType, _OsVersion) ->
false.

%% Mandatory an a lot of system other than those listed below. Please add more...
mandatory_reuseport({win32, _}, _OsVersion) ->
true; %% reuseaddr and reuseport are emulated by the inet-driver
mandatory_reuseport({unix, linux}, {X,Y,_Z}) when X > 4 orelse X == 4 andalso Y >= 6 ->
true;
mandatory_reuseport({unix, freebsd}, {X,Y,_Z}) when X > 11 orelse X == 11 andalso Y >= 4 ->
%% I know that it is available on 11.4, but it may be available earlier...
true;
mandatory_reuseport({unix, darwin}, {X,Y,_Z}) when X > 26 orelse X == 26 andalso Y >= 6 ->
%% I know that it is available on 26.6, but it may be available earlier...
true;
mandatory_reuseport(_OsType, _OsVersion) ->
false.

%% Perhaps mandatory an system other than those listed below. Please add more...
mandatory_reuseport_lb({unix, linux}, {X,Y,_Z}) when X > 4 orelse X == 4 andalso Y >= 6 ->
true;
mandatory_reuseport_lb({unix, freebsd}, {X,Y,_Z}) when X > 13 orelse X == 13 andalso Y >= 1 ->
%% I know that it is available on 13.1, but it may be available earlier...
true;
mandatory_reuseport_lb(_OsType, _OsVersion) ->
false.

mandatory_exclusiveaddruse({win32, _}, {X,Y,_Z}) when X > 5 orelse X == 5 andalso Y >= 2 ->
true;
mandatory_exclusiveaddruse(_OsType, _OsVersion) ->
false.

create_socketpair(ListenOptions,ConnectOptions) ->
{ok,LS}=gen_tcp:listen(0,ListenOptions),
{ok,Port}=inet:port(LS),
Expand Down