6e34311d06600334153bbb35a568e484865623eb
[project/luci.git] / applications / luci-app-mwan3 / luasrc / model / cbi / mwan / interface.lua
1 -- Copyright 2014 Aedan Renner <chipdankly@gmail.com
2 -- Copyright 2018 Florian Eckert <fe@dev.tdt.de>
3 -- Licensed to the public under the GNU General Public License v2.
4
5 local dsp = require "luci.dispatcher"
6 local uci = require "uci"
7
8 local m, mwan_interface, enabled, track_method, reliability, interval
9 local down, up, metric
10
11 function interfaceWarnings(overview, count, iface_max)
12 local warnings = ""
13 if count <= iface_max then
14 warnings = string.format("<strong>%s</strong><br />",
15 translatef("There are currently %d of %d supported interfaces configured", count, iface_max)
16 )
17 else
18 warnings = string.format("<strong>%s</strong><br />",
19 translatef("WARNING: %d interfaces are configured exceeding the maximum of %d!", count, iface_max)
20 )
21 end
22
23 for i, k in pairs(overview) do
24 if overview[i]["network"] == false then
25 warnings = warnings .. string.format("<strong>%s</strong><br />",
26 translatef("WARNING: Interface %s are not found in /etc/config/network", i)
27 )
28 end
29
30 if overview[i]["default_route"] == false then
31 warnings = warnings .. string.format("<strong>%s</strong><br />",
32 translatef("WARNING: Interface %s has no default route in the main routing table", i)
33 )
34 end
35
36 if overview[i]["reliability"] == false then
37 warnings = warnings .. string.format("<strong>%s</strong><br />",
38 translatef("WARNING: Interface %s has a higher reliability " ..
39 "requirement than tracking hosts (%d)", i, overview[i]["tracking"])
40 )
41 end
42
43 if overview[i]["duplicate_metric"] == true then
44 warnings = warnings .. string.format("<strong>%s</strong><br />",
45 translatef("WARNING: Interface %s has a duplicate metric %s configured", i, overview[i]["metric"])
46 )
47 end
48 end
49
50 return warnings
51 end
52
53 function configCheck()
54 local overview = {}
55 local count = 0
56 local duplicate_metric = {}
57 uci.cursor():foreach("mwan3", "interface",
58 function (section)
59 local uci = uci.cursor(nil, "/var/state")
60 local iface = section[".name"]
61 overview[iface] = {}
62 count = count + 1
63 local network = uci:get("network", iface)
64 overview[iface]["network"] = false
65 if network ~= nil then
66 overview[iface]["network"] = true
67
68 local device = uci:get("network", iface, "ifname")
69 if device ~= nil then
70 overview[iface]["device"] = device
71 end
72
73 local metric = uci:get("network", iface, "metric")
74 if metric ~= nil then
75 overview[iface]["metric"] = metric
76 overview[iface]["duplicate_metric"] = false
77 for _, m in ipairs(duplicate_metric) do
78 if m == metric then
79 overview[iface]["duplicate_metric"] = true
80 end
81 end
82 table.insert(duplicate_metric, metric)
83 end
84
85 local dump = require("luci.util").ubus("network.interface.%s" % iface, "status", {})
86 overview[iface]["default_route"] = false
87 if dump and dump.route then
88 local _, route
89 for _, route in ipairs(dump.route) do
90 if dump.route[_].target == "0.0.0.0" then
91 overview[iface]["default_route"] = true
92 end
93 end
94 end
95 end
96
97 local trackingNumber = uci:get("mwan3", iface, "track_ip")
98 overview[iface]["tracking"] = 0
99 if trackingNumber and #trackingNumber > 0 then
100 overview[iface]["tracking"] = #trackingNumber
101 overview[iface]["reliability"] = false
102 local reliabilityNumber = tonumber(uci:get("mwan3", iface, "reliability") or "1")
103 if reliabilityNumber and reliabilityNumber <= #trackingNumber then
104 overview[iface]["reliability"] = true
105 end
106 end
107 end
108 )
109
110 -- calculate iface_max usage from firewall mmx_mask
111 function bit(p)
112 return 2 ^ (p - 1)
113 end
114 function hasbit(x, p)
115 return x % (p + p) >= p
116 end
117 function setbit(x, p)
118 return hasbit(x, p) and x or x + p
119 end
120
121 local uci = require("uci").cursor(nil, "/var/state")
122 local mmx_mask = uci:get("mwan3", "globals", "mmx_mask") or "0x3F00"
123 local number = tonumber(mmx_mask, 16)
124 local bits = 0
125 local iface_max = 0
126 for i=1,16 do
127 if hasbit(number, bit(i)) then
128 bits = bits + 1
129 iface_max = setbit( iface_max, bit(bits))
130 end
131 end
132
133 -- subtract blackhole, unreachable and default table from iface_max
134 iface_max = iface_max - 3
135
136 return overview, count, iface_max
137 end
138
139 m = Map("mwan3", translate("MWAN - Interfaces"),
140 interfaceWarnings(configCheck()))
141
142 mwan_interface = m:section(TypedSection, "interface", nil,
143 translate("mwan3 requires that all interfaces have a unique metric configured in /etc/config/network<br />" ..
144 "Names must match the interface name found in /etc/config/network<br />" ..
145 "Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" ..
146 "Interfaces may not share the same name as configured members, policies or rules"))
147 mwan_interface.addremove = true
148 mwan_interface.dynamic = false
149 mwan_interface.sectionhead = translate("Interface")
150 mwan_interface.sortable = false
151 mwan_interface.template = "cbi/tblsection"
152 mwan_interface.extedit = dsp.build_url("admin", "network", "mwan", "interface", "%s")
153 function mwan_interface.create(self, section)
154 TypedSection.create(self, section)
155 m.uci:save("mwan3")
156 luci.http.redirect(dsp.build_url("admin", "network", "mwan", "interface", section))
157 end
158
159 enabled = mwan_interface:option(DummyValue, "enabled", translate("Enabled"))
160 enabled.rawhtml = true
161 function enabled.cfgvalue(self, s)
162 if self.map:get(s, "enabled") == "1" then
163 return translate("Yes")
164 else
165 return translate("No")
166 end
167 end
168
169 track_method = mwan_interface:option(DummyValue, "track_method", translate("Tracking method"))
170 track_method.rawhtml = true
171 function track_method.cfgvalue(self, s)
172 local tracked = self.map:get(s, "track_ip")
173 if tracked then
174 return self.map:get(s, "track_method") or "ping"
175 else
176 return "&#8212;"
177 end
178 end
179
180 reliability = mwan_interface:option(DummyValue, "reliability", translate("Tracking reliability"))
181 reliability.rawhtml = true
182 function reliability.cfgvalue(self, s)
183 local tracked = self.map:get(s, "track_ip")
184 if tracked then
185 return self.map:get(s, "reliability") or "1"
186 else
187 return "&#8212;"
188 end
189 end
190
191 interval = mwan_interface:option(DummyValue, "interval", translate("Ping interval"))
192 interval.rawhtml = true
193 function interval.cfgvalue(self, s)
194 local tracked = self.map:get(s, "track_ip")
195 if tracked then
196 local intervalValue = self.map:get(s, "interval")
197 if intervalValue then
198 return intervalValue .. "s"
199 else
200 return "5s"
201 end
202 else
203 return "&#8212;"
204 end
205 end
206
207 down = mwan_interface:option(DummyValue, "down", translate("Interface down"))
208 down.rawhtml = true
209 function down.cfgvalue(self, s)
210 local tracked = self.map:get(s, "track_ip")
211 if tracked then
212 return self.map:get(s, "down") or "3"
213 else
214 return "&#8212;"
215 end
216 end
217
218 up = mwan_interface:option(DummyValue, "up", translate("Interface up"))
219 up.rawhtml = true
220 function up.cfgvalue(self, s)
221 local tracked = self.map:get(s, "track_ip")
222 if tracked then
223 return self.map:get(s, "up") or "3"
224 else
225 return "&#8212;"
226 end
227 end
228
229 metric = mwan_interface:option(DummyValue, "metric", translate("Metric"))
230 metric.rawhtml = true
231 function metric.cfgvalue(self, s)
232 local uci = uci.cursor(nil, "/var/state")
233 local metric = uci:get("network", s, "metric")
234 if metric then
235 return metric
236 else
237 return "&#8212;"
238 end
239 end
240
241 return m