fdff48d4931b39e493857c641664e8e3c55554ed
[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_3g     = fs.access("/usr/bin/gcom")
24 local has_pptp   = fs.access("/usr/sbin/pptp")
25 local has_pppd   = fs.access("/usr/sbin/pppd")
26 local has_pppoe  = fs.glob("/usr/lib/pppd/*/rp-pppoe.so")()
27 local has_pppoa  = fs.glob("/usr/lib/pppd/*/pppoatm.so")()
28 local has_ipv6   = fs.access("/proc/net/ipv6_route")
29 local has_6in4   = fs.access("/lib/network/6in4.sh")
30 local has_6to4   = fs.access("/lib/network/6to4.sh")
31
32 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>)."))
33 m:chain("firewall")
34 m:chain("wireless")
35
36 nw.init(m.uci)
37 fw.init(m.uci)
38
39
40 local net = nw:get_network(arg[1])
41
42 -- redirect to overview page if network does not exist anymore (e.g. after a revert)
43 if not net then
44         luci.http.redirect(luci.dispatcher.build_url("admin/network/network"))
45         return
46 end
47
48 local ifc = net:get_interfaces()[1]
49
50 s = m:section(NamedSection, arg[1], "interface", translate("Common Configuration"))
51 s.addremove = false
52
53 s:tab("general", translate("General Setup"))
54 if has_ipv6  then s:tab("ipv6", translate("IPv6 Setup")) end
55 if has_pppd  then s:tab("ppp", translate("PPP Settings")) end
56 if has_pppoa then s:tab("atm", translate("ATM Settings")) end
57 if has_6in4 or has_6to4 then s:tab("tunnel", translate("Tunnel Settings")) end
58 s:tab("physical", translate("Physical Settings"))
59 s:tab("firewall", translate("Firewall Settings"))
60
61 st = s:taboption("general", DummyValue, "__status", translate("Status"))
62 st.template = "admin_network/iface_status"
63 st.network  = arg[1]
64
65 --[[
66 back = s:taboption("general", DummyValue, "_overview", translate("Overview"))
67 back.value = ""
68 back.titleref = luci.dispatcher.build_url("admin", "network", "network")
69 ]]
70
71 p = s:taboption("general", ListValue, "proto", translate("Protocol"))
72 p.override_scheme = true
73 p.default = "static"
74 p:value("static", translate("static"))
75 p:value("dhcp", "DHCP")
76 if has_pppd  then p:value("ppp",   "PPP")     end
77 if has_pppoe then p:value("pppoe", "PPPoE")   end
78 if has_pppoa then p:value("pppoa", "PPPoA")   end
79 if has_3g    then p:value("3g",    "UMTS/3G") end
80 if has_pptp  then p:value("pptp",  "PPTP")    end
81 if has_6in4  then p:value("6in4",  "6in4")    end
82 if has_6to4  then p:value("6to4",  "6to4")    end
83 p:value("none", translate("none"))
84
85 if not ( has_pppd and has_pppoe and has_pppoa and has_3g and has_pptp ) then
86         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")
87 end
88
89 br = s:taboption("physical", Flag, "type", translate("Bridge interfaces"), translate("creates a bridge over specified interface(s)"))
90 br.enabled = "bridge"
91 br.rmempty = true
92 br:depends("proto", "static")
93 br:depends("proto", "dhcp")
94 br:depends("proto", "none")
95
96 stp = s:taboption("physical", Flag, "stp", translate("Enable <abbr title=\"Spanning Tree Protocol\">STP</abbr>"),
97         translate("Enables the Spanning Tree Protocol on this bridge"))
98 stp:depends("type", "bridge")
99 stp.rmempty = true
100
101 ifname_single = s:taboption("physical", Value, "ifname_single", translate("Interface"))
102 ifname_single.template = "cbi/network_ifacelist"
103 ifname_single.widget = "radio"
104 ifname_single.nobridges = true
105 ifname_single.network = arg[1]
106 ifname_single.rmempty = true
107 ifname_single:depends({ type = "", proto = "static" })
108 ifname_single:depends({ type = "", proto = "dhcp"   })
109 ifname_single:depends({ type = "", proto = "pppoe"  })
110 ifname_single:depends({ type = "", proto = "pppoa"  })
111 ifname_single:depends({ type = "", proto = "none"   })
112
113 function ifname_single.cfgvalue(self, s)
114         return self.map.uci:get("network", s, "ifname")
115 end
116
117 function ifname_single.write(self, s, val)
118         local n = nw:get_network(s)
119         if n then
120                 local i
121                 for _, i in ipairs(n:get_interfaces()) do
122                         n:del_interface(i)
123                 end
124
125                 for i in ut.imatch(val) do
126                         n:add_interface(i)
127
128                         -- if this is not a bridge, only assign first interface
129                         if self.option == "ifname_single" then
130                                 break
131                         end
132                 end
133         end
134 end
135
136 ifname_multi = s:taboption("physical", Value, "ifname_multi", translate("Interface"))
137 ifname_multi.template = "cbi/network_ifacelist"
138 ifname_multi.nobridges = true
139 ifname_multi.network = arg[1]
140 ifname_multi.widget = "checkbox"
141 ifname_multi:depends("type", "bridge")
142 ifname_multi.cfgvalue = ifname_single.cfgvalue
143 ifname_multi.write = ifname_single.write
144
145
146 local fwd_to, fwd_from
147
148 fwzone = s:taboption("firewall", Value, "_fwzone",
149         translate("Create / Assign firewall-zone"),
150         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."))
151
152 fwzone.template = "cbi/firewall_zonelist"
153 fwzone.network = arg[1]
154 fwzone.rmempty = false
155
156 function fwzone.cfgvalue(self, section)
157         self.iface = section
158         local z = fw:get_zone_by_network(section)
159         return z and z:name()
160 end
161
162 function fwzone.write(self, section, value)
163         local zone = fw:get_zone(value)
164
165         if not zone and value == '-' then
166                 value = m:formvalue(self:cbid(section) .. ".newzone")
167                 if value and #value > 0 then
168                         zone = fw:add_zone(value)
169                 else
170                         fw:del_network(section)
171                 end
172         end
173
174         if zone then
175                 fw:del_network(section)
176                 zone:add_network(section)
177         end
178 end
179
180 ipaddr = s:taboption("general", Value, "ipaddr", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address"))
181 ipaddr.optional = true
182 ipaddr.datatype = "ip4addr"
183 ipaddr:depends("proto", "static")
184
185 nm = s:taboption("general", Value, "netmask", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"))
186 nm.optional = true
187 nm.datatype = "ip4addr"
188 nm:depends("proto", "static")
189 nm:value("255.255.255.0")
190 nm:value("255.255.0.0")
191 nm:value("255.0.0.0")
192
193 gw = s:taboption("general", Value, "gateway", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Gateway"))
194 gw.optional = true
195 gw.datatype = "ip4addr"
196 gw:depends("proto", "static")
197
198 bcast = s:taboption("general", Value, "bcast", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Broadcast"))
199 bcast.optional = true
200 bcast.datatype = "ip4addr"
201 bcast:depends("proto", "static")
202
203 if has_ipv6 then
204         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"))
205         ip6addr.optional = true
206         ip6addr.datatype = "ip6addr"
207         ip6addr:depends("proto", "static")
208         ip6addr:depends("proto", "6in4")
209
210         ip6gw = s:taboption("ipv6", Value, "ip6gw", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Gateway"))
211         ip6gw.optional = true
212         ip6gw.datatype = "ip6addr"
213         ip6gw:depends("proto", "static")
214 end
215
216 dns = s:taboption("general", DynamicList, "dns", translate("<abbr title=\"Domain Name System\">DNS</abbr>-Server"),
217         translate("You can specify multiple DNS servers here, press enter to add a new entry. Servers entered here will override " ..
218                 "automatically assigned ones."))
219
220 dns.optional = true
221 dns.cast = "string"
222 dns.datatype = "ipaddr"
223 dns:depends({ peerdns = "", proto = "static" })
224 dns:depends({ peerdns = "", proto = "dhcp"   })
225 dns:depends({ peerdns = "", proto = "pppoe"  })
226 dns:depends({ peerdns = "", proto = "pppoa"  })
227 dns:depends({ peerdns = "", proto = "none"   })
228
229 mtu = s:taboption("physical", Value, "mtu", "MTU")
230 mtu.optional = true
231 mtu.datatype = "uinteger"
232 mtu.placeholder = 1500
233
234 srv = s:taboption("general", Value, "server", translate("<abbr title=\"Point-to-Point Tunneling Protocol\">PPTP</abbr>-Server"))
235 srv:depends("proto", "pptp")
236 srv.optional = false
237 srv.datatype = "ip4addr"
238
239 if has_6in4 then
240         peer = s:taboption("general", Value, "peeraddr", translate("Server IPv4-Address"))
241         peer.optional = false
242         peer.datatype = "ip4addr"
243         peer:depends("proto", "6in4")
244 end
245
246 if has_6in4 or has_6to4 then
247         ttl = s:taboption("physical", Value, "ttl", translate("TTL"))
248         ttl.default = "64"
249         ttl.optional = true
250         ttl.datatype = "uinteger"
251         ttl:depends("proto", "6in4")
252         ttl:depends("proto", "6to4")
253 end
254
255 if has_6to4 then
256         advi = s:taboption("general", Value, "adv_interface", translate("Advertise IPv6 on network"))
257         advi.widget = "radio"
258         advi.exclude = arg[1]
259         advi.default = "lan"
260         advi.template = "cbi/network_netlist"
261         advi.nocreate = true
262         advi.nobridges = true
263         advi:depends("proto", "6to4")
264
265         advn = s:taboption("general", Value, "adv_subnet", translate("Advertised network ID"), translate("Allowed range is 1 to FFFF"))
266         advn.default = "1"
267         advn:depends("proto", "6to4")
268
269         function advn.write(self, section, value)
270                 value = tonumber(value, 16) or 1
271
272                 if value > 65535 then value = 65535
273                 elseif value < 1 then value = 1 end
274
275                 Value.write(self, section, "%X" % value)
276         end
277 end
278
279 mac = s:taboption("physical", Value, "macaddr", translate("<abbr title=\"Media Access Control\">MAC</abbr>-Address"))
280 mac:depends("proto", "none")
281 mac:depends("proto", "static")
282 mac:depends("proto", "dhcp")
283 mac.placeholder = ifc and ifc:mac():upper()
284
285 if has_3g then
286         service = s:taboption("general", ListValue, "service", translate("Service type"))
287         service:value("", translate("-- Please choose --"))
288         service:value("umts", "UMTS/GPRS")
289         service:value("cdma", "CDMA")
290         service:value("evdo", "EV-DO")
291         service:depends("proto", "3g")
292         service.rmempty = true
293
294         apn = s:taboption("general", Value, "apn", translate("Access point (APN)"))
295         apn:depends("proto", "3g")
296
297         pincode = s:taboption("general", Value, "pincode",
298          translate("PIN code"),
299          translate("Make sure that you provide the correct pin code here or you might lock your sim card!")
300         )
301         pincode:depends("proto", "3g")
302 end
303
304 if has_6in4 then
305         tunid = s:taboption("general", Value, "tunnelid", translate("HE.net Tunnel ID"))
306         tunid.optional = true
307         tunid.datatype = "uinteger"
308         tunid:depends("proto", "6in4")
309 end
310
311 if has_pppd or has_pppoe or has_pppoa or has_3g or has_pptp or has_6in4 then
312         user = s:taboption("general", Value, "username", translate("Username"))
313         user.rmempty = true
314         user:depends("proto", "pptp")
315         user:depends("proto", "pppoe")
316         user:depends("proto", "pppoa")
317         user:depends("proto", "ppp")
318         user:depends("proto", "3g")
319         user:depends("proto", "6in4")
320
321         pass = s:taboption("general", Value, "password", translate("Password"))
322         pass.rmempty = true
323         pass.password = true
324         pass:depends("proto", "pptp")
325         pass:depends("proto", "pppoe")
326         pass:depends("proto", "pppoa")
327         pass:depends("proto", "ppp")
328         pass:depends("proto", "3g")
329         pass:depends("proto", "6in4")
330 end
331
332 if has_pppd or has_pppoe or has_pppoa or has_3g or has_pptp then
333         ka = s:taboption("ppp", Value, "keepalive",
334          translate("Keep-Alive"),
335          translate("Number of failed connection tests to initiate automatic reconnect")
336         )
337         ka:depends("proto", "pptp")
338         ka:depends("proto", "pppoe")
339         ka:depends("proto", "pppoa")
340         ka:depends("proto", "ppp")
341         ka:depends("proto", "3g")
342
343         demand = s:taboption("ppp", Value, "demand",
344          translate("Automatic Disconnect"),
345          translate("Time (in seconds) after which an unused connection will be closed")
346         )
347         demand.optional = true
348         demand.datatype = "uinteger"
349         demand:depends("proto", "pptp")
350         demand:depends("proto", "pppoe")
351         demand:depends("proto", "pppoa")
352         demand:depends("proto", "ppp")
353         demand:depends("proto", "3g")
354 end
355
356 if has_pppoa then
357         encaps = s:taboption("atm", ListValue, "encaps", translate("PPPoA Encapsulation"))
358         encaps:depends("proto", "pppoa")
359         encaps:value("vc", "VC-Mux")
360         encaps:value("llc", "LLC")
361
362         atmdev = s:taboption("atm", Value, "atmdev", translate("ATM device number"))
363         atmdev:depends("proto", "pppoa")
364         atmdev.default = "0"
365         atmdev.datatype = "uinteger"
366
367         vci = s:taboption("atm", Value, "vci", translate("ATM Virtual Channel Identifier (VCI)"))
368         vci:depends("proto", "pppoa")
369         vci.default = "35"
370         vci.datatype = "uinteger"
371
372         vpi = s:taboption("atm", Value, "vpi", translate("ATM Virtual Path Identifier (VPI)"))
373         vpi:depends("proto", "pppoa")
374         vpi.default = "8"
375         vpi.datatype = "uinteger"
376 end
377
378 if has_pptp or has_pppd or has_pppoe or has_pppoa or has_3g then
379         device = s:taboption("general", Value, "device",
380          translate("Modem device"),
381          translate("The device node of your modem, e.g. /dev/ttyUSB0")
382         )
383         device:depends("proto", "ppp")
384         device:depends("proto", "3g")
385
386         defaultroute = s:taboption("ppp", Flag, "defaultroute",
387          translate("Replace default route"),
388          translate("Let pppd replace the current default route to use the PPP interface after successful connect")
389         )
390         defaultroute:depends("proto", "ppp")
391         defaultroute:depends("proto", "pppoa")
392         defaultroute:depends("proto", "pppoe")
393         defaultroute:depends("proto", "pptp")
394         defaultroute:depends("proto", "3g")
395         defaultroute.rmempty = false
396         function defaultroute.cfgvalue(...)
397                 return ( AbstractValue.cfgvalue(...) or '1' )
398         end
399
400         peerdns = s:taboption("ppp", Flag, "peerdns",
401          translate("Use peer DNS"),
402          translate("Configure the local DNS server to use the name servers adverticed by the PPP peer")
403         )
404         peerdns:depends("proto", "ppp")
405         peerdns:depends("proto", "pppoa")
406         peerdns:depends("proto", "pppoe")
407         peerdns:depends("proto", "pptp")
408         peerdns:depends("proto", "3g")
409         peerdns.rmempty = false
410         function peerdns.cfgvalue(...)
411                 return ( AbstractValue.cfgvalue(...) or '1' )
412         end
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 m2 = Map("dhcp", "", "")
511 function m2.on_parse()
512         local has_section = false
513
514         m2.uci:foreach("dhcp", "dhcp", function(s)
515                 if s.interface == arg[1] then
516                         has_section = true
517                         return false
518                 end
519         end)
520
521         if not has_section then
522                 m2.uci:section("dhcp", "dhcp", nil, { interface = arg[1], ignore = "1" })
523                 m2.uci:save("dhcp")
524         end
525 end
526
527 s = m2:section(TypedSection, "dhcp", translate("DHCP Server"))
528 s.addremove = false
529 s.anonymous = true
530 s:tab("general",  translate("General Setup"))
531 s:tab("advanced", translate("Advanced Settings"))
532
533 function s.filter(self, section)
534         return m2.uci:get("dhcp", section, "interface") == arg[1]
535 end
536
537 local ignore = s:taboption("general", Flag, "ignore",
538         translate("Ignore interface"),
539         translate("Disable <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr> for " ..
540                 "this interface."))
541
542 ignore.rmempty = false
543
544 local start = s:taboption("general", Value, "start", translate("Start"),
545         translate("Lowest leased address as offset from the network address."))
546 start.optional = true
547 start.datatype = "uinteger"
548 start.default = "100"
549
550 local limit = s:taboption("general", Value, "limit", translate("Limit"),
551         translate("Maximum number of leased addresses."))
552 limit.optional = true
553 limit.datatype = "uinteger"
554 limit.default = "150"
555
556 local ltime = s:taboption("general", Value, "leasetime", translate("Leasetime"),
557         translate("Expiry time of leased addresses, minimum is 2 Minutes (<code>2m</code>)."))
558 ltime.rmempty = true
559 ltime.default = "12h"
560
561 local dd = s:taboption("advanced", Flag, "dynamicdhcp",
562         translate("Dynamic <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr>"),
563         translate("Dynamically allocate DHCP addresses for clients. If disabled, only " ..
564                 "clients having static leases will be served."))
565
566 dd.rmempty = false
567 function dd.cfgvalue(self, section)
568         return Flag.cfgvalue(self, section) or "1"
569 end
570
571 s:taboption("advanced", Flag, "force", translate("Force"),
572         translate("Force DHCP on this network even if another server is detected."))
573
574 -- XXX: is this actually useful?
575 --s:taboption("advanced", Value, "name", translate("Name"),
576 --      translate("Define a name for this network."))
577
578 mask = s:taboption("advanced", Value, "netmask",
579         translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"),
580         translate("Override the netmask sent to clients. Normally it is calculated " ..
581                 "from the subnet that is served."))
582
583 mask.optional = true
584 mask.datatype = "ip4addr"
585
586 s:taboption("advanced", DynamicList, "dhcp_option", translate("DHCP-Options"),
587         translate("Define additional DHCP options, for example \"<code>6,192.168.2.1," ..
588                 "192.168.2.2</code>\" which advertises different DNS servers to clients."))
589
590 for i, n in ipairs(s.children) do
591         if n ~= ignore then
592                 n:depends("ignore", "")
593         end
594 end
595
596 return m, m2