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