summaryrefslogtreecommitdiff
path: root/src/fib_macos.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/fib_macos.erl')
-rw-r--r--src/fib_macos.erl179
1 files changed, 179 insertions, 0 deletions
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.