From c80a762a85c5cc30f480f2c4609367beba9f9067 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Tue, 27 Aug 2013 11:42:00 -0400 Subject: [PATCH] add {active,N} socket option for TCP, UDP, and SCTP Add the {active,N} socket option, where N is an integer in the range -32768..32767, to allow a caller to specify the number of data messages to be delivered to the controlling process. Once the socket's delivered message count either reaches 0 or is explicitly set to 0 with inet:setopts/2 or by including {active,0} as an option when the socket is created, the socket transitions to passive ({active, false}) mode and the socket's controlling process receives a message to inform it of the transition. TCP sockets receive {tcp_passive,Socket}, UDP sockets receive {udp_passive,Socket} and SCTP sockets receive {sctp_passive,Socket}. The socket's delivered message counter defaults to 0, but it can be set using {active,N} via any gen_tcp, gen_udp, or gen_sctp function that takes socket options as arguments, or via inet:setopts/2. New N values are added to the socket's current counter value, and negative numbers can be used to reduce the counter value. Specifying a number that would cause the socket's counter value to go above 32767 causes an einval error. If a negative number is specified such that the counter value would become negative, the socket's counter value is set to 0 and the socket transitions to passive mode. If the counter value is already 0 and inet:setopts(Socket, [{active,0}]) is specified, the counter value remains at 0 but the appropriate passive mode transition message is generated for the socket. This commit contains a modified preloaded prim_inet.beam due to changes in prim_inet.erl. Add tests for {active,N} mode for TCP, UDP, and SCTP sockets. Add documentation for {active,N} mode for inet, gen_tcp, gen_udp, and gen_sctp. --- erts/emulator/drivers/common/inet_drv.c | 119 ++++++++++++++++++++-- erts/preloaded/ebin/prim_inet.beam | Bin 70960 -> 71456 bytes erts/preloaded/src/prim_inet.erl | 20 +++- lib/kernel/doc/src/gen_sctp.xml | 31 ++++-- lib/kernel/doc/src/gen_tcp.xml | 6 ++ lib/kernel/doc/src/gen_udp.xml | 17 +++- lib/kernel/doc/src/inet.xml | 51 +++++++--- lib/kernel/src/gen_sctp.erl | 2 +- lib/kernel/src/gen_tcp.erl | 2 +- lib/kernel/src/gen_udp.erl | 2 +- lib/kernel/src/inet.erl | 14 ++- lib/kernel/src/inet_int.hrl | 1 + lib/kernel/test/gen_sctp_SUITE.erl | 125 +++++++++++++++++++++++- lib/kernel/test/gen_tcp_misc_SUITE.erl | 114 ++++++++++++++++++++- lib/kernel/test/gen_udp_SUITE.erl | 106 +++++++++++++++++++- 15 files changed, 557 insertions(+), 53 deletions(-) diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 60db50e80a7c..7dcb191a83ef 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -86,6 +86,13 @@ #endif typedef unsigned long long llu_t; +#ifndef INT16_MIN +#define INT16_MIN (-32768) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif + #ifdef __WIN32__ #define STRNCASECMP strncasecmp @@ -581,6 +588,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_PASSIVE 0 /* false */ #define INET_ACTIVE 1 /* true */ #define INET_ONCE 2 /* true; active once then passive */ +#define INET_MULTI 3 /* true; active N then passive */ /* INET_REQ_GETSTATUS enumeration */ #define INET_F_OPEN 0x0001 @@ -925,6 +933,7 @@ typedef struct { inet_async_op op_queue[INET_MAX_ASYNC]; /* call queue */ int active; /* 0 = passive, 1 = active, 2 = active once */ + Sint16 active_count; /* counter for {active,N} */ int stype; /* socket type: SOCK_STREAM/SOCK_DGRAM/SOCK_SEQPACKET */ int sprotocol; /* socket protocol: @@ -1160,22 +1169,36 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event); static int async_ref = 0; /* async reference id generator */ #define NEW_ASYNC_ID() ((async_ref++) & 0xffff) +/* check for transition from active to passive */ +#define INET_CHECK_ACTIVE_TO_PASSIVE(inet) \ + do { \ + if ((inet)->active == INET_ONCE) \ + (inet)->active = INET_PASSIVE; \ + else if ((inet)->active == INET_MULTI && --((inet)->active_count) == 0) { \ + (inet)->active = INET_PASSIVE; \ + packet_passive_message(inet); \ + } \ + } while (0) static ErlDrvTermData am_ok; static ErlDrvTermData am_tcp; static ErlDrvTermData am_udp; static ErlDrvTermData am_error; +static ErlDrvTermData am_einval; static ErlDrvTermData am_inet_async; static ErlDrvTermData am_inet_reply; static ErlDrvTermData am_timeout; static ErlDrvTermData am_closed; +static ErlDrvTermData am_tcp_passive; static ErlDrvTermData am_tcp_closed; static ErlDrvTermData am_tcp_error; +static ErlDrvTermData am_udp_passive; static ErlDrvTermData am_udp_error; static ErlDrvTermData am_empty_out_q; static ErlDrvTermData am_ssl_tls; #ifdef HAVE_SCTP static ErlDrvTermData am_sctp; +static ErlDrvTermData am_sctp_passive; static ErlDrvTermData am_sctp_error; static ErlDrvTermData am_true; static ErlDrvTermData am_false; @@ -1185,6 +1208,7 @@ static ErlDrvTermData am_list; static ErlDrvTermData am_binary; static ErlDrvTermData am_active; static ErlDrvTermData am_once; +static ErlDrvTermData am_multi; static ErlDrvTermData am_buffer; static ErlDrvTermData am_linger; static ErlDrvTermData am_recbuf; @@ -3336,6 +3360,34 @@ static int packet_binary_message return erl_drv_output_term(desc->dport, spec, i); } +/* +** active mode message: send active-to-passive transition message +** {tcp_passive, S} or +** {udp_passive, S} or +** {sctp_passive, S} +*/ + static int packet_passive_message(inet_descriptor* desc) + { + ErlDrvTermData spec[6]; + int i = 0; + + DEBUGF(("packet_passive_message(%ld):\r\n", (long)desc->port)); + + if (desc->sprotocol == IPPROTO_TCP) + i = LOAD_ATOM(spec, i, am_tcp_passive); + else { +#ifdef HAVE_SCTP + i = LOAD_ATOM(spec, i, IS_SCTP(desc) ? am_sctp_passive : am_udp_passive); +#else + i = LOAD_ATOM(spec, i, am_udp_passive); +#endif + } + i = LOAD_PORT(spec, i, desc->dport); + i = LOAD_TUPLE(spec, i, 2); + ASSERT(i <= 6); + return erl_drv_output_term(desc->dport, spec, i); + } + /* ** send active message {udp_error|sctp_error, S, Error} */ @@ -3378,7 +3430,7 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len) int code; const char* body = buf; int bodylen = len; - + packet_get_body(desc->inet.htype, &body, &bodylen); if (desc->inet.deliver == INET_DELIVER_PORT) { @@ -3396,8 +3448,7 @@ static int tcp_reply_data(tcp_descriptor* desc, char* buf, int len) if (code < 0) return code; - if (desc->inet.active == INET_ONCE) - desc->inet.active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc)); return code; } @@ -3424,8 +3475,7 @@ tcp_reply_binary_data(tcp_descriptor* desc, ErlDrvBinary* bin, int offs, int len } if (code < 0) return code; - if (desc->inet.active == INET_ONCE) - desc->inet.active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(INETP(desc)); return code; } @@ -3448,8 +3498,7 @@ packet_reply_binary_data(inet_descriptor* desc, unsigned int hsz, code = packet_binary_message(desc, bin, offs, len, extra); if (code < 0) return code; - if (desc->active == INET_ONCE) - desc->active = INET_PASSIVE; + INET_CHECK_ACTIVE_TO_PASSIVE(desc); return code; } } @@ -3494,6 +3543,7 @@ sock_init(void) /* May be called multiple times. */ #ifdef HAVE_SCTP static void inet_init_sctp(void) { INIT_ATOM(sctp); + INIT_ATOM(sctp_passive); INIT_ATOM(sctp_error); INIT_ATOM(true); INIT_ATOM(false); @@ -3503,6 +3553,7 @@ static void inet_init_sctp(void) { INIT_ATOM(binary); INIT_ATOM(active); INIT_ATOM(once); + INIT_ATOM(multi); INIT_ATOM(buffer); INIT_ATOM(linger); INIT_ATOM(recbuf); @@ -3628,12 +3679,15 @@ static int inet_init() INIT_ATOM(tcp); INIT_ATOM(udp); INIT_ATOM(error); + INIT_ATOM(einval); INIT_ATOM(inet_async); INIT_ATOM(inet_reply); INIT_ATOM(timeout); INIT_ATOM(closed); + INIT_ATOM(tcp_passive); INIT_ATOM(tcp_closed); INIT_ATOM(tcp_error); + INIT_ATOM(udp_passive); INIT_ATOM(udp_error); INIT_ATOM(empty_out_q); INIT_ATOM(ssl_tls); @@ -5508,8 +5562,25 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_LOPT_ACTIVE: DEBUGF(("inet_set_opts(%ld): s=%d, ACTIVE=%d\r\n", - (long)desc->port, desc->s,ival)); + (long)desc->port, desc->s, ival)); desc->active = ival; + if (desc->active == INET_MULTI) { + long ac = desc->active_count; + Sint16 nval = get_int16(ptr); + ptr += 2; + len -= 2; + ac += nval; + if (ac > INT16_MAX || ac < INT16_MIN) + return -1; + desc->active_count += nval; + if (desc->active_count < 0) + desc->active_count = 0; + if (desc->active_count == 0) { + desc->active = INET_PASSIVE; + packet_passive_message(desc); + } + } else + desc->active_count = 0; if ((desc->stype == SOCK_STREAM) && (desc->active != INET_PASSIVE) && (desc->state == INET_STATE_CLOSED)) { tcp_closed_message((tcp_descriptor *) desc); @@ -5820,7 +5891,8 @@ static int inet_set_opts(inet_descriptor* desc, char* ptr, int len) /* XXX fprintf(stderr,"desc->htype == %d, old_htype == %d, desc->active == %d, old_active == %d\r\n",(int)desc->htype, (int) old_htype, (int) desc->active, (int) old_active );*/ - return 1+(desc->htype == old_htype && desc->active == INET_ONCE); + return 1+(desc->htype == old_htype && + (desc->active == INET_ONCE || desc->active == INET_MULTI)); } return 0; } @@ -5953,6 +6025,21 @@ static int sctp_set_opts(inet_descriptor* desc, char* ptr, int len) case INET_LOPT_ACTIVE: desc->active = get_int32(curr); curr += 4; + if (desc->active == INET_MULTI) { + long ac = desc->active_count; + Sint16 nval = get_int16(curr); curr += 2; + ac += nval; + if (ac > INT16_MAX || ac < INT16_MIN) + return -1; + desc->active_count += nval; + if (desc->active_count < 0) + desc->active_count = 0; + if (desc->active_count == 0) { + desc->active = INET_PASSIVE; + packet_passive_message(desc); + } + } else + desc->active_count = 0; res = 0; continue; @@ -6475,6 +6562,11 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, case INET_LOPT_ACTIVE: *ptr++ = opt; put_int32(desc->active, ptr); + if (desc->active == INET_MULTI) { + PLACE_FOR(2,ptr); + put_int16(desc->active_count, ptr); + ptr += 2; + } continue; case INET_LOPT_PACKET: *ptr++ = opt; @@ -6847,7 +6939,10 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, } case INET_LOPT_ACTIVE: { - PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT); + if (desc->active == INET_MULTI) + PLACE_FOR(spec, i, LOAD_ATOM_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT); + else + PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_TUPLE_CNT); i = LOAD_ATOM (spec, i, am_active); switch (desc->active) { @@ -6860,6 +6955,9 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, case INET_ONCE : { i = LOAD_ATOM (spec, i, am_once); break; } + case INET_MULTI : + { i = LOAD_INT(spec, i, desc->active_count); break; } + default: ASSERT (0); } i = LOAD_TUPLE (spec, i, 2); @@ -7656,6 +7754,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) socket */ desc->deliver = INET_DELIVER_TERM; /* standard term format */ desc->active = INET_PASSIVE; /* start passive */ + desc->active_count = 0; desc->oph = NULL; desc->opt = NULL; diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 5b388712823df845abba4b7a92ee4417dc3ba8af..f3e9712289f5cdd84025394c9717515ab303800e 100644 GIT binary patch delta 47181 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 delta 46804 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 diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index fa621681f364..69eed716ff3c 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1237,7 +1237,8 @@ type_opt_1(buffer) -> int; type_opt_1(active) -> {enum,[{false, ?INET_PASSIVE}, {true, ?INET_ACTIVE}, - {once, ?INET_ONCE}]}; + {once, ?INET_ONCE}, + {multi, ?INET_MULTI}]}; type_opt_1(packet) -> {enum,[{0, ?TCP_PB_RAW}, {1, ?TCP_PB_1}, @@ -1716,11 +1717,14 @@ encode_opt_val(Opts) -> Reason -> {error,Reason} end. +%% {active, once} and {active, N} are specially optimized because they will +%% be used for every packet or every N packets, not only once when +%% initializing the socket. Measurements show that this optimization is +%% worthwhile. enc_opt_val([{active,once}|Opts], Acc) -> - %% Specially optimized because {active,once} will be used for - %% every packet, not only once when initializing the socket. - %% Measurements show that this optimization is worthwhile. enc_opt_val(Opts, [<>|Acc]); +enc_opt_val([{active,N}|Opts], Acc) when is_integer(N), N < 32768, N >= -32768 -> + enc_opt_val(Opts, [<>|Acc]); enc_opt_val([{raw,P,O,B}|Opts], Acc) -> enc_opt_val(Opts, Acc, raw, {P,O,B}); enc_opt_val([{Opt,Val}|Opts], Acc) -> @@ -1810,6 +1814,14 @@ dec_opt_val([]) -> []. dec_opt_val(Buf, raw, Type) -> {{P,O,B},T} = dec_value(Type, Buf), [{raw,P,O,B}|dec_opt_val(T)]; +dec_opt_val(Buf, active, Type) -> + case dec_value(Type, Buf) of + {multi,[M0,M1|T]} -> + <> = list_to_binary([M0,M1]), + [{active,N}|dec_opt_val(T)]; + {Val,T} -> + [{active,Val}|dec_opt_val(T)] + end; dec_opt_val(Buf, Opt, Type) -> {Val,T} = dec_value(Type, Buf), [{Opt,Val}|dec_opt_val(T)]. diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 7ea58ffffff0..c57f858c075e 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -496,9 +496,11 @@ orthogonal to the sets of TCP, UDP and generic INET options: only those options which are explicitly listed below are allowed for SCTP sockets. Options can be set on the socket using - gen_sctp:open/1,2 or inet:setopts/2, - retrieved using inet:getopts/2, and when calling - gen_sctp:connect/4,5 options can be changed.

+ gen_sctp:open/1,2 + or inet:setopts/2, + retrieved using inet:getopts/2, + and when calling gen_sctp:connect/4,5 + options can be changed.

@@ -507,7 +509,7 @@

Determines the type of data returned from gen_sctp:recv/1,2.

- {active, true|false|once} + {active, true|false|once|N} @@ -524,11 +526,28 @@

If once, only one message is automatically placed - in the message queue, after that the mode is automatically - re-set to passive. This provides flow control as well as + in the message queue, and after that the mode is automatically + reset to passive. This provides flow control as well as the possibility for the receiver to listen for its incoming SCTP data interleaved with other inter-process messages.

+ +

If active is specified as an integer N in the + range -32768 to 32767 (inclusive), then that number is added to + the socket's count of the number of data messages to be + delivered to the controlling process. If the result of the + addition would be negative, the count is set to 0. Once the + count reaches 0, either through the delivery of messages or by + being explicitly set with inet:setopts/2, the socket's + mode is automatically reset to passive ({active, + false}) mode. When a socket in this active mode transitions to + passive mode, the message {sctp_passive, Socket} is sent + to the controlling process to notify it that if it wants to + receive more data messages from the socket, it must call + inet:setopts/2 to set + the socket back into an active mode.

+
{tos, integer()} diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 11a0843c104c..c74a96fbaf45 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -148,6 +148,12 @@ do_recv(Sock, Bs) -> as messages:

{tcp, Socket, Data} +

If the socket is in {active, N} mode (see + inet:setopts/2 for details) and its message counter + drops to 0, the following message is delivered to indicate that the + socket has transitioned to passive ({active, false}) mode:

+ +{tcp_passive, Socket}

If the socket is closed, the following message is delivered:

{tcp_closed, Socket} diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 4850278a64b5..a8f4e11f0379 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -145,14 +145,23 @@ inet:setopts/2.

-

The returned socket Socket is used to send packets - from this port with send/4. When UDP packets arrive at - the opened port, they are delivered as messages:

+

The returned socket Socket is used to send + packets from this port with send/4. When UDP packets arrive + at the opened port, if the socket is in an active mode the packets + are delivered as messages to the controlling process:

{udp, Socket, IP, InPortNo, Packet} -

Note that arriving UDP packets that are longer than +

If the socket is not in an active mode, data can be + retrieved via the recv/2,3 calls. + Note that arriving UDP packets that are longer than the receive buffer option specifies, might be truncated without warning.

+

When a socket in {active, N} mode (see + inet:setopts/2 for details) transitions to passive + ({active, false}) mode, the controlling process is notified by a + message of the following form:

+ +{udp_passive, Socket}

IP and InPortNo define the address from which Packet came. Packet is a list of bytes if the option list was specified. Packet is a diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index fd62f778a23e..92af3434567c 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -456,47 +456,66 @@ fe80::204:acff:fe17:bf38

Sets one or more options for a socket. The following options are available:

- {active, true | false | once} + {active, true | false | once | N}

If the value is true, which is the default, everything received from the socket will be sent as messages to the receiving process. If the value is false (passive mode), the process must explicitly - receive incoming data by calling gen_tcp:recv/2,3 - or gen_udp:recv/2,3 (depending on the type of - socket).

+ receive incoming data by calling + gen_tcp:recv/2,3, + gen_udp:recv/2,3 + or gen_sctp:recv/1,2 + (depending on the type of socket).

If the value is once ({active, once}), one data message from the socket will be sent to the process. To receive one more message, setopts/2 must be called again with the {active, once} option.

-

When using {active, once}, the socket changes - behaviour automatically when data is received. This can - sometimes be confusing in combination with connection - oriented sockets (i.e. gen_tcp) as a socket with - {active, false} behaviour reports closing +

If the value is an integer N in the range -32768 to 32767 + (inclusive), the value is added to the socket's count of data + messages sent to the controlling process. A socket's default + message count is 0. If a negative value is specified and its + magnitude is equal to or greater than the socket's current + message count, the socket's message count is set to 0. Once + the socket's message count reaches 0, either due to sending + received data messages to the process or by being explicitly set, + the process is then notified by a special message, specific to + the type of socket, that the socket has entered passive + mode. Once the socket enters passive mode, to receive more + messages setopts/2 must be called again to set the + socket back into an active mode.

+

When using {active, once} or {active, N}, the + socket changes behaviour automatically when data is received. + This can sometimes be confusing in combination with + connection-oriented sockets (i.e. gen_tcp) as a socket + with {active, false} behaviour reports closing differently than a socket with {active, true} behaviour. To make programming easier, a socket where the peer closed and this was detected while in {active, false} mode, will still generate the message - {tcp_closed,Socket} when set to {active, once} or {active, true} mode. It is therefore + {tcp_closed,Socket} when set to {active, once}, + {active, true} or {active, N} mode. It is therefore safe to assume that the message {tcp_closed,Socket}, possibly followed by socket port termination (depending on the exit_on_close option) will eventually appear when a socket changes back and forth between {active, true} and - {active, false} mode. However, + {active, false} mode. However, when peer closing is detected is all up to the underlying TCP/IP stack and protocol.

-

Note that {active,true} mode provides no flow +

Note that {active, true} mode provides no flow control; a fast sender could easily overflow the - receiver with incoming messages. Use active mode only if + receiver with incoming messages. The same is true of + {active, N} mode while the message count is greater + than zero. Use active mode only if your high-level protocol provides its own flow control (for instance, acknowledging received messages) or the - amount of data exchanged is small. {active,false} - mode or use of the {active, once} mode provides - flow control; the other side will not be able send + amount of data exchanged is small. {active, false} + mode, use of the {active, once} mode or {active, N} + mode with values of N appropriate for the application + provides flow control; the other side will not be able send faster than the receiver can read.

diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index 58d84ae92460..86ef4fa58447 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -36,7 +36,7 @@ -type assoc_id() :: term(). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {dontroute, boolean()} | {high_msgq_watermark, pos_integer()} | diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index a98ed4c238a0..dad74d82b55d 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -30,7 +30,7 @@ -include("file.hrl"). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {buffer, non_neg_integer()} | {delay_send, boolean()} | {deliver, port | term} | diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index e82b11d2ef58..70dceb3679d8 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -26,7 +26,7 @@ -include("inet_int.hrl"). -type option() :: - {active, true | false | once} | + {active, true | false | once | -32768..32767} | {add_membership, {inet:ip_address(), inet:ip_address()}} | {broadcast, boolean()} | {buffer, non_neg_integer()} | diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 27f085c3aaea..e0c1666feef0 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -667,6 +667,9 @@ con_opt([Opt | Opts], R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#connect_opts.opts), + con_opt(Opts, R#connect_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> con_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -733,6 +736,9 @@ list_opt([Opt | Opts], R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#listen_opts.opts), + list_opt(Opts, R#listen_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> list_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -787,6 +793,9 @@ udp_opt([Opt | Opts], R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#udp_opts.opts), + udp_opt(Opts, R#udp_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} when is_atom(Name) -> udp_add(Name, Val, R, Opts, As); _ -> {error, badarg} end; @@ -805,7 +814,7 @@ udp_add(Name, Val, R, Opts, As) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Currently supported options include: % (*) {mode, list|binary} or just list|binary -% (*) {active, true|false|once} +% (*) {active, true|false|once|N} % (*) {sctp_module, inet_sctp|inet6_sctp} or just inet|inet6 % (*) options set via setsockopt. % The full list is below in sctp_options/0 . @@ -867,6 +876,9 @@ sctp_opt([Opt|Opts], Mod, R, As) -> false -> {error, badarg} end; + {active,N} when is_integer(N), N < 32768, N >= -32768 -> + NOpts = lists:keydelete(active, 1, R#sctp_opts.opts), + sctp_opt(Opts, Mod, R#sctp_opts { opts = [{active,N}|NOpts] }, As); {Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val); _ -> {error,badarg} end; diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index 18a4a61b2f9a..024a5fd3b67b 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -46,6 +46,7 @@ -define(INET_PASSIVE, 0). -define(INET_ACTIVE, 1). -define(INET_ONCE, 2). % Active once then passive +-define(INET_MULTI, 3). % Active N then passive %% state codes (getstatus, INET_REQ_GETSTATUS) -define(INET_F_OPEN, 16#0001). diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index e89cb4479751..6dad52395b93 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -35,8 +35,9 @@ open_unihoming_ipv6_socket/1, open_multihoming_ipv6_socket/1, open_multihoming_ipv4_and_ipv6_socket/1, - basic_stream/1, xfer_stream_min/1, peeloff_active_once/1, - peeloff_active_true/1, buffers/1]). + basic_stream/1, xfer_stream_min/1, active_n/1, + peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1, + buffers/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -46,9 +47,9 @@ all() -> open_multihoming_ipv4_socket, open_unihoming_ipv6_socket, open_multihoming_ipv6_socket, - open_multihoming_ipv4_and_ipv6_socket, + open_multihoming_ipv4_and_ipv6_socket, active_n, basic_stream, xfer_stream_min, peeloff_active_once, - peeloff_active_true, buffers]. + peeloff_active_true, peeloff_active_n, buffers]. groups() -> []. @@ -767,6 +768,106 @@ implicit_inet6(S1, Addr) -> end, ?line ok = gen_sctp:close(S2). +active_n(doc) -> + "Verify {active,N} socket management"; +active_n(suite) -> + []; +active_n(Config) when is_list(Config) -> + N = 3, + S1 = ok(gen_sctp:open([{active,N}])), + [{active,N}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,-N}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,0}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + ok = inet:setopts(S1, [{active,32767}]), + {error,einval} = inet:setopts(S1, [{active,1}]), + {error,einval} = inet:setopts(S1, [{active,-32769}]), + ok = inet:setopts(S1, [{active,-32768}]), + receive + {sctp_passive, S1} -> ok + after + 5000 -> + exit({error,sctp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,true}]), + [{active,true}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,once}]), + [{active,once}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(S1, [{active,32768}]), + ok = inet:setopts(S1, [{active,false}]), + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = gen_sctp:listen(S1, true), + S1Port = ok(inet:port(S1)), + S2 = ok(gen_sctp:open(0, [{active,false}])), + Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])), + ok = inet:setopts(S1, [{active,N}]), + [{active,N}] = ok(inet:getopts(S1, [active])), + LoopFun = fun(Count, Count, _Fn) -> + receive + {sctp_passive,S1} -> + ok + after + 5000 -> + exit({error,timeout}) + end; + (I, Count, Fn) -> + Msg = list_to_binary("message "++integer_to_list(I)), + ok = gen_sctp:send(S2, Assoc, 0, Msg), + receive + {sctp,S1,_,_,{[SR],Msg}} when is_record(SR, sctp_sndrcvinfo) -> + Fn(I+1, Count, Fn); + {sctp,S1,_,_,_} -> + %% ignore non-data messages + ok = inet:setopts(S1, [{active,1}]), + Fn(I, Count, Fn); + Other -> + exit({unexpected, Other}) + after + 5000 -> + exit({error,timeout}) + end + end, + ok = LoopFun(1, N, LoopFun), + S3 = ok(gen_sctp:open([{active,0}])), + receive + {sctp_passive,S3} -> + [{active,false}] = ok(inet:getopts(S3, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + ok = gen_sctp:close(S3), + ok = gen_sctp:close(S2), + ok = gen_sctp:close(S1), + ok. + basic_stream(doc) -> "Hello world stream socket"; basic_stream(suite) -> @@ -941,6 +1042,14 @@ peeloff_active_true(suite) -> peeloff_active_true(Config) -> peeloff(Config, [{active,true}]). +peeloff_active_n(doc) -> + "Peel off an SCTP stream socket ({active,N})"; +peeloff_active_n(suite) -> + []; + +peeloff_active_n(Config) -> + peeloff(Config, [{active,1}]). + peeloff(Config, SockOpts) when is_list(Config) -> ?line Addr = {127,0,0,1}, ?line Stream = 0, @@ -1519,7 +1628,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> end. again(Socket) -> - inet:setopts(Socket, [{active,once}]). + receive + {sctp_passive,Socket} -> + [{active, false}] = ok(inet:getopts(Socket, [active])), + ok = inet:setopts(Socket,[{active,1}]) + after 0 -> + ok = inet:setopts(Socket, [{active,once}]) + end. gb_push(Key, Val, GBT) -> case gb_trees:lookup(Key, GBT) of diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index ee271fbdfa74..ee8bfcceb1b2 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -25,7 +25,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, controlling_process/1, controlling_process_self/1, - no_accept/1, close_with_pending_output/1, + no_accept/1, close_with_pending_output/1, active_n/1, data_before_close/1, iter_max_socks/1, get_status/1, passive_sockets/1, accept_closed_by_other_process/1, init_per_testcase/2, end_per_testcase/2, @@ -70,7 +70,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [controlling_process, controlling_process_self, no_accept, close_with_pending_output, data_before_close, - iter_max_socks, passive_sockets, + iter_max_socks, passive_sockets, active_n, accept_closed_by_other_process, otp_3924, closed_socket, shutdown_active, shutdown_passive, shutdown_pending, default_options, http_bad_packet, busy_send, @@ -407,6 +407,114 @@ send_loop(Sock, Data, Left) -> ok = gen_tcp:send(Sock, Data), send_loop(Sock, Data, Left-1). +%% Test {active,N} option +active_n(doc) -> + ["Verify operation of the {active,N} option."]; +active_n(suite) -> []; +active_n(Config) when is_list(Config) -> + N = 3, + LS = ok(gen_tcp:listen(0, [{active,N}])), + [{active,N}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,-N}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,0}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + ok = inet:setopts(LS, [{active,32767}]), + {error,einval} = inet:setopts(LS, [{active,1}]), + {error,einval} = inet:setopts(LS, [{active,-32769}]), + ok = inet:setopts(LS, [{active,-32768}]), + receive + {tcp_passive, LS} -> ok + after + 5000 -> + exit({error,tcp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(LS, [active])), + ok = inet:setopts(LS, [{active,N}]), + ok = inet:setopts(LS, [{active,true}]), + [{active,true}] = ok(inet:getopts(LS, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(LS, [{active,N}]), + ok = inet:setopts(LS, [{active,once}]), + [{active,once}] = ok(inet:getopts(LS, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(LS, [{active,32768}]), + ok = inet:setopts(LS, [{active,false}]), + [{active,false}] = ok(inet:getopts(LS, [active])), + Port = ok(inet:port(LS)), + C = ok(gen_tcp:connect("localhost", Port, [{active,N}])), + [{active,N}] = ok(inet:getopts(C, [active])), + S = ok(gen_tcp:accept(LS)), + ok = inet:setopts(S, [{active,N}]), + [{active,N}] = ok(inet:getopts(S, [active])), + repeat(3, + fun(I) -> + Msg = "message "++integer_to_list(I), + ok = gen_tcp:send(C, Msg), + receive + {tcp,S,Msg} -> + ok = gen_tcp:send(S, Msg) + after + 5000 -> + exit({error,timeout}) + end, + receive + {tcp,C,Msg} -> + ok + after + 5000 -> + exit({error,timeout}) + end + end), + receive + {tcp_passive,S} -> + [{active,false}] = ok(inet:getopts(S, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + receive + {tcp_passive,C} -> + [{active,false}] = ok(inet:getopts(C, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + LS2 = ok(gen_tcp:listen(0, [{active,0}])), + receive + {tcp_passive,LS2} -> + [{active,false}] = ok(inet:getopts(LS2, [active])) + after + 5000 -> + exit({error,tcp_passive}) + end, + ok = gen_tcp:close(LS2), + ok = gen_tcp:close(C), + ok = gen_tcp:close(S), + ok = gen_tcp:close(LS), + ok. + -define(OTP_3924_MAX_DELAY, 100). %% Taken out of the blue, but on intra host connections %% I expect propagation of a close to be quite fast @@ -2659,3 +2767,5 @@ oct_aloop(S,X,Times) -> gen_tcp:close(S), closed end. + +ok({ok,V}) -> V. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index cd768813cf6f..fc90e0e6549d 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -34,7 +34,7 @@ init_per_group/2,end_per_group/2]). -export([init_per_testcase/2, end_per_testcase/2]). --export([send_to_closed/1, +-export([send_to_closed/1, active_n/1, buffer_size/1, binary_passive_recv/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1]). @@ -43,7 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [send_to_closed, buffer_size, binary_passive_recv, bad_address, read_packets, open_fd, connect, - implicit_inet6]. + implicit_inet6, active_n]. groups() -> []. @@ -466,6 +466,108 @@ open_fd(Config) when is_list(Config) -> ?t:fail(io_lib:format("~w", [flush()])) end. +active_n(Config) when is_list(Config) -> + N = 3, + S1 = ok(gen_udp:open(0, [{active,N}])), + [{active,N}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,-N}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,0}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + ok = inet:setopts(S1, [{active,32767}]), + {error,einval} = inet:setopts(S1, [{active,1}]), + {error,einval} = inet:setopts(S1, [{active,-32769}]), + ok = inet:setopts(S1, [{active,-32768}]), + receive + {udp_passive, S1} -> ok + after + 5000 -> + exit({error,udp_passive_failure}) + end, + [{active,false}] = ok(inet:getopts(S1, [active])), + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,true}]), + [{active,true}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + ok = inet:setopts(S1, [{active,N}]), + ok = inet:setopts(S1, [{active,once}]), + [{active,once}] = ok(inet:getopts(S1, [active])), + receive + _ -> exit({error,active_n}) + after + 0 -> + ok + end, + {error,einval} = inet:setopts(S1, [{active,32768}]), + ok = inet:setopts(S1, [{active,false}]), + [{active,false}] = ok(inet:getopts(S1, [active])), + S1Port = ok(inet:port(S1)), + S2 = ok(gen_udp:open(0, [{active,N}])), + S2Port = ok(inet:port(S2)), + [{active,N}] = ok(inet:getopts(S2, [active])), + ok = inet:setopts(S1, [{active,N}]), + [{active,N}] = ok(inet:getopts(S1, [active])), + lists:foreach( + fun(I) -> + Msg = "message "++integer_to_list(I), + ok = gen_udp:send(S2, "localhost", S1Port, Msg), + receive + {udp,S1,_,S2Port,Msg} -> + ok = gen_udp:send(S1, "localhost", S2Port, Msg) + after + 5000 -> + exit({error,timeout}) + end, + receive + {udp,S2,_,S1Port,Msg} -> + ok + after + 5000 -> + exit({error,timeout}) + end + end, lists:seq(1,N)), + receive + {udp_passive,S1} -> + [{active,false}] = ok(inet:getopts(S1, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + receive + {udp_passive,S2} -> + [{active,false}] = ok(inet:getopts(S2, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + S3 = ok(gen_udp:open(0, [{active,0}])), + receive + {udp_passive,S3} -> + [{active,false}] = ok(inet:getopts(S3, [active])) + after + 5000 -> + exit({error,udp_passive}) + end, + ok = gen_udp:close(S3), + ok = gen_udp:close(S2), + ok = gen_udp:close(S1), + ok. % % Utils