aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/configure.in2
-rw-r--r--erts/emulator/beam/io.c2
-rw-r--r--erts/emulator/drivers/common/inet_drv.c242
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin70856 -> 72644 bytes
-rw-r--r--erts/preloaded/src/prim_inet.erl70
-rw-r--r--lib/kernel/doc/src/inet.xml92
-rw-r--r--lib/kernel/src/inet.erl39
-rw-r--r--lib/kernel/src/inet_int.hrl2
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl333
9 files changed, 662 insertions, 120 deletions
diff --git a/erts/configure.in b/erts/configure.in
index b47a6ff094..ba80fdbbbe 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1647,7 +1647,7 @@ if test "x$enable_sctp" != "xno" ; then
fi
if test x"$ac_cv_header_netinet_sctp_h" = x"yes"; then
- AC_CHECK_FUNCS([sctp_bindx sctp_peeloff])
+ AC_CHECK_FUNCS([sctp_bindx sctp_peeloff sctp_getladdrs sctp_freeladdrs sctp_getpaddrs sctp_freepaddrs])
AC_CHECK_DECLS([SCTP_UNORDERED, SCTP_ADDR_OVER, SCTP_ABORT,
SCTP_EOF, SCTP_SENDALL, SCTP_ADDR_CONFIRMED,
SCTP_DELAYED_ACK_TIME,
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index db19f6c142..6911fd8241 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -4078,7 +4078,7 @@ erts_port_control(Process* c_p,
copy = 1;
else {
binp = ((ProcBin *) ebinp)->val;
- ASSERT(bufp < bufp + size);
+ ASSERT(bufp <= bufp + size);
ASSERT(binp->orig_bytes <= bufp
&& bufp + size <= binp->orig_bytes + binp->orig_size);
erts_refc_inc(&binp->refc, 1);
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 12f45245b5..45fac69303 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -417,13 +417,44 @@ static unsigned long one_value = 1;
# define sctp_adaptation_layer_event sctp_adaption_layer_event
#endif
-#ifdef __GNUC__
+#if defined(__GNUC__) && defined(HAVE_SCTP_BINDX)
static typeof(sctp_bindx) *p_sctp_bindx = NULL;
+#else
+static int (*p_sctp_bindx)
+ (int sd, struct sockaddr *addrs, int addrcnt, int flags) = NULL;
+#endif
+
+#if defined(__GNUC__) && defined(HAVE_SCTP_PEELOFF)
static typeof(sctp_peeloff) *p_sctp_peeloff = NULL;
#else
-static int (*p_sctp_bindx)(int sd, struct sockaddr *addrs,
- int addrcnt, int flags) = NULL;
-static int (*p_sctp_peeloff)(int sd, sctp_assoc_t assoc_id) = NULL;
+static int (*p_sctp_peeloff)
+ (int sd, sctp_assoc_t assoc_id) = NULL;
+#endif
+
+#if defined(__GNUC__) && defined(HAVE_SCTP_GETLADDRS)
+static typeof(sctp_getladdrs) *p_sctp_getladdrs = NULL;
+#else
+static int (*p_sctp_getladdrs)
+ (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL;
+#endif
+
+#if defined(__GNUC__) && defined(HAVE_SCTP_FREELADDRS)
+static typeof(sctp_freeladdrs) *p_sctp_freeladdrs = NULL;
+#else
+static void (*p_sctp_freeladdrs)(struct sockaddr *addrs) = NULL;
+#endif
+
+#if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS)
+static typeof(sctp_getpaddrs) *p_sctp_getpaddrs = NULL;
+#else
+static int (*p_sctp_getpaddrs)
+ (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL;
+#endif
+
+#if defined(__GNUC__) && defined(HAVE_SCTP_FREEPADDRS)
+static typeof(sctp_freepaddrs) *p_sctp_freepaddrs = NULL;
+#else
+static void (*p_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
#endif
#endif /* #if defined(HAVE_SCTP_H) */
@@ -593,7 +624,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_F_BUSY 0x0080
#define INET_F_MULTI_CLIENT 0x0100 /* Multiple clients for one descriptor, i.e. multi-accept */
-/* One numberspace for *_REC_* so if an e.g UDP request is issued
+/* One numberspace for *_REQ_* so if an e.g UDP request is issued
** for a TCP socket, the driver can protest.
*/
#define INET_REQ_OPEN 1
@@ -624,6 +655,8 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_REQ_ACCEPT 26
#define INET_REQ_LISTEN 27
#define INET_REQ_IGNOREFD 28
+#define INET_REQ_GETLADDRS 29
+#define INET_REQ_GETPADDRS 30
/* TCP requests */
/* #define TCP_REQ_ACCEPT 40 MOVED */
@@ -1417,8 +1450,7 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf)
#ifdef HAVE_SCTP
/* For SCTP, we often need to return {IP, Port} tuples: */
-static int inet_get_address
- (int family, char* dst, inet_address* src, unsigned int* len);
+static int inet_get_address(char* dst, inet_address* src, unsigned int* len);
#define LOAD_IP_AND_PORT_CNT \
(8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT)
@@ -1433,8 +1465,7 @@ static int load_ip_and_port
unsigned int len = sizeof(struct sockaddr_storage);
unsigned int alen = len;
char abuf [len];
- int res =
- inet_get_address(desc->sfamily, abuf, (inet_address*) addr, &alen);
+ int res = inet_get_address(abuf, (inet_address*) addr, &alen);
ASSERT(res==0);
res = 0;
/* Now "abuf" contains: Family(1b), Port(2b), IP(4|16b) */
@@ -3658,9 +3689,27 @@ static int inet_init()
/* Check the size of SCTP AssocID -- currently both this driver and the
Erlang part require 32 bit: */
ASSERT(sizeof(sctp_assoc_t)==ASSOC_ID_LEN);
-# if defined(HAVE_SCTP_BINDX) && defined (HAVE_SCTP_PEELOFF)
+# if defined(HAVE_SCTP_BINDX)
p_sctp_bindx = sctp_bindx;
+# if defined(HAVE_SCTP_PEELOFF)
p_sctp_peeloff = sctp_peeloff;
+# else
+ p_sctp_peeloff = NULL;
+# endif
+# if defined(HAVE_SCTP_GETLADDRS) && defined(HAVE_SCTP_FREELADDRS)
+ p_sctp_getladdrs = sctp_getladdrs;
+ p_sctp_freeladdrs = sctp_freeladdrs;
+# else
+ p_sctp_getladdrs = NULL;
+ p_sctp_freeladdrs = NULL;
+# endif
+# if defined(HAVE_SCTP_GETPADDRS) && defined(HAVE_SCTP_FREEPADDRS)
+ p_sctp_getpaddrs = sctp_getpaddrs;
+ p_sctp_freepaddrs = sctp_freepaddrs;
+# else
+ p_sctp_getpaddrs = NULL;
+ p_sctp_freepaddrs = NULL;
+# endif
inet_init_sctp();
add_driver_entry(&sctp_inet_driver_entry);
# else
@@ -3675,12 +3724,36 @@ static int inet_init()
void *ptr;
if (erts_sys_ddll_sym(h_libsctp, "sctp_bindx", &ptr) == 0) {
p_sctp_bindx = ptr;
- inet_init_sctp();
- add_driver_entry(&sctp_inet_driver_entry);
if (erts_sys_ddll_sym(h_libsctp, "sctp_peeloff", &ptr) == 0) {
p_sctp_peeloff = ptr;
}
+ else p_sctp_peeloff = NULL;
+ if (erts_sys_ddll_sym(h_libsctp, "sctp_getladdrs", &ptr) == 0) {
+ p_sctp_getladdrs = ptr;
+ }
+ else p_sctp_getladdrs = NULL;
+ if (erts_sys_ddll_sym(h_libsctp, "sctp_freeladdrs", &ptr) == 0) {
+ p_sctp_freeladdrs = ptr;
+ }
+ else {
+ p_sctp_freeladdrs = NULL;
+ p_sctp_getladdrs = NULL;
+ }
+ if (erts_sys_ddll_sym(h_libsctp, "sctp_getpaddrs", &ptr) == 0) {
+ p_sctp_getpaddrs = ptr;
+ }
+ else p_sctp_getpaddrs = NULL;
+ if (erts_sys_ddll_sym(h_libsctp, "sctp_freepaddrs", &ptr) == 0) {
+ p_sctp_freepaddrs = ptr;
+ }
+ else {
+ p_sctp_freepaddrs = NULL;
+ p_sctp_getpaddrs = NULL;
+ }
+ inet_init_sctp();
+ add_driver_entry(&sctp_inet_driver_entry);
}
+ else p_sctp_bindx = NULL;
}
}
# endif
@@ -3835,10 +3908,12 @@ static char *inet_set_faddress(int family, inet_address* dst,
** and *len is the length of dst on return
** (suitable to deliver to erlang)
*/
-static int inet_get_address(int family, char* dst, inet_address* src, unsigned int* len)
+static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
{
+ int family;
short port;
+ family = src->sa.sa_family;
if ((family == AF_INET) && (*len >= sizeof(struct sockaddr_in))) {
dst[0] = INET_AF_INET;
port = sock_ntohs(src->sai.sin_port);
@@ -3860,6 +3935,75 @@ static int inet_get_address(int family, char* dst, inet_address* src, unsigned i
return -1;
}
+/* Same as the above, but take family from the address structure,
+** and advance the address pointer to the next address
+** according to the size of the current,
+** and return the resulting encoded size
+*/
+static int inet_address_to_erlang(char *dst, inet_address **src) {
+ short port;
+
+ switch ((*src)->sa.sa_family) {
+ case AF_INET:
+ if (dst) {
+ dst[0] = INET_AF_INET;
+ port = sock_ntohs((*src)->sai.sin_port);
+ put_int16(port, dst+1);
+ sys_memcpy(dst+1+2, (char *) &(*src)->sai.sin_addr, 4);
+ }
+ (*src) = (inet_address *) (&(*src)->sai + 1);
+ return 1 + 2 + 4;
+#if defined(HAVE_IN6) && defined(AF_INET6)
+ case AF_INET6:
+ if (dst) {
+ dst[0] = INET_AF_INET6;
+ port = sock_ntohs((*src)->sai6.sin6_port);
+ put_int16(port, dst+1);
+ VALGRIND_MAKE_MEM_DEFINED(&(*src)->sai6.sin6_addr,16); /* false undefs from syscall sctp_get[lp]addrs */
+ sys_memcpy(dst+1+2, (char *) &(*src)->sai6.sin6_addr, 16);
+ }
+ (*src) = (inet_address *) (&(*src)->sai6 + 1);
+ return 1 + 2 + 16;
+#endif
+ default:
+ return -1;
+ }
+}
+
+/* Encode n encoded addresses from addrs in the result buffer
+*/
+static ErlDrvSizeT reply_inet_addrs
+(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) {
+ inet_address *ia;
+ int i, s;
+ ErlDrvSizeT rlen;
+
+ if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize);
+ if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize);
+
+ /* Calculate result length */
+ rlen = 1;
+ ia = addrs;
+ for (i = 0; i < n; i++) {
+ s = inet_address_to_erlang(NULL, &ia);
+ if (s < 0) break;
+ rlen += s;
+ }
+
+ if (rlen > rsize) (*rbuf) = ALLOC(rlen);
+
+ (*rbuf)[0] = INET_REP_OK;
+ rlen = 1;
+ ia = addrs;
+ for (i = 0; i < n; i++) {
+ s = inet_address_to_erlang((*rbuf)+rlen, &ia);
+ if (s < 0) break;
+ rlen += s;
+ }
+
+ return rlen;
+}
+
static void desc_close(inet_descriptor* desc)
{
if (desc->s != INVALID_SOCKET) {
@@ -7879,6 +8023,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
return ctl_reply(INET_REP_OK, tbuf, strlen(tbuf), rbuf, rsize);
}
+ case INET_REQ_GETPADDRS: {
+ DEBUGF(("inet_ctl(%ld): INET_GETPADDRS\r\n", (long)desc->port));
+
+ if (len != 4) return ctl_error(EINVAL, rbuf, rsize);
+
+ if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize);
+ if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize);
+
+#ifdef HAVE_SCTP
+ if (IS_SCTP(desc) && p_sctp_getpaddrs) {
+ struct sockaddr *sa;
+ Uint32 assoc_id;
+ int n;
+ ErlDrvSizeT rlen;
+
+ assoc_id = get_int32(buf);
+ n = p_sctp_getpaddrs(desc->s, assoc_id, &sa);
+ rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize);
+ if (n > 0) p_sctp_freepaddrs(sa);
+ return rlen;
+ }
+#endif
+ { /* Fallback to sock_peer */
+ inet_address addr;
+ unsigned int sz;
+ int i;
+
+ sz = sizeof(addr);
+ i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz);
+ return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize);
+ }
+ }
+
case INET_REQ_PEER: { /* get peername */
char tbuf[sizeof(inet_address)];
inet_address peer;
@@ -7894,7 +8071,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz)))
return ctl_error(sock_errno(), rbuf, rsize);
}
- if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0)
+ if (inet_get_address(tbuf, ptr, &sz) < 0)
return ctl_error(EINVAL, rbuf, rsize);
return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize);
}
@@ -7915,6 +8092,39 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
}
}
+ case INET_REQ_GETLADDRS: {
+ DEBUGF(("inet_ctl(%ld): INET_GETLADDRS\r\n", (long)desc->port));
+
+ if (len != 4) return ctl_error(EINVAL, rbuf, rsize);
+
+ if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize);
+ if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize);
+
+#ifdef HAVE_SCTP
+ if (IS_SCTP(desc) && p_sctp_getladdrs) {
+ struct sockaddr *sa;
+ Uint32 assoc_id;
+ int n;
+ ErlDrvSizeT rlen;
+
+ assoc_id = get_int32(buf);
+ n = p_sctp_getladdrs(desc->s, assoc_id, &sa);
+ rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize);
+ if (n > 0) p_sctp_freeladdrs(sa);
+ return rlen;
+ }
+#endif
+ { /* Fallback to sock_name */
+ inet_address addr;
+ unsigned int sz;
+ int i;
+
+ sz = sizeof(addr);
+ i = sock_name(desc->s, (struct sockaddr *) &addr, &sz);
+ return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize);
+ }
+ }
+
case INET_REQ_NAME: { /* get sockname */
char tbuf[sizeof(inet_address)];
inet_address name;
@@ -7931,7 +8141,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz)))
return ctl_error(sock_errno(), rbuf, rsize);
}
- if (inet_get_address(desc->sfamily, tbuf, ptr, &sz) < 0)
+ if (inet_get_address(tbuf, ptr, &sz) < 0)
return ctl_error(EINVAL, rbuf, rsize);
return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize);
}
@@ -10770,7 +10980,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event)
inet_input_count(desc, n);
udesc->i_ptr += n;
- inet_get_address(desc->sfamily, abuf, &other, &len);
+ inet_get_address(abuf, &other, &len);
/* Copy formatted address to the buffer allocated; "len" is the
actual length which must be <= than the original reserved.
This means that the addr + data in the buffer are contiguous,
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index b0bef84e68..3fd0b9e106 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index fa621681f3..aa700c5194 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -41,8 +41,8 @@
getifaddrs/1, getiflist/1, ifget/3, ifset/3,
gethostname/1]).
-export([getservbyname/3, getservbyport/3]).
--export([peername/1, setpeername/2]).
--export([sockname/1, setsockname/2]).
+-export([peername/1, setpeername/2, peernames/1, peernames/2]).
+-export([sockname/1, setsockname/2, socknames/1, socknames/2]).
-export([attach/1, detach/1]).
-include("inet_sctp.hrl").
@@ -576,6 +576,36 @@ setpeername(S, undefined) when is_port(S) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
+%% PEERNAMES(insock()) -> {ok, [{IP, Port}, ...]} | {error, Reason}
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+peernames(S) when is_port(S) ->
+ peernames(S, undefined).
+
+peernames(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
+ peernames(S, AssocId);
+peernames(S, AssocId)
+ when is_port(S), is_integer(AssocId);
+ is_port(S), AssocId =:= undefined ->
+ Q = get,
+ Type = [[sctp_assoc_id,0]],
+ case type_value(Q, Type, AssocId) of
+ true ->
+ case ctl_cmd
+ (S, ?INET_REQ_GETPADDRS,
+ enc_value(Q, Type, AssocId)) of
+ {ok,Addrs} ->
+ {ok,get_addrs(Addrs)};
+ Error ->
+ Error
+ end;
+ false ->
+ {error,einval}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
%% SOCKNAME(insock()) -> {ok, {IP, Port}} | {error, Reason}
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -601,6 +631,36 @@ setsockname(S, undefined) when is_port(S) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
+%% SOCKNAMES(insock()) -> {ok, [{IP, Port}, ...]} | {error, Reason}
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+socknames(S) when is_port(S) ->
+ socknames(S, undefined).
+
+socknames(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
+ socknames(S, AssocId);
+socknames(S, AssocId)
+ when is_port(S), is_integer(AssocId);
+ is_port(S), AssocId =:= undefined ->
+ Q = get,
+ Type = [[sctp_assoc_id,0]],
+ case type_value(Q, Type, AssocId) of
+ true ->
+ case ctl_cmd
+ (S, ?INET_REQ_GETLADDRS,
+ enc_value(Q, Type, AssocId)) of
+ {ok,Addrs} ->
+ {ok,get_addrs(Addrs)};
+ Error ->
+ Error
+ end;
+ false ->
+ {error,einval}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
%% SETOPT(insock(), Opt, Value) -> ok | {error, Reason}
%% SETOPTS(insock(), [{Opt,Value}]) -> ok | {error, Reason}
%%
@@ -2213,6 +2273,12 @@ ip6_to_bytes({A,B,C,D,E,F,G,H}) ->
[?int16(A), ?int16(B), ?int16(C), ?int16(D),
?int16(E), ?int16(F), ?int16(G), ?int16(H)].
+get_addrs([]) ->
+ [];
+get_addrs([F,P1,P0|Addr]) ->
+ {IP,Addrs} = get_ip(F, Addr),
+ [{IP,?u16(P1, P0)}|get_addrs(Addrs)].
+
get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr);
get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr).
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index fd62f778a2..bc4c68230e 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -430,8 +430,56 @@ fe80::204:acff:fe17:bf38
<name name="peername" arity="1"/>
<fsummary>Return the address and port for the other end of a connection</fsummary>
<desc>
- <p>Returns the address and port for the other end of a
- connection.</p>
+ <p>
+ Returns the address and port for the other end of a
+ connection.
+ </p>
+ <p>
+ Note that for SCTP sockets this function only returns
+ one of the socket's peer addresses. The function
+ <seealso marker="#peernames/1">peernames/1,2</seealso>
+ returns all.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="peernames" arity="1"/>
+ <fsummary>
+ Return all address/port numbers for the other end of a connection
+ </fsummary>
+ <desc>
+ <p>
+ Equivalent to
+ <seealso marker="#peernames/2"><c>peernames(<anno>Socket</anno>, 0)</c></seealso>.
+ Note that this function's behaviour for an SCTP
+ one-to-many style socket is not defined by the
+ <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="peernames" arity="2"/>
+ <fsummary>
+ Return all address/port numbers for the other end of a connection
+ </fsummary>
+ <desc>
+ <p>
+ Returns a list of all address/port number pairs for the other end
+ of a socket's association <c><anno>Assoc</anno></c>.
+ </p>
+ <p>
+ This function can return multiple addresses for multihomed
+ sockets such as SCTP sockets. For other sockets it
+ returns a one element list.
+ </p>
+ <p>
+ Note that the <c><anno>Assoc</anno></c> parameter is by the
+ <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>
+ defined to be ignored for
+ one-to-one style sockets. What the special value <c>0</c>
+ means hence its behaviour for one-to-many style sockets
+ is unfortunately not defined.
+ </p>
</desc>
</func>
<func>
@@ -446,6 +494,46 @@ fe80::204:acff:fe17:bf38
<fsummary>Return the local address and port number for a socket</fsummary>
<desc>
<p>Returns the local address and port number for a socket.</p>
+ <p>
+ Note that for SCTP sockets this function only returns
+ one of the socket addresses. The function
+ <seealso marker="#socknames/1">socknames/1,2</seealso>
+ returns all.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="socknames" arity="1"/>
+ <fsummary>Return all local address/port numbers for a socket</fsummary>
+ <desc>
+ <p>
+ Equivalent to
+ <seealso marker="#socknames/2"><c>socknames(<anno>Socket</anno>, 0)</c></seealso>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="socknames" arity="2"/>
+ <fsummary>Return all local address/port numbers for a socket</fsummary>
+ <desc>
+ <p>
+ Returns a list of all local address/port number pairs for a socket
+ for the given association <c><anno>Assoc</anno></c>.
+ </p>
+ <p>
+ This function can return multiple addresses for multihomed
+ sockets such as SCTP sockets. For other sockets it
+ returns a one element list.
+ </p>
+ <p>
+ Note that the <c><anno>Assoc</anno></c> parameter is by the
+ <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>
+ defined to be ignored for one-to-one style sockets.
+ For one-to-many style sockets the special value <c>0</c>
+ is defined to mean that the returned addresses shall be
+ without regard to any particular association.
+ How different SCTP implementations interprets this varies somewhat.
+ </p>
</desc>
</func>
<func>
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index d4c78505da..b1c9d56c2d 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -24,6 +24,7 @@
%% socket
-export([peername/1, sockname/1, port/1, send/2,
+ peernames/1, peernames/2, socknames/1, socknames/2,
setopts/2, getopts/2,
getifaddrs/0, getifaddrs/1,
getif/1, getif/0, getiflist/0, getiflist/1,
@@ -157,6 +158,7 @@ close(Socket) ->
ok
end.
+
-spec peername(Socket) -> {ok, {Address, Port}} | {error, posix()} when
Socket :: socket(),
Address :: ip_address(),
@@ -173,6 +175,24 @@ setpeername(Socket, {IP,Port}) ->
setpeername(Socket, undefined) ->
prim_inet:setpeername(Socket, undefined).
+-spec peernames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+peernames(Socket) ->
+ prim_inet:peernames(Socket).
+
+-spec peernames(Socket, Assoc) ->
+ {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Assoc :: #sctp_assoc_change{} | gen_sctp:assoc_id(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+peernames(Socket, Assoc) ->
+ prim_inet:peernames(Socket, Assoc).
+
-spec sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} when
Socket :: socket(),
@@ -190,6 +210,25 @@ setsockname(Socket, {IP,Port}) ->
setsockname(Socket, undefined) ->
prim_inet:setsockname(Socket, undefined).
+-spec socknames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+socknames(Socket) ->
+ prim_inet:socknames(Socket).
+
+-spec socknames(Socket, Assoc) ->
+ {ok, [{Address, Port}]} | {error, posix()} when
+ Socket :: socket(),
+ Assoc :: #sctp_assoc_change{} | gen_sctp:assoc_id(),
+ Address :: ip_address(),
+ Port :: non_neg_integer().
+
+socknames(Socket, Assoc) ->
+ prim_inet:socknames(Socket, Assoc).
+
+
-spec port(Socket) -> {'ok', Port} | {'error', any()} when
Socket :: socket(),
Port :: port_number().
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 18a4a61b2f..641a8dc0ca 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -86,6 +86,8 @@
-define(INET_REQ_ACCEPT, 26).
-define(INET_REQ_LISTEN, 27).
-define(INET_REQ_IGNOREFD, 28).
+-define(INET_REQ_GETLADDRS, 29).
+-define(INET_REQ_GETPADDRS, 30).
%% TCP requests
%%-define(TCP_REQ_ACCEPT, 40). MOVED
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index e89cb44797..d2de96b269 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -1,4 +1,4 @@
-%%
+%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-2013. All Rights Reserved.
@@ -36,7 +36,9 @@
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]).
+ peeloff_active_true/1, buffers/1,
+ names_unihoming_ipv4/1, names_unihoming_ipv6/1,
+ names_multihoming_ipv4/1, names_multihoming_ipv6/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -48,7 +50,9 @@ all() ->
open_multihoming_ipv6_socket,
open_multihoming_ipv4_and_ipv6_socket,
basic_stream, xfer_stream_min, peeloff_active_once,
- peeloff_active_true, buffers].
+ peeloff_active_true, buffers,
+ names_unihoming_ipv4, names_unihoming_ipv6,
+ names_multihoming_ipv4, names_multihoming_ipv6].
groups() ->
[].
@@ -107,7 +111,7 @@ xfer_min(Config) when is_list(Config) ->
?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
?line {ok,Pb} = inet:port(Sb),
?line ok = gen_sctp:listen(Sb, true),
-
+
?line {ok,Sa} = gen_sctp:open(),
?line {ok,Pa} = inet:port(Sa),
?line {ok,#sctp_assoc_change{state=comm_up,
@@ -118,18 +122,18 @@ xfer_min(Config) when is_list(Config) ->
gen_sctp:connect(Sa, Loopback, Pb, []),
?line {SbAssocId,SaOutboundStreams,SaInboundStreams} =
case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
- {Loopback,Pa,
- #sctp_assoc_change{state=comm_up,
- error=0,
- outbound_streams=SbOutboundStreams,
- inbound_streams=SbInboundStreams,
- assoc_id=AssocId}} ->
- {AssocId,SbInboundStreams,SbOutboundStreams};
- {Loopback,Pa,
- #sctp_paddr_change{state=addr_confirmed,
- addr={Loopback,Pa},
- error=0,
- assoc_id=AssocId}} ->
+ {Loopback,Pa,
+ #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SbOutboundStreams,
+ inbound_streams=SbInboundStreams,
+ assoc_id=AssocId}} ->
+ {AssocId,SbInboundStreams,SbOutboundStreams};
+ {Loopback,Pa,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={Loopback,Pa},
+ error=0,
+ assoc_id=AssocId}} ->
{Loopback,Pa,
#sctp_assoc_change{state=comm_up,
error=0,
@@ -148,17 +152,20 @@ xfer_min(Config) when is_list(Config) ->
assoc_id=SbAssocId}],
Data} -> ok;
Event1 ->
- {Loopback,Pa,
- #sctp_paddr_change{addr = {Loopback,_},
- state = addr_available,
- error = 0,
- assoc_id = SbAssocId}} =
- recv_event(Event1),
- {ok,{Loopback,
- Pa,
- [#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} = gen_sctp:recv(Sb, infinity)
+ case recv_event(Event1) of
+ {Loopback,Pa,
+ #sctp_paddr_change{addr = {Loopback,_},
+ state = State,
+ error = 0,
+ assoc_id = SbAssocId}}
+ when State =:= addr_available;
+ State =:= addr_confirmed ->
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} = log_ok(gen_sctp:recv(Sb, infinity))
+ end
end,
?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
?line case log_ok(gen_sctp:recv(Sa, infinity)) of
@@ -197,7 +204,7 @@ xfer_min(Config) when is_list(Config) ->
recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
-
+
?line receive
Msg -> test_server:fail({received,Msg})
after 17 -> ok
@@ -216,7 +223,7 @@ xfer_active(Config) when is_list(Config) ->
?line {ok,Sb} = gen_sctp:open([{active,true}]),
?line {ok,Pb} = inet:port(Sb),
?line ok = gen_sctp:listen(Sb, true),
-
+
?line {ok,Sa} = gen_sctp:open([{active,true}]),
?line {ok,Pa} = inet:port(Sa),
?line ok = gen_sctp:connect_init(Sa, Loopback, Pb, []),
@@ -348,7 +355,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
%%
?line S1 =
log_ok(gen_sctp:open(
- 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
+ 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
?LOGVAR(S1),
?line P1 =
log_ok(inet:port(S1)),
@@ -455,18 +462,22 @@ def_sndrcvinfo(Config) when is_list(Config) ->
stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
<<"3: ",Data/binary>>} -> ok;
Event2 ->
- {Loopback,P2,
- #sctp_paddr_change{
- addr={Loopback,_}, state=addr_available,
- error=0, assoc_id=S1AssocId}} =
- recv_event(Event2),
- ?line case log_ok(gen_sctp:recv(S1)) of
- {Loopback,P2,
- [#sctp_sndrcvinfo{
- stream=1, ppid=0, context=0,
- assoc_id=S1AssocId}],
- <<"3: ",Data/binary>>} -> ok
- end
+ case recv_event(Event2) of
+ {Loopback,P2,
+ #sctp_paddr_change{
+ addr={Loopback,_},
+ state=State,
+ error=0, assoc_id=S1AssocId}}
+ when State =:= addr_available;
+ State =:= addr_confirmed ->
+ ?line case log_ok(gen_sctp:recv(S1)) of
+ {Loopback,P2,
+ [#sctp_sndrcvinfo{
+ stream=1, ppid=0, context=0,
+ assoc_id=S1AssocId}],
+ <<"3: ",Data/binary>>} -> ok
+ end
+ end
end,
?line ok =
do_from_other_process(
@@ -509,6 +520,13 @@ log_ok(X) -> log(ok(X)).
ok({ok,X}) -> X.
+err([], Result) ->
+ erlang:error(Result);
+err([Reason|_], {error,Reason}) ->
+ ok;
+err([_|Reasons], Result) ->
+ err(Reasons, Result).
+
log(X) ->
io:format("LOG[~w]: ~p~n", [self(),X]),
X.
@@ -529,57 +547,57 @@ api_open_close(Config) when is_list(Config) ->
?line {ok,S1} = gen_sctp:open(0),
?line {ok,P} = inet:port(S1),
?line ok = gen_sctp:close(S1),
-
+
?line {ok,S2} = gen_sctp:open(P),
?line {ok,P} = inet:port(S2),
?line ok = gen_sctp:close(S2),
-
+
?line {ok,S3} = gen_sctp:open([{port,P}]),
?line {ok,P} = inet:port(S3),
?line ok = gen_sctp:close(S3),
-
+
?line {ok,S4} = gen_sctp:open(P, []),
?line {ok,P} = inet:port(S4),
?line ok = gen_sctp:close(S4),
-
+
?line {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]),
?line {ok,P} = inet:port(S5),
?line ok = gen_sctp:close(S5),
?line ok = gen_sctp:close(S5),
-
+
?line try gen_sctp:close(0)
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open({})
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(-1)
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(65536)
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(make_ref(), [])
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(0, {})
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(0, [make_ref()])
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open([{invalid_option,0}])
catch error:badarg -> ok
end,
-
+
?line try gen_sctp:open(0, [{mode,invalid_mode}])
catch error:badarg -> ok
end,
@@ -591,11 +609,11 @@ api_listen(suite) ->
[];
api_listen(Config) when is_list(Config) ->
?line Localhost = {127,0,0,1},
-
+
?line try gen_sctp:listen(0, true)
catch error:badarg -> ok
end,
-
+
?line {ok,S} = gen_sctp:open(),
?line {ok,Pb} = inet:port(S),
?line try gen_sctp:listen(S, not_allowed_for_listen)
@@ -603,7 +621,7 @@ api_listen(Config) when is_list(Config) ->
end,
?line ok = gen_sctp:close(S),
?line {error,closed} = gen_sctp:listen(S, true),
-
+
?line {ok,Sb} = gen_sctp:open(Pb),
?line {ok,Sa} = gen_sctp:open(),
?line case gen_sctp:connect(Sa, localhost, Pb, []) of
@@ -615,8 +633,8 @@ api_listen(Config) when is_list(Config) ->
gen_sctp:recv(Sa, infinity);
{error,#sctp_assoc_change{state=cant_assoc}} ->
ok%;
- %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
- %% ok
+ %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
+ %% ok
end,
?line ok = gen_sctp:listen(Sb, true),
?line {ok,#sctp_assoc_change{state=comm_up,
@@ -840,23 +858,36 @@ xfer_stream_min(Config) when is_list(Config) ->
?line SbOutboundStreams = SaInboundStreams,
?line ?LOGVAR(SbOutboundStreams),
?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
- ?line case gen_sctp:recv(Sb, infinity) of
- {ok,{Loopback,
+ ?line case log_ok(gen_sctp:recv(Sb, infinity)) of
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} -> ok;
+ {Loopback,
+ Pa,[],
+ #sctp_paddr_change{addr = {Loopback,_},
+ state = addr_available,
+ error = 0,
+ assoc_id = SbAssocId}} ->
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} = log_ok(gen_sctp:recv(Sb, infinity));
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ #sctp_paddr_change{addr = {Loopback,_},
+ state = addr_confirmed,
+ error = 0,
+ assoc_id = SbAssocId}} ->
+ {Loopback,
Pa,
[#sctp_sndrcvinfo{stream=Stream,
assoc_id=SbAssocId}],
- Data}} -> ok;
- {ok,{Loopback,
- Pa,[],
- #sctp_paddr_change{addr = {Loopback,_},
- state = addr_available,
- error = 0,
- assoc_id = SbAssocId}}} ->
- {ok,{Loopback,
- Pa,
- [#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} = gen_sctp:recv(Sb, infinity)
+ Data} = log_ok(gen_sctp:recv(Sb, infinity))
end,
?line ok =
do_from_other_process(
@@ -972,10 +1003,10 @@ peeloff(Config, SockOpts) when is_list(Config) ->
?line ?LOGVAR(S2Ai),
?line S1Ai =
receive
- {S1,{Addr,P2,
- #sctp_assoc_change{
- state=comm_up,
- assoc_id=AssocId1}}} -> AssocId1
+ {S1,{Addr,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId1}}} -> AssocId1
after Timeout ->
socket_bailout([S1,S2])
end,
@@ -1003,8 +1034,8 @@ peeloff(Config, SockOpts) when is_list(Config) ->
?line P3 = case P3_X of 0 -> P1; _ -> P3_X end,
?line [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] =
socket_call(S3,
- {getopts,[{sctp_get_peer_addr_info,
- #sctp_paddrinfo{address={Addr,P2}}}]}),
+ {getopts,[{sctp_get_peer_addr_info,
+ #sctp_paddrinfo{address={Addr,P2}}}]}),
%%?line S3Ai = S1Ai,
?line ?LOGVAR(S3Ai),
%%
@@ -1087,9 +1118,9 @@ buffers(Config) when is_list(Config) ->
%%
?line socket_call(S1, {setopts,[{recbuf,Limit}]}),
?line Recbuf =
- case socket_call(S1, {getopts,[recbuf]}) of
- [{recbuf,RB1}] when RB1 >= Limit -> RB1
- end,
+ case socket_call(S1, {getopts,[recbuf]}) of
+ [{recbuf,RB1}] when RB1 >= Limit -> RB1
+ end,
?line Data = mk_data(Recbuf+Limit),
?line socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}),
?line socket_call(S2, {send,S2Ai,Stream,Data}),
@@ -1190,6 +1221,93 @@ open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) ->
{skip, Reason}
end.
+names_unihoming_ipv4(doc) ->
+ "Test inet:socknames/peernames on unihoming IPv4 sockets";
+names_unihoming_ipv4(suite) ->
+ [];
+names_unihoming_ipv4(Config) when is_list(Config) ->
+ ?line do_names(Config, inet, 1).
+
+names_unihoming_ipv6(doc) ->
+ "Test inet:socknames/peernames on unihoming IPv6 sockets";
+names_unihoming_ipv6(suite) ->
+ [];
+names_unihoming_ipv6(Config) when is_list(Config) ->
+ ?line do_names(Config, inet6, 1).
+
+names_multihoming_ipv4(doc) ->
+ "Test inet:socknames/peernames on multihoming IPv4 sockets";
+names_multihoming_ipv4(suite) ->
+ [];
+names_multihoming_ipv4(Config) when is_list(Config) ->
+ ?line do_names(Config, inet, 2).
+
+names_multihoming_ipv6(doc) ->
+ "Test inet:socknames/peernames on multihoming IPv6 sockets";
+names_multihoming_ipv6(suite) ->
+ [];
+names_multihoming_ipv6(Config) when is_list(Config) ->
+ ?line do_names(Config, inet6, 2).
+
+
+
+do_names(_, FamilySpec, AddressCount) ->
+ Fun =
+ fun (ServerSocket, _, ServerAssoc, ClientSocket, _, ClientAssoc) ->
+ ?line ServerSocknamesNoassoc =
+ lists:sort(ok(inet:socknames(ServerSocket))),
+ ?line ?LOGVAR(ServerSocknamesNoassoc),
+ ?line ServerSocknames =
+ lists:sort(ok(inet:socknames(ServerSocket, ServerAssoc))),
+ ?line ?LOGVAR(ServerSocknames),
+ ?line [_|_] =
+ ordsets:intersection
+ (ServerSocknamesNoassoc, ServerSocknames),
+ ?line ClientSocknamesNoassoc =
+ lists:sort(ok(inet:socknames(ClientSocket))),
+ ?line ?LOGVAR(ClientSocknamesNoassoc),
+ ?line ClientSocknames =
+ lists:sort(ok(inet:socknames(ClientSocket, ClientAssoc))),
+ ?line ?LOGVAR(ClientSocknames),
+ ?line [_|_] =
+ ordsets:intersection
+ (ClientSocknamesNoassoc, ClientSocknames),
+ ?line err([einval,enotconn], inet:peernames(ServerSocket)),
+ ?line ServerPeernames =
+ lists:sort(ok(inet:peernames(ServerSocket, ServerAssoc))),
+ ?line ?LOGVAR(ServerPeernames),
+ ?line err([einval,enotconn], inet:peernames(ClientSocket)),
+ ?line ClientPeernames =
+ lists:sort(ok(inet:peernames(ClientSocket, ClientAssoc))),
+ ?line ?LOGVAR(ClientPeernames),
+ ?line ServerSocknames = ClientPeernames,
+ ?line ClientSocknames = ServerPeernames,
+ ?line {ok,Socket} =
+ gen_sctp:peeloff(ServerSocket, ServerAssoc),
+ ?line SocknamesNoassoc =
+ lists:sort(ok(inet:socknames(Socket))),
+ ?line ?LOGVAR(SocknamesNoassoc),
+ ?line Socknames =
+ lists:sort(ok(inet:socknames(Socket, ServerAssoc))),
+ ?line ?LOGVAR(Socknames),
+ ?line true =
+ ordsets:is_subset(SocknamesNoassoc, Socknames),
+ ?line Peernames =
+ lists:sort(ok(inet:peernames(Socket, ServerAssoc))),
+ ?line ?LOGVAR(Peernames),
+ ?line ok = gen_sctp:close(Socket),
+ ?line Socknames = ClientPeernames,
+ ?line ClientSocknames = Peernames,
+ ok
+ end,
+ ?line case get_addrs_by_family(FamilySpec, AddressCount) of
+ {ok, Addresses} when length(Addresses) =:= AddressCount ->
+ ?line do_open_and_connect(Addresses, hd(Addresses), Fun);
+ {error, Reason} ->
+ {skip, Reason}
+ end.
+
+
get_addrs_by_family(Family, NumAddrs) ->
case os:type() of
@@ -1274,6 +1392,10 @@ f(F, A) ->
lists:flatten(io_lib:format(F, A)).
do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
+ ?line Fun = fun (_, _, _, _, _, _) -> ok end,
+ ?line do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun).
+%%
+do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) ->
?line ServerFamily = get_family_by_addrs(ServerAddresses),
?line io:format("Serving ~p addresses: ~p~n",
[ServerFamily, ServerAddresses]),
@@ -1284,14 +1406,26 @@ do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
?line ClientFamily = get_family_by_addr(AddressToConnectTo),
?line io:format("Connecting to ~p ~p~n",
[ClientFamily, AddressToConnectTo]),
- ?line S2 = ok(gen_sctp:open(0, [ClientFamily])),
+ ?line ClientOpts =
+ [ClientFamily |
+ case ClientFamily of
+ inet6 ->
+ [{ipv6_v6only,true}];
+ _ ->
+ []
+ end],
+ ?line S2 = ok(gen_sctp:open(0, ClientOpts)),
+ log(open),
%% Verify client can connect
- ?line #sctp_assoc_change{state=comm_up} =
+ ?line #sctp_assoc_change{state=comm_up} = S2Assoc =
ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])),
+ log(comm_up),
%% verify server side also receives comm_up from client
- ?line recv_comm_up_eventually(S1),
+ ?line S1Assoc = recv_comm_up_eventually(S1),
+ ?line Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc),
?line ok = gen_sctp:close(S2),
- ?line ok = gen_sctp:close(S1).
+ ?line ok = gen_sctp:close(S1),
+ Result.
%% If at least one of the addresses is an ipv6 address, return inet6, else inet.
get_family_by_addrs(Addresses) ->
@@ -1306,9 +1440,11 @@ get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6.
recv_comm_up_eventually(S) ->
?line case ok(gen_sctp:recv(S)) of
- {_Addr, _Port, _Info, #sctp_assoc_change{state=comm_up}} ->
- ok;
- {_Addr, _Port, _Info, _OtherSctpMsg} ->
+ {_Addr, _Port, _Info,
+ #sctp_assoc_change{state=comm_up} = Assoc} ->
+ Assoc;
+ {_Addr, _Port, _Info, _OtherSctpMsg} = Msg ->
+ ?line log({unexpected,Msg}),
?line recv_comm_up_eventually(S)
end.
@@ -1367,10 +1503,10 @@ socket_bailout([]) ->
socket_history({State,Flush}) ->
{lists:keysort(
- 2,
- lists:flatten(
- [[{Key,Val} || Val <- Vals]
- || {Key,Vals} <- gb_trees:to_list(State)])),
+ 2,
+ lists:flatten(
+ [[{Key,Val} || Val <- Vals]
+ || {Key,Vals} <- gb_trees:to_list(State)])),
Flush}.
s_handler(Socket) ->
@@ -1453,7 +1589,7 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
#sctp_assoc_change{
state=comm_up,
inbound_streams=Is}}}|_]
- when 0 =< Stream, Stream < Is-> ok;
+ when 0 =< Stream, Stream < Is-> ok;
[] -> ok
end,
Key = {msg,AssocId,Stream},
@@ -1473,7 +1609,7 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
case {gb_get(Key, State),St} of
{[],_} -> ok;
{[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
- when St =:= comm_lost; St =:= shutdown_comp -> ok
+ when St =:= comm_lost; St =:= shutdown_comp -> ok
end,
NewState = gb_push(Key, Val, State),
Parent ! {self(),{Addr,Port,SAC}},
@@ -1489,8 +1625,9 @@ s_loop(Socket, Timeout, Parent, Handler, State) ->
[] -> ok
end,
case {gb_get({assoc_change,AssocId}, State),St} of
- {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],
- addr_available} -> ok;
+ {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_}
+ when St =:= addr_available;
+ St =:= addr_confirmed -> ok;
{[],addr_confirmed} -> ok
end,
Key = {paddr_change,AssocId},