summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMicael Karlberg <bmk@erlang.org>2019-11-27 10:38:26 +0100
committerMicael Karlberg <bmk@erlang.org>2019-11-27 10:38:26 +0100
commit7e2ee5d8efa9d75688d649441ad524466375c034 (patch)
tree023a3837023bde39c3c0047bbe16557780247b4b
parent28a90a039dc513a702fb2825076e8e4de1df4445 (diff)
parent171cf38c343e50ae7a4f28a6c1a1a58acad17407 (diff)
downloaderlang-7e2ee5d8efa9d75688d649441ad524466375c034.tar.gz
Merge branch 'bmk/erts/esock/20191125/add_getifaddrs_in_net/OTP-16212' into maint
-rw-r--r--erts/configure.in6
-rw-r--r--erts/doc/src/socket.xml6
-rw-r--r--erts/emulator/nifs/common/prim_net_nif.c747
-rw-r--r--erts/emulator/nifs/common/socket_int.h19
-rw-r--r--erts/emulator/nifs/common/socket_nif.c8
-rw-r--r--erts/emulator/nifs/common/socket_util.c280
-rw-r--r--erts/emulator/nifs/common/socket_util.h26
-rw-r--r--erts/emulator/test/Makefile6
-rw-r--r--erts/preloaded/ebin/prim_net.beambin4568 -> 5192 bytes
-rw-r--r--erts/preloaded/ebin/socket.beambin78564 -> 78736 bytes
-rw-r--r--erts/preloaded/src/prim_net.erl43
-rw-r--r--erts/preloaded/src/socket.erl13
-rw-r--r--lib/kernel/doc/src/net.xml45
-rw-r--r--lib/kernel/src/net.erl122
-rw-r--r--lib/kernel/test/Makefile1
-rw-r--r--lib/kernel/test/net_SUITE.erl (renamed from erts/emulator/test/net_SUITE.erl)0
16 files changed, 1258 insertions, 64 deletions
diff --git a/erts/configure.in b/erts/configure.in
index 4e88e1769a..a887f86621 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -1664,6 +1664,12 @@ dnl Check if we have timerfds to be used for high accuracy
dnl epoll_wait timeouts
AC_CHECK_HEADERS([sys/timerfd.h])
+dnl Check if we have the header file 'netpacket/packat.h' in which
+dnl type 'struct sockaddr_ll' is defined.
+AC_CHECK_HEADERS([netpacket/packet.h],
+ have_netpacket_packet_h=yes,
+ have_netpacket_packet_h=no)
+
dnl Check for kernel SCTP support
AC_SUBST(LIBSCTP)
if test "x$enable_sctp" != "xno" ; then
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index 889c35bf10..08a9889146 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -144,6 +144,12 @@
<name name="sockaddr_un"/>
</datatype>
<datatype>
+ <name name="sockaddr_ll"/>
+ </datatype>
+ <datatype>
+ <name name="packet_type"/>
+ </datatype>
+ <datatype>
<name name="port_number"/>
</datatype>
<datatype>
diff --git a/erts/emulator/nifs/common/prim_net_nif.c b/erts/emulator/nifs/common/prim_net_nif.c
index 5f7a2716f9..42bfd8b5e0 100644
--- a/erts/emulator/nifs/common/prim_net_nif.c
+++ b/erts/emulator/nifs/common/prim_net_nif.c
@@ -20,6 +20,9 @@
* ----------------------------------------------------------------------
* Purpose : The NIF (C) part of the net interface
* This is a module of miscellaneous functions.
+ *
+ * We (try to) avoid name clashes by prefixing "all" internal
+ * function names with enet.
* ----------------------------------------------------------------------
*
*/
@@ -161,6 +164,7 @@
#include "socket_dbg.h"
#include "socket_int.h"
+#include "socket_tarray.h"
#include "socket_util.h"
@@ -257,7 +261,8 @@ extern char* erl_errno_id(int error);
ENET_NIF_FUNC_DEF(command); \
ENET_NIF_FUNC_DEF(gethostname); \
ENET_NIF_FUNC_DEF(getnameinfo); \
- ENET_NIF_FUNC_DEF(getaddrinfo); \
+ ENET_NIF_FUNC_DEF(getaddrinfo); \
+ ENET_NIF_FUNC_DEF(getifaddrs); \
ENET_NIF_FUNC_DEF(if_name2index); \
ENET_NIF_FUNC_DEF(if_index2name); \
ENET_NIF_FUNC_DEF(if_names);
@@ -271,22 +276,24 @@ ENET_NIF_FUNCS
/* And here comes the functions that does the actual work (for the most part) */
-static ERL_NIF_TERM ncommand(ErlNifEnv* env,
- ERL_NIF_TERM cmd);
-static ERL_NIF_TERM ngethostname(ErlNifEnv* env);
-static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env,
- const ESockAddress* saP,
- SOCKLEN_T saLen,
- int flags);
-static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env,
- char* host,
- char* serv);
-static ERL_NIF_TERM nif_name2index(ErlNifEnv* env,
- char* ifn);
-static ERL_NIF_TERM nif_index2name(ErlNifEnv* env,
- unsigned int id);
-static ERL_NIF_TERM nif_names(ErlNifEnv* env);
-static unsigned int nif_names_length(struct if_nameindex* p);
+static ERL_NIF_TERM enet_command(ErlNifEnv* env,
+ ERL_NIF_TERM cmd);
+static ERL_NIF_TERM enet_gethostname(ErlNifEnv* env);
+static ERL_NIF_TERM enet_getnameinfo(ErlNifEnv* env,
+ const ESockAddress* saP,
+ SOCKLEN_T saLen,
+ int flags);
+static ERL_NIF_TERM enet_getaddrinfo(ErlNifEnv* env,
+ char* host,
+ char* serv);
+static ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env,
+ char* netns);
+static ERL_NIF_TERM enet_if_name2index(ErlNifEnv* env,
+ char* ifn);
+static ERL_NIF_TERM enet_if_index2name(ErlNifEnv* env,
+ unsigned int id);
+static ERL_NIF_TERM enet_if_names(ErlNifEnv* env);
+static unsigned int enet_if_names_length(struct if_nameindex* p);
/*
static void net_dtor(ErlNifEnv* env, void* obj);
@@ -300,6 +307,36 @@ static void net_down(ErlNifEnv* env,
const ErlNifMonitor* mon);
*/
+static ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env,
+ char* netns);
+static ERL_NIF_TERM enet_getifaddrs_process(ErlNifEnv* env,
+ struct ifaddrs* ifap);
+static unsigned int enet_getifaddrs_length(struct ifaddrs* ifap);
+static BOOLEAN_T encode_ifaddrs(ErlNifEnv* env,
+ struct ifaddrs* ifap,
+ ERL_NIF_TERM* eifa);
+static ERL_NIF_TERM encode_ifaddrs_name(ErlNifEnv* env,
+ char* name);
+static ERL_NIF_TERM encode_ifaddrs_flags(ErlNifEnv* env,
+ unsigned int flags);
+static ERL_NIF_TERM encode_ifaddrs_addr(ErlNifEnv* env,
+ struct sockaddr* sa);
+static char* make_ifaddrs(ErlNifEnv* env,
+ ERL_NIF_TERM name,
+ ERL_NIF_TERM flags,
+ ERL_NIF_TERM addr,
+ ERL_NIF_TERM netmask,
+ ERL_NIF_TERM ifu_key,
+ ERL_NIF_TERM ifu_value,
+ ERL_NIF_TERM data,
+ ERL_NIF_TERM* ifAddrs);
+#ifdef HAVE_SETNS
+static BOOLEAN_T enet_getifaddrs_netns(ErlNifEnv* env,
+ ERL_NIF_TERM map,
+ char** netns);
+static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err);
+static BOOLEAN_T restore_network_namespace(int ns, int* err);
+#endif
static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env,
const ERL_NIF_TERM eflags,
int* flags);
@@ -371,15 +408,32 @@ static const struct in6_addr in6addr_loopback =
#define LOCAL_ATOMS \
LOCAL_ATOM_DECL(address_info); \
+ LOCAL_ATOM_DECL(automedia); \
+ LOCAL_ATOM_DECL(broadaddr); \
+ LOCAL_ATOM_DECL(broadcast); \
LOCAL_ATOM_DECL(debug); \
+ LOCAL_ATOM_DECL(dstaddr); \
+ LOCAL_ATOM_DECL(dynamic); \
LOCAL_ATOM_DECL(host); \
LOCAL_ATOM_DECL(idn); \
+ LOCAL_ATOM_DECL(master); \
+ LOCAL_ATOM_DECL(multicast); \
+ LOCAL_ATOM_DECL(name); \
LOCAL_ATOM_DECL(namereqd); \
LOCAL_ATOM_DECL(name_info); \
+ LOCAL_ATOM_DECL(netmask); \
+ LOCAL_ATOM_DECL(noarp); \
LOCAL_ATOM_DECL(nofqdn); \
+ LOCAL_ATOM_DECL(notrailers); \
LOCAL_ATOM_DECL(numerichost); \
LOCAL_ATOM_DECL(numericserv); \
- LOCAL_ATOM_DECL(service);
+ LOCAL_ATOM_DECL(pointopoint); \
+ LOCAL_ATOM_DECL(portsel); \
+ LOCAL_ATOM_DECL(promisc); \
+ LOCAL_ATOM_DECL(running); \
+ LOCAL_ATOM_DECL(service); \
+ LOCAL_ATOM_DECL(slave); \
+ LOCAL_ATOM_DECL(up);
#define LOCAL_ERROR_REASON_ATOMS \
LOCAL_ATOM_DECL(eaddrfamily); \
@@ -427,6 +481,7 @@ static ErlNifResourceTypeInit netInit = {
* nif_gethostname/0
* nif_getnameinfo/2
* nif_getaddrinfo/3
+ * nif_getifaddrs/1
* nif_if_name2index/1
* nif_if_index2name/1
* nif_if_names/0
@@ -495,7 +550,7 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env,
NDBG( ("NET", "command -> ecmd: %T\r\n", ecmd) );
- result = ncommand(env, ecmd);
+ result = enet_command(env, ecmd);
NDBG( ("NET", "command -> result: %T\r\n", result) );
@@ -511,8 +566,8 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env,
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM ncommand(ErlNifEnv* env,
- ERL_NIF_TERM cmd)
+ERL_NIF_TERM enet_command(ErlNifEnv* env,
+ ERL_NIF_TERM cmd)
{
const ERL_NIF_TERM* t;
int tsz;
@@ -562,7 +617,7 @@ ERL_NIF_TERM nif_gethostname(ErlNifEnv* env,
if (argc != 0)
return enif_make_badarg(env);
- result = ngethostname(env);
+ result = enet_gethostname(env);
NDBG( ("NET", "nif_gethostname -> done when result: %T\r\n", result) );
@@ -573,7 +628,7 @@ ERL_NIF_TERM nif_gethostname(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM ngethostname(ErlNifEnv* env)
+ERL_NIF_TERM enet_gethostname(ErlNifEnv* env)
{
ERL_NIF_TERM result;
char buf[NET_MAXHOSTNAMELEN + 1];
@@ -581,7 +636,7 @@ ERL_NIF_TERM ngethostname(ErlNifEnv* env)
res = net_gethostname(buf, sizeof(buf));
- NDBG( ("NET", "ngethostname -> gethostname res: %d\r\n", res) );
+ NDBG( ("NET", "enet_gethostname -> gethostname res: %d\r\n", res) );
switch (res) {
case 0:
@@ -661,7 +716,7 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env,
if (!decode_nameinfo_flags(env, eFlags, &flags))
return enif_make_badarg(env);
- result = ngetnameinfo(env, &sa, saLen, flags);
+ result = enet_getnameinfo(env, &sa, saLen, flags);
NDBG( ("NET",
"nif_getnameinfo -> done when result: "
@@ -678,10 +733,10 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env,
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env,
- const ESockAddress* saP,
- SOCKLEN_T saLen,
- int flags)
+ERL_NIF_TERM enet_getnameinfo(ErlNifEnv* env,
+ const ESockAddress* saP,
+ SOCKLEN_T saLen,
+ int flags)
{
ERL_NIF_TERM result;
char host[HOSTNAME_LEN];
@@ -694,7 +749,7 @@ ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env,
serv, servLen,
flags);
- NDBG( ("NET", "ngetnameinfo -> res: %d\r\n", res) );
+ NDBG( ("NET", "enet_getnameinfo -> res: %d\r\n", res) );
switch (res) {
case 0:
@@ -821,14 +876,14 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env,
return enif_make_badarg(env);
/*
- if (decode_addrinfo_hints(env, eHints, &hints))
- return enif_make_badarg(env);
+ if (decode_addrinfo_hints(env, eHints, &hints))
+ return enif_make_badarg(env);
*/
if ((hostName == NULL) && (servName == NULL))
return enif_make_badarg(env);
- result = ngetaddrinfo(env, hostName, servName);
+ result = enet_getaddrinfo(env, hostName, servName);
if (hostName != NULL)
FREE(hostName);
@@ -837,8 +892,8 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env,
FREE(servName);
/*
- if (hints != NULL)
- FREE(hints);
+ if (hints != NULL)
+ FREE(hints);
*/
NDBG( ("NET",
@@ -852,15 +907,15 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env,
- char* host,
- char* serv)
+ERL_NIF_TERM enet_getaddrinfo(ErlNifEnv* env,
+ char* host,
+ char* serv)
{
ERL_NIF_TERM result;
struct addrinfo* addrInfoP;
int res;
- NDBG( ("NET", "ngetaddrinfo -> entry with"
+ NDBG( ("NET", "enet_getaddrinfo -> entry with"
"\r\n host: %s"
"\r\n serv: %s"
"\r\n",
@@ -869,7 +924,7 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env,
res = getaddrinfo(host, serv, NULL, &addrInfoP);
- NDBG( ("NET", "ngetaddrinfo -> res: %d\r\n", res) );
+ NDBG( ("NET", "enet_getaddrinfo -> res: %d\r\n", res) );
switch (res) {
case 0:
@@ -958,6 +1013,488 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env,
/* ----------------------------------------------------------------------
+ * nif_getifaddrs
+ *
+ * Description:
+ * Get interface addresses
+ *
+ * Arguments:
+ * Extra - A way to pass 'extra' arguments.
+ * Currently only used for netns (name space).
+ */
+
+static
+ERL_NIF_TERM nif_getifaddrs(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[])
+{
+#if defined(__WIN32__)
+ return enif_raise_exception(env, MKA(env, "notsup"));
+#elif defined(HAVE_GETIFADDRS)
+ ERL_NIF_TERM extra;
+ char* netns;
+ ERL_NIF_TERM result;
+
+ NDBG( ("NET", "nif_getifaddrs -> entry (%d)\r\n", argc) );
+
+ if ((argc != 1) ||
+ !IS_MAP(env, argv[0])) {
+ return enif_make_badarg(env);
+ }
+ extra = argv[0];
+
+#ifdef HAVE_SETNS
+ /* We *currently* only support one extra option: netns */
+ if (!enet_getifaddrs_netns(env, extra, &netns)) {
+ NDBG( ("NET", "nif_getifaddrs -> namespace: %s\r\n", netns) );
+ return enif_make_badarg(env);
+ }
+#else
+ netns = NULL;
+#endif
+
+ result = enet_getifaddrs(env, netns);
+
+ NDBG( ("NET",
+ "nif_getifaddrs -> done when result: "
+ "\r\n %T\r\n", result) );
+
+ return result;
+#else // HAVE_GETIFADDRS
+ return esock_make_error(env, esock_atom_notsup);
+#endif
+}
+
+
+#ifdef HAVE_GETIFADDRS
+#ifdef HAVE_SETNS
+/* enet_getifaddrs_netns - extract the netns field from the 'extra' map
+ *
+ * Note that the 'extra' map *may* contain other options, but here we
+ * only care about 'netns'.
+ */
+static
+BOOLEAN_T enet_getifaddrs_netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns)
+{
+ size_t sz;
+ ERL_NIF_TERM key;
+ ERL_NIF_TERM value;
+ unsigned int len;
+ char* buf;
+ int written;
+
+ /* Note that its acceptable that the extra map is empty */
+ if (!enif_get_map_size(env, map, &sz) ||
+ (sz != 1)) {
+ *netns = NULL;
+ return TRUE;
+ }
+
+ /* Regardless of the content of the 'extra' map, we only care about 'netns' */
+ key = enif_make_atom(env, "netns");
+ if (!GET_MAP_VAL(env, map, key, &value)) {
+ *netns = NULL;
+ return TRUE;
+ }
+
+ /* So far so good. The value should be a string, check. */
+ if (!enif_is_list(env, value)) {
+ *netns = NULL; // Just in case...
+ return FALSE;
+ }
+
+ if (!enif_get_list_length(env, value, &len)) {
+ *netns = NULL; // Just in case...
+ return FALSE;
+ }
+
+ if ((buf = MALLOC(len+1)) == NULL) {
+ *netns = NULL; // Just in case...
+ return FALSE;
+ }
+
+ written = enif_get_string(env, value, buf, len+1, ERL_NIF_LATIN1);
+ if (written == (len+1)) {
+ *netns = buf;
+ return TRUE;
+ } else {
+ *netns = NULL; // Just in case...
+ return FALSE;
+ }
+}
+#endif
+
+
+
+static
+ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env, char* netns)
+{
+ ERL_NIF_TERM result;
+ struct ifaddrs* ifap;
+ int save_errno;
+#ifdef HAVE_SETNS
+ int current_ns = 0;
+#endif
+
+ NDBG( ("NET", "enet_getifaddrs -> entry with"
+ "\r\n netns: %s"
+ "\r\n", ((netns == NULL) ? "NULL" : netns)) );
+
+#ifdef HAVE_SETNS
+ if ((netns != NULL) &&
+ !change_network_namespace(netns, &current_ns, &save_errno))
+ return esock_make_error_errno(env, save_errno);
+#endif
+
+ if (0 == getifaddrs(&ifap)) {
+ result = enet_getifaddrs_process(env, ifap);
+ freeifaddrs(ifap);
+ } else {
+ save_errno = get_errno();
+
+ NDBG( ("NET", "enet_getifaddrs -> failed get addrs: %d", save_errno) );
+
+ result = esock_make_error_errno(env, save_errno);
+ }
+
+
+#ifdef HAVE_SETNS
+ if ((netns != NULL) &&
+ !restore_network_namespace(current_ns, &save_errno))
+ return esock_make_error_errno(env, save_errno);
+
+ if (netns != NULL)
+ FREE(netns);
+#endif
+
+ NDBG( ("NET", "enet_getifaddrs -> done when"
+ "\r\n result: %T"
+ "\r\n", result) );
+
+ return result;
+}
+
+
+static
+ERL_NIF_TERM enet_getifaddrs_process(ErlNifEnv* env, struct ifaddrs* ifap)
+{
+ ERL_NIF_TERM result;
+ unsigned int len = ((ifap == NULL) ? 0 : enet_getifaddrs_length(ifap));
+
+ NDBG( ("NET", "enet_getifaddrs_process -> len: %d\r\n", len) );
+
+ if (len > 0) {
+ ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM));
+ unsigned int i = 0;
+ struct ifaddrs* p = ifap;
+
+ while (i < len) {
+ ERL_NIF_TERM entry;
+
+ if (!encode_ifaddrs(env, p, &entry)) {
+ /* If this fail, we are f*ed, so give up */
+ FREE(array);
+ return esock_make_error(env, esock_atom_einval);
+ }
+
+ NDBG( ("NET", "enet_getifaddrs_process -> entry: %T\r\n", entry) );
+
+ array[i] = entry;
+ p = p->ifa_next;
+ i++;
+ }
+
+ NDBG( ("NET", "enet_getifaddrs_process -> all entries processed\r\n") );
+
+ result = esock_make_ok2(env, MKLA(env, array, len));
+ FREE(array);
+
+ } else {
+ result = esock_make_ok2(env, MKEL(env));
+ }
+
+ NDBG( ("NET", "enet_getifaddrs_process -> result: "
+ "\r\n %T\r\n", result) );
+
+ return result;
+}
+
+
+
+/* Calculate the length of the interface adress linked list
+ * The list is NULL-terminated, so the only way is to
+ * iterate through the list until we find next = NULL.
+ */
+static
+unsigned int enet_getifaddrs_length(struct ifaddrs* ifap)
+{
+ unsigned int len = 1;
+ struct ifaddrs* tmp;
+ BOOLEAN_T done = FALSE;
+
+ tmp = ifap;
+
+ while (!done) {
+ if (tmp->ifa_next != NULL) {
+ len++;
+ tmp = tmp->ifa_next;
+ } else {
+ done = TRUE;
+ }
+ }
+
+ return len;
+}
+
+
+
+static
+BOOLEAN_T encode_ifaddrs(ErlNifEnv* env,
+ struct ifaddrs* ifap,
+ ERL_NIF_TERM* eifa)
+{
+ char* xres;
+ ERL_NIF_TERM ename, eflags, eaddr, enetmask, eifu_key, eifu_value, edata;
+ ERL_NIF_TERM eifAddrs;
+
+ ename = encode_ifaddrs_name(env, ifap->ifa_name);
+ NDBG( ("NET", "encode_ifaddrs -> name: %T\r\n", ename) );
+ eflags = encode_ifaddrs_flags(env, ifap->ifa_flags);
+ NDBG( ("NET", "encode_ifaddrs -> flags: %T\r\n", eflags) );
+ eaddr = encode_ifaddrs_addr(env, ifap->ifa_addr);
+ NDBG( ("NET", "encode_ifaddrs -> addr: %T\r\n", eaddr) );
+ enetmask = encode_ifaddrs_addr(env, ifap->ifa_netmask);
+ NDBG( ("NET", "encode_ifaddrs -> netmask: %T\r\n", enetmask) );
+ if (ifap->ifa_dstaddr && (ifap->ifa_flags & IFF_POINTOPOINT)) {
+ eifu_key = atom_dstaddr;
+ eifu_value = encode_ifaddrs_addr(env, ifap->ifa_dstaddr);
+ } else if (ifap->ifa_broadaddr && (ifap->ifa_flags & IFF_BROADCAST)) {
+ eifu_key = atom_broadaddr;
+ eifu_value = encode_ifaddrs_addr(env, ifap->ifa_broadaddr);
+ } else {
+ eifu_key = esock_atom_undefined;
+ eifu_value = esock_atom_undefined;
+ }
+ NDBG( ("NET", "encode_ifaddrs -> ifu: "
+ "\r\n key: %T"
+ "\r\n val: %T"
+ "\r\n", eifu_key, eifu_value) );
+ /* Don't know how to encode this yet...
+ * We don't even know the size...
+ */
+ edata = esock_atom_undefined;
+
+ if ((xres = make_ifaddrs(env,
+ ename, eflags, eaddr, enetmask,
+ eifu_key, eifu_value, edata,
+ &eifAddrs)) == NULL) {
+ NDBG( ("NET", "encode_ifaddrs -> encoded ifAddrs: %T\r\n", eifAddrs) );
+ *eifa = eifAddrs;
+
+ return TRUE;
+
+ } else {
+
+ /* *** Ouch ***
+ *
+ * Failed to construct the ifaddrs map => try for a minumum map
+ *
+ */
+ ERL_NIF_TERM keys[] = {atom_name};
+ ERL_NIF_TERM vals[] = {ename};
+ unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
+
+ esock_warning_msg("Failed constructing IfAddr map for %s: %s",
+ ifap->ifa_name, xres);
+
+ if (!MKMA(env, keys, vals, numKeys, eifa)) {
+ // We are in a bad way, give up */
+ *eifa = esock_atom_undefined;
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+
+ }
+}
+
+
+
+static
+ERL_NIF_TERM encode_ifaddrs_name(ErlNifEnv* env, char* name)
+{
+ return ((name == NULL) ? esock_atom_undefined : MKS(env, name));
+}
+
+
+
+static
+ERL_NIF_TERM encode_ifaddrs_flags(ErlNifEnv* env, unsigned int flags)
+{
+ SocketTArray ta = TARRAY_CREATE(16);
+ ERL_NIF_TERM eflags;
+
+ if (flags & IFF_UP)
+ TARRAY_ADD(ta, atom_up);
+
+ if (flags & IFF_BROADCAST)
+ TARRAY_ADD(ta, atom_broadcast);
+
+ if (flags & IFF_DEBUG)
+ TARRAY_ADD(ta, atom_debug);
+
+ if (flags & IFF_LOOPBACK)
+ TARRAY_ADD(ta, esock_atom_loopback);
+
+ if (flags & IFF_POINTOPOINT)
+ TARRAY_ADD(ta, atom_pointopoint);
+
+ if (flags & IFF_NOTRAILERS)
+ TARRAY_ADD(ta, atom_notrailers);
+
+ if (flags & IFF_RUNNING)
+ TARRAY_ADD(ta, atom_running);
+
+ if (flags & IFF_NOARP)
+ TARRAY_ADD(ta, atom_noarp);
+
+ if (flags & IFF_PROMISC)
+ TARRAY_ADD(ta, atom_promisc);
+
+ if (flags & IFF_MASTER)
+ TARRAY_ADD(ta, atom_master);
+
+ if (flags & IFF_SLAVE)
+ TARRAY_ADD(ta, atom_slave);
+
+ if (flags & IFF_MULTICAST)
+ TARRAY_ADD(ta, atom_multicast);
+
+ if (flags & IFF_PORTSEL)
+ TARRAY_ADD(ta, atom_portsel);
+
+ if (flags & IFF_AUTOMEDIA)
+ TARRAY_ADD(ta, atom_automedia);
+
+ if (flags & IFF_DYNAMIC)
+ TARRAY_ADD(ta, atom_dynamic);
+
+ TARRAY_TOLIST(ta, env, &eflags);
+
+ return eflags;
+}
+
+
+static
+ERL_NIF_TERM encode_ifaddrs_addr(ErlNifEnv* env, struct sockaddr* sa)
+{
+ ERL_NIF_TERM esa;
+
+ if (sa != NULL) {
+ unsigned int sz = sizeof(ESockAddress);
+ if (esock_encode_sockaddr(env, (ESockAddress*) sa, sz, &esa) != NULL) {
+ NDBG( ("NET", "encode_ifaddrs_addr -> "
+ "encoding address field 0x%lX failed => use default\r\n", sa) );
+ esa = esock_atom_undefined;
+ }
+ } else {
+ esa = esock_atom_undefined;
+ }
+
+ return esa;
+}
+
+
+static
+char* make_ifaddrs(ErlNifEnv* env,
+ ERL_NIF_TERM ename,
+ ERL_NIF_TERM eflags,
+ ERL_NIF_TERM eaddr,
+ ERL_NIF_TERM enetmask,
+ ERL_NIF_TERM eifu_key,
+ ERL_NIF_TERM eifu_value,
+ ERL_NIF_TERM edata,
+ ERL_NIF_TERM* eifAddrs)
+{
+ /* Several of these values can be (the atom) undefined, which
+ * means that they should *not* be included in the result map.
+ */
+ ERL_NIF_TERM keys[6]; // There are only (at most) siz (6) fields...
+ ERL_NIF_TERM vals[6];
+ unsigned int len = sizeof(keys) / sizeof(ERL_NIF_TERM); // Just in case...
+ unsigned int idx = 0;
+
+ /* *** Name *** */
+ NDBG( ("NET", "make_ifaddrs -> name: %T\r\n", ename) );
+ keys[idx] = atom_name;
+ vals[idx] = ename;
+ idx++;
+
+ /* *** Flags *** */
+ NDBG( ("NET", "make_ifaddrs -> flags: %T\r\n", eflags) );
+ keys[idx] = esock_atom_flags;
+ vals[idx] = eflags;
+ idx++;
+
+ /* *** Addr (can be 'undefined' = NULL) *** */
+ NDBG( ("NET", "make_ifaddrs -> addr: %T\r\n", eaddr) );
+ if (COMPARE(eaddr, esock_atom_undefined) != 0) {
+ keys[idx] = esock_atom_addr;
+ vals[idx] = eaddr;
+ idx++;
+ } else {
+ len--;
+ }
+
+ /* *** Netmask (can be 'undefined' = NULL) *** */
+ NDBG( ("NET", "make_ifaddrs -> netmask: %T\r\n", enetmask) );
+ if (COMPARE(enetmask, esock_atom_undefined) != 0) {
+ keys[idx] = atom_netmask;
+ vals[idx] = enetmask;
+ idx++;
+ } else {
+ len--;
+ }
+
+ /* *** Netmask (can be 'undefined' = NULL) *** */
+ NDBG( ("NET", "make_ifaddrs -> ifu: %T, %T\r\n", eifu_key, eifu_value) );
+ if ((COMPARE(eifu_key, esock_atom_undefined) != 0) &&
+ (COMPARE(eifu_value, esock_atom_undefined) != 0)) {
+ keys[idx] = eifu_key;
+ vals[idx] = eifu_value;
+ idx++;
+ } else {
+ len--;
+ }
+
+ /* *** Data (can be 'undefined' = NULL) *** */
+ NDBG( ("NET", "make_ifaddrs -> data: %T\r\n", edata) );
+ if (COMPARE(edata, esock_atom_undefined) != 0) {
+ keys[idx] = esock_atom_data;
+ vals[idx] = edata;
+ idx++;
+ } else {
+ len--;
+ }
+
+ NDBG( ("NET", "make_ifaddrs -> construct ifa with:"
+ "\r\n len: %d"
+ "\r\n"
+ ) );
+ if (!MKMA(env, keys, vals, len, eifAddrs)) {
+ NDBG( ("NET", "make_ifaddrs -> "
+ "failed contructing interface adress map\r\n") );
+ *eifAddrs = esock_atom_undefined;
+ return ESOCK_STR_EINVAL;
+ } else {
+ return NULL;
+ }
+}
+
+#endif // HAVE_GETIFADDRS
+
+
+/* ----------------------------------------------------------------------
* nif_if_name2index
*
* Description:
@@ -993,7 +1530,7 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env,
if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn)))
return esock_make_error(env, esock_atom_einval);
- result = nif_name2index(env, ifn);
+ result = enet_if_name2index(env, ifn);
NDBG( ("NET", "nif_if_name2index -> done when result: %T\r\n", result) );
@@ -1005,16 +1542,16 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nif_name2index(ErlNifEnv* env,
+ERL_NIF_TERM enet_if_name2index(ErlNifEnv* env,
char* ifn)
{
unsigned int idx;
- NDBG( ("NET", "nif_name2index -> entry with ifn: %s\r\n", ifn) );
+ NDBG( ("NET", "enet_if_name2index -> entry with ifn: %s\r\n", ifn) );
idx = if_nametoindex(ifn);
- NDBG( ("NET", "nif_name2index -> idx: %d\r\n", idx) );
+ NDBG( ("NET", "enet_if_name2index -> idx: %d\r\n", idx) );
if (idx == 0) {
int save_errno = get_errno();
@@ -1061,7 +1598,7 @@ ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env,
"\r\n Idx: %T"
"\r\n", argv[0]) );
- result = nif_index2name(env, idx);
+ result = enet_if_index2name(env, idx);
NDBG( ("NET", "nif_if_index2name -> done when result: %T\r\n", result) );
@@ -1073,7 +1610,7 @@ ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nif_index2name(ErlNifEnv* env,
+ERL_NIF_TERM enet_if_index2name(ErlNifEnv* env,
unsigned int idx)
{
ERL_NIF_TERM result;
@@ -1120,7 +1657,7 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env,
return enif_make_badarg(env);
}
- result = nif_names(env);
+ result = enet_if_names(env);
NDBG( ("NET", "nif_if_names -> done when result: %T\r\n", result) );
@@ -1132,12 +1669,12 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nif_names(ErlNifEnv* env)
+ERL_NIF_TERM enet_if_names(ErlNifEnv* env)
{
ERL_NIF_TERM result;
struct if_nameindex* ifs = if_nameindex();
- NDBG( ("NET", "nif_names -> ifs: 0x%lX\r\n", ifs) );
+ NDBG( ("NET", "enet_if_names -> ifs: 0x%lX\r\n", ifs) );
if (ifs == NULL) {
result = esock_make_error_errno(env, get_errno());
@@ -1154,9 +1691,9 @@ ERL_NIF_TERM nif_names(ErlNifEnv* env)
* Or shall we instead build a list in reverse order and then when
* its done, reverse that? Check
*/
- unsigned int len = nif_names_length(ifs);
+ unsigned int len = enet_if_names_length(ifs);
- NDBG( ("NET", "nif_names -> len: %d\r\n", len) );
+ NDBG( ("NET", "enet_if_names -> len: %d\r\n", len) );
if (len > 0) {
ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM));
@@ -1183,14 +1720,14 @@ ERL_NIF_TERM nif_names(ErlNifEnv* env)
static
-unsigned int nif_names_length(struct if_nameindex* p)
+unsigned int enet_if_names_length(struct if_nameindex* p)
{
unsigned int len = 0;
BOOLEAN_T done = FALSE;
while (!done) {
- NDBG( ("NET", "nif_names_length -> %d: "
+ NDBG( ("NET", "enet_if_names_length -> %d: "
"\r\n if_index: %d"
"\r\n if_name: 0x%lX"
"\r\n", len, p[len].if_index, p[len].if_name) );
@@ -1467,7 +2004,7 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env,
if (make_address_info(env, fam, type, proto, addr, &addrInfo) == NULL)
return addrInfo;
else
- return esock_atom_undefined; // We should to better...
+ return esock_atom_undefined; // We should do better...
}
@@ -1558,6 +2095,106 @@ char* make_address_info(ErlNifEnv* env,
+
+
+#ifdef HAVE_SETNS
+/* We should really have another API, so that we can return errno... */
+
+/* *** change network namespace ***
+ * Retreive the current namespace and set the new.
+ * Return result and previous namespace if successfull.
+ */
+#if !defined(__WIN32__)
+static
+BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err)
+{
+ int save_errno;
+ int current_ns = 0;
+ int new_ns = 0;
+
+ NDBG( ("NET", "change_network_namespace -> entry with"
+ "\r\n new ns: %s", netns) );
+
+ if (netns != NULL) {
+ current_ns = open("/proc/self/ns/net", O_RDONLY);
+ if (current_ns == -1) {
+ *cns = current_ns;
+ *err = get_errno();
+ return FALSE;
+ }
+ new_ns = open(netns, O_RDONLY);
+ if (new_ns == -1) {
+ save_errno = get_errno();
+ while (close(current_ns) == -1 &&
+ get_errno() == EINTR);
+ *cns = -1;
+ *err = save_errno;
+ return FALSE;
+ }
+ if (setns(new_ns, CLONE_NEWNET) != 0) {
+ save_errno = get_errno();
+ while ((close(new_ns) == -1) &&
+ (get_errno() == EINTR));
+ while ((close(current_ns) == -1) &&
+ (get_errno() == EINTR));
+ *cns = -1;
+ *err = save_errno;
+ return FALSE;
+ } else {
+ while ((close(new_ns) == -1) &&
+ (get_errno() == EINTR));
+ *cns = current_ns;
+ *err = 0;
+ return TRUE;
+ }
+ } else {
+ *cns = -1;
+ *err = 0;
+ return TRUE;
+ }
+}
+
+
+/* *** restore network namespace ***
+ * Restore the previous namespace (see above).
+ */
+static
+BOOLEAN_T restore_network_namespace(int ns, int* err)
+{
+ int save_errno;
+
+ NDBG( ("NET", "restore_network_namespace -> entry with"
+ "\r\n ns: %d", ns) );
+
+ if (ns != -1) {
+ if (setns(ns, CLONE_NEWNET) != 0) {
+ /* XXX Failed to restore network namespace.
+ * What to do? Tidy up and return an error...
+ * Note that the thread now might still be in the namespace.
+ * Can this even happen? Should the emulator be aborted?
+ */
+ save_errno = get_errno();
+ while (close(ns) == -1 &&
+ get_errno() == EINTR);
+ *err = save_errno;
+ return FALSE;
+ } else {
+ while (close(ns) == -1 &&
+ get_errno() == EINTR);
+ *err = 0;
+ return TRUE;
+ }
+ }
+
+ *err = 0;
+ return TRUE;
+}
+#endif // if !defined(__WIN32__)
+#endif // ifdef HAVE_SETNS
+
+
+
+
/* ----------------------------------------------------------------------
* C a l l b a c k F u n c t i o n s
* ----------------------------------------------------------------------
@@ -1623,6 +2260,8 @@ ErlNifFunc net_funcs[] =
/* address and name translation in protocol-independent manner */
{"nif_getnameinfo", 2, nif_getnameinfo, 0},
{"nif_getaddrinfo", 3, nif_getaddrinfo, 0},
+
+ {"nif_getifaddrs", 1, nif_getifaddrs, ERL_NIF_DIRTY_JOB_IO_BOUND},
/* Network interface (name and/or index) functions */
{"nif_if_name2index", 1, nif_if_name2index, 0},
diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h
index 93b196a618..21427b0ae3 100644
--- a/erts/emulator/nifs/common/socket_int.h
+++ b/erts/emulator/nifs/common/socket_int.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2018-2018. All Rights Reserved.
+ * Copyright Ericsson AB 2018-2019. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -60,6 +60,10 @@
#include <sys/un.h>
#endif
+#ifdef HAVE_NETPACKET_PACKET_H
+#include <netpacket/packet.h>
+#endif
+
#endif
#include <erl_nif.h>
@@ -82,6 +86,11 @@ typedef union {
struct sockaddr_un un;
#endif
+ /* Link Layer sockaddr (used for address family PACKET) */
+#if defined(HAVE_NETPACKET_PACKET_H) && defined(AF_PACKET)
+ struct sockaddr_ll ll;
+#endif
+
} ESockAddress;
@@ -167,15 +176,18 @@ typedef unsigned int BOOLEAN_T;
GLOBAL_ATOM_DEF(faith); \
GLOBAL_ATOM_DEF(false); \
GLOBAL_ATOM_DEF(family); \
+ GLOBAL_ATOM_DEF(fastroute); \
GLOBAL_ATOM_DEF(flags); \
GLOBAL_ATOM_DEF(flowinfo); \
GLOBAL_ATOM_DEF(fragment_interleave); \
GLOBAL_ATOM_DEF(freebind); \
GLOBAL_ATOM_DEF(get_peer_addr_info); \
+ GLOBAL_ATOM_DEF(hatype); \
GLOBAL_ATOM_DEF(hdrincl); \
GLOBAL_ATOM_DEF(hmac_ident); \
GLOBAL_ATOM_DEF(hoplimit); \
GLOBAL_ATOM_DEF(hopopts); \
+ GLOBAL_ATOM_DEF(host); \
GLOBAL_ATOM_DEF(icmp); \
GLOBAL_ATOM_DEF(icmp6); \
GLOBAL_ATOM_DEF(ifindex); \
@@ -212,6 +224,7 @@ typedef unsigned int BOOLEAN_T;
GLOBAL_ATOM_DEF(msfilter); \
GLOBAL_ATOM_DEF(mtu); \
GLOBAL_ATOM_DEF(mtu_discover); \
+ GLOBAL_ATOM_DEF(multicast); \
GLOBAL_ATOM_DEF(multicast_all); \
GLOBAL_ATOM_DEF(multicast_hops); \
GLOBAL_ATOM_DEF(multicast_if); \
@@ -229,6 +242,9 @@ typedef unsigned int BOOLEAN_T;
GLOBAL_ATOM_DEF(oobinline); \
GLOBAL_ATOM_DEF(options); \
GLOBAL_ATOM_DEF(origdstaddr); \
+ GLOBAL_ATOM_DEF(otherhost); \
+ GLOBAL_ATOM_DEF(outgoing); \
+ GLOBAL_ATOM_DEF(packet); \
GLOBAL_ATOM_DEF(partial_delivery_point); \
GLOBAL_ATOM_DEF(passcred); \
GLOBAL_ATOM_DEF(path); \
@@ -239,6 +255,7 @@ typedef unsigned int BOOLEAN_T;
GLOBAL_ATOM_DEF(peer_auth_chunks); \
GLOBAL_ATOM_DEF(pktinfo); \
GLOBAL_ATOM_DEF(pktoptions); \
+ GLOBAL_ATOM_DEF(pkttype); \
GLOBAL_ATOM_DEF(port); \
GLOBAL_ATOM_DEF(portrange); \
GLOBAL_ATOM_DEF(primary_addr); \
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index f8a7792c68..e36cd38ece 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -2809,15 +2809,18 @@ static char str_exsend[] = "exsend"; // failed send
GLOBAL_ATOM_DECL(faith); \
GLOBAL_ATOM_DECL(false); \
GLOBAL_ATOM_DECL(family); \
+ GLOBAL_ATOM_DECL(fastroute); \
GLOBAL_ATOM_DECL(flags); \
GLOBAL_ATOM_DECL(flowinfo); \
GLOBAL_ATOM_DECL(fragment_interleave); \
GLOBAL_ATOM_DECL(freebind); \
GLOBAL_ATOM_DECL(get_peer_addr_info); \
+ GLOBAL_ATOM_DECL(hatype); \
GLOBAL_ATOM_DECL(hdrincl); \
GLOBAL_ATOM_DECL(hmac_ident); \
GLOBAL_ATOM_DECL(hoplimit); \
GLOBAL_ATOM_DECL(hopopts); \
+ GLOBAL_ATOM_DECL(host); \
GLOBAL_ATOM_DECL(icmp); \
GLOBAL_ATOM_DECL(icmp6); \
GLOBAL_ATOM_DECL(ifindex); \
@@ -2854,6 +2857,7 @@ static char str_exsend[] = "exsend"; // failed send
GLOBAL_ATOM_DECL(msfilter); \
GLOBAL_ATOM_DECL(mtu); \
GLOBAL_ATOM_DECL(mtu_discover); \
+ GLOBAL_ATOM_DECL(multicast); \
GLOBAL_ATOM_DECL(multicast_all); \
GLOBAL_ATOM_DECL(multicast_hops); \
GLOBAL_ATOM_DECL(multicast_if); \
@@ -2871,6 +2875,9 @@ static char str_exsend[] = "exsend"; // failed send
GLOBAL_ATOM_DECL(oobinline); \
GLOBAL_ATOM_DECL(options); \
GLOBAL_ATOM_DECL(origdstaddr); \
+ GLOBAL_ATOM_DECL(otherhost); \
+ GLOBAL_ATOM_DECL(outgoing); \
+ GLOBAL_ATOM_DECL(packet); \
GLOBAL_ATOM_DECL(partial_delivery_point); \
GLOBAL_ATOM_DECL(passcred); \
GLOBAL_ATOM_DECL(path); \
@@ -2881,6 +2888,7 @@ static char str_exsend[] = "exsend"; // failed send
GLOBAL_ATOM_DECL(peer_auth_chunks); \
GLOBAL_ATOM_DECL(pktinfo); \
GLOBAL_ATOM_DECL(pktoptions); \
+ GLOBAL_ATOM_DECL(pkttype); \
GLOBAL_ATOM_DECL(port); \
GLOBAL_ATOM_DECL(portrange); \
GLOBAL_ATOM_DECL(primary_addr); \
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c
index 9496757b77..5514ef217a 100644
--- a/erts/emulator/nifs/common/socket_util.c
+++ b/erts/emulator/nifs/common/socket_util.c
@@ -39,6 +39,18 @@
#include "config.h"
#endif
+#ifdef HAVE_SOCKLEN_T
+# define SOCKLEN_T socklen_t
+#else
+# define SOCKLEN_T size_t
+#endif
+
+#ifdef __WIN32__
+#define SOCKOPTLEN_T int
+#else
+#define SOCKOPTLEN_T SOCKLEN_T
+#endif
+
/* We don't have a "debug flag" to check here, so we
* should use the compile debug flag, whatever that is...
*/
@@ -60,6 +72,11 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */
#endif
+static void esock_encode_packet_addr_tuple(ErlNifEnv* env,
+ unsigned char len,
+ unsigned char* addr,
+ ERL_NIF_TERM* eAddr);
+
static char* make_sockaddr_in4(ErlNifEnv* env,
ERL_NIF_TERM port,
ERL_NIF_TERM addr,
@@ -73,6 +90,13 @@ static char* make_sockaddr_in6(ErlNifEnv* env,
static char* make_sockaddr_un(ErlNifEnv* env,
ERL_NIF_TERM path,
ERL_NIF_TERM* sa);
+static char* make_sockaddr_ll(ErlNifEnv* env,
+ ERL_NIF_TERM proto,
+ ERL_NIF_TERM ifindex,
+ ERL_NIF_TERM hatype,
+ ERL_NIF_TERM pkttype,
+ ERL_NIF_TERM addr,
+ ERL_NIF_TERM* sa);
/* +++ esock_encode_iov +++
@@ -310,6 +334,12 @@ char* esock_encode_sockaddr(ErlNifEnv* env,
break;
#endif
+#ifdef HAVE_NETPACKET_PACKET_H
+ case AF_PACKET:
+ xres = esock_encode_sockaddr_ll(env, &sockAddrP->ll, addrLen, eSockAddr);
+ break;
+#endif
+
default:
*eSockAddr = esock_atom_undefined;
xres = ESOCK_STR_EAFNOSUPPORT;
@@ -717,6 +747,65 @@ char* esock_encode_sockaddr_un(ErlNifEnv* env,
+/* +++ esock_encode_sockaddr_ll +++
+ *
+ * Encode a PACKET address - sockaddr_ll (link layer). In erlang its
+ * represented as a map, which has a specific set of attributes
+ * (beside the mandatory family attribute, which is "inherited" from
+ * the "sockaddr" type):
+ *
+ * protocol: integer() (should be an atom really)
+ * ifindex: integer()
+ * hatype: integer() (should be an atom really)
+ * pkttype: integer() (should be an atom really)
+ * addr: list() (should be something usefull...)
+ *
+ */
+
+#ifdef HAVE_NETPACKET_PACKET_H
+extern
+char* esock_encode_sockaddr_ll(ErlNifEnv* env,
+ struct sockaddr_ll* sockAddrP,
+ unsigned int addrLen,
+ ERL_NIF_TERM* eSockAddr)
+{
+ ERL_NIF_TERM eProto, eIfIdx, eHaType, ePktType, eAddr;
+ char* xres;
+
+ if (addrLen >= sizeof(struct sockaddr_ll)) {
+
+ /* protocol - the standard ethernet protocol type */
+ esock_encode_packet_protocol(env, ntohs(sockAddrP->sll_protocol), &eProto);
+
+ /* ifindex - the interface index of the interface */
+ eIfIdx = MKI(env, sockAddrP->sll_ifindex);
+
+ /* hatype - is an ARP (hardware) type */
+ esock_encode_packet_hatype(env, sockAddrP->sll_hatype, &eHaType);
+
+ /* pkttype - the packet type */
+ esock_encode_packet_pkttype(env, sockAddrP->sll_pkttype, &ePktType);
+
+ /* addr - the physical-layer (e.g., IEEE 802.3) address */
+ esock_encode_packet_addr(env,
+ sockAddrP->sll_halen, sockAddrP->sll_addr,
+ &eAddr);
+
+ xres = make_sockaddr_ll(env,
+ eProto, eIfIdx, eHaType, ePktType, eAddr,
+ eSockAddr);
+
+ } else {
+ *eSockAddr = esock_atom_undefined;
+ xres = ESOCK_STR_EINVAL;
+ }
+
+ return xres;
+}
+#endif
+
+
+
/* +++ esock_decode_ip4_address +++
*
* Decode a IPv4 address. This can be three things:
@@ -1293,6 +1382,164 @@ char* esock_decode_protocol(ErlNifEnv* env,
+/* +++ esock_encode_packet_protocol +++
+ *
+ * Encode the Link Layer sockaddr protocol.
+ *
+ * Currently we just represent this as an unsigned int.
+ */
+extern
+void esock_encode_packet_protocol(ErlNifEnv* env,
+ unsigned short protocol,
+ ERL_NIF_TERM* eProtocol)
+{
+ *eProtocol = MKUI(env, protocol);
+}
+
+
+/* +++ esock_encode_packet_hatype +++
+ *
+ * Encode the Link Layer sockaddr hatype.
+ *
+ * Currently we just represent this as an unsigned int.
+ */
+extern
+void esock_encode_packet_hatype(ErlNifEnv* env,
+ unsigned short hatype,
+ ERL_NIF_TERM* eHaType)
+{
+ *eHaType = MKUI(env, hatype);
+}
+
+
+/* +++ esock_encode_packet_pkttype +++
+ *
+ * Encode the Link Layer sockaddr pkttype.
+ *
+ * PACKET_HOST => host
+ * PACKET_BROADCAST => broadcast
+ * PACKET_MULTICAST => multicast
+ * PACKET_OTHERHOST => otherhost
+ * PACKET_OUTGOING => outgoing
+ * PACKET_LOOPBACK => loopback
+ * PACKET_USER => user
+ * PACKET_KERNEL => kernel
+ *
+ */
+extern
+void esock_encode_packet_pkttype(ErlNifEnv* env,
+ unsigned short pkttype,
+ ERL_NIF_TERM* ePktType)
+{
+ switch (pkttype) {
+#if defined(PACKET_HOST)
+ case PACKET_HOST:
+ *ePktType = esock_atom_host;
+ break;
+#endif
+
+#if defined(PACKET_BROADCAST)
+ case PACKET_BROADCAST:
+ *ePktType = esock_atom_broadcast;
+ break;
+#endif
+
+#if defined(PACKET_MULTICAST)
+ case PACKET_MULTICAST:
+ *ePktType = esock_atom_multicast;
+ break;
+#endif
+
+#if defined(PACKET_OTHERHOST)
+ case PACKET_OTHERHOST:
+ *ePktType = esock_atom_otherhost;
+ break;
+#endif
+
+#if defined(PACKET_OUTGOING)
+ case PACKET_OUTGOING:
+ *ePktType = esock_atom_outgoing;
+ break;
+#endif
+
+#if defined(PACKET_LOOPBACK)
+ case PACKET_LOOPBACK:
+ *ePktType = esock_atom_loopback;
+ break;
+#endif
+
+#if defined(PACKET_USER)
+ case PACKET_USER:
+ *ePktType = esock_atom_user;
+ break;
+#endif
+
+#if defined(PACKET_KERNEL)
+ case PACKET_KERNEL:
+ *ePktType = esock_atom_kernel;
+ break;
+#endif
+
+#if defined(PACKET_FASTROUTE)
+ case PACKET_FASTROUTE:
+ *ePktType = esock_atom_fastroute;
+ break;
+#endif
+
+ default:
+ *ePktType = MKUI(env, pkttype);
+ break;
+ }
+
+}
+
+
+
+/* +++ esock_encode_packet_addr +++
+ *
+ * Encode the Link Layer sockaddr address.
+ *
+ */
+extern
+void esock_encode_packet_addr(ErlNifEnv* env,
+ unsigned char len,
+ unsigned char* addr,
+ ERL_NIF_TERM* eAddr)
+{
+#if defined(ESOCK_PACKET_ADDRESS_AS_TUPLE)
+ esock_encode_packet_addr_tuple(env, len, addr, eAddr);
+#else
+ SOCKOPTLEN_T vsz = len;
+ ErlNifBinary val;
+
+ if (ALLOC_BIN(vsz, &val)) {
+ sys_memcpy(val.data, addr, len);
+ *eAddr = MKBIN(env, &val);
+ } else {
+ esock_encode_packet_addr_tuple(env, len, addr, eAddr);
+ }
+#endif
+}
+
+
+static
+void esock_encode_packet_addr_tuple(ErlNifEnv* env,
+ unsigned char len,
+ unsigned char* addr,
+ ERL_NIF_TERM* eAddr)
+{
+ ERL_NIF_TERM array[len];
+ unsigned char i;
+
+ for (i = 0; i < len; i++) {
+ array[i] = MKUI(env, addr[i]);
+ }
+
+ *eAddr = MKTA(env, array, len);
+}
+
+
+
/* +++ esock_decode_bufsz +++
*
* Decode an buffer size. The size of a buffer is:
@@ -1737,3 +1984,36 @@ char* make_sockaddr_un(ErlNifEnv* env,
}
+/* Construct the Link Layer socket address */
+#ifdef HAVE_NETPACKET_PACKET_H
+static
+char* make_sockaddr_ll(ErlNifEnv* env,
+ ERL_NIF_TERM proto,
+ ERL_NIF_TERM ifindex,
+ ERL_NIF_TERM hatype,
+ ERL_NIF_TERM pkttype,
+ ERL_NIF_TERM addr,
+ ERL_NIF_TERM* sa)
+{
+ ERL_NIF_TERM keys[] = {esock_atom_family,
+ esock_atom_protocol,
+ esock_atom_ifindex,
+ esock_atom_hatype,
+ esock_atom_pkttype,
+ esock_atom_addr};
+ ERL_NIF_TERM vals[] = {esock_atom_packet, proto, ifindex,
+ hatype, pkttype, addr};
+ unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
+ unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM);
+
+ ESOCK_ASSERT( (numKeys == numVals) );
+
+ if (!MKMA(env, keys, vals, numKeys, sa)) {
+ *sa = esock_atom_undefined;
+ return ESOCK_STR_EINVAL;
+ } else {
+ return NULL;
+ }
+}
+#endif
+
diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h
index 725dac6c95..976d94cd8c 100644
--- a/erts/emulator/nifs/common/socket_util.h
+++ b/erts/emulator/nifs/common/socket_util.h
@@ -104,6 +104,14 @@ char* esock_encode_sockaddr_un(ErlNifEnv* env,
ERL_NIF_TERM* eSockAddr);
#endif
+#ifdef HAVE_NETPACKET_PACKET_H
+extern
+char* esock_encode_sockaddr_ll(ErlNifEnv* env,
+ struct sockaddr_ll* sockAddrP,
+ unsigned int addrLen,
+ ERL_NIF_TERM* eSockAddr);
+#endif
+
extern
char* esock_decode_ip4_address(ErlNifEnv* env,
ERL_NIF_TERM eAddr,
@@ -158,6 +166,24 @@ char* esock_encode_protocol(ErlNifEnv* env,
ERL_NIF_TERM* eProtocol);
extern
+void esock_encode_packet_protocol(ErlNifEnv* env,
+ unsigned short protocol,
+ ERL_NIF_TERM* eProtocol);
+extern
+void esock_encode_packet_hatype(ErlNifEnv* env,
+ unsigned short hatype,
+ ERL_NIF_TERM* eHaType);
+extern
+void esock_encode_packet_pkttype(ErlNifEnv* env,
+ unsigned short pkttype,
+ ERL_NIF_TERM* ePktType);
+extern
+void esock_encode_packet_addr(ErlNifEnv* env,
+ unsigned char len,
+ unsigned char* addr,
+ ERL_NIF_TERM* eAddr);
+
+extern
char* esock_decode_bufsz(ErlNifEnv* env,
ERL_NIF_TERM eVal,
size_t defSz,
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 9fdb4003db..478555a902 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -43,9 +43,6 @@ SOCKET_MODULES = \
socket_test_ttest_tcp_server_socket \
socket_SUITE
-NET_MODULES = \
- net_SUITE
-
MODULES= \
a_SUITE \
after_SUITE \
@@ -102,7 +99,6 @@ MODULES= \
monitor_SUITE \
multi_load_SUITE \
nested_SUITE \
- $(NET_MODULES) \
nif_SUITE \
node_container_SUITE \
nofrag_SUITE \
@@ -181,7 +177,6 @@ HRL_FILES= \
socket_test_ttest_client.hrl
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-NET_TARGETS = $(NET_MODULES:%=$(EBIN)/%.$(EMULATOR))
SOCKET_TARGETS = $(SOCKET_MODULES:%=$(EBIN)/%.$(EMULATOR))
EMAKEFILE=Emakefile
@@ -227,7 +222,6 @@ docs:
targets: $(TARGET_FILES)
socket_targets: $(SOCKET_TARGETS)
-net_targets: $(NET_TARGETS)
# ----------------------------------------------------
# Special targets
diff --git a/erts/preloaded/ebin/prim_net.beam b/erts/preloaded/ebin/prim_net.beam
index b7a617ce47..b9481777e5 100644
--- a/erts/preloaded/ebin/prim_net.beam
+++ b/erts/preloaded/ebin/prim_net.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index cbb1d7e47f..8e45b1373c 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_net.erl b/erts/preloaded/src/prim_net.erl
index 99e00ee2e7..fcd528bdaa 100644
--- a/erts/preloaded/src/prim_net.erl
+++ b/erts/preloaded/src/prim_net.erl
@@ -33,6 +33,7 @@
gethostname/0,
getnameinfo/2,
getaddrinfo/2,
+ getifaddrs/1,
if_name2index/1,
if_index2name/1,
@@ -41,18 +42,37 @@
-export_type([
address_info/0,
+ ifaddrs/0,
name_info/0,
- name_info_flags/0,
+ ifaddrs_flag/0,
+ ifaddrs_flags/0,
+
name_info_flag/0,
name_info_flag_ext/0,
+ name_info_flags/0,
network_interface_name/0,
network_interface_index/0
]).
--type name_info_flags() :: [name_info_flag()|name_info_flag_ext()].
+-type ifaddrs_flag() :: up | broadcast | debug | loopback | pointopoint |
+ notrailers | running | noarp | promisc | master | slave |
+ multicast | portsel | automedia | dynamic.
+-type ifaddrs_flags() :: [ifaddrs_flag()].
+
+%% Note that not all of these fields are mandatory.
+%% Actually there are (error) cases when only the name will be included.
+%% And broadaddr and dstaddr are mutually exclusive!
+
+-type ifaddrs() :: #{name := string(),
+ flags := ifaddrs_flags(),
+ addr := socket:sockaddr(),
+ netmask := socket:sockaddr(),
+ broadaddr := socket:sockaddr(),
+ dstaddr := socket:sockaddr()}.
+
-type name_info_flag() :: namereqd |
dgram |
nofqdn |
@@ -62,6 +82,7 @@
%% (as they are deprecated by later version of gcc):
%% idn_allow_unassigned | idn_use_std3_ascii_rules.
-type name_info_flag_ext() :: idn.
+-type name_info_flags() :: [name_info_flag()|name_info_flag_ext()].
-type name_info() :: #{host := string(),
service := string()}.
-type address_info() :: #{family := socket:domain(),
@@ -177,6 +198,21 @@ getaddrinfo(Host, Service)
%% ===========================================================================
%%
+%% getifaddrs - Get interface addresses
+%%
+
+-spec getifaddrs(Extra) -> {ok, IfInfo} | {error, Reason} when
+ Extra :: map(),
+ IfInfo :: [ifaddrs()],
+ Reason :: term().
+
+getifaddrs(Extra) when is_map(Extra) ->
+ nif_getifaddrs(Extra).
+
+
+
+%% ===========================================================================
+%%
%% if_name2index - Mappings between network interface names and indexes:
%% name -> idx
%%
@@ -279,6 +315,9 @@ nif_getnameinfo(_Addr, _Flags) ->
nif_getaddrinfo(_Host, _Service, _Hints) ->
erlang:nif_error(undef).
+nif_getifaddrs(_Extra) ->
+ erlang:nif_error(undef).
+
nif_if_name2index(_Name) ->
erlang:nif_error(undef).
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index 8a6641c030..5984b5bda9 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -86,6 +86,7 @@
sockaddr_in4/0,
sockaddr_in6/0,
sockaddr_un/0,
+ sockaddr_ll/0,
send_flags/0,
send_flag/0,
@@ -322,9 +323,19 @@
addr := any | loopback | ip6_address(),
flowinfo := in6_flow_info(),
scope_id := in6_scope_id()}.
+-type sockaddr_ll() :: #{family := packet,
+ protocol := non_neg_integer(),
+ ifindex := integer(),
+ pkttype := packet_type(),
+ hatype := non_neg_integer(),
+ addr := binary()}.
+-type packet_type() :: host | broadcast | multicast | otherhost |
+ outgoing | loopback | user | kernel | fastroute |
+ non_neg_integer().
-type sockaddr() :: sockaddr_in4() |
sockaddr_in6() |
- sockaddr_un().
+ sockaddr_un() |
+ sockaddr_ll().
-define(SOCKADDR_IN4_DEFAULTS(A), #{port => 0,
addr => A}).
diff --git a/lib/kernel/doc/src/net.xml b/lib/kernel/doc/src/net.xml
index 425a16b104..c8844f5257 100644
--- a/lib/kernel/doc/src/net.xml
+++ b/lib/kernel/doc/src/net.xml
@@ -47,6 +47,34 @@
<name name="address_info"/>
</datatype>
<datatype>
+ <name name="ifaddrs"/>
+ <desc>
+ <p>This type defines all addresses (and flags) associated with
+ the interface. </p>
+ <p>Not all fields of this map has to be present.
+ The flags field can be used to test this for some of the fields.
+ For example <c>broadaddr</c> will only be present if the
+ <c>broadcast</c> flag is present in flags. </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="ifaddrs_flag"/>
+ </datatype>
+ <datatype>
+ <name name="ifaddrs_filter_map"/>
+ <desc>
+ <p>The '<c>default</c>' value for <c>family</c> is interpreted
+ as <c>inet</c> <em>and</em> <c>inet6</c>. </p>
+ <p>Also, the <c>family</c> field can only have the specified values
+ (and not the all the values of socket:domain()). </p>
+ <p>The use of the <c>flags</c> field is that any flag provided must
+ exist for the interface. </p>
+ <p>For example, if <c>family</c> is set to <c>inet</c> and <c>flags</c>
+ to <c>[broadcast, multicast]</c> only interfaces address family <c>inet</c>
+ and the flags <c>broadcast</c> and <c>multicast</c> will be listen. </p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="name_info"/>
</datatype>
<datatype>
@@ -104,6 +132,23 @@
</func>
<func>
+ <name name="getifaddrs" arity="0" since="@OTP-16212@"/>
+ <name name="getifaddrs" arity="1" clause_i="1" since="@OTP-16212@"/>
+ <name name="getifaddrs" arity="1" clause_i="2" since="@OTP-16212@"/>
+ <name name="getifaddrs" arity="2" since="@OTP-16212@"/>
+ <fsummary>Get interface addresses.</fsummary>
+ <desc>
+ <p>Get interface addresses.</p>
+ <p>This function is used to get the machines interface addresses,
+ possibly filtered with the <c>FilterMap</c>. </p>
+ <p>By default, a filter with the content:
+ <c>#{family => default, flags => any}</c> is used.
+ This will return all interfaces with adresses in the
+ <c>inet</c> and <c>inet6</c> families. </p>
+ </desc>
+ </func>
+
+ <func>
<name name="if_name2index" arity="1" since="OTP 22.0"/>
<fsummary>Mappings between network interface names and indexes.</fsummary>
<desc>
diff --git a/lib/kernel/src/net.erl b/lib/kernel/src/net.erl
index 207f023bbe..a81666885b 100644
--- a/lib/kernel/src/net.erl
+++ b/lib/kernel/src/net.erl
@@ -37,6 +37,7 @@
gethostname/0,
getnameinfo/1, getnameinfo/2,
getaddrinfo/1, getaddrinfo/2,
+ getifaddrs/0, getifaddrs/1, getifaddrs/2,
if_name2index/1,
if_index2name/1,
@@ -54,8 +55,11 @@
%% Should we define these here or refer to the prim_net module
-export_type([
address_info/0,
+ ifaddrs/0,
name_info/0,
+ ifaddrs_flag/0,
+
name_info_flags/0,
name_info_flag/0,
name_info_flag_ext/0,
@@ -73,6 +77,24 @@
-deprecated({sleep, 1, eventually}).
+-type ifaddrs_flag() :: up | broadcast | debug | loopback | pointopoint |
+ notrailers | running | noarp | promisc | master | slave |
+ multicast | portsel | automedia | dynamic.
+
+%% Note that not all of these fields are mandatory.
+%% Actually there are (error) cases when only the name will be included.
+%% And broadaddr and dstaddr are mutually exclusive!
+
+-type ifaddrs() :: #{name := string(),
+ flags := [ifaddrs_flag()],
+ addr := socket:sockaddr(),
+ netmask := socket:sockaddr(),
+ broadaddr := socket:sockaddr(),
+ dstaddr := socket:sockaddr()}.
+
+-type ifaddrs_filter_map() :: #{family := default | inet | inet6 | packet | all,
+ flags := any | [ifaddrs_flag()]}.
+
-type name_info_flags() :: [name_info_flag()|name_info_flag_ext()].
-type name_info_flag() :: namereqd |
dgram |
@@ -272,6 +294,106 @@ getaddrinfo(Host, Service)
%% ===========================================================================
%%
+%% getifaddrs - Get interface addresses
+%%
+
+-spec getifaddrs() -> {ok, IfAddrs} | {error, Reason} when
+ IfAddrs :: [ifaddrs()],
+ Reason :: term().
+
+-ifdef(USE_ESOCK).
+getifaddrs() ->
+ getifaddrs(getifaddrs_filter_map_default()).
+-else.
+getifaddrs() ->
+ erlang:error(notsup).
+-endif.
+
+
+-spec getifaddrs(FilterMap) -> {ok, IfAddrs} | {error, Reason} when
+ FilterMap :: ifaddrs_filter_map(),
+ IfAddrs :: [ifaddrs()],
+ Reason :: term();
+ (Namespace) -> {ok, IfAddrs} | {error, Reason} when
+ Namespace :: file:filename_all(),
+ IfAddrs :: [ifaddrs()],
+ Reason :: term().
+
+-ifdef(USE_ESOCK).
+getifaddrs(Namespace) when is_list(Namespace) ->
+ prim_net:getifaddrs(#{netns => Namespace});
+getifaddrs(FilterMap) when is_map(FilterMap) ->
+ do_getifaddrs(getifaddrs_filter_map(FilterMap),
+ fun() -> prim_net:getifaddrs(#{}) end).
+-else.
+-dialyzer({nowarn_function, getifaddrs/1}).
+getifaddrs(Namespace) when is_list(Namespace) ->
+ erlang:error(notsup);
+getifaddrs(FilterMap) when is_map(FilterMap) ->
+ erlang:error(notsup).
+-endif.
+
+
+-spec getifaddrs(FilterMap, Namespace) -> {ok, IfAddrs} | {error, Reason} when
+ FilterMap :: ifaddrs_filter_map(),
+ Namespace :: file:filename_all(),
+ IfAddrs :: [ifaddrs()],
+ Reason :: term().
+
+getifaddrs(FilterMap, Namespace)
+ when is_map(FilterMap) andalso is_list(Namespace) ->
+ do_getifaddrs(getifaddrs_filter_map(FilterMap),
+ fun() -> getifaddrs(Namespace) end).
+
+do_getifaddrs(FilterMap, GetIfAddrs) ->
+ case GetIfAddrs() of
+ {ok, IfAddrs0} ->
+ Filter = fun(Elem) -> getifaddrs_filter(FilterMap, Elem) end,
+ {ok, lists:filtermap(Filter, IfAddrs0)};
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+getifaddrs_filter_map(FilterMap) ->
+ maps:merge(getifaddrs_filter_map_default(), FilterMap).
+
+getifaddrs_filter_map_default() ->
+ #{family => default, flags => any}.
+
+getifaddrs_filter(#{family := FFamily, flags := FFlags},
+ #{addr := #{family := Family}, flags := Flags} = _Entry)
+ when (FFamily =:= default) andalso
+ ((Family =:= inet) orelse (Family =:= inet6)) ->
+ getifaddrs_filter_flags(FFlags, Flags);
+getifaddrs_filter(#{family := FFamily, flags := FFlags},
+ #{addr := #{family := Family}, flags := Flags} = _Entry)
+ when (FFamily =:= inet) andalso (Family =:= inet) ->
+ getifaddrs_filter_flags(FFlags, Flags);
+getifaddrs_filter(#{family := FFamily, flags := FFlags},
+ #{addr := #{family := Family}, flags := Flags} = _Entry)
+ when (FFamily =:= inet6) andalso (Family =:= inet6) ->
+ getifaddrs_filter_flags(FFlags, Flags);
+getifaddrs_filter(#{family := FFamily, flags := FFlags},
+ #{addr := #{family := Family}, flags := Flags} = _Entry)
+ when (FFamily =:= packet) andalso (Family =:= packet) ->
+ getifaddrs_filter_flags(FFlags, Flags);
+getifaddrs_filter(#{family := FFamily, flags := FFlags},
+ #{flags := Flags} = _Entry)
+ when (FFamily =:= all) ->
+ getifaddrs_filter_flags(FFlags, Flags);
+getifaddrs_filter(_Filter, _Entry) ->
+ false.
+
+
+getifaddrs_filter_flags(any, _Flags) ->
+ true;
+getifaddrs_filter_flags(FilterFlags, Flags) ->
+ [] =:= (FilterFlags -- Flags).
+
+
+
+%% ===========================================================================
+%%
%% if_name2index - Mappings between network interface names and indexes:
%% name -> idx
%%
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 6763a04d9f..bd1590ee8f 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -83,6 +83,7 @@ MODULES= \
logger_std_h_SUITE \
logger_stress_SUITE \
logger_test_lib \
+ net_SUITE \
os_SUITE \
pg2_SUITE \
seq_trace_SUITE \
diff --git a/erts/emulator/test/net_SUITE.erl b/lib/kernel/test/net_SUITE.erl
index c6e77a5373..c6e77a5373 100644
--- a/erts/emulator/test/net_SUITE.erl
+++ b/lib/kernel/test/net_SUITE.erl