From 0ab2ce1aad7596e81c1c59f71ccb3b2ad4f7f070 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 10:16:50 +0200 Subject: [socket-nif] Network interface mappings and sock addr Add functions for mapping network interface names and indexes. Also refined the types for socket addresses: in4_sockaddr and in6_sockaddr. --- erts/emulator/nifs/common/socket_nif.c | 209 +++++++++++++++++++++++++++++++++ erts/preloaded/src/socket.erl | 91 ++++++++++++-- 2 files changed, 293 insertions(+), 7 deletions(-) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index b01f26e4d4..f4d2fbf021 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -371,6 +371,7 @@ typedef union { #define MKA(E,S) enif_make_atom((E), (S)) #define MKBIN(E,B) enif_make_binary((E), (B)) #define MKI(E,I) enif_make_int((E), (I)) +#define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) #define MKREF(E) enif_make_ref((E)) #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) @@ -404,6 +405,8 @@ typedef union { enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) #define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_STR(E, L, B, SZ) \ + enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) @@ -700,6 +703,15 @@ static ERL_NIF_TERM nif_setopt(ErlNifEnv* env, static ERL_NIF_TERM nif_getopt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -778,6 +790,12 @@ static ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, int level, int opt, SocketOptValue* valP); +static ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, + char* ifn); +static ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, + unsigned int id); +static ERL_NIF_TERM nlink_ifs(ErlNifEnv* env); +static unsigned int nlink_ifs_length(struct if_nameindex* p); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -1123,6 +1141,12 @@ static SocketData socketData; * nif_setopt/3 * nif_getopt/2 * + * And some utility functions: + * ------------------------------------------------------------- + * nif_link_if2idx/1 + * nif_link_idx2if/1 + * nif_link_ifs/0 + * * And some socket admin functions: * ------------------------------------------------------------- * nif_cancel(Sock, Ref) @@ -3663,6 +3687,186 @@ int socket_setopt(int sock, int level, int opt, } + +/* ---------------------------------------------------------------------- + * nif_link_if2idx + * + * Description: + * Perform a Interface Name to Interface Index translation. + * + * Arguments: + * Ifn - Interface name to be translated. + */ + +static +ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eifn; + char ifn[IF_NAMESIZE+1]; + + if (argc != 1) { + return enif_make_badarg(env); + } + eifn = argv[0]; + + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) + return make_error2(env, atom_einval); + + return nlink_if2idx(env, ifn); +} + + + +static +ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, + char* ifn) +{ + unsigned int idx = if_nametoindex(ifn); + + if (idx == 0) + return make_error2(env, sock_errno()); + else + return make_ok2(env, idx); + +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Perform a Interface Index to Interface Name translation. + * + * Arguments: + * Idx - Interface index to be translated. + */ + +static +ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + unsigned int idx; + + if ((argc != 1) || + !GET_UINT(env, argv[0], &idx)) { + return enif_make_badarg(env); + } + + return nlink_idx2if(env, idx); +} + + + +static +ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, + unsigned int idx) +{ + ERL_NIF_TERM result; + char* ifn = MALLOC(IF_NAMESIZE+1); + + if (ifn == NULL) + return enif_make_badarg(env); // PLACEHOLDER + + if (NULL == if_indextoname(idx, ifn)) { + result = make_ok2(env, MKS(env, ifn)); + } else { + result = make_error2(env, sock_errno()); + } + + FREE(ifn); // OR IS THIS AUTOMATIC? + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Get network interface names and indexes. + * + */ + +static +ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) { + return enif_make_badarg(env); + } + + return nlink_ifs(env); +} + + + +static +ERL_NIF_TERM nlink_ifs(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + struct if_nameindex* ifs = if_nameindex(); + + if (ifs == NULL) { + result = make_error2(env, sock_errno()); + } else { + /* + * We got some interfaces: + * 1) Calculate how many - the only way is to iterate through the list + * until its end (which is indicated by an entry with index = zero + * and if_name = NULL). + * 2) Allocate an ERL_NIF_TERM array of the calculated length. + * 3) Iterate through the array of interfaces and for each create + * a two tuple: {Idx, If} + * + * Or shall we instead build a list in reverse order and then when + * its done, reverse that? Check + */ + unsigned int len = nlink_ifs_length(ifs); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = MKT2(env, + MKI(env, ifs[i].if_index), + MKS(env, ifs[i].if_name)); + } + + result = make_ok2(env, MKLA(env, array, len)); + FREE(array); + } else { + result = make_ok2(env, enif_make_list(env, 0)); + } + } + + if (ifs != NULL) + if_freenameindex(ifs); + + return result; +} + + +static +unsigned int nlink_ifs_length(struct if_nameindex* p) +{ + unsigned int len = 0; + + while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { + len++; + } + + return len; +} + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -5202,6 +5406,11 @@ ErlNifFunc socket_funcs[] = {"nif_setopt", 3, nif_setopt, 0}, {"nif_getopt", 2, nif_getopt, 0}, + /* Misc utility functions */ + {"nif_link_if2idx", 1, nif_link_if2idx, 0}, + {"nif_link_idx2if", 1, nif_link_idx2if, 0}, + {"nif_link_ifs", 0, nif_link_ifs, 0}, + /* "Extra" functions to "complete" the socket interface. * For instance, the function nif_finalize_connection * is called after the connect *select* has "completed". diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 1090380769..5bea039783 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -49,7 +49,12 @@ shutdown/2, setopt/4, - getopt/3 + getopt/3, + + %% Some IPv6 utility functions + link_if2idx/1, + link_idx2if/1, + link_ifs/0 ]). -export_type([ @@ -61,6 +66,8 @@ ip_address/0, ip4_address/0, ip6_address/0, + in_sockaddr/0, + in4_sockaddr/0, in6_sockaddr/0, port_number/0, @@ -98,9 +105,10 @@ -type ip4_address() :: {0..255, 0..255, 0..255, 0..255}. +-type uint20() :: 0..16#FFFFF. -type uint32() :: 0..16#FFFFFFFF. --type ip6_flow_info() :: uint32(). --type ip6_scope_id() :: uint32(). +-type in6_flow_info() :: uint20(). +-type in6_scope_id() :: uint32(). -type ip6_address() :: {0..65535, @@ -111,12 +119,23 @@ 0..65535, 0..65535, 0..65535}. -%% We need to polish this further... --record(in6_sockaddr, {addr :: ip6_address(), - flowinfo = 0 :: ip6_flow_info(), - scope_id :: ip6_scope_id()}). + +%% +%% Should we do these as maps instead? +%% If we do we may need to include the family (domain) in the +%% map (as the native type do. See struct sockaddr_in6). +%% +-record(in4_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip4_address()}). +-type in4_sockaddr() :: #in4_sockaddr{}. +-record(in6_sockaddr, {port = 0 :: port_number(), + addr = any :: any | loopback | ip6_address(), + flowinfo = 0 :: in6_flow_info(), + scope_id = 0 :: in6_scope_id()}). -type in6_sockaddr() :: #in6_sockaddr{}. +-type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). + -type port_number() :: 0..65535. %% otp - The option is internal to our (OTP) imeplementation. @@ -1406,6 +1425,55 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> +%% =========================================================================== +%% +%% link_if2idx - Mappings between network interface names and indexes: if -> idx +%% +%% + +-spec link_if2idx(If) -> {ok, Idx} | {error, Reason} when + If :: string(), + Idx :: non_neg_integer(), + Reason :: term(). + +link_if2idx(If) when is_list(If) -> + nif_link_if2idx(If). + + + +%% =========================================================================== +%% +%% link_idx2if - Mappings between network interface names and indexes: idx -> if +%% +%% + +-spec link_idx2if(Idx) -> {ok, If} | {error, Reason} when + Idx :: non_neg_integer(), + If :: string(), + Reason :: term(). + +link_idx2if(Idx) when is_integer(Idx) -> + nif_link_idx2if(Idx). + + + +%% =========================================================================== +%% +%% link_ifs - get network interface names and indexes +%% +%% + +-spec link_ifs() -> Names | {error, Reason} when + Names :: [{Idx, If}], + Idx :: non_neg_integer(), + If :: string(), + Reason :: term(). + +link_ifs() -> + nif_link_ifs(). + + + %% =========================================================================== %% %% Encode / decode @@ -2010,3 +2078,12 @@ nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) -> nif_getopt(_Ref, _IsEnc, _Lev, _Key) -> erlang:error(badarg). + +nif_link_if2idx(_Name) -> + erlang:error(badarg). + +nif_link_idx2if(_Id) -> + erlang:error(badarg). + +nif_link_ifs() -> + erlang:error(badarg). -- cgit v1.2.3