1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
-module(pf_route_freebsd).
-include("pf_route.hrl").
-include("pf_route_freebsd.hrl").
-export([default_table/0, current_table/0, tables/0]).
-export([list/1]).
-export([monitor/1]).
-export([parse_pf_route_packet/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.
monitor(Table) ->
Self = self(),
OpenFun = fun (_Socket) ->
{todo_set_table, Table}
end,
PcktFun = fun (Binary) ->
Packet = parse_pf_route_packet(Binary),
Self ! {pf_route_monitor, Packet}
end,
spawn_link(fun () ->
pf_route_pf_route_socket:open(?PF_ROUTE, ?SOCK_RAW, ?AF_UNSPEC, OpenFun, PcktFun)
end).
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_ADD:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, add);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_DELETE:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, delete);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_CHANGE:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, change);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_GET:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, get);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_LOSING:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, losing);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_REDIRECT:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, redirect);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_MISS:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, miss);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_LOCK:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, lock);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_RESOLVE:?U_CHAR, Binary/binary>>) -> parse_rt_msghdr(Binary, resolve);
%%parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_NEWADDR:?U_CHAR, Binary/binary>>) -> parse_ifa_msghdr(Binary, newaddr);
%%parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_DELADDR:?U_CHAR, Binary/binary>>) -> parse_ifa_msghdr(Binary, deladdr);
parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_IFINFO:?U_CHAR, Binary/binary>>) -> parse_if_msghdr(Binary, ifinfo);
%%parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_NEWMADDR:?U_CHAR, Binary/binary>>) -> parse_ifma_msghdr(Binary, newmaddr);
%%parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_DELMADDR:?U_CHAR, Binary/binary>>) -> parse_if_mamsghdr(Binary, delmaddr);
%%parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_IFANNOUNCE:?U_CHAR, Binary/binary>>) -> parse_ifannounce_msghdr(Binary, ifannounce);
%%parse_pf_route_packet(<<?RTM_VERSION:?U_CHAR, ?RTM_IEEE80211:?U_CHAR, Binary/binary>>) -> parse_ifannounce_msghdr(Binary, ieee80211).
parse_pf_route_packet(Binary) -> {error, {invalid_packet, Binary}}.
parse_rt_msghdr(<<% struct rt_msghdr (len, ver and type removed)
IfpIndex:?U_SHORT,
_Spare1:?U_SHORT,
Flags:?INT,
Addrs:?INT,
Pid:?PID_T,
Seq:?INT,
Errno:?INT,
FMask:?INT, % bitmask used in RTM_CHANGE
Inits:?U_LONG,
% rt_metrics
Locks:?U_LONG,
Mtu:?U_LONG,
Hopcount:?U_LONG,
Expire:?U_LONG,
Recv:?U_LONG,
Send:?U_LONG,
Ssthresh:?U_LONG,
Rtt:?U_LONG,
Rttvar:?U_LONG,
Pksent:?U_LONG,
Weight:?U_LONG,
Nhidx:?U_LONG,
Filler1:?U_LONG,
Filler2:?U_LONG,
% List of addrs
BinaryAddrs/binary
>>, Type) ->
Rt = #freebsd_rt{type = Type, ifp_index = IfpIndex, flags = Flags, addrs = Addrs, pid = Pid, seq = Seq, errno = Errno,
fmask = FMask, metrics_init = Inits, metrics_lock = Locks, mtu = Mtu, hopcount = Hopcount, expire = Expire,
recvpipe = Recv, sendpipe = Send, ssthresh = Ssthresh, rtt = Rtt, rttvar = Rttvar, pksent = Pksent,
weight = Weight, nhidx = Nhidx, filler = {Filler1, Filler2}},
parse_rt(Rt, BinaryAddrs).
parse_rt(Rt0, BinaryAddrs) ->
Rt1 = Rt0#freebsd_rt{flags = pf_route_util:read_bitmask(?RTF, Rt0#freebsd_rt.flags),
addrs = pf_route_util:read_bitmask(?RTA, Rt0#freebsd_rt.addrs),
metrics_init = pf_route_util:read_bitmask(?RTV, Rt0#freebsd_rt.metrics_init),
metrics_lock = pf_route_util:read_bitmask(?RTV, Rt0#freebsd_rt.metrics_lock)},
logger:warning("pf_route_freebsd:monitor Rt = ~p~nBinary: ~p", [Rt1, BinaryAddrs]),
Rt = get_addrs(BinaryAddrs, Rt1#freebsd_rt.addrs, Rt1),
logger:warning("pf_route_freebsd:monitor Rt = ~p", [Rt]),
Rt.
get_addrs(<<Len:?U_CHAR, Family:?U_CHAR, Content/binary>>, [Type | Rest], Rt) ->
<<BinaryAddr:(Len - 2)/binary, Binary/binary>> = Content,
get_addrs(Binary, Rest, set_rt_addr(Type, process_address(BinaryAddr, Family), Rt));
get_addrs(_, [], Rt) ->
Rt.
set_rt_addr(destination, Value, Rt) -> Rt#freebsd_rt{destination = Value};
set_rt_addr(gateway, Value, Rt) -> Rt#freebsd_rt{gateway = Value};
set_rt_addr(netmask, Value, Rt) -> Rt#freebsd_rt{netmask = Value};
set_rt_addr(ifp, Value, Rt) -> Rt#freebsd_rt{ifp = Value};
set_rt_addr(ifa, Value, Rt) -> Rt#freebsd_rt{ifa = Value};
set_rt_addr(author, Value, Rt) -> Rt#freebsd_rt{author = Value};
set_rt_addr(brd, Value, Rt) -> Rt#freebsd_rt{brd = Value}.
process_address(<<_Port:?U_SHORT, A, B, C, D, _Zero:64/little-unsigned-integer>>, ?AF_INET) ->
{A, B, C, D};
process_address(<<Index:?U_SHORT, Type:?U_CHAR, NameLen:?U_CHAR, AddrLen:?U_CHAR, SelLen:?U_CHAR, Data/binary>>, ?AF_LINK) ->
<<Name:NameLen/binary, Address:AddrLen/binary, Selector:SelLen/binary, _Rest/binary>> = Data,
Link = #freebsd_link{index = Index, type = Type, name = Name, address = binary_to_list(Address), selector = Selector},
Link;
process_address(Data, Family) ->
logger:error("freebsd:process_address: no clause for family ~p = ~p", [Family, Data]),
{sockaddr, Family, Data}.
parse_if_msghdr(<<% if_msghdr
Addrs:?INT,
Flags:?INT,
Index:?U_SHORT,
_Spare1:?U_SHORT,
% if_data
Type:?UINT8,
Physical:?UINT8,
AddrLen:?UINT8,
HdrLen:?UINT8,
Linkstate:?UINT8,
Vhid:?UINT8,
DataLen:?UINT16,
Mtu:?UINT32,
Metric:?UINT32,
Baudrate:?UINT64,
IPackets:?UINT64,
IErrors:?UINT64,
OPackets:?UINT64,
OErrors:?UINT64,
Collisions:?UINT64,
IBytes:?UINT64,
OBytes:?UINT64,
IMcasts:?UINT64,
OMcasts:?UINT64,
IQdrops:?UINT64,
OQdrops:?UINT64,
Noproto:?UINT64,
Hwassist:?UINT64,
Data/binary>>, ifinfo) ->
If = #freebsd_if{addrs = pf_route_util:read_bitmask(?RTA, Addrs), flags = pf_route_util:read_bitmask(?IFF, Flags),
index = Index, type = Type, physical = Physical,
addrlen = AddrLen,hdrlen = HdrLen,
linkstate = maps:get(Linkstate, ?LINK_STATE), vhid = Vhid,datalen = DataLen,mtu = Mtu,metric = Metric,
baudrate = Baudrate,
ipackets = IPackets,ierrors = IErrors,opackets = OPackets,oerrors = OErrors,collisions = Collisions,
ibytes = IBytes,obytes = OBytes,imcasts = IMcasts,omcasts = OMcasts,iqdrops = IQdrops,
oqdrops = OQdrops,noproto = Noproto,hwassist = Hwassist},
logger:warning("pf_route_freebsd: freebsd_if: ~p~nBinary: ~p", [If, Data]),
If.
do_list(Table) ->
os:cmd(["setfib ", Table, " netstat -rn"]).
|