From b32911645a20230864eaee965d6a7f1be248e47e Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Fri, 21 Sep 2018 15:50:39 +0200 Subject: 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(). --- lib/kernel/doc/src/inet.xml | 182 +++++++++++++++++++++++++++++++++++--------- lib/kernel/src/inet.erl | 82 ++++++++++++++------ 2 files changed, 202 insertions(+), 62 deletions(-) (limited to 'lib') diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index ed775d67eb..127c110df4 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -197,6 +197,79 @@ fe80::204:acff:fe17:bf38

+ + + +

+ Interface address description list returned from + getifaddrs/0,1 + for a named interface, translated from the returned + data of the POSIX API function getaddrinfo(). +

+

+ Hwaddr is hardware dependent, + for example, on Ethernet interfaces it is + the 6-byte Ethernet address (MAC address (EUI-48 address)). +

+

+ The tuples {addr,Addr}, + {netmask,Netmask}, and possibly + {broadaddr,Broadaddr} or + {dstaddr,Dstaddr} + are repeated in the list + if the interface has got multiple addresses. + An interface may have multiple {flag,_} tuples + for example if it has different flags + for different address families. + Multiple {hwaddr,Hwaddr} tuples + is hard to say anything definite about, though. + The tuple {flag,Flags} is mandatory, + all others are optional. +

+

+ Do not rely too much on the order + of Flags atoms + or the Ifopt tuples. + There are however some rules: +

+ +

+ A {flag,_} tuple applies to all other tuples that follow. +

+

+ Immediately after {addr,_} follows {netmask,_}. +

+

+ Immediately thereafter may {broadaddr,_} follow + if broadcast is member of Flags, + or {dstaddr,_} if pointtopoint + is member of Flags. + Both {dstaddr,_} and {broadaddr,_} + does not occur for the same {addr,_}. +

+

+ Any {netmask,_}, {broadaddr,_}, or + {dstaddr,_} tuples that follow an + {addr,Addr} + tuple concerns the address Addr. +

+
+

+ The tuple {hwaddr,_} is not returned on Solaris, as the + hardware address historically belongs to the link layer + and it is not returned by the Solaris API function + getaddrinfo(). +

+ +

+ On Windows, the data is fetched from different + OS API functions, so the Netmask + and Broadaddr values may be calculated, + just as some Flags values. +

+
+
+
@@ -324,38 +397,64 @@ fe80::204:acff:fe17:bf38 Return a list of interfaces and their addresses. -

Returns a list of 2-tuples containing interface names and the - interface addresses. Ifname is a Unicode string. - Hwaddr is hardware dependent, for example, on - Ethernet interfaces - it is the 6-byte Ethernet address (MAC address (EUI-48 address)).

-

The tuples {addr,Addr}, {netmask,_}, and - {broadaddr,_} are repeated in the result list if the interface - has multiple addresses. If you come across an interface with - multiple {flag,_} or {hwaddr,_} tuples, you have - a strange interface or possibly a bug in this function. The tuple - {flag,_} is mandatory, all others are optional.

-

Do not rely too much on the order of Flag atoms - or Ifopt tuples. There are however some rules:

- -

Immediately after - {addr,_} follows {netmask,_}.

-

Immediately thereafter follows {broadaddr,_} if flag - broadcast is not set and flag - pointtopoint is set.

-

Any {netmask,_}, {broadaddr,_}, or - {dstaddr,_} tuples that follow an {addr,_} - tuple concerns that address.

-
-

The tuple {hwaddr,_} is not returned on Solaris, as the - hardware address historically belongs to the link layer and only - the superuser can read such addresses.

- -

On Windows, the data is fetched from different OS API functions, - so the Netmask and Broadaddr - values can be calculated, just as some Flag - values. Report flagrant bugs.

-
+

+ Returns a list of 2-tuples containing interface names and + the interfaces' addresses. Ifname + is a Unicode string and + Ifopts is a list of + interface address description tuples. +

+

+ The interface address description tuples + are documented under the type of the + + Ifopts + + value. +

+
+ + + + getifaddrs(Opts) -> + {ok, [{Ifname, Ifopts}]} | {error, Posix} + + Return a list of interfaces and their addresses. + + + Opts = [{netns, Namespace}] + + + Namespace = + + file:filename_all() + + + Ifname = string() + + Ifopts = + + getifaddrs_ifopts() + + + Posix = posix() + + +

+ The same as + getifaddrs/0 + but the Option + {netns, Namespace} sets a network namespace + for the OS call, on platforms that supports that feature. +

+

+ See the socket option + + {netns, Namespace} + + under + setopts/2. +

@@ -950,20 +1049,29 @@ get_tcpi_sacked(Sock) -> {mode, Mode :: binary | list} -

Received Packet is delivered as defined by Mode. +

+ Received Packet is delivered as defined by Mode.

- {netns, Namespace :: file:filename_all()} + + + {netns, Namespace :: file:filename_all()} + -

Sets a network namespace for the socket. Parameter +

+ Sets a network namespace for the socket. Parameter Namespace is a filename defining the namespace, for example, "/var/run/netns/example", typically created by command ip netns add example. This option must be used in a function call that creates a socket, that is, gen_tcp:connect/3,4, gen_tcp:listen/2, - gen_udp:open/1,2, or - gen_sctp:open/0,1,2.

+ gen_udp:open/1,2 + or + gen_sctp:open/0,1,2, + and also + getifaddrs/1. +

This option uses the Linux-specific syscall setns(), such as in Linux kernel 3.0 or later, and therefore only exists when the runtime system diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 5dd68dc285..9f22eb6aaa 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -154,6 +154,15 @@ 'running' | 'multicast' | 'loopback']} | {'hwaddr', ether_address()}. +-type getifaddrs_ifopts() :: + [Ifopt :: {flags, Flags :: [up | broadcast | loopback | + pointtopoint | running | multicast]} | + {addr, Addr :: ip_address()} | + {netmask, Netmask :: ip_address()} | + {broadaddr, Broadaddr :: ip_address()} | + {dstaddr, Dstaddr :: ip_address()} | + {hwaddr, Hwaddr :: [byte()]}]. + -type address_family() :: 'inet' | 'inet6' | 'local'. -type socket_protocol() :: 'tcp' | 'udp' | 'sctp'. -type socket_type() :: 'stream' | 'dgram' | 'seqpacket'. @@ -321,32 +330,32 @@ getopts(Socket, Opts) -> Other end. --spec getifaddrs(Socket :: socket()) -> - {'ok', [string()]} | {'error', posix()}. - +-spec getifaddrs( + [Option :: {netns, Namespace :: file:filename_all()}] + | socket()) -> + {'ok', [{Ifname :: string(), + Ifopts :: getifaddrs_ifopts()}]} + | {'error', posix()}. +getifaddrs(Opts) when is_list(Opts) -> + withsocket(fun(S) -> prim_inet:getifaddrs(S) end, Opts); getifaddrs(Socket) -> prim_inet:getifaddrs(Socket). --spec getifaddrs() -> {ok, Iflist} | {error, posix()} when - Iflist :: [{Ifname,[Ifopt]}], - Ifname :: string(), - Ifopt :: {flags,[Flag]} | {addr,Addr} | {netmask,Netmask} - | {broadaddr,Broadaddr} | {dstaddr,Dstaddr} - | {hwaddr,Hwaddr}, - Flag :: up | broadcast | loopback | pointtopoint - | running | multicast, - Addr :: ip_address(), - Netmask :: ip_address(), - Broadaddr :: ip_address(), - Dstaddr :: ip_address(), - Hwaddr :: [byte()]. - +-spec getifaddrs() -> + {'ok', [{Ifname :: string(), + Ifopts :: getifaddrs_ifopts()}]} + | {'error', posix()}. getifaddrs() -> withsocket(fun(S) -> prim_inet:getifaddrs(S) end). --spec getiflist(Socket :: socket()) -> - {'ok', [string()]} | {'error', posix()}. +-spec getiflist( + [Option :: {netns, Namespace :: file:filename_all()}] + | socket()) -> + {'ok', [string()]} | {'error', posix()}. + +getiflist(Opts) when is_list(Opts) -> + withsocket(fun(S) -> prim_inet:getiflist(S) end, Opts); getiflist(Socket) -> prim_inet:getiflist(Socket). @@ -363,11 +372,19 @@ getiflist() -> ifget(Socket, Name, Opts) -> prim_inet:ifget(Socket, Name, Opts). --spec ifget(Name :: string() | atom(), Opts :: [if_getopt()]) -> +-spec ifget( + Name :: string() | atom(), + Opts :: [if_getopt() | + {netns, Namespace :: file:filename_all()}]) -> {'ok', [if_getopt_result()]} | {'error', posix()}. ifget(Name, Opts) -> - withsocket(fun(S) -> prim_inet:ifget(S, Name, Opts) end). + {NSOpts,IFOpts} = + lists:partition( + fun ({netns,_}) -> true; + (_) -> false + end, Opts), + withsocket(fun(S) -> prim_inet:ifget(S, Name, IFOpts) end, NSOpts). -spec ifset(Socket :: socket(), Name :: string() | atom(), @@ -377,11 +394,19 @@ ifget(Name, Opts) -> ifset(Socket, Name, Opts) -> prim_inet:ifset(Socket, Name, Opts). --spec ifset(Name :: string() | atom(), Opts :: [if_setopt()]) -> +-spec ifset( + Name :: string() | atom(), + Opts :: [if_setopt() | + {netns, Namespace :: file:filename_all()}]) -> 'ok' | {'error', posix()}. ifset(Name, Opts) -> - withsocket(fun(S) -> prim_inet:ifset(S, Name, Opts) end). + {NSOpts,IFOpts} = + lists:partition( + fun ({netns,_}) -> true; + (_) -> false + end, Opts), + withsocket(fun(S) -> prim_inet:ifset(S, Name, IFOpts) end, NSOpts). -spec getif() -> {'ok', [{ip_address(), ip_address() | 'undefined', ip_address()}]} | @@ -391,10 +416,14 @@ getif() -> withsocket(fun(S) -> getif(S) end). %% backwards compatible getif --spec getif(Socket :: socket()) -> +-spec getif( + [Option :: {netns, Namespace :: file:filename_all()}] + | socket()) -> {'ok', [{ip_address(), ip_address() | 'undefined', ip_address()}]} | {'error', posix()}. +getif(Opts) when is_list(Opts) -> + withsocket(fun(S) -> getif(S) end, Opts); getif(Socket) -> case prim_inet:getiflist(Socket) of {ok, IfList} -> @@ -415,7 +444,10 @@ getif(Socket) -> end. withsocket(Fun) -> - case inet_udp:open(0,[]) of + withsocket(Fun, []). +%% +withsocket(Fun, Opts) -> + case inet_udp:open(0, Opts) of {ok,Socket} -> Res = Fun(Socket), inet_udp:close(Socket), -- cgit v1.2.3