summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJordan Bracco <href@random.sh>2021-10-27 18:38:03 +0200
committerJordan Bracco <href@random.sh>2021-10-27 18:38:03 +0200
commit27eec8413540bfd11454cba6e35b0cc4daa5c5ad (patch)
tree405a35c02ccbc3adfb7cddd7f71bea8b2714b289 /src
🤷
Diffstat (limited to 'src')
-rw-r--r--src/fib.erl37
-rw-r--r--src/fib.hrl3
-rw-r--r--src/fib_app.erl16
-rw-r--r--src/fib_behaviour.erl7
-rw-r--r--src/fib_freebsd.erl35
-rw-r--r--src/fib_linux.erl18
-rw-r--r--src/fib_macos.erl179
-rw-r--r--src/fib_macos.hrl146
-rw-r--r--src/fib_pf_route_socket.erl23
-rw-r--r--src/fib_sup.erl12
-rw-r--r--src/fib_util.erl41
11 files changed, 517 insertions, 0 deletions
diff --git a/src/fib.erl b/src/fib.erl
new file mode 100644
index 0000000..130e8ec
--- /dev/null
+++ b/src/fib.erl
@@ -0,0 +1,37 @@
+-module(fib).
+-export([tables/0, current_table/0]).
+-export([list/0, list/1]).
+-export_types([table/0, flags/0, entry/0]).
+
+-type table() :: non_neg_integer.
+
+-type flags() :: [].
+
+-type entry() :: #{destination := inet:ip_address() | default,
+ netmask := inet:ip_address() | undefined,
+ gateway := inet:ip_address() | {mac, string()} | {link, integer()},
+ scope := string() | undefined,
+ interface := string(),
+ flags := flags(),
+ expire := undefined}.
+
+-spec tables() -> [table()].
+tables() ->
+ persistent_term:get('fib/tables').
+
+-spec current_table() -> table().
+current_table() ->
+ persistent_term:get('fib/current_table').
+
+-spec list() -> [entry()].
+list() ->
+ Mod = mod(),
+ Mod:list(Mod:default_table()).
+
+-spec list(table) -> [entry()].
+list(Table) ->
+ Mod = mod(),
+ Mod:list(Table).
+
+mod() ->
+ persistent_term:get('fib/module').
diff --git a/src/fib.hrl b/src/fib.hrl
new file mode 100644
index 0000000..6f88bfb
--- /dev/null
+++ b/src/fib.hrl
@@ -0,0 +1,3 @@
+-define(SOCK_RAW, 3).
+-define(AF_UNSPEC, 0).
+-define(PF_ROUTE, 17).
diff --git a/src/fib_app.erl b/src/fib_app.erl
new file mode 100644
index 0000000..3acb41c
--- /dev/null
+++ b/src/fib_app.erl
@@ -0,0 +1,16 @@
+-module(fib_app).
+-behaviour(application).
+
+-export([start/2]).
+-export([stop/1]).
+
+start(_Type, _Args) ->
+ %% Find which platform module to use and cache persistent data.
+ {ok, Mod} = fib_util:module(),
+ persistent_term:put('fib/module', Mod),
+ persistent_term:put('fib/current_table', Mod:current_table()),
+ persistent_term:put('fib/tables', Mod:tables()),
+ fib_sup:start_link().
+
+stop(_State) ->
+ ok.
diff --git a/src/fib_behaviour.erl b/src/fib_behaviour.erl
new file mode 100644
index 0000000..fd96ca7
--- /dev/null
+++ b/src/fib_behaviour.erl
@@ -0,0 +1,7 @@
+-module(fib_behaviour).
+
+-callback tables() -> [fib:table()].
+-callback default_table() -> fib:table().
+-callback current_table() -> fib:table().
+
+-callback list(fib:table()) -> {ok, []} | {error, {no_table, fib:table()}}.
diff --git a/src/fib_freebsd.erl b/src/fib_freebsd.erl
new file mode 100644
index 0000000..b85d458
--- /dev/null
+++ b/src/fib_freebsd.erl
@@ -0,0 +1,35 @@
+-module(fib_freebsd).
+-behaviour(fib_behaviour).
+-export([default_table/0, current_table/0, tables/0]).
+-export([list/1]).
+
+default_table() ->
+ 1.
+
+current_table() ->
+ case os:cmd("sysctl net.my_fibnum") of
+ [$n, $e, $t, $., $m, $y, $_, $f, $i, $n, $b, $u, $m, $:, $\s | Rest] ->
+ {ok, list_to_integer(string:trim(Rest))};
+ _ ->
+ error
+ end.
+
+tables() ->
+ case os:cmd("sysctl net.fibs") of
+ [$n, $e, $t, $., $f, $i, $b, $s, $:, $\s | Rest] ->
+ Count = list_to_integer(string:trim(Rest)),
+ {ok, lists:seq(1, Count)};
+ _ ->
+ error
+ end.
+
+list(Table) ->
+ case lists:member(fib:tables(), Table) of
+ true ->
+ do_list(Table);
+ _ ->
+ {error, {no_table, Table}}
+ end.
+
+do_list(Table) ->
+ os:cmd(["setfib ", Table, " netstat -rn"]).
diff --git a/src/fib_linux.erl b/src/fib_linux.erl
new file mode 100644
index 0000000..f87e885
--- /dev/null
+++ b/src/fib_linux.erl
@@ -0,0 +1,18 @@
+-module(fib_linux).
+-behaviour(fib_behaviour).
+-export([default_table/0, current_table/0, tables/0]).
+-export([list/1]).
+
+%% Linux has 255 routing tables.
+
+default_table() ->
+ 254.
+
+current_table() ->
+ default_table().
+
+tables() ->
+ lists:seq(1, 255).
+
+list(_Table) ->
+ todo.
diff --git a/src/fib_macos.erl b/src/fib_macos.erl
new file mode 100644
index 0000000..3df046d
--- /dev/null
+++ b/src/fib_macos.erl
@@ -0,0 +1,179 @@
+-module(fib_macos).
+-behaviour(fib_behaviour).
+-include("fib_macos.hrl").
+-export([default_table/0, current_table/0, tables/0]).
+-export([list/1]).
+-export([monitor/1]).
+-export([parse_pf_route_packet/1]).
+
+%% MacOS has no tables.
+
+default_table() ->
+ ?TABLE.
+
+current_table() ->
+ ?TABLE.
+
+tables() ->
+ {ok, [?TABLE]}.
+
+list(?TABLE) ->
+ Netstat = os:cmd("netstat -rn"),
+ List = string:split(string:trim(Netstat), [$\n], all),
+ do_list(List, []);
+list(Table) ->
+ {error, {no_table, Table}}.
+
+monitor(?TABLE) ->
+ spawn_link(fun () ->
+ fib_pf_route_socket:open(fun (_) -> noop end, fun parse_pf_route_packet/1)
+ end);
+monitor(Table) ->
+ {error, {no_table, Table}}.
+
+%% rt_msghdr struct followed by rt_metrics and a list of sockaddr
+
+%% MacOS 12
+parse_pf_route_packet(<<% struct rt_msghdr (len removed)
+ ?RTM_VERSION:8/little-unsigned-integer,
+ Type:8/little-unsigned-integer,
+ Index:32/little-unsigned-integer,
+ Flags:32/little-signed-integer,
+ Addrs:32/little-signed-integer,
+ Pid:32/little-signed-integer,
+ Seq:32/little-signed-integer,
+ Errno:32/little-signed-integer,
+ Use:32/little-signed-integer,
+ Inits:32/little-unsigned-integer,
+ % struct rt_metrics
+ Locks:32/little-unsigned-integer,
+ Mtu:32/little-unsigned-integer,
+ Hopcount:32/little-unsigned-integer,
+ Expire:32/little-signed-integer,
+ Recv:32/little-unsigned-integer,
+ Send:32/little-unsigned-integer,
+ Ssthresh:32/little-unsigned-integer,
+ Rtt:32/little-unsigned-integer,
+ Rttvar:32/little-unsigned-integer,
+ Pksent:32/little-unsigned-integer,
+ State:32/little-unsigned-integer,
+ Filler1:32/little-unsigned-integer,
+ Filler2:32/little-unsigned-integer,
+ Filler3:32/little-unsigned-integer,
+ % List of sockaddrs
+ BinaryAddrs/binary
+ >>) ->
+ Rt = #macos_rt{type = Type, ifp_index = Index, flags = Flags, addrs = Addrs, pid = Pid, seq = Seq, errno = Errno, use = Use,
+ metrics_init = Inits, metrics_lock = Locks, mtu = Mtu, hopcount = Hopcount, expire = Expire, recvpipe = Recv,
+ sendpipe = Send, ssthresh = Ssthresh, rtt = Rtt, rttvar = Rttvar, pksent = Pksent, state = State,
+ filler = {Filler1, Filler2, Filler3}},
+ parse_rt(Rt, BinaryAddrs).
+
+parse_rt(Rt0, BinaryAddrs) ->
+ Rt1 = Rt0#macos_rt{type = maps:get(Rt0#macos_rt.type, ?RTM, Rt0#macos_rt.type),
+ flags = bitmask(?RTF, Rt0#macos_rt.flags),
+ addrs = bitmask(?RTA, Rt0#macos_rt.addrs),
+ metrics_init = bitmask(?RTV, Rt0#macos_rt.metrics_init),
+ metrics_lock = bitmask(?RTV, Rt0#macos_rt.metrics_lock)},
+ logger:warning("fib_macos:monitor Rt = ~p~nBinary: ~p", [Rt1, BinaryAddrs]),
+ Rt = get_addrs(Rt1#macos_rt.addrs, BinaryAddrs, Rt1),
+ logger:warning("fib_macos:monitor Rt = ~p", [Rt]),
+ Rt.
+
+get_addrs([_Type | Rest], <<0:32/little-unsigned-integer, Binary/binary>>, Rt) ->
+ get_addrs(Rest, Binary, Rt);
+get_addrs([Type | Rest], <<Len:8/little-unsigned-integer, Family:8/little-unsigned-integer, Content/binary>>, Rt) ->
+ logger:error("fib_macos:get_addrs() ~p len=~p family=~p ~nbinary ~p", [Type, Len, Family, Content]),
+ ToRead = case Type of
+ netmask -> Len - 1;
+ _Other -> Len - 2
+ end,
+ <<BinaryAddr:ToRead/binary, Binary/binary>> = Content,
+ Idx = string:str(record_info(fields, macos_rt), [Type]),
+ logger:warning("fib_macos:get_addrs() - get_addr ~p = ~p~nRest ~p~nBinRest = ~p", [Type, BinaryAddr, Rest, Binary]),
+ get_addrs(Rest, Binary, setelement(Idx+1, Rt, process_address(Family, BinaryAddr)));
+get_addrs([], _, Rt) ->
+ Rt.
+
+%% sockaddr_in, without the zeros (? only in netmasks)
+process_address(?AF_INET, <<_Port:16/little-unsigned-integer, A, B, C, D>>) ->
+ {A, B, C, D};
+%% sockaddr_in full ; _Zero is `sin_zero`.
+process_address(?AF_INET, <<_Port:16/little-unsigned-integer, A, B, C, D, _Zero:64/little-unsigned-integer>>) ->
+ {A, B, C, D};
+%% ???????? netmask in a RTM_GET message. see fib_macos_SUITE:pckt_rt_get.
+process_address(255, <<255, 255, A, B, C, D>>) ->
+ {A, B, C, D};
+process_address(Family, Data) ->
+ logger:error("macos:process_address: no clause for family ~p = ~p", [Family, Data]),
+ {Family, Data}.
+
+bitmask(List, Integer) ->
+ bitmask(List, Integer, []).
+
+bitmask([{V, Name} | Rest], I, Acc) ->
+ if
+ (I band V) =:= V ->
+ bitmask(Rest, I, [Name | Acc]);
+ true ->
+ bitmask(Rest, I, Acc)
+ end;
+bitmask([], _, Acc) ->
+ lists:reverse(Acc).
+
+do_list([], Acc) ->
+ Acc;
+do_list([[] | Rest], Acc) ->
+ do_list(Rest, Acc);
+
+%% Header "Routing Tables"
+do_list([[$R, $o, $u, $t, $i, $n, $g | _] | Rest], Acc) ->
+ do_list(Rest, Acc);
+%% Header "Internet"/"Internet6"
+do_list([[$I, $n, $t, $e, $r, $n, $e, $t | _] | Rest], Acc) ->
+ do_list(Rest, Acc);
+%% Header fields
+do_list([[$D, $e, $s, $t, $i, $n, $a, $t, $i, $o, $n | _] | Rest], Acc) ->
+ do_list(Rest, Acc);
+%% Data line
+do_list([Line | Rest], Acc) ->
+ [Destination, Gateway, Flags, Netif | Expire] = string:lexemes(Line, [$\s, $\t]),
+ {DestIp, Scope, Netmask} = case Destination of
+ "default" -> {default, undefined, undefined};
+ IPStr -> fib_util:parse_ip_address_with_netmask(IPStr)
+ end,
+ GW = case Gateway of
+ [$l, $i, $n, $k, $# | Num] ->
+ {link, list_to_integer(Num)};
+ GWStr ->
+ case inet:parse_address(GWStr) of
+ {ok, GwIP} -> GwIP;
+ {error, einval} -> {mac, GWStr}
+ end
+ end,
+ Route = #{destination => DestIp, netmask => Netmask, scope => Scope, gateway => GW, flags => [flag(F) || F <- Flags], interface => Netif, expire => Expire},
+ do_list(Rest, [Route | Acc]).
+
+flag($1) -> proto1;
+flag($2) -> proto2;
+flag($3) -> proto3;
+flag($B) -> blackhole;
+flag($b) -> broadcast;
+flag($C) -> cloning;
+flag($c) -> prcloning;
+flag($D) -> dynamic;
+flag($G) -> gateway;
+flag($H) -> host;
+flag($I) -> ifscope;
+flag($i) -> ifref;
+flag($L) -> llinfo;
+flag($M) -> modified;
+flag($m) -> multicast;
+flag($R) -> reject;
+flag($r) -> router;
+flag($S) -> static;
+flag($U) -> up;
+flag($W) -> wascloned;
+flag($X) -> xresolve;
+flag($Y) -> proxy;
+flag($g) -> global.
diff --git a/src/fib_macos.hrl b/src/fib_macos.hrl
new file mode 100644
index 0000000..d1cd1a1
--- /dev/null
+++ b/src/fib_macos.hrl
@@ -0,0 +1,146 @@
+-define(TABLE, 1).
+
+%% Constants and structs are mostly defined in `net/route.h`.
+%% /Library/Developer/CommandLineTools/SDKs/MacOSX12.0.sdk/usr/include/net/route.h
+
+-define(RTM_VERSION, 5).
+
+%% PF_ROUTE messages types
+-define(RTM_ADD, 1).
+-define(RTM_DELETE, 2).
+-define(RTM_CHANGE, 3).
+-define(RTM_GET, 4).
+-define(RTM_REDIRECT, 6).
+-define(RTM_MISS, 7).
+-define(RTM_RESOLVE, 16#b).
+-define(RTM_NEWADDR, 16#c).
+-define(RTM_DELADDR, 16#d).
+-define(RTM_IFINFO, 16#e).
+-define(RTM_NEWMADDR, 16#f).
+-define(RTM_DELMADDR, 16#10).
+-define(RTM_IFINFO2, 16#12).
+-define(RTM_NEWMADDR2, 16#13).
+-define(RTM_GET2, 16#14).
+-define(RTM, #{
+ ?RTM_ADD => add,
+ ?RTM_DELETE => delete,
+ ?RTM_CHANGE => change,
+ ?RTM_GET => get,
+ ?RTM_REDIRECT => redirect,
+ ?RTM_MISS => miss,
+ ?RTM_RESOLVE => resolve,
+ ?RTM_NEWADDR => newaddr,
+ ?RTM_DELADDR => deladdr,
+ ?RTM_IFINFO => ifinfo,
+ ?RTM_NEWMADDR => newmaddr,
+ ?RTM_DELMADDR => delmaddr,
+ ?RTM_IFINFO2 => ifinfo2,
+ ?RTM_NEWMADDR2 => newmaddr2,
+ ?RTM_GET2 => get2
+ }).
+
+%% Bitmask values for flags
+-define(RTF_UP, 1).
+-define(RTF_GATEWAY, 2).
+-define(RTF_HOST, 4).
+-define(RTF_REJECT, 8).
+-define(RTF_DYNAMIC, 16#10).
+-define(RTF_MODIFIED, 16#20).
+-define(RTF_DONE, 16#40).
+-define(RTF_DELCLONE, 16#80).
+-define(RTF_CLONING, 16#100).
+-define(RTF_XRESOLVE, 16#200).
+-define(RTF_LLDATA, 16#400).
+-define(RTF_STATIC, 16#800).
+-define(RTF_BLACKHOLE, 16#1000).
+-define(RTF_NOIFREF, 16#2000).
+-define(RTF_PROTO2, 16#4000).
+-define(RTF_PROTO1, 16#8000).
+-define(RTF_PRCLONING, 16#10000).
+-define(RTF_WASCLONED, 16#20000).
+-define(RTF_PROTO3, 16#40000).
+-define(RTF_PINNED, 16#100000).
+-define(RTF_LOCAL, 16#200000).
+-define(RTF_BROADCAST, 16#400000).
+-define(RTF_MULTICAST, 16#800000).
+-define(RTF_IFSCOPE, 16#1000000).
+-define(RTF_CONDEMNED, 16#2000000).
+-define(RTF_IFREF, 16#4000000).
+-define(RTF_PROXY, 16#8000000).
+-define(RTF_ROUTER, 16#10000000).
+-define(RTF_DEAD, 16#20000000).
+-define(RTF_GLOBAL, 16#40000000).
+-define(RTF, [{?RTF_UP, up}, {?RTF_GATEWAY, gateway}, {?RTF_HOST, host}, {?RTF_REJECT, reject},
+ {?RTF_DYNAMIC, dynamic}, {?RTF_MODIFIED, modified}, {?RTF_DONE, done}, {?RTF_DELCLONE, delclone},
+ {?RTF_CLONING, cloning}, {?RTF_XRESOLVE, xresolve}, {?RTF_LLDATA, lldata}, {?RTF_STATIC, static},
+ {?RTF_BLACKHOLE, blackhole}, {?RTF_NOIFREF, noifref}, {?RTF_PROTO2, proto2}, {?RTF_PROTO1, proto1},
+ {?RTF_PRCLONING, prcloning}, {?RTF_WASCLONED, wascloned}, {?RTF_PROTO3, proto3}, {?RTF_PINNED, pinned},
+ {?RTF_LOCAL, local}, {?RTF_BROADCAST, broadcast}, {?RTF_MULTICAST, multicast}, {?RTF_IFSCOPE, ifscope},
+ {?RTF_CONDEMNED, condemned}, {?RTF_IFREF, ifref}, {?RTF_PROXY, proxy}, {?RTF_ROUTER, router},
+ {?RTF_DEAD, dead}, {?RTF_GLOBAL, global}]).
+
+%% Bitmask values, rtm_inits and rtm_locks
+-define(RTV_MTU, 1).
+-define(RTV_HOPCOUNT, 2).
+-define(RTV_EXPIRE, 4).
+-define(RTV_RPIPE, 8).
+-define(RTV_SPIPE, 16#10).
+-define(RTV_SSTHRESH, 16#20).
+-define(RTV_RTT, 16#40).
+-define(RTV_RTTVAR, 16#80).
+-define(RTV, [{?RTV_MTU, mtu}, {?RTV_HOPCOUNT, hopcount}, {?RTV_EXPIRE, expire}, {?RTV_RPIPE, recvpipe}, {?RTV_SPIPE, sendpipe},
+ {?RTV_SSTHRESH, ssthresh}, {?RTV_RTT, rtt}, {?RTV_RTTVAR, rttvar}]).
+
+%% Bitmask values for rtm_addrs
+-define(RTA_DST, 1). % destination sockaddr present
+-define(RTA_GATEWAY, 2). % gateway sockaddr present
+-define(RTA_NETMASK, 4). % netmask sockaddr present
+-define(RTA_GENMASK, 8). % cloning mask sockaddr present
+-define(RTA_IFP, 16#10). % interface name sockaddr present
+-define(RTA_IFA, 16#20). % interface addr sockaddr present
+-define(RTA_AUTHOR, 16#40). % sockaddr for addr of redirect
+-define(RTA_BRD, 16#80). % for NEWADDR, broadcast or p-p dest addr
+-define(RTA, [{?RTA_DST, destination}, {?RTA_GATEWAY, gateway}, {?RTA_NETMASK, netmask},
+ {?RTA_GENMASK, genmask}, {?RTA_IFP, ifp}, {?RTA_IFA, ifa},
+ {?RTA_AUTHOR, author}, {?RTA_BRD, brd}]).
+
+%% usr/include/sys/socket.h
+-define(AF_UNSPEC, 0).
+-define(AF_INET, 2).
+-define(AF_LINK, 18).
+-define(AF_INET6, 31).
+
+-record(macos_rt, {type, % Message type, atom
+ flags, % Route flags
+ addrs, % Addresses list
+ pid, % Originating pid
+ seq, % user data
+ errno, % error number
+ use, % documented as: "from rentry"
+
+ % Metrics
+ metrics_init, % metrics that are initializing
+ metrics_lock, % metrics that are locked
+ mtu, % mtu
+ hopcount, % max hops expected
+ expire, % lifetime for route
+ recvpipe, % inbound delay-bandwith product
+ sendpipe, % outbound delay-bandwith product
+ ssthresh, % outbound gateway buffer limit
+ rtt, % estimated round trip time
+ rttvar, % estimated rtt variance
+ pksent, % packets sent using this route
+ state, % route state
+ filler, % "will be used for TCP's peer-MSS cache"
+
+ % Addresses
+ destination, % Destination
+ gateway, % Gateway
+ netmask, % netmask
+ clonemask, % (C: genmask) cloning mask
+ ifp, % interface name
+ ifp_index, % (C: index) Scope for associated ifp, if present
+ ifa, % interface address
+ author, % author of redirect
+ brd % (only for type=newaddr) broadcast or p-p destination address
+}).
diff --git a/src/fib_pf_route_socket.erl b/src/fib_pf_route_socket.erl
new file mode 100644
index 0000000..ea07003
--- /dev/null
+++ b/src/fib_pf_route_socket.erl
@@ -0,0 +1,23 @@
+-module(fib_pf_route_socket).
+-export([test/0, open/2]).
+-include("fib.hrl").
+
+test() ->
+ open(fun (_) -> ok end, fun (_) -> ok end).
+
+open(OpenFun, ParseFun) ->
+ {ok, Socket} = socket:open(?PF_ROUTE, ?SOCK_RAW, ?AF_UNSPEC),
+ OpenFun(Socket),
+ recv(Socket, ParseFun).
+
+recv(Socket, ParseFun) ->
+ case socket:recv(Socket, 0) of
+ {ok, <<Len:16/little-unsigned-integer, Rest/binary>>} ->
+ ToRead = Len - 2,
+ <<Pckt:ToRead/binary>> = Rest,
+ logger:warning("Got a packet ~p", [Pckt]),
+ ParseFun(Pckt),
+ recv(Socket, ParseFun);
+ Error ->
+ Error
+ end.
diff --git a/src/fib_sup.erl b/src/fib_sup.erl
new file mode 100644
index 0000000..06efb9d
--- /dev/null
+++ b/src/fib_sup.erl
@@ -0,0 +1,12 @@
+-module(fib_sup).
+-behaviour(supervisor).
+
+-export([start_link/0]).
+-export([init/1]).
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+init([]) ->
+ Procs = [],
+ {ok, {{one_for_one, 1, 5}, Procs}}.
diff --git a/src/fib_util.erl b/src/fib_util.erl
new file mode 100644
index 0000000..b495f25
--- /dev/null
+++ b/src/fib_util.erl
@@ -0,0 +1,41 @@
+-module(fib_util).
+-export([module/0, parse_ip_address_with_netmask/1]).
+
+module() ->
+ case os:type() of
+ {unix, linux} ->
+ {ok, fib_linux};
+ {unix, darwin} ->
+ {ok, fib_macos};
+ {unix, freebsd} ->
+ {ok, fib_freebsd};
+ unsupported ->
+ {error, {unsupported_platform, unsupported}}
+ end.
+
+-spec parse_ip_address_with_netmask(string()) -> {inet:ip_address(), non_neg_integer()}.
+parse_ip_address_with_netmask(S) ->
+ logger:warning("IPNets ~p", [S]),
+ case re:split(S, "/", [{return, list}, {parts, 2}]) of
+ [Prefix, LenStr] ->
+ {Prefix1, Scope} = ipv6_scope(Prefix),
+ {ok, Addr} = inet:parse_address(Prefix1),
+ {PrefixLen, _} = string:to_integer(LenStr),
+ {Addr, Scope, PrefixLen};
+ [Prefix] ->
+ {Prefix1, Scope} = ipv6_scope(Prefix),
+ {ok, Addr} = inet:parse_address(Prefix1),
+ PrefixLen = case inet_cidr:is_ipv6(Addr) of
+ true -> 128;
+ false -> 32
+ end,
+ {Addr, Scope, PrefixLen}
+ end.
+
+ipv6_scope(Addr) ->
+ case re:split(Addr, "%", [{return, list}, {parts, 2}]) of
+ [IP, Scope] ->
+ {IP, Scope};
+ [IP] ->
+ {IP, undefined}
+ end.