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