diff options
author | Raimo Niskanen <[email protected]> | 2018-09-21 15:50:39 +0200 |
---|---|---|
committer | Raimo Niskanen <[email protected]> | 2018-10-02 15:40:52 +0200 |
commit | b32911645a20230864eaee965d6a7f1be248e47e (patch) | |
tree | c227679b03a23781afe9e921c3f48b0dd14994e3 /erts | |
parent | 50274e72fa6278f4f43ffe9fbf461683916dce09 (diff) | |
download | otp-b32911645a20230864eaee965d6a7f1be248e47e.tar.gz otp-b32911645a20230864eaee965d6a7f1be248e47e.tar.bz2 otp-b32911645a20230864eaee965d6a7f1be248e47e.zip |
Implement {netns,NS} option for inet:getifaddrs/1
Also implement the same option for the legacy undocumented functions
inet:getif/1,getiflist/1,ifget/2,ifset/2.
The arity 1 functions had before this change got signatures that
took a socket port that was used to do the needed syscall, so now
the signature was extended to also take an option list with the
only supported option {netns,Namespace}. The Socket argument
variant remains unsupported.
For inet:getifaddrs/1 the documentation file was changed to old
style function name definition so be able to hide the Socket
argument variant that is visible in the type spec.
The arity 2 functions had got an option list as second argument.
This list had to be partitioned into one list for the namespace
option(s) and the other for the rest.
The namespace option list was then fed to the already existing
namespace support for socket opening, which places the socket
in a namespace and hence made all these functions that in
inet_drv.c used getsockopt() work without change.
The functions that used getifaddrs() in inet_drv.c had to be
changed in inet_drv.c to swap namespaces around the
getifaddrs() syscall. This functionality was separated into
a new function call_getifaddrs().
Diffstat (limited to 'erts')
-rw-r--r-- | erts/emulator/drivers/common/inet_drv.c | 76 | ||||
-rw-r--r-- | erts/preloaded/ebin/prim_inet.beam | bin | 79028 -> 80120 bytes | |||
-rw-r--r-- | erts/preloaded/src/prim_inet.erl | 127 |
3 files changed, 156 insertions, 47 deletions
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 7f20477363..6d1c5cf6cc 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -5178,6 +5178,71 @@ static int hwaddr_libdlpi_lookup(const char *ifnm, } #endif +#ifdef HAVE_GETIFADDRS +/* Returns 0 for success and errno() for failure */ +static int call_getifaddrs(inet_descriptor* desc_p, struct ifaddrs **ifa_pp) +{ + int result, save_errno; +#ifdef HAVE_SETNS + int current_ns; + + current_ns = 0; + if (desc_p->netns != NULL) { + int new_ns; + /* Temporarily change network namespace for this thread + * over the getifaddrs() call + */ + current_ns = open("/proc/self/ns/net", O_RDONLY); + if (current_ns == INVALID_SOCKET) + return sock_errno(); + new_ns = open(desc_p->netns, O_RDONLY); + if (new_ns == INVALID_SOCKET) { + save_errno = sock_errno(); + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return save_errno; + } + if (setns(new_ns, CLONE_NEWNET) != 0) { + save_errno = sock_errno(); + while (close(new_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + return save_errno; + } + else { + while (close(new_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + } + } +#endif + save_errno = 0; + result = getifaddrs(ifa_pp); + if (result < 0) + save_errno = sock_errno(); +#ifdef HAVE_SETNS + if (desc_p->netns != NULL) { + /* Restore network namespace */ + if (setns(current_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 set namespace. + * Can this even happen? Should the emulator be aborted? + */ + if (result >= 0) { + /* We got a result but have to waste it */ + save_errno = sock_errno(); + freeifaddrs(*ifa_pp); + } + } + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + } +#endif + return save_errno; +} +#endif /* #ifdef HAVE_GETIFADDRS */ + /* FIXME: temporary hack */ #ifndef IFHWADDRLEN #define IFHWADDRLEN 6 @@ -5255,8 +5320,8 @@ static ErlDrvSSizeT inet_ctl_ifget(inet_descriptor* desc, struct sockaddr_dl *sdlp; int found = 0; - if (getifaddrs(&ifa) == -1) - goto error; + if (call_getifaddrs(desc, &ifa) != 0) + goto error; for (ifp = ifa; ifp; ifp = ifp->ifa_next) { if ((ifp->ifa_addr->sa_family == AF_LINK) && @@ -5974,6 +6039,7 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, ErlDrvSizeT buf_size; char *buf_p; char *buf_alloc_p; + int save_errno; buf_size = GETIFADDRS_BUFSZ; buf_alloc_p = ALLOC(GETIFADDRS_BUFSZ); @@ -6008,9 +6074,9 @@ static ErlDrvSSizeT inet_ctl_getifaddrs(inet_descriptor* desc_p, } \ } while (0) - if (getifaddrs(&ifa_p) < 0) { - return ctl_error(sock_errno(), rbuf_pp, rsize); - } + if ((save_errno = call_getifaddrs(desc_p, &ifa_p)) != 0) + return ctl_error(save_errno, rbuf_pp, rsize); + ifa_free_p = ifa_p; *buf_p++ = INET_REP_OK; for (; ifa_p; ifa_p = ifa_p->ifa_next) { diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 4a345f8152..eebfe19a11 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 8169943dde..1da852deb2 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -870,9 +870,9 @@ chgopts(S, Opts) when is_port(S), is_list(Opts) -> getifaddrs(S) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_GETIFADDRS, []) of - {ok, Data} -> - {ok, comp_ifaddrs(build_ifaddrs(Data), ktree_empty())}; - {error,enotsup} -> + {ok, Data} -> + {ok, comp_ifaddrs(build_ifaddrs(Data))}; + {error,enotsup} -> case getiflist(S) of {ok, IFs} -> {ok, getifaddrs_ifget(S, IFs)}; @@ -881,30 +881,75 @@ getifaddrs(S) when is_port(S) -> Err2 -> Err2 end. -%% Restructure interface properties per interface and remove duplicates - -comp_ifaddrs([{If,Opts}|IfOpts], T) -> - case ktree_is_defined(If, T) of - true -> - OptSet = comp_ifaddrs_add(ktree_get(If, T), Opts), - comp_ifaddrs(IfOpts, ktree_update(If, OptSet, T)); - false -> - OptSet = comp_ifaddrs_add(ktree_empty(), Opts), - comp_ifaddrs(IfOpts, ktree_insert(If, OptSet, T)) - end; -comp_ifaddrs([], T) -> - [{If,ktree_keys(ktree_get(If, T))} || If <- ktree_keys(T)]. - -comp_ifaddrs_add(OptSet, [Opt|Opts]) -> - case ktree_is_defined(Opt, OptSet) of - true - when element(1, Opt) =:= flags; - element(1, Opt) =:= hwaddr -> - comp_ifaddrs_add(OptSet, Opts); - _ -> - comp_ifaddrs_add(ktree_insert(Opt, undefined, OptSet), Opts) +%% Restructure interface properties per interface + +comp_ifaddrs(IfOpts) -> + comp_ifaddrs(IfOpts, ktree_empty()). +%% +comp_ifaddrs([{If,[{flags,Flags}|Opts]}|IfOpts], IfT) -> + case ktree_is_defined(If, IfT) of + true -> + comp_ifaddrs( + IfOpts, + ktree_update( + If, + comp_ifaddrs_flags(Flags, Opts, ktree_get(If, IfT)), + IfT)); + false -> + comp_ifaddrs( + IfOpts, + ktree_insert( + If, + comp_ifaddrs_flags(Flags, Opts, ktree_empty()), + IfT)) end; -comp_ifaddrs_add(OptSet, []) -> OptSet. +comp_ifaddrs([], IfT) -> + comp_ifaddrs_2(ktree_keys(IfT), IfT). + +comp_ifaddrs_flags(Flags, Opts, FlagsT) -> + case ktree_is_defined(Flags, FlagsT) of + true -> + ktree_update( + Flags, + rev(Opts, ktree_get(Flags, FlagsT)), + FlagsT); + false -> + ktree_insert(Flags, rev(Opts), FlagsT) + end. + +comp_ifaddrs_2([If|Ifs], IfT) -> + FlagsT = ktree_get(If, IfT), + [{If,comp_ifaddrs_3(ktree_keys(FlagsT), FlagsT)} + | comp_ifaddrs_2(Ifs, IfT)]; +comp_ifaddrs_2([], _IfT) -> + []. +%% +comp_ifaddrs_3([Flags|FlagsL], FlagsT) -> + [{flags,Flags}|hwaddr_last(rev(ktree_get(Flags, FlagsT)))] + ++ hwaddr_last(comp_ifaddrs_3(FlagsL, FlagsT)); +comp_ifaddrs_3([], _FlagsT) -> + []. + +%% Place hwaddr last to look more like legacy emulation +hwaddr_last(Opts) -> + hwaddr_last(Opts, Opts, []). +%% +hwaddr_last([{hwaddr,_} = Opt|Opts], L, R) -> + hwaddr_last(Opts, L, [Opt|R]); +hwaddr_last([_|Opts], L, R) -> + hwaddr_last(Opts, L, R); +hwaddr_last([], L, []) -> + L; +hwaddr_last([], L, R) -> + rev(hwaddr_last(L, []), rev(R)). +%% +hwaddr_last([{hwaddr,_}|Opts], R) -> + hwaddr_last(Opts, R); +hwaddr_last([Opt|Opts], R) -> + hwaddr_last(Opts, [Opt|R]); +hwaddr_last([], R) -> + R. + %% Legacy emulation of getifaddrs @@ -912,21 +957,19 @@ getifaddrs_ifget(_, []) -> []; getifaddrs_ifget(S, [IF|IFs]) -> case ifget(S, IF, [flags]) of {ok,[{flags,Flags}]=FlagsVals} -> - BroadOpts = - case member(broadcast, Flags) of - true -> - [broadaddr,hwaddr]; - false -> - [hwaddr] - end, - P2POpts = - case member(pointtopoint, Flags) of - true -> - [dstaddr|BroadOpts]; - false -> - BroadOpts - end, - getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|P2POpts]); + GetOpts = + case member(pointtopoint, Flags) of + true -> + [dstaddr,hwaddr]; + false -> + case member(broadcast, Flags) of + true -> + [broadaddr,hwaddr]; + false -> + [hwaddr] + end + end, + getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|GetOpts]); _ -> getifaddrs_ifget(S, IFs, IF, [], [addr,netmask,hwaddr]) end. @@ -2500,7 +2543,7 @@ get_addrs([F|Addrs]) -> [Addr|get_addrs(Rest)]. get_addr(?INET_AF_LOCAL, [N|Addr]) -> - {A,Rest} = lists:split(N, Addr), + {A,Rest} = split(N, Addr), {{local,iolist_to_binary(A)},Rest}; get_addr(?INET_AF_UNSPEC, Rest) -> {{unspec,<<>>},Rest}; |