modules/admin-full: add support for 6in4, enable live validation
[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
280         ka = s:taboption("ppp", Value, "keepalive",
281          translate("Keep-Alive"),
282          translate("Number of failed connection tests to initiate automatic reconnect")
283         )
284         ka:depends("proto", "pptp")
285         ka:depends("proto", "pppoe")
286         ka:depends("proto", "pppoa")
287         ka:depends("proto", "ppp")
288         ka:depends("proto", "3g")
289
290         demand = s:taboption("ppp", Value, "demand",
291          translate("Automatic Disconnect"),
292          translate("Time (in seconds) after which an unused connection will be closed")
293         )
294         demand.optional = true
295         demand.datatype = "uinteger"
296         demand:depends("proto", "pptp")
297         demand:depends("proto", "pppoe")
298         demand:depends("proto", "pppoa")
299         demand:depends("proto", "ppp")
300         demand:depends("proto", "3g")
301 end
302
303 if has_pppoa then
304         encaps = s:taboption("atm", ListValue, "encaps", translate("PPPoA Encapsulation"))
305         encaps:depends("proto", "pppoa")
306         encaps:value("vc", "VC-Mux")
307         encaps:value("llc", "LLC")
308
309         atmdev = s:taboption("atm", Value, "atmdev", translate("ATM device number"))
310         atmdev:depends("proto", "pppoa")
311         atmdev.default = "0"
312         atmdev.datatype = "uinteger"
313
314         vci = s:taboption("atm", Value, "vci", translate("ATM Virtual Channel Identifier (VCI)"))
315         vci:depends("proto", "pppoa")
316         vci.default = "35"
317         vci.datatype = "uinteger"
318
319         vpi = s:taboption("atm", Value, "vpi", translate("ATM Virtual Path Identifier (VPI)"))
320         vpi:depends("proto", "pppoa")
321         vpi.default = "8"
322         vpi.datatype = "uinteger"
323 end
324
325 if has_pptp or has_pppd or has_pppoe or has_pppoa or has_3g then
326         device = s:taboption("general", Value, "device",
327          translate("Modem device"),
328          translate("The device node of your modem, e.g. /dev/ttyUSB0")
329         )
330         device:depends("proto", "ppp")
331         device:depends("proto", "3g")
332
333         defaultroute = s:taboption("ppp", Flag, "defaultroute",
334          translate("Replace default route"),
335          translate("Let pppd replace the current default route to use the PPP interface after successful connect")
336         )
337         defaultroute:depends("proto", "ppp")
338         defaultroute:depends("proto", "pppoa")
339         defaultroute:depends("proto", "pppoe")
340         defaultroute:depends("proto", "pptp")
341         defaultroute:depends("proto", "3g")
342         defaultroute.rmempty = false
343         function defaultroute.cfgvalue(...)
344                 return ( AbstractValue.cfgvalue(...) or '1' )
345         end
346
347         peerdns = s:taboption("ppp", Flag, "peerdns",
348          translate("Use peer DNS"),
349          translate("Configure the local DNS server to use the name servers adverticed by the PPP peer")
350         )
351         peerdns:depends("proto", "ppp")
352         peerdns:depends("proto", "pppoa")
353         peerdns:depends("proto", "pppoe")
354         peerdns:depends("proto", "pptp")
355         peerdns:depends("proto", "3g")
356         peerdns.rmempty = false
357         function peerdns.cfgvalue(...)
358                 return ( AbstractValue.cfgvalue(...) or '1' )
359         end
360
361         if has_ipv6 then
362                 ipv6 = s:taboption("ppp", Flag, "ipv6", translate("Enable IPv6 on PPP link") )
363                 ipv6:depends("proto", "ppp")
364                 ipv6:depends("proto", "pppoa")
365                 ipv6:depends("proto", "pppoe")
366                 ipv6:depends("proto", "pptp")
367                 ipv6:depends("proto", "3g")
368         end
369
370         connect = s:taboption("ppp", Value, "connect",
371          translate("Connect script"),
372          translate("Let pppd run this script after establishing the PPP link")
373         )
374         connect:depends("proto", "ppp")
375         connect:depends("proto", "pppoe")
376         connect:depends("proto", "pppoa")
377         connect:depends("proto", "pptp")
378         connect:depends("proto", "3g")
379
380         disconnect = s:taboption("ppp", Value, "disconnect",
381          translate("Disconnect script"),
382          translate("Let pppd run this script before tearing down the PPP link")
383         )
384         disconnect:depends("proto", "ppp")
385         disconnect:depends("proto", "pppoe")
386         disconnect:depends("proto", "pppoa")
387         disconnect:depends("proto", "pptp")
388         disconnect:depends("proto", "3g")
389
390         pppd_options = s:taboption("ppp", Value, "pppd_options",
391          translate("Additional pppd options"),
392          translate("Specify additional command line arguments for pppd here")
393         )
394         pppd_options:depends("proto", "ppp")
395         pppd_options:depends("proto", "pppoa")
396         pppd_options:depends("proto", "pppoe")
397         pppd_options:depends("proto", "pptp")
398         pppd_options:depends("proto", "3g")
399
400         maxwait = s:taboption("ppp", Value, "maxwait",
401          translate("Setup wait time"),
402          translate("Seconds to wait for the modem to become ready before attempting to connect")
403         )
404         maxwait:depends("proto", "3g")
405         maxwait.default  = "0"
406         maxwait.optional = true
407         maxwait.datatype = "uinteger"
408 end
409
410 s2 = m:section(TypedSection, "alias", translate("IP-Aliases"))
411 s2.addremove = true
412
413 s2:depends("interface", arg[1])
414 s2.defaults.interface = arg[1]
415
416 s2:tab("general", translate("General Setup"))
417 s2.defaults.proto = "static"
418
419 ip = s2:taboption("general", Value, "ipaddr", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Address"))
420 ip.optional = true
421 ip.datatype = "ip4addr"
422
423 nm = s2:taboption("general", Value, "netmask", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"))
424 nm.optional = true
425 nm.datatype = "ip4addr"
426 nm:value("255.255.255.0")
427 nm:value("255.255.0.0")
428 nm:value("255.0.0.0")
429
430 gw = s2:taboption("general", Value, "gateway", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Gateway"))
431 gw.optional = true
432 gw.datatype = "ip4addr"
433
434 if has_ipv6 then
435         s2:tab("ipv6", translate("IPv6 Setup"))
436
437         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"))
438         ip6.optional = true
439         ip6.datatype = "ip6addr"
440
441         gw6 = s2:taboption("ipv6", Value, "ip6gw", translate("<abbr title=\"Internet Protocol Version 6\">IPv6</abbr>-Gateway"))
442         gw6.optional = true
443         gw6.datatype = "ip6addr"
444 end
445
446 s2:tab("advanced", translate("Advanced Settings"))
447
448 bcast = s2:taboption("advanced", Value, "bcast", translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Broadcast"))
449 bcast.optional = true
450 bcast.datatype = "ip4addr"
451
452 dns = s2:taboption("advanced", Value, "dns", translate("<abbr title=\"Domain Name System\">DNS</abbr>-Server"))
453 dns.optional = true
454 dns.datatype = "ip4addr"
455
456
457 m2 = Map("dhcp", "", "")
458 function m2.on_parse()
459         local has_section = false
460
461         m2.uci:foreach("dhcp", "dhcp", function(s)
462                 if s.interface == arg[1] then
463                         has_section = true
464                         return false
465                 end
466         end)
467
468         if not has_section then
469                 m2.uci:section("dhcp", "dhcp", nil, { interface = arg[1], ignore = "1" })
470                 m2.uci:save("dhcp")
471         end
472 end
473
474 s = m2:section(TypedSection, "dhcp", translate("DHCP Server"))
475 s.addremove = false
476 s.anonymous = true
477 s:tab("general",  translate("General Setup"))
478 s:tab("advanced", translate("Advanced Settings"))
479
480 function s.filter(self, section)
481         return m2.uci:get("dhcp", section, "interface") == arg[1]
482 end
483
484 local ignore = s:taboption("general", Flag, "ignore",
485         translate("Ignore interface"),
486         translate("Disable <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr> for " ..
487                 "this interface."))
488
489 ignore.rmempty = false
490
491 local start = s:taboption("general", Value, "start", translate("Start"),
492         translate("Lowest leased address as offset from the network address."))
493 start.optional = true
494 start.datatype = "uinteger"
495 start.default = "100"
496
497 local limit = s:taboption("general", Value, "limit", translate("Limit"),
498         translate("Maximum number of leased addresses."))
499 limit.optional = true
500 limit.datatype = "uinteger"
501 limit.default = "150"
502
503 local ltime = s:taboption("general", Value, "leasetime", translate("Leasetime"),
504         translate("Expiry time of leased addresses, minimum is 2 Minutes (<code>2m</code>)."))
505 ltime.rmempty = true
506 ltime.default = "12h"
507
508 local dd = s:taboption("advanced", Flag, "dynamicdhcp",
509         translate("Dynamic <abbr title=\"Dynamic Host Configuration Protocol\">DHCP</abbr>"),
510         translate("Dynamically allocate DHCP addresses for clients. If disabled, only " ..
511                 "clients having static leases will be served."))
512
513 dd.rmempty = false
514 function dd.cfgvalue(self, section)
515         return Flag.cfgvalue(self, section) or "1"
516 end
517
518 s:taboption("advanced", Flag, "force", translate("Force"),
519         translate("Force DHCP on this network even if another server is detected."))
520
521 -- XXX: is this actually useful?
522 --s:taboption("advanced", Value, "name", translate("Name"),
523 --      translate("Define a name for this network."))
524
525 mask = s:taboption("advanced", Value, "netmask",
526         translate("<abbr title=\"Internet Protocol Version 4\">IPv4</abbr>-Netmask"),
527         translate("Override the netmask sent to clients. Normally it is calculated " ..
528                 "from the subnet that is served."))
529
530 mask.optional = true
531 mask.datatype = "ip4addr"
532
533 s:taboption("advanced", DynamicList, "dhcp_option", translate("DHCP-Options"),
534         translate("Define additional DHCP options, for example \"<code>6,192.168.2.1," ..
535                 "192.168.2.2</code>\" which advertises different DNS servers to clients."))
536
537 for i, n in ipairs(s.children) do
538         if n ~= ignore then
539                 n:depends("ignore", "")
540         end
541 end
542
543 return m, m2