-
Notifications
You must be signed in to change notification settings - Fork 1
/
pm_netifd_pull.lua
285 lines (245 loc) · 8.61 KB
/
pm_netifd_pull.lua
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#!/usr/bin/env lua
-- -*-lua-*-
--
-- $Id: pm_netifd_pull.lua $
--
-- Author: Markus Stenberg <markus [email protected]>
--
-- Copyright (c) 2013 cisco Systems, Inc.
--
-- Created: Thu Oct 3 16:48:11 2013 mstenber
-- Last modified: Fri Dec 6 16:37:00 2013 mstenber
-- Edit time: 119 min
--
-- This handler listens to the skv change of
-- 'network-interface-update', and when it detects it, it refreshes
-- the 'network.interface dump' and propagates it to other event
-- handlers.
-- It will also update the pd.* based on the contents of the interface
-- dump.
-- (Note: push is not possible without initial pull, as pull provides
-- the openwrt interface <> real physical device mapping information)
require 'pm_handler'
module(..., package.seeall)
NETWORK_INTERFACE_UPDATED_KEY='network-interface-updated'
PROTO_HNET='hnet'
local _parent = pm_handler.pm_handler
-- abstraction class around the structure we get from ubus that
-- represents the current network state
network_interface_dump = mst.create_class{class='network_interface_dump'}
function network_interface_dump:repr_data()
return '?'
end
function network_interface_dump:get_device2interface_multimap()
if not self.device2ifo
then
local mm = mst.multimap:new{}
for i, ifo in ipairs(self.interface)
do
local dev = ifo.l3_device or ifo.device
if dev
then
mm:insert(dev, ifo)
end
end
self.device2ifo = mm
end
return self.device2ifo
end
function network_interface_dump:device2interface(d, filter)
local d2i = self:get_device2interface_multimap()
for i, v in ipairs(d2i[d] or {})
do
if (not filter or filter(v, d))
then
return v.interface
end
end
end
function network_interface_dump:ifo_is_itself_external(ifo)
-- IPv6 indicator: ipv6-prefix set
local pl = ifo['ipv6-prefix']
if pl and #pl > 0
then
return true
end
-- IPv4 indicator: has an external route and non-empty list of ipv4 addresses
local al = ifo['ipv4-address']
if al and #al > 0
then
-- now that we have IPv4 address, make sure there's default
-- route on the interface (I'm not sure if this is the way to
-- detect external IF, but as we don't inject default
-- routes(??), it should be ok)
for i, v in ipairs(ifo['route'] or {})
do
if v.target == "0.0.0.0" and v.mask == 0
then
return true
end
end
end
-- if no indicator is found, it's internal
return false
end
function network_interface_dump:device2hnet_interface(d)
return self:device2interface(d, function (v)
return v.proto == PROTO_HNET
end)
end
function network_interface_dump:device_is_external(d)
return self:device2interface(d, function (ifo)
return self:ifo_is_itself_external(ifo)
end)
end
function network_interface_dump:ifo_is_external(ifo)
-- this is nontrivial to determine; what we have to do is to make
-- sure that the underlying _device_ is external.. and that depends
-- on every interface object attached to that device.
local dev = ifo.l3_device or ifo.device
return self:device_is_external(dev)
end
function network_interface_dump:interface2device(i)
local interface_list = self.interface
mst.a(interface_list, 'no interface list?!?')
for i, v in ipairs(interface_list)
do
if v.interface == i
then
return v.l3_device or v.device
end
end
end
function network_interface_dump:iterate_interfaces(f, want_ext, want_hnet)
self:d('starting iteration', not not want_ext, not not want_hnet)
for i, ifo in ipairs(self.interface)
do
local is_hnet = ifo.proto == PROTO_HNET
local is_ext = self:ifo_is_external(ifo)
local match = (want_ext == nil or (not want_ext == not is_ext)) and
(want_hnet == nil or (not want_hnet == not is_hnet))
self:d('iteratating', ifo.interface, is_ext, is_hnet, match)
if match
then
f(ifo)
end
end
end
pm_netifd_pull = _parent:new_subclass{class='pm_netifd_pull',
sources={pm_handler.skv_source}}
function pm_netifd_pull:init()
_parent.init(self)
self.set_pd_state = mst.map:new()
self.set_dhcp_state = mst.map:new()
-- make sure we get run as soon as possible
self.last_run = 'xxx' -- force first run, even if just to set this to nil
self:queue()
end
function pm_netifd_pull:get_network_interface_dump()
local conn = self:get_ubus_connection()
self:a(conn, 'unable to connect ubus')
local r, err = conn:call('network.interface', 'dump', {})
setmetatable(r, network_interface_dump)
conn:close()
return r, err
end
function pm_netifd_pull:skv_changed(k, v)
if k == NETWORK_INTERFACE_UPDATED_KEY
then
self:d('noticed network interface update, queued')
self.updated = v
self:queue()
end
end
function pm_netifd_pull:get_state(ni)
local pd_state = mst.map:new()
local dhcp_state = mst.map:new()
local function _ext_if_iterator(ifo)
for i, p in ipairs(ifo['ipv6-prefix'] or {})
do
if ifo.delegation == false
then
local device = ifo.l3_device or ifo.device
local prefix = string.format('%s/%s', p.address, p.mask)
local now = self:time()
local pclass = tonumber(p.class)
local o = {[elsa_pa.PREFIX_KEY]=prefix,
[elsa_pa.VALID_KEY]=p.valid+now,
[elsa_pa.PREFERRED_KEY]=p.preferred+now,
-- no prefix class info for now, sigh
[elsa_pa.PREFIX_CLASS_KEY]=pclass,
}
local l = pd_state:setdefault_lazy(device, mst.array.new, mst.array)
l:insert(o)
end
end
for i, d in ipairs(ifo['dns-server'] or {})
do
local device = ifo.l3_device or ifo.device
local state = ipv6s.address_is_ipv4(d) and dhcp_state or pd_state
local l = state:setdefault_lazy(device, mst.array.new, mst.array)
l:insert{[elsa_pa.DNS_KEY]=d}
end
for i, d in ipairs(ifo['dns-search'] or {})
do
local device = ifo.l3_device or ifo.device
local l = pd_state:setdefault_lazy(device, mst.array.new, mst.array)
l:insert{[elsa_pa.DNS_SEARCH_KEY]=d}
end
end
ni:iterate_interfaces(_ext_if_iterator, true)
return pd_state, dhcp_state
end
function pm_netifd_pull:run()
self:d('run')
-- don't do anything if there does not seem to be a need
if self.last_run == self.updated
then
return
end
self.last_run = self.updated
-- there isn't any useful way how we can verify it isn't same ->
-- just forward it as-is
local ni, err = self:get_network_interface_dump()
self:a(ni, 'got error when getting interface list', err)
self._pm.network_interface_changed(ni)
-- second thing we do is update pd.* in skv; we're responsible for
-- keeping that in sync with whatever is in netifd
local pd_state, dhcp_state = self:get_state(ni)
-- synchronize them with 'known state'
mst.sync_tables(self.set_pd_state, pd_state,
-- remove
function (k)
self:d('removing pd', k)
self._pm.skv:set(elsa_pa.PD_SKVPREFIX .. k, {})
self.set_pd_state[k] = nil
end,
-- add
function (k, v)
self:d('adding pd', k, v)
self._pm.skv:set(elsa_pa.PD_SKVPREFIX .. k, v)
self.set_pd_state[k] = v
end,
-- are values same? use repr
function (k, v1, v2)
return mst.repr(v2) == mst.repr(v1)
end)
-- synchronize them with 'known state'
mst.sync_tables(self.set_dhcp_state, dhcp_state,
-- remove
function (k)
self:d('removing dhcpv4 info', k)
self._pm.skv:set(elsa_pa.DHCPV4_SKVPREFIX .. k, {})
self.set_dhcp_state[k] = nil
end,
-- add
function (k, v)
self:d('adding dhcpv4 info', k, v)
self._pm.skv:set(elsa_pa.DHCPV4_SKVPREFIX .. k, v)
self.set_dhcp_state[k] = v
end,
-- are values same? use repr
function (k, v1, v2)
return mst.repr(v2) == mst.repr(v1)
end)
end