libs/core: mark radvd as affected by network restarts
[project/luci.git] / modules / admin-full / luasrc / model / cbi / admin_network / ifaces.lua
1 --[[
2 LuCI - Lua Configuration Interface
3
4 Copyright 2008 Steven Barth <steven@midlink.org>
5 Copyright 2008 Jo-Philipp Wich <xm@subsignal.org>
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 $Id$
14 ]]--
15
16 local fs = require "nixio.fs"
17 local ut = require "luci.util"
18 local nw = require "luci.model.network"
19 local fw = require "luci.model.firewall"
20
21 arg[1] = arg[1] or ""
22
23 local has_dnsmasq = fs.access("/etc/config/dhcp")
24 local has_firewall = fs.access("/etc/config/firewall")
25
26 local has_3g = fs.access("/usr/bin/gcom")
27 local has_pptp = fs.access("/usr/sbin/pptp")
28 local has_pppd = fs.access("/usr/sbin/pppd")
29 local has_pppoe = fs.glob("/usr/lib/pppd/*/rp-pppoe.so")()
30 local has_pppoa = fs.glob("/usr/lib/pppd/*/pppoatm.so")()
31 local has_ipv6 = fs.access("/proc/net/ipv6_route")
32 local has_6in4 = fs.access("/lib/network/6in4.sh")
33 local has_6to4 = fs.access("/lib/network/6to4.sh")
34
35 m = Map("network", translate("Interfaces") .. " - " .. arg[1]:upper(), translate("On this page you can configure the network interfaces. You can bridge several interfaces by ticking the \"bridge interfaces\" field and enter the names of several network interfaces separated by spaces. You can also use <abbr title=\"Virtual Local Area Network\">VLAN</abbr> notation <samp>INTERFACE.VLANNR</samp> (<abbr title=\"for example\">e.g.</abbr>: <samp>eth0.1</samp>)."))
36 m:chain("wireless")
37
38 if has_firewall then
39 m:chain("firewall")
40 end
41
42 nw.init(m.uci)
43 fw.init(m.uci)
44
45
46 local net = nw:get_network(arg[1])
47
48 -- redirect to overview page if network does not exist anymore (e.g. after a revert)
49 if not net then
50 luci.http.redirect(luci.dispatcher.build_url("admin/network/network"))
51 return
52 end
53
54 local ifc = net:get_interfaces()[1]
55
56 s = m:section(NamedSection, arg[1], "interface", translate("Common Configuration"))
57 s.addremove = false
58
59 s:tab("general", translate("General Setup"))
60 if has_ipv6 then s:tab("ipv6", translate("IPv6 Setup")) end
61 if has_pppd then s:tab("ppp", translate("PPP Settings")) end
62 if has_pppoa then s:tab("atm", translate("ATM Settings")) end
63 if has_6in4 or has_6to4 then s:tab("tunnel", translate("Tunnel Settings")) end
64 s:tab("physical", translate("Physical Settings"))
65 if has_firewall then s:tab("firewall", translate("Firewall Settings")) end
66
67 st = s:taboption("general", DummyValue, "__status", translate("Status"))
68 st.template = "admin_network/iface_status"
69 st.network = arg[1]
70
71 --[[
72 back = s:taboption("general", DummyValue, "_overview", translate("Overview"))
73 back.value = ""
74 back.titleref = luci.dispatcher.build_url("admin", "network", "network")
75 ]]
76
77 p = s:taboption("general", ListValue, "proto", translate("Protocol"))
78 p.override_scheme = true
79 p.default = "static"
80 p:value("static", translate("static"))
81 p:value("dhcp", "DHCP")
82 if has_pppd then p:value("ppp", "PPP") end
83 if has_pppoe then p:value("pppoe", "PPPoE") end
84 if has_pppoa then p:value("pppoa", "PPPoA") end
85 if has_3g then p:value("3g", "UMTS/3G") end
86 if has_pptp then p:value("pptp", "PPTP") end
87 if has_6in4 then p:value("6in4", "6in4") end
88 if has_6to4 then p:value("6to4", "6to4") end
89 p:value("none", translate("none"))
90
91 if not ( has_pppd and has_pppoe and has_pppoa and has_3g and has_pptp ) then
92 p.description = translate("You need to install \"comgt\" for UMTS/GPRS, \"ppp-mod-pppoe\" for PPPoE, \"ppp-mod-pppoa\" for PPPoA or \"pptp\" for PPtP support")
93 end
94
95 br = s:taboption("physical", Flag, "type", translate("Bridge interfaces"), translate("creates a bridge over specified interface(s)"))
96 br.enabled = "bridge"
97 br.rmempty = true
98 br:depends("proto", "static")
99 br:depends("proto", "dhcp")
100 br:depends("proto", "none")
101
102 stp = s:taboption("physical", Flag, "stp", translate("Enable <abbr title=\"Spanning Tree Protocol\">STP</abbr>"),
103 translate("Enables the Spanning Tree Protocol on this bridge"))
104 stp:depends("type", "bridge")
105 stp.rmempty = true
106
107 ifname_single = s:taboption("physical", Value, "ifname_single", translate("Interface"))
108 ifname_single.template = "cbi/network_ifacelist"
109 ifname_single.widget = "radio"
110 ifname_single.nobridges = true
111 ifname_single.network = arg[1]
112 ifname_single.rmempty = true
113 ifname_single:depends({ type = "", proto = "static" })
114 ifname_single:depends({ type = "", proto = "dhcp" })
115 ifname_single:depends({ type = "", proto = "pppoe" })
116 ifname_single:depends({ type = "", proto = "pppoa" })
117 ifname_single:depends({ type = "", proto = "none" })
118
119 function ifname_single.cfgvalue(self, s)
120 return self.map.uci:get("network", s, "ifname")
121 end
122
123 function ifname_single.write(self, s, val)
124 local n = nw:get_network(s)
125 if n then
126 local i
127 for _, i in ipairs(n:get_interfaces()) do
128 n:del_interface(i)
129 end
130
131 for i in ut.imatch(val) do
132 n:add_interface(i)
133
134 -- if this is not a bridge, only assign first interface
135 if self.option == "ifname_single" then
136 break
137 end
138 end
139 end
140 end
141
142 ifname_multi = s:taboption("physical", Value, "ifname_multi", translate("Interface"))
143 ifname_multi.template = "cbi/network_ifacelist"
144 ifname_multi.nobridges = true
145 ifname_multi.network = arg[1]
146 ifname_multi.widget = "checkbox"
147 ifname_multi:depends("type", "bridge")
148 ifname_multi.cfgvalue = ifname_single.cfgvalue
149 ifname_multi.write = ifname_single.write
150
151
152 if has_firewall then
153 fwzone = s:taboption("firewall", Value, "_fwzone",
154 translate("Create / Assign firewall-zone"),
155 translate("Choose the firewall zone you want to assign to this interface. Select <em>unspecified</em> to remove the interface from the associated zone or fill out the <em>create</em> field to define a new zone and attach the interface to it."))
156
157 fwzone.template = "cbi/firewall_zonelist"
158 fwzone.network = arg[1]
159 fwzone.rmempty = false
160
161 function fwzone.cfgvalue(self, section)
162 self.iface = section
163 local z = fw:get_zone_by_network(section)
164 return z and z:name()
165 end
166
167 function fwzone.write(self, section, value)
168 local zone = fw:get_zone(value)
169
170 if not zone and value == '-' then
171 value = m:formvalue(self:cbid(section) .. ".newzone")
172 if value and #value > 0 then
173 zone = fw:add_zone(value)
174 else
175 fw:del_network(section)
176 end
177 end
178
179 if zone then
180 fw:del_network(section)
181 zone:add_network(section)
182 end
183 end
184 end
185
186 ipaddr = s:taboption("general", Value, "ipaddr", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address"))
187 ipaddr.optional = true
188 ipaddr.datatype = "ip4addr"
189 ipaddr:depends("proto", "static")
190
191 nm = s:taboption("general", Value, "netmask", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"))
192 nm.optional = true
193 nm.datatype = "ip4addr"
194 nm:depends("proto", "static")
195 nm:value("255.255.255.0")
196 nm:value("255.255.0.0")
197 nm:value("255.0.0.0")
198
199 gw = s:taboption("general", Value, "gateway", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Gateway"))
200 gw.optional = true
201 gw.datatype = "ip4addr"
202 gw:depends("proto", "static")
203
204 bcast = s:taboption("general", Value, "broadcast", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Broadcast"))
205 bcast.optional = true
206 bcast.datatype = "ip4addr"
207 bcast:depends("proto", "static")
208
209 if has_ipv6 then
210 ip6addr = s:taboption("ipv6", Value, "ip6addr", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Address"), translate("<abbr title=\"Classless Inter-Domain Routing\">CIDR</abbr>-Notation: address/prefix"))
211 ip6addr.optional = true
212 ip6addr.datatype = "ip6addr"
213 ip6addr:depends("proto", "static")
214 ip6addr:depends("proto", "6in4")
215
216 ip6gw = s:taboption("ipv6", Value, "ip6gw", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Gateway"))
217 ip6gw.optional = true
218 ip6gw.datatype = "ip6addr"
219 ip6gw:depends("proto", "static")
220 end
221
222 dns = s:taboption("general", DynamicList, "dns", translate("<abbr title=\"Domain Name System\">DNS</abbr>-Server"),
223 translate("You can specify multiple DNS servers here, press enter to add a new entry. Servers entered here will override " ..
224 "automatically assigned ones."))
225
226 dns.optional = true
227 dns.cast = "string"
228 dns.datatype = "ipaddr"
229 dns:depends({ peerdns = "", proto = "static" })
230 dns:depends({ peerdns = "", proto = "dhcp" })
231 dns:depends({ peerdns = "", proto = "pppoe" })
232 dns:depends({ peerdns = "", proto = "pppoa" })
233 dns:depends({ peerdns = "", proto = "none" })
234
235 mtu = s:taboption("physical", Value, "mtu", "MTU")
236 mtu.optional = true
237 mtu.datatype = "uinteger"
238 mtu.placeholder = 1500
239
240 srv = s:taboption("general", Value, "server", translate("<abbr title=\"Point-to-Point Tunneling Protocol\">PPTP</abbr>-Server"))
241 srv:depends("proto", "pptp")
242 srv.optional = false
243 srv.datatype = "host"
244
245 if has_6in4 then
246 peer = s:taboption("general", Value, "peeraddr", translate("Server IPv4-Address"))
247 peer.optional = false
248 peer.datatype = "ip4addr"
249 peer:depends("proto", "6in4")
250 end
251
252 if has_6in4 or has_6to4 then
253 ttl = s:taboption("physical", Value, "ttl", translate("TTL"))
254 ttl.default = "64"
255 ttl.optional = true
256 ttl.datatype = "uinteger"
257 ttl:depends("proto", "6in4")
258 ttl:depends("proto", "6to4")
259 end
260
261 if has_6to4 then
262 advi = s:taboption("general", Value, "adv_interface", translate("Advertise IPv6 on network"))
263 advi.widget = "checkbox"
264 advi.exclude = arg[1]
265 advi.default = "lan"
266 advi.template = "cbi/network_netlist"
267 advi.nocreate = true
268 advi.nobridges = true
269 advi:depends("proto", "6to4")
270
271 advn = s:taboption("general", Value, "adv_subnet", translate("Advertised network ID"), translate("Allowed range is 1 to FFFF"))
272 advn.default = "1"
273 advn:depends("proto", "6to4")
274
275 function advn.write(self, section, value)
276 value = tonumber(value, 16) or 1
277
278 if value > 65535 then value = 65535
279 elseif value < 1 then value = 1 end
280
281 Value.write(self, section, "%X" % value)
282 end
283 end
284
285 mac = s:taboption("physical", Value, "macaddr", translate("<abbr title=\"Media Access Control\">MAC</abbr>-Address"))
286 mac:depends("proto", "none")
287 mac:depends("proto", "static")
288 mac:depends("proto", "dhcp")
289 mac.placeholder = ifc and ifc:mac():upper()
290
291 if has_3g then
292 service = s:taboption("general", ListValue, "service", translate("Service type"))
293 service:value("", translate("-- Please choose --"))
294 service:value("umts", "UMTS/GPRS")
295 service:value("cdma", "CDMA")
296 service:value("evdo", "EV-DO")
297 service:depends("proto", "3g")
298 service.rmempty = true
299
300 apn = s:taboption("general", Value, "apn", translate("Access point (APN)"))
301 apn:depends("proto", "3g")
302
303 pincode = s:taboption("general", Value, "pincode",
304 translate("PIN code"),
305 translate("Make sure that you provide the correct pin code here or you might lock your sim card!")
306 )
307 pincode:depends("proto", "3g")
308 end
309
310 if has_6in4 then
311 tunid = s:taboption("general", Value, "tunnelid", translate("HE.net Tunnel ID"))
312 tunid.optional = true
313 tunid.datatype = "uinteger"
314 tunid:depends("proto", "6in4")
315 end
316
317 if has_pppd or has_pppoe or has_pppoa or has_3g or has_pptp or has_6in4 then
318 user = s:taboption("general", Value, "username", translate("Username"))
319 user.rmempty = true
320 user:depends("proto", "pptp")
321 user:depends("proto", "pppoe")
322 user:depends("proto", "pppoa")
323 user:depends("proto", "ppp")
324 user:depends("proto", "3g")
325 user:depends("proto", "6in4")
326
327 pass = s:taboption("general", Value, "password", translate("Password"))
328 pass.rmempty = true
329 pass.password = true
330 pass:depends("proto", "pptp")
331 pass:depends("proto", "pppoe")
332 pass:depends("proto", "pppoa")
333 pass:depends("proto", "ppp")
334 pass:depends("proto", "3g")
335 pass:depends("proto", "6in4")
336 end
337
338 if has_pppd or has_pppoe or has_pppoa or has_3g or has_pptp then
339 ka = s:taboption("ppp", Value, "keepalive",
340 translate("Keep-Alive"),
341 translate("Number of failed connection tests to initiate automatic reconnect")
342 )
343 ka:depends("proto", "pptp")
344 ka:depends("proto", "pppoe")
345 ka:depends("proto", "pppoa")
346 ka:depends("proto", "ppp")
347 ka:depends("proto", "3g")
348
349 demand = s:taboption("ppp", Value, "demand",
350 translate("Automatic Disconnect"),
351 translate("Time (in seconds) after which an unused connection will be closed")
352 )
353 demand.optional = true
354 demand.datatype = "uinteger"
355 demand:depends("proto", "pptp")
356 demand:depends("proto", "pppoe")
357 demand:depends("proto", "pppoa")
358 demand:depends("proto", "ppp")
359 demand:depends("proto", "3g")
360 end
361
362 if has_pppoa then
363 encaps = s:taboption("atm", ListValue, "encaps", translate("PPPoA Encapsulation"))
364 encaps:depends("proto", "pppoa")
365 encaps:value("vc", "VC-Mux")
366 encaps:value("llc", "LLC")
367
368 atmdev = s:taboption("atm", Value, "atmdev", translate("ATM device number"))
369 atmdev:depends("proto", "pppoa")
370 atmdev.default = "0"
371 atmdev.datatype = "uinteger"
372
373 vci = s:taboption("atm", Value, "vci", translate("ATM Virtual Channel Identifier (VCI)"))
374 vci:depends("proto", "pppoa")
375 vci.default = "35"
376 vci.datatype = "uinteger"
377
378 vpi = s:taboption("atm", Value, "vpi", translate("ATM Virtual Path Identifier (VPI)"))
379 vpi:depends("proto", "pppoa")
380 vpi.default = "8"
381 vpi.datatype = "uinteger"
382 end
383
384 if has_pptp or has_pppd or has_pppoe or has_pppoa or has_3g then
385 device = s:taboption("general", Value, "device",
386 translate("Modem device"),
387 translate("The device node of your modem, e.g. /dev/ttyUSB0")
388 )
389 device:depends("proto", "ppp")
390 device:depends("proto", "3g")
391
392 defaultroute = s:taboption("ppp", Flag, "defaultroute",
393 translate("Replace default route"),
394 translate("Let pppd replace the current default route to use the PPP interface after successful connect")
395 )
396 defaultroute:depends("proto", "ppp")
397 defaultroute:depends("proto", "pppoa")
398 defaultroute:depends("proto", "pppoe")
399 defaultroute:depends("proto", "pptp")
400 defaultroute:depends("proto", "3g")
401 defaultroute.default = defaultroute.enabled
402
403 peerdns = s:taboption("ppp", Flag, "peerdns",
404 translate("Use peer DNS"),
405 translate("Configure the local DNS server to use the name servers adverticed by the PPP peer")
406 )
407 peerdns:depends("proto", "ppp")
408 peerdns:depends("proto", "pppoa")
409 peerdns:depends("proto", "pppoe")
410 peerdns:depends("proto", "pptp")
411 peerdns:depends("proto", "3g")
412 peerdns.default = peerdns.enabled
413
414 if has_ipv6 then
415 ipv6 = s:taboption("ppp", Flag, "ipv6", translate("Enable IPv6 on PPP link") )
416 ipv6:depends("proto", "ppp")
417 ipv6:depends("proto", "pppoa")
418 ipv6:depends("proto", "pppoe")
419 ipv6:depends("proto", "pptp")
420 ipv6:depends("proto", "3g")
421 end
422
423 connect = s:taboption("ppp", Value, "connect",
424 translate("Connect script"),
425 translate("Let pppd run this script after establishing the PPP link")
426 )
427 connect:depends("proto", "ppp")
428 connect:depends("proto", "pppoe")
429 connect:depends("proto", "pppoa")
430 connect:depends("proto", "pptp")
431 connect:depends("proto", "3g")
432
433 disconnect = s:taboption("ppp", Value, "disconnect",
434 translate("Disconnect script"),
435 translate("Let pppd run this script before tearing down the PPP link")
436 )
437 disconnect:depends("proto", "ppp")
438 disconnect:depends("proto", "pppoe")
439 disconnect:depends("proto", "pppoa")
440 disconnect:depends("proto", "pptp")
441 disconnect:depends("proto", "3g")
442
443 pppd_options = s:taboption("ppp", Value, "pppd_options",
444 translate("Additional pppd options"),
445 translate("Specify additional command line arguments for pppd here")
446 )
447 pppd_options:depends("proto", "ppp")
448 pppd_options:depends("proto", "pppoa")
449 pppd_options:depends("proto", "pppoe")
450 pppd_options:depends("proto", "pptp")
451 pppd_options:depends("proto", "3g")
452
453 maxwait = s:taboption("ppp", Value, "maxwait",
454 translate("Setup wait time"),
455 translate("Seconds to wait for the modem to become ready before attempting to connect")
456 )
457 maxwait:depends("proto", "3g")
458 maxwait.default = "0"
459 maxwait.optional = true
460 maxwait.datatype = "uinteger"
461 end
462
463 s2 = m:section(TypedSection, "alias", translate("IP-Aliases"))
464 s2.addremove = true
465
466 s2:depends("interface", arg[1])
467 s2.defaults.interface = arg[1]
468
469 s2:tab("general", translate("General Setup"))
470 s2.defaults.proto = "static"
471
472 ip = s2:taboption("general", Value, "ipaddr", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address"))
473 ip.optional = true
474 ip.datatype = "ip4addr"
475
476 nm = s2:taboption("general", Value, "netmask", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"))
477 nm.optional = true
478 nm.datatype = "ip4addr"
479 nm:value("255.255.255.0")
480 nm:value("255.255.0.0")
481 nm:value("255.0.0.0")
482
483 gw = s2:taboption("general", Value, "gateway", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Gateway"))
484 gw.optional = true
485 gw.datatype = "ip4addr"
486
487 if has_ipv6 then
488 s2:tab("ipv6", translate("IPv6 Setup"))
489
490 ip6 = s2:taboption("ipv6", Value, "ip6addr", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Address"), translate("<abbr title=\"Classless Inter-Domain Routing\">CIDR</abbr>-Notation: address/prefix"))
491 ip6.optional = true
492 ip6.datatype = "ip6addr"
493
494 gw6 = s2:taboption("ipv6", Value, "ip6gw", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Gateway"))
495 gw6.optional = true
496 gw6.datatype = "ip6addr"
497 end
498
499 s2:tab("advanced", translate("Advanced Settings"))
500
501 bcast = s2:taboption("advanced", Value, "bcast", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Broadcast"))
502 bcast.optional = true
503 bcast.datatype = "ip4addr"
504
505 dns = s2:taboption("advanced", Value, "dns", translate("<abbr title=\"Domain Name System\">DNS</abbr>-Server"))
506 dns.optional = true
507 dns.datatype = "ip4addr"
508
509
510 --
511 -- Display DNS settings if dnsmasq is available
512 --
513
514 if has_dnsmasq then
515 m2 = Map("dhcp", "", "")
516 function m2.on_parse()
517 local has_section = false
518
519 m2.uci:foreach("dhcp", "dhcp", function(s)
520 if s.interface == arg[1] then
521 has_section = true
522 return false
523 end
524 end)
525
526 if not has_section then
527 m2.uci:section("dhcp", "dhcp", nil, { interface = arg[1], ignore = "1" })
528 m2.uci:save("dhcp")
529 end
530 end
531
532 s = m2:section(TypedSection, "dhcp", translate("DHCP Server"))
533 s.addremove = false
534 s.anonymous = true
535 s:tab("general", translate("General Setup"))
536 s:tab("advanced", translate("Advanced Settings"))
537
538 function s.filter(self, section)
539 return m2.uci:get("dhcp", section, "interface") == arg[1]
540 end
541
542 local ignore = s:taboption("general", Flag, "ignore",
543 translate("Ignore interface"),
544 translate("Disable <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr> for " ..
545 "this interface."))
546
547 ignore.rmempty = false
548
549 local start = s:taboption("general", Value, "start", translate("Start"),
550 translate("Lowest leased address as offset from the network address."))
551 start.optional = true
552 start.datatype = "uinteger"
553 start.default = "100"
554
555 local limit = s:taboption("general", Value, "limit", translate("Limit"),
556 translate("Maximum number of leased addresses."))
557 limit.optional = true
558 limit.datatype = "uinteger"
559 limit.default = "150"
560
561 local ltime = s:taboption("general", Value, "leasetime", translate("Leasetime"),
562 translate("Expiry time of leased addresses, minimum is 2 Minutes (<code>2m</code>)."))
563 ltime.rmempty = true
564 ltime.default = "12h"
565
566 local dd = s:taboption("advanced", Flag, "dynamicdhcp",
567 translate("Dynamic <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr>"),
568 translate("Dynamically allocate DHCP addresses for clients. If disabled, only " ..
569 "clients having static leases will be served."))
570 dd.default = dd.enabled
571
572 s:taboption("advanced", Flag, "force", translate("Force"),
573 translate("Force DHCP on this network even if another server is detected."))
574
575 -- XXX: is this actually useful?
576 --s:taboption("advanced", Value, "name", translate("Name"),
577 -- translate("Define a name for this network."))
578
579 mask = s:taboption("advanced", Value, "netmask",
580 translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"),
581 translate("Override the netmask sent to clients. Normally it is calculated " ..
582 "from the subnet that is served."))
583
584 mask.optional = true
585 mask.datatype = "ip4addr"
586
587 s:taboption("advanced", DynamicList, "dhcp_option", translate("DHCP-Options"),
588 translate("Define additional DHCP options, for example \"<code>6,192.168.2.1," ..
589 "192.168.2.2</code>\" which advertises different DNS servers to clients."))
590
591 for i, n in ipairs(s.children) do
592 if n ~= ignore then
593 n:depends("ignore", "")
594 end
595 end
596 end
597
598 return m, m2