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