Add cjdns, sqm-scripts and adblock to core
authorRISCi_ATOM <bob@bobcall.me>
Tue, 12 Dec 2017 17:01:05 +0000 (12:01 -0500)
committerRISCi_ATOM <bob@bobcall.me>
Tue, 12 Dec 2017 17:01:05 +0000 (12:01 -0500)
38 files changed:
package/luci/applications/luci-app-cjdns/Makefile [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/controller/cjdns.lua [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/cjdrouteconf.lua [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/iptunnel.lua [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/overview.lua [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/peering.lua [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/settings.lua [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/view/admin_status/index/cjdns.htm [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/view/cjdns/status.htm [new file with mode: 0644]
package/luci/applications/luci-app-cjdns/luasrc/view/cjdns/value.htm [new file with mode: 0644]
package/network/config/adblock/Makefile [new file with mode: 0644]
package/network/config/adblock/files/README.md [new file with mode: 0644]
package/network/config/adblock/files/adblock.blacklist [new file with mode: 0644]
package/network/config/adblock/files/adblock.conf [new file with mode: 0644]
package/network/config/adblock/files/adblock.init [new file with mode: 0755]
package/network/config/adblock/files/adblock.sh [new file with mode: 0755]
package/network/config/adblock/files/adblock.whitelist [new file with mode: 0644]
package/network/config/sqm-scripts-extra/Makefile [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_LAN_dual-isolate__piece_of_cake.qos [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_LAN_dual-isolate__piece_of_cake.qos.help [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_LAN_triple-isolate__piece_of_cake.qos [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_LAN_triple-isolate__piece_of_cake.qos.help [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_WAN_dual-isolate__piece_of_cake.qos [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_WAN_dual-isolate__piece_of_cake.qos.help [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_WAN_triple-isolate__piece_of_cake.qos [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_WAN_triple-isolate__piece_of_cake.qos.help [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_triple_isolated_llt_cake.qos [new file with mode: 0644]
package/network/config/sqm-scripts-extra/src/test_triple_isolated_llt_cake.qos.help [new file with mode: 0644]
package/network/config/sqm-scripts/Makefile [new file with mode: 0644]
package/network/services/cjdns/Makefile [new file with mode: 0644]
package/network/services/cjdns/files/cjdns.defaults [new file with mode: 0644]
package/network/services/cjdns/files/cjdns.init [new file with mode: 0755]
package/network/services/cjdns/files/cjdrouteconf [new file with mode: 0755]
package/network/services/cjdns/lua/cjdns/admin.lua [new file with mode: 0644]
package/network/services/cjdns/lua/cjdns/common.lua [new file with mode: 0644]
package/network/services/cjdns/lua/cjdns/init.lua [new file with mode: 0644]
package/network/services/cjdns/lua/cjdns/uci.lua [new file with mode: 0644]
package/network/services/cjdns/lua/cjdns/udp.lua [new file with mode: 0644]

diff --git a/package/luci/applications/luci-app-cjdns/Makefile b/package/luci/applications/luci-app-cjdns/Makefile
new file mode 100644 (file)
index 0000000..793b4ec
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2014,2015 Hyperboria.net
+#
+# You may redistribute this program and/or modify it under the terms of
+# the GNU General Public License as published by the Free Software Foundation,
+# either version 3 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-cjdns
+PKG_VERSION:=1.3
+PKG_RELEASE:=5
+
+PKG_LICENSE:=GPL-3.0
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/luci-app-cjdns
+       SECTION:=luci
+       CATEGORY:=LuCI
+       SUBMENU:=3. Applications
+       TITLE:=Encrypted near-zero-conf mesh routing protocol
+       URL:=https://github.com/hyperboria/cjdns
+       MAINTAINER:=Lars Gierth <larsg@systemli.org>
+       DEPENDS:=+cjdns +luci-base
+endef
+
+define Package/luci-app-cjdns/description
+       This package allows you to configure and inspect cjdns networking using LuCI.
+
+       Cjdns implements an encrypted IPv6 network using public-key cryptography
+       for address allocation and a distributed hash table for routing.
+       This provides near-zero-configuration networking, and prevents many
+       of the security and scalability issues that plague existing networks.
+endef
+
+define Build/Compile
+endef
+
+define Package/luci-app-cjdns/install
+       $(INSTALL_DIR) $(1)/usr/lib/lua/luci
+       $(CP) ./luasrc/* $(1)/usr/lib/lua/luci
+endef
+
+$(eval $(call BuildPackage,luci-app-cjdns))
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/controller/cjdns.lua b/package/luci/applications/luci-app-cjdns/luasrc/controller/cjdns.lua
new file mode 100644 (file)
index 0000000..63644cb
--- /dev/null
@@ -0,0 +1,105 @@
+module("luci.controller.cjdns", package.seeall)
+
+cjdns  = require "cjdns/init"
+dkjson = require "dkjson"
+
+function index()
+       if not nixio.fs.access("/etc/config/cjdns") then
+               return
+       end
+
+       entry({"admin", "services", "cjdns"},
+               cbi("cjdns/overview"), _("cjdns")).dependent = true
+
+       entry({"admin", "services", "cjdns", "overview"},
+               cbi("cjdns/overview"), _("Overview"), 1).leaf = false
+
+       entry({"admin", "services", "cjdns", "peering"},
+               cbi("cjdns/peering"), _("Peers"), 2).leaf = false
+
+       entry({"admin", "services", "cjdns", "iptunnel"},
+               cbi("cjdns/iptunnel"), _("IP Tunnel"), 3).leaf = false
+
+       entry({"admin", "services", "cjdns", "settings"},
+               cbi("cjdns/settings"), _("Settings"), 4).leaf = false
+
+       entry({"admin", "services", "cjdns", "cjdrouteconf"},
+               cbi("cjdns/cjdrouteconf"), _("cjdroute.conf"), 5).leaf = false
+
+       entry({"admin", "services", "cjdns", "peers"}, call("act_peers")).leaf = true
+       entry({"admin", "services", "cjdns", "ping"}, call("act_ping")).leaf = true
+end
+
+function act_peers()
+       require("cjdns/uci")
+       admin = cjdns.uci.makeInterface()
+
+       local page = 0
+       local peers = {}
+
+       while page do
+               local response, err = admin:auth({
+                       q = "InterfaceController_peerStats",
+                       page = page
+               })
+
+               if err or response.error then
+                       luci.http.status(502, "Bad Gateway")
+                       luci.http.prepare_content("application/json")
+                       luci.http.write_json({ err = err, response = response })
+                       return
+               end
+
+               for i,peer in pairs(response.peers) do
+                       peer.ipv6 = publictoip6(peer.publicKey)
+                       if peer.user == nil then
+                               peer.user = ''
+                               uci.cursor():foreach("cjdns", "udp_peer", function(udp_peer)
+                                       if peer.publicKey == udp_peer.public_key then
+                                               peer.user = udp_peer.user
+                                       end
+                               end)
+                       end
+                       peers[#peers + 1] = peer
+               end
+
+               if response.more then
+                       page = page + 1
+               else
+                       page = nil
+               end
+       end
+
+       luci.http.status(200, "OK")
+       luci.http.prepare_content("application/json")
+       luci.http.write_json(peers)
+end
+
+function act_ping()
+       require("cjdns/uci")
+       admin = cjdns.uci.makeInterface()
+
+       local response, err = admin:auth({
+               q = "SwitchPinger_ping",
+               path = luci.http.formvalue("label"),
+               timeout = tonumber(luci.http.formvalue("timeout"))
+       })
+
+       if err or response.error then
+               luci.http.status(502, "Bad Gateway")
+               luci.http.prepare_content("application/json")
+               luci.http.write_json({ err = err, response = response })
+               return
+       end
+
+       luci.http.status(200, "OK")
+       luci.http.prepare_content("application/json")
+       luci.http.write_json(response)
+end
+
+function publictoip6(publicKey)
+       local process = io.popen("/usr/bin/publictoip6 " .. publicKey, "r")
+       local ipv6    = process:read()
+       process:close()
+       return ipv6
+end
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/cjdrouteconf.lua b/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/cjdrouteconf.lua
new file mode 100644 (file)
index 0000000..00e9ae0
--- /dev/null
@@ -0,0 +1,32 @@
+m = Map("cjdns", translate("cjdns"),
+  translate("Implements an encrypted IPv6 network using public-key \
+    cryptography for address allocation and a distributed hash table for \
+    routing. This provides near-zero-configuration networking, and prevents \
+    many of the security and scalability issues that plague existing \
+    networks."))
+
+dkjson = require("dkjson")
+cjdns = require("cjdns")
+require("cjdns/uci")
+
+local f = SimpleForm("cjdrouteconf", translate("Edit cjdroute.conf"),
+       translate("JSON interface to what's /etc/cjdroute.conf on other systems. \
+    Will be parsed and written to UCI by <code>cjdrouteconf set</code>."))
+
+local o = f:field(Value, "_cjdrouteconf")
+o.template = "cbi/tvalue"
+o.rows = 25
+
+function o.cfgvalue(self, section)
+       return dkjson.encode(cjdns.uci.get(), { indent = true })
+end
+
+function o.write(self, section, value)
+  local obj, pos, err = dkjson.decode(value, 1, nil)
+
+  if obj then
+    cjdns.uci.set(obj)
+  end
+end
+
+return f
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/iptunnel.lua b/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/iptunnel.lua
new file mode 100644 (file)
index 0000000..02b37dd
--- /dev/null
@@ -0,0 +1,46 @@
+uci = require "luci.model.uci"
+cursor = uci:cursor_state()
+
+m = Map("cjdns", translate("cjdns"),
+  translate("Implements an encrypted IPv6 network using public-key \
+    cryptography for address allocation and a distributed hash table for \
+    routing. This provides near-zero-configuration networking, and prevents \
+    many of the security and scalability issues that plague existing \
+    networks."))
+
+m.on_after_commit = function(self)
+  os.execute("/etc/init.d/cjdns restart")
+end
+
+-- Outgoing
+outgoing = m:section(TypedSection, "iptunnel_outgoing", translate("Outgoing IP Tunnel Connections"),
+  translate("Enter the public keys of the nodes that will provide Internet access."))
+outgoing.anonymous = true
+outgoing.addremove = true
+outgoing.template  = "cbi/tblsection"
+
+outgoing:option(Value, "public_key", translate("Public Key")).size = 55
+
+-- Allowed
+allowed = m:section(TypedSection, "iptunnel_allowed", translate("Allowed IP Tunnel Connections"),
+  translate("Enter the public key of the node you will provide Internet access to, along with the \
+             IPv4 and/or IPv6 address you will assign them."))
+allowed.anonymous = true
+allowed.addremove = true
+
+public_key = allowed:option(Value, "public_key", translate("Public Key"))
+public_key.template = "cjdns/value"
+public_key.size = 55
+
+ipv4 = allowed:option(Value, "ipv4", translate("IPv4"))
+ipv4.template = "cjdns/value"
+ipv4.datatype = 'ipaddr'
+ipv4.size = 55
+
+ipv6 = allowed:option(Value, "ipv6", translate("IPv6"),
+  translate("IPv6 addresses should be entered <em>without</em> brackets here, e.g. <code>2001:123:ab::10</code>."))
+ipv6.template = "cjdns/value"
+ipv6.datatype = 'ip6addr'
+ipv6.size = 55
+
+return m
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/overview.lua b/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/overview.lua
new file mode 100644 (file)
index 0000000..efa3a03
--- /dev/null
@@ -0,0 +1,10 @@
+m = Map("cjdns", translate("cjdns"),
+  translate("Implements an encrypted IPv6 network using public-key \
+    cryptography for address allocation and a distributed hash table for \
+    routing. This provides near-zero-configuration networking, and prevents \
+    many of the security and scalability issues that plague existing \
+    networks."))
+
+m:section(SimpleSection).template  = "cjdns/status"
+
+return m
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/peering.lua b/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/peering.lua
new file mode 100644 (file)
index 0000000..2b1fc1b
--- /dev/null
@@ -0,0 +1,73 @@
+uci = require "luci.model.uci"
+cursor = uci:cursor_state()
+
+cjdns = require("cjdns")
+require("cjdns/uci")
+
+m = Map("cjdns", translate("cjdns"),
+  translate("Implements an encrypted IPv6 network using public-key \
+    cryptography for address allocation and a distributed hash table for \
+    routing. This provides near-zero-configuration networking, and prevents \
+    many of the security and scalability issues that plague existing \
+    networks."))
+
+m.on_after_commit = function(self)
+  os.execute("/etc/init.d/cjdns restart")
+end
+
+-- Authorized Passwords
+passwords = m:section(TypedSection, "password", translate("Authorized Passwords"),
+  translate("Anyone offering one of the these passwords will be allowed to peer with you on the existing UDP and Ethernet interfaces."))
+passwords.anonymous = true
+passwords.addremove = true
+passwords.template  = "cbi/tblsection"
+
+passwords:option(Value, "user", translate("User/Name"),
+  translate("Must be unique.")
+).default = "user-" .. cjdns.uci.random_string(6)
+passwords:option(Value, "contact", translate("Contact"), translate("Optional, for out-of-band communication."))
+passwords:option(Value, "password", translate("Password"),
+  translate("Hand out to your peer, in accordance with the peering best practices of the network.")
+).default = cjdns.uci.random_string(32)
+
+-- UDP Peers
+udp_peers = m:section(TypedSection, "udp_peer", translate("Outgoing UDP Peers"),
+  translate("For peering via public IP networks, the peer handed you their Public Key and IP address/port along with a password. IPv6 addresses should be entered with square brackets, like so: <code>[2001::1]</code>."))
+udp_peers.anonymous = true
+udp_peers.addremove = true
+udp_peers.template  = "cbi/tblsection"
+udp_peers:option(Value, "user", translate("User/Name")).datatype = "string"
+
+udp_interface = udp_peers:option(Value, "interface", translate("UDP interface"))
+local index = 1
+for i,section in pairs(cursor:get_all("cjdns")) do
+  if section[".type"] == "udp_interface" then
+    udp_interface:value(index, section.address .. ":" .. section.port)
+  end
+end
+udp_interface.default = 1
+udp_peers:option(Value, "address", translate("IP address"))
+udp_peers:option(Value, "port", translate("Port")).datatype = "portrange"
+udp_peers:option(Value, "public_key", translate("Public key"))
+udp_peers:option(Value, "password", translate("Password"))
+
+-- Ethernet Peers
+eth_peers = m:section(TypedSection, "eth_peer", translate("Outgoing Ethernet Peers"),
+  translate("For peering via local Ethernet networks, the peer handed you their Public Key and MAC address along with a password."))
+eth_peers.anonymous = true
+eth_peers.addremove = true
+eth_peers.template  = "cbi/tblsection"
+
+eth_interface = eth_peers:option(Value, "interface", translate("Ethernet interface"))
+local index = 1
+for i,section in pairs(cursor:get_all("cjdns")) do
+  if section[".type"] == "eth_interface" then
+    eth_interface:value(index, section.bind)
+  end
+end
+eth_interface.default = 1
+eth_peers:option(Value, "address", translate("MAC address")).datatype = "macaddr"
+eth_peers:option(Value, "public_key", translate("Public key"))
+eth_peers:option(Value, "password", translate("Password"))
+
+return m
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/settings.lua b/package/luci/applications/luci-app-cjdns/luasrc/model/cbi/cjdns/settings.lua
new file mode 100644 (file)
index 0000000..d188915
--- /dev/null
@@ -0,0 +1,67 @@
+m = Map("cjdns", translate("cjdns"),
+  translate("Implements an encrypted IPv6 network using public-key \
+    cryptography for address allocation and a distributed hash table for \
+    routing. This provides near-zero-configuration networking, and prevents \
+    many of the security and scalability issues that plague existing \
+    networks."))
+
+m.on_after_commit = function(self)
+  os.execute("/etc/init.d/cjdns restart")
+end
+
+s = m:section(NamedSection, "cjdns", nil, translate("Settings"))
+s.addremove = false
+
+-- Identity
+s:tab("identity", translate("Identity"))
+node6 = s:taboption("identity", Value, "ipv6", translate("IPv6 address"),
+      translate("This node's IPv6 address within the cjdns network."))
+node6.datatype = "ip6addr"
+pbkey = s:taboption("identity", Value, "public_key", translate("Public key"),
+      translate("Used for packet encryption and authentication."))
+pbkey.datatype = "string"
+prkey = s:taboption("identity", Value, "private_key", translate("Private key"),
+      translate("Keep this private. When compromised, generate a new keypair and IPv6."))
+prkey.datatype = "string"
+
+-- Admin Interface
+s:tab("admin", translate("Admin API"), translate("The Admin API can be used by other applications or services to configure and inspect cjdns' routing and peering.<br/><br/>Documentation: <a href=\"https://github.com/cjdelisle/cjdns/tree/master/admin#cjdns-admin-api\">admin/README.md</a>"))
+aip = s:taboption("admin", Value, "admin_address", translate("IP Address"),
+      translate("IPv6 addresses should be entered like so: <code>[2001::1]</code>."))
+apt = s:taboption("admin", Value, "admin_port", translate("Port"))
+apt.datatype = "port"
+apw = s:taboption("admin", Value, "admin_password", translate("Password"))
+apw.datatype = "string"
+
+-- Security
+s:tab("security", translate("Security"), translate("Functionality related to hardening the cjdroute process."))
+s:taboption("security", Flag, "seccomp", translate("SecComp sandboxing"))
+
+-- UDP Interfaces
+udp_interfaces = m:section(TypedSection, "udp_interface", translate("UDP Interfaces"),
+  translate("These interfaces allow peering via public IP networks, such as the Internet, or many community-operated wireless networks. IPv6 addresses should be entered with square brackets, like so: <code>[2001::1]</code>."))
+udp_interfaces.anonymous = true
+udp_interfaces.addremove = true
+udp_interfaces.template = "cbi/tblsection"
+
+udp_address = udp_interfaces:option(Value, "address", translate("IP Address"))
+udp_address.placeholder = "0.0.0.0"
+udp_interfaces:option(Value, "port", translate("Port")).datatype = "portrange"
+
+-- Ethernet Interfaces
+eth_interfaces = m:section(TypedSection, "eth_interface", translate("Ethernet Interfaces"),
+  translate("These interfaces allow peering via local Ethernet networks, such as home or office networks, or phone tethering. If an interface name is set to \"all\" each available device will be used."))
+eth_interfaces.anonymous = true
+eth_interfaces.addremove = true
+eth_interfaces.template = "cbi/tblsection"
+
+eth_bind = eth_interfaces:option(Value, "bind", translate("Network Interface"))
+eth_bind.placeholder = "br-lan"
+eth_beacon = eth_interfaces:option(Value, "beacon", translate("Beacon Mode"))
+eth_beacon:value(0, translate("0 -- Disabled"))
+eth_beacon:value(1, translate("1 -- Accept beacons"))
+eth_beacon:value(2, translate("2 -- Accept and send beacons"))
+eth_beacon.default = 2
+eth_beacon.datatype = "integer(range(0,2))"
+
+return m
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/view/admin_status/index/cjdns.htm b/package/luci/applications/luci-app-cjdns/luasrc/view/admin_status/index/cjdns.htm
new file mode 100644 (file)
index 0000000..58c3843
--- /dev/null
@@ -0,0 +1 @@
+<%+cjdns/status%>
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/view/cjdns/status.htm b/package/luci/applications/luci-app-cjdns/luasrc/view/cjdns/status.htm
new file mode 100644 (file)
index 0000000..9d43e85
--- /dev/null
@@ -0,0 +1,116 @@
+<script type="text/javascript">//<![CDATA[
+
+       var peersURI = '<%=luci.dispatcher.build_url("admin", "services", "cjdns", "peers")%>';
+       var updatePeers = function(x, peers) {
+               var table = document.getElementById('cjdns-peerings');
+               while (table.rows.length > 1) {
+                       table.deleteRow(1);
+               }
+
+               if ((peers) && ((peers.err) || (typeof peers.length === 'undefined'))) {
+                       var errpeer = (peers.err)
+                                               ? 'Socket Error: unable to connect to Admin API'
+                                               : 'No active peers';
+                       var row = table.insertRow(-1);
+                       row.className = 'cbi-section-table-row';
+                       var cell = row.insertCell(-1);
+                       cell.colSpan = 7;
+                       cell.textContent = errpeer;
+                       return;
+               };
+
+               peers.forEach(function(peer, i) {
+                       if (peer.user == null) {
+                               var user = '';
+                       } else if (peer.user == 'Local Peers') {
+                               var user = 'beacon';
+                       } else {
+                               var user = peer.user;
+                       }
+
+                       if (peer.isIncoming === 0) {
+                               var interface = 'outgoing';
+                       } else {
+                               var interface = 'incoming';
+                       }
+
+                       var status = interface + ', ' + peer.state.toLowerCase();
+
+                       if (peer.version === 0) {
+                               var version = '-';
+                       } else {
+                               var version = 'v' + peer.version;
+                       }
+
+                       var rxtx = lbbytes(peer.bytesIn) + ' / ' + lbbytes(peer.bytesOut);
+
+                       var row = table.insertRow(-1);
+                       row.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
+                       row.insertCell(-1).textContent = user;
+                       row.insertCell(-1).textContent = peer.ipv6;
+                       row.insertCell(-1).textContent = status;
+                       row.insertCell(-1).textContent = version;
+                       row.insertCell(-1).textContent = rxtx;
+                       var latencyCell = row.insertCell(-1);
+                       latencyCell.textContent = 'waiting';
+
+                       var pingURI = '<%=luci.dispatcher.build_url("admin", "services", "cjdns", "ping")%>';
+                       var timeout = 2000;
+                       XHR.get(pingURI, { label: peer.switchLabel, timeout: timeout }, function(x, pong) {
+                               var pongrsp = ((pong.err == "ai:recv > timeout") || (pong == "undefined") || (pong.ms >= timeout))
+                                       ? '> ' + timeout + ' ms'
+                                       : pong.ms + ' ms';
+                               latencyCell.textContent = pongrsp;
+                       })
+               });
+
+       };
+
+       XHR.get(peersURI, null, updatePeers);
+       XHR.poll(5, peersURI, null, updatePeers);
+
+//]]></script>
+
+<script type="text/javascript">
+<%# Author: [GitHub/75lb] -%>
+//<![CDATA[
+function lbbytes (bytes){
+
+       var kilobyte = 1024,
+           megabyte = kilobyte * 1024,
+           gigabyte = megabyte * 1024,
+           terabyte = gigabyte * 1024;
+
+       if ((bytes >= 0) && (bytes < kilobyte)) {
+               return bytes + " B";
+       } else if ((bytes >= kilobyte) && (bytes < megabyte)) {
+               return (bytes / kilobyte).toFixed(2) + " KB";
+       } else if ((bytes >= megabyte) && (bytes < gigabyte)) {
+               return (bytes / megabyte).toFixed(2) + " MB";
+       } else if ((bytes >= gigabyte) && (bytes < terabyte)) {
+               return (bytes / gigabyte).toFixed(2) + " GB";
+       } else if (bytes >= terabyte) {
+               return (bytes / terabyte).toFixed(2) + " TB";
+       } else {
+               return bytes + " B";
+       }
+};
+//]]>
+</script>
+
+<fieldset class="cbi-section">
+       <legend>Active cjdns peers</legend>
+       <table class="cbi-section-table" id="cjdns-peerings">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell">User/Name</th>
+                       <th class="cbi-section-table-cell">IPv6</th>
+                       <th class="cbi-section-table-cell">Status</th>
+                       <th class="cbi-section-table-cell">Version</th>
+                       <th class="cbi-section-table-cell">Rx / Tx</th>
+                       <th class="cbi-section-table-cell">Latency</th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="7">Querying Admin API</td>
+               </tr>
+       </table>
+</fieldset>
diff --git a/package/luci/applications/luci-app-cjdns/luasrc/view/cjdns/value.htm b/package/luci/applications/luci-app-cjdns/luasrc/view/cjdns/value.htm
new file mode 100644 (file)
index 0000000..d1e54bb
--- /dev/null
@@ -0,0 +1,35 @@
+<%+cbi/valueheader%>
+       <input type="<%=self.password and 'password" class="cbi-input-password' or 'text" class="cbi-input-text' %>" onchange="cbi_d_update(this.id)"<%=
+               attr("name", cbid) .. attr("id", cbid) .. attr("value", self:cfgvalue(section) or self.default) ..
+               ifattr(self.size, "size") .. ifattr(self.placeholder, "placeholder")
+       %> style="width: auto" />
+       <% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
+       <% if #self.keylist > 0 or self.datatype then -%>
+       <script type="text/javascript">//<![CDATA[
+               <% if #self.keylist > 0 then -%>
+               cbi_combobox_init('<%=cbid%>', {
+               <%-
+                       for i, k in ipairs(self.keylist) do
+               -%>
+                       <%-=string.format("%q", k) .. ":" .. string.format("%q", self.vallist[i])-%>
+                       <%-if i<#self.keylist then-%>,<%-end-%>
+               <%-
+                       end
+               -%>
+               }, '<%- if not self.rmempty and not self.optional then -%>
+                       <%-: -- Please choose -- -%>
+                       <%- elseif self.placeholder then -%>
+                       <%-= pcdata(self.placeholder) -%>
+               <%- end -%>', '
+               <%- if self.combobox_manual then -%>
+                       <%-=self.combobox_manual-%>
+               <%- else -%>
+                       <%-: -- custom -- -%>
+               <%- end -%>');
+               <%- end %>
+               <% if self.datatype then -%>
+               cbi_validate_field('<%=cbid%>', <%=tostring((self.optional or self.rmempty) == true)%>, '<%=self.datatype:gsub("'", "\\'")%>');
+               <%- end %>
+       //]]></script>
+       <% end -%>
+<%+cbi/valuefooter%>
diff --git a/package/network/config/adblock/Makefile b/package/network/config/adblock/Makefile
new file mode 100644 (file)
index 0000000..28a7fee
--- /dev/null
@@ -0,0 +1,60 @@
+#
+# Copyright (c) 2015-2017 Dirk Brenken (dev@brenken.org)
+# This is free software, licensed under the GNU General Public License v3.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=adblock
+PKG_VERSION:=3.1.1
+PKG_RELEASE:=1
+PKG_LICENSE:=GPL-3.0+
+PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/adblock
+       SECTION:=net
+       CATEGORY:=Network
+       TITLE:=Powerful adblock script to block ad/abuse domains
+       PKGARCH:=all
+endef
+
+define Package/adblock/description
+Powerful adblock script to block ad/abuse domains via dnsmasq, unbound or bind dns backend.
+The script supports many domain blacklist sites plus manual black- and whitelist overrides.
+Please see https://github.com/openwrt/packages/blob/master/net/adblock/files/README.md for further information.
+
+endef
+
+define Package/adblock/conffiles
+/etc/config/adblock
+/etc/adblock/adblock.whitelist
+/etc/adblock/adblock.blacklist
+endef
+
+define Build/Prepare
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/adblock/install
+       $(INSTALL_DIR) $(1)/usr/bin
+       $(INSTALL_BIN) ./files/adblock.sh $(1)/usr/bin/
+
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/adblock.init $(1)/etc/init.d/adblock
+
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_CONF) ./files/adblock.conf $(1)/etc/config/adblock
+
+       $(INSTALL_DIR) $(1)/etc/adblock
+       $(INSTALL_CONF) ./files/adblock.blacklist $(1)/etc/adblock/
+       $(INSTALL_CONF) ./files/adblock.whitelist $(1)/etc/adblock/
+endef
+
+$(eval $(call BuildPackage,adblock))
diff --git a/package/network/config/adblock/files/README.md b/package/network/config/adblock/files/README.md
new file mode 100644 (file)
index 0000000..6b373da
--- /dev/null
@@ -0,0 +1,283 @@
+# dns based ad/abuse domain blocking
+
+## Description
+A lot of people already use adblocker plugins within their desktop browsers, but what if you are using your (smart) phone, tablet, watch or any other wlan gadget...getting rid of annoying ads, trackers and other abuse sites (like facebook ;-) is simple: block them with your router. When the dns server on your router receives dns requests, you will sort out queries that ask for the resource records of ad servers and return a simple 'NXDOMAIN'. This is nothing but **N**on-e**X**istent Internet or Intranet domain name, if domain name is unable to resolved using the dns server, a condition called the 'NXDOMAIN' occurred.  
+
+## Main Features
+* support of the following domain blocklist sources (free for private usage, for commercial use please check their individual licenses):
+    * [adaway](https://adaway.org)
+    * => infrequent updates, approx. 400 entries (enabled by default)
+    * [adguard](https://adguard.com)
+    * => numerous updates on the same day, approx. 12.000 entries
+    * [bitcoin](https://github.com/hoshsadiq/adblock-nocoin-list)
+    * => infrequent updates, approx. 15 entries
+    * [blacklist]()
+    * => static local blacklist, located by default in '/etc/adblock/adblock.blacklist'
+    * [disconnect](https://disconnect.me)
+    * => numerous updates on the same day, approx. 6.500 entries (enabled by default)
+    * [dshield](http://dshield.org)
+    * => daily updates, approx. 4.500 entries
+    * [feodotracker](https://feodotracker.abuse.ch)
+    * => daily updates, approx. 0-10 entries
+    * [hphosts](https://hosts-file.net)
+    * => monthly updates, approx. 50.000 entries
+    * [malwaredomains](http://malwaredomains.com)
+    * => daily updates, approx. 16.000 entries
+    * [malwaredomainlist](http://www.malwaredomainlist.com)
+    * => daily updates, approx. 1.500 entries
+    * [openphish](https://openphish.com)
+    * => numerous updates on the same day, approx. 1.800 entries
+    * [ransomware tracker](https://ransomwaretracker.abuse.ch)
+    * => daily updates, approx. 150 entries
+    * [reg_cn](https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt)
+    * => regional blocklist for China, daily updates, approx. 1.600 entries
+    * [reg_de](https://easylist-downloads.adblockplus.org/easylistgermany+easylist.txt)
+    * => regional blocklist for Germany, daily updates, approx. 9.200 entries
+    * [reg_id](https://easylist-downloads.adblockplus.org/abpindo+easylist.txt)
+    * => regional blocklist for Indonesia, daily updates, approx. 800 entries
+    * [reg_nl](https://easylist-downloads.adblockplus.org/easylistdutch+easylist.txt)
+    * => regional blocklist for the Netherlands, weekly updates, approx. 1300 entries
+    * [reg_pl](http://adblocklist.org)
+    * => regional blocklist for Poland, daily updates, approx. 50 entries
+    * [reg_ro](https://easylist-downloads.adblockplus.org/rolist+easylist.txt)
+    * => regional blocklist for Romania, weekly updates, approx. 600 entries
+    * [reg_ru](https://code.google.com/p/ruadlist)
+    * => regional blocklist for Russia, weekly updates, approx. 2.000 entries
+    * [securemecca](http://www.securemecca.com)
+    * => infrequent updates, approx. 25.000 entries
+    * [shallalist](http://www.shallalist.de) (categories "adv" "costtraps" "spyware" "tracker" "warez" enabled by default)
+    * => daily updates, approx. 32.000 entries (a short description of all shallalist categories can be found [online](http://www.shallalist.de/categories.html))
+    * [spam404](http://www.spam404.com)
+    * => infrequent updates, approx. 5.000 entries
+    * [sysctl/cameleon](http://sysctl.org/cameleon)
+    * => weekly updates, approx. 21.000 entries
+    * [whocares](http://someonewhocares.org)
+    * => weekly updates, approx. 12.000 entries
+    * [winhelp](http://winhelp2002.mvps.org)
+    * => infrequent updates, approx. 15.000 entries
+    * [winspy](https://github.com/crazy-max/WindowsSpyBlocker)
+    * => infrequent updates, approx. 120 entries
+    * [yoyo](http://pgl.yoyo.org/adservers)
+    * => weekly updates, approx. 2.500 entries (enabled by default)
+    * [zeus tracker](https://zeustracker.abuse.ch)
+    * => daily updates, approx. 440 entries
+* zero-conf like automatic installation & setup, usually no manual changes needed
+* simple but yet powerful adblock engine: adblock does not use error prone external iptables rulesets, http pixel server instances and things like that
+* supports five different dns backends / blocklist formats: dnsmasq, unbound, named (bind), kresd and dnscrypt-proxy
+* automatically selects uclient-fetch or wget as download utility (other tools like curl or aria2c are supported as well)
+* provides 'http only' mode without installed ssl library for all non-SSL blocklist sources
+* supports a wide range of router modes, even AP modes are supported
+* full IPv4 and IPv6 support
+* provides top level domain compression ('tld compression'), this feature removes thousands of needless host entries from the blocklist and lowers the memory footprint for the dns backends
+* blocklist source parsing by fast & flexible regex rulesets
+* overall duplicate removal in central blocklist 'adb_list.overall'
+* additional whitelist for manual overrides, located by default in /etc/adblock/adblock.whitelist
+* quality checks during blocklist update to ensure a reliable dns backend service
+* minimal status & error logging to syslog, enable debug logging to receive more output
+* procd based init system support (start/stop/restart/reload/suspend/resume/query/status)
+* procd network interface trigger support or classic time based startup
+* conditional dns backend restarts by old/new blocklist comparison with sha256sum (default) or md5sum
+* suspend & resume adblock actions temporarily without blocklist reloading
+* output comprehensive runtime information via LuCI or via 'status' init command
+* query function to quickly identify blocked (sub-)domains, e.g. for whitelisting
+* strong LuCI support
+* optional: force dns requests to local resolver
+* optional: force overall sort / duplicate removal for low memory devices (handle with care!)
+* optional: automatic blocklist backup & restore, they will be used in case of download errors or during startup in backup mode
+* optional: 'backup mode' to re-use blocklist backups during startup, get fresh lists only via reload or restart action
+* optional: 'whitelist mode' to block access to all domains except those explicitly listed in the whitelist file
+* optional: add new adblock sources on your own via uci config
+
+## Prerequisites
+* [LEDE project](https://www.lede-project.org), tested with latest stable release (LEDE 17.01) and with current LEDE snapshot
+* a usual setup with an enabled dns backend at minimum - dump AP modes without a working dns backend are _not_ supported
+* a download utility:
+    * to support all blocklist sources a full version (with ssl support) of 'wget', 'uclient-fetch' with one of the 'libustream-*' ssl libraries, 'aria2c' or 'curl' is required
+    * for limited devices with real memory constraints, adblock provides also a 'http only' option and supports wget-nossl and uclient-fetch (without libustream-ssl) as well
+    * for more configuration options see examples below
+
+## Installation & Usage
+* install 'adblock' (_opkg install adblock_)
+* at minimum configure the appropriate dns backend ('dnsmasq' by default) and enable the adblock service in _/etc/config/adblock_
+* control the adblock service manually with _/etc/init.d/adblock_ start/stop/restart/reload/suspend/resume/status or use the LuCI frontend
+
+## LuCI adblock companion package
+* for easy management of the various blocklist sources and all other adblock options you should use the provided LuCI frontend
+* install 'luci-app-adblock' (_opkg install luci-app-adblock_)
+* the application is located in LuCI under 'Services' menu
+
+## Tweaks
+* **runtime information:** the adblock status is available via _/etc/init.d/adblock status_ (see example below)
+* **debug logging:** for script debugging please set the config option 'adb\_debug' to '1' and check the runtime output with _logread -e "adblock"_
+* **storage expansion:** to process and store all blocklist sources at once it might helpful to enlarge your temp directory with a swap partition => see [openwrt wiki](https://wiki.openwrt.org/doc/uci/fstab) for further details
+* **add white- / blacklist entries:** add domain white- or blacklist entries to always-allow or -deny certain (sub) domains, by default both lists are empty and located in _/etc/adblock_. Please add one domain per line - ip addresses, wildcards & regex are _not_ allowed (see example below)
+* **backup & restore blocklists:** enable this feature, to restore automatically the latest compressed backup of your blocklists in case of any processing error (e.g. a single blocklist source is not available during update). Please use an (external) solid partition and _not_ your volatile router temp directory for this
+* **scheduled list updates:** for a scheduled call of the adblock service add an appropriate crontab entry (see example below)
+* **change startup behaviour:** by default the startup will be triggered by the 'wan' procd interface trigger. Choose 'none' to disable automatic startups, 'timed' to use a classic timeout (default 30 sec.) or select another trigger interface.
+* **suspend & resume adblocking:** to quickly switch the adblock service 'on' or 'off', simply use _/etc/init.d/adblock [suspend|resume]_
+* **domain query:** to query the active blocklist for a specific domain, please run _/etc/init.d/adblock query `<DOMAIN>`_ (see example below)
+* **add new list sources:** you could add new blocklist sources on your own via uci config, all you need is a source url and an awk one-liner (see example below)
+* **disable active dns probing in windows 10:** to prevent a yellow exclamation mark on your internet connection icon (which wrongly means connected, but no internet), please change the following registry key/value from "1" to "0" _HKLM\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet\EnableActiveProbing_
+
+## Further adblock config options
+* usually the pre-configured adblock setup works quite well and no manual config overrides are needed, all listed options apply to the 'global' config section:
+    * adb\_enabled => main switch to enable/disable adblock service (default: '0', disabled)
+    * adb\_debug => enable/disable adblock debug output (default: '0', disabled)
+    * adb\_dns => select the dns backend for your environment: 'dnsmasq', 'unbound', 'named', 'kresd' or 'dnscrypt-proxy' (default: 'dnsmasq')
+    * adb\_dnsdir => target directory for the generated blocklist 'adb_list.overall' (default: not set, use dns backend default)
+    * adb\_trigger => set the startup trigger to a certain interface, to 'timed' or to 'none' (default: 'wan')
+    * adb\_triggerdelay => additional trigger delay in seconds before adblock processing begins (default: '1')
+    * adb\_fetch => full path to a dedicated download utility, see example below (default: not set, use wget default)
+    * adb\_fetchparm => options for the download utility, see example below (default: not set, use wget default options)
+    * adb\_forcedns => force dns requests to local resolver (default: '0', disabled)
+    * adb\_forcesrt => force overall sort on low memory devices with less than 64 MB RAM (default: '0', disabled)
+    * adb\_backup_mode => do not automatically update blocklists during startup, use backups instead (default: '0', disabled)
+    * adb\_whitelist_mode => block access to all domains except those explicitly listed in the whitelist file (default: '0', disabled)
+
+## Examples
+**change default dns backend to 'unbound':**  
+
+Adblock deposits the final blocklist 'adb_list.overall' in '/var/lib/unbound' where unbound can find them in its jail. If you use manual configuration for unbound, then just include the following line in your 'server' clause:
+<pre><code>
+  include: "/var/lib/unbound/adb_list.overall"
+</code></pre>
+  
+**change default dns backend to 'named' (bind):**  
+
+Adblock deposits the final blocklist 'adb_list.overall' in '/var/lib/bind'. To use the blocklist please modify '/etc/bind/named.conf':
+<pre><code>
+in the 'options' namespace add:
+  response-policy { zone "rpz"; };
+
+and at the end of the file add:
+  zone "rpz" {
+    type master;
+    file "/var/lib/bind/adb_list.overall";
+    allow-query { none; };
+    allow-transfer { none; };
+  };
+</code></pre>
+  
+**change default dns backend to 'kresd':**  
+
+The knot-resolver (kresd) is only available on turris omnia devices. Adblock deposits the final blocklist 'adb_list.overall' in '/etc/kresd'. To use the blocklist please modify '/etc/config/resolver':
+<pre><code>
+    list rpz_file '/etc/kresd/adb_list.overall'
+</code></pre>
+  
+**change default dns backend to 'dnscrypt-proxy':**  
+
+The required 'blacklist' option of dnscrypt-proxy is not enabled by default, because the package will be compiled without plugins support. Take a custom LEDE build with plugins support to use this feature. Adblock deposits the final blocklist 'adb_list.overall' in '/tmp'. To use the blocklist please modify '/etc/config/dnscrypt-proxy' per instance:
+<pre><code>
+  list blacklist 'domains:/tmp/adb_list.overall'
+</code></pre>
+  
+**configuration for different download utilities:**
+<pre><code>
+wget (default):
+  option adb_fetch '/usr/bin/wget'
+  option adb_fetchparm '--quiet --no-cache --no-cookies --max-redirect=0 --timeout=10 --no-check-certificate -O'
+
+aria2c:
+  option adb_fetch '/usr/bin/aria2c'
+  option adb_fetchparm '-q --timeout=10 --allow-overwrite=true --auto-file-renaming=false --check-certificate=false -o'
+
+uclient-fetch:
+  option adb_fetch '/bin/uclient-fetch'
+  option adb_fetchparm '-q --timeout=10 --no-check-certificate -O'
+
+curl:
+  option adb_fetch '/usr/bin/curl'
+  option adb_fetchparm '-s --connect-timeout 10 --insecure -o'
+</code></pre>
+  
+**receive adblock runtime information:**
+<pre><code>
+/etc/init.d/adblock status
+::: adblock runtime information
+  + adblock_status  : enabled
+  + adblock_version : 3.1.0
+  + overall_domains : 5117
+  + fetch_utility   : wget (built-in)
+  + dns_backend     : kresd (/etc/kresd)
+  + last_rundate    : 03.11.2017 22:57:41
+  + system_release  : Turris Omnia, OpenWrt omnia 15.05/3.8.4
+</code></pre>
+  
+**cronjob for a regular block list update (/etc/crontabs/root):**
+<pre><code>
+0 06 * * *    /etc/init.d/adblock reload
+</code></pre>
+  
+**blacklist entry (/etc/adblock/adblock.blacklist):**
+<pre><code>
+ads.example.com
+
+This entry blocks the following (sub)domains:
+  http://ads.example.com/foo.gif
+  http://server1.ads.example.com/foo.gif
+  https://ads.example.com:8000/
+
+This entry does not block:
+  http://ads.example.com.ua/foo.gif
+  http://example.com/
+</code></pre>
+  
+**whitelist entry (/etc/adblock/adblock.whitelist):**
+<pre><code>
+here.com
+
+This entry removes the following (sub)domains from the blocklist:
+  maps.here.com
+  here.com
+
+This entry does not remove:
+  where.com
+  www.adwhere.com
+</code></pre>
+  
+**query the active blocklist for a certain (sub-)domain, e.g. for whitelisting:**  
+
+The query function checks against the submitted (sub-)domain and recurses automatically to the upper top level domain. For every (sub-)domain it returns the first ten relevant results.
+<pre><code>
+/etc/init.d/adblock query www.example.google.com
+::: max. ten results for domain 'www.example.google.com'
+  - no match
+::: max. ten results for domain 'example.google.com'
+  - no match
+::: max. ten results for domain 'google.com'
+  + analytics.google.com
+  + googleadapis.l.google.com
+  + pagead.l.google.com
+  + partnerad.l.google.com
+  + ssl-google-analytics.l.google.com
+  + video-stats.video.google.com
+  + www-google-analytics.l.google.com
+</code></pre>
+  
+**add a new blocklist source:**  
+
+1. the easy way ...  
+example: https://easylist-downloads.adblockplus.org/rolist+easylist.txt  
+Adblock already supports an easylist source, called 'reg_ru'. To add the additional local easylist as a new source, copy the existing config source section and change only
+the source name, the url and the description - that's all!
+<pre><code>
+config source 'reg_ro'
+  option enabled '0'
+  option adb_src 'https://easylist-downloads.adblockplus.org/rolist+easylist.txt'
+  option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+  option adb_src_desc 'focus on romanian ads plus generic easylist additions, weekly updates, approx. 9.400 entries'
+</code></pre>
+
+2. a bit harder ...  
+To add a really new source with different domain/host format you have to write a suitable awk one-liner on your own, so basic awk skills are needed. As a starting point check the already existing awk rulesets 'adb_src_rset' in the config file, probably you need only small changes for your individual list. Download the desired list and test your new awk string locally. The output result should be a sequential list with one domain/host per line - nothing more. If your awk one-liner works quite well, add a new source section to the adblock config file and test the new source.  
+
+## Support
+Please join the adblock discussion in this [forum thread](https://forum.lede-project.org/t/adblock-2-x-support-thread/507) or contact me by mail <dev@brenken.org>  
+
+## Removal
+* stop all adblock related services with _/etc/init.d/adblock stop_
+* optional: remove the adblock package (_opkg remove adblock_)
+
+Have fun!  
+Dirk  
diff --git a/package/network/config/adblock/files/adblock.blacklist b/package/network/config/adblock/files/adblock.blacklist
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/package/network/config/adblock/files/adblock.conf b/package/network/config/adblock/files/adblock.conf
new file mode 100644 (file)
index 0000000..da2f6fe
--- /dev/null
@@ -0,0 +1,186 @@
+# adblock configuration, for further information
+# see 'https://github.com/openwrt/packages/blob/master/net/adblock/files/README.md'
+
+config adblock 'global'
+       option adb_enabled '0'
+       option adb_dns 'dnsmasq'
+       option adb_trigger 'wan'
+
+config adblock 'extra'
+       option adb_debug '0'
+       option adb_forcesrt '0'
+       option adb_forcedns '0'
+       option adb_backup '0'
+
+config source 'adaway'
+       option enabled '1'
+       option adb_src 'https://adaway.org/hosts.txt'
+       option adb_src_rset '\$0~/^127\.0\.0\.1[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'focus on mobile ads, infrequent updates, approx. 400 entries'
+
+config source 'adguard'
+       option enabled '0'
+       option adb_src 'https://filters.adtidy.org/windows/filters/15.txt'
+       option adb_src_rset 'BEGIN{FS=\"[/|^|\r]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+([\/\^\r]|$)/{print tolower(\$3)}'
+       option adb_src_desc 'combined adguard dns filter list, frequent updates, approx. 15.700 entries'
+
+config source 'bitcoin'
+       option enabled '0'
+       option adb_src 'https://raw.githubusercontent.com/hoshsadiq/adblock-nocoin-list/master/hosts.txt'
+       option adb_src_rset '\$0~/^0\.0\.0\.0[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'focus on malicious bitcoin mining sites, infrequent updates, approx. 20 entries'
+
+config source 'blacklist'
+       option enabled '0'
+       option adb_src '/etc/adblock/adblock.blacklist'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'static local domain blacklist, always deny these domains'
+
+config source 'disconnect'
+       option enabled '1'
+       option adb_src 'https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'mozilla driven blocklist, numerous updates on the same day, approx. 4.600 entries'
+
+config source 'dshield'
+       option enabled '0'
+       option adb_src 'https://www.dshield.org/feeds/suspiciousdomains_Low.txt'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'generic blocklist, daily updates, approx. 3.500 entries'
+
+config source 'feodo'
+       option enabled '0'
+       option adb_src 'https://feodotracker.abuse.ch/blocklist/?download=domainblocklist'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'focus on feodo botnet, daily updates, approx. 0-10 entries'
+
+config source 'hphosts'
+       option enabled '0'
+       option adb_src 'https://hosts-file.net/ad_servers.txt'
+       option adb_src_rset '\$0~/^127\.0\.0\.1[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'broad blocklist, monthly updates, approx. 19.200 entries'
+
+config source 'malware'
+       option enabled '0'
+       option adb_src 'https://mirror.cedia.org.ec/malwaredomains/justdomains'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'broad blocklist, daily updates, approx. 18.300 entries'
+
+config source 'malwarelist'
+       option enabled '0'
+       option adb_src 'http://www.malwaredomainlist.com/hostslist/hosts.txt'
+       option adb_src_rset '\$0~/^127\.0\.0\.1[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'focus on malware, daily updates, approx. 1.200 entries'
+
+config source 'openphish'
+       option enabled '0'
+       option adb_src 'https://openphish.com/feed.txt'
+       option adb_src_rset 'BEGIN{FS=\"/\"}\$0~/^http[s]?:\/\/([[:alnum:]_-]+\.){1,}[[:alpha:]]+(\/|$)/{print tolower(\$3)}'
+       option adb_src_desc 'focus on phishing, numerous updates on the same day, approx. 2.400 entries'
+
+config source 'ransomware'
+       option enabled '0'
+       option adb_src 'https://ransomwaretracker.abuse.ch/downloads/RW_DOMBL.txt'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|\r|$)/{print tolower(\$1)}'
+       option adb_src_desc 'focus on ransomware, numerous updates on the same day, approx. 1900 entries'
+
+config source 'reg_cn'
+       option enabled '0'
+       option adb_src 'https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt'
+       option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+       option adb_src_desc 'focus on chinese ads plus generic easylist additions, daily updates, approx. 11.700 entries'
+
+config source 'reg_de'
+       option enabled '0'
+       option adb_src 'https://easylist-downloads.adblockplus.org/easylistgermany+easylist.txt'
+       option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+       option adb_src_desc 'focus on german ads plus generic easylist additions, daily updates, approx. 9.200 entries'
+
+config source 'reg_id'
+       option enabled '0'
+       option adb_src 'https://easylist-downloads.adblockplus.org/abpindo+easylist.txt'
+       option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+       option adb_src_desc 'focus on indonesian ads plus generic easylist additions, weekly updates, approx. 9.600 entries'
+
+config source 'reg_nl'
+       option enabled '0'
+       option adb_src 'https://easylist-downloads.adblockplus.org/easylistdutch+easylist.txt'
+       option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+       option adb_src_desc 'focus on dutch ads plus generic easylist additions, weekly updates, approx. 9.400 entries'
+
+config source 'reg_pl'
+       option enabled '0'
+       option adb_src 'http://adblocklist.org/adblock-pxf-polish.txt'
+       option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+       option adb_src_desc 'focus on polish ads, daily updates, approx. 90 entries'
+
+config source 'reg_ro'
+       option enabled '0'
+       option adb_src 'https://easylist-downloads.adblockplus.org/rolist+easylist.txt'
+       option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+       option adb_src_desc 'focus on romanian ads plus generic easylist additions, weekly updates, approx. 9.400 entries'
+
+config source 'reg_ru'
+       option enabled '0'
+       option adb_src 'https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt'
+       option adb_src_rset 'BEGIN{FS=\"[|^]\"}\$0~/^\|\|([[:alnum:]_-]+\.){1,}[[:alpha:]]+\^("\\\$third-party")?$/{print tolower(\$3)}'
+       option adb_src_desc 'focus on russian ads plus generic easylist additions, weekly updates, approx. 14.500 entries'
+
+config source 'securemecca'
+       option enabled '0'
+       option adb_src 'http://securemecca.com/Downloads/hosts.txt'
+       option adb_src_rset '\$0~/^127\.0\.0\.1[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'broad blocklist, infrequent updates, approx. 13.700 entries'
+
+config source 'shalla'
+       option enabled '0'
+       option adb_src 'http://www.shallalist.de/Downloads/shallalist.tar.gz'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'broad blocklist subdivided in different categories, daily updates, approx. 31.700 entries'
+       list adb_src_cat 'adv'
+       list adb_src_cat 'costtraps'
+       list adb_src_cat 'spyware'
+       list adb_src_cat 'tracker'
+       list adb_src_cat 'warez'
+
+config source 'spam404'
+       option enabled '0'
+       option adb_src 'https://raw.githubusercontent.com/Dawsey21/Lists/master/main-blacklist.txt'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'generic blocklist, infrequent updates, approx. 6.000 entries'
+
+config source 'sysctl' 
+       option enabled '0'
+       option adb_src 'http://sysctl.org/cameleon/hosts'
+       option adb_src_rset '\$0~/^127\.0\.0\.1[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'broad blocklist, weekly updates, approx. 16.500 entries'
+
+config source 'whocares'
+       option enabled '0'
+       option adb_src 'http://someonewhocares.org/hosts/hosts'
+       option adb_src_rset '\$0~/^127\.0\.0\.1[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'broad blocklist, weekly updates, approx. 10.000 entries'
+
+config source 'winspy'
+       option enabled '0'
+       option adb_src 'https://raw.githubusercontent.com/crazy-max/WindowsSpyBlocker/master/data/hosts/win10/spy.txt'
+       option adb_src_rset '\$0~/^0\.0\.0\.0[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'focus on windows spy & telemetry domains, infrequent updates, approx. 300 entries'
+
+config source 'winhelp'
+       option enabled '0'
+       option adb_src 'http://winhelp2002.mvps.org/hosts.txt'
+       option adb_src_rset '\$0~/^0\.0\.0\.0[[:space:]]+([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$2)}'
+       option adb_src_desc 'broad blocklist, infrequent updates, approx. 13.000 entries'
+
+config source 'yoyo'
+       option enabled '1'
+       option adb_src 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=nohtml&showintro=0&mimetype=plaintext'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'focus on ad related domains, weekly updates, approx. 2.400 entries'
+
+config source 'zeus'
+       option enabled '0'
+       option adb_src 'https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist'
+       option adb_src_rset '\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}'
+       option adb_src_desc 'focus on zeus botnet, daily updates, approx. 400 entries'
diff --git a/package/network/config/adblock/files/adblock.init b/package/network/config/adblock/files/adblock.init
new file mode 100755 (executable)
index 0000000..d49c6ea
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh /etc/rc.common
+#
+
+START=30
+USE_PROCD=1
+
+EXTRA_COMMANDS="suspend resume query status"
+EXTRA_HELP="   suspend Suspend adblock processing
+       resume  Resume adblock processing
+       query   <DOMAIN> Query active blocklists for specific domains
+       status  Print runtime information"
+
+adb_init="/etc/init.d/adblock"
+adb_script="/usr/bin/adblock.sh"
+
+boot()
+{
+    adb_boot=1
+    rc_procd start_service
+}
+
+start_service()
+{
+    if [ $("${adb_init}" enabled; printf "%u" ${?}) -eq 0 ]
+    then
+        if [ -n "${adb_boot}" ]
+        then
+            local trigger="$(uci_get adblock.global.adb_trigger)"
+            if [ "${trigger}" != "timed" ]
+            then
+                return 0
+            fi
+        fi
+        procd_open_instance "adblock"
+        procd_set_param command "${adb_script}" "${@}"
+        procd_set_param stdout 1
+        procd_set_param stderr 1
+        procd_close_instance
+    fi
+}
+
+reload_service()
+{
+    rc_procd start_service reload
+}
+
+stop_service()
+{
+    rc_procd "${adb_script}" stop
+    rc_procd start_service
+}
+
+restart()
+{
+    rc_procd start_service restart
+}
+
+suspend()
+{
+    rc_procd "${adb_script}" suspend
+}
+
+resume()
+{
+    rc_procd "${adb_script}" resume
+}
+
+query()
+{
+    rc_procd "${adb_script}" query "${1}"
+}
+
+status()
+{
+    rc_procd "${adb_script}" status
+}
+
+service_triggers()
+{
+    local trigger="$(uci_get adblock.global.adb_trigger)"
+    local delay="$(uci_get adblock.global.adb_triggerdelay)"
+
+    if [ "${trigger}" != "none" ] && [ "${trigger}" != "timed" ]
+    then
+        PROCD_RELOAD_DELAY=$((${delay:=1} * 1000))
+        procd_add_interface_trigger "interface.*.up" "${trigger}" "${adb_init}" start
+    fi
+    procd_add_reload_trigger "adblock"
+}
diff --git a/package/network/config/adblock/files/adblock.sh b/package/network/config/adblock/files/adblock.sh
new file mode 100755 (executable)
index 0000000..209fc00
--- /dev/null
@@ -0,0 +1,789 @@
+#!/bin/sh
+# dns based ad/abuse domain blocking
+# written by Dirk Brenken (dev@brenken.org)
+
+# This is free software, licensed under the GNU General Public License v3.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# set initial defaults
+#
+LC_ALL=C
+PATH="/usr/sbin:/usr/bin:/sbin:/bin"
+adb_ver="3.1.1"
+adb_sysver="unknown"
+adb_enabled=0
+adb_debug=0
+adb_backup_mode=0
+adb_whitelist_mode=0
+adb_forcesrt=0
+adb_forcedns=0
+adb_triggerdelay=0
+adb_backup=0
+adb_backupdir="/mnt"
+adb_fetch="/usr/bin/wget"
+adb_fetchparm="--quiet --no-cache --no-cookies --max-redirect=0 --timeout=10 --no-check-certificate -O"
+adb_dns="dnsmasq"
+adb_dnsprefix="adb_list"
+adb_dnsfile="${adb_dnsprefix}.overall"
+adb_whitelist="/etc/adblock/adblock.whitelist"
+adb_rtfile="/tmp/adb_runtime.json"
+adb_hashsum="$(command -v sha256sum)"
+adb_action="${1:-"start"}"
+adb_cnt=0
+adb_rc=0
+
+# f_envload: load adblock environment
+#
+f_envload()
+{
+    local dns_up sys_call sys_desc sys_model sys_ver cnt=0
+
+    # get system information
+    #
+    sys_call="$(ubus -S call system board 2>/dev/null)"
+    if [ -n "${sys_call}" ]
+    then
+        sys_desc="$(printf '%s' "${sys_call}" | jsonfilter -e '@.release.description')"
+        sys_model="$(printf '%s' "${sys_call}" | jsonfilter -e '@.model')"
+        sys_ver="$(cat /etc/turris-version 2>/dev/null)"
+        if [ -n "${sys_ver}" ]
+        then
+            sys_desc="${sys_desc}/${sys_ver}"
+        fi
+        adb_sysver="${sys_model}, ${sys_desc}"
+    fi
+
+    # source in system libraries
+    #
+    if [ -r "/lib/functions.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]
+    then
+        . "/lib/functions.sh"
+        . "/usr/share/libubox/jshn.sh"
+    else
+        f_log "error" "system libraries not found"
+    fi
+
+    # parse 'global' and 'extra' section by callback
+    #
+    config_cb()
+    {
+        local type="${1}"
+        if [ "${type}" = "adblock" ]
+        then
+            option_cb()
+            {
+                local option="${1}"
+                local value="${2}"
+                eval "${option}=\"${value}\""
+            }
+        else
+            reset_cb
+        fi
+    }
+
+    # parse 'source' typed sections
+    #
+    parse_config()
+    {
+        local value opt section="${1}" options="enabled adb_src adb_src_rset adb_src_cat"
+        eval "adb_sources=\"${adb_sources} ${section}\""
+        for opt in ${options}
+        do
+            config_get value "${section}" "${opt}"
+            if [ -n "${value}" ]
+            then
+                eval "${opt}_${section}=\"${value}\""
+            fi
+        done
+    }
+
+    # load adblock config
+    #
+    config_load adblock
+    config_foreach parse_config source
+
+    # set/check dns backend environment
+    #
+    case "${adb_dns}" in
+        dnsmasq)
+            adb_dnsuser="${adb_dnsuser:-"dnsmasq"}"
+            adb_dnsdir="${adb_dnsdir:-"/tmp/dnsmasq.d"}"
+            adb_dnsformat="awk '{print \"local=/\"\$0\"/\"}'"
+            if [ ${adb_whitelist_mode} -eq 1 ]
+            then
+                adb_dnsformat="awk '{print \"local=/\"\$0\"/#\"}'"
+                adb_dnsblock="local=/#/"
+            fi
+            ;;
+        unbound)
+            adb_dnsuser="${adb_dnsuser:-"unbound"}"
+            adb_dnsdir="${adb_dnsdir:-"/var/lib/unbound"}"
+            adb_dnsformat="awk '{print \"local-zone: \042\"\$0\"\042 static\"}'"
+            if [ ${adb_whitelist_mode} -eq 1 ]
+            then
+                adb_dnsformat="awk '{print \"local-zone: \042\"\$0\"\042 transparent\"}'"
+                adb_dnsblock="local-zone: \".\" static"
+            fi
+            ;;
+        named)
+            adb_dnsuser="${adb_dnsuser:-"bind"}"
+            adb_dnsdir="${adb_dnsdir:-"/var/lib/bind"}"
+            adb_dnsheader="\$TTL 2h"$'\n'"@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)"$'\n'"  IN NS localhost."
+            adb_dnsformat="awk '{print \"\"\$0\" CNAME .\n*.\"\$0\" CNAME .\"}'"
+            if [ ${adb_whitelist_mode} -eq 1 ]
+            then
+                adb_dnsformat="awk '{print \"\"\$0\" CNAME rpz-passthru.\n*.\"\$0\" CNAME rpz-passthru.\"}'"
+                adb_dnsblock="* CNAME ."
+            fi
+            ;;
+        kresd)
+            adb_dnsuser="${adb_dnsuser:-"root"}"
+            adb_dnsdir="${adb_dnsdir:-"/etc/kresd"}"
+            adb_dnsheader="\$TTL 2h"$'\n'"@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)"$'\n'"  IN NS  localhost."
+            adb_dnsformat="awk '{print \"\"\$0\" CNAME .\n*.\"\$0\" CNAME .\"}'"
+            if [ ${adb_whitelist_mode} -eq 1 ]
+            then
+                adb_dnsformat="awk '{print \"\"\$0\" CNAME rpz-passthru.\n*.\"\$0\" CNAME rpz-passthru.\"}'"
+                adb_dnsblock="* CNAME ."
+            fi
+            ;;
+        dnscrypt-proxy)
+            adb_dnsuser="${adb_dnsuser:-"nobody"}"
+            adb_dnsdir="${adb_dnsdir:-"/tmp"}"
+            adb_dnsformat="awk '{print \$0}'"
+            ;;
+    esac
+
+    # check adblock status
+    #
+    if [ ${adb_enabled} -eq 0 ]
+    then
+        if [ -s "${adb_dnsdir}/${adb_dnsfile}" ]
+        then
+            f_rmdns
+            f_dnsrestart
+        fi
+        f_extconf
+        f_jsnupdate
+        f_log "info " "adblock is currently disabled, please set adb_enabled to '1' to use this service"
+        exit 0
+    fi
+
+    if [ -d "${adb_dnsdir}" ] && [ ! -f "${adb_dnsdir}/${adb_dnsfile}" ]
+    then
+        > "${adb_dnsdir}/${adb_dnsfile}"
+    fi
+
+    case "${adb_action}" in
+        start|restart|reload)
+            > "${adb_rtfile}"
+            if [ "${adb_action}" = "start" ] && [ "${adb_trigger}" = "timed" ]
+            then
+                sleep ${adb_triggerdelay}
+            fi
+        ;;
+    esac
+
+    while [ ${cnt} -le 30 ]
+    do
+        dns_up="$(ubus -S call service list "{\"name\":\"${adb_dns}\"}" 2>/dev/null | jsonfilter -l1 -e "@[\"${adb_dns}\"].instances.*.running" 2>/dev/null)"
+        if [ "${dns_up}" = "true" ]
+        then
+            break
+        fi
+        sleep 1
+        cnt=$((cnt+1))
+    done
+
+    if [ -z "${adb_dns}" ] || [ -z "${adb_dnsformat}" ] || [ ! -x "$(command -v ${adb_dns})" ] || [ ! -d "${adb_dnsdir}" ]
+    then
+        f_log "error" "'${adb_dns}' not running, DNS backend not found"
+    fi
+}
+
+# f_envcheck: check/set environment prerequisites
+#
+f_envcheck()
+{
+    local ssl_lib
+
+    # check external uci config files
+    #
+    f_extconf
+
+    # check fetch utility
+    #
+    ssl_lib="-"
+    if [ -x "${adb_fetch}" ]
+    then
+        if [ "$(readlink -fn "${adb_fetch}")" = "/usr/bin/wget-nossl" ]
+        then
+            adb_fetchparm="--quiet --no-cache --no-cookies --max-redirect=0 --timeout=10 -O"
+        elif [ "$(readlink -fn "${adb_fetch}")" = "/bin/busybox" ] ||
+            ([ "$(readlink -fn "/bin/wget")" = "/bin/busybox" ] && [ "$(readlink -fn "${adb_fetch}")" != "/usr/bin/wget" ])
+        then
+            adb_fetch="/bin/busybox"
+            adb_fetchparm="-q -O"
+        else
+            ssl_lib="built-in"
+        fi
+    fi
+    if [ ! -x "${adb_fetch}" ] && [ "$(readlink -fn "/bin/wget")" = "/bin/uclient-fetch" ]
+    then
+        adb_fetch="/bin/uclient-fetch"
+        if [ -f "/lib/libustream-ssl.so" ]
+        then
+            adb_fetchparm="-q --timeout=10 --no-check-certificate -O"
+            ssl_lib="libustream-ssl"
+        else
+            adb_fetchparm="-q --timeout=10 -O"
+        fi
+    fi
+    if [ ! -x "${adb_fetch}" ] || [ -z "${adb_fetch}" ] || [ -z "${adb_fetchparm}" ]
+    then
+        f_log "error" "no download utility found, please install 'uclient-fetch' with 'libustream-mbedtls' or the full 'wget' package"
+    fi
+    adb_fetchinfo="${adb_fetch##*/} (${ssl_lib})"
+
+    # check hashsum utility
+    #
+    if [ ! -x "${adb_hashsum}" ]
+    then
+        adb_hashsum="$(command -v md5sum)"
+    fi
+
+    # initialize temp files and directories
+    #
+    adb_tmpload="$(mktemp -tu)"
+    adb_tmpfile="$(mktemp -tu)"
+    adb_tmpdir="$(mktemp -p /tmp -d)"
+    > "${adb_tmpdir}/tmp.whitelist"
+}
+
+# f_extconf: set external config options
+#
+f_extconf()
+{
+    # kresd related options
+    #
+    if [ "${adb_dns}" = "kresd" ]
+    then
+        if [ ${adb_enabled} -eq 1 ] && [ -z "$(uci -q get resolver.kresd.rpz_file | grep -Fo "${adb_dnsdir}/${adb_dnsfile}")" ]
+        then
+            uci -q add_list resolver.kresd.rpz_file="${adb_dnsdir}/${adb_dnsfile}"
+        elif [ ${adb_enabled} -eq 0 ] && [ -n "$(uci -q get resolver.kresd.rpz_file | grep -Fo "${adb_dnsdir}/${adb_dnsfile}")" ]
+        then
+            uci -q del_list resolver.kresd.rpz_file="${adb_dnsdir}/${adb_dnsfile}"
+        fi
+        if [ -n "$(uci -q changes resolver)" ]
+        then
+            uci -q commit resolver
+        fi
+    fi
+
+    # firewall related options
+    #
+    if [ ${adb_enabled} -eq 1 ] && [ ${adb_forcedns} -eq 1 ] && [ -z "$(uci -q get firewall.adblock_dns)" ]
+    then
+        uci -q set firewall.adblock_dns="redirect"
+        uci -q set firewall.adblock_dns.name="Adblock DNS"
+        uci -q set firewall.adblock_dns.src="lan"
+        uci -q set firewall.adblock_dns.proto="tcp udp"
+        uci -q set firewall.adblock_dns.src_dport="53"
+        uci -q set firewall.adblock_dns.dest_port="53"
+        uci -q set firewall.adblock_dns.target="DNAT"
+    elif [ -n "$(uci -q get firewall.adblock_dns)" ] && ([ ${adb_enabled} -eq 0 ] || [ ${adb_forcedns} -eq 0 ])
+    then
+        uci -q delete firewall.adblock_dns
+    fi
+    if [ -n "$(uci -q changes firewall)" ]
+    then
+        uci -q commit firewall
+        if [ $(/etc/init.d/firewall enabled; printf "%u" ${?}) -eq 0 ]
+        then
+            /etc/init.d/firewall reload >/dev/null 2>&1
+        fi
+    fi
+}
+
+# f_rmtemp: remove temporary files & directories
+#
+f_rmtemp()
+{
+    if [ -d "${adb_tmpdir}" ]
+    then
+        rm -f "${adb_tmpload}"
+        rm -f "${adb_tmpfile}"
+        rm -rf "${adb_tmpdir}"
+    fi
+}
+
+# f_rmdns: remove dns related files & directories
+#
+f_rmdns()
+{
+    if [ -n "${adb_dns}" ]
+    then
+        > "${adb_dnsdir}/${adb_dnsfile}"
+        > "${adb_rtfile}"
+        rm -f "${adb_dnsdir}/.${adb_dnsfile}"
+        rm -f "${adb_backupdir}/${adb_dnsprefix}"*.gz
+    fi
+}
+
+# f_dnsrestart: restart the dns backend
+#
+f_dnsrestart()
+{
+    local dns_up cnt=0
+
+    "/etc/init.d/${adb_dns}" restart >/dev/null 2>&1
+    while [ ${cnt} -le 10 ]
+    do
+        dns_up="$(ubus -S call service list "{\"name\":\"${adb_dns}\"}" | jsonfilter -l1 -e "@[\"${adb_dns}\"].instances.*.running")"
+        if [ "${dns_up}" = "true" ]
+        then
+            return 0
+        fi
+        cnt=$((cnt+1))
+        sleep 1
+    done
+    return 1
+}
+
+# f_list: backup/restore/remove blocklists
+#
+f_list()
+{
+    local mode="${1}" in_rc="${adb_rc}" cnt=0
+
+    case "${mode}" in
+        backup)
+            cnt="$(wc -l < "${adb_tmpfile}")"
+            if [ ${adb_backup} -eq 1 ] && [ -d "${adb_backupdir}" ]
+            then
+                gzip -cf "${adb_tmpfile}" > "${adb_backupdir}/${adb_dnsprefix}.${src_name}.gz"
+                adb_rc=${?}
+            fi
+            ;;
+        restore)
+            if [ ${adb_backup} -eq 1 ] && [ -d "${adb_backupdir}" ] &&
+                [ -f "${adb_backupdir}/${adb_dnsprefix}.${src_name}.gz" ]
+            then
+                gunzip -cf "${adb_backupdir}/${adb_dnsprefix}.${src_name}.gz" > "${adb_tmpfile}"
+                adb_rc=${?}
+            fi
+            ;;
+        remove)
+            if [ -d "${adb_backupdir}" ]
+            then
+                rm -f "${adb_backupdir}/${adb_dnsprefix}.${src_name}.gz"
+            fi
+            adb_rc=${?}
+            ;;
+        merge)
+            if [ -s "${adb_tmpfile}" ]
+            then
+                cat "${adb_tmpfile}" >> "${adb_tmpdir}/${adb_dnsfile}"
+                adb_rc=${?}
+            fi
+            ;;
+        format)
+            if [ -s "${adb_tmpdir}/tmp.whitelist" ]
+            then
+                grep -vf "${adb_tmpdir}/tmp.whitelist" "${adb_tmpdir}/${adb_dnsfile}" | eval "${adb_dnsformat}" > "${adb_dnsdir}/${adb_dnsfile}"
+            else
+                eval "${adb_dnsformat}" "${adb_tmpdir}/${adb_dnsfile}" > "${adb_dnsdir}/${adb_dnsfile}"
+            fi
+            if [ -n "${adb_dnsheader}" ]
+            then
+                printf '%s\n' "${adb_dnsheader}" | cat - "${adb_dnsdir}/${adb_dnsfile}" > "${adb_tmpdir}/${adb_dnsfile}"
+                cat "${adb_tmpdir}/${adb_dnsfile}" > "${adb_dnsdir}/${adb_dnsfile}"
+            fi
+            adb_rc=${?}
+            ;;
+    esac
+    f_log "debug" "name: ${src_name}, mode: ${mode}, count: ${cnt}, in_rc: ${in_rc}, out_rc: ${adb_rc}"
+}
+
+# f_tldcompression: top level domain compression
+#
+f_tldcompression()
+{
+    local source="${1}" temp="${adb_tmpload}"
+
+    awk -F "." '{for(f=NF;f > 1;f--) printf "%s.", $f;print $1}' "${source}" 2>/dev/null | sort -u > "${temp}"
+    awk '{if(NR==1){tld=$NF};while(getline){if($NF !~ tld"\\."){print tld;tld=$NF}}print tld}' "${temp}" 2>/dev/null > "${source}"
+    awk -F "." '{for(f=NF;f > 1;f--) printf "%s.", $f;print $1}' "${source}" 2>/dev/null > "${temp}"
+    sort -u "${temp}" > "${source}"
+}
+
+# f_switch: suspend/resume adblock processing
+#
+f_switch()
+{
+    local source target status mode="${1}"
+
+    if [ -s "${adb_dnsdir}/${adb_dnsfile}" ] && [ "${mode}" = "suspend" ]
+    then
+        source="${adb_dnsdir}/${adb_dnsfile}"
+        target="${adb_dnsdir}/.${adb_dnsfile}"
+        status="suspended"
+    elif [ -s "${adb_dnsdir}/.${adb_dnsfile}" ] && [ "${mode}" = "resume" ]
+    then
+        source="${adb_dnsdir}/.${adb_dnsfile}"
+        target="${adb_dnsdir}/${adb_dnsfile}"
+        status="resumed"
+    fi
+    if [ -n "${status}" ]
+    then
+        cat "${source}" > "${target}"
+        > "${source}"
+        chown "${adb_dnsuser}" "${target}" 2>/dev/null
+        f_dnsrestart
+        f_jsnupdate
+        f_log "info " "adblock processing ${status}"
+    fi
+}
+
+# f_query: query blocklist for certain (sub-)domains
+#
+f_query()
+{
+    local search result cnt
+    local domain="${1}"
+    local tld="${domain#*.}"
+
+    if [ ! -s "${adb_dnsdir}/${adb_dnsfile}" ]
+    then
+         printf "%s\n" "::: no active blocklist found, please start / resume adblock first"
+    elif [ -z "${domain}" ] || [ "${domain}" = "${tld}" ]
+    then
+        printf "%s\n" "::: invalid domain input, please submit a single domain, e.g. 'doubleclick.net'"
+    else
+        cd "${adb_dnsdir}"
+        while [ "${domain}" != "${tld}" ]
+        do
+            search="${domain//./\.}"
+            if [ "${adb_dns}" = "dnsmasq" ] || [ "${adb_dns}" = "unbound" ]
+            then
+                result="$(awk -F '/|\"' "/[\/\"\.]${search}/{i++;{printf(\"  + %s\n\",\$2)};if(i>9){exit}}" "${adb_dnsfile}")"
+            else
+                result="$(awk "/(^[^\*][[:alpha:]]*[\.]+${search}|^${search})/{i++;{printf(\"  + %s\n\",\$1)};if(i>9){exit}}" "${adb_dnsfile}")"
+            fi
+            printf "%s\n" "::: max. ten results for domain '${domain}'"
+            printf "%s\n" "${result:-"  - no match"}"
+            domain="${tld}"
+            tld="${domain#*.}"
+        done
+    fi
+}
+
+# f_jsnupdate: update runtime information
+#
+f_jsnupdate()
+{
+    local status rundate="$(/bin/date "+%d.%m.%Y %H:%M:%S")"
+
+    if [ ${adb_rc} -gt 0 ]
+    then
+        status="error"
+    elif [ ${adb_enabled} -eq 0 ]
+    then
+        status="disabled"
+    elif [ -s "${adb_dnsdir}/.${adb_dnsfile}" ]
+    then
+        status="paused"
+    else
+        status="enabled"
+        if [ -s "${adb_dnsdir}/${adb_dnsfile}" ]
+        then
+            if [ "${adb_dns}" = "named" ] || [ "${adb_dns}" = "kresd" ]
+            then
+                adb_cnt="$(( ( $(wc -l < "${adb_dnsdir}/${adb_dnsfile}") - $(printf "%s" "${adb_dnsheader}" | grep -c "^") ) / 2 ))"
+            else
+                adb_cnt="$(wc -l < "${adb_dnsdir}/${adb_dnsfile}")"
+            fi
+        fi
+    fi
+
+    if [ -z "${adb_fetchinfo}" ] && [ -s "${adb_rtfile}" ]
+    then
+        json_load "$(cat "${adb_rtfile}" 2>/dev/null)"
+        json_select data
+        json_get_var adb_fetchinfo "fetch_utility"
+    fi
+
+    json_init
+    json_add_object "data"
+    json_add_string "adblock_status" "${status}"
+    json_add_string "adblock_version" "${adb_ver}"
+    json_add_string "overall_domains" "${adb_cnt}"
+    json_add_string "fetch_utility" "${adb_fetchinfo}"
+    json_add_string "dns_backend" "${adb_dns} (${adb_dnsdir})"
+    json_add_string "last_rundate" "${rundate}"
+    json_add_string "system_release" "${adb_sysver}"
+    json_close_object
+    json_dump > "${adb_rtfile}"
+}
+
+# f_status: output runtime information
+#
+f_status()
+{
+    local key keylist value
+
+    if [ -s "${adb_rtfile}" ]
+    then
+        printf "%s\n" "::: adblock runtime information"
+        json_load "$(cat "${adb_rtfile}" 2>/dev/null)"
+        json_select data
+        json_get_keys keylist
+        for key in ${keylist}
+        do
+            json_get_var value "${key}"
+            printf "  + %-15s : %s\n" "${key}" "${value}"
+        done
+    fi
+}
+
+# f_log: write to syslog, exit on error
+#
+f_log()
+{
+    local class="${1}" log_msg="${2}"
+
+    if [ -n "${log_msg}" ] && ([ "${class}" != "debug" ] || [ ${adb_debug} -eq 1 ])
+    then
+        logger -t "adblock-[${adb_ver}] ${class}" "${log_msg}"
+        if [ "${class}" = "error" ]
+        then
+            logger -t "adblock-[${adb_ver}] ${class}" "Please check 'https://github.com/openwrt/packages/blob/master/net/adblock/files/README.md' (${adb_sysver})"
+            f_rmtemp
+            if [ -s "${adb_dnsdir}/${adb_dnsfile}" ]
+            then
+                f_rmdns
+                f_dnsrestart
+            fi
+            adb_rc=1
+            f_jsnupdate
+            exit 1
+        fi
+    fi
+}
+
+# main function for blocklist processing
+#
+f_main()
+{
+    local src_name src_rset shalla_archive enabled url hash_old hash_new
+    local mem_total="$(awk '/^MemTotal/ {print int($2/1000)}' "/proc/meminfo")"
+
+    f_log "info " "start adblock processing ..."
+    f_log "debug" "action: ${adb_action}, dns: ${adb_dns}, fetch: ${adb_fetchinfo}, hashsum: ${adb_hashsum}, backup: ${adb_backup}, backup_mode: ${adb_backup_mode}, whitelist_mode: ${adb_whitelist_mode}, force_srt/_dns: ${adb_forcesrt}/${adb_forcedns}, mem_total: ${mem_total}"
+    > "${adb_rtfile}"
+    > "${adb_dnsdir}/.${adb_dnsfile}"
+
+    # prepare whitelist entries
+    #
+    if [ -s "${adb_whitelist}" ]
+    then
+        if [ ${adb_whitelist_mode} -eq 1 ] && [ "${adb_dns}" != "dnscrypt-proxy" ]
+        then
+            adb_whitelist_rset="\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{print tolower(\$1)}"
+        else
+            adb_whitelist_rset="\$0~/^([[:alnum:]_-]+\.){1,}[[:alpha:]]+([[:space:]]|$)/{gsub(\"\\\.\",\"\\\.\",\$1);print tolower(\"^\"\$1\"\\\|\\\.\"\$1)}"
+        fi
+        awk "${adb_whitelist_rset}" "${adb_whitelist}" > "${adb_tmpdir}/tmp.whitelist"
+    fi
+
+    # whitelist mode
+    #
+    if [ ${adb_whitelist_mode} -eq 1 ] && [ "${adb_dns}" != "dnscrypt-proxy" ]
+    then
+        f_tldcompression "${adb_tmpdir}/tmp.whitelist"
+        eval "${adb_dnsformat}" "${adb_tmpdir}/tmp.whitelist" > "${adb_dnsdir}/${adb_dnsfile}"
+        printf '%s\n' "${adb_dnsblock}" >> "${adb_dnsdir}/${adb_dnsfile}"
+        if [ -n "${adb_dnsheader}" ]
+        then
+            printf '%s\n' "${adb_dnsheader}" | cat - "${adb_dnsdir}/${adb_dnsfile}" > "${adb_tmpdir}/${adb_dnsfile}"
+            cat "${adb_tmpdir}/${adb_dnsfile}" > "${adb_dnsdir}/${adb_dnsfile}"
+        fi
+        f_dnsrestart
+        if [ ${?} -eq 0 ]
+        then
+            f_jsnupdate "${adb_cnt}"
+            f_log "info " "whitelist with overall ${adb_cnt} domains loaded successfully (${adb_sysver})"
+        else
+            f_log "error" "dns backend restart with active whitelist failed"
+        fi
+        return
+    fi
+
+    # normal & backup mode
+    #
+    for src_name in ${adb_sources}
+    do
+        eval "enabled=\"\${enabled_${src_name}}\""
+        eval "url=\"\${adb_src_${src_name}}\""
+        eval "src_rset=\"\${adb_src_rset_${src_name}}\""
+        > "${adb_tmpload}"
+        > "${adb_tmpfile}"
+        adb_rc=4
+
+        # basic pre-checks
+        #
+        f_log "debug" "name: ${src_name}, enabled: ${enabled}, url: ${url}, rset: ${src_rset}"
+        if [ "${enabled}" != "1" ] || [ -z "${url}" ] || [ -z "${src_rset}" ]
+        then
+            f_list remove
+            continue
+        fi
+
+        # backup mode
+        #
+        if [ ${adb_backup_mode} -eq 1 ] && [ "${adb_action}" = "start" ] && [ "${src_name}" != "blacklist" ]
+        then
+            f_list restore
+            if [ ${adb_rc} -eq 0 ] && [ -s "${adb_tmpfile}" ]
+            then
+                f_list merge
+                continue
+            fi
+        fi
+
+        # download blocklist
+        #
+        if [ "${src_name}" = "blacklist" ] && [ -s "${url}" ]
+        then
+            cat "${url}" > "${adb_tmpload}"
+            adb_rc=${?}
+        elif [ "${src_name}" = "shalla" ]
+        then
+            shalla_archive="${adb_tmpdir}/shallalist.tar.gz"
+            "${adb_fetch}" ${adb_fetchparm} "${shalla_archive}" "${url}" 2>/dev/null
+            adb_rc=${?}
+            if [ ${adb_rc} -eq 0 ]
+            then
+                for category in ${adb_src_cat_shalla}
+                do
+                    tar -xOzf "${shalla_archive}" "BL/${category}/domains" >> "${adb_tmpload}"
+                    adb_rc=${?}
+                    if [ ${adb_rc} -ne 0 ]
+                    then
+                        break
+                    fi
+                done
+            fi
+            rm -f "${shalla_archive}"
+            rm -rf "${adb_tmpdir}/BL"
+        else
+            "${adb_fetch}" ${adb_fetchparm} "${adb_tmpload}" "${url}" 2>/dev/null
+            adb_rc=${?}
+        fi
+
+        # check download result and prepare list output (incl. tld compression, list backup & restore)
+        #
+        if [ ${adb_rc} -eq 0 ] && [ -s "${adb_tmpload}" ]
+        then
+            awk "${src_rset}" "${adb_tmpload}" 2>/dev/null > "${adb_tmpfile}"
+            if [ -s "${adb_tmpfile}" ]
+            then
+                f_tldcompression "${adb_tmpfile}"
+                if [ "${src_name}" != "blacklist" ]
+                then
+                    f_list backup
+                fi
+            else
+                f_list restore
+            fi
+        else
+            f_list restore
+        fi
+
+        # list merge
+        #
+        if [ ${adb_rc} -eq 0 ] && [ -s "${adb_tmpfile}" ]
+        then
+            f_list merge
+            if [ ${adb_rc} -ne 0 ]
+            then
+                f_list remove
+            fi
+        else
+            f_list remove
+        fi
+    done
+
+    # hash preparation, whitelist removal and overall sort
+    #
+    if [ -x "${adb_hashsum}" ] && [ -f "${adb_dnsdir}/${adb_dnsfile}" ]
+    then
+        hash_old="$(${adb_hashsum} "${adb_dnsdir}/${adb_dnsfile}" 2>/dev/null | awk '{print $1}')"
+    fi
+    if [ -s "${adb_tmpdir}/${adb_dnsfile}" ]
+    then
+        if [ ${mem_total} -ge 64 ] || [ ${adb_forcesrt} -eq 1 ]
+        then
+            f_tldcompression "${adb_tmpdir}/${adb_dnsfile}"
+        fi
+        f_list format
+    else
+        > "${adb_dnsdir}/${adb_dnsfile}"
+    fi
+    chown "${adb_dnsuser}" "${adb_dnsdir}/${adb_dnsfile}" 2>/dev/null
+    f_rmtemp
+
+    # conditional restart of the dns backend and runtime information export
+    #
+    if [ -x "${adb_hashsum}" ] && [ -f "${adb_dnsdir}/${adb_dnsfile}" ]
+    then
+        hash_new="$(${adb_hashsum} "${adb_dnsdir}/${adb_dnsfile}" 2>/dev/null | awk '{print $1}')"
+    fi
+    if [ -z "${hash_old}" ] || [ -z "${hash_new}" ] || [ "${hash_old}" != "${hash_new}" ]
+    then
+        f_dnsrestart
+    fi
+    if [ ${?} -eq 0 ]
+    then
+        f_jsnupdate "${adb_cnt}"
+        f_log "info " "blocklist with overall ${adb_cnt} domains loaded successfully (${adb_sysver})"
+    else
+        f_log "error" "dns backend restart with active blocklist failed"
+    fi
+}
+
+# handle different adblock actions
+#
+f_envload
+case "${adb_action}" in
+    stop)
+        f_rmtemp
+        f_rmdns
+        f_dnsrestart
+        ;;
+    restart)
+        f_rmtemp
+        f_rmdns
+        f_envcheck
+        f_main
+        ;;
+    suspend)
+        f_switch suspend
+        ;;
+    resume)
+        f_switch resume
+        ;;
+    query)
+        f_query "${2}"
+        ;;
+    status)
+        f_status
+        ;;
+    *)
+        f_envcheck
+        f_main
+        ;;
+esac
+exit 0
diff --git a/package/network/config/adblock/files/adblock.whitelist b/package/network/config/adblock/files/adblock.whitelist
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/package/network/config/sqm-scripts-extra/Makefile b/package/network/config/sqm-scripts-extra/Makefile
new file mode 100644 (file)
index 0000000..28e6ccb
--- /dev/null
@@ -0,0 +1,47 @@
+#
+# This is free software, licensed under the GNU General Public License v2.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=sqm-scripts-extra
+PKG_VERSION:=2016-06-08
+PKG_RELEASE:=1
+PKG_LICENSE:=GPLv2
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE).tar.xz
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/sqm-scripts-extra
+  SECTION:=net
+  CATEGORY:=Base system
+  DEPENDS:=+sqm-scripts
+  TITLE:=SQM Scripts - additional experimental scripts for testing
+  PKGARCH:=all
+  MAINTAINER:=Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>, \
+       Sebastian Moeller <moeller0@gmx.de>
+endef
+
+define Package/sqm-scripts-extra/description
+  A set of experimental scripts for sqm-scripts QoS package. The contents of this package may
+  change as new qdiscs like 'cake' are developed and tested.
+endef
+
+define Package/sqm-scripts-extra/download
+endef
+
+define Build/Prepare
+endef
+
+define Build/Compile
+endef
+
+define Package/sqm-scripts-extra/install
+       $(INSTALL_DIR) $(1)/usr/lib/sqm
+       $(INSTALL_DATA) ./src/* $(1)/usr/lib/sqm/
+endef
+
+$(eval $(call BuildPackage,sqm-scripts-extra))
+
diff --git a/package/network/config/sqm-scripts-extra/src/test_LAN_dual-isolate__piece_of_cake.qos b/package/network/config/sqm-scripts-extra/src/test_LAN_dual-isolate__piece_of_cake.qos
new file mode 100644 (file)
index 0000000..4f1380d
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+################################################################################
+# test_LAN_triple-isolate__piece_of_cake.qos (Cero3 Shaper)
+#
+# Abstract:
+# This is a one band cake and ipv6 enabled shaping script for Ethernet
+# gateways. This exists for the purpose of testing cake's different isolation
+# options, specifically the non-directional triple-isolate feature that promises
+# to finally tackle the bit-torrent hole in simple.qos.
+# This specific version is supposed to be used on LAN interfaces:
+#      the script's ingress direction equals the internet/WAN upload direction
+#      the script's egress direction equals the internet/WAN download direction
+#
+# NOTE: Shaping on a LAN interface only affects machines connected via that
+#      LAN interface, so typically WIFI/WLAN traffic will not be shaped!
+#
+################################################################################
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#  Copyright (C) 2012-2016
+#    Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+#
+################################################################################
+
+. ${SQM_LIB_DIR}/defaults.sh
+
+################################################################################
+
+
+# this will only work with cake as qdisc...
+QDISC=cake
+
+
+# to keep this as simple as possible we ignore the *_CAKE_OPTS from defaults.sh
+INGRESS_CAKE_OPTS="besteffort dual-srchost"
+EGRESS_CAKE_OPTS="besteffort dual-dsthost"
+
+
+egress() {
+    sqm_debug "egress"
+    $TC qdisc del dev $IFACE root 2>/dev/null
+    $TC qdisc add dev $IFACE root $( get_stab_string ) cake bandwidth ${UPLINK}kbit $( get_cake_lla_string ) ${EGRESS_CAKE_OPTS} ${EQDISC_OPTS}
+}
+
+
+ingress() {
+    sqm_debug "ingress"
+    $TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null
+    $TC qdisc add dev $IFACE handle ffff: ingress
+    $TC qdisc del dev $DEV root 2>/dev/null
+    $TC qdisc add dev $DEV root $( get_stab_string ) cake bandwidth ${DOWNLINK}kbit $( get_cake_lla_string ) ${INGRESS_CAKE_OPTS} ${IQDISC_OPTS}
+
+    $IP link set dev $DEV up
+
+    # redirect all IP packets arriving in $IFACE to ifb0
+
+    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+       match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+}
+
+sqm_start() {
+
+    #sm: flip upload and download bandwith so that the GUI values reflect directionality in regard to the ISP
+    #sm: NOTE this is ugly and should be performed in defaults.sh or functions.sh if at all
+    #sm: but for quick and dirty testing this should do
+    local ORIG_UPLINK=${UPLINK}
+    local ORIG_DOWNLINK=${DOWNLINK}
+    UPLINK=${ORIG_DOWNLINK}
+    DOWNLINK=${ORIG_UPLINK}
+
+
+    [ -n "$IFACE" ] || return 1
+    do_modules
+    verify_qdisc $QDISC "cake" || return 1
+    sqm_debug "Starting ${SCRIPT}"
+
+    [ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} )
+
+
+    if [ "${UPLINK}" -ne 0 ];
+    then
+        egress
+        sqm_debug "egress shaping activated"
+    else
+        sqm_debug "egress shaping deactivated"
+        $TC qdisc del dev ${IFACE} root 2> /dev/null
+    fi
+    if [ "${DOWNLINK}" -ne 0 ];
+    then
+       verify_qdisc ingress "ingress" || return 1
+        ingress
+        sqm_debug "ingress shaping activated"
+    else
+        sqm_debug "ingress shaping deactivated"
+        $TC qdisc del dev ${DEV} root 2> /dev/null
+        $TC qdisc del dev ${IFACE} ingress 2> /dev/null
+    fi
+    return 0
+}
diff --git a/package/network/config/sqm-scripts-extra/src/test_LAN_dual-isolate__piece_of_cake.qos.help b/package/network/config/sqm-scripts-extra/src/test_LAN_dual-isolate__piece_of_cake.qos.help
new file mode 100644 (file)
index 0000000..616b5e4
--- /dev/null
@@ -0,0 +1,12 @@
+test_LAN_dual-isolate__piece_of_cake.qos (Cero3 Shaper)
+
+Abstract:
+This is a one band cake and ipv6 enabled shaping script for Ethernet gateways. 
+This exists for the purpose of testing cake's different isolation options, 
+specifically the non-directional dual-[src|dst]host feature that promises to finally 
+tackle the bit-torrent hole in simple.qos. 
+This specific version is supposed to be used on LAN interfaces: 
+ the script's ingress direction equals the internet/WAN upload direction 
+ the script's egress direction equals the internet/WAN download direction
+NOTE: Shaping on a LAN interface only affects machines connected via that 
+LAN interface, so typically WIFI/WLAN traffic will not be shaped!
diff --git a/package/network/config/sqm-scripts-extra/src/test_LAN_triple-isolate__piece_of_cake.qos b/package/network/config/sqm-scripts-extra/src/test_LAN_triple-isolate__piece_of_cake.qos
new file mode 100644 (file)
index 0000000..761b27d
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+################################################################################
+# test_LAN_triple-isolate__piece_of_cake.qos (Cero3 Shaper)
+#
+# Abstract:
+# This is a one band cake and ipv6 enabled shaping script for Ethernet
+# gateways. This exists for the purpose of testing cake's different isolation
+# options, specifically the non-directional triple-isolate feature that promises
+# to finally tackle the bit-torrent hole in simple.qos.
+# This specific version is supposed to be used on LAN interfaces:
+#      the script's ingress direction equals the internet/WAN upload direction
+#      the script's egress direction equals the internet/WAN download direction
+#
+# NOTE: Shaping on a LAN interface only affects machines connected via that
+#      LAN interface, so typically WIFI/WLAN traffic will not be shaped!
+#
+################################################################################
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#  Copyright (C) 2012-2016
+#    Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+#
+################################################################################
+
+. ${SQM_LIB_DIR}/defaults.sh
+
+################################################################################
+
+
+# this will only work with cake as qdisc...
+QDISC=cake
+
+
+# to keep this as simple as possible we ignore the *_CAKE_OPTS from defaults.sh
+INGRESS_CAKE_OPTS="besteffort triple-isolate"
+EGRESS_CAKE_OPTS="besteffort triple-isolate"
+
+
+egress() {
+    sqm_debug "egress"
+    $TC qdisc del dev $IFACE root 2>/dev/null
+    $TC qdisc add dev $IFACE root $( get_stab_string ) cake bandwidth ${UPLINK}kbit $( get_cake_lla_string ) ${EGRESS_CAKE_OPTS} ${EQDISC_OPTS}
+}
+
+
+ingress() {
+    sqm_debug "ingress"
+    $TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null
+    $TC qdisc add dev $IFACE handle ffff: ingress
+    $TC qdisc del dev $DEV root 2>/dev/null
+    $TC qdisc add dev $DEV root $( get_stab_string ) cake bandwidth ${DOWNLINK}kbit $( get_cake_lla_string ) ${INGRESS_CAKE_OPTS} ${IQDISC_OPTS}
+
+    $IP link set dev $DEV up
+
+    # redirect all IP packets arriving in $IFACE to ifb0
+
+    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+       match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+}
+
+sqm_start() {
+
+    #sm: flip upload and download bandwith so that the GUI values reflect directionality in regard to the ISP
+    #sm: NOTE this is ugly and should be performed in defaults.sh or functions.sh if at all
+    #sm: but for quick and dirty testing this should do
+    local ORIG_UPLINK=${UPLINK}
+    local ORIG_DOWNLINK=${DOWNLINK}
+    UPLINK=${ORIG_DOWNLINK}
+    DOWNLINK=${ORIG_UPLINK}
+
+
+    [ -n "$IFACE" ] || return 1
+    do_modules
+    verify_qdisc $QDISC "cake" || return 1
+    sqm_debug "Starting ${SCRIPT}"
+
+    [ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} )
+
+
+    if [ "${UPLINK}" -ne 0 ];
+    then
+        egress
+        sqm_debug "egress shaping activated"
+    else
+        sqm_debug "egress shaping deactivated"
+        $TC qdisc del dev ${IFACE} root 2> /dev/null
+    fi
+    if [ "${DOWNLINK}" -ne 0 ];
+    then
+       verify_qdisc ingress "ingress" || return 1
+        ingress
+        sqm_debug "ingress shaping activated"
+    else
+        sqm_debug "ingress shaping deactivated"
+        $TC qdisc del dev ${DEV} root 2> /dev/null
+        $TC qdisc del dev ${IFACE} ingress 2> /dev/null
+    fi
+    return 0
+}
diff --git a/package/network/config/sqm-scripts-extra/src/test_LAN_triple-isolate__piece_of_cake.qos.help b/package/network/config/sqm-scripts-extra/src/test_LAN_triple-isolate__piece_of_cake.qos.help
new file mode 100644 (file)
index 0000000..a3501dc
--- /dev/null
@@ -0,0 +1,12 @@
+test_LAN_triple-isolate__piece_of_cake.qos (Cero3 Shaper)
+
+Abstract:
+This is a one band cake and ipv6 enabled shaping script for Ethernet gateways. 
+This exists for the purpose of testing cake's different isolation options, 
+specifically the non-directional triple-isolate feature that promises to finally 
+tackle the bit-torrent hole in simple.qos. 
+This specific version is supposed to be used on LAN interfaces: 
+ the script's ingress direction equals the internet/WAN upload direction 
+ the script's egress direction equals the internet/WAN download direction
+NOTE: Shaping on a LAN interface only affects machines connected via that 
+LAN interface, so typically WIFI/WLAN traffic will not be shaped!
diff --git a/package/network/config/sqm-scripts-extra/src/test_WAN_dual-isolate__piece_of_cake.qos b/package/network/config/sqm-scripts-extra/src/test_WAN_dual-isolate__piece_of_cake.qos
new file mode 100644 (file)
index 0000000..87eaebf
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh
+################################################################################
+# test_WAN_dual-isolate__piece_of_cake.qos (Cero3 Shaper)
+#
+# Abstract:
+# This is a one band cake and ipv6 enabled shaping script for Ethernet
+# gateways. This exists for the purpose of testing cake's different isolation
+# options, specifically the directional dual-[src|dst]host feature that promises
+# to finally tackle the bit-torrent hole in simple.qos.
+# This specific version is supposed to be used on WAN interfaces:
+#      the script's ingress direction equals the internet/WAN download direction
+#      the script's egress direction equals the internet/WAN upload direction
+#
+################################################################################
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#  Copyright (C) 2012-2016
+#    Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+#
+################################################################################
+
+. ${SQM_LIB_DIR}/defaults.sh
+
+################################################################################
+
+
+# this will only work with cake as qdisc...
+QDISC=cake
+
+
+# to keep this as simple as possible we ignore the *_CAKE_OPTS from defaults.sh
+INGRESS_CAKE_OPTS="besteffort dual-dsthost"
+EGRESS_CAKE_OPTS="besteffort dual-srchost"
+
+
+egress() {
+    sqm_debug "egress"
+    $TC qdisc del dev $IFACE root 2>/dev/null
+    $TC qdisc add dev $IFACE root $( get_stab_string ) cake bandwidth ${UPLINK}kbit $( get_cake_lla_string ) ${EGRESS_CAKE_OPTS} ${EQDISC_OPTS}
+}
+
+
+ingress() {
+    sqm_debug "ingress"
+    $TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null
+    $TC qdisc add dev $IFACE handle ffff: ingress
+    $TC qdisc del dev $DEV root 2>/dev/null
+    $TC qdisc add dev $DEV root $( get_stab_string ) cake bandwidth ${DOWNLINK}kbit $( get_cake_lla_string ) ${INGRESS_CAKE_OPTS} ${IQDISC_OPTS}
+
+    $IP link set dev $DEV up
+
+    # redirect all IP packets arriving in $IFACE to ifb0
+
+    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+       match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+}
+
+sqm_start() {
+    [ -n "$IFACE" ] || return 1
+    do_modules
+    verify_qdisc $QDISC "cake" || return 1
+    sqm_debug "Starting ${SCRIPT}"
+
+    [ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} )
+
+
+    if [ "${UPLINK}" -ne 0 ];
+    then
+        egress
+        sqm_debug "egress shaping activated"
+    else
+        sqm_debug "egress shaping deactivated"
+        $TC qdisc del dev ${IFACE} root 2> /dev/null
+    fi
+    if [ "${DOWNLINK}" -ne 0 ];
+    then
+       verify_qdisc ingress "ingress" || return 1
+        ingress
+        sqm_debug "ingress shaping activated"
+    else
+        sqm_debug "ingress shaping deactivated"
+        $TC qdisc del dev ${DEV} root 2> /dev/null
+        $TC qdisc del dev ${IFACE} ingress 2> /dev/null
+    fi
+    return 0
+}
diff --git a/package/network/config/sqm-scripts-extra/src/test_WAN_dual-isolate__piece_of_cake.qos.help b/package/network/config/sqm-scripts-extra/src/test_WAN_dual-isolate__piece_of_cake.qos.help
new file mode 100644 (file)
index 0000000..c239917
--- /dev/null
@@ -0,0 +1,10 @@
+test_WAN_dual-isolate__piece_of_cake.qos (Cero3 Shaper)
+
+Abstract:
+This is a one band cake and ipv6 enabled shaping script for Ethernet gateways. 
+This exists for the purpose of testing cake's different isolation options, 
+specifically the directional dual-[src|dst]host feature that promises to finally 
+tackle the bit-torrent hole in simple.qos. 
+This specific version is supposed to be used on WAN interfaces: 
+ the script's ingress direction equals the internet/WAN download direction 
+ the script's egress direction equals the internet/WAN upload direction
diff --git a/package/network/config/sqm-scripts-extra/src/test_WAN_triple-isolate__piece_of_cake.qos b/package/network/config/sqm-scripts-extra/src/test_WAN_triple-isolate__piece_of_cake.qos
new file mode 100644 (file)
index 0000000..5f0d7d7
--- /dev/null
@@ -0,0 +1,89 @@
+#!/bin/sh
+################################################################################
+# test_WAN_triple-isolate__piece_of_cake.qos (Cero3 Shaper)
+#
+# Abstract:
+# This is a one band cake and ipv6 enabled shaping script for Ethernet
+# gateways. This exists for the purpose of testing cake's different isolation
+# options, specifically the non-directional triple-isolate feature that promises
+# to finally tackle the bit-torrent hole in simple.qos.
+# This specific version is supposed to be used on WAN interfaces:
+#      the script's ingress direction equals the internet/WAN download direction
+#      the script's egress direction equals the internet/WAN upload direction
+#
+################################################################################
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#  Copyright (C) 2012-2016
+#    Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+#
+################################################################################
+
+. ${SQM_LIB_DIR}/defaults.sh
+
+################################################################################
+
+
+# this will only work with cake as qdisc...
+QDISC=cake
+
+
+# to keep this as simple as possible we ignore the *_CAKE_OPTS from defaults.sh
+INGRESS_CAKE_OPTS="besteffort triple-isolate"
+EGRESS_CAKE_OPTS="besteffort triple-isolate"
+
+
+egress() {
+    sqm_debug "egress"
+    $TC qdisc del dev $IFACE root 2>/dev/null
+    $TC qdisc add dev $IFACE root $( get_stab_string ) cake bandwidth ${UPLINK}kbit $( get_cake_lla_string ) ${EGRESS_CAKE_OPTS} ${EQDISC_OPTS}
+}
+
+
+ingress() {
+    sqm_debug "ingress"
+    $TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null
+    $TC qdisc add dev $IFACE handle ffff: ingress
+    $TC qdisc del dev $DEV root 2>/dev/null
+    $TC qdisc add dev $DEV root $( get_stab_string ) cake bandwidth ${DOWNLINK}kbit $( get_cake_lla_string ) ${INGRESS_CAKE_OPTS} ${IQDISC_OPTS}
+
+    $IP link set dev $DEV up
+
+    # redirect all IP packets arriving in $IFACE to ifb0
+
+    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+       match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+}
+
+sqm_start() {
+    [ -n "$IFACE" ] || return 1
+    do_modules
+    verify_qdisc $QDISC "cake" || return 1
+    sqm_debug "Starting ${SCRIPT}"
+
+    [ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} )
+
+
+    if [ "${UPLINK}" -ne 0 ];
+    then
+        egress
+        sqm_debug "egress shaping activated"
+    else
+        sqm_debug "egress shaping deactivated"
+        $TC qdisc del dev ${IFACE} root 2> /dev/null
+    fi
+    if [ "${DOWNLINK}" -ne 0 ];
+    then
+       verify_qdisc ingress "ingress" || return 1
+        ingress
+        sqm_debug "ingress shaping activated"
+    else
+        sqm_debug "ingress shaping deactivated"
+        $TC qdisc del dev ${DEV} root 2> /dev/null
+        $TC qdisc del dev ${IFACE} ingress 2> /dev/null
+    fi
+    return 0
+}
diff --git a/package/network/config/sqm-scripts-extra/src/test_WAN_triple-isolate__piece_of_cake.qos.help b/package/network/config/sqm-scripts-extra/src/test_WAN_triple-isolate__piece_of_cake.qos.help
new file mode 100644 (file)
index 0000000..72df509
--- /dev/null
@@ -0,0 +1,10 @@
+test_WAN_triple-isolate__piece_of_cake.qos (Cero3 Shaper)
+
+Abstract:
+This is a one band cake and ipv6 enabled shaping script for Ethernet gateways. 
+This exists for the purpose of testing cake's different isolation options, 
+specifically the non-directional triple-isolate feature that promises to finally 
+tackle the bit-torrent hole in simple.qos. 
+This specific version is supposed to be used on WAN interfaces: 
+ the script's ingress direction equals the internet/WAN download direction 
+ the script's egress direction equals the internet/WAN upload direction
diff --git a/package/network/config/sqm-scripts-extra/src/test_triple_isolated_llt_cake.qos b/package/network/config/sqm-scripts-extra/src/test_triple_isolated_llt_cake.qos
new file mode 100644 (file)
index 0000000..df17b39
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/sh
+################################################################################
+# test_triple_isolated_llt_cake.qos
+# One bin cake shaper for ethernet gateways 
+# using cake qdisc with triple-isolate and diffserv-llt (Latency-Loss-Tradeoff)
+#
+################################################################################
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+#  Copyright (C) 2012-2016
+#    Michael D. Taht, Toke Høiland-Jørgensen, Sebastian Moeller
+#
+################################################################################
+
+. ${SQM_LIB_DIR}/defaults.sh
+
+################################################################################
+
+
+# this will only work with cake as qdisc...
+QDISC=cake
+
+
+# to keep this as simple as possible we ignore the *_CAKE_OPTS from defaults.sh
+INGRESS_CAKE_OPTS="diffserv-llt triple-isolate"
+EGRESS_CAKE_OPTS="diffserv-llt triple-isolate"
+
+
+egress() {
+    sqm_debug "egress"
+    $TC qdisc del dev $IFACE root 2>/dev/null
+    $TC qdisc add dev $IFACE root $( get_stab_string ) cake bandwidth ${UPLINK}kbit $( get_cake_lla_string ) ${EGRESS_CAKE_OPTS} ${EQDISC_OPTS}
+}
+
+
+ingress() {
+    sqm_debug "ingress"
+    $TC qdisc del dev $IFACE handle ffff: ingress 2>/dev/null
+    $TC qdisc add dev $IFACE handle ffff: ingress
+    $TC qdisc del dev $DEV root 2>/dev/null
+    $TC qdisc add dev $DEV root $( get_stab_string ) cake bandwidth ${DOWNLINK}kbit $( get_cake_lla_string ) ${INGRESS_CAKE_OPTS} ${IQDISC_OPTS}
+
+    $IP link set dev $DEV up
+
+    # redirect all IP packets arriving in $IFACE to ifb0
+
+    $TC filter add dev $IFACE parent ffff: protocol all prio 10 u32 \
+       match u32 0 0 flowid 1:1 action mirred egress redirect dev $DEV
+}
+
+sqm_start() {
+    [ -n "$IFACE" ] || return 1
+    do_modules
+    verify_qdisc $QDISC "cake" || return 1
+    sqm_debug "Starting ${SCRIPT}"
+
+    [ -z "$DEV" ] && DEV=$( get_ifb_for_if ${IFACE} )
+
+
+    if [ "${UPLINK}" -ne 0 ];
+    then
+        egress
+        sqm_debug "egress shaping activated"
+    else
+        sqm_debug "egress shaping deactivated"
+        $TC qdisc del dev ${IFACE} root 2> /dev/null
+    fi
+    if [ "${DOWNLINK}" -ne 0 ];
+    then
+       verify_qdisc ingress "ingress" || return 1
+        ingress
+        sqm_debug "ingress shaping activated"
+    else
+        sqm_debug "ingress shaping deactivated"
+        $TC qdisc del dev ${DEV} root 2> /dev/null
+        $TC qdisc del dev ${IFACE} ingress 2> /dev/null
+    fi
+    return 0
+}
diff --git a/package/network/config/sqm-scripts-extra/src/test_triple_isolated_llt_cake.qos.help b/package/network/config/sqm-scripts-extra/src/test_triple_isolated_llt_cake.qos.help
new file mode 100644 (file)
index 0000000..06ffdad
--- /dev/null
@@ -0,0 +1,3 @@
+Cake qdisc with triple-isolate and diffserv-llt (Latency-Loss-Tradeoff)
+options as the shaper and fq_codel as leaf qdisc. This script requires
+that cake is selected as qdisc.
diff --git a/package/network/config/sqm-scripts/Makefile b/package/network/config/sqm-scripts/Makefile
new file mode 100644 (file)
index 0000000..37730b6
--- /dev/null
@@ -0,0 +1,88 @@
+# 
+# Copyright (C) 2014 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=sqm-scripts
+PKG_SOURCE_VERSION:=8217081f7e52af342c362b29480461575c496387
+PKG_VERSION:=1.1.3
+PKG_RELEASE:=1
+PKG_LICENSE:=GPLv2
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE).tar.xz
+PKG_MIRROR_HASH:=a287ce3bf68ed76f4fd7ae3df5e0066d99105b5c139c88bce99555dcb1d230a3
+PKG_SOURCE_URL:=https://github.com/tohojo/sqm-scripts.git
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE)
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/sqm-scripts
+  SECTION:=net
+  CATEGORY:=Base system
+  DEPENDS:=+tc +kmod-sched-core +kmod-sched-cake +kmod-ifb +iptables \
+       +iptables-mod-ipopt +iptables-mod-conntrack-extra
+  TITLE:=SQM Scripts (QoS)
+  PKGARCH:=all
+  MAINTAINER:=Toke Høiland-Jørgensen <toke@toke.dk>
+endef
+
+define Package/sqm-scripts/description
+ A set of scripts that does simple SQM configuration.
+endef
+
+define Package/sqm-scripts/conffiles
+/etc/config/sqm
+/etc/sqm/sqm.conf
+endef
+
+define Package/sqm-scripts/install
+       make -C $(PKG_BUILD_DIR) DESTDIR=$(1) PLATFORM=openwrt install
+endef
+
+define Package/luci-app-sqm
+  SECTION:=luci
+  CATEGORY:=LuCI
+  TITLE:=SQM Scripts - LuCI interface
+  MAINTAINER:=Toke Høiland-Jørgensen <toke@toke.dk>
+  PKGARCH:=all
+  DEPENDS:= lua luci-base +sqm-scripts
+  SUBMENU:=3. Applications
+endef
+
+define Package/luci-app-sqm/description
+       Luci interface for the SQM scripts queue management configuration package.
+endef
+
+define Package/luci-app-sqm/install
+       make -C $(PKG_BUILD_DIR) DESTDIR=$(1) PLATFORM=openwrt install-luci
+endef
+
+define Package/luci-app-sqm/postinst
+#!/bin/sh
+which uci > /dev/null || exit 0
+uci -q get ucitrack.@sqm[0] > /dev/null || {
+  uci add ucitrack sqm > /dev/null
+  uci set ucitrack.@sqm[0].init=sqm
+  uci add_list ucitrack.@firewall[0].affects=sqm
+  uci commit
+}
+endef
+
+define Package/luci-app-sqm/postrm
+#!/bin/sh
+which uci > /dev/null || exit 0
+uci -q get ucitrack.@sqm[0] > /dev/null && {
+  uci delete ucitrack.@sqm[0]
+  uci del_list ucitrack.@firewall[0].affects=sqm
+  uci commit
+}
+endef
+
+$(eval $(call BuildPackage,sqm-scripts))
+$(eval $(call BuildPackage,luci-app-sqm))
diff --git a/package/network/services/cjdns/Makefile b/package/network/services/cjdns/Makefile
new file mode 100644 (file)
index 0000000..8efd282
--- /dev/null
@@ -0,0 +1,148 @@
+#
+# Copyright (C) 2014,2015 Hyperboria.net
+#
+# You may redistribute this program and/or modify it under the terms of
+# the GNU General Public License as published by the Free Software Foundation,
+# either version 3 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=cjdns
+PKG_VERSION:=0.17
+PKG_RELEASE:=3
+
+PKG_SOURCE_URL:=https://github.com/hyperboria/cjdns.git
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_VERSION:=40e87d9419c19063e772e39c7c59a8a8771c5ee8
+PKG_LICENSE:=GPL-3.0
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.bz2
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_SOURCE_VERSION)
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_SOURCE_VERSION)
+
+include $(INCLUDE_DIR)/package.mk
+
+
+define Package/cjdns
+       SECTION:=net
+       CATEGORY:=Network
+       SUBMENU:=Routing and Redirection
+       TITLE:=Encrypted near-zero-conf mesh routing protocol
+       URL:=https://github.com/hyperboria/cjdns
+       MAINTAINER:=Lars Gierth <larsg@systemli.org>
+       DEPENDS:=@IPV6 +kmod-tun +libnl-tiny +libpthread +librt \
+               +libuci-lua +lua-bencode +dkjson +luasocket +lua-sha2
+endef
+
+define Package/cjdns/description
+       Cjdns implements an encrypted IPv6 network using public-key cryptography \
+       for address allocation and a distributed hash table for routing. \
+       This provides near-zero-configuration networking, and prevents many \
+       of the security and scalability issues that plague existing networks.
+endef
+
+define Package/cjdns-tests
+       SECTION:=net
+       CATEGORY:=Network
+       SUBMENU:=Routing and Redirection
+       TITLE:=cjdns test cases
+       URL:=https://github.com/hyperboria/cjdns
+       MAINTAINER:=Lars Gierth <larsg@systemli.org>
+       DEPENDS:=+libpthread +librt
+endef
+
+define Package/cjdns-test/description
+       cjdns test cases
+endef
+
+define Build/Configure
+endef
+
+PKG_DO_VARS:=CJDNS_RELEASE_VERSION=$(PKG_SOURCE_VERSION)
+
+ifneq ($(CONFIG_KERNEL_SECCOMP_FILTER),y)
+PKG_DO_VARS+= Seccomp_NO=1
+endif
+
+ifneq ($(CONFIG_USE_UCLIBC),)
+PKG_DO_VARS+= UCLIBC=1
+endif
+
+define Build/Compile
+       $(INSTALL_DIR) $(PKG_BUILD_DIR)/tmp
+       CROSS="true" \
+       CC="$(TARGET_CC)" \
+       AR="$(TARGET_AR)" \
+       RANLIB="$(TARGET_RANLIB)" \
+       CFLAGS="$(TARGET_CFLAGS)" \
+       LDFLAGS="$(TARGET_LDFLAGS)" \
+       SYSTEM="linux" \
+       TARGET_ARCH="$(CONFIG_ARCH)" \
+       SSP_SUPPORT="$(CONFIG_SSP_SUPPORT)" \
+       GYP_ADDITIONAL_ARGS="-f make-linux" \
+       CJDNS_BUILD_TMPDIR="$(PKG_BUILD_DIR)/tmp" \
+       $(PKG_DO_VARS) \
+       $(PKG_BUILD_DIR)/do
+endef
+
+define Package/cjdns/install
+       $(INSTALL_DIR) \
+               $(1)/usr/sbin \
+               $(1)/usr/bin \
+               $(1)/etc/config \
+               $(1)/etc/init.d \
+               $(1)/etc/uci-defaults \
+               $(1)/usr/lib/lua/cjdns
+
+       $(INSTALL_BIN) \
+               ./files/cjdrouteconf \
+               $(1)/usr/bin
+
+       $(INSTALL_BIN) \
+               $(PKG_BUILD_DIR)/cjdroute \
+               $(1)/usr/sbin
+
+       $(INSTALL_BIN) \
+               $(PKG_BUILD_DIR)/publictoip6 \
+               $(1)/usr/bin
+
+       $(INSTALL_BIN) \
+               ./files/cjdns.init \
+               $(1)/etc/init.d/cjdns
+
+       $(INSTALL_BIN) \
+               ./files/cjdns.defaults \
+               $(1)/etc/uci-defaults/cjdns
+
+       $(CP) \
+               ./lua/cjdns/* \
+               $(1)/usr/lib/lua/cjdns
+endef
+
+define Package/cjdns/postinst
+#!/bin/sh
+if [ -z $${IPKG_INSTROOT} ] ; then
+       ( . /etc/uci-defaults/cjdns ) && rm -f /etc/uci-defaults/cjdns
+       # TODO: we should have an 'Enable' button instead
+       /etc/init.d/cjdns enabled || /etc/init.d/cjdns enable
+       exit 0
+fi
+endef
+
+define Package/cjdns-tests/install
+       $(INSTALL_DIR) $(1)/usr/bin
+       $(INSTALL_BIN) \
+               $(PKG_BUILD_DIR)/build_linux/test_testcjdroute_c \
+               $(1)/usr/bin
+endef
+
+$(eval $(call BuildPackage,cjdns))
+$(eval $(call BuildPackage,cjdns-tests))
diff --git a/package/network/services/cjdns/files/cjdns.defaults b/package/network/services/cjdns/files/cjdns.defaults
new file mode 100644 (file)
index 0000000..f2baf6d
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+# if there is an existing config, our work is already done
+uci get cjdns.cjdns.ipv6 >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+
+  # register commit handler
+  uci -q batch <<-EOF >/dev/null
+    delete ucitrack.@cjdns[-1]
+    add ucitrack cjdns
+    set ucitrack.@cjdns[-1].init=cjdns
+    commit ucitrack
+EOF
+
+  # generate configuration
+  touch /etc/config/cjdns
+  cjdroute --genconf | cjdroute --cleanconf | cjdrouteconf set
+
+  # make sure config is present (might fail for any reason)
+  uci get cjdns.cjdns.ipv6 >/dev/null 2>&1
+  if [ $? -ne 0 ]; then
+    exit 1
+  fi
+
+  # enable auto-peering on ethernet interface lan, if existing
+  uci get network.lan | grep interface >/dev/null 2>&1
+  if [ $? -eq 0 ]; then
+    uci get network.lan.type | grep bridge >/dev/null 2>&1
+    if [ $? -eq 0 ]; then
+      # most routers will set up an ethernet bridge for the lan
+      ifname="br-lan"
+    else
+      # docker containers don't have permission to create bridges by default,
+      # so we bind to the underlying interface instead (likely eth0)
+      ifname=`uci get network.lan.ifname`
+    fi
+    uci -q batch <<-EOF >/dev/null
+      add cjdns eth_interface
+      set cjdns.@eth_interface[-1].beacon=2
+      set cjdns.@eth_interface[-1].bind=$ifname
+EOF
+  fi
+  # set the tun interface name
+  uci set cjdns.cjdns.tun_device=tuncjdns
+
+  # create the network interface
+  uci -q batch <<-EOF >/dev/null
+    set network.cjdns=interface
+    set network.cjdns.ifname=tuncjdns
+    set network.cjdns.proto=none
+EOF
+
+  # firewall rules by @dangowrt -- thanks <3
+
+  # create the firewall zone
+  uci -q batch <<-EOF >/dev/null
+    add firewall zone
+    set firewall.@zone[-1].name=cjdns
+    add_list firewall.@zone[-1].network=cjdns
+    set firewall.@zone[-1].input=REJECT
+    set firewall.@zone[-1].output=ACCEPT
+    set firewall.@zone[-1].forward=REJECT
+    set firewall.@zone[-1].conntrack=1
+    set firewall.@zone[-1].family=ipv6
+EOF
+
+  # allow ICMP from cjdns zone, e.g. ping6
+  uci -q batch <<-EOF >/dev/null
+    add firewall rule
+    set firewall.@rule[-1].name='Allow-ICMPv6-cjdns'
+    set firewall.@rule[-1].src=cjdns
+    set firewall.@rule[-1].proto=icmp
+    add_list firewall.@rule[-1].icmp_type=echo-request
+    add_list firewall.@rule[-1].icmp_type=echo-reply
+    add_list firewall.@rule[-1].icmp_type=destination-unreachable
+    add_list firewall.@rule[-1].icmp_type=packet-too-big
+    add_list firewall.@rule[-1].icmp_type=time-exceeded
+    add_list firewall.@rule[-1].icmp_type=bad-header
+    add_list firewall.@rule[-1].icmp_type=unknown-header-type
+    set firewall.@rule[-1].limit='1000/sec'
+    set firewall.@rule[-1].family=ipv6
+    set firewall.@rule[-1].target=ACCEPT
+EOF
+
+  # allow SSH from cjdns zone, needs to be explicitly enabled
+  uci -q batch <<-EOF >/dev/null
+    add firewall rule
+    set firewall.@rule[-1].enabled=0
+    set firewall.@rule[-1].name='Allow-SSH-cjdns'
+    set firewall.@rule[-1].src=cjdns
+    set firewall.@rule[-1].proto=tcp
+    set firewall.@rule[-1].dest_port=22
+    set firewall.@rule[-1].target=ACCEPT
+EOF
+
+  # allow LuCI access from cjdns zone, needs to be explicitly enabled
+  uci -q batch <<-EOF >/dev/null
+    add firewall rule
+    set firewall.@rule[-1].enabled=0
+    set firewall.@rule[-1].name='Allow-HTTP-cjdns'
+    set firewall.@rule[-1].src=cjdns
+    set firewall.@rule[-1].proto=tcp
+    set firewall.@rule[-1].dest_port=80
+    set firewall.@rule[-1].target=ACCEPT
+EOF
+
+  # allow UDP peering from wan zone, if it exists
+  uci show network.wan >/dev/null 2>&1
+  if [ $? -eq 0 ]; then
+    peeringPort=`uci get cjdns.@udp_interface[0].port`
+    uci -q batch <<-EOF >/dev/null
+      add firewall rule
+      set firewall.@rule[-1].name='Allow-cjdns-wan'
+      set firewall.@rule[-1].src=wan
+      set firewall.@rule[-1].proto=udp
+      set firewall.@rule[-1].dest_port=$peeringPort
+      set firewall.@rule[-1].target=ACCEPT
+EOF
+  fi
+
+  uci commit cjdns
+  uci commit firewall
+  uci commit network
+
+fi
+
+exit 0
diff --git a/package/network/services/cjdns/files/cjdns.init b/package/network/services/cjdns/files/cjdns.init
new file mode 100755 (executable)
index 0000000..b6371d7
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh /etc/rc.common
+
+START=90
+STOP=85
+
+USE_PROCD=1
+
+start_service()
+{
+       [ -f /etc/uci-defaults/cjdns ] && ( . /etc/uci-defaults/cjdns )
+
+       procd_open_instance
+       procd_set_param respawn
+       procd_set_param command /bin/ash -c "cjdrouteconf get | tee /tmp/etc/cjdroute.conf | cjdroute --nobg | logger -t cjdns"
+       procd_close_instance
+}
+
+stop_service()
+{
+       killall cjdroute
+}
+
+reload_service()
+{
+       # cat /tmp/etc/cjdroute.conf | cjdrouteconf reload
+       restart
+}
+
+service_triggers()
+{
+       procd_add_reload_trigger cjdns
+}
diff --git a/package/network/services/cjdns/files/cjdrouteconf b/package/network/services/cjdns/files/cjdrouteconf
new file mode 100755 (executable)
index 0000000..fa5e073
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env lua
+
+dkjson = require("dkjson")
+cjdns = require("cjdns")
+require("cjdns/uci")
+
+function help()
+  print("JSON interface to /etc/config/cjdns\n\nExamples: \
+    cjdrouteconf get > /tmp/etc/cjdroute.conf \
+    cat /tmp/etc/cjdroute.conf | cjdrouteconf set \
+    uci changes \
+    cjdrouteconf get | cjdroute")
+end
+
+if arg[1] == "get" then
+  local json = dkjson.encode(cjdns.uci.get(), { indent = true })
+  print(json)
+elseif arg[1] == "set" then
+  local json = io.stdin:read("*a")
+  local obj, pos, err = dkjson.decode(json, 1, nil)
+
+  if obj then
+    cjdns.uci.set(obj)
+  else
+    print("dkjson: " .. err .. " (try cjdroute --cleanconf)")
+    os.exit(1)
+  end
+else
+  help()
+end
diff --git a/package/network/services/cjdns/lua/cjdns/admin.lua b/package/network/services/cjdns/lua/cjdns/admin.lua
new file mode 100644 (file)
index 0000000..2bb58d2
--- /dev/null
@@ -0,0 +1,105 @@
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+common = require 'cjdns/common'
+
+AdminInterface = {}
+AdminInterface.__index = AdminInterface
+common.AdminInterface = AdminInterface
+
+function AdminInterface.new(properties)
+    properties = properties or {}
+
+    properties.host     = properties.host or "127.0.0.1"
+    properties.port     = properties.port or 11234
+    properties.password = properties.password or nil
+    properties.config   = properties.config   or common.ConfigFile.new("/etc/cjdroute.conf", false)
+    properties.timeout  = properties.timeout  or 2
+
+    properties.udp      = common.UDPInterface.new(properties)
+
+    return setmetatable(properties, AdminInterface)
+end
+
+function AdminInterface:send(object)
+    local bencoded, err = bencode.encode(object)
+    if err then
+        return nil, err
+    end
+
+    local sock_obj = assert(socket.udp())
+    sock_obj:settimeout(self.timeout)
+
+    local _, err = sock_obj:sendto(bencoded, self.host, self.port)
+    if err then
+        return nil, err
+    end
+
+    return sock_obj
+end
+
+function AdminInterface:recv(sock_obj)
+    local retrieved, err = sock_obj:receive()
+    if not retrieved then
+        return nil, "ai:recv > " .. err
+    end
+    local bencoded, err = bencode.decode(retrieved)
+    if bencoded then
+        return bencoded
+    else
+        return nil, "ai:recv > " .. err
+    end
+end
+
+function AdminInterface:call(request)
+    local sock_obj, err = self:send(request)
+    if err then
+        return nil, "ai:call > " .. err
+    end
+
+    return self:recv(sock_obj)
+end
+
+function AdminInterface:getCookie()
+    local cookie_response, err = self:call({ q = "cookie" })
+    if not cookie_response then
+        return nil, "ai:getCookie > " .. err
+    end
+    return cookie_response.cookie
+end
+
+function AdminInterface:auth(request)
+    local funcname = request.q
+    local args = {}
+    for k, v in pairs(request) do
+        args[k] = v
+    end
+
+    -- Step 1: Get cookie
+    local cookie, err = self:getCookie()
+    if err then
+        return nil, err
+    end
+
+    -- Step 2: Calculate hash1 (password + cookie)
+    local plaintext1 = self.password .. cookie
+    local hash1 = sha2.sha256hex(plaintext1)
+
+    -- Step 3: Calculate hash2 (intermediate stage request)
+    local request = {
+        q      = "auth",
+        aq     = funcname,
+        args   = args,
+        hash   = hash1,
+        cookie = cookie
+    }
+    local plaintext2, err = bencode.encode(request)
+    if err then
+        return nil, err
+    end
+    local hash2 = sha2.sha256hex(plaintext2)
+
+    -- Step 4: Update hash in request, then ship it out
+    request.hash = hash2
+    return self:call(request)
+end
diff --git a/package/network/services/cjdns/lua/cjdns/common.lua b/package/network/services/cjdns/lua/cjdns/common.lua
new file mode 100644 (file)
index 0000000..45f7dad
--- /dev/null
@@ -0,0 +1,7 @@
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+-- This table is preserved over multiple imports, and collects
+-- submodules import-by-import via init.lua.
+
+return {}
diff --git a/package/network/services/cjdns/lua/cjdns/init.lua b/package/network/services/cjdns/lua/cjdns/init.lua
new file mode 100644 (file)
index 0000000..32abbfc
--- /dev/null
@@ -0,0 +1,12 @@
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+bencode = require "bencode" -- https://bitbucket.org/wilhelmy/lua-bencode/
+dkjson  = require "dkjson"  -- http://dkolf.de/src/dkjson-lua.fsl/home
+socket  = require "socket"  -- http://w3.impa.br/~diego/software/luasocket/
+sha2    = require "sha2"    -- https://code.google.com/p/sha2/
+
+require "cjdns/admin"
+require "cjdns/udp"
+
+return require "cjdns/common"
diff --git a/package/network/services/cjdns/lua/cjdns/uci.lua b/package/network/services/cjdns/lua/cjdns/uci.lua
new file mode 100644 (file)
index 0000000..0127f44
--- /dev/null
@@ -0,0 +1,289 @@
+common = require("cjdns/common")
+uci    = require("uci")
+
+UCI = {}
+common.uci = UCI
+
+--- Return the configuration defaults as a table suitable for JSON output
+--
+-- Mostly taken from cjdroute --genconf
+-- @return table with configuration defaults
+function UCI.defaults()
+  return {
+    security = {
+      { setuser = "nobody", keepNetAdmin = 1 },
+      { chroot = "/var/run/" },
+      { nofiles = 0 },
+      { noforks = 1 },
+      { seccomp = 0 },
+      { setupComplete = 1 }
+    },
+    router = {
+        ipTunnel = { outgoingConnections = {}, allowedConnections = {} },
+        interface = { type = "TUNInterface" }
+    },
+    interfaces = { UDPInterface = {}, ETHInterface = {} },
+    authorizedPasswords = {},
+    logging = { logTo = "stdout" }
+  }
+end
+
+--- Return the cjdns configuration as a table suitable for JSON output
+--
+-- Iterates over cjdns, eth_interface, udp_interface, eth_peer, udp_peer,
+-- and password sections. Doesn't include IPTunnel related options yet.
+-- @return table with cjdns configuration
+function UCI.get()
+  local obj = UCI.defaults()
+
+  local cursor = uci.cursor()
+
+  local config = cursor:get_all("cjdns", "cjdns")
+  if not config then return obj end
+
+  obj.ipv6 = config.ipv6
+  obj.publicKey = config.public_key
+  obj.privateKey = config.private_key
+  obj.admin = {
+    bind = config.admin_address .. ":" .. config.admin_port,
+    password = config.admin_password }
+
+  if config.tun_device and string.len(config.tun_device) > 0 then
+    obj.router.interface.tunDevice = config.tun_device
+  end
+
+  for i,section in pairs(obj.security) do
+    if type(section.seccomp) == "number" then
+      obj.security[i].seccomp = tonumber(config.seccomp)
+    end
+  end
+
+  cursor:foreach("cjdns", "iptunnel_outgoing", function(outgoing)
+    table.insert(obj.router.ipTunnel.outgoingConnections, outgoing.public_key)
+  end)
+
+  cursor:foreach("cjdns", "iptunnel_allowed", function(allowed)
+    entry = { publicKey = allowed.public_key }
+    if allowed.ipv4 then
+      entry["ip4Address"] = allowed.ipv4
+    end
+    if allowed.ipv6 then
+      entry["ip6Address"] = allowed.ipv6
+    end
+    table.insert(obj.router.ipTunnel.allowedConnections, entry)
+  end)
+
+  cursor:foreach("cjdns", "eth_interface", function(eth_interface)
+    table.insert(obj.interfaces.ETHInterface, {
+      bind = eth_interface.bind,
+      beacon = tonumber(eth_interface.beacon),
+      connectTo = {}
+    })
+  end)
+
+  cursor:foreach("cjdns", "udp_interface", function(udp_interface)
+    table.insert(obj.interfaces.UDPInterface, {
+      bind = udp_interface.address .. ":" .. udp_interface.port,
+      connectTo = {}
+    })
+  end)
+
+  cursor:foreach("cjdns", "eth_peer", function(eth_peer)
+    if not eth_peer.address == "" then
+      local i = tonumber(eth_peer.interface)
+      obj.interfaces.ETHInterface[i].connectTo[eth_peer.address] = {
+        publicKey = eth_peer.public_key,
+        password = eth_peer.password
+      }
+    end
+  end)
+
+  cursor:foreach("cjdns", "udp_peer", function(udp_peer)
+    local bind = udp_peer.address .. ":" .. udp_peer.port
+    local i = tonumber(udp_peer.interface)
+    obj.interfaces.UDPInterface[i].connectTo[bind] = {
+      user = udp_peer.user,
+      publicKey = udp_peer.public_key,
+      password = udp_peer.password
+    }
+  end)
+
+  cursor:foreach("cjdns", "password", function(password)
+    table.insert(obj.authorizedPasswords, {
+      password = password.password,
+      user = password.user,
+      contact = password.contact
+    })
+  end)
+
+  return obj
+end
+
+--- Parse and save updated configuration from JSON input
+--
+-- Transforms general settings, ETHInterface, UDPInterface, connectTo, and
+-- authorizedPasswords fields into UCI sections, and replaces the UCI config's
+-- contents with them.
+-- @param table JSON input
+-- @return Boolean whether saving succeeded
+function UCI.set(obj)
+  local cursor = uci.cursor()
+
+  for i, section in pairs(cursor:get_all("cjdns")) do
+    cursor:delete("cjdns", section[".name"])
+  end
+
+  local admin_address, admin_port = string.match(obj.admin.bind, "^(.*):(.*)$")
+  UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
+    ipv6 = obj.ipv6,
+    public_key = obj.publicKey,
+    private_key = obj.privateKey,
+    admin_password = obj.admin.password,
+    admin_address = admin_address,
+    admin_port = admin_port
+  })
+
+  if obj.router.interface.tunDevice then
+    UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
+      tun_device = tostring(obj.router.interface.tunDevice)
+    })
+  end
+
+  if obj.security then
+    for i,section in pairs(obj.security) do
+      for key,value in pairs(section) do
+        if key == "seccomp" then
+          UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
+            seccomp = tonumber(value)
+          })
+        end
+      end
+    end
+  end
+
+  if obj.router.ipTunnel.outgoingConnections then
+    for i,public_key in pairs(obj.router.ipTunnel.outgoingConnections) do
+      UCI.cursor_section(cursor, "cjdns", "iptunnel_outgoing", nil, {
+        public_key = public_key
+      })
+    end
+  end
+
+  if obj.router.ipTunnel.allowedConnections then
+    for i,allowed in pairs(obj.router.ipTunnel.allowedConnections) do
+      entry = { public_key = allowed.publicKey }
+      if allowed.ip4Address then
+        entry["ipv4"] = allowed.ip4Address
+      end
+      if allowed.ip6Address then
+        entry["ipv6"] = allowed.ip6Address
+      end
+
+      UCI.cursor_section(cursor, "cjdns", "iptunnel_allowed", nil, entry)
+    end
+  end
+
+  if obj.interfaces.ETHInterface then
+    for i,interface in pairs(obj.interfaces.ETHInterface) do
+      UCI.cursor_section(cursor, "cjdns", "eth_interface", nil, {
+        bind = interface.bind,
+        beacon = tostring(interface.beacon)
+      })
+
+      if interface.connectTo then
+        for peer_address,peer in pairs(interface.connectTo) do
+          UCI.cursor_section(cursor, "cjdns", "eth_peer", nil, {
+            interface = i,
+            address = peer_address,
+            public_key = peer.publicKey,
+            password = peer.password
+          })
+        end
+      end
+    end
+  end
+
+  if obj.interfaces.UDPInterface then
+    for i,interface in pairs(obj.interfaces.UDPInterface) do
+      local address, port = string.match(interface.bind, "^(.*):(.*)$")
+      UCI.cursor_section(cursor, "cjdns", "udp_interface", nil, {
+        address = address,
+        port = port
+      })
+
+      if interface.connectTo then
+        for peer_bind,peer in pairs(interface.connectTo) do
+          local peer_address, peer_port = string.match(peer_bind, "^(.*):(.*)$")
+          UCI.cursor_section(cursor, "cjdns", "udp_peer", nil, {
+            interface = i,
+            address = peer_address,
+            port = peer_port,
+            user = peer.user,
+            public_key = peer.publicKey,
+            password = peer.password
+          })
+        end
+      end
+    end
+  end
+
+  if obj.authorizedPasswords then
+    for i,password in pairs(obj.authorizedPasswords) do
+      local user = password.user
+      if not user or string.len(user) == 0 then
+        user = "user-" .. UCI.random_string(6)
+      end
+
+      UCI.cursor_section(cursor, "cjdns", "password", nil, {
+        password = password.password,
+        user = user,
+        contact = password.contact
+      })
+    end
+  end
+
+  return cursor:save("cjdns")
+end
+
+--- Simple backport of Cursor:section from luci.model.uci
+--
+-- Backport reason: we don't wanna depend on LuCI.
+-- @param Cursor the UCI cursor to operate on
+-- @param string name of the config
+-- @param string type of the section
+-- @param string name of the section (optional)
+-- @param table config values
+function UCI.cursor_section(cursor, config, type, section, values)
+  if section then
+    cursor:set(config, section, type)
+  else
+    section = cursor:add("cjdns", type)
+  end
+
+  for k,v in pairs(values) do
+    cursor:set(config, section, k, v)
+  end
+end
+
+function UCI.makeInterface()
+  local cursor = uci.cursor()
+
+  local config = cursor:get_all("cjdns", "cjdns")
+  if not config then return nil end
+
+  return common.AdminInterface.new({
+    host = config.admin_address,
+    port = config.admin_port,
+    password = config.admin_password,
+    config = UCI.get(),
+    timeout = 2
+  })
+end
+
+function UCI.random_string(length)
+  -- tr -cd 'A-Za-z0-9' < /dev/urandom
+  local urandom = io.popen("tr -cd 'A-Za-z0-9' 2> /dev/null < /dev/urandom", "r")
+  local string = urandom:read(length)
+  urandom:close()
+  return string
+end
diff --git a/package/network/services/cjdns/lua/cjdns/udp.lua b/package/network/services/cjdns/lua/cjdns/udp.lua
new file mode 100644 (file)
index 0000000..9dd5901
--- /dev/null
@@ -0,0 +1,102 @@
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+common = require 'cjdns/common'
+
+UDPInterface = {}
+UDPInterface.__index = UDPInterface
+common.UDPInterface = UDPInterface
+
+function UDPInterface.new(ai, config, ptype)
+    properties = {
+        ai     = ai,
+        config = config or ai.config,
+        ptype  = ptype or "ai"
+    }
+
+    return setmetatable(properties, UDPInterface)
+end
+
+function UDPInterface:call(name, args)
+    local func = self[name .. "_" .. self.ptype]
+    return func(self, unpack(args))
+end
+
+function UDPInterface:newBind(...)
+    return self:call("newBind", arg)
+end
+
+function UDPInterface:beginConnection(...)
+    return self:call("beginConnection", arg)
+end
+
+function UDPInterface:newBind_ai(address)
+    local response, err = self.ai:auth({
+        q = "UDPInterface_new",
+        bindAddress = address
+    })
+    if not response then
+        return nil, err
+    elseif response.error ~= "none" then
+        return nil, response.error
+    elseif response.interfaceNumber then
+        return response.interfaceNumber
+    else
+        return nil, "bad response format"
+    end
+end
+
+function UDPInterface:newBind_config(address)
+    local udpif       = self.config.contents.interfaces.UDPInterface
+    local new_interface = {
+        bind = address,
+        connectTo = {}
+    }
+    table.insert(udpif, new_interface)
+    return (#udpif - 1), new_interface
+end
+
+function UDPInterface:newBind_perm(...)
+    return
+        self:newBind_config(unpack(arg)),
+        self:newBind_ai(unpack(arg))
+end
+
+function UDPInterface:beginConnection_ai(pubkey, addr, password, interface)
+    local request = {
+        q = "UDPInterface_beginConnection",
+        publicKey = pubkey,
+        address   = addr,
+        password  = password
+    }
+    if interface then
+        request.interfaceNumber = interface
+    end
+
+    local response, err = self.ai:auth(request)
+    if not response then
+        return nil, err
+    elseif response.error == "none" then
+        -- Unfortunately, no real success indicator either.
+        return "No error"
+    else
+        return nil, response.error
+    end
+end
+
+function UDPInterface:beginConnection_config(pubkey, addr, password, interface)
+    local udpif       = self.config.contents.interfaces.UDPInterface
+    local connections = udpif[(interface or 0) + 1].connectTo
+    local this_conn   = {
+        password  = password,
+        publicKey = pubkey
+    }
+    connections[addr] = this_conn
+    return this_conn -- allows adding metadata fields afterwards
+end
+
+function UDPInterface:beginConnection_perm(...)
+    return
+        self:beginConnection_config(unpack(arg)),
+        self:beginConnection_ai(unpack(arg))
+end