2 LuCI - Lua Configuration Interface
4 Copyright 2014 Steven Barth <steven@midlink.org>
5 Copyright 2014 Dave Taht <dave.taht@bufferbloat.net>
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
11 http://www.apache.org/licenses/LICENSE-2.0
16 local wa = require "luci.tools.webadmin"
17 local fs = require "nixio.fs"
18 local net = require "luci.model.network".init()
19 local sys = require "luci.sys"
20 --local ifaces = net:get_interfaces()
21 local ifaces = sys.net:devices()
22 local path = "/usr/lib/sqm"
23 local run_path = "/tmp/run/sqm/available_qdiscs"
25 m = Map("sqm", translate("Smart Queue Management"),
26 translate("With <abbr title=\"Smart Queue Management\">SQM</abbr> you " ..
27 "can enable traffic shaping, better mixing (Fair Queueing)," ..
28 " active queue length management (AQM) " ..
29 " and prioritisation on one " ..
30 "network interface."))
32 s = m:section(TypedSection, "queue", translate("Queues"))
33 s:tab("tab_basic", translate("Basic Settings"))
34 s:tab("tab_qdisc", translate("Queue Discipline"))
35 s:tab("tab_linklayer", translate("Link Layer Adaptation"))
36 s.addremove = true -- set to true to allow adding SQM instances in the GUI
40 e = s:taboption("tab_basic", Flag, "enabled", translate("Enable this SQM instance."))
43 -- sm: following jow's advise, be helpful to the user and enable
44 -- sqm's init script if even a single sm instance/interface
45 -- is enabled; this is unexpected in that the init script gets
46 -- enabled as soon as at least one sqm instance is enabled
47 -- and that state is saved, so it does not require "Save & Apply"
48 -- to effect the init scripts.
49 -- the implementation was inpired/lifted from
50 -- https://github.com/openwrt/luci/blob/master/applications/luci-app-minidlna/luasrc/model/cbi/minidlna.lua
51 function e.write(self, section, value)
53 luci.sys.init.enable("sqm")
54 m.message = translate("The SQM GUI has just enabled the sqm initscript on your behalf. Remember to disable the sqm initscript manually under System Startup menu in case this change was not wished for.")
56 return Flag.write(self, section, value)
58 -- TODO: inform the user what we just did...
61 -- Add to physical interface list a hint of the correpsonding network names,
62 -- used to help users better select e.g. lan or wan interface.
64 n = s:taboption("tab_basic", ListValue, "interface", translate("Interface name"))
65 -- sm lifted from luci-app-wol, the original implementation failed to show pppoe-ge00 type interface names
66 for _, iface in ipairs(ifaces) do
67 if not (iface == "lo" or iface:match("^ifb.*")) then
68 local nets = net:get_interface(iface)
69 nets = nets and nets:get_networks() or {}
70 for k, v in pairs(nets) do
73 nets = table.concat(nets, ",")
74 n:value(iface, ((#nets > 0) and "%s (%s)" % {iface, nets} or iface))
80 dl = s:taboption("tab_basic", Value, "download", translate("Download speed (kbit/s) (ingress) set to 0 to selectively disable ingress shaping:"))
81 dl.datatype = "and(uinteger,min(0))"
84 ul = s:taboption("tab_basic", Value, "upload", translate("Upload speed (kbit/s) (egress) set to 0 to selectively disable egress shaping:"))
85 ul.datatype = "and(uinteger,min(0))"
88 dbl = s:taboption("tab_basic", Flag, "debug_logging", translate("Create log file for this SQM instance under /var/run/sqm/${Interface_name}.[start|stop]-sqm.log."))
92 verb = s:taboption("tab_basic", ListValue, "verbosity", translate("Verbosity of SQM's output into the system log."))
93 verb:value("0", "silent")
94 verb:value("1", "error")
95 verb:value("2", "warning")
96 verb:value("5", "info ("..translate("default")..")")
97 verb:value("8", "debug")
98 verb:value("10", "trace")
107 local val_qdisc_name = ""
108 c = s:taboption("tab_qdisc", ListValue, "qdisc", translate("Queuing disciplines useable on this system. After installing a new qdisc, you need to restart the router to see updates!"))
109 c:value("fq_codel", "fq_codel ("..translate("default")..")")
111 if fs.stat(run_path) then
112 for file in fs.dir(run_path) do
116 c.default = "fq_codel"
122 sc = s:taboption("tab_qdisc", ListValue, "script", translate("Queue setup script"))
123 for file in fs.dir(path) do
124 if string.find(file, ".qos$") and not fs.stat(path .. "/" .. file .. ".hidden") then
126 qos_desc = qos_desc .. "<p><b>" .. file .. ":</b><br />"
127 fh = io.open(path .. "/" .. file .. ".help", "r")
129 qos_desc = qos_desc .. fh:read("*a") .. "</p>"
131 qos_desc = qos_desc .. "No help text</p>"
135 sc.default = "simple.qos"
137 sc.description = qos_desc
139 ad = s:taboption("tab_qdisc", Flag, "qdisc_advanced", translate("Show and Use Advanced Configuration. Advanced options will only be used as long as this box is checked."))
143 squash_dscp = s:taboption("tab_qdisc", ListValue, "squash_dscp", translate("Squash DSCP on inbound packets (ingress):"))
144 squash_dscp:value("1", "SQUASH")
145 squash_dscp:value("0", "DO NOT SQUASH")
146 squash_dscp.default = "1"
147 squash_dscp.rmempty = true
148 squash_dscp:depends("qdisc_advanced", "1")
150 squash_ingress = s:taboption("tab_qdisc", ListValue, "squash_ingress", translate("Ignore DSCP on ingress:"))
151 squash_ingress:value("1", "Ignore")
152 squash_ingress:value("0", "Allow")
153 squash_ingress.default = "1"
154 squash_ingress.rmempty = true
155 squash_ingress:depends("qdisc_advanced", "1")
157 iecn = s:taboption("tab_qdisc", ListValue, "ingress_ecn", translate("Explicit congestion notification (ECN) status on inbound packets (ingress):"))
158 iecn:value("ECN", "ECN ("..translate("default")..")")
162 iecn:depends("qdisc_advanced", "1")
164 eecn = s:taboption("tab_qdisc", ListValue, "egress_ecn", translate("Explicit congestion notification (ECN) status on outbound packets (egress)."))
165 eecn:value("NOECN", "NOECN ("..translate("default")..")")
167 eecn.default = "NOECN"
169 eecn:depends("qdisc_advanced", "1")
171 ad2 = s:taboption("tab_qdisc", Flag, "qdisc_really_really_advanced", translate("Show and Use Dangerous Configuration. Dangerous options will only be used as long as this box is checked."))
174 ad2:depends("qdisc_advanced", "1")
176 ilim = s:taboption("tab_qdisc", Value, "ilimit", translate("Hard limit on ingress queues; leave empty for default."))
177 -- ilim.default = 1000
179 ilim.datatype = "and(uinteger,min(0))"
181 ilim:depends("qdisc_really_really_advanced", "1")
183 elim = s:taboption("tab_qdisc", Value, "elimit", translate("Hard limit on egress queues; leave empty for default."))
184 -- elim.default = 1000
185 elim.datatype = "and(uinteger,min(0))"
187 elim:depends("qdisc_really_really_advanced", "1")
190 itarg = s:taboption("tab_qdisc", Value, "itarget", translate("Latency target for ingress, e.g 5ms [units: s, ms, or us]; leave empty for automatic selection, put in the word default for the qdisc's default."))
191 itarg.datatype = "string"
193 itarg:depends("qdisc_really_really_advanced", "1")
195 etarg = s:taboption("tab_qdisc", Value, "etarget", translate("Latency target for egress, e.g. 5ms [units: s, ms, or us]; leave empty for automatic selection, put in the word default for the qdisc's default."))
196 etarg.datatype = "string"
198 etarg:depends("qdisc_really_really_advanced", "1")
202 iqdisc_opts = s:taboption("tab_qdisc", Value, "iqdisc_opts", translate("Advanced option string to pass to the ingress queueing disciplines; no error checking, use very carefully."))
203 iqdisc_opts.rmempty = true
204 iqdisc_opts:depends("qdisc_really_really_advanced", "1")
206 eqdisc_opts = s:taboption("tab_qdisc", Value, "eqdisc_opts", translate("Advanced option string to pass to the egress queueing disciplines; no error checking, use very carefully."))
207 eqdisc_opts.rmempty = true
208 eqdisc_opts:depends("qdisc_really_really_advanced", "1")
211 ll = s:taboption("tab_linklayer", ListValue, "linklayer", translate("Which link layer to account for:"))
212 ll:value("none", "none ("..translate("default")..")")
213 ll:value("ethernet", "Ethernet with overhead: select for e.g. VDSL2.")
214 ll:value("atm", "ATM: select for e.g. ADSL1, ADSL2, ADSL2+.")
217 po = s:taboption("tab_linklayer", Value, "overhead", translate("Per Packet Overhead (byte):"))
218 po.datatype = "and(integer,min(-1500))"
222 po:depends("linklayer", "ethernet")
223 po:depends("linklayer", "atm")
226 adll = s:taboption("tab_linklayer", Flag, "linklayer_advanced", translate("Show Advanced Linklayer Options, (only needed if MTU > 1500). Advanced options will only be used as long as this box is checked."))
228 adll:depends("linklayer", "ethernet")
229 adll:depends("linklayer", "atm")
231 smtu = s:taboption("tab_linklayer", Value, "tcMTU", translate("Maximal Size for size and rate calculations, tcMTU (byte); needs to be >= interface MTU + overhead:"))
232 smtu.datatype = "and(uinteger,min(0))"
236 smtu:depends("linklayer_advanced", "1")
238 stsize = s:taboption("tab_linklayer", Value, "tcTSIZE", translate("Number of entries in size/rate tables, TSIZE; for ATM choose TSIZE = (tcMTU + 1) / 16:"))
239 stsize.datatype = "and(uinteger,min(0))"
241 stsize.isnumber = true
242 stsize.rmempty = true
243 stsize:depends("linklayer_advanced", "1")
245 smpu = s:taboption("tab_linklayer", Value, "tcMPU", translate("Minimal packet size, MPU (byte); needs to be > 0 for ethernet size tables:"))
246 smpu.datatype = "and(uinteger,min(0))"
250 smpu:depends("linklayer_advanced", "1")
252 lla = s:taboption("tab_linklayer", ListValue, "linklayer_adaptation_mechanism", translate("Which linklayer adaptation mechanism to use; for testing only"))
253 lla:value("default", "default ("..translate("default")..")")
255 lla:value("htb_private")
257 lla.default = "default"
259 lla:depends("linklayer_advanced", "1")