From 5d9de1cdc46c75117f15f1ab17f017cdb700eb4c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 11:28:49 +0200 Subject: [socket-nif] Add support for socket (level ip) option msfilter Added support for ip level socket option MSFILTER. This option has not been tested *in any way*... OTP-14831 --- erts/doc/src/socket.xml | 6 ++ erts/doc/src/socket_usage.xml | 7 ++ erts/emulator/nifs/common/socket_nif.c | 173 ++++++++++++++++++++++++++++++++- erts/preloaded/ebin/socket.beam | Bin 58080 -> 59008 bytes erts/preloaded/src/socket.erl | 40 +++++++- 5 files changed, 219 insertions(+), 7 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml index 14a21823b8..53d1516f1e 100644 --- a/erts/doc/src/socket.xml +++ b/erts/doc/src/socket.xml @@ -131,6 +131,12 @@ + + + + + + diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml index 30b855f925..e7e27629ac 100644 --- a/erts/doc/src/socket_usage.xml +++ b/erts/doc/src/socket_usage.xml @@ -302,6 +302,13 @@ yes type = raw + + msfilter + null | ip_msfilter() + yes + no + none + mtu integer() diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 3f87232000..ae14e6b4ab 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -521,6 +521,7 @@ typedef union { #define SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP 6 #define SOCKET_OPT_IP_FREEBIND 7 #define SOCKET_OPT_IP_MINTTL 9 +#define SOCKET_OPT_IP_MSFILTER 10 #define SOCKET_OPT_IP_MTU 11 #define SOCKET_OPT_IP_MTU_DISCOVER 12 #define SOCKET_OPT_IP_MULTICAST_ALL 13 @@ -1114,6 +1115,18 @@ static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_MSFILTER) +static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env, + ERL_NIF_TERM eVal, + uint32_t* mode); +static ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env, + SOCKET sock, + struct ip_msfilter* msfP, + SOCKLEN_T optLen); +#endif #if defined(IP_MTU_DISCOVER) static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, SocketDescriptor* descP, @@ -2058,13 +2071,15 @@ static char str_cookie_life[] = "cookie_life"; static char str_data_in[] = "data_in"; static char str_do[] = "do"; static char str_dont[] = "dont"; +static char str_exclude[] = "exclude"; static char str_false[] = "false"; static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; -static char str_iow[] = "iow"; +static char str_include[] = "include"; static char str_initial[] = "initial"; static char str_interface[] = "interface"; +static char str_iow[] = "iow"; static char str_local_rwnd[] = "local_rwnd"; // static char str_loopback[] = "loopback"; static char str_max[] = "max"; @@ -2073,8 +2088,10 @@ static char str_max_init_timeo[] = "max_init_timeo"; static char str_max_instreams[] = "max_instreams"; static char str_max_rxt[] = "max_rxt"; static char str_min[] = "min"; +static char str_mode[] = "mode"; static char str_multiaddr[] = "multiaddr"; static char str_nif_abort[] = "nif_abort"; +static char str_null[] = "null"; static char str_num_dlocal[] = "num_domain_local"; static char str_num_dinet[] = "num_domain_inet"; static char str_num_dinet6[] = "num_domain_inet6"; @@ -2097,6 +2114,7 @@ static char str_select[] = "select"; static char str_sender_dry[] = "sender_dry"; static char str_send_failure[] = "send_failure"; static char str_shutdown[] = "shutdown"; +static char str_slist[] = "slist"; static char str_sourceaddr[] = "sourceaddr"; static char str_timeout[] = "timeout"; static char str_true[] = "true"; @@ -2170,13 +2188,15 @@ static ERL_NIF_TERM atom_cookie_life; static ERL_NIF_TERM atom_data_in; static ERL_NIF_TERM atom_do; static ERL_NIF_TERM atom_dont; +static ERL_NIF_TERM atom_exclude; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; -static ERL_NIF_TERM atom_iow; +static ERL_NIF_TERM atom_include; static ERL_NIF_TERM atom_initial; static ERL_NIF_TERM atom_interface; +static ERL_NIF_TERM atom_iow; static ERL_NIF_TERM atom_local_rwnd; static ERL_NIF_TERM atom_max; static ERL_NIF_TERM atom_max_attempts; @@ -2184,8 +2204,10 @@ static ERL_NIF_TERM atom_max_init_timeo; static ERL_NIF_TERM atom_max_instreams; static ERL_NIF_TERM atom_max_rxt; static ERL_NIF_TERM atom_min; +static ERL_NIF_TERM atom_mode; static ERL_NIF_TERM atom_multiaddr; static ERL_NIF_TERM atom_nif_abort; +static ERL_NIF_TERM atom_null; static ERL_NIF_TERM atom_num_dinet; static ERL_NIF_TERM atom_num_dinet6; static ERL_NIF_TERM atom_num_dlocal; @@ -2208,6 +2230,7 @@ static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_sender_dry; static ERL_NIF_TERM atom_send_failure; static ERL_NIF_TERM atom_shutdown; +static ERL_NIF_TERM atom_slist; static ERL_NIF_TERM atom_sourceaddr; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; @@ -4934,6 +4957,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MSFILTER) + case SOCKET_OPT_IP_MSFILTER: + result = nsetopt_lvl_ip_msfilter(env, descP, eVal); + break; +#endif + #if defined(IP_MTU_DISCOVER) case SOCKET_OPT_IP_MTU_DISCOVER: result = nsetopt_lvl_ip_mtu_discover(env, descP, eVal); @@ -5178,6 +5207,139 @@ ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, +/* nsetopt_lvl_ip_msfilter - Level IP MSFILTER option + * + * The value can be *either* the atom 'null' or a map of type ip_msfilter(). + */ +#if defined(IP_MSFILTER) +static +ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + + if (COMPARE(eVal, atom_null) == 0) { + return nsetopt_lvl_ip_msfilter_set(env, descP->sock, NULL, 0); + } else { + struct ip_msfilter* msfP; + uint32_t msfSz; + ERL_NIF_TERM eMultiAddr, eInterface, eFMode, eSList, elem, tail; + size_t sz; + unsigned int slistLen, idx; + + if (!IS_MAP(env, eVal)) + return esock_make_error(env, esock_atom_einval); + + // It must have atleast four attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz < 4)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_mode, &eFMode)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_slist, &eSList)) + return esock_make_error(env, esock_atom_einval); + + /* We start (decoding) with the slist, since without it we don't + * really know how much (memory) to allocate. + */ + if (!GET_LIST_LEN(env, eSList, &slistLen)) + return esock_make_error(env, esock_atom_einval); + + msfSz = IP_MSFILTER_SIZE(slistLen); + msfP = MALLOC(msfSz); + + if (!esock_decode_ip4_address(env, eMultiAddr, &msfP->imsf_multiaddr)) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } + + if (!esock_decode_ip4_address(env, eInterface, &msfP->imsf_interface)) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } + + if (!decode_ip_msfilter_mode(env, eFMode, &msfP->imsf_fmode)) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } + + /* And finally, extract the source addresses */ + msfP->imsf_numsrc = slistLen; + for (idx = 0; idx < slistLen; idx++) { + if (GET_LIST_ELEM(env, eSList, &elem, &tail)) { + if (!esock_decode_ip4_address(env, elem, &msfP->imsf_slist[idx])) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } else { + eSList = tail; + } + } + } + + /* And now, finally, set the option */ + result = nsetopt_lvl_ip_msfilter_set(env, descP->sock, msfP, msfSz); + FREE(msfP); + return result; + } + +} + + +static +BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env, + ERL_NIF_TERM eVal, + uint32_t* mode) +{ + BOOLEAN_T result; + + if (COMPARE(eVal, atom_include) == 0) { + *mode = MCAST_INCLUDE; + result = TRUE; + } else if (COMPARE(eVal, atom_exclude) == 0) { + *mode = MCAST_EXCLUDE; + result = TRUE; + } else { + result = FALSE; + } + + return result; +} + + +static +ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env, + SOCKET sock, + struct ip_msfilter* msfP, + SOCKLEN_T optLen) +{ + ERL_NIF_TERM result; + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + res = socket_setopt(sock, level, IP_MSFILTER, (void*) msfP, optLen); + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + return result; +} +#endif // IP_MSFILTER + + + /* nsetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option * * The value is an atom of the type ip_pmtudisc(). @@ -11441,13 +11603,15 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_data_in = MKA(env, str_data_in); atom_do = MKA(env, str_do); atom_dont = MKA(env, str_dont); + atom_exclude = MKA(env, str_exclude); atom_false = MKA(env, str_false); atom_global_counters = MKA(env, str_global_counters); atom_in4_sockaddr = MKA(env, str_in4_sockaddr); atom_in6_sockaddr = MKA(env, str_in6_sockaddr); - atom_iow = MKA(env, str_iow); + atom_include = MKA(env, str_include); atom_initial = MKA(env, str_initial); atom_interface = MKA(env, str_interface); + atom_iow = MKA(env, str_iow); atom_local_rwnd = MKA(env, str_local_rwnd); atom_max = MKA(env, str_max); atom_max_attempts = MKA(env, str_max_attempts); @@ -11455,8 +11619,10 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_max_instreams = MKA(env, str_max_instreams); atom_max_rxt = MKA(env, str_max_rxt); atom_min = MKA(env, str_min); + atom_mode = MKA(env, str_mode); atom_multiaddr = MKA(env, str_multiaddr); atom_nif_abort = MKA(env, str_nif_abort); + atom_null = MKA(env, str_null); atom_num_dinet = MKA(env, str_num_dinet); atom_num_dinet6 = MKA(env, str_num_dinet6); atom_num_dlocal = MKA(env, str_num_dlocal); @@ -11479,6 +11645,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_sender_dry = MKA(env, str_sender_dry); atom_send_failure = MKA(env, str_send_failure); atom_shutdown = MKA(env, str_shutdown); + atom_slist = MKA(env, str_slist); atom_sourceaddr = MKA(env, str_sourceaddr); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam index 8ca75307bc..14ee41257b 100644 Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 5695e45ac5..acda4e3811 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -94,6 +94,8 @@ ip_mreq/0, ip_mreq_source/0, ip_pmtudisc/0, + ip_msfilter_mode/0, + ip_msfilter/0, ipv6_mreq/0, ipv6_pmtudisc/0, sctp_event_subscribe/0, @@ -196,6 +198,17 @@ -type ip_pmtudisc() :: want | dont | do | probe. +%% multiaddr: Multicast group address +%% interface: Address of local interface +%% mode: Filter mode +%% slist: List of source addresses +-type ip_msfilter_mode() :: include | exclude. + +-type ip_msfilter() :: #{multiaddr => ip4_address(), + interface => ip4_address(), + mode => ip_msfilter_mode(), + slist => [ip4_address()]}. + -type ipv6_mreq() :: #{multiaddr := ip6_address(), interface := non_neg_integer()}. @@ -604,13 +617,13 @@ -define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1). -define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2). -define(SOCKET_OPT_IP_BLOCK_SOURCE, 3). -%% -define(SOCKET_OPT_IP_DONTFRAG, 4). +%% -define(SOCKET_OPT_IP_DONTFRAG, 4). % FreeBSD -define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5). -define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6). -define(SOCKET_OPT_IP_FREEBIND, 7). %% -define(SOCKET_OPT_IP_HDRINCL, 8). -define(SOCKET_OPT_IP_MINTTL, 9). -%% -define(SOCKET_OPT_IP_MSFILTER, 10). +-define(SOCKET_OPT_IP_MSFILTER, 10). -define(SOCKET_OPT_IP_MTU, 11). -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). @@ -2197,6 +2210,18 @@ enc_setopt_value(ip, freebind, V, _D, _T, _P) when is_boolean(V) -> V; enc_setopt_value(ip, minttl, V, _D, _T, _P) when is_integer(V) -> V; +enc_setopt_value(ip, msfilter, null = V, _D, _T, _P) -> + V; +enc_setopt_value(ip, msfilter, #{multiaddr := MA, + interface := IF, + fmode := FMode, + slist := SL} = V, _D, _T, _P) + when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso + (is_tuple(IF) andalso (size(IF) =:= 4)) andalso + ((FMode =:= include) orelse (FMode =:= exclude)) andalso + is_list(SL) -> + ensure_ip_msfilter_slist(SL), + V; enc_setopt_value(ip, mtu_discover, V, _D, _T, _P) when (V =:= want) orelse (V =:= dont) orelse @@ -2656,8 +2681,8 @@ enc_sockopt_key(ip = L, hdrincl = Opt, _Dir, _D, raw = _T, _P) -> %% FreeBSD only? enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) -> ?SOCKET_OPT_IP_MINTTL; -enc_sockopt_key(ip = L, msfilter = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, msfilter = _Opt, set = _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MSFILTER; enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MTU; enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) -> @@ -2922,6 +2947,13 @@ enc_shutdown_how(read_write) -> %% %% =========================================================================== +ensure_ip_msfilter_slist(SL) -> + EnsureSA = fun(SA) when is_tuple(SA) andalso (size(SA) =:= 4) -> ok; + (_) -> einval() + end, + lists:foreach(EnsureSA, SL). + + ensure_sockaddr(#{family := inet} = SockAddr) -> maps:merge(?SOCKADDR_IN4_DEFAULTS, SockAddr); ensure_sockaddr(#{family := inet6} = SockAddr) -> -- cgit v1.2.3