diff options
author | Micael Karlberg <bmk@erlang.org> | 2019-11-27 10:38:26 +0100 |
---|---|---|
committer | Micael Karlberg <bmk@erlang.org> | 2019-11-27 10:38:26 +0100 |
commit | 7e2ee5d8efa9d75688d649441ad524466375c034 (patch) | |
tree | 023a3837023bde39c3c0047bbe16557780247b4b | |
parent | 28a90a039dc513a702fb2825076e8e4de1df4445 (diff) | |
parent | 171cf38c343e50ae7a4f28a6c1a1a58acad17407 (diff) | |
download | erlang-7e2ee5d8efa9d75688d649441ad524466375c034.tar.gz |
Merge branch 'bmk/erts/esock/20191125/add_getifaddrs_in_net/OTP-16212' into maint
-rw-r--r-- | erts/configure.in | 6 | ||||
-rw-r--r-- | erts/doc/src/socket.xml | 6 | ||||
-rw-r--r-- | erts/emulator/nifs/common/prim_net_nif.c | 747 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_int.h | 19 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_nif.c | 8 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.c | 280 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.h | 26 | ||||
-rw-r--r-- | erts/emulator/test/Makefile | 6 | ||||
-rw-r--r-- | erts/preloaded/ebin/prim_net.beam | bin | 4568 -> 5192 bytes | |||
-rw-r--r-- | erts/preloaded/ebin/socket.beam | bin | 78564 -> 78736 bytes | |||
-rw-r--r-- | erts/preloaded/src/prim_net.erl | 43 | ||||
-rw-r--r-- | erts/preloaded/src/socket.erl | 13 | ||||
-rw-r--r-- | lib/kernel/doc/src/net.xml | 45 | ||||
-rw-r--r-- | lib/kernel/src/net.erl | 122 | ||||
-rw-r--r-- | lib/kernel/test/Makefile | 1 | ||||
-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, ¤t_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 Binary files differindex b7a617ce47..b9481777e5 100644 --- a/erts/preloaded/ebin/prim_net.beam +++ b/erts/preloaded/ebin/prim_net.beam diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differindex cbb1d7e47f..8e45b1373c 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam 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 |