aboutsummaryrefslogtreecommitdiff
path: root/src/pf_route_freebsd.erl
blob: 871211c6e5b3efa9bf66a5c451b691764143ccf3 (plain) (blame)
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"]).