modules/admin-full: convert cbi maps to new network 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 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 --function m.on_commit(map)
38 --      nw.init(map.uci)
39 --      fw.init(map.uci)
40 --end
41
42 s = m:section(NamedSection, arg[1], "interface", translate("Common Configuration"))
43 s.addremove = false
44
45 s:tab("general", translate("General Setup"))
46 if has_ipv6  then s:tab("ipv6", translate("IPv6 Setup")) end
47 if has_pppd  then s:tab("ppp", translate("PPP Settings")) end
48 if has_pppoa then s:tab("atm", translate("ATM Settings")) end
49 if has_6in4  then s:tab("tunnel", translate("Tunnel Settings")) end
50 s:tab("physical", translate("Physical Settings"))
51 s:tab("firewall", translate("Firewall Settings"))
52
53 st = s:taboption("general", DummyValue, "__status", translate("Status"))
54 st.template = "admin_network/iface_status"
55 st.network  = arg[1]
56
57 --[[
58 back = s:taboption("general", DummyValue, "_overview", translate("Overview"))
59 back.value = ""
60 back.titleref = luci.dispatcher.build_url("admin", "network", "network")
61 ]]
62
63 p = s:taboption("general", ListValue, "proto", translate("Protocol"))
64 p.override_scheme = true
65 p.default = "static"
66 p:value("static", translate("static"))
67 p:value("dhcp", "DHCP")
68 if has_pppd  then p:value("ppp",   "PPP")     end
69 if has_pppoe then p:value("pppoe", "PPPoE")   end
70 if has_pppoa then p:value("pppoa", "PPPoA")   end
71 if has_3g    then p:value("3g",    "UMTS/3G") end
72 if has_pptp  then p:value("pptp",  "PPTP")    end
73 if has_6in4  then p:value("6in4",  "6in4")    end
74 p:value("none", translate("none"))
75
76 if not ( has_pppd and has_pppoe and has_pppoa and has_3g and has_pptp ) then
77         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")
78 end
79
80 br = s:taboption("physical", Flag, "type", translate("Bridge interfaces"), translate("creates a bridge over specified interface(s)"))
81 br.enabled = "bridge"
82 br.rmempty = true
83 br:depends("proto", "static")
84 br:depends("proto", "dhcp")
85 br:depends("proto", "none")
86
87 stp = s:taboption("physical", Flag, "stp", translate("Enable <abbr title=\"Spanning Tree Protocol\">STP</abbr>"),
88         translate("Enables the Spanning Tree Protocol on this bridge"))
89 stp:depends("type", "1")
90 stp.rmempty = true
91
92 ifname_single = s:taboption("physical", Value, "ifname_single", translate("Interface"))
93 ifname_single.template = "cbi/network_ifacelist"
94 ifname_single.widget = "radio"
95 ifname_single.nobridges = true
96 ifname_single.network = arg[1]
97 ifname_single.rmempty = true
98 ifname_single:depends({ type = "", proto = "static" })
99 ifname_single:depends({ type = "", proto = "dhcp"   })
100 ifname_single:depends({ type = "", proto = "pppoe"  })
101 ifname_single:depends({ type = "", proto = "pppoa"  })
102 ifname_single:depends({ type = "", proto = "none"   })
103
104 function ifname_single.cfgvalue(self, s)
105         return self.map.uci:get("network", s, "ifname")
106 end
107
108 function ifname_single.write(self, s, val)
109         local n = nw:get_network(s)
110         if n then
111                 local i
112                 for _, i in ipairs(n:get_interfaces()) do
113                         n:del_interface(i)
114                 end
115
116                 for i in val:gmatch("%S+") do
117                         n:add_interface(i)
118
119                         -- if this is not a bridge, only assign first interface
120                         if self.option == "ifname_single" then
121                                 break
122                         end
123                 end
124         end
125 end
126
127
128 ifname_multi = s:taboption("physical", MultiValue, "ifname_multi", translate("Interface"))
129 ifname_multi.template = "cbi/network_ifacelist"
130 ifname_multi.nobridges = true
131 ifname_multi.network = arg[1]
132 ifname_multi.widget = "checkbox"
133 ifname_multi:depends("type", "1")
134 ifname_multi.cfgvalue = ifname_single.cfgvalue
135 ifname_multi.write = ifname_single.write
136
137
138 for _, d in ipairs(nw:get_interfaces()) do
139         if not d:is_bridge() then
140                 ifname_single:value(d:name())
141                 ifname_multi:value(d:name())
142         end
143 end
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", "")
224
225 mtu = s:taboption("physical", Value, "mtu", "MTU")
226 mtu.optional = true
227 mtu.datatype = "uinteger"
228
229 srv = s:taboption("general", Value, "server", translate("<abbr title=\"Point-to-Point Tunneling Protocol\">PPTP</abbr>-Server"))
230 srv:depends("proto", "pptp")
231 srv.optional = false
232 srv.datatype = "ip4addr"
233
234 if has_6in4 then
235         peer = s:taboption("general", Value, "peeraddr", translate("Server IPv4-Address"))
236         peer.optional = false
237         peer.datatype = "ip4addr"
238         peer:depends("proto", "6in4")
239
240         ttl = s:taboption("physical", Value, "ttl", translate("TTL"))
241         ttl.default = "64"
242         ttl.optional = true
243         ttl.datatype = "uinteger"
244         ttl:depends("proto", "6in4")
245 end
246
247 mac = s:taboption("physical", Value, "macaddr", translate("<abbr title=\"Media Access Control\">MAC</abbr>-Address"))
248 mac:depends("proto", "none")
249 mac:depends("proto", "static")
250 mac:depends("proto", "dhcp")
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