Browse Source

Upload of mwan3 LuCI package for OpenWrt feeds.

Aedan ARFETT Renner 10 years ago
parent
commit
c5ecb78285
25 changed files with 2081 additions and 0 deletions
  1. 39
    0
      net/mwan3-luci/Makefile
  2. 38
    0
      net/mwan3-luci/files/etc/hotplug.d/iface/16-mwan3custombak
  3. 14
    0
      net/mwan3-luci/files/etc/uci-defaults/mwan3
  4. 355
    0
      net/mwan3-luci/files/usr/lib/lua/luci/controller/mwan3.lua
  5. 55
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_hotplug.lua
  6. 32
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_mwan3.lua
  7. 32
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_network.lua
  8. 266
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interface.lua
  9. 191
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interfaceconfig.lua
  10. 46
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_member.lua
  11. 47
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_memberconfig.lua
  12. 82
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policy.lua
  13. 58
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policyconfig.lua
  14. 109
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_rule.lua
  15. 99
    0
      net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_ruleconfig.lua
  16. 1
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/admin_status/index/mwan3.htm
  17. 134
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_diagnostics.htm
  18. 23
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_hotplug.htm
  19. 23
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_mwan3.htm
  20. 23
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_network.htm
  21. 77
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_troubleshoot.htm
  22. 34
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_config_css.htm
  23. 62
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_detail.htm
  24. 146
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_interface.htm
  25. 95
    0
      net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_status.htm

+ 39
- 0
net/mwan3-luci/Makefile View File

@@ -0,0 +1,39 @@
1
+#
2
+# Copyright (C) 2006-2014 OpenWrt.org
3
+#
4
+# This is free software, licensed under the GNU General Public License v2.
5
+# See /LICENSE for more information.
6
+#
7
+
8
+include $(TOPDIR)/rules.mk
9
+
10
+PKG_NAME:=luci-app-mwan3
11
+PKG_VERSION:=1.2
12
+PKG_RELEASE:=19
13
+PKG_MAINTAINER:=Aedan Renner <chipdankly@gmail.com>
14
+PKG_LICENSE:=GPLv2
15
+
16
+include $(INCLUDE_DIR)/package.mk
17
+
18
+define Package/luci-app-mwan3
19
+  SECTION:=LuCI
20
+  CATEGORY:=LuCI
21
+  SUBMENU:=3. Applications
22
+  TITLE:=LuCI support for MWAN3 Multiwan hotplug script with connection tracking support
23
+  DEPENDS:=+mwan3
24
+  PKGARCH:=all
25
+  MAINTAINER:=Aedan Renner <chipdankly@gmail.com>
26
+endef
27
+
28
+define Package/luci-app-mwan3/description
29
+   Hotplug script which makes configuration of multiple WAN interfaces simple and manageable. With loadbalancing/failover support for up to 250 wan interfaces, connection tracking and an easy to manage traffic ruleset.
30
+endef
31
+
32
+define Build/Compile
33
+endef
34
+
35
+define Package/luci-app-mwan3/install
36
+	$(CP) ./files/* $(1)
37
+endef
38
+
39
+$(eval $(call BuildPackage,luci-app-mwan3))

+ 38
- 0
net/mwan3-luci/files/etc/hotplug.d/iface/16-mwan3custombak View File

@@ -0,0 +1,38 @@
1
+#!/bin/sh
2
+
3
+# to enable this script uncomment the case loop at the bottom
4
+# to report MWAN3 status on interface up/down events modify the lines in the send_reportdata function
5
+
6
+send_alert()
7
+{
8
+	# $1 stores the mwan3 status information
9
+	# insert your code here to send the contents of $1
10
+	echo "$1"
11
+}
12
+
13
+gather_event_info()
14
+{
15
+	# create event information message
16
+	local EVENT_INFO="Interface [ "$INTERFACE" ($DEVICE) ] on router [ "$(uci get -p /var/state system.@system[0].hostname)" ] has triggered an [ "$ACTION" ] hotplug event on "$(date)""
17
+
18
+	# get current interface, policy and rule status
19
+	local CURRENT_STATUS="$(mwan3 status)"
20
+
21
+	# get last 50 mwan3 systemlog messages
22
+	local MWAN3_LOG="$(echo -e "Last 50 MWAN3 systemlog entries. Newest entries sorted at the top:\n$(logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x')")"
23
+
24
+	# pass event info to send_alert function
25
+	send_alert "$(echo -e "$EVENT_INFO\n\n$CURRENT_STATUS\n\n$MWAN3_LOG")"
26
+}
27
+
28
+#case "$ACTION" in
29
+#	ifup)
30
+#		gather_event_info
31
+#	;;
32
+#
33
+#	ifdown)
34
+#		gather_event_info
35
+#	;;
36
+#esac
37
+
38
+exit 0

+ 14
- 0
net/mwan3-luci/files/etc/uci-defaults/mwan3 View File

@@ -0,0 +1,14 @@
1
+#!/bin/sh
2
+
3
+# delete existing mwan3 ucitrack entry and add new entry
4
+uci -q batch <<-EOF >/dev/null
5
+	del ucitrack.@mwan3[-1]
6
+	add ucitrack mwan3
7
+	set ucitrack.@mwan3[-1].exec="/etc/init.d/mwan3 restart"
8
+	commit ucitrack
9
+EOF
10
+
11
+# make controller file addition take effect without system restart
12
+rm -rf /tmp/luci-indexcache /tmp/luci-modulecache
13
+
14
+exit 0

+ 355
- 0
net/mwan3-luci/files/usr/lib/lua/luci/controller/mwan3.lua View File

@@ -0,0 +1,355 @@
1
+module("luci.controller.mwan3", package.seeall)
2
+
3
+sys = require "luci.sys"
4
+ut = require "luci.util"
5
+
6
+function index()
7
+	if not nixio.fs.access("/etc/config/mwan3") then
8
+		return
9
+	end
10
+
11
+	entry({"admin", "network", "mwan3"},
12
+		alias("admin", "network", "mwan3", "overview"),
13
+		_("Load Balancing"), 600)
14
+
15
+	entry({"admin", "network", "mwan3", "overview"},
16
+		alias("admin", "network", "mwan3", "overview", "over_iface"),
17
+		_("Overview"), 10)
18
+	entry({"admin", "network", "mwan3", "overview", "over_iface"},
19
+		template("mwan3/mwan3_over_interface"))
20
+	entry({"admin", "network", "mwan3", "overview", "iface_status"},
21
+		call("mwan3_iface_status"))
22
+	entry({"admin", "network", "mwan3", "overview", "over_detail"},
23
+		template("mwan3/mwan3_over_detail"))
24
+	entry({"admin", "network", "mwan3", "overview", "detail_status"},
25
+		call("mwan3_detail_status"))
26
+
27
+	entry({"admin", "network", "mwan3", "configuration"},
28
+		alias("admin", "network", "mwan3", "configuration", "interface"),
29
+		_("Configuration"), 20)
30
+	entry({"admin", "network", "mwan3", "configuration", "interface"},
31
+		arcombine(cbi("mwan3/mwan3_interface"), cbi("mwan3/mwan3_interfaceconfig")),
32
+		_("Interfaces"), 10).leaf = true
33
+	entry({"admin", "network", "mwan3", "configuration", "member"},
34
+		arcombine(cbi("mwan3/mwan3_member"), cbi("mwan3/mwan3_memberconfig")),
35
+		_("Members"), 20).leaf = true
36
+	entry({"admin", "network", "mwan3", "configuration", "policy"},
37
+		arcombine(cbi("mwan3/mwan3_policy"), cbi("mwan3/mwan3_policyconfig")),
38
+		_("Policies"), 30).leaf = true
39
+	entry({"admin", "network", "mwan3", "configuration", "rule"},
40
+		arcombine(cbi("mwan3/mwan3_rule"), cbi("mwan3/mwan3_ruleconfig")),
41
+		_("Rules"), 40).leaf = true
42
+
43
+	entry({"admin", "network", "mwan3", "advanced"},
44
+		alias("admin", "network", "mwan3", "advanced", "hotplug"),
45
+		_("Advanced"), 100)
46
+	entry({"admin", "network", "mwan3", "advanced", "hotplug"},
47
+		form("mwan3/mwan3_adv_hotplug"))
48
+	entry({"admin", "network", "mwan3", "advanced", "mwan3"},
49
+		form("mwan3/mwan3_adv_mwan3"))
50
+	entry({"admin", "network", "mwan3", "advanced", "network"},
51
+		form("mwan3/mwan3_adv_network"))
52
+	entry({"admin", "network", "mwan3", "advanced", "diag"},
53
+		template("mwan3/mwan3_adv_diagnostics"))
54
+	entry({"admin", "network", "mwan3", "advanced", "diag_display"},
55
+		call("mwan3_diag_data"), nil).leaf = true
56
+	entry({"admin", "network", "mwan3", "advanced", "tshoot"},
57
+		template("mwan3/mwan3_adv_troubleshoot"))
58
+	entry({"admin", "network", "mwan3", "advanced", "tshoot_display"},
59
+		call("mwan3_tshoot_data"))
60
+end
61
+
62
+function mwan3_get_iface_status(rulenum, ifname)
63
+	if ut.trim(sys.exec("uci get -p /var/state mwan3." .. ifname .. ".enabled")) == "1" then
64
+		if ut.trim(sys.exec("ip route list table " .. rulenum)) ~= "" then
65
+			if ut.trim(sys.exec("uci get -p /var/state mwan3." .. ifname .. ".track_ip")) ~= "" then
66
+				return "on"
67
+			else
68
+				return "nm"
69
+			end
70
+		else
71
+			return "off"
72
+		end
73
+	else
74
+		return "ne"
75
+	end
76
+end
77
+
78
+function mwan3_get_iface()
79
+	local rulenum, str = 0, ""
80
+	uci.cursor():foreach("mwan3", "interface",
81
+		function (section)
82
+			rulenum = rulenum+1
83
+			str = str .. section[".name"] .. "[" .. mwan3_get_iface_status(rulenum, section[".name"]) .. "]"
84
+		end
85
+	)
86
+	return str
87
+end
88
+
89
+function mwan3_iface_status()
90
+	local ntm = require "luci.model.network".init()
91
+
92
+	local rv = {	}
93
+
94
+	-- overview status
95
+	local statstr = mwan3_get_iface()
96
+	if statstr ~= "" then
97
+		rv.wans = { }
98
+		wansid = {}
99
+
100
+		for wanname, ifstat in string.gfind(statstr, "([^%[]+)%[([^%]]+)%]") do
101
+			local wanifname = ut.trim(sys.exec("uci get -p /var/state network." .. wanname .. ".ifname"))
102
+				if wanifname == "" then
103
+					wanifname = "X"
104
+				end
105
+			local wanlink = ntm:get_interface(wanifname)
106
+				wanlink = wanlink and wanlink:get_network()
107
+				wanlink = wanlink and wanlink:adminlink() or "#"
108
+			wansid[wanname] = #rv.wans + 1
109
+			rv.wans[wansid[wanname]] = { name = wanname, link = wanlink, ifname = wanifname, status = ifstat }
110
+		end
111
+	end
112
+
113
+	-- overview status log
114
+	local mwlg = ut.trim(sys.exec("logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x'"))
115
+	if mwlg ~= "" then
116
+		rv.mwan3log = { }
117
+		mwlog = {}
118
+		mwlog[mwlg] = #rv.mwan3log + 1
119
+		rv.mwan3log[mwlog[mwlg]] = { mwanlog = mwlg }
120
+	end
121
+
122
+	luci.http.prepare_content("application/json")
123
+	luci.http.write_json(rv)
124
+end
125
+
126
+function mwan3_detail_status()
127
+	local rv = {	}
128
+
129
+	-- detailed mwan3 status
130
+	local dst = ut.trim(sys.exec("mwan3 status"))
131
+	if dst ~= "" then
132
+		rv.mwan3dst = { }
133
+		dstat = {}
134
+		dstat[dst] = #rv.mwan3dst + 1
135
+		rv.mwan3dst[dstat[dst]] = { detailstat = dst }
136
+	end
137
+
138
+	luci.http.prepare_content("application/json")
139
+	luci.http.write_json(rv)
140
+end
141
+
142
+function mwan3_diag_data(iface, tool, alt)
143
+	function get_ifnum()
144
+		local num = 0
145
+		uci.cursor():foreach("mwan3", "interface",
146
+			function (section)
147
+				num = num+1
148
+				if section[".name"] == iface then
149
+					ifnum = num
150
+				end
151
+			end
152
+		)
153
+	end
154
+
155
+	local rv = {	}
156
+
157
+	local res = ""
158
+	if tool == "service" then
159
+		os.execute("mwan3 " .. alt)
160
+		if alt == "restart" then
161
+			res = "MWAN3 restarted"
162
+		elseif alt == "stop" then
163
+			res = "MWAN3 stopped"
164
+		else
165
+			res = "MWAN3 started"
166
+		end
167
+	else
168
+		local ifdev = ut.trim(sys.exec("uci get -p /var/state network." .. iface .. ".ifname"))
169
+		if ifdev ~= "" then
170
+			if tool == "ping" then
171
+				local gateway = ut.trim(sys.exec("route -n | awk -F' ' '{ if ($8 == \"" .. ifdev .. "\" && $1 == \"0.0.0.0\") print $2 }'"))
172
+				if gateway ~= "" then
173
+					if alt == "gateway" then
174
+						local cmd = "ping -c 3 -W 2 -I " .. ifdev .. " " .. gateway
175
+						res = cmd .. "\n\n" .. sys.exec(cmd)
176
+					else
177
+						local str = ut.trim(sys.exec("uci get -p /var/state mwan3." .. iface .. ".track_ip"))
178
+						if str ~= "" then
179
+							for z in str:gmatch("[^ ]+") do
180
+								local cmd = "ping -c 3 -W 2 -I " .. ifdev .. " " .. z
181
+								res = res .. cmd .. "\n\n" .. sys.exec(cmd) .. "\n\n"
182
+							end
183
+						else
184
+							res = "No tracking IP addresses configured on " .. iface
185
+						end
186
+					end
187
+				else
188
+					res = "No default gateway for " .. iface .. " found. Default route does not exist or is configured incorrectly"
189
+				end
190
+			elseif tool == "rulechk" then
191
+				get_ifnum()
192
+				local rule1 = sys.exec("ip rule | grep $(echo $((" .. ifnum .. " + 1000)))")
193
+				local rule2 = sys.exec("ip rule | grep $(echo $((" .. ifnum .. " + 2000)))")
194
+				if rule1 ~= "" and rule2 ~= "" then
195
+					res = "All required interface IP rules found:\n\n" .. rule1 .. rule2
196
+				elseif rule1 ~= "" or rule2 ~= "" then
197
+					res = "Missing 1 of the 2 required interface IP rules\n\n\nRules found:\n\n" .. rule1 .. rule2
198
+				else
199
+					res = "Missing both of the required interface IP rules"
200
+				end
201
+			elseif tool == "routechk" then
202
+				get_ifnum()
203
+				local table = sys.exec("ip route list table " .. ifnum)
204
+				if table ~= "" then
205
+					res = "Interface routing table " .. ifnum .. " was found:\n\n" .. table
206
+				else
207
+					res = "Missing required interface routing table " .. ifnum
208
+				end
209
+			elseif tool == "hotplug" then
210
+				if alt == "ifup" then
211
+					os.execute("mwan3 ifup " .. iface)
212
+					res = "Hotplug ifup sent to interface " .. iface .. "..."
213
+				else
214
+					os.execute("mwan3 ifdown " .. iface)
215
+					res = "Hotplug ifdown sent to interface " .. iface .. "..."
216
+				end
217
+			end
218
+		else
219
+			res = "Unable to perform diagnostic tests on " .. iface .. ". There is no physical or virtual device associated with this interface"
220
+		end
221
+	end
222
+	if res ~= "" then
223
+		res = ut.trim(res)
224
+		rv.diagres = { }
225
+		dres = {}
226
+		dres[res] = #rv.diagres + 1
227
+		rv.diagres[dres[res]] = { diagresult = res }
228
+	end
229
+
230
+	luci.http.prepare_content("application/json")
231
+	luci.http.write_json(rv)
232
+end
233
+
234
+function mwan3_tshoot_data()
235
+	local rv = {	}
236
+
237
+	-- software versions
238
+	local wrtrelease = ut.trim(luci.version.distversion)
239
+		if wrtrelease ~= "" then
240
+			wrtrelease = "OpenWrt - " .. wrtrelease
241
+		else
242
+			wrtrelease = "OpenWrt - unknown"
243
+		end
244
+	local lucirelease = ut.trim(luci.version.luciversion)
245
+		if lucirelease ~= "" then
246
+			lucirelease = "\nLuCI - " .. lucirelease
247
+		else
248
+			lucirelease = "\nLuCI - unknown"
249
+		end
250
+	local mwan3version = ut.trim(sys.exec("opkg info mwan3 | grep Version | awk -F' ' '{ print $2 }'"))
251
+		if mwan3version ~= "" then
252
+			mwan3version = "\n\nmwan3 - " .. mwan3version
253
+		else
254
+			mwan3version = "\nmwan3 - unknown"
255
+		end
256
+	local mwan3lversion = ut.trim(sys.exec("opkg info luci-app-mwan3 | grep Version | awk -F' ' '{ print $2 }'"))
257
+		if mwan3lversion ~= "" then
258
+			mwan3lversion = "\nluci-app-mwan3 - " .. mwan3lversion
259
+		else
260
+			mwan3lversion = "\nluci-app-mwan3 - unknown"
261
+		end
262
+	local softrev = wrtrelease .. lucirelease .. mwan3version .. mwan3lversion
263
+	rv.mw3ver = { }
264
+	mwv = {}
265
+	mwv[softrev] = #rv.mw3ver + 1
266
+	rv.mw3ver[mwv[softrev]] = { mwan3v = softrev }
267
+
268
+	-- mwan3 config
269
+	local mwcg = ut.trim(sys.exec("cat /etc/config/mwan3"))
270
+		if mwcg == "" then
271
+			mwcg = "No data found"
272
+		end
273
+	rv.mwan3config = { }
274
+	mwan3cfg = {}
275
+	mwan3cfg[mwcg] = #rv.mwan3config + 1
276
+	rv.mwan3config[mwan3cfg[mwcg]] = { mwn3cfg = mwcg }
277
+
278
+	-- network config
279
+	local netcg = ut.trim(sys.exec("cat /etc/config/network | sed -e 's/.*username.*/	USERNAME HIDDEN/' -e 's/.*password.*/	PASSWORD HIDDEN/'"))
280
+		if netcg == "" then
281
+			netcg = "No data found"
282
+		end
283
+	rv.netconfig = { }
284
+	ncfg = {}
285
+	ncfg[netcg] = #rv.netconfig + 1
286
+	rv.netconfig[ncfg[netcg]] = { netcfg = netcg }
287
+
288
+	-- ifconfig
289
+	local ifcg = ut.trim(sys.exec("ifconfig"))
290
+		if ifcg == "" then
291
+			ifcg = "No data found"
292
+		end
293
+	rv.ifconfig = { }
294
+	icfg = {}
295
+	icfg[ifcg] = #rv.ifconfig + 1
296
+	rv.ifconfig[icfg[ifcg]] = { ifcfg = ifcg }
297
+
298
+	-- route -n
299
+	local routeshow = ut.trim(sys.exec("route -n"))
300
+		if routeshow == "" then
301
+			routeshow = "No data found"
302
+		end
303
+	rv.rtshow = { }
304
+	rshw = {}
305
+	rshw[routeshow] = #rv.rtshow + 1
306
+	rv.rtshow[rshw[routeshow]] = { iprtshow = routeshow }
307
+
308
+	-- ip rule show
309
+	local ipr = ut.trim(sys.exec("ip rule show"))
310
+		if ipr == "" then
311
+			ipr = "No data found"
312
+		end
313
+	rv.iprule = { }
314
+	ipruleid = {}
315
+	ipruleid[ipr] = #rv.iprule + 1
316
+	rv.iprule[ipruleid[ipr]] = { rule = ipr }
317
+
318
+	-- ip route list table 1-250
319
+	local routelisting, rlstr = ut.trim(sys.exec("ip rule | sed 's/://g' | awk -F' ' '$1>=2001 && $1<=2250' | awk -F' ' '{ print $NF }'")), ""
320
+		if routelisting ~= "" then
321
+			for line in routelisting:gmatch("[^\r\n]+") do
322
+				rlstr = rlstr .. line .. "\n" .. sys.exec("ip route list table " .. line)
323
+			end
324
+			rlstr = ut.trim(rlstr)
325
+		else
326
+			rlstr = "No data found"
327
+		end
328
+	rv.routelist = { }
329
+	rtlist = {}
330
+	rtlist[rlstr] = #rv.routelist + 1
331
+	rv.routelist[rtlist[rlstr]] = { iprtlist = rlstr }
332
+
333
+	-- default firewall output policy
334
+	local defout = ut.trim(sys.exec("uci get -p /var/state firewall.@defaults[0].output"))
335
+		if defout == "" then
336
+			defout = "No data found"
337
+		end
338
+	rv.fidef = { }
339
+	fwdf = {}
340
+	fwdf[defout] = #rv.fidef + 1
341
+	rv.fidef[fwdf[defout]] = { firedef = defout }
342
+
343
+	-- iptables
344
+	local iptbl = ut.trim(sys.exec("iptables -L -t mangle -v -n"))
345
+		if iptbl == "" then
346
+			iptbl = "No data found"
347
+		end
348
+	rv.iptables = { }
349
+	tables = {}
350
+	tables[iptbl] = #rv.iptables + 1
351
+	rv.iptables[tables[iptbl]] = { iptbls = iptbl }
352
+
353
+	luci.http.prepare_content("application/json")
354
+	luci.http.write_json(rv)
355
+end

+ 55
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_hotplug.lua View File

@@ -0,0 +1,55 @@
1
+-- ------ hotplug script configuration ------ --
2
+
3
+fs = require "nixio.fs"
4
+sys = require "luci.sys"
5
+ut = require "luci.util"
6
+
7
+script = "/etc/hotplug.d/iface/16-mwan3custom"
8
+scriptbak = "/etc/hotplug.d/iface/16-mwan3custombak"
9
+
10
+if luci.http.formvalue("cbid.luci.1._restorebak") then -- restore button has been clicked
11
+	luci.http.redirect(luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug") .. "?restore=yes")
12
+elseif luci.http.formvalue("restore") == "yes" then -- restore script from backup
13
+	os.execute("cp -f " .. scriptbak .. " " .. script)
14
+end
15
+
16
+
17
+m5 = SimpleForm("luci", nil)
18
+	m5:append(Template("mwan3/mwan3_adv_hotplug")) -- highlight current tab
19
+
20
+f = m5:section(SimpleSection, nil,
21
+	translate("This section allows you to modify the contents of /etc/hotplug.d/iface/16-mwan3custom<br />" ..
22
+	"This is useful for running system commands and/or scripts based on interface ifup or ifdown hotplug events<br /><br />" ..
23
+	"Notes:<br />" ..
24
+	"The first line of the script must be &#34;#!/bin/sh&#34; without quotes<br />" ..
25
+	"Lines beginning with # are comments and are not executed<br /><br />" ..
26
+	"Available variables:<br />" ..
27
+	"$ACTION is the hotplug event (ifup, ifdown)<br />" ..
28
+	"$INTERFACE is the interface name (wan1, wan2, etc.)<br />" ..
29
+	"$DEVICE is the device name attached to the interface (eth0.1, eth1, etc.)"))
30
+
31
+
32
+restore = f:option(Button, "_restorebak", translate("Restore default hotplug script"))
33
+	restore.inputtitle = translate("Restore...")
34
+	restore.inputstyle = "apply"
35
+
36
+t = f:option(TextValue, "lines")
37
+	t.rmempty = true
38
+	t.rows = 20
39
+
40
+	function t.cfgvalue()
41
+		local hps = fs.readfile(script)
42
+		if not hps or hps == "" then -- if script does not exist or is blank restore from backup
43
+			sys.call("cp -f " .. scriptbak .. " " .. script)
44
+			return fs.readfile(script)
45
+		else
46
+			return hps
47
+		end
48
+	end
49
+
50
+	function t.write(self, section, data) -- format and write new data to script
51
+		return fs.writefile(script, ut.trim(data:gsub("\r\n", "\n")) .. "\n")
52
+	end
53
+
54
+
55
+return m5

+ 32
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_mwan3.lua View File

@@ -0,0 +1,32 @@
1
+-- ------ mwan3 configuration ------ --
2
+
3
+ut = require "luci.util"
4
+
5
+mwan3file = "/etc/config/mwan3"
6
+
7
+
8
+m5 = SimpleForm("luci", nil)
9
+	m5:append(Template("mwan3/mwan3_adv_mwan3")) -- highlight current tab
10
+
11
+
12
+f = m5:section(SimpleSection, nil,
13
+	translate("This section allows you to modify the contents of /etc/config/mwan3"))
14
+
15
+t = f:option(TextValue, "lines")
16
+	t.rmempty = true
17
+	t.rows = 20
18
+
19
+	function t.cfgvalue()
20
+		return nixio.fs.readfile(mwan3file) or ""
21
+	end
22
+
23
+	function t.write(self, section, data) -- format and write new data to script
24
+		return nixio.fs.writefile(mwan3file, "\n" .. ut.trim(data:gsub("\r\n", "\n")) .. "\n")
25
+	end
26
+
27
+	function f.handle(self, state, data)
28
+		return true
29
+	end
30
+
31
+
32
+return m5

+ 32
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_adv_network.lua View File

@@ -0,0 +1,32 @@
1
+-- ------ network configuration ------ --
2
+
3
+ut = require "luci.util"
4
+
5
+netfile = "/etc/config/network"
6
+
7
+
8
+m5 = SimpleForm("networkconf", nil)
9
+	m5:append(Template("mwan3/mwan3_adv_network")) -- highlight current tab
10
+
11
+
12
+f = m5:section(SimpleSection, nil,
13
+	translate("This section allows you to modify the contents of /etc/config/network"))
14
+
15
+t = f:option(TextValue, "lines")
16
+	t.rmempty = true
17
+	t.rows = 20
18
+
19
+	function t.cfgvalue()
20
+		return nixio.fs.readfile(netfile) or ""
21
+	end
22
+
23
+	function t.write(self, section, data) -- format and write new data to script
24
+		return nixio.fs.writefile(netfile, "\n" .. ut.trim(data:gsub("\r\n", "\n")) .. "\n")
25
+	end
26
+
27
+	function f.handle(self, state, data)
28
+		return true
29
+	end
30
+
31
+
32
+return m5

+ 266
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interface.lua View File

@@ -0,0 +1,266 @@
1
+-- ------ extra functions ------ --
2
+
3
+function iface_check() -- find issues with too many interfaces, reliability and metric
4
+	uci.cursor():foreach("mwan3", "interface",
5
+		function (section)
6
+			local ifname = section[".name"]
7
+			ifnum = ifnum+1 -- count number of mwan3 interfaces configured
8
+			-- create list of metrics for none and duplicate checking
9
+			local metlkp = ut.trim(sys.exec("uci get -p /var/state network." .. ifname .. ".metric"))
10
+			if metlkp == "" then
11
+				err_found = 1
12
+				err_nomet_list = err_nomet_list .. ifname .. " "
13
+			else
14
+				metric_list = metric_list .. ifname .. " " .. metlkp .. "\n"
15
+			end
16
+			-- check if any interfaces have a higher reliability requirement than tracking IPs configured
17
+			local tipnum = tonumber(ut.trim(sys.exec("echo $(uci get -p /var/state mwan3." .. ifname .. ".track_ip) | wc -w")))
18
+			if tipnum > 0 then
19
+				local relnum = tonumber(ut.trim(sys.exec("uci get -p /var/state mwan3." .. ifname .. ".reliability")))
20
+				if relnum and relnum > tipnum then
21
+					err_found = 1
22
+					err_rel_list = err_rel_list .. ifname .. " "
23
+				end
24
+			end
25
+			-- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table
26
+			if ut.trim(sys.exec("uci get -p /var/state network." .. ifname)) == "interface" then
27
+				local ifdev = ut.trim(sys.exec("uci get -p /var/state network." .. ifname .. ".ifname"))
28
+				if ifdev == "uci: Entry not found" or ifdev == "" then
29
+					err_found = 1
30
+					err_netcfg_list = err_netcfg_list .. ifname .. " "
31
+					err_route_list = err_route_list .. ifname .. " "
32
+				else
33
+					local rtcheck = ut.trim(sys.exec("route -n | awk -F' ' '{ if ($8 == \"" .. ifdev .. "\" && $1 == \"0.0.0.0\") print $1 }'"))
34
+					if rtcheck == "" then
35
+						err_found = 1
36
+						err_route_list = err_route_list .. ifname .. " "
37
+					end
38
+				end
39
+			else
40
+				err_found = 1
41
+				err_netcfg_list = err_netcfg_list .. ifname .. " "
42
+				err_route_list = err_route_list .. ifname .. " "
43
+			end
44
+		end
45
+	)
46
+	-- check if any interfaces have duplicate metrics
47
+	local metric_dupnums = sys.exec("echo '" .. metric_list .. "' | awk -F' ' '{ print $2 }' | uniq -d")
48
+	if metric_dupnums ~= "" then
49
+		err_found = 1
50
+		local metric_dupes = ""
51
+		for line in metric_dupnums:gmatch("[^\r\n]+") do
52
+			metric_dupes = sys.exec("echo '" .. metric_list .. "' | grep '" .. line .. "' | awk -F' ' '{ print $1 }'")
53
+			err_dupmet_list = err_dupmet_list .. metric_dupes
54
+		end
55
+		err_dupmet_list = sys.exec("echo '" .. err_dupmet_list .. "' | tr '\n' ' '")
56
+	end
57
+end
58
+
59
+function iface_warn() -- display status and warning messages at the top of the page
60
+	local warns = ""
61
+	if ifnum <= 250 then
62
+		warns = "<strong>There are currently " .. ifnum .. " of 250 supported interfaces configured</strong>"
63
+	else
64
+		warns = "<font color=\"ff0000\"><strong>WARNING: " .. ifnum .. " interfaces are configured exceeding the maximum of 250!</strong></font>"
65
+	end
66
+	if err_rel_list ~= " " then
67
+		warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have a higher reliability requirement than there are tracking IP addresses!</strong></font>"
68
+	end
69
+	if err_route_list ~= " " then
70
+		warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have no default route in the main routing table!</strong></font>"
71
+	end
72
+	if err_netcfg_list ~= " " then
73
+		warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces are configured incorrectly or not at all in /etc/config/network!</strong></font>"
74
+	end
75
+	if err_nomet_list ~= " " then
76
+		warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have no metric configured in /etc/config/network!</strong></font>"
77
+	end
78
+	if err_dupmet_list ~= " " then
79
+		warns = warns .. "<br /><br /><font color=\"ff0000\"><strong>WARNING: some interfaces have duplicate metrics configured in /etc/config/network!</strong></font>"
80
+	end
81
+	return warns
82
+end
83
+
84
+-- ------ interface configuration ------ --
85
+
86
+dsp = require "luci.dispatcher"
87
+sys = require "luci.sys"
88
+ut = require "luci.util"
89
+
90
+ifnum = 0
91
+metric_list = ""
92
+err_found = 0
93
+err_dupmet_list = " "
94
+err_netcfg_list = " "
95
+err_nomet_list = " "
96
+err_rel_list = " "
97
+err_route_list = " "
98
+iface_check()
99
+
100
+
101
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Interface Configuration"),
102
+	translate(iface_warn()))
103
+	m5:append(Template("mwan3/mwan3_config_css"))
104
+
105
+
106
+mwan_interface = m5:section(TypedSection, "interface", translate("Interfaces"),
107
+	translate("MWAN3 supports up to 250 physical and/or logical interfaces<br />" ..
108
+	"MWAN3 requires that all interfaces have a unique metric configured in /etc/config/network<br />" ..
109
+	"Names must match the interface name found in /etc/config/network (see advanced tab)<br />" ..
110
+	"Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" ..
111
+	"Interfaces may not share the same name as configured members, policies or rules"))
112
+	mwan_interface.addremove = true
113
+	mwan_interface.dynamic = false
114
+	mwan_interface.sectionhead = "Interface"
115
+	mwan_interface.sortable = true
116
+	mwan_interface.template = "cbi/tblsection"
117
+	mwan_interface.extedit = dsp.build_url("admin", "network", "mwan3", "configuration", "interface", "%s")
118
+	function mwan_interface.create(self, section)
119
+		TypedSection.create(self, section)
120
+		m5.uci:save("mwan3")
121
+		luci.http.redirect(dsp.build_url("admin", "network", "mwan3", "configuration", "interface", section))
122
+	end
123
+
124
+
125
+enabled = mwan_interface:option(DummyValue, "enabled", translate("Enabled"))
126
+	enabled.rawhtml = true
127
+	function enabled.cfgvalue(self, s)
128
+		if self.map:get(s, "enabled") == "1" then
129
+			return "Yes"
130
+		else
131
+			return "No"
132
+		end
133
+	end
134
+
135
+track_ip = mwan_interface:option(DummyValue, "track_ip", translate("Tracking IP"))
136
+	track_ip.rawhtml = true
137
+	function track_ip.cfgvalue(self, s)
138
+		local str = ""
139
+		tracked = self.map:get(s, "track_ip")
140
+		if tracked then
141
+			for k,v in pairs(tracked) do
142
+				str = str .. v .. "<br />"
143
+			end
144
+			return str
145
+		else
146
+			return "&#8212;"
147
+		end
148
+	end
149
+
150
+reliability = mwan_interface:option(DummyValue, "reliability", translate("Tracking reliability"))
151
+	reliability.rawhtml = true
152
+	function reliability.cfgvalue(self, s)
153
+		if tracked then
154
+			return self.map:get(s, "reliability") or "&#8212;"
155
+		else
156
+			return "&#8212;"
157
+		end
158
+	end
159
+
160
+count = mwan_interface:option(DummyValue, "count", translate("Ping count"))
161
+	count.rawhtml = true
162
+	function count.cfgvalue(self, s)
163
+		if tracked then
164
+			return self.map:get(s, "count") or "&#8212;"
165
+		else
166
+			return "&#8212;"
167
+		end
168
+	end
169
+
170
+timeout = mwan_interface:option(DummyValue, "timeout", translate("Ping timeout"))
171
+	timeout.rawhtml = true
172
+	function timeout.cfgvalue(self, s)
173
+		if tracked then
174
+			local tcheck = self.map:get(s, "timeout")
175
+			if tcheck then
176
+				return tcheck .. "s"
177
+			else
178
+				return "&#8212;"
179
+			end
180
+		else
181
+			return "&#8212;"
182
+		end
183
+	end
184
+
185
+interval = mwan_interface:option(DummyValue, "interval", translate("Ping interval"))
186
+	interval.rawhtml = true
187
+	function interval.cfgvalue(self, s)
188
+		if tracked then
189
+			local icheck = self.map:get(s, "interval")
190
+			if icheck then
191
+				return icheck .. "s"
192
+			else
193
+				return "&#8212;"
194
+			end
195
+		else
196
+			return "&#8212;"
197
+		end
198
+	end
199
+
200
+down = mwan_interface:option(DummyValue, "down", translate("Interface down"))
201
+	down.rawhtml = true
202
+	function down.cfgvalue(self, s)
203
+		if tracked then
204
+			return self.map:get(s, "down") or "&#8212;"
205
+		else
206
+			return "&#8212;"
207
+		end
208
+	end
209
+
210
+up = mwan_interface:option(DummyValue, "up", translate("Interface up"))
211
+	up.rawhtml = true
212
+	function up.cfgvalue(self, s)
213
+		if tracked then
214
+			return self.map:get(s, "up") or "&#8212;"
215
+		else
216
+			return "&#8212;"
217
+		end
218
+	end
219
+
220
+metric = mwan_interface:option(DummyValue, "metric", translate("Metric"))
221
+	metric.rawhtml = true
222
+	function metric.cfgvalue(self, s)
223
+		local metcheck = sys.exec("uci get -p /var/state network." .. s .. ".metric")
224
+		if metcheck ~= "" then
225
+			return metcheck
226
+		else
227
+			return "&#8212;"
228
+		end
229
+	end
230
+
231
+errors = mwan_interface:option(DummyValue, "errors", translate("Errors"))
232
+	errors.rawhtml = true
233
+	function errors.cfgvalue(self, s)
234
+		if err_found == 1 then
235
+			local mouseover, linebrk = "", ""
236
+			if string.find(err_rel_list, " " .. s .. " ") then
237
+				mouseover = "Higher reliability requirement than there are tracking IP addresses"
238
+				linebrk = "&#10;&#10;"
239
+			end
240
+			if string.find(err_route_list, " " .. s .. " ") then
241
+				mouseover = mouseover .. linebrk .. "No default route in the main routing table"
242
+				linebrk = "&#10;&#10;"
243
+			end
244
+			if string.find(err_netcfg_list, " " .. s .. " ") then
245
+				mouseover = mouseover .. linebrk .. "Configured incorrectly or not at all in /etc/config/network"
246
+				linebrk = "&#10;&#10;"
247
+			end
248
+			if string.find(err_nomet_list, " " .. s .. " ") then
249
+				mouseover = mouseover .. linebrk .. "No metric configured in /etc/config/network"
250
+				linebrk = "&#10;&#10;"
251
+			end
252
+			if string.find(err_dupmet_list, " " .. s .. " ") then
253
+				mouseover = mouseover .. linebrk .. "Duplicate metric configured in /etc/config/network"
254
+			end
255
+			if mouseover == "" then
256
+				return ""
257
+			else
258
+				return "<span title=\"" .. mouseover .. "\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>"
259
+			end
260
+		else
261
+			return ""
262
+		end
263
+	end
264
+
265
+
266
+return m5

+ 191
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_interfaceconfig.lua View File

@@ -0,0 +1,191 @@
1
+-- ------ extra functions ------ --
2
+
3
+function iface_check()
4
+	metcheck = ut.trim(sys.exec("uci get -p /var/state network." .. arg[1] .. ".metric"))
5
+	if metcheck == "" then -- no metric
6
+		err_nomet = 1
7
+	else -- if metric exists create list of interface metrics to compare against for duplicates
8
+		uci.cursor():foreach("mwan3", "interface",
9
+			function (section)
10
+				local metlkp = ut.trim(sys.exec("uci get -p /var/state network." .. section[".name"] .. ".metric"))
11
+				metric_list = metric_list .. section[".name"] .. " " .. metlkp .. "\n"
12
+			end
13
+		)
14
+		-- compare metric against list
15
+		local metric_dupnums, metric_dupes = sys.exec("echo '" .. metric_list .. "' | awk -F' ' '{ print $2 }' | uniq -d"), ""
16
+		for line in metric_dupnums:gmatch("[^\r\n]+") do
17
+			metric_dupes = sys.exec("echo '" .. metric_list .. "' | grep '" .. line .. "' | awk -F' ' '{ print $1 }'")
18
+			err_dupmet_list = err_dupmet_list .. metric_dupes
19
+		end
20
+		if sys.exec("echo '" .. err_dupmet_list .. "' | grep -w " .. arg[1]) ~= "" then
21
+			err_dupmet = 1
22
+		end
23
+	end
24
+	-- check if this interface has a higher reliability requirement than track IPs configured
25
+	local tipnum = tonumber(ut.trim(sys.exec("echo $(uci get -p /var/state mwan3." .. arg[1] .. ".track_ip) | wc -w")))
26
+	if tipnum > 0 then
27
+		local relnum = tonumber(ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".reliability")))
28
+		if relnum and relnum > tipnum then
29
+			err_reliability = 1
30
+		end
31
+	end
32
+	-- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table
33
+	if ut.trim(sys.exec("uci get -p /var/state network." .. arg[1])) == "interface" then
34
+		local ifdev = ut.trim(sys.exec("uci get -p /var/state network." .. arg[1] .. ".ifname"))
35
+		if ifdev == "uci: Entry not found" or ifdev == "" then
36
+			err_netcfg = 1
37
+			err_route = 1
38
+		else
39
+			local rtcheck = ut.trim(sys.exec("route -n | awk -F' ' '{ if ($8 == \"" .. ifdev .. "\" && $1 == \"0.0.0.0\") print $1 }'"))
40
+			if rtcheck == "" then
41
+				err_route = 1
42
+			end
43
+		end
44
+	else
45
+		err_netcfg = 1
46
+		err_route = 1
47
+	end
48
+end
49
+
50
+function iface_warn() -- display warning messages at the top of the page
51
+	local warns, linebrk = "", ""
52
+	if err_reliability == 1 then
53
+		warns = "<font color=\"ff0000\"><strong>WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!</strong></font>"
54
+		linebrk = "<br /><br />"
55
+	end
56
+	if err_route == 1 then
57
+		warns = warns .. linebrk .. "<font color=\"ff0000\"><strong>WARNING: this interface has no default route in the main routing table!</strong></font>"
58
+		linebrk = "<br /><br />"
59
+	end
60
+	if err_netcfg == 1 then
61
+		warns = warns .. linebrk .. "<font color=\"ff0000\"><strong>WARNING: this interface is configured incorrectly or not at all in /etc/config/network!</strong></font>"
62
+		linebrk = "<br /><br />"
63
+	end
64
+	if err_nomet == 1 then
65
+		warns = warns .. linebrk .. "<font color=\"ff0000\"><strong>WARNING: this interface has no metric configured in /etc/config/network!</strong></font>"
66
+	elseif err_dupmet == 1 then
67
+		warns = warns .. linebrk .. "<font color=\"ff0000\"><strong>WARNING: this and other interfaces have duplicate metrics configured in /etc/config/network!</strong></font>"
68
+	end
69
+	return warns
70
+end
71
+
72
+-- ------ interface configuration ------ --
73
+
74
+dsp = require "luci.dispatcher"
75
+sys = require "luci.sys"
76
+ut = require "luci.util"
77
+arg[1] = arg[1] or ""
78
+
79
+metcheck = ""
80
+metric_list = ""
81
+err_dupmet_list = ""
82
+err_rel_list = ""
83
+err_nomet = 0
84
+err_dupmet = 0
85
+err_route = 0
86
+err_netcfg = 0
87
+err_reliability = 0
88
+iface_check()
89
+
90
+
91
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Interface Configuration - " .. arg[1]),
92
+	translate(iface_warn()))
93
+	m5.redirect = dsp.build_url("admin", "network", "mwan3", "configuration", "interface")
94
+
95
+
96
+mwan_interface = m5:section(NamedSection, arg[1], "interface", "")
97
+	mwan_interface.addremove = false
98
+	mwan_interface.dynamic = false
99
+
100
+
101
+enabled = mwan_interface:option(ListValue, "enabled", translate("Enabled"))
102
+	enabled.default = "1"
103
+	enabled:value("1", translate("Yes"))
104
+	enabled:value("0", translate("No"))
105
+
106
+track_ip = mwan_interface:option(DynamicList, "track_ip", translate("Tracking IP"),
107
+	translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online"))
108
+	track_ip.datatype = "ipaddr"
109
+
110
+reliability = mwan_interface:option(Value, "reliability", translate("Tracking reliability"),
111
+	translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up"))
112
+	reliability.datatype = "range(1, 100)"
113
+	reliability.default = "1"
114
+
115
+count = mwan_interface:option(ListValue, "count", translate("Ping count"))
116
+	count.default = "1"
117
+	count:value("1")
118
+	count:value("2")
119
+	count:value("3")
120
+	count:value("4")
121
+	count:value("5")
122
+
123
+timeout = mwan_interface:option(ListValue, "timeout", translate("Ping timeout"))
124
+	timeout.default = "2"
125
+	timeout:value("1", translate("1 second"))
126
+	timeout:value("2", translate("2 seconds"))
127
+	timeout:value("3", translate("3 seconds"))
128
+	timeout:value("4", translate("4 seconds"))
129
+	timeout:value("5", translate("5 seconds"))
130
+	timeout:value("6", translate("6 seconds"))
131
+	timeout:value("7", translate("7 seconds"))
132
+	timeout:value("8", translate("8 seconds"))
133
+	timeout:value("9", translate("9 seconds"))
134
+	timeout:value("10", translate("10 seconds"))
135
+
136
+interval = mwan_interface:option(ListValue, "interval", translate("Ping interval"))
137
+	interval.default = "5"
138
+	interval:value("1", translate("1 second"))
139
+	interval:value("3", translate("3 seconds"))
140
+	interval:value("5", translate("5 seconds"))
141
+	interval:value("10", translate("10 seconds"))
142
+	interval:value("20", translate("20 seconds"))
143
+	interval:value("30", translate("30 seconds"))
144
+	interval:value("60", translate("1 minute"))
145
+	interval:value("300", translate("5 minutes"))
146
+	interval:value("600", translate("10 minutes"))
147
+	interval:value("900", translate("15 minutes"))
148
+	interval:value("1800", translate("30 minutes"))
149
+	interval:value("3600", translate("1 hour"))
150
+
151
+down = mwan_interface:option(ListValue, "down", translate("Interface down"),
152
+	translate("Interface will be deemed down after this many failed ping tests"))
153
+	down.default = "3"
154
+	down:value("1")
155
+	down:value("2")
156
+	down:value("3")
157
+	down:value("4")
158
+	down:value("5")
159
+	down:value("6")
160
+	down:value("7")
161
+	down:value("8")
162
+	down:value("9")
163
+	down:value("10")
164
+
165
+up = mwan_interface:option(ListValue, "up", translate("Interface up"),
166
+	translate("Downed interface will be deemed up after this many successful ping tests"))
167
+	up.default = "3"
168
+	up:value("1")
169
+	up:value("2")
170
+	up:value("3")
171
+	up:value("4")
172
+	up:value("5")
173
+	up:value("6")
174
+	up:value("7")
175
+	up:value("8")
176
+	up:value("9")
177
+	up:value("10")
178
+
179
+metric = mwan_interface:option(DummyValue, "metric", translate("Metric"),
180
+	translate("This displays the metric assigned to this interface in /etc/config/network"))
181
+	metric.rawhtml = true
182
+	function metric.cfgvalue(self, s)
183
+		if err_nomet == 0 then
184
+			return metcheck
185
+		else
186
+			return "&#8212;"
187
+		end
188
+	end
189
+
190
+
191
+return m5

+ 46
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_member.lua View File

@@ -0,0 +1,46 @@
1
+-- ------ member configuration ------ --
2
+
3
+ds = require "luci.dispatcher"
4
+
5
+
6
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Member Configuration"))
7
+	m5:append(Template("mwan3/mwan3_config_css"))
8
+
9
+
10
+mwan_member = m5:section(TypedSection, "member", translate("Members"),
11
+	translate("Members are profiles attaching a metric and weight to an MWAN3 interface<br />" ..
12
+	"Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" ..
13
+	"Members may not share the same name as configured interfaces, policies or rules"))
14
+	mwan_member.addremove = true
15
+	mwan_member.dynamic = false
16
+	mwan_member.sectionhead = "Member"
17
+	mwan_member.sortable = true
18
+	mwan_member.template = "cbi/tblsection"
19
+	mwan_member.extedit = ds.build_url("admin", "network", "mwan3", "configuration", "member", "%s")
20
+	function mwan_member.create(self, section)
21
+		TypedSection.create(self, section)
22
+		m5.uci:save("mwan3")
23
+		luci.http.redirect(ds.build_url("admin", "network", "mwan3", "configuration", "member", section))
24
+	end
25
+
26
+
27
+interface = mwan_member:option(DummyValue, "interface", translate("Interface"))
28
+	interface.rawhtml = true
29
+	function interface.cfgvalue(self, s)
30
+		return self.map:get(s, "interface") or "&#8212;"
31
+	end
32
+
33
+metric = mwan_member:option(DummyValue, "metric", translate("Metric"))
34
+	metric.rawhtml = true
35
+	function metric.cfgvalue(self, s)
36
+		return self.map:get(s, "metric") or "1"
37
+	end
38
+
39
+weight = mwan_member:option(DummyValue, "weight", translate("Weight"))
40
+	weight.rawhtml = true
41
+	function weight.cfgvalue(self, s)
42
+		return self.map:get(s, "weight") or "1"
43
+	end
44
+
45
+
46
+return m5

+ 47
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_memberconfig.lua View File

@@ -0,0 +1,47 @@
1
+-- ------ extra functions ------ --
2
+
3
+function cbi_add_interface(field)
4
+	uci.cursor():foreach("mwan3", "interface",
5
+		function (section)
6
+			field:value(section[".name"])
7
+		end
8
+	)
9
+end
10
+
11
+-- ------ member configuration ------ --
12
+
13
+dsp = require "luci.dispatcher"
14
+arg[1] = arg[1] or ""
15
+
16
+
17
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Member Configuration - ") .. arg[1])
18
+	m5.redirect = dsp.build_url("admin", "network", "mwan3", "configuration", "member")
19
+
20
+
21
+mwan_member = m5:section(NamedSection, arg[1], "member", "")
22
+	mwan_member.addremove = false
23
+	mwan_member.dynamic = false
24
+
25
+
26
+interface = mwan_member:option(Value, "interface", translate("Interface"))
27
+	cbi_add_interface(interface)
28
+
29
+metric = mwan_member:option(Value, "metric", translate("Metric"),
30
+	translate("Acceptable values: 1-1000. Defaults to 1 if not set"))
31
+	metric.datatype = "range(1, 1000)"
32
+
33
+weight = mwan_member:option(Value, "weight", translate("Weight"),
34
+	translate("Acceptable values: 1-1000. Defaults to 1 if not set"))
35
+	weight.datatype = "range(1, 1000)"
36
+
37
+
38
+-- ------ currently configured interfaces ------ --
39
+
40
+mwan_interface = m5:section(TypedSection, "interface", translate("Currently Configured Interfaces"))
41
+	mwan_interface.addremove = false
42
+	mwan_interface.dynamic = false
43
+	mwan_interface.sortable = false
44
+	mwan_interface.template = "cbi/tblsection"
45
+
46
+
47
+return m5

+ 82
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policy.lua View File

@@ -0,0 +1,82 @@
1
+-- ------ extra functions ------ --
2
+
3
+function policy_check() -- check to see if any policy names exceed the maximum of 15 characters
4
+	uci.cursor():foreach("mwan3", "policy",
5
+		function (section)
6
+			if string.len(section[".name"]) > 15 then
7
+				toolong = 1
8
+				err_name_list = err_name_list .. section[".name"] .. " "
9
+			end
10
+		end
11
+	)
12
+end
13
+
14
+function policy_warn() -- display status and warning messages at the top of the page
15
+	if toolong == 1 then
16
+		return "<font color=\"ff0000\"><strong>WARNING: Some policies have names exceeding the maximum of 15 characters!</strong></font>"
17
+	else
18
+		return ""
19
+	end
20
+end
21
+
22
+-- ------ policy configuration ------ --
23
+
24
+ds = require "luci.dispatcher"
25
+sys = require "luci.sys"
26
+
27
+toolong = 0
28
+err_name_list = " "
29
+policy_check()
30
+
31
+
32
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Policy Configuration"),
33
+	translate(policy_warn()))
34
+	m5:append(Template("mwan3/mwan3_config_css"))
35
+
36
+
37
+mwan_policy = m5:section(TypedSection, "policy", translate("Policies"),
38
+	translate("Policies are profiles grouping one or more members controlling how MWAN3 distributes traffic<br />" ..
39
+	"Member interfaces with lower metrics are used first. Interfaces with the same metric load-balance<br />" ..
40
+	"Load-balanced member interfaces distribute more traffic out those with higher weights<br />" ..
41
+	"Names may contain characters A-Z, a-z, 0-9, _ and no spaces. Names must be 15 characters or less<br />" ..
42
+	"Policies may not share the same name as configured interfaces, members or rules"))
43
+	mwan_policy.addremove = true
44
+	mwan_policy.dynamic = false
45
+	mwan_policy.sectionhead = "Policy"
46
+	mwan_policy.sortable = true
47
+	mwan_policy.template = "cbi/tblsection"
48
+	mwan_policy.extedit = ds.build_url("admin", "network", "mwan3", "configuration", "policy", "%s")
49
+	function mwan_policy.create(self, section)
50
+		TypedSection.create(self, section)
51
+		m5.uci:save("mwan3")
52
+		luci.http.redirect(ds.build_url("admin", "network", "mwan3", "configuration", "policy", section))
53
+	end
54
+
55
+
56
+use_member = mwan_policy:option(DummyValue, "use_member", translate("Members assigned"))
57
+	use_member.rawhtml = true
58
+	function use_member.cfgvalue(self, s)
59
+		local tab, str = self.map:get(s, "use_member"), ""
60
+		if tab then
61
+			for k,v in pairs(tab) do
62
+				str = str .. v .. "<br />"
63
+			end
64
+			return str
65
+		else
66
+			return "&#8212;"
67
+		end
68
+		
69
+	end
70
+
71
+errors = mwan_policy:option(DummyValue, "errors", translate("Errors"))
72
+	errors.rawhtml = true
73
+	function errors.cfgvalue(self, s)
74
+		if not string.find(err_name_list, " " .. s .. " ") then
75
+			return ""
76
+		else
77
+			return "<span title=\"Name exceeds 15 characters\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>"
78
+		end
79
+	end
80
+
81
+
82
+return m5

+ 58
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_policyconfig.lua View File

@@ -0,0 +1,58 @@
1
+-- ------ extra functions ------ --
2
+
3
+function policy_check() -- check to see if this policy's name exceed the maximum of 15 characters
4
+	polchar = string.len(arg[1])
5
+	if polchar > 15 then
6
+		toolong = 1
7
+	end
8
+end
9
+
10
+function policy_warn() -- display status and warning messages at the top of the page
11
+	if toolong == 1 then
12
+		return "<font color=\"ff0000\"><strong>WARNING: this policy's name is " .. polchar .. " characters exceeding the maximum of 15!</strong></font>"
13
+	else
14
+		return ""
15
+	end
16
+end
17
+
18
+function cbi_add_member(field)
19
+	uci.cursor():foreach("mwan3", "member",
20
+		function (section)
21
+			field:value(section[".name"])
22
+		end
23
+	)
24
+end
25
+
26
+-- ------ policy configuration ------ --
27
+
28
+dsp = require "luci.dispatcher"
29
+arg[1] = arg[1] or ""
30
+
31
+toolong = 0
32
+policy_check()
33
+
34
+
35
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Policy Configuration - " .. arg[1]),
36
+	translate(policy_warn()))
37
+	m5.redirect = dsp.build_url("admin", "network", "mwan3", "configuration", "policy")
38
+
39
+
40
+mwan_policy = m5:section(NamedSection, arg[1], "policy", "")
41
+	mwan_policy.addremove = false
42
+	mwan_policy.dynamic = false
43
+
44
+
45
+use_member = mwan_policy:option(DynamicList, "use_member", translate("Member used"))
46
+	cbi_add_member(use_member)
47
+
48
+
49
+-- ------ currently configured members ------ --
50
+
51
+mwan_member = m5:section(TypedSection, "member", translate("Currently Configured Members"))
52
+	mwan_member.addremove = false
53
+	mwan_member.dynamic = false
54
+	mwan_member.sortable = false
55
+	mwan_member.template = "cbi/tblsection"
56
+
57
+
58
+return m5

+ 109
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_rule.lua View File

@@ -0,0 +1,109 @@
1
+-- ------ extra functions ------ --
2
+
3
+function rule_check() -- determine if rules needs a proper protocol configured
4
+	uci.cursor():foreach("mwan3", "rule",
5
+		function (section)
6
+			local sport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. section[".name"] .. ".src_port"))
7
+			local dport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. section[".name"] .. ".dest_port"))
8
+			if sport ~= "" or dport ~= "" then -- ports configured
9
+				local proto = ut.trim(sys.exec("uci get -p /var/state mwan3." .. section[".name"] .. ".proto"))
10
+				if proto == "" or proto == "all" then -- no or improper protocol
11
+					err_proto_list = err_proto_list .. section[".name"] .. " "
12
+				end
13
+			end
14
+		end
15
+	)
16
+end
17
+
18
+function rule_warn() -- display warning messages at the top of the page
19
+	if err_proto_list ~= " " then
20
+		return "<font color=\"ff0000\"><strong>WARNING: some rules have a port configured with no or improper protocol specified! Please configure a specific protocol!</strong></font>"
21
+	else
22
+		return ""
23
+	end
24
+end
25
+
26
+-- ------ rule configuration ------ --
27
+
28
+dsp = require "luci.dispatcher"
29
+sys = require "luci.sys"
30
+ut = require "luci.util"
31
+
32
+err_proto = 0
33
+err_proto_list = " "
34
+rule_check()
35
+
36
+
37
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Traffic Rule Configuration"),
38
+	translate(rule_warn()))
39
+	m5:append(Template("mwan3/mwan3_config_css"))
40
+
41
+
42
+mwan_rule = m5:section(TypedSection, "rule", translate("Traffic Rules"),
43
+	translate("Rules specify which traffic will use a particular MWAN3 policy based on IP address, port or protocol<br />" ..
44
+	"Rules are matched from top to bottom. Rules below a matching rule are ignored. Traffic not matching any rule is routed using the main routing table<br />" ..
45
+	"Traffic destined for known (other than default) networks is handled by the main routing table. Traffic matching a rule, but all WAN interfaces for that policy are down will be blackholed<br />" ..
46
+	"Names may contain characters A-Z, a-z, 0-9, _ and no spaces<br />" ..
47
+	"Rules may not share the same name as configured interfaces, members or policies"))
48
+	mwan_rule.addremove = true
49
+	mwan_rule.anonymous = false
50
+	mwan_rule.dynamic = false
51
+	mwan_rule.sectionhead = "Rule"
52
+	mwan_rule.sortable = true
53
+	mwan_rule.template = "cbi/tblsection"
54
+	mwan_rule.extedit = dsp.build_url("admin", "network", "mwan3", "configuration", "rule", "%s")
55
+	function mwan_rule.create(self, section)
56
+		TypedSection.create(self, section)
57
+		m5.uci:save("mwan3")
58
+		luci.http.redirect(dsp.build_url("admin", "network", "mwan3", "configuration", "rule", section))
59
+	end
60
+
61
+
62
+src_ip = mwan_rule:option(DummyValue, "src_ip", translate("Source address"))
63
+	src_ip.rawhtml = true
64
+	function src_ip.cfgvalue(self, s)
65
+		return self.map:get(s, "src_ip") or "&#8212;"
66
+	end
67
+
68
+src_port = mwan_rule:option(DummyValue, "src_port", translate("Source port"))
69
+	src_port.rawhtml = true
70
+	function src_port.cfgvalue(self, s)
71
+		return self.map:get(s, "src_port") or "&#8212;"
72
+	end
73
+
74
+dest_ip = mwan_rule:option(DummyValue, "dest_ip", translate("Destination address"))
75
+	dest_ip.rawhtml = true
76
+	function dest_ip.cfgvalue(self, s)
77
+		return self.map:get(s, "dest_ip") or "&#8212;"
78
+	end
79
+
80
+dest_port = mwan_rule:option(DummyValue, "dest_port", translate("Destination port"))
81
+	dest_port.rawhtml = true
82
+	function dest_port.cfgvalue(self, s)
83
+		return self.map:get(s, "dest_port") or "&#8212;"
84
+	end
85
+
86
+proto = mwan_rule:option(DummyValue, "proto", translate("Protocol"))
87
+	proto.rawhtml = true
88
+	function proto.cfgvalue(self, s)
89
+		return self.map:get(s, "proto") or "all"
90
+	end
91
+
92
+use_policy = mwan_rule:option(DummyValue, "use_policy", translate("Policy assigned"))
93
+	use_policy.rawhtml = true
94
+	function use_policy.cfgvalue(self, s)
95
+		return self.map:get(s, "use_policy") or "&#8212;"
96
+	end
97
+
98
+errors = mwan_rule:option(DummyValue, "errors", translate("Errors"))
99
+	errors.rawhtml = true
100
+	function errors.cfgvalue(self, s)
101
+		if not string.find(err_proto_list, " " .. s .. " ") then
102
+			return ""
103
+		else
104
+			return "<span title=\"No protocol specified\"><img src=\"/luci-static/resources/cbi/reset.gif\" alt=\"error\"></img></span>"
105
+		end
106
+	end
107
+
108
+
109
+return m5

+ 99
- 0
net/mwan3-luci/files/usr/lib/lua/luci/model/cbi/mwan3/mwan3_ruleconfig.lua View File

@@ -0,0 +1,99 @@
1
+-- ------ extra functions ------ --
2
+
3
+function rule_check() -- determine if rule needs a protocol specified
4
+	local sport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".src_port"))
5
+	local dport = ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".dest_port"))
6
+	if sport ~= "" or dport ~= "" then -- ports configured
7
+		local proto = ut.trim(sys.exec("uci get -p /var/state mwan3." .. arg[1] .. ".proto"))
8
+		if proto == "" or proto == "all" then -- no or improper protocol
9
+			err_proto = 1
10
+		end
11
+	end
12
+end
13
+
14
+function rule_warn() -- display warning message at the top of the page
15
+	if err_proto == 1 then
16
+		return "<font color=\"ff0000\"><strong>WARNING: this rule is incorrectly configured with no or improper protocol specified! Please configure a specific protocol!</strong></font>"
17
+	else
18
+		return ""
19
+	end
20
+end
21
+
22
+function cbi_add_policy(field)
23
+	uci.cursor():foreach("mwan3", "policy",
24
+		function (section)
25
+			field:value(section[".name"])
26
+		end
27
+	)
28
+end
29
+
30
+function cbi_add_protocol(field)
31
+	local protos = ut.trim(sys.exec("cat /etc/protocols | grep '	# ' | awk -F' ' '{print $1}' | grep -vw -e 'ip' -e 'tcp' -e 'udp' -e 'icmp' -e 'esp' | grep -v 'ipv6' | sort | tr '\n' ' '"))
32
+	for p in string.gmatch(protos, "%S+") do
33
+		field:value(p)
34
+	end
35
+end
36
+
37
+-- ------ rule configuration ------ --
38
+
39
+dsp = require "luci.dispatcher"
40
+sys = require "luci.sys"
41
+ut = require "luci.util"
42
+arg[1] = arg[1] or ""
43
+
44
+err_proto = 0
45
+rule_check()
46
+
47
+
48
+m5 = Map("mwan3", translate("MWAN3 Multi-WAN Rule Configuration - ") .. arg[1],
49
+	translate(rule_warn()))
50
+	m5.redirect = dsp.build_url("admin", "network", "mwan3", "configuration", "rule")
51
+
52
+
53
+mwan_rule = m5:section(NamedSection, arg[1], "rule", "")
54
+	mwan_rule.addremove = false
55
+	mwan_rule.dynamic = false
56
+
57
+
58
+src_ip = mwan_rule:option(Value, "src_ip", translate("Source address"),
59
+	translate("Supports CIDR notation (eg \"192.168.100.0/24\") without quotes"))
60
+	src_ip.datatype = ipaddr
61
+
62
+src_port = mwan_rule:option(Value, "src_port", translate("Source port"),
63
+	translate("May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes"))
64
+
65
+dest_ip = mwan_rule:option(Value, "dest_ip", translate("Destination address"),
66
+	translate("Supports CIDR notation (eg \"192.168.100.0/24\") without quotes"))
67
+	dest_ip.datatype = ipaddr
68
+
69
+dest_port = mwan_rule:option(Value, "dest_port", translate("Destination port"),
70
+	translate("May be entered as a single or multiple port(s) (eg \"22\" or \"80,443\") or as a portrange (eg \"1024:2048\") without quotes"))
71
+
72
+proto = mwan_rule:option(Value, "proto", translate("Protocol"),
73
+	translate("View the contents of /etc/protocols for protocol descriptions"))
74
+	proto.default = "all"
75
+	proto.rmempty = false
76
+	proto:value("all")
77
+	proto:value("ip")
78
+	proto:value("tcp")
79
+	proto:value("udp")
80
+	proto:value("icmp")
81
+	proto:value("esp")
82
+	cbi_add_protocol(proto)
83
+
84
+use_policy = mwan_rule:option(Value, "use_policy", translate("Policy assigned"))
85
+	cbi_add_policy(use_policy)
86
+	use_policy:value("unreachable")
87
+	use_policy:value("default")
88
+
89
+
90
+-- ------ currently configured policies ------ --
91
+
92
+mwan_policy = m5:section(TypedSection, "policy", translate("Currently Configured Policies"))
93
+	mwan_policy.addremove = false
94
+	mwan_policy.dynamic = false
95
+	mwan_policy.sortable = false
96
+	mwan_policy.template = "cbi/tblsection"
97
+
98
+
99
+return m5

+ 1
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/admin_status/index/mwan3.htm View File

@@ -0,0 +1 @@
1
+<%+mwan3/mwan3_status%>

+ 134
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_diagnostics.htm View File

@@ -0,0 +1,134 @@
1
+<%+header%>
2
+
3
+<ul class="cbi-tabmenu">
4
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
5
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
6
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
7
+	<li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
8
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
9
+</ul>
10
+
11
+<%
12
+	local uci = require "luci.model.uci"
13
+
14
+	str = ""
15
+	uci.cursor():foreach("mwan3", "interface",
16
+		function (section)
17
+			str = str .. section[".name"] .. " "
18
+		end
19
+	)
20
+%>
21
+
22
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
23
+<script type="text/javascript">//<![CDATA[
24
+	var stxhr = new XHR();
25
+
26
+	function update_status(tool, alt)
27
+	{
28
+		var iface = document.getElementById('mwan3iface').value;
29
+		var output = document.getElementById('diag_output');
30
+
31
+		if (tool == "service")
32
+			{
33
+				output.innerHTML =
34
+					'<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' +
35
+					"Waiting for MWAN3 to " + alt + "..."
36
+				;
37
+			}
38
+			else
39
+			{
40
+				output.innerHTML =
41
+					'<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="padding: 20px; vertical-align: middle;" /> ' +
42
+					"Waiting for diagnostic results..."
43
+				;
44
+			}
45
+
46
+		output.parentNode.style.display = 'block';
47
+		output.style.display = 'inline';
48
+
49
+		stxhr.get('<%=luci.dispatcher.build_url("admin", "network", "mwan3", "advanced")%>/diag_display' + '/' + iface + '/' + tool + '/' + alt, null,
50
+			function(x, st)
51
+			{
52
+				if (st.diagres)
53
+				{
54
+					output.innerHTML = String.format('<pre id="diag_output_css">%h</pre>', st.diagres[0].diagresult);
55
+				}
56
+				else
57
+				{
58
+					var temp = '';
59
+					var ncint = 'No diagnostic results returned';
60
+					temp = String.format(
61
+						'<pre id="diag_output_css"><strong>%s</strong></pre>',
62
+							ncint
63
+					);
64
+					output.innerHTML = temp;
65
+				}
66
+			}
67
+		);
68
+	}
69
+//]]></script>
70
+
71
+<div id="mwan3_diagnostics" class="cbi-map">
72
+	<fieldset id="diag_select" class="cbi-section">
73
+		<legend><%:MWAN3 Interface Diagnostics%></legend>
74
+		<select id="mwan3iface">
75
+			<% for z in str:gmatch("[^ ]+") do -%><option value="<%=z%>"><%=z%></option><%- end %>
76
+		</select>
77
+		<div id="buttoncss">
78
+			<input type="button" value="<%:Ping default gateway%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'gateway')" />
79
+			<input type="button" value="<%:Ping tracking IP%>" class="cbi-button cbi-button-apply" onclick="update_status('ping', 'track_ip')" />
80
+			<input type="button" value="<%:Check IP rules%>" class="cbi-button cbi-button-apply" onclick="update_status('rulechk', null)" />
81
+			<input type="button" value="<%:Check routing table%>" class="cbi-button cbi-button-apply" onclick="update_status('routechk', null)" />
82
+			<input type="button" value="<%:Hotplug ifup%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifup')" />
83
+			<input type="button" value="<%:Hotplug ifdown%>" class="cbi-button cbi-button-apply" onclick="update_status('hotplug', 'ifdown')" />
84
+		</div>
85
+	</fieldset>
86
+	<fieldset id="diag_select" class="cbi-section">
87
+		<legend><%:MWAN3 Service Control%></legend>
88
+		<div id="buttoncss">
89
+			<input type="button" value="<%:Restart MWAN3%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'restart')" />
90
+			<input type="button" value="<%:Stop MWAN3%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'stop')" />
91
+			<input type="button" value="<%:Start MWAN3%>" class="cbi-button cbi-button-apply" onclick="update_status('service', 'start')" />
92
+		</div>
93
+	</fieldset>
94
+	<fieldset class="cbi-section" style="display:none">
95
+		<legend><%:Diagnostic Results%></legend>
96
+		<div id="diag_output"></div>
97
+	</fieldset>
98
+</div>
99
+
100
+<style type="text/css">
101
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
102
+	max-width: none;
103
+	margin-left: 30px;
104
+	padding-right: 30px;
105
+	width: auto;
106
+  }
107
+  #mwan3_diagnostics {
108
+	background-color: #FFFFFF;
109
+	border: 1px dotted #555555;
110
+	padding: 20px;
111
+  }
112
+  #diag_select {
113
+	padding: 12px 20px 20px 20px;
114
+  }
115
+  #mwan3iface {
116
+	float: left;
117
+	margin: 8px 20px 0px 0px;
118
+  }
119
+  #buttoncss {
120
+	display: table;
121
+	float: left;
122
+	text-align: left;
123
+  }
124
+  .cbi-button {
125
+	margin: 8px 20px 0px 0px;
126
+	min-width: 153px;
127
+  }
128
+  #diag_output_css {
129
+	padding: 20px;
130
+	text-align: left;
131
+  }
132
+</style>
133
+
134
+<%+footer%>

+ 23
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_hotplug.htm View File

@@ -0,0 +1,23 @@
1
+<ul class="cbi-tabmenu">
2
+	<li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
3
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
4
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
5
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
6
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
7
+</ul>
8
+
9
+<style type="text/css">
10
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
11
+	max-width: none;
12
+	margin: 0px 0px 0px 30px;
13
+	padding-right: 30px;
14
+	width: auto;
15
+  }
16
+  .cbi-section-node {
17
+	margin-top: 20px;
18
+  }
19
+  .cbi-section {
20
+	border: 1px dotted #555555;
21
+	padding: 20px;
22
+  }
23
+</style>

+ 23
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_mwan3.htm View File

@@ -0,0 +1,23 @@
1
+<ul class="cbi-tabmenu">
2
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
3
+	<li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
4
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
5
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
6
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
7
+</ul>
8
+
9
+<style type="text/css">
10
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
11
+	max-width: none;
12
+	margin: 0px 0px 0px 30px;
13
+	padding-right: 30px;
14
+	width: auto;
15
+  }
16
+  .cbi-section-node {
17
+	margin-top: 20px;
18
+  }
19
+  .cbi-section {
20
+	border: 1px dotted #555555;
21
+	padding: 20px;
22
+  }
23
+</style>

+ 23
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_network.htm View File

@@ -0,0 +1,23 @@
1
+<ul class="cbi-tabmenu">
2
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
3
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
4
+	<li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
5
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
6
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
7
+</ul>
8
+
9
+<style type="text/css">
10
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
11
+	max-width: none;
12
+	margin: 0px 0px 0px 30px;
13
+	padding-right: 30px;
14
+	width: auto;
15
+  }
16
+  .cbi-section-node {
17
+	margin-top: 20px;
18
+  }
19
+  .cbi-section {
20
+	border: 1px dotted #555555;
21
+	padding: 20px;
22
+  }
23
+</style>

+ 77
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_adv_troubleshoot.htm View File

@@ -0,0 +1,77 @@
1
+<%+header%>
2
+
3
+<ul class="cbi-tabmenu">
4
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/hotplug")%>"><%:Hotplug Script%></a></li>
5
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/mwan3")%>"><%:MWAN3 Config%></a></li>
6
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/network")%>"><%:Network Config%></a></li>
7
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/diag")%>"><%:Diagnostics%></a></li>
8
+	<li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/advanced/tshoot")%>"><%:Troubleshooting%></a></li>
9
+</ul>
10
+
11
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
12
+<script type="text/javascript">//<![CDATA[
13
+	XHR.poll(15, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "advanced", "tshoot_display")%>', null,
14
+		function(x, st)
15
+		{
16
+			var tx = document.getElementById('mwan3_tshoot_text');
17
+			if (st.mw3ver)
18
+			{
19
+				var temp = '';
20
+				var mwanvers = 'Software versions : <br /><br />';
21
+				var mwan3cnfg = '<br /><br />Output of &#34;cat /etc/config/mwan3&#34; : <br /><br />';
22
+				var netcnfg = '<br /><br />Output of &#34;cat /etc/config/network&#34; : <br /><br />';
23
+				var ifcnfg = '<br /><br />Output of &#34;ifconfig&#34; : <br /><br />';
24
+				var iproute = '<br /><br />Output of &#34;route -n&#34; : <br /><br />';
25
+				var iprulesh = '<br /><br />Output of &#34;ip rule show&#34; : <br /><br />';
26
+				var routelisttbl = '<br /><br />Output of &#34;ip route list table 1-250&#34; : <br /><br />';
27
+				var firewalldef = '<br /><br />Firewall default output policy (must be ACCEPT) : <br /><br />';
28
+				var iptable = '<br /><br />Output of &#34;iptables -L -t mangle -v -n&#34; : <br /><br />';
29
+
30
+				temp = String.format(
31
+					'<pre><span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s<span class="tsht">%s</span>%s</pre>',
32
+						mwanvers, st.mw3ver[0].mwan3v, mwan3cnfg, st.mwan3config[0].mwn3cfg, netcnfg, st.netconfig[0].netcfg, ifcnfg, st.ifconfig[0].ifcfg, iproute, st.rtshow[0].iprtshow, iprulesh, st.iprule[0].rule, routelisttbl, st.routelist[0].iprtlist, firewalldef, st.fidef[0].firedef, iptable, st.iptables[0].iptbls
33
+				);
34
+				tx.innerHTML = temp;
35
+			}
36
+			else
37
+			{
38
+				var temp = '';
39
+				var terror = 'Error collecting troubleshooting information';
40
+				temp = String.format(
41
+					'<strong>%s</strong>',
42
+						terror
43
+				);
44
+				tx.innerHTML = temp;
45
+			}
46
+		}
47
+	);
48
+//]]></script>
49
+
50
+<div id="tshoot_div">
51
+	<fieldset class="cbi-section">
52
+		<legend><%:Troubleshooting Data%></legend>
53
+		<div id="mwan3_tshoot_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
54
+	</fieldset>
55
+</div>
56
+<style type="text/css">
57
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
58
+	max-width: none;
59
+	margin-left: 30px;
60
+	padding-right: 30px;
61
+	width: auto;
62
+  }
63
+  #tshoot_div {
64
+	background-color: #FFFFFF;
65
+	border: 1px dotted #555555;
66
+	padding: 20px;
67
+  }
68
+  #mwan3_tshoot_text {
69
+	padding: 20px;
70
+	text-align: left;
71
+  }
72
+  .tsht {
73
+	background-color: rgb(78, 186, 241);
74
+  }
75
+</style>
76
+
77
+<%+footer%>

+ 34
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_config_css.htm View File

@@ -0,0 +1,34 @@
1
+<style type="text/css">
2
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
3
+	max-width: none;
4
+	margin-left: 30px;
5
+	padding-right: 30px;
6
+	width: auto;
7
+  }
8
+  table td {  /*cells showing the configuration values*/
9
+	padding: 0px;
10
+	text-align: center;
11
+	vertical-align: middle;
12
+  }
13
+  table th {  /*column for configuration section name*/
14
+	padding: 0px;
15
+	text-align: center;
16
+	vertical-align: middle;
17
+  }
18
+  table tbody th {  /*column for configuration section name*/
19
+	padding: 0px;
20
+	vertical-align: middle;
21
+  }
22
+  .cbi-section-node table div {  /*rows*/
23
+	padding-top: 5px;
24
+  }
25
+  table.cbi-section-table td.cbi-section-table-cell {  /*sort buttons column*/
26
+	text-align: center;
27
+  }
28
+  .cbi-section h3 {
29
+	color: rgb(85, 85, 85);
30
+	font-family: Trebuchet MS,Verdana,sans-serif;
31
+	font-style: italic;
32
+	font-weight: normal;
33
+  }
34
+</style>

+ 62
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_detail.htm View File

@@ -0,0 +1,62 @@
1
+<%+header%>
2
+
3
+<ul class="cbi-tabmenu">
4
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview")%>"><%:Interface Status%></a></li>
5
+	<li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview/over_detail")%>"><%:Detailed Status%></a></li>
6
+</ul>
7
+
8
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
9
+<script type="text/javascript">//<![CDATA[
10
+	XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "overview", "detail_status")%>', null,
11
+		function(x, st)
12
+		{
13
+			var tx = document.getElementById('mwan3_detail_text');
14
+			if (st.mwan3dst)
15
+			{
16
+				var temp = '';
17
+				temp = String.format(
18
+					'<pre>%s</pre>',
19
+						st.mwan3dst[0].detailstat
20
+				);
21
+				tx.innerHTML = temp;
22
+			}
23
+			else
24
+			{
25
+				var temp = '';
26
+				var nslg = 'No detailed status information available';
27
+				temp = String.format(
28
+					'<strong>%s</strong>',
29
+						nslg
30
+				);
31
+				tx.innerHTML = temp;
32
+			}
33
+		}
34
+	);
35
+//]]></script>
36
+
37
+<div id="mwan3_detail_status">
38
+	<fieldset class="cbi-section">
39
+		<legend><%:MWAN3 Multi-WAN Detailed Status%></legend>
40
+		<div id="mwan3_detail_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
41
+	</fieldset>
42
+</div>
43
+
44
+<style type="text/css">
45
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
46
+	max-width: none;
47
+	margin-left: 30px;
48
+	padding-right: 30px;
49
+	width: auto;
50
+  }
51
+  #mwan3_detail_status {
52
+	border: 1px dotted #555555;
53
+	background-color: #FFFFFF;
54
+	padding: 20px;
55
+  }
56
+  #mwan3_detail_text {
57
+	padding: 20px;
58
+	text-align: left;
59
+  }
60
+</style>
61
+
62
+<%+footer%>

+ 146
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_over_interface.htm View File

@@ -0,0 +1,146 @@
1
+<%+header%>
2
+
3
+<ul class="cbi-tabmenu">
4
+	<li class="cbi-tab"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview")%>"><%:Interface Status%></a></li>
5
+	<li class="cbi-tab-disabled"><a href="<%=luci.dispatcher.build_url("admin/network/mwan3/overview/over_detail")%>"><%:Detailed Status%></a></li>
6
+</ul>
7
+
8
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
9
+<script type="text/javascript">//<![CDATA[
10
+	XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "overview", "iface_status")%>', null,
11
+		function(x, st)
12
+		{
13
+			var tx = document.getElementById('mwan3_status_text');
14
+			if (st.wans)
15
+			{
16
+				var temp = '';
17
+
18
+				for( var i = 0; i < st.wans.length; i++ )
19
+				{
20
+					var stat = '';
21
+					var cssc = '';
22
+
23
+					switch (st.wans[i].status)
24
+					{
25
+						case 'on':
26
+							stat = 'Online (tracking active)';
27
+							cssc = 'wanon';
28
+							break;
29
+
30
+						case 'nm':
31
+							stat = 'Online (tracking off)';
32
+							cssc = 'wanon';
33
+							break;
34
+
35
+						case 'off':
36
+							stat = 'Offline';
37
+							cssc = 'wanoff';
38
+							break;
39
+
40
+						case 'ne':
41
+							stat = 'Disabled';
42
+							cssc = 'wanoff';
43
+							break;
44
+					}
45
+
46
+					temp += String.format(
47
+						'<span class="%s"><strong>%s (<a href="%q">%s</a>)</strong><br />%s</span>',
48
+							cssc, st.wans[i].name, st.wans[i].link, st.wans[i].ifname, stat
49
+					);
50
+				}
51
+				tx.innerHTML = temp;
52
+			}
53
+			else
54
+			{
55
+				var temp = '';
56
+				var ncint = 'No MWAN3 interfaces found';
57
+				temp = String.format(
58
+					'<strong>%s</strong>',
59
+						ncint
60
+				);
61
+				tx.innerHTML = temp;
62
+			}
63
+
64
+			var tx = document.getElementById('mwan3_statuslog_text');
65
+			if (st.mwan3log)
66
+			{
67
+				var temp = '';
68
+				var mwan3lg = 'Last 50 MWAN3 systemlog entries. Newest entries sorted at the top :';
69
+
70
+				temp = String.format(
71
+					'<pre>%s<br /><br />%s</pre>',
72
+						mwan3lg, st.mwan3log[0].mwanlog
73
+				);
74
+				tx.innerHTML = temp;
75
+			}
76
+			else
77
+			{
78
+				var temp = '';
79
+				var nslg = 'No MWAN3 systemlog history found';
80
+				temp = String.format(
81
+					'<strong>%s</strong>',
82
+						nslg
83
+				);
84
+				tx.innerHTML = temp;
85
+			}
86
+		}
87
+	);
88
+//]]></script>
89
+
90
+<div id="mwan3_interface_status">
91
+	<fieldset id="interface_field" class="cbi-section">
92
+		<legend><%:MWAN3 Multi-WAN Interface Live Status%></legend>
93
+		<div id="mwan3_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
94
+	</fieldset>
95
+	<fieldset class="cbi-section">
96
+		<legend><%:MWAN3 Multi-WAN Interface Systemlog%></legend>
97
+		<div id="mwan3_statuslog_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
98
+	</fieldset>
99
+</div>
100
+
101
+<style type="text/css">
102
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
103
+	max-width: none;
104
+	margin-left: 30px;
105
+	padding-right: 30px;
106
+	width: auto;
107
+  }
108
+  #mwan3_interface_status {
109
+	background-color: #FFFFFF;
110
+	border: 1px dotted #555555;
111
+	padding: 20px;
112
+  }
113
+  #interface_field {
114
+	padding: 12px 20px 20px 20px;
115
+  }
116
+  #mwan3_status_text {
117
+	display: table;
118
+	font-size: 14px;
119
+	margin: auto;
120
+	max-width: 1044px;
121
+	min-width: 246px;
122
+	width: 100%;
123
+  }
124
+  .wanon {
125
+	background-color: rgb(144, 240, 144);
126
+  }
127
+  .wanoff {
128
+	background-color: rgb(240, 144, 144);
129
+  }
130
+  .wanon, .wanoff {
131
+	border-radius: 60px;
132
+	box-shadow: 0px 2px 5px -3px;
133
+	float: left;
134
+	margin: 8px 3px 0px 3px;
135
+	min-height: 30px;
136
+	min-width: 235px;
137
+	padding: 5px 10px 8px 10px;
138
+	text-align: center;
139
+  }
140
+  #mwan3_statuslog_text {
141
+	padding: 20px;
142
+	text-align: left;
143
+  }
144
+</style>
145
+
146
+<%+footer%>

+ 95
- 0
net/mwan3-luci/files/usr/lib/lua/luci/view/mwan3/mwan3_status.htm View File

@@ -0,0 +1,95 @@
1
+<script type="text/javascript">//<![CDATA[
2
+	XHR.poll(5, '<%=luci.dispatcher.build_url("admin", "network", "mwan3", "overview", "iface_status")%>', null,
3
+		function(x, st)
4
+		{
5
+			var tx = document.getElementById('mwan3_status_text');
6
+			if (st.wans)
7
+			{
8
+				var temp = '';
9
+
10
+				for( var i = 0; i < st.wans.length; i++ )
11
+				{
12
+					var stat = '';
13
+					var cssc = '';
14
+
15
+					switch (st.wans[i].status)
16
+					{
17
+						case 'on':
18
+							stat = 'Online (tracking active)';
19
+							cssc = 'wanon';
20
+							break;
21
+
22
+						case 'nm':
23
+							stat = 'Online (tracking off)';
24
+							cssc = 'wanon';
25
+							break;
26
+
27
+						case 'off':
28
+							stat = 'Offline';
29
+							cssc = 'wanoff';
30
+							break;
31
+
32
+						case 'ne':
33
+							stat = 'Disabled';
34
+							cssc = 'wanoff';
35
+							break;
36
+					}
37
+
38
+					temp += String.format(
39
+						'<span class="%s"><strong>%s (<a href="%q">%s</a>)</strong><br />%s</span>',
40
+							cssc, st.wans[i].name, st.wans[i].link, st.wans[i].ifname, stat
41
+					);
42
+				}
43
+				tx.innerHTML = temp;
44
+			}
45
+			else
46
+			{
47
+				var temp = '';
48
+				var ncint = 'No MWAN3 interfaces found';
49
+				temp += String.format(
50
+					'<strong>%s</strong>',
51
+						ncint
52
+				);
53
+				tx.innerHTML = temp;
54
+			}
55
+		}
56
+	);
57
+//]]></script>
58
+
59
+<fieldset id="interface_field" class="cbi-section">
60
+	<legend><%:MWAN3 Multi-WAN Interface Live Status%></legend>
61
+	<div id="mwan3_status_text"><img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> Collecting data...</div>
62
+</fieldset>
63
+
64
+<style type="text/css">
65
+  .container {  /*container for entire page. fixes bootstrap theme's ridiculously small page width*/
66
+	max-width: 1044px;
67
+  }
68
+  #interface_field {
69
+	padding: 12px 20px 20px 20px;
70
+  }
71
+  #mwan3_status_text {
72
+	display: table;
73
+	font-size: 14px;
74
+	margin: auto;
75
+	max-width: 1044px;
76
+	min-width: 246px;
77
+	width: 100%;
78
+  }
79
+  .wanon {
80
+	background-color: rgb(144, 240, 144);
81
+  }
82
+  .wanoff {
83
+	background-color: rgb(240, 144, 144);
84
+  }
85
+  .wanon, .wanoff {
86
+	border-radius: 60px;
87
+	box-shadow: 0px 2px 5px -3px;
88
+	float: left;
89
+	margin: 8px 3px 0px 3px;
90
+	min-height: 30px;
91
+	min-width: 235px;
92
+	padding: 5px 10px 8px 10px;
93
+	text-align: center;
94
+  }
95
+</style>