diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 4c2379c134bd..f6d5e7915cda 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -145,10 +145,25 @@ #define D2S(__D__) (((__D__) == INET_DELIVER_PORT) ? "port" : \ (((__D__) == INET_DELIVER_TERM) ? "term" : \ "undefined")) -#define DOM2S(__D__) (((__D__) == INET_AF_INET) ? "inet" : \ - (((__D__) == INET_AF_INET6) ? "inet6" : \ - (((__D__) == INET_AF_LOCAL) ? "local" : \ +#define DOM2S(__D__) (((__D__) == INET_AF_INET) ? "inet" : \ + (((__D__) == INET_AF_INET6) ? "inet6" : \ + (((__D__) == INET_AF_LOCAL) ? "local" : \ "undefined"))) +#if defined(AF_LINK) +#define FAM2S(__F__) (((__F__) == AF_INET) ? "inet" : \ + (((__F__) == AF_INET6) ? "inet6" : \ + (((__F__) == AF_LINK) ? "link" : \ + "undefined"))) +#elif defined(AF_PACKET) +#define FAM2S(__F__) (((__F__) == AF_INET) ? "inet" : \ + (((__F__) == AF_INET6) ? "inet6" : \ + (((__F__) == AF_PACKET) ? "packet" : \ + "undefined"))) +#else +#define FAM2S(__F__) (((__F__) == AF_INET) ? "inet" : \ + (((__F__) == AF_INET6) ? "inet6" : \ + "undefined")) +#endif #if defined(__WIN32__) && defined(ARCH_64) #define SOCKET_FSTR "%lld" @@ -6571,6 +6586,12 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, } \ } while (0) + DDBG(desc_p, + ("INET-DRV-DBG[%d][" SOCKET_FSTR ",%T] " + "%s -> get if addrs" + "\r\n", + __LINE__, desc_p->s, driver_caller(desc_p->port), __FUNCTION__) ); + if ((save_errno = call_getifaddrs(desc_p, &ifa_p)) != 0) return ctl_error(save_errno, rbuf_pp, rsize); @@ -6578,6 +6599,15 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, *buf_p++ = INET_REP_OK; for (; ifa_p; ifa_p = ifa_p->ifa_next) { int len = utf8_len(ifa_p->ifa_name, -1); + + DDBG(desc_p, + ("INET-DRV-DBG[%d][" SOCKET_FSTR ",%T] " + "%s -> process if-addr %s" + "\r\n flags: 0x%X" + "\r\n", + __LINE__, desc_p->s, driver_caller(desc_p->port), __FUNCTION__, + ifa_p->ifa_name, ifa_p->ifa_flags) ); + BUF_ENSURE(len+1 + 1+4 + 1); utf8_encode(ifa_p->ifa_name, -1, buf_p); buf_p += len; @@ -6585,6 +6615,15 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, *buf_p++ = INET_IFOPT_FLAGS; put_int32(IFGET_FLAGS(ifa_p->ifa_flags), buf_p); buf_p += 4; if (ifa_p->ifa_addr) { + + DDBG(desc_p, + ("INET-DRV-DBG[%d][" SOCKET_FSTR ",%T] " + "%s -> family: %d (%s)" + "\r\n", + __LINE__, desc_p->s, driver_caller(desc_p->port), __FUNCTION__, + ifa_p->ifa_addr->sa_family, + FAM2S(ifa_p->ifa_addr->sa_family)) ); + if (ifa_p->ifa_addr->sa_family == AF_INET #if defined(AF_INET6) || ifa_p->ifa_addr->sa_family == AF_INET6 @@ -6632,6 +6671,14 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, /* buf_p is now unreliable */ freeifaddrs(ifa_free_p); *rbuf_pp = buf_alloc_p; + + DDBG(desc_p, + ("INET-DRV-DBG[%d][" SOCKET_FSTR ",%T] " + "%s -> done when buffer size: %d" + "\r\n", + __LINE__, desc_p->s, driver_caller(desc_p->port), __FUNCTION__, + buf_size) ); + return buf_size; # undef BUF_ENSURE } diff --git a/erts/emulator/nifs/common/prim_net_nif.c b/erts/emulator/nifs/common/prim_net_nif.c index 3b27e90e3cc4..579e5c7b0e51 100644 --- a/erts/emulator/nifs/common/prim_net_nif.c +++ b/erts/emulator/nifs/common/prim_net_nif.c @@ -1610,6 +1610,8 @@ void encode_ifaddrs(ErlNifEnv* env, ERL_NIF_TERM eifAddrs; BOOLEAN_T extraAddr; // This is just for debugging... + NDBG( ("NET", "encode_ifaddrs -> entry\r\n") ); + 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); @@ -1618,6 +1620,7 @@ void encode_ifaddrs(ErlNifEnv* env, NDBG( ("NET", "encode_ifaddrs -> addr: " "\r\n %T" "\r\n", eaddr) ); + /* This is an ugly (OpenBSD?) hack... * "For some reason" the netmask family is set to 'AF_UNSPEC' * (when the addr family is AF_INET) on OpenBSD, @@ -1625,6 +1628,7 @@ void encode_ifaddrs(ErlNifEnv* env, * So force the family to AF_INET in this case to allow encoding * the netmask... */ + if ((ifap->ifa_addr != NULL) && (((ESockAddress*)ifap->ifa_addr)->sa.sa_family == AF_INET)) { if ((ifap->ifa_netmask != NULL) && @@ -1637,18 +1641,41 @@ void encode_ifaddrs(ErlNifEnv* env, "\r\n %T" "\r\n", enetmask) ); if (ifap->ifa_dstaddr && (ifap->ifa_flags & IFF_POINTOPOINT)) { + + NDBG( ("NET", "encode_ifaddrs -> try encode dest addr\r\n") ); + + /* What the eff is this fakery? */ + if (((ESockAddress*)ifap->ifa_dstaddr)->sa.sa_family == AF_UNSPEC) + ((ESockAddress*)ifap->ifa_dstaddr)->sa.sa_family = AF_INET; + extraAddr = TRUE; eifu_key = atom_dstaddr; eifu_value = encode_ifaddrs_addr(env, ifap->ifa_dstaddr); + + NDBG( ("NET", "encode_ifaddrs -> dest addr: " + "\r\n %T" + "\r\n", eifu_value) ); + } else if (ifap->ifa_broadaddr && (ifap->ifa_flags & IFF_BROADCAST)) { + + NDBG( ("NET", "encode_ifaddrs -> try encode broad addr\r\n") ); + extraAddr = TRUE; eifu_key = atom_broadaddr; eifu_value = encode_ifaddrs_addr(env, ifap->ifa_broadaddr); + + NDBG( ("NET", "encode_ifaddrs -> broad addr: " + "\r\n %T" + "\r\n", eifu_value) ); + } else { + extraAddr = FALSE; eifu_key = esock_atom_undefined; eifu_value = esock_atom_undefined; + } + if (extraAddr) { NDBG( ("NET", "encode_ifaddrs -> ifu: " "\r\n key: %T" diff --git a/erts/emulator/nifs/common/prim_socket_nif.c b/erts/emulator/nifs/common/prim_socket_nif.c index 6db99c5e6012..d6e0ca437979 100644 --- a/erts/emulator/nifs/common/prim_socket_nif.c +++ b/erts/emulator/nifs/common/prim_socket_nif.c @@ -1948,6 +1948,7 @@ static const struct in6_addr in6addr_loopback = * in the socket_int.h file! */ #define GLOBAL_ATOMS \ + GLOBAL_ATOM_DECL(6to4); \ GLOBAL_ATOM_DECL(abort); \ GLOBAL_ATOM_DECL(accept); \ GLOBAL_ATOM_DECL(acceptconn); \ @@ -1984,6 +1985,7 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(base_addr); \ GLOBAL_ATOM_DECL(bindtodevice); \ GLOBAL_ATOM_DECL(block_source); \ + GLOBAL_ATOM_DECL(bridge); \ GLOBAL_ATOM_DECL(broadcast); \ GLOBAL_ATOM_DECL(bsp_state); \ GLOBAL_ATOM_DECL(busy_poll); \ @@ -1995,6 +1997,7 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(cancel); \ GLOBAL_ATOM_DECL(cancelled); \ GLOBAL_ATOM_DECL(cantconfig); \ + GLOBAL_ATOM_DECL(cellular); \ GLOBAL_ATOM_DECL(chaos); \ GLOBAL_ATOM_DECL(checksum); \ GLOBAL_ATOM_DECL(close); \ @@ -2070,7 +2073,9 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(frelay); \ GLOBAL_ATOM_DECL(get_overlapped_result); \ GLOBAL_ATOM_DECL(get_peer_addr_info); \ + GLOBAL_ATOM_DECL(gif); \ GLOBAL_ATOM_DECL(hatype); \ + GLOBAL_ATOM_DECL(hdh1822); \ GLOBAL_ATOM_DECL(hdrincl); \ GLOBAL_ATOM_DECL(hmac_ident); \ GLOBAL_ATOM_DECL(hoplimit); \ @@ -2097,6 +2102,7 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(ipcomp_level); \ GLOBAL_ATOM_DECL(ipip); \ GLOBAL_ATOM_DECL(iplevel); \ + GLOBAL_ATOM_DECL(ipv4); \ GLOBAL_ATOM_DECL(ipv6); \ GLOBAL_ATOM_DECL(irq); \ GLOBAL_ATOM_DECL(i_want_mapped_v4_addr); \ @@ -2119,6 +2125,7 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(local); \ GLOBAL_ATOM_DECL(localtlk); \ GLOBAL_ATOM_DECL(local_auth_chunks); \ + GLOBAL_ATOM_DECL(loop); \ GLOBAL_ATOM_DECL(loopback); \ GLOBAL_ATOM_DECL(lowdelay); \ GLOBAL_ATOM_DECL(lower_up); \ @@ -2179,6 +2186,7 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(oobinline); \ GLOBAL_ATOM_DECL(options); \ GLOBAL_ATOM_DECL(origdstaddr); \ + GLOBAL_ATOM_DECL(other); \ GLOBAL_ATOM_DECL(otherhost); \ GLOBAL_ATOM_DECL(outgoing); \ GLOBAL_ATOM_DECL(packet); \ @@ -2197,7 +2205,8 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(port); \ GLOBAL_ATOM_DECL(portrange); \ GLOBAL_ATOM_DECL(portsel); \ - GLOBAL_ATOM_DECL(ppromisc); \ + GLOBAL_ATOM_DECL(ppromisc); \ + GLOBAL_ATOM_DECL(ppp); \ GLOBAL_ATOM_DECL(primary_addr); \ GLOBAL_ATOM_DECL(prim_file); \ GLOBAL_ATOM_DECL(priority); \ @@ -2285,6 +2294,7 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(staticarp); \ GLOBAL_ATOM_DECL(state); \ GLOBAL_ATOM_DECL(status); \ + GLOBAL_ATOM_DECL(stf); \ GLOBAL_ATOM_DECL(stream); \ GLOBAL_ATOM_DECL(syncnt); \ GLOBAL_ATOM_DECL(syn_rcvd); \ @@ -2330,6 +2340,8 @@ static const struct in6_addr in6addr_loopback = GLOBAL_ATOM_DECL(write_pkg); \ GLOBAL_ATOM_DECL(write_tries); \ GLOBAL_ATOM_DECL(write_waits); \ + GLOBAL_ATOM_DECL(x25ddn); \ + GLOBAL_ATOM_DECL(x25); \ GLOBAL_ATOM_DECL(zero) diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 18cc4b7a29ee..4a27c6d0f2de 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -205,6 +205,7 @@ typedef long ssize_t; */ #define GLOBAL_ATOM_DEFS \ + GLOBAL_ATOM_DEF(6to4); \ GLOBAL_ATOM_DEF(abort); \ GLOBAL_ATOM_DEF(accept); \ GLOBAL_ATOM_DEF(acceptconn); \ @@ -240,6 +241,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(base_addr); \ GLOBAL_ATOM_DEF(bindtodevice); \ GLOBAL_ATOM_DEF(block_source); \ + GLOBAL_ATOM_DEF(bridge); \ GLOBAL_ATOM_DEF(broadcast); \ GLOBAL_ATOM_DEF(bsp_state); \ GLOBAL_ATOM_DEF(busy_poll); \ @@ -251,6 +253,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(cancel); \ GLOBAL_ATOM_DEF(cancelled); \ GLOBAL_ATOM_DEF(cantconfig); \ + GLOBAL_ATOM_DEF(cellular); \ GLOBAL_ATOM_DEF(chaos); \ GLOBAL_ATOM_DEF(checksum); \ GLOBAL_ATOM_DEF(close); \ @@ -326,7 +329,9 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(frelay); \ GLOBAL_ATOM_DEF(get_overlapped_result); \ GLOBAL_ATOM_DEF(get_peer_addr_info); \ + GLOBAL_ATOM_DEF(gif); \ GLOBAL_ATOM_DEF(hatype); \ + GLOBAL_ATOM_DEF(hdh1822); \ GLOBAL_ATOM_DEF(hdrincl); \ GLOBAL_ATOM_DEF(hmac_ident); \ GLOBAL_ATOM_DEF(hoplimit); \ @@ -353,6 +358,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(ipcomp_level); \ GLOBAL_ATOM_DEF(ipip); \ GLOBAL_ATOM_DEF(iplevel); \ + GLOBAL_ATOM_DEF(ipv4); \ GLOBAL_ATOM_DEF(ipv6); \ GLOBAL_ATOM_DEF(irq); \ GLOBAL_ATOM_DEF(i_want_mapped_v4_addr); \ @@ -375,6 +381,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(local); \ GLOBAL_ATOM_DEF(localtlk); \ GLOBAL_ATOM_DEF(local_auth_chunks); \ + GLOBAL_ATOM_DEF(loop); \ GLOBAL_ATOM_DEF(loopback); \ GLOBAL_ATOM_DEF(lowdelay); \ GLOBAL_ATOM_DEF(lower_up); \ @@ -435,6 +442,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(oobinline); \ GLOBAL_ATOM_DEF(options); \ GLOBAL_ATOM_DEF(origdstaddr); \ + GLOBAL_ATOM_DEF(other); \ GLOBAL_ATOM_DEF(otherhost); \ GLOBAL_ATOM_DEF(outgoing); \ GLOBAL_ATOM_DEF(packet); \ @@ -449,11 +457,12 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(pktinfo); \ GLOBAL_ATOM_DEF(pktoptions); \ GLOBAL_ATOM_DEF(pkttype); \ - GLOBAL_ATOM_DEF(ppromisc); \ GLOBAL_ATOM_DEF(pointopoint); \ GLOBAL_ATOM_DEF(port); \ GLOBAL_ATOM_DEF(portrange); \ GLOBAL_ATOM_DEF(portsel); \ + GLOBAL_ATOM_DEF(ppromisc); \ + GLOBAL_ATOM_DEF(ppp); \ GLOBAL_ATOM_DEF(primary_addr); \ GLOBAL_ATOM_DEF(prim_file); \ GLOBAL_ATOM_DEF(priority); \ @@ -542,6 +551,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(state); \ GLOBAL_ATOM_DEF(status); \ GLOBAL_ATOM_DEF(staticarp); \ + GLOBAL_ATOM_DEF(stf); \ GLOBAL_ATOM_DEF(stream); \ GLOBAL_ATOM_DEF(syncnt); \ GLOBAL_ATOM_DEF(syn_rcvd); \ @@ -585,6 +595,8 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(write_pkg); \ GLOBAL_ATOM_DEF(write_tries); \ GLOBAL_ATOM_DEF(write_waits); \ + GLOBAL_ATOM_DEF(x25ddn); \ + GLOBAL_ATOM_DEF(x25); \ GLOBAL_ATOM_DEF(zero) diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 81aa018ce3f2..acd0c5f51250 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -44,6 +44,10 @@ #include #endif +#if defined(HAVE_NET_IF_DL_H) && defined(AF_LINK) +#include +#endif + #include "socket_int.h" #include "sys.h" #include "socket_util.h" @@ -146,6 +150,11 @@ static void make_sockaddr_dl(ErlNifEnv* env, static SOCKLEN_T sa_local_length(int l, struct sockaddr_un* sa); #endif +#if defined(HAVE_NET_IF_DL_H) && defined(AF_LINK) +static ERL_NIF_TERM esock_encode_if_type(ErlNifEnv* env, + unsigned int ifType); +#endif + /* *** esock_get_uint_from_map *** * @@ -1321,7 +1330,7 @@ void esock_encode_sockaddr_dl(ErlNifEnv* env, /* type - interface type */ UDBG( ("SUTIL", "esock_encode_sockaddr_dl -> type: %d" "\r\n", sockAddrP->sdl_type) ); - etype = MKUI(env, sockAddrP->sdl_type); + etype = esock_encode_if_type(env, sockAddrP->sdl_type); /* nlen - interface name length, no trailing 0 reqd. */ UDBG( ("SUTIL", "esock_encode_sockaddr_dl -> nlen: %d" @@ -1354,6 +1363,114 @@ void esock_encode_sockaddr_dl(ErlNifEnv* env, addrLen, esock_atom_link, eSockAddr); } } + + +static +ERL_NIF_TERM esock_encode_if_type(ErlNifEnv* env, + unsigned int ifType) +{ + ERL_NIF_TERM eIfType; + + switch (ifType) { +#if defined(IFT_OTHER) + case IFT_OTHER: + eIfType = esock_atom_other; + break; +#endif + +#if defined(IFT_HDH1822) + case IFT_HDH1822: + eIfType = esock_atom_hdh1822; + break; +#endif + +#if defined(IFT_X25DDN) + case IFT_X25DDN: + eIfType = esock_atom_x25ddn; + break; +#endif + +#if defined(IFT_X25) + case IFT_X25: + eIfType = esock_atom_x25; + break; +#endif + +#if defined(IFT_ETHER) + case IFT_ETHER: + eIfType = esock_atom_ether; + break; +#endif + +#if defined(IFT_PPP) + case IFT_PPP: + eIfType = esock_atom_ppp; + break; +#endif + +#if defined(IFT_LOOP) + case IFT_LOOP: + eIfType = esock_atom_loop; + break; +#endif + +#if defined(IFT_IPV4) + case IFT_IPV4: + eIfType = esock_atom_ipv4; + break; +#endif + +#if defined(IFT_IPV6) + case IFT_IPV6: + eIfType = esock_atom_ipv6; + break; +#endif + +#if defined(IFT_6TO4) + case IFT_6TO4: + eIfType = esock_atom_6to4; + break; +#endif + +#if defined(IFT_GIF) + case IFT_GIF: + eIfType = esock_atom_gif; + break; +#endif + +#if defined(IFT_FAITH) + case IFT_FAITH: + eIfType = esock_atom_faith; + break; +#endif + +#if defined(IFT_STF) + case IFT_STF: + eIfType = esock_atom_stf; + break; +#endif + +#if defined(IFT_BRIDGE) + case IFT_BRIDGE: + eIfType = esock_atom_bridge; + break; +#endif + +#if defined(IFT_CELLULAR) + case IFT_CELLULAR: + eIfType = esock_atom_cellular; + break; +#endif + + default: + eIfType = MKUI(env, ifType); + break; + } + + return eIfType; +} + + #endif diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 7f0ba07a9601..ba1d303c1f83 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -309,10 +309,11 @@ Function `parse_address/1` can be useful: %% Socket utility functions -export([ensure_sockaddr/1]). --export_type([address_family/0, socket_protocol/0, hostent/0, hostname/0, ip4_address/0, - ip6_address/0, ip_address/0, port_number/0, - family_address/0, local_address/0, - socket_address/0, returned_non_ip_address/0, +-export_type([socket_protocol/0, hostent/0, hostname/0, + address_family/0, ip4_address/0, ip6_address/0, ip_address/0, + port_number/0, + family_address/0, local_address/0, socket_address/0, + returned_non_ip_address/0, socket_setopt/0, socket_getopt/0, socket_optval/0, ancillary_data/0, posix/0, socket/0, inet_backend/0, stat_option/0]). @@ -331,8 +332,9 @@ Function `parse_address/1` can be useful: %% Two kinds of debug macros (depnds on what you need to debug) %% -define(DBG(T), erlang:display({{self(), ?MODULE, ?LINE, ?FUNCTION_NAME}, T})). -%% -define(DBG(F, A), io:format("~w -> " ++ F ++ "~n", [?FUNCTION_NAME | A])). +%% -define(DBG(F, A), io:format("~w(~w) -> " ++ F ++ "~n", [?FUNCTION_NAME, ?LINE | A])). %% -define(DBG(F), ?DBG(F, [])). +-define(DBG(F, A), ok). %%% --------------------------------- @@ -1719,125 +1721,192 @@ net_collect_ifopts([], _AllIfs, AllNameAndOpts) -> lists:reverse(AllNameAndOpts); net_collect_ifopts([IfName|IfNames], AllIfs, NameAndOpts) -> %% Get the Ifs with the name IfName + ?DBG("entry with" + "~n IfName: ~p", [IfName]), Ifs = [If || #{name := N} = If <- AllIfs, (N =:= IfName)], IfOpts = net_ifs2ifopts(Ifs), + ?DBG("collected for interface ~s:" + "~n ~p", [IfName, IfOpts]), net_collect_ifopts(IfNames, AllIfs, [{IfName, IfOpts}|NameAndOpts]). net_ifs2ifopts(Ifs) -> - net_ifs2ifopts(Ifs, #{flags => [], - addrs => [], - hwaddr => []}). - - -net_ifs2ifopts([], #{flags := Flags, - addrs := Addrs, - hwaddr := HwAddr}) -> - [{flags, net_flags_to_inet_flags(Flags)}] ++ - lists:reverse(Addrs) ++ - case HwAddr of + net_ifs2ifopts(Ifs, + %% Family: inet + #{flags => [], + addrs => []}, + %% Family: inet6 + #{flags => [], + addrs => []}, + %% Family: packet | link + #{flags => [], + addr => []}). + + +net_ifs2ifopts([], + %% Family: inet + #{flags := [], + addrs := []}, + %% Family: inet6 + #{flags := [], + addrs := []}, + %% Family: packet | link + #{flags := [], + addr := []}) -> + [{flags, []}]; +net_ifs2ifopts([], + %% Family: inet + #{flags := Flags4, + addrs := Addrs4}, + %% Family: inet6 + #{flags := Flags6, + addrs := Addrs6}, + %% Family: packet | link + #{flags := FlagsHw, + addr := AddrHw}) -> + ?DBG("entry when done with" + "~n Flags4: ~p" + "~n Addrs4: ~p" + "~n Flags6: ~p" + "~n Addrs6: ~p" + "~n FlagsHw: ~p" + "~n AddrHw: ~p", + [Flags4, Addrs4, Flags6, Addrs6, FlagsHw, AddrHw]), + case {Flags4, Addrs4} of + {[], []} -> + []; + _ -> + [{flags, net_flags_to_inet_flags(Flags4)}] ++ + lists:reverse(Addrs4) + end ++ + case Addrs6 of [] -> []; _ -> - [{hwaddr, HwAddr}] - end; -net_ifs2ifopts([If|Ifs], #{flags := []} = IfOpts0) -> - IfOpts = - case If of - %% LINK or PACKET - %% - On some platforms LINK is used (FreeBSD for instance) - %% LINK does not include an explicit HW address. Instead - %% its part of the 'data', together with name and possibly - %% link layer selector (the lengths can be used to decode - %% the data).. - %% - On others PACKET is used. - #{flags := Flags, - addr := #{family := packet, - addr := HwAddrBin}} -> - IfOpts0#{flags => Flags, - hwaddr => binary_to_list(HwAddrBin)}; - #{flags := Flags, - addr := #{family := link, - nlen := NLen, - alen := ALen, - data := Data}} when (ALen > 0) -> - case Data of - <<_:NLen/binary, ABin:ALen/binary, _/binary>> -> - IfOpts0#{flags => Flags, - hwaddr => binary_to_list(ABin)}; - _ -> - IfOpts0#{flags => Flags} - end; - #{flags := Flags, - addr := #{family := Fam, - addr := Addr}, - netmask := #{family := Fam, - addr := Mask}} when (Fam =:= inet) orelse - (Fam =:= inet6) -> - %% We may also have broadcast or dest addr - BroadAddr = case maps:get(broadaddr, If, undefined) of - undefined -> - []; - #{addr := BA} -> - [{broadaddr, BA}] - end, - DstAddr = case maps:get(dstaddr, If, undefined) of - undefined -> - []; - #{addr := DA} -> - [{dstaddr, DA}] - end, - IfOpts0#{flags => Flags, - addrs => DstAddr ++ BroadAddr ++ [{netmask, Mask}, - {addr, Addr}]}; - #{flags := Flags} -> - IfOpts0#{flags => Flags} - end, - net_ifs2ifopts(Ifs, IfOpts); -net_ifs2ifopts([If|Ifs], IfOpts0) -> - %% We can only have one 'flags' entry - %% (they are supposed to be the same for all if:s of the same name). - %% For each 'addr' entry we can have one 'netmask' and 'broadcast' - %% or 'dstaddr' - IfOpts = - case If of - #{flags := Flags, - addr := #{family := packet, - addr := HwAddrBin}} -> - Flags0 = maps:get(flags, IfOpts0, []), - IfOpts0#{flags => Flags0 ++ (Flags -- Flags0), - hwaddr => binary_to_list(HwAddrBin)}; - #{flags := Flags, - addr := #{family := Fam, - addr := Addr}, - netmask := #{family := Fam, - addr := Mask}} when (Fam =:= inet) orelse - (Fam =:= inet6) -> - Addrs0 = maps:get(addrs, IfOpts0, []), - Flags0 = maps:get(flags, IfOpts0, []), - %% We may also have broadcast or dest addr - BroadAddr = case maps:get(broadaddr, If, undefined) of - undefined -> - []; - #{addr := BA} -> - [{broadaddr, BA}] - end, - DstAddr = case maps:get(dstaddr, If, undefined) of - undefined -> - []; - #{addr := DA} -> - [{dstaddr, DA}] - end, - IfOpts0#{flags => Flags0 ++ (Flags -- Flags0), - addrs => - DstAddr ++ - BroadAddr ++ - [{netmask, Mask}, - {addr, Addr}] ++ - Addrs0}; + case Flags6 of + Flags4 -> + lists:reverse(Addrs6); + [] -> + lists:reverse(Addrs6); + _ -> + [{flags, net_flags_to_inet_flags(Flags6)}] ++ + lists:reverse(Addrs6) + end + end ++ + case {FlagsHw, AddrHw} of + {[], []} -> + []; + {[], _} -> + [{hwaddr, AddrHw}]; + {_, _} when ((FlagsHw =:= Flags4) orelse + (FlagsHw =:= Flags6)) andalso (AddrHw =/= []) -> + [{hwaddr, AddrHw}]; _ -> - IfOpts0 - end, - net_ifs2ifopts(Ifs, IfOpts). + [{flags, net_flags_to_inet_flags(FlagsHw)}] ++ + [{hwaddr, AddrHw}] + end; +net_ifs2ifopts([If|Ifs], IfOpts4_0, IfOpts6_0, IfOptsHw_0) -> + case If of + %% LINK or PACKET + %% - On some platforms LINK is used (FreeBSD for instance) + %% LINK does not include an explicit HW address. Instead + %% its part of the 'data', together with name and possibly + %% link layer selector (the lengths can be used to decode + %% the data).. + %% - On others PACKET is used. + #{flags := Flags, + addr := #{family := packet, + addr := HwAddrBin}} -> + %% This should only come once (per interface) so we + %% do not actually check... + ?DBG("packet entry:" + "~n Flags: ~p" + "~n HwAddrBin: ~p", [Flags, HwAddrBin]), + IfOptsHw = + IfOptsHw_0#{flags => Flags, + addr => binary_to_list(HwAddrBin)}, + net_ifs2ifopts(Ifs, IfOpts4_0, IfOpts6_0, IfOptsHw); + #{flags := Flags, + addr := #{family := link, + nlen := NLen, + alen := ALen, + data := Data}} when (ALen > 0) -> + ?DBG("link entry:" + "~n Flags: ~p" + "~n NLen: ~p" + "~n ALen: ~p" + "~n Data: ~p", [Flags, NLen, ALen, Data]), + IfOptsHw = + case Data of + <<_:NLen/binary, ABin:ALen/binary, _/binary>> -> + IfOptsHw_0#{flags => Flags, + addr => binary_to_list(ABin)}; + _ -> + IfOptsHw_0#{flags => Flags} + end, + net_ifs2ifopts(Ifs, IfOpts4_0, IfOpts6_0, IfOptsHw); + + #{flags := Flags, + addr := #{family := Fam, + addr := Addr}, + netmask := #{family := Fam, + addr := Mask}} when (Fam =:= inet) orelse + (Fam =:= inet6) -> + %% We may also have broadcast or dest addr + BroadAddr = case maps:get(broadaddr, If, undefined) of + undefined -> + []; + #{addr := BA} -> + [{broadaddr, BA}] + end, + DstAddr = case maps:get(dstaddr, If, undefined) of + undefined -> + []; + #{addr := DA} -> + [{dstaddr, DA}] + end, + ?DBG("~w entry:" + "~n Flags: ~p" + "~n Addr: ~p" + "~n Mask: ~p" + "~n Broad Addr: ~p" + "~n Dest Addr: ~p", + [Fam, Flags, Addr, Mask, BroadAddr, DstAddr]), + case Fam of + inet -> + Flags4_0 = maps:get(flags, IfOpts4_0, []), + Flags4 = Flags4_0 ++ (Flags -- Flags4_0), + Addrs4_0 = maps:get(addrs, IfOpts4_0, []), + IfOpts4 = + IfOpts4_0#{flags => Flags4, + addrs => + DstAddr ++ + BroadAddr ++ + [{netmask, Mask}, + {addr, Addr}] ++ + Addrs4_0}, + net_ifs2ifopts(Ifs, IfOpts4, IfOpts6_0, IfOptsHw_0); + inet6 -> + Flags6_0 = maps:get(flags, IfOpts6_0, []), + Flags6 = Flags6_0 ++ (Flags -- Flags6_0), + Addrs6_0 = maps:get(addrs, IfOpts6_0, []), + IfOpts6 = + IfOpts6_0#{flags => Flags6, + addrs => + DstAddr ++ + BroadAddr ++ + [{netmask, Mask}, + {addr, Addr}] ++ + Addrs6_0}, + net_ifs2ifopts(Ifs, IfOpts4_0, IfOpts6, IfOptsHw_0) + end; + + #{flags := Flags} -> + ?DBG("other entry => retain flags" + "~n ~p", [If]), + %% Reuse the IPv4 opts + net_ifs2ifopts(Ifs, + IfOpts4_0#{flags => Flags}, IfOpts6_0, IfOptsHw_0) + end. net_flags_to_inet_flags(Flags) -> net_flags_to_inet_flags(Flags, []). diff --git a/lib/kernel/src/net.erl b/lib/kernel/src/net.erl index 494774e24d4e..5804408bd57c 100644 --- a/lib/kernel/src/net.erl +++ b/lib/kernel/src/net.erl @@ -461,7 +461,10 @@ getifaddrs_filter_map_hwaddr() -> getifaddrs_filter(#{family := FFamily, flags := FFlags} = _FilterMap, #{addr := #{family := EFamily}, flags := EFlags} = _Entry) when (FFamily =:= default) andalso - ((EFamily =:= inet) orelse (EFamily =:= inet6)) -> + ((EFamily =:= inet) orelse + (EFamily =:= inet6) orelse + (EFamily =:= link) orelse + (EFamily =:= packet)) -> getifaddrs_filter_flags(FFlags, EFlags); getifaddrs_filter(#{family := FFamily, flags := FFlags} = _FilterMap, #{addr := #{family := EFamily}, flags := EFlags} = _Entry) diff --git a/lib/kernel/src/socket.erl b/lib/kernel/src/socket.erl index c75dd2d68f30..998862bfd1d4 100644 --- a/lib/kernel/src/socket.erl +++ b/lib/kernel/src/socket.erl @@ -319,6 +319,7 @@ server(Addr, Port) -> sockaddr_unspec/0, sockaddr_native/0, + interface_type/0, msg_flag/0, level/0, @@ -754,6 +755,15 @@ C: `struct sockaddr_ll` hatype := hatype(), addr := binary()}. +-doc """ +The interface type (of the datalink). We only translate a few values to atoms, +the rest are left as (unsigned) integer values. +""". +-type interface_type() :: 'other' | 'hdh1822' | 'x25ddh' | 'x25' | 'ether' | + 'ppp' | 'loop' | 'ipv4' | 'ipv6' | '6to4' | + 'gif' | 'faith' | 'stf' | 'bridge' | 'cellular' | + non_neg_integer(). + -doc """ C: `struct sockaddr_dl` @@ -762,7 +772,7 @@ Link level address (PF_LINK) on BSD:s. -type sockaddr_dl() :: #{family := 'link', index := non_neg_integer(), - type := non_neg_integer(), + type := interface_type(), nlen := non_neg_integer(), alen := non_neg_integer(), slen := non_neg_integer(), diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 8c753f20d61b..912e166cc248 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -1895,6 +1895,8 @@ check_addr(Addr) -> ok = gen_tcp:close(L). ifaddrs(IfOpts) -> + ?P("~w(~w) -> entry with" + "~n IfOpts: ~p", [?FUNCTION_NAME, ?LINE, IfOpts]), IfMap = collect_ifopts(IfOpts), ChkFun = fun Self({{_,Flags} = Key, Opts}, ok) -> @@ -1936,8 +1938,14 @@ collect_ifopts(IfOpts) -> collect_ifopts(IfOpts, #{}). %% collect_ifopts(IfOpts, IfMap) -> + ?P("~w(~w) -> entry with" + "~n IfMap: ~p", [?FUNCTION_NAME, ?LINE, IfMap]), case IfOpts of [{If,[{flags,Flags}|Opts]}|IfOs] -> + ?P("~w(~w) -> found: " + "~n If: ~p" + "~n Flags: ~p" + "~n Opts: ~p", [?FUNCTION_NAME, ?LINE, If, Flags, Opts]), Key = {If,Flags}, case maps:is_key(Key, IfMap) of true -> @@ -1948,7 +1956,9 @@ collect_ifopts(IfOpts, IfMap) -> [] -> IfMap; _ -> - ct:fail({unexpected_ifopts,IfOpts,IfMap}) + ?P("~w(~w) -> invalid: " + "~n IfOpts: ~p", [?FUNCTION_NAME, ?LINE, IfOpts]), + ct:fail({unexpected_ifopts, IfOpts, IfMap}) end. %% collect_ifopts(IfOpts, IfMap, Opts, Key, R) -> @@ -2067,10 +2077,71 @@ getifaddrs_verify_backends3([{I_IF, _}|_], [{S_IF, _}|_]) -> %% (inet and net versions) because the information %% comes from several sources. And the processing %% is different on inet and net. -%% The info always starts with 'flags' and ends with +%% The info allways starts with 'flags' and ends with %% 'hwaddr'. So deal with those two first and the rest %% can compared after. +%% getifaddrs_verify_backend(IF, +%% [], +%% []) -> +%% io:format("done => " +%% "~n backend(s) equal (enough) for ~p~n", [IF]), +%% ok; +%% getifaddrs_verify_backend(IF, +%% [{flags, Flags}, {hwaddr, HwAddr}], +%% [{flags, Flags}, {hwaddr, HwAddr}]) -> +%% io:format("done when hwaddr (and flags) equal => " +%% "~n backend(s) equal (enough) for ~p~n", [IF]), +%% ok; +%% getifaddrs_verify_backend(IF, +%% [{flags, []}, {hwaddr, HwAddr}], +%% [{flags, Flags}, {hwaddr, HwAddr}]) +%% when (Flags =/= []) -> +%% io:format("done when hwaddr (inet flags empty and socket flags non-empty) " +%% "equal enough => " +%% "~n backend(s) equal (enough) for ~p~n", [IF]), +%% ok; +%% getifaddrs_verify_backend(IF, +%% [{flags, []}, {hwaddr, HwAddr}], +%% [{hwaddr, HwAddr}]) -> +%% io:format("done when hwaddr (inet flags empty and no socket flags) " +%% "equal => " +%% "~n backend(s) equal (enough) for ~p~n", [IF]), +%% ok; +%% getifaddrs_verify_backend(IF, +%% [{hwaddr, HwAddr}], +%% [{hwaddr, HwAddr}]) -> +%% io:format("done when hwaddr equal => " +%% "~n backend(s) equal (enough) for ~p~n", [IF]), +%% ok; + +%% getifaddrs_verify_backend(IF, +%% [{flags, Flags} | I_Info], +%% [{flags, Flags} | S_Info]) -> +%% io:format("flags are equal for ~p => continue~n", [IF]), +%% getifaddrs_verify_backend(IF, I_Info, S_Info); + +%% getifaddrs_verify_backend( +%% IF, +%% [{addr, Addr}, {netmask, Mask}, {broadaddr, BAddr} | I_Info], +%% [{addr, Addr}, {netmask, Mask}, {broadaddr, BAddr} | S_Info]) -> +%% io:format("addr, netmask and bradcast addr equal for ~p => contunue: " +%% "~n Addr: ~p" +%% "~n Mask: ~p" +%% "~n BAddr: ~p" +%% "~n", [IF, Addr, Mask, BAddr]), +%% getifaddrs_verify_backend(IF, I_Info, S_Info); +%% getifaddrs_verify_backend( +%% IF, +%% [{addr, Addr}, {netmask, Mask}, {broadaddr, BAddr} | I_Info], +%% S_Info0) -> +%% io:format("addr, netmask and bradcast addr equal for ~p => contunue: " +%% "~n Addr: ~p" +%% "~n Mask: ~p" +%% "~n BAddr: ~p" +%% "~n", [IF, Addr, Mask, BAddr]), +%% getifaddrs_verify_backend(IF, I_Info, S_Info); + getifaddrs_verify_backend(IF, I_INFO, S_INFO) -> {I_Rest1, S_Rest1} = case {I_INFO, S_INFO} of @@ -2131,6 +2202,26 @@ getifaddrs_verify_backend(IF, I_INFO, S_INFO) -> end, {I_Rest2, S_Rest2} = case {lists:reverse(I_Rest1), lists:reverse(S_Rest1)} of + {[{hwaddr, HWADDR}, {flags, I_Flags2}|IR2], + [{hwaddr, HWADDR}, {flags, S_Flags2}|SR2]} + when I_Flags2 =:= S_Flags2 -> + io:format("hwaddr for ~p *is* equal (and flags are also equal)~n", + [IF]), + {lists:reverse(IR2), lists:reverse(SR2)}; + {[{hwaddr, HWADDR}, {flags, I_Flags2}|_IR2], + [{hwaddr, HWADDR}, {flags, S_Flags2}|_SR2]} + when I_Flags2 =/= S_Flags2 -> + io:format("hwaddr for ~p *is* equal" + "~n But flags not equal: " + "~n Inet Flags: ~p" + "~n Sock Flags: ~p" + "~n", + [IF, I_Flags2, S_Flags2]), + ct:fail(ifaddrs_equal_but_flags_are_not); + {[{hwaddr, HWADDR}, {flags, []}|IR2], [{hwaddr, HWADDR}|SR2]} -> + io:format("hwaddr for ~p *is* equal " + "(empty flags on inet - ignore)~n", [IF]), + {lists:reverse(IR2), lists:reverse(SR2)}; {[{hwaddr, HWADDR}|IR2], [{hwaddr, HWADDR}|SR2]} -> io:format("hwaddr for ~p *is* equal~n", [IF]), {lists:reverse(IR2), lists:reverse(SR2)}; diff --git a/lib/kernel/test/socket_traffic_SUITE.erl b/lib/kernel/test/socket_traffic_SUITE.erl index 215bcdf302f4..28617362909a 100644 --- a/lib/kernel/test/socket_traffic_SUITE.erl +++ b/lib/kernel/test/socket_traffic_SUITE.erl @@ -5367,6 +5367,9 @@ traffic_ping_pong_send_and_receive_stream2(InitState) -> ?SEV_IPRINT("remote client bind failure:" "~n ~p", [Reason]), {skip, Reason}; + {error, no_address = Reason} -> + ?SEV_IPRINT("remote valid address"), + {skip, Reason}; {error, Reason} = ERROR -> ?SEV_EPRINT("remote client failure:" "~n ~p", [Reason]), @@ -7039,14 +7042,6 @@ is_slow_ubuntu(Config) -> end. -is_not_solaris() -> - case os:type() of - {unix, solaris} -> - skip("Solaris"); - _ -> - ok - end. - is_not_windows() -> case os:type() of {win32, nt} -> @@ -7068,7 +7063,7 @@ has_support_sctp() -> {win32, _} -> skip("Not supported"); {unix, netbsd} -> - %% XXX We will have to investigate this later... + %% XYZ We will have to investigate this later... skip("Not supported"); _ -> case socket:is_supported(sctp) of