diff --git a/package/gluon-alfred/Makefile b/package/gluon-alfred/Makefile new file mode 100644 index 00000000..bb6926a5 --- /dev/null +++ b/package/gluon-alfred/Makefile @@ -0,0 +1,32 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-alfred +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-alfred + SECTION:=gluon + CATEGORY:=Gluon + DEPENDS:=+gluon-core +gluon-announce +gluon-cron +alfred + TITLE:=Configure alfred +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-alfred/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-alfred)) diff --git a/package/gluon-alfred/files/lib/gluon/cron/alfred b/package/gluon-alfred/files/lib/gluon/cron/alfred new file mode 100644 index 00000000..9f79bc64 --- /dev/null +++ b/package/gluon-alfred/files/lib/gluon/cron/alfred @@ -0,0 +1 @@ +* * * * * /lib/gluon/announce/collect.lua nodeinfo | gzip | alfred -s 158; /lib/gluon/announce/collect.lua statistics | gzip | alfred -s 159; /lib/gluon/announce/collect.lua neighbours | gzip | alfred -s 160 diff --git a/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred b/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred new file mode 100755 index 00000000..8c52d8a6 --- /dev/null +++ b/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred @@ -0,0 +1,19 @@ +#!/usr/bin/lua + +local uci = require 'luci.model.uci' +local c = uci.cursor() + + +c:delete('alfred', 'alfred') +c:section('alfred', 'alfred', 'alfred', + { + interface = 'br-client', + mode = 'slave', + batmanif = 'bat0', + start_vis = '1', + run_facters = '0', + } +) + +c:save('alfred') +c:commit('alfred') diff --git a/package/gluon-announce/Makefile b/package/gluon-announce/Makefile new file mode 100644 index 00000000..3f62c889 --- /dev/null +++ b/package/gluon-announce/Makefile @@ -0,0 +1,32 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-announce +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-announce + SECTION:=gluon + CATEGORY:=Gluon + DEPENDS:=+gluon-core +luci-lib-json +lua-ethtool-stats + TITLE:=Lua scripts announcing various information +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-announce/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-announce)) diff --git a/package/gluon-announce/files/lib/gluon/announce/collect.lua b/package/gluon-announce/files/lib/gluon/announce/collect.lua new file mode 100755 index 00000000..e2974b49 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/collect.lua @@ -0,0 +1,10 @@ +#!/usr/bin/lua + +local announce = require 'gluon.announce' +local json = require 'luci.json' +local ltn12 = require 'luci.ltn12' + +local announce_dir = '/lib/gluon/announce/' .. arg[1] .. '.d' + +encoder = json.Encoder(announce.collect_dir(announce_dir)) +ltn12.pump.all(encoder:source(), ltn12.sink.file(io.stdout)) diff --git a/package/gluon-announce/files/lib/gluon/announce/neighbours.d/node_id b/package/gluon-announce/files/lib/gluon/announce/neighbours.d/node_id new file mode 100644 index 00000000..66303f4c --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/neighbours.d/node_id @@ -0,0 +1 @@ +return require('gluon.util').node_id() diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/model b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/model new file mode 100644 index 00000000..aee3cd81 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/model @@ -0,0 +1 @@ +return require('platform_info').get_model() diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hostname b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hostname new file mode 100644 index 00000000..7d4f0521 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hostname @@ -0,0 +1 @@ +return uci:get_first('system', 'system', 'hostname') diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/network/mac b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/network/mac new file mode 100644 index 00000000..049eea58 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/network/mac @@ -0,0 +1 @@ +return require('gluon.sysconfig').primary_mac diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/node_id b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/node_id new file mode 100644 index 00000000..66303f4c --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/node_id @@ -0,0 +1 @@ +return require('gluon.util').node_id() diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware new file mode 100644 index 00000000..cf50f79f --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware @@ -0,0 +1,4 @@ +return { + base = 'gluon-' .. util.trim(fs.readfile('/lib/gluon/gluon-version')), + release = util.trim(fs.readfile('/lib/gluon/release')), +} diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code new file mode 100644 index 00000000..876fb6d8 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code @@ -0,0 +1,3 @@ +local site = require 'gluon.site_config' + +return site.site_code diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime b/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime new file mode 100644 index 00000000..845de268 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime @@ -0,0 +1 @@ +return tonumber(fs.readfile('/proc/uptime'):match('^[^ ]+ ([^ ]+)')) diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg b/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg new file mode 100644 index 00000000..d79973aa --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg @@ -0,0 +1 @@ +return tonumber(fs.readfile('/proc/loadavg'):match('^([^ ]+) ')) diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory b/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory new file mode 100644 index 00000000..7b07a107 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory @@ -0,0 +1,13 @@ +local data = fs.readfile('/proc/meminfo') + +local fields = {} +for k, v in data:gmatch('([^\n:]+):%s*(%d+) kB') do + fields[k] = tonumber(v) +end + +return { + total = fields.MemTotal, + free = fields.MemFree, + buffers = fields.Buffers, + cached = fields.Cached, +} diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/node_id b/package/gluon-announce/files/lib/gluon/announce/statistics.d/node_id new file mode 100644 index 00000000..66303f4c --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/node_id @@ -0,0 +1 @@ +return require('gluon.util').node_id() diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes b/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes new file mode 100644 index 00000000..33ecff66 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes @@ -0,0 +1,3 @@ +local running, total = fs.readfile('/proc/loadavg'):match('^[^ ]+ [^ ]+ [^ ]+ (%d+)/(%d+)') + +return { running = tonumber(running), total = tonumber(total) } diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/rootfs_usage b/package/gluon-announce/files/lib/gluon/announce/statistics.d/rootfs_usage new file mode 100644 index 00000000..8426e9e1 --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/rootfs_usage @@ -0,0 +1,4 @@ +local fs = require "nixio.fs" + +local st = fs.statvfs("/") +return 1 - st.bfree / st.blocks diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime b/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime new file mode 100644 index 00000000..0bc45bea --- /dev/null +++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime @@ -0,0 +1 @@ +return tonumber(fs.readfile('/proc/uptime'):match('^([^ ]+) ')) diff --git a/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua b/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua new file mode 100755 index 00000000..7f3f62e2 --- /dev/null +++ b/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua @@ -0,0 +1,33 @@ +#!/usr/bin/lua + +module('gluon.announce', package.seeall) + +fs = require 'luci.fs' +uci = require('luci.model.uci').cursor() +util = require 'luci.util' + +local function collect_entry(entry) + if fs.isdirectory(entry) then + return collect_dir(entry) + else + return setfenv(loadfile(entry), _M)() + end +end + +function collect_dir(dir) + local ret = {} + + for _, entry in ipairs(fs.dir(dir)) do + if entry:sub(1, 1) ~= '.' then + local ok, val = pcall(collect_entry, dir .. '/' .. entry) + if ok then + ret[entry] = val + else + io.stderr:write(val, '\n') + end + end + end + + return ret +end + diff --git a/package/gluon-announced/Makefile b/package/gluon-announced/Makefile new file mode 100644 index 00000000..1f802dc2 --- /dev/null +++ b/package/gluon-announced/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-announced +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-announced + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=announced support + DEPENDS:=+gluon-announce +endef + +define Package/gluon-announced/description + Gluon community wifi mesh firmware framework: announced support +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Configure +endef + +define Build/Compile + CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) +endef + +define Package/gluon-announced/install + $(CP) ./files/* $(1)/ + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-announced $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,gluon-announced)) diff --git a/package/gluon-announced/files/etc/hotplug.d/iface/10-gluon-announced b/package/gluon-announced/files/etc/hotplug.d/iface/10-gluon-announced new file mode 100644 index 00000000..4e8635bd --- /dev/null +++ b/package/gluon-announced/files/etc/hotplug.d/iface/10-gluon-announced @@ -0,0 +1,45 @@ +#!/bin/sh + +. /usr/share/libubox/jshn.sh +. /lib/functions/service.sh + +DEVLIST=/var/run/gluon-announced.devs +DAEMON=/usr/bin/gluon-announced + +ifname_to_dev () { + json_load "$(ubus call network.interface.$1 status)" + json_get_var dev device + + echo "$dev" +} + +restart_announced () { + SERVICE_USE_PID=1 + SERVICE_WRITE_PID=1 + SERVICE_DAEMONIZE=1 + + DEVS=$(cat $DEVLIST | while read dev iface;do echo -n " -i $dev";done) + + service_stop $DAEMON + service_start $DAEMON -g ff02:0:0:0:0:0:2:1001 -p 1001 -s '/lib/gluon/announce/collect.lua nodeinfo' $DEVS +} + +case "$ACTION" in + ifdown) + sed -i "/$INTERFACE/d" $DEVLIST + ;; + ifup) + DEVICE=$(ifname_to_dev $INTERFACE) + MESH=$(cat /sys/class/net/$DEVICE/batman_adv/mesh_iface) + + [ $MESH = "bat0" ] || exit 0 + + DEVS="$(cat $DEVLIST; echo $DEVICE $INTERFACE)" + + echo "$DEVS" | sort | uniq > $DEVLIST + + restart_announced + + ;; +esac + diff --git a/package/gluon-announced/src/Makefile b/package/gluon-announced/src/Makefile new file mode 100644 index 00000000..73e7a9e2 --- /dev/null +++ b/package/gluon-announced/src/Makefile @@ -0,0 +1,6 @@ +all: gluon-announced + +gluon-announced: gluon-announced.c + +clean: + rm gluon-announced diff --git a/package/gluon-announced/src/gluon-announced.c b/package/gluon-announced/src/gluon-announced.c new file mode 100644 index 00000000..27de6ece --- /dev/null +++ b/package/gluon-announced/src/gluon-announced.c @@ -0,0 +1,221 @@ +/* + Copyright (c) 2014, Nils Schneider + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void usage() { + puts("Usage: gluon-announced [-h] -g -p -i [-i ..] -s + + +<% end %> +
+ <% if self.title and #self.title > 0 then %>

<%=self.title%>

<% end %> + <% if self.description and #self.description > 0 then %>
<%=self.description%>
<% end %> + <% self:render_children() %> +
+
+<%- if self.message then %> +
<%=self.message%>
+<%- end %> +<%- if self.errmessage then %> +
<%=self.errmessage%>
+<%- end %> +<% if not self.embedded then %> +
+<%- + if type(self.hidden) == "table" then + for k, v in pairs(self.hidden) do +-%> + +<%- + end + end +%> + + +
+ +<% end %> diff --git a/package/gluon-config-mode-core/files/usr/lib/lua/luci/view/gluon-config-mode/reboot.htm b/package/gluon-config-mode-core/files/usr/lib/lua/luci/view/gluon-config-mode/reboot.htm new file mode 100644 index 00000000..e8f32d99 --- /dev/null +++ b/package/gluon-config-mode-core/files/usr/lib/lua/luci/view/gluon-config-mode/reboot.htm @@ -0,0 +1,17 @@ + + + + + + <%=hostname%> is rebooting + + + +
+
+

<%:Your node's setup is now complete.%>

+ <% for k, v in ipairs(parts) do v() end %> +
+
+ + diff --git a/package/gluon-config-mode-core/i18n/de.po b/package/gluon-config-mode-core/i18n/de.po new file mode 100644 index 00000000..58d23894 --- /dev/null +++ b/package/gluon-config-mode-core/i18n/de.po @@ -0,0 +1,24 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"PO-Revision-Date: 2015-03-19 02:07+0100\n" +"Last-Translator: Matthias Schiffer \n" +"Language-Team: German\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#, fuzzy +msgid "Save & restart" +msgstr "Speichern & Neustarten" + +msgid "Welcome!" +msgstr "Willkommen!" + +msgid "Wizard" +msgstr "Wizard" + +msgid "Your node's setup is now complete." +msgstr "Dein Knoten ist nun fertig eingerichtet." diff --git a/package/gluon-config-mode-core/i18n/gluon-config-mode-core.pot b/package/gluon-config-mode-core/i18n/gluon-config-mode-core.pot new file mode 100644 index 00000000..a80d3b97 --- /dev/null +++ b/package/gluon-config-mode-core/i18n/gluon-config-mode-core.pot @@ -0,0 +1,14 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Save & restart" +msgstr "" + +msgid "Welcome!" +msgstr "" + +msgid "Wizard" +msgstr "" + +msgid "Your node's setup is now complete." +msgstr "" diff --git a/package/gluon-config-mode-geo-location/Makefile b/package/gluon-config-mode-geo-location/Makefile new file mode 100644 index 00000000..4b5204e0 --- /dev/null +++ b/package/gluon-config-mode-geo-location/Makefile @@ -0,0 +1,36 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-config-mode-geo-location +PKG_VERSION:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG) + + +define Package/gluon-config-mode-geo-location + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Set geographic location of a node + DEPENDS:=+gluon-config-mode-core +gluon-node-info +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile + $(call GluonBuildI18N,gluon-config-mode-geo-location,i18n) +endef + +define Package/gluon-config-mode-geo-location/install + $(CP) ./files/* $(1)/ + $(call GluonInstallI18N,gluon-config-mode-geo-location,$(1)) +endef + +$(eval $(call BuildPackage,gluon-config-mode-geo-location)) diff --git a/package/gluon-config-mode-geo-location/files/lib/gluon/config-mode/wizard/0400-geo-location.lua b/package/gluon-config-mode-geo-location/files/lib/gluon/config-mode/wizard/0400-geo-location.lua new file mode 100644 index 00000000..a3ac089d --- /dev/null +++ b/package/gluon-config-mode-geo-location/files/lib/gluon/config-mode/wizard/0400-geo-location.lua @@ -0,0 +1,60 @@ +local cbi = require "luci.cbi" +local i18n = require "luci.i18n" +local uci = luci.model.uci.cursor() + +local M = {} + +function M.section(form) + local s = form:section(cbi.SimpleSection, nil, i18n.translate( + 'If you want the location of your node to be displayed on the map, ' + .. 'you can enter its coordinates here. Specifying the altitude ' + .. 'is optional and should only be done if a proper value is known.')) + + + local o + + o = s:option(cbi.Flag, "_location", i18n.translate("Show node on the map")) + o.default = uci:get_first("gluon-node-info", "location", "share_location", o.disabled) + o.rmempty = false + + o = s:option(cbi.Value, "_latitude", i18n.translate("Latitude")) + o.default = uci:get_first("gluon-node-info", "location", "latitude") + o:depends("_location", "1") + o.rmempty = false + o.datatype = "float" + o.description = i18n.translatef("e.g. %s", "53.873621") + + o = s:option(cbi.Value, "_longitude", i18n.translate("Longitude")) + o.default = uci:get_first("gluon-node-info", "location", "longitude") + o:depends("_location", "1") + o.rmempty = false + o.datatype = "float" + o.description = i18n.translatef("e.g. %s", "10.689901") + + o = s:option(cbi.Value, "_altitude", i18n.translate("Altitude")) + o.default = uci:get_first("gluon-node-info", "location", "altitude") + o:depends("_location", "1") + o.rmempty = true + o.datatype = "float" + o.description = i18n.translatef("e.g. %s", "11.51") + +end + +function M.handle(data) + local sname = uci:get_first("gluon-node-info", "location") + + uci:set("gluon-node-info", sname, "share_location", data._location) + if data._location and data._latitude ~= nil and data._longitude ~= nil then + uci:set("gluon-node-info", sname, "latitude", data._latitude) + uci:set("gluon-node-info", sname, "longitude", data._longitude) + if data._altitude ~= nil then + uci:set("gluon-node-info", sname, "altitude", data._altitude) + else + uci:delete("gluon-node-info", sname, "altitude") + end + end + uci:save("gluon-node-info") + uci:commit("gluon-node-info") +end + +return M diff --git a/package/gluon-config-mode-geo-location/i18n/de.po b/package/gluon-config-mode-geo-location/i18n/de.po new file mode 100644 index 00000000..e83443dd --- /dev/null +++ b/package/gluon-config-mode-geo-location/i18n/de.po @@ -0,0 +1,36 @@ +msgid "" +msgstr "" +"Project-Id-Version: gluon-config-mode-geo-location\n" +"PO-Revision-Date: 2015-03-23 02:18+0100\n" +"Last-Translator: Martin Weinelt \n" +"Language-Team: German\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "" +"If you want the location of your node to be displayed on the map, you can " +"enter its coordinates here. Specifying the altitude is optional and should " +"only be done if a proper value is known." +msgstr "" +"Um deinen Knoten auf der Karte anzeigen zu können, benötigen wir seine " +"Koordinaten. Hier hast du die Möglichkeit, diese zu hinterlegen. Die " +"Höhenangabe ist optional und sollte nur gesetzt werden, wenn ein exakter " +"Wert bekannt ist." + +msgid "Latitude" +msgstr "Breitengrad" + +msgid "Longitude" +msgstr "Längengrad" + +msgid "Altitude" +msgstr "Höhenmeter über Normalnull" + +msgid "Show node on the map" +msgstr "Knoten auf der Karte anzeigen" + +msgid "e.g. %s" +msgstr "z.B. %s" diff --git a/package/gluon-config-mode-geo-location/i18n/gluon-config-mode-geo-location.pot b/package/gluon-config-mode-geo-location/i18n/gluon-config-mode-geo-location.pot new file mode 100644 index 00000000..84800c8a --- /dev/null +++ b/package/gluon-config-mode-geo-location/i18n/gluon-config-mode-geo-location.pot @@ -0,0 +1,20 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "" +"If you want the location of your node to be displayed on the map, you can " +"enter its coordinates here. Specifying the altitude is optional and should " +"only be done if a proper value is known." +msgstr "" + +msgid "Latitude" +msgstr "" + +msgid "Longitude" +msgstr "" + +msgid "Show node on the map" +msgstr "" + +msgid "e.g. %s" +msgstr "" diff --git a/package/gluon-config-mode-hostname/Makefile b/package/gluon-config-mode-hostname/Makefile new file mode 100644 index 00000000..255740e4 --- /dev/null +++ b/package/gluon-config-mode-hostname/Makefile @@ -0,0 +1,36 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-config-mode-hostname +PKG_VERSION:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG) + + +define Package/gluon-config-mode-hostname + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Set the hostname + DEPENDS:=+gluon-config-mode-core +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile + $(call GluonBuildI18N,gluon-config-mode-hostname,i18n) +endef + +define Package/gluon-config-mode-hostname/install + $(CP) ./files/* $(1)/ + $(call GluonInstallI18N,gluon-config-mode-hostname,$(1)) +endef + +$(eval $(call BuildPackage,gluon-config-mode-hostname)) diff --git a/package/gluon-config-mode-hostname/files/lib/gluon/config-mode/wizard/0100-hostname.lua b/package/gluon-config-mode-hostname/files/lib/gluon/config-mode/wizard/0100-hostname.lua new file mode 100644 index 00000000..cf8bbf89 --- /dev/null +++ b/package/gluon-config-mode-hostname/files/lib/gluon/config-mode/wizard/0100-hostname.lua @@ -0,0 +1,21 @@ +local cbi = require "luci.cbi" +local i18n = require "luci.i18n" +local uci = luci.model.uci.cursor() + +local M = {} + +function M.section(form) + local s = form:section(cbi.SimpleSection, nil, nil) + local o = s:option(cbi.Value, "_hostname", i18n.translate("Node name")) + o.value = uci:get_first("system", "system", "hostname") + o.rmempty = false + o.datatype = "hostname" +end + +function M.handle(data) + uci:set("system", uci:get_first("system", "system"), "hostname", data._hostname) + uci:save("system") + uci:commit("system") +end + +return M diff --git a/package/gluon-config-mode-hostname/i18n/de.po b/package/gluon-config-mode-hostname/i18n/de.po new file mode 100644 index 00000000..f1d2a765 --- /dev/null +++ b/package/gluon-config-mode-hostname/i18n/de.po @@ -0,0 +1,14 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: PACKAGE VERSION\n" +"PO-Revision-Date: 2015-03-19 00:54+0100\n" +"Last-Translator: Matthias Schiffer \n" +"Language-Team: German\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Node name" +msgstr "Name dieses Knotens" diff --git a/package/gluon-config-mode-hostname/i18n/gluon-config-mode-hostname.pot b/package/gluon-config-mode-hostname/i18n/gluon-config-mode-hostname.pot new file mode 100644 index 00000000..dbaafc0b --- /dev/null +++ b/package/gluon-config-mode-hostname/i18n/gluon-config-mode-hostname.pot @@ -0,0 +1,5 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Node name" +msgstr "" diff --git a/package/gluon-config-mode-mesh-vpn/Makefile b/package/gluon-config-mode-mesh-vpn/Makefile new file mode 100644 index 00000000..4792e3d3 --- /dev/null +++ b/package/gluon-config-mode-mesh-vpn/Makefile @@ -0,0 +1,36 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-config-mode-mesh-vpn +PKG_VERSION:=2 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG) + + +define Package/gluon-config-mode-mesh-vpn + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Toggle mesh-vpn and bandwidth limit + DEPENDS:=+gluon-config-mode-core +gluon-mesh-vpn-fastd +gluon-simple-tc +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile + $(call GluonBuildI18N,gluon-config-mode-mesh-vpn,i18n) +endef + +define Package/gluon-config-mode-mesh-vpn/install + $(CP) ./files/* $(1)/ + $(call GluonInstallI18N,gluon-config-mode-mesh-vpn,$(1)) +endef + +$(eval $(call BuildPackage,gluon-config-mode-mesh-vpn)) diff --git a/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua new file mode 100644 index 00000000..1b32c731 --- /dev/null +++ b/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua @@ -0,0 +1,29 @@ +local uci = luci.model.uci.cursor() +local meshvpn_enabled = uci:get("fastd", "mesh_vpn", "enabled", "0") + +if meshvpn_enabled ~= "1" then + return nil +else + local i18n = require "luci.i18n" + local util = require "luci.util" + local site = require 'gluon.site_config' + local sysconfig = require 'gluon.sysconfig' + + local pubkey = util.trim(util.exec("/etc/init.d/fastd show_key " .. "mesh_vpn")) + local hostname = uci:get_first("system", "system", "hostname") + + local msg = [[

]] .. i18n.translate('gluon-config-mode:pubkey') .. [[

+
+ # <%= hostname %> +
+ <%= pubkey %> +
]] + + return function () + luci.template.render_string(msg, { pubkey=pubkey + , hostname=hostname + , site=site + , sysconfig=sysconfig + }) + end +end diff --git a/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua new file mode 100644 index 00000000..669a7bc3 --- /dev/null +++ b/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua @@ -0,0 +1,64 @@ +local cbi = require "luci.cbi" +local i18n = require "luci.i18n" +local uci = luci.model.uci.cursor() + +local M = {} + +function M.section(form) + local msg = i18n.translate('Your internet connection can be used to establish an ' .. + 'encrypted connection with other nodes. ' .. + 'Enable this option if there are no other nodes reachable ' .. + 'over WLAN in your vicinity or you want to make a part of ' .. + 'your connection\'s bandwidth available for the network. You can limit how ' .. + 'much bandwidth the node will use at most.') + local s = form:section(cbi.SimpleSection, nil, msg) + + local o + + o = s:option(cbi.Flag, "_meshvpn", i18n.translate("Use internet connection (mesh VPN)")) + o.default = uci:get_bool("fastd", "mesh_vpn", "enabled") and o.enabled or o.disabled + o.rmempty = false + + o = s:option(cbi.Flag, "_limit_enabled", i18n.translate("Limit bandwidth")) + o:depends("_meshvpn", "1") + o.default = uci:get_bool("gluon-simple-tc", "mesh_vpn", "enabled") and o.enabled or o.disabled + o.rmempty = false + + o = s:option(cbi.Value, "_limit_ingress", i18n.translate("Downstream (kbit/s)")) + o:depends("_limit_enabled", "1") + o.value = uci:get("gluon-simple-tc", "mesh_vpn", "limit_ingress") + o.rmempty = false + o.datatype = "integer" + + o = s:option(cbi.Value, "_limit_egress", i18n.translate("Upstream (kbit/s)")) + o:depends("_limit_enabled", "1") + o.value = uci:get("gluon-simple-tc", "mesh_vpn", "limit_egress") + o.rmempty = false + o.datatype = "integer" +end + +function M.handle(data) + uci:set("fastd", "mesh_vpn", "enabled", data._meshvpn) + uci:save("fastd") + uci:commit("fastd") + + -- checks for nil needed due to o:depends(...) + if data._limit_enabled ~= nil then + uci:set("gluon-simple-tc", "mesh_vpn", "interface") + uci:set("gluon-simple-tc", "mesh_vpn", "enabled", data._limit_enabled) + uci:set("gluon-simple-tc", "mesh_vpn", "ifname", "mesh-vpn") + + if data._limit_ingress ~= nil then + uci:set("gluon-simple-tc", "mesh_vpn", "limit_ingress", data._limit_ingress) + end + + if data._limit_egress ~= nil then + uci:set("gluon-simple-tc", "mesh_vpn", "limit_egress", data._limit_egress) + end + + uci:commit("gluon-simple-tc") + uci:commit("gluon-simple-tc") + end +end + +return M diff --git a/package/gluon-config-mode-mesh-vpn/i18n/de.po b/package/gluon-config-mode-mesh-vpn/i18n/de.po new file mode 100644 index 00000000..8c613aa0 --- /dev/null +++ b/package/gluon-config-mode-mesh-vpn/i18n/de.po @@ -0,0 +1,36 @@ +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"PO-Revision-Date: 2015-03-19 22:05+0100\n" +"Last-Translator: Matthias Schiffer \n" +"Language-Team: German\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Downstream (kbit/s)" +msgstr "Downstream (kbit/s)" + +msgid "Limit bandwidth" +msgstr "Bandbreite begrenzen" + +msgid "Upstream (kbit/s)" +msgstr "Upstream (kbit/s)" + +msgid "Use internet connection (mesh VPN)" +msgstr "Internetverbindung nutzen (Mesh-VPN)" + +msgid "" +"Your internet connection can be used to establish an encrypted connection " +"with other nodes. Enable this option if there are no other nodes reachable " +"over WLAN in your vicinity or you want to make a part of your connection's " +"bandwidth available for the network. You can limit how much bandwidth the " +"node will use at most." +msgstr "" +"Dein Knoten kann deine Internetverbindung nutzen um darüber eine " +"verschlüsselte Verbindung zu anderen Knoten aufzubauen. Die dafür " +"genutzte Bandbreite kannst du beschränken. Aktiviere die Option, falls keine " +"per WLAN erreichbaren Nachbarknoten in deiner Nähe sind oder du deine " +"Internetverbindung für das Mesh-Netzwerk zur Verfügung stellen möchtest." diff --git a/package/gluon-config-mode-mesh-vpn/i18n/gluon-config-mode-mesh-vpn.pot b/package/gluon-config-mode-mesh-vpn/i18n/gluon-config-mode-mesh-vpn.pot new file mode 100644 index 00000000..52e2eef8 --- /dev/null +++ b/package/gluon-config-mode-mesh-vpn/i18n/gluon-config-mode-mesh-vpn.pot @@ -0,0 +1,22 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Downstream (kbit/s)" +msgstr "" + +msgid "Limit bandwidth" +msgstr "" + +msgid "Upstream (kbit/s)" +msgstr "" + +msgid "Use internet connection (mesh VPN)" +msgstr "" + +msgid "" +"Your internet connection can be used to establish an encrypted connection " +"with other nodes. Enable this option if there are no other nodes reachable " +"over WLAN in your vicinity or you want to make a part of your connection's " +"bandwidth available for the network. You can limit how much bandwidth the " +"node will use at most." +msgstr "" diff --git a/package/gluon-core/Makefile b/package/gluon-core/Makefile new file mode 100644 index 00000000..2c632a00 --- /dev/null +++ b/package/gluon-core/Makefile @@ -0,0 +1,59 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-core +PKG_VERSION:=3 +PKG_RELEASE:=$(GLUON_VERSION) + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-core + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Base files of Gluon + DEPENDS:=+gluon-site +lua-platform-info +luci-lib-nixio +odhcp6c +firewall +endef + + +define LangConfig +config GLUON_LANG_$(1) + bool "$(GLUON_LANG_$(1)) language support" + depends on PACKAGE_gluon-core + default n + +endef + + +define Package/gluon-core/config +$(foreach lang,$(GLUON_SUPPORTED_LANGS),$(call LangConfig,$(lang))) +endef + + +define Package/gluon-core/description + Gluon community wifi mesh firmware framework: core +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-core/install + $(CP) ./files/* $(1)/ + + $(INSTALL_DIR) $(1)/lib/gluon + echo "$(GLUON_VERSION)" > $(1)/lib/gluon/gluon-version +endef + +define Package/gluon-core/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-core)) diff --git a/package/gluon-core/check_site.lua b/package/gluon-core/check_site.lua new file mode 100644 index 00000000..1a6987a0 --- /dev/null +++ b/package/gluon-core/check_site.lua @@ -0,0 +1,10 @@ +need_string 'site_code' +need_string 'site_name' + +need_string('hostname_prefix', false) +need_string 'timezone' + +need_string_array('ntp_servers', false) + +need_string_match('prefix4', '^%d+.%d+.%d+.%d+/%d+$') +need_string_match('prefix6', '^[%x:]+/%d+$') diff --git a/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade b/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade new file mode 100755 index 00000000..a12ce78e --- /dev/null +++ b/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade @@ -0,0 +1,5 @@ +#!/bin/sh + +for script in /lib/gluon/upgrade/*; do + "$script" +done diff --git a/package/gluon-core/files/lib/gluon/core/sysconfig/.keep b/package/gluon-core/files/lib/gluon/core/sysconfig/.keep new file mode 100644 index 00000000..e69de29b diff --git a/package/gluon-core/files/lib/gluon/upgrade/001-upgrade b/package/gluon-core/files/lib/gluon/upgrade/001-upgrade new file mode 100755 index 00000000..6caba148 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/001-upgrade @@ -0,0 +1,10 @@ +#!/usr/bin/lua + +local fs = require 'luci.fs' +local sysconfig = require 'gluon.sysconfig' + + +if fs.isfile('/lib/gluon/version/core') and not sysconfig.gluon_version then + -- This isn't an initial upgrade, so set gluon_version + sysconfig.gluon_version = '' +end diff --git a/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac b/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac new file mode 100755 index 00000000..70aee39d --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac @@ -0,0 +1,42 @@ +#!/usr/bin/lua + +local sysconfig = require 'gluon.sysconfig' + + +if sysconfig.primary_mac then + os.exit(0) +end + + +local platform = require 'gluon.platform' + +local fs = require 'luci.fs' +local util = require 'luci.util' + + +local try_files = { + '/sys/class/ieee80211/phy0/macaddress', + '/sys/class/net/eth0/address', +} + +if platform.match('ar71xx', 'generic', {'tl-wdr3600', 'tl-wdr4300'}) then + table.insert(try_files, 1, '/sys/class/ieee80211/phy1/macaddress') +end + +if platform.match('ar71xx', 'generic', {'unifi-outdoor-plus'}) then + table.insert(try_files, 1, '/sys/class/net/eth0/address') +end + +if platform.match('ar71xx', 'generic', {'archer-c5', 'archer-c7'}) then + table.insert(try_files, 1, '/sys/class/net/eth1/address') +end + + +for _, file in ipairs(try_files) do + local addr = fs.readfile(file) + + if addr then + sysconfig.primary_mac = util.trim(addr) + break + end +end diff --git a/package/gluon-core/files/lib/gluon/upgrade/020-interfaces b/package/gluon-core/files/lib/gluon/upgrade/020-interfaces new file mode 100755 index 00000000..a051c738 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/020-interfaces @@ -0,0 +1,36 @@ +#!/usr/bin/lua + +local sysconfig = require 'gluon.sysconfig' +local gluon_util = require 'gluon.util' +local platform = require 'gluon.platform' + +local uci = require('luci.model.uci').cursor() + + +if not (sysconfig.lan_ifname or sysconfig.wan_ifname) then + local function iface_exists(name) + return (gluon_util.exec('ip', 'link', 'show', 'dev', (name:gsub('%..*$', ''))) == 0) + end + + + local lan_ifname = uci:get('network', 'lan', 'ifname') + local wan_ifname = uci:get('network', 'wan', 'ifname') + + if platform.match('ar71xx', 'generic', {'cpe510', 'nanostation-m', 'nanostation-m-xw', 'unifi-outdoor-plus'}) then + lan_ifname, wan_ifname = wan_ifname, lan_ifname + end + + if wan_ifname and iface_exists(wan_ifname) then + sysconfig.wan_ifname = wan_ifname + sysconfig.lan_ifname = lan_ifname + else + sysconfig.wan_ifname = lan_ifname + end + + + uci:delete('network', 'lan') + uci:delete('network', 'wan') + + uci:save('network') + uci:commit('network') +end diff --git a/package/gluon-core/files/lib/gluon/upgrade/030-system b/package/gluon-core/files/lib/gluon/upgrade/030-system new file mode 100755 index 00000000..d7a66605 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/030-system @@ -0,0 +1,18 @@ +#!/usr/bin/lua + +local sysconfig = require 'gluon.sysconfig' + +-- Initial +if not sysconfig.gluon_version then + local site = require 'gluon.site_config' + local util = require 'gluon.util' + local uci = require('luci.model.uci').cursor() + + local system = uci:get_first('system', 'system') + + uci:set('system', system, 'hostname', (site.hostname_prefix or '') .. util.node_id()) + uci:set('system', system, 'timezone', site.timezone) + + uci:save('system') + uci:commit('system') +end diff --git a/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq b/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq new file mode 100755 index 00000000..3636fbf6 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ -e /etc/dnsmasq.conf ]; then + sed -i -e '/^conf-dir=\/lib\/gluon\/dnsmasq\.d$/d' -e '/^conf-dir=\/var\/gluon\/dnsmasq\.d$/d' /etc/dnsmasq.conf +fi diff --git a/package/gluon-core/files/lib/gluon/upgrade/110-network b/package/gluon-core/files/lib/gluon/upgrade/110-network new file mode 100755 index 00000000..1fd78f3c --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/110-network @@ -0,0 +1,58 @@ +#!/usr/bin/lua + +local uci = require('luci.model.uci').cursor() +local sysctl = require 'gluon.sysctl' +local sysconfig = require 'gluon.sysconfig' + + +uci:section('network', 'interface', 'wan', + { + ifname = sysconfig.wan_ifname, + type = 'bridge', + peerdns = 0, + auto = 1, + } +) + +if not uci:get('network', 'wan', 'proto') then + uci:set('network', 'wan', 'proto', 'dhcp') +end + + +uci:section('network', 'interface', 'wan6', + { + ifname = 'br-wan', + peerdns = 0, + ip6table = 1, + } +) + +if not uci:get('network', 'wan6', 'proto') then + uci:set('network', 'wan6', 'proto', 'dhcpv6') +end + + +uci:section('network', 'rule6', 'wan6_lookup', + { + mark = '0x01/0x01', + lookup = 1, + } +) + +uci:section('network', 'route6', 'wan6_unreachable', + { + type = 'unreachable', + interface = 'loopback', + target = '::/0', + gateway = '::', + table = 1, + metric = 65535, + } +) + +uci:save('network') +uci:commit('network') + + +sysctl.set('net.ipv6.conf.all.accept_ra', 0) +sysctl.set('net.ipv6.conf.default.accept_ra', 0) diff --git a/package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers b/package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers new file mode 100755 index 00000000..2b3a2df6 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers @@ -0,0 +1,14 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require 'luci.model.uci' + +if not site.ntp_servers or #site.ntp_servers == 0 then + os.exit(0) +end + +local c = uci.cursor() +c:delete('system', 'ntp', 'server') +c:set_list('system', 'ntp', 'server', site.ntp_servers) +c:save('system') +c:commit('system') diff --git a/package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom b/package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom new file mode 100755 index 00000000..48cfc5a8 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom @@ -0,0 +1,5 @@ +#!/usr/bin/lua + +local sysctl = require 'gluon.sysctl' + +sysctl.set('vm.panic_on_oom', 1) diff --git a/package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules b/package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules new file mode 100755 index 00000000..792e06a2 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules @@ -0,0 +1,30 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require 'luci.model.uci' + +local c = uci.cursor() + + +local function reject_input_on_wan(zone) + if zone.name == 'wan' then + c:set('firewall', zone['.name'], 'input', 'REJECT') + c:set('firewall', zone['.name'], 'conntrack', '1') + end + + return true +end +c:foreach('firewall', 'zone', reject_input_on_wan) + +c:section('firewall', 'rule', 'wan_ssh', + { + name = 'wan_ssh', + src = 'wan', + dest_port = '22', + proto = 'tcp', + target = 'ACCEPT', + } +) + +c:save('firewall') +c:commit('firewall') diff --git a/package/gluon-core/files/lib/gluon/upgrade/200-wireless b/package/gluon-core/files/lib/gluon/upgrade/200-wireless new file mode 100755 index 00000000..219e505d --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/200-wireless @@ -0,0 +1,12 @@ +#!/usr/bin/lua + +local sysconfig = require 'gluon.sysconfig' + +-- Initial +if not sysconfig.gluon_version then + local uci = require('luci.model.uci').cursor() + + uci:delete_all('wireless', 'wifi-iface') + uci:save('wireless') + uci:commit('wireless') +end diff --git a/package/gluon-core/files/lib/gluon/upgrade/999-version b/package/gluon-core/files/lib/gluon/upgrade/999-version new file mode 100755 index 00000000..62f08206 --- /dev/null +++ b/package/gluon-core/files/lib/gluon/upgrade/999-version @@ -0,0 +1,11 @@ +#!/usr/bin/lua + +local sysconfig = require 'gluon.sysconfig' + +local fs = require 'luci.fs' +local util = require 'luci.util' + + +-- Save the Gluon version in the sysconfig so we know which version we +-- upgraded from after the next upgrade +sysconfig.gluon_version = util.trim(fs.readfile('/lib/gluon/gluon-version')) diff --git a/package/gluon-core/files/lib/upgrade/keep.d/gluon b/package/gluon-core/files/lib/upgrade/keep.d/gluon new file mode 100644 index 00000000..bc82c775 --- /dev/null +++ b/package/gluon-core/files/lib/upgrade/keep.d/gluon @@ -0,0 +1 @@ +/lib/gluon/core/sysconfig/ diff --git a/package/gluon-core/files/usr/lib/lua/gluon/platform.lua b/package/gluon-core/files/usr/lib/lua/gluon/platform.lua new file mode 100644 index 00000000..3d56f081 --- /dev/null +++ b/package/gluon-core/files/usr/lib/lua/gluon/platform.lua @@ -0,0 +1,31 @@ +local platform_info = require 'platform_info' +local util = require 'luci.util' + +local setmetatable = setmetatable + + +module 'gluon.platform' + +setmetatable(_M, + { + __index = platform_info, + } +) + +function match(target, subtarget, boards) + if get_target() ~= target then + return false + end + + if get_subtarget() ~= subtarget then + return false + end + + if not util.contains(boards, get_board_name()) then + return false + end + + return true +end + + diff --git a/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua b/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua new file mode 100644 index 00000000..cf151483 --- /dev/null +++ b/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua @@ -0,0 +1,21 @@ +local config = os.getenv('GLUON_SITE_CONFIG') or '/lib/gluon/site.conf' + +local function loader() + coroutine.yield('return ') + coroutine.yield(io.open(config):read('*a')) +end + +-- setfenv doesn't work with Lua 5.2 anymore, but we're using 5.1 +local site_config = setfenv(assert(load(coroutine.wrap(loader), 'site.conf')), {})() + +local setmetatable = setmetatable + +module 'gluon.site_config' + +setmetatable(_M, + { + __index = site_config, + } +) + +return _M diff --git a/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua b/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua new file mode 100644 index 00000000..ff61f05b --- /dev/null +++ b/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua @@ -0,0 +1,34 @@ +local sysconfigdir = '/lib/gluon/core/sysconfig/' + +local function get(_, name) + local f = io.open(sysconfigdir .. name) + if f then + local ret = f:read('*line') + f:close() + return (ret or '') + end + return nil +end + +local function set(_, name, val) + if val then + local f = io.open(sysconfigdir .. name, 'w+') + f:write(val) + f:close() + else + os.remove(sysconfigdir .. name) + end +end + +local setmetatable = setmetatable + +module 'gluon.sysconfig' + +setmetatable(_M, + { + __index = get, + __newindex = set, + } +) + +return _M diff --git a/package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua b/package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua new file mode 100644 index 00000000..44b0c217 --- /dev/null +++ b/package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua @@ -0,0 +1,8 @@ +local util = require 'gluon.util' + + +module 'gluon.sysctl' + +function set(name, value) + util.replace_prefix('/etc/sysctl.conf', name .. '=', name .. '=' .. value .. '\n') +end diff --git a/package/gluon-core/files/usr/lib/lua/gluon/users.lua b/package/gluon-core/files/usr/lib/lua/gluon/users.lua new file mode 100644 index 00000000..8e618d88 --- /dev/null +++ b/package/gluon-core/files/usr/lib/lua/gluon/users.lua @@ -0,0 +1,33 @@ +local util = require 'gluon.util' + +local os = os +local string = string + + +module 'gluon.users' + +function add_user(username, uid, gid) + util.lock('/var/lock/passwd') + util.replace_prefix('/etc/passwd', username .. ':', string.format('%s:*:%u:%u::/var:/bin/false\n', username, uid, gid)) + util.replace_prefix('/etc/shadow', username .. ':', string.format('%s:*:0:0:99999:7:::\n', username)) + util.unlock('/var/lock/passwd') +end + +function remove_user(username) + util.lock('/var/lock/passwd') + util.replace_prefix('/etc/passwd', username .. ':') + util.replace_prefix('/etc/shadow', username .. ':') + util.unlock('/var/lock/passwd') +end + +function add_group(groupname, gid) + util.lock('/var/lock/group') + util.replace_prefix('/etc/group', groupname .. ':', string.format('%s:x:%u:\n', groupname, gid)) + util.unlock('/var/lock/group') +end + +function remove_group(groupname) + util.lock('/var/lock/group') + util.replace_prefix('/etc/group', groupname .. ':') + util.unlock('/var/lock/group') +end diff --git a/package/gluon-core/files/usr/lib/lua/gluon/util.lua b/package/gluon-core/files/usr/lib/lua/gluon/util.lua new file mode 100644 index 00000000..cf3677cb --- /dev/null +++ b/package/gluon-core/files/usr/lib/lua/gluon/util.lua @@ -0,0 +1,79 @@ +-- Writes all lines from the file input to the file output except those starting with prefix +-- Doesn't close the output file, but returns the file object +local function do_filter_prefix(input, output, prefix) + local f = io.open(output, 'w+') + local l = prefix:len() + + for line in io.lines(input) do + if line:sub(1, l) ~= prefix then + f:write(line, '\n') + end + end + + return f +end + + +local function escape_args(ret, arg0, ...) + if not arg0 then + return ret + end + + return escape_args(ret .. "'" .. string.gsub(arg0, "'", "'\\''") .. "' ", ...) +end + + +local os = os +local string = string +local tonumber = tonumber + +local nixio = require 'nixio' +local sysconfig = require 'gluon.sysconfig' + + +module 'gluon.util' + +function exec(...) + return os.execute(escape_args('', ...)) +end + +-- Removes all lines starting with a prefix from a file, optionally adding a new one +function replace_prefix(file, prefix, add) + local tmp = file .. '.tmp' + local f = do_filter_prefix(file, tmp, prefix) + if add then + f:write(add) + end + f:close() + os.rename(tmp, file) +end + +function lock(file) + exec('lock', file) +end + +function unlock(file) + exec('lock', '-u', file) +end + +function node_id() + return string.gsub(sysconfig.primary_mac, ':', '') +end + +-- Generates a (hopefully) unique MAC address +-- The first parameter defines the function and the second +-- parameter an ID to add to the MAC address +-- Functions and IDs defined so far: +-- (1, 0): WAN (for mesh-on-WAN) +-- (1, 1): LAN (for mesh-on-LAN) +-- (2, n): client interface for the n'th radio +-- (3, n): adhoc interface for n'th radio +-- (4, 0): mesh VPN +function generate_mac(f, i) + local m1, m2, m3, m4, m5, m6 = string.match(sysconfig.primary_mac, '(%x%x):(%x%x):(%x%x):(%x%x):(%x%x):(%x%x)') + m1 = nixio.bit.bor(tonumber(m1, 16), 0x02) + m2 = (tonumber(m2, 16)+f) % 0x100 + m3 = (tonumber(m3, 16)+i) % 0x100 + + return string.format('%02x:%02x:%02x:%s:%s:%s', m1, m2, m3, m4, m5, m6) +end diff --git a/package/gluon-cron/Makefile b/package/gluon-cron/Makefile new file mode 100644 index 00000000..ac92a92d --- /dev/null +++ b/package/gluon-cron/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-cron +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-cron + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Cron support + DEPENDS:=+gluon-core +endef + +define Package/gluon-cron/description + Gluon community wifi mesh firmware framework: cron support +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Configure +endef + +define Build/Compile + CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) +endef + +define Package/gluon-cron/install + $(CP) ./files/* $(1)/ + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-crond $(1)/usr/sbin/ +endef + +$(eval $(call BuildPackage,gluon-cron)) diff --git a/package/gluon-cron/files/etc/init.d/gluon-cron b/package/gluon-cron/files/etc/init.d/gluon-cron new file mode 100755 index 00000000..27a05e1d --- /dev/null +++ b/package/gluon-cron/files/etc/init.d/gluon-cron @@ -0,0 +1,18 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2013 Project Gluon + +START=50 + +SERVICE_USE_PID=1 +SERVICE_WRITE_PID=1 +SERVICE_DAEMONIZE=1 + +CRONDIR=/lib/gluon/cron + +start () { + service_start /usr/sbin/gluon-crond "$CRONDIR" +} + +stop() { + service_stop /usr/sbin/gluon-crond +} diff --git a/package/gluon-cron/files/lib/gluon/cron/.keep b/package/gluon-cron/files/lib/gluon/cron/.keep new file mode 100644 index 00000000..e69de29b diff --git a/package/gluon-cron/src/Makefile b/package/gluon-cron/src/Makefile new file mode 100644 index 00000000..3f4c7a50 --- /dev/null +++ b/package/gluon-cron/src/Makefile @@ -0,0 +1,3 @@ +all: gluon-crond + +gluon-crond: gluon-crond.c diff --git a/package/gluon-cron/src/gluon-crond.c b/package/gluon-cron/src/gluon-crond.c new file mode 100644 index 00000000..11a87c42 --- /dev/null +++ b/package/gluon-cron/src/gluon-crond.c @@ -0,0 +1,316 @@ +/* + Copyright (c) 2013, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct job { + struct job *next; + + uint64_t minutes; + uint32_t hours; + uint32_t doms; + uint16_t months; + uint8_t dows; + + char *command; +} job_t; + + +static const char const *const MONTHS[12] = { + "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" +}; + +static const char const *const WEEKDAYS[7] = { + "sun", "mon", "tue", "wed", "thu", "fri", "sat" +}; + + +static const char *crondir; + +static job_t *jobs = NULL; + + +static void usage(void) { + fprintf(stderr, "Usage: gluon-crond \n"); +} + + +static inline uint64_t bit(unsigned b) { + return ((uint64_t)1) << b; +} + +static int strict_atoi(const char *s) { + char *end; + int ret = strtol(s, &end, 10); + + if (*end) + return -1; + else + return ret; +} + +static uint64_t parse_strings(const char *input, const char *const *strings, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + if (strcasecmp(input, strings[i]) == 0) + return bit(i); + } + + return 0; +} + +static uint64_t parse_times(char *input, int min, int n) { + uint64_t ret = 0; + int step = 1; + + char *comma = strchr(input, ','); + if (comma) { + *comma = 0; + ret = parse_times(comma+1, min, n); + + if (!ret) + return 0; + } + + char *slash = strchr(input, '/'); + if (slash) { + *slash = 0; + step = strict_atoi(slash+1); + + if (step <= 0) + return 0; + } + + int begin, end; + char *minus = strchr(input, '-'); + if (minus) { + *minus = 0; + begin = strict_atoi(input); + end = strict_atoi(minus+1); + } + else if (strcmp(input, "*") == 0) { + begin = min; + end = min+n-1; + } + else { + begin = end = strict_atoi(input); + } + + if (begin < min || end < min) + return 0; + + int i; + for (i = begin-min; i <= end-min; i += step) + ret |= bit(i % n); + + return ret; +} + +static int handle_line(const char *line) { + job_t job = {}; + int ret = -1; + char *columns[5]; + int i; + int len; + + int matches = sscanf(line, "%ms %ms %ms %ms %ms %n", &columns[0], &columns[1], &columns[2], &columns[3], &columns[4], &len); + if (matches != 5 && matches != 6) { + if (matches <= 0) + ret = 0; + + goto end; + } + + job.minutes = parse_times(columns[0], 0, 60); + if (!job.minutes) + goto end; + + job.hours = parse_times(columns[1], 0, 24); + if (!job.hours) + goto end; + + job.doms = parse_times(columns[2], 1, 31); + if (!job.doms) + goto end; + + + job.months = parse_strings(columns[3], MONTHS, 12); + + if (!job.months) + job.months = parse_times(columns[3], 1, 12); + if (!job.months) + goto end; + + job.dows = parse_strings(columns[4], WEEKDAYS, 7); + if (!job.dows) + job.dows = parse_times(columns[4], 0, 7); + if (!job.dows) + goto end; + + job.command = strdup(line+len); + + job_t *jobp = malloc(sizeof(job_t)); + *jobp = job; + + jobp->next = jobs; + jobs = jobp; + + ret = 0; + + end: + for (i = 0; i < matches && i < 5; i++) + free(columns[i]); + + return ret; +} + + +static void read_crontab(const char *name) { + FILE *file = fopen(name, "r"); + if (!file) { + syslog(LOG_WARNING, "unable to read crontab `%s'", name); + return; + } + + char line[16384]; + unsigned lineno = 0; + + while (fgets(line, sizeof(line), file)) { + lineno++; + + char *comment = strchr(line, '#'); + if (comment) + *comment = 0; + + if (handle_line(line)) + syslog(LOG_WARNING, "syntax error in `%s', line %u", name, lineno); + } + + fclose(file); +} + + +static void read_crondir(void) { + DIR *dir; + + if (chdir(crondir) || ((dir = opendir(".")) == NULL)) { + fprintf(stderr, "Unable to read crondir `%s'\n", crondir); + usage(); + exit(1); + } + + struct dirent *ent; + while ((ent = readdir(dir)) != NULL) { + if (ent->d_name[0] == '.') + continue; + + read_crontab(ent->d_name); + } + + closedir(dir); +} + + +static void run_job(const job_t *job) { + pid_t pid = fork(); + if (pid == 0) { + execl("/bin/sh", "/bin/sh", "-c", job->command, (char*)NULL); + syslog(LOG_ERR, "unable to run job: exec failed"); + _exit(1); + } + else if (pid < 0) { + syslog(LOG_ERR, "unable to run job: fork failed"); + } +} + + +static void check_job(const job_t *job, const struct tm *tm) { + if (!(job->minutes & bit(tm->tm_min))) + return; + + if (!(job->hours & bit(tm->tm_hour))) + return; + + if (!(job->doms & bit(tm->tm_mday-1))) + return; + + if (!(job->months & bit(tm->tm_mon))) + return; + + if (!(job->dows & bit(tm->tm_wday))) + return; + + run_job(job); +} + + +int main(int argc, char *argv[]) { + if (argc != 2) { + usage(); + + exit(argc < 2 ? 0 : 1); + } + + crondir = argv[1]; + + signal(SIGCHLD, SIG_IGN); + + read_crondir(); + + time_t t = time(NULL); + struct tm *tm = localtime(&t); + int minute = tm->tm_min; + + while (1) { + sleep(60 - t%60); + + t = time(NULL); + tm = localtime(&t); + + minute = (minute+1)%60; + if (tm->tm_min != minute) { + /* clock has moved, don't execute jobs */ + minute = tm->tm_min; + continue; + } + + job_t *job; + for (job = jobs; job; job = job->next) + check_job(job, tm); + } +} diff --git a/package/gluon-ebtables-filter-multicast/Makefile b/package/gluon-ebtables-filter-multicast/Makefile new file mode 100644 index 00000000..93b7f9a5 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-ebtables-filter-multicast +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-ebtables-filter-multicast + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Ebtables filters for multicast packets + DEPENDS:=+gluon-core +gluon-ebtables +endef + +define Package/gluon-ebtables-filter-multicast/description + Gluon community wifi mesh firmware framework: Ebtables filters for multicast packets + + These filters drop non-essential multicast traffic before it enters the mesh. + + Allowed protocols are: DHCP, DHCPv6, ARP, ICMP, ICMPv6, BitTorrent local peer discovery, BABEL and OSPF +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-ebtables-filter-multicast/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-ebtables-filter-multicast)) diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain new file mode 100644 index 00000000..ec0013a3 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain @@ -0,0 +1 @@ +chain('MULTICAST_OUT', 'DROP') diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-arp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-arp new file mode 100644 index 00000000..8af1900a --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-arp @@ -0,0 +1,3 @@ +rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-ip-src 0.0.0.0 -j DROP' +rule 'MULTICAST_OUT -p ARP --arp-opcode Request --arp-ip-dst 0.0.0.0 -j DROP' +rule 'MULTICAST_OUT -p ARP -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-babel b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-babel new file mode 100644 index 00000000..d5b81771 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-babel @@ -0,0 +1 @@ +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 6696 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-btlpd b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-btlpd new file mode 100644 index 00000000..20b709f8 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-btlpd @@ -0,0 +1 @@ +rule 'MULTICAST_OUT -p IPv4 --ip-destination 239.192.152.143 --ip-protocol udp --ip-destination-port 6771 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv4 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv4 new file mode 100644 index 00000000..2fca2223 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv4 @@ -0,0 +1 @@ +rule 'MULTICAST_OUT -p IPv4 --ip-protocol udp --ip-destination-port 67 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv6 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv6 new file mode 100644 index 00000000..6d7f0f55 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv6 @@ -0,0 +1 @@ +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmp new file mode 100644 index 00000000..25a95f39 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmp @@ -0,0 +1 @@ +rule 'MULTICAST_OUT -p IPv4 --ip-protocol icmp -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 new file mode 100644 index 00000000..a7b67414 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 @@ -0,0 +1,2 @@ +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol 0 -j RETURN' -- hop-by-hop +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp new file mode 100644 index 00000000..2d3814ae --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp @@ -0,0 +1 @@ +rule 'MULTICAST_OUT -p IPv4 --ip-protocol igmp -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ospf b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ospf new file mode 100644 index 00000000..da928d4b --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ospf @@ -0,0 +1,2 @@ +rule 'MULTICAST_OUT -p IPv4 --ip-protocol ospf -j RETURN' +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ospf -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ripng b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ripng new file mode 100644 index 00000000..37d31877 --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ripng @@ -0,0 +1 @@ +rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination ff02::9 --ip6-destination-port 521 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast new file mode 100644 index 00000000..c52f122f --- /dev/null +++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast @@ -0,0 +1,2 @@ +rule 'FORWARD --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT' +rule 'OUTPUT --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT' diff --git a/package/gluon-ebtables-filter-ra-dhcp/Makefile b/package/gluon-ebtables-filter-ra-dhcp/Makefile new file mode 100644 index 00000000..ea6a737d --- /dev/null +++ b/package/gluon-ebtables-filter-ra-dhcp/Makefile @@ -0,0 +1,39 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-ebtables-filter-ra-dhcp +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-ebtables-filter-ra-dhcp + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Ebtables filters for Router Advertisement and DHCP packets + DEPENDS:=+gluon-core +gluon-ebtables +endef + +define Package/gluon-ebtables-filter-ra-dhcp/description + Gluon community wifi mesh firmware framework: Ebtables filters for Router Advertisement and DHCP packets + + These filters ensure that RA and DHCP packets are only forwarded from the mesh into the + client network, and not vice-versa. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-ebtables-filter-ra-dhcp/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-ebtables-filter-ra-dhcp)) diff --git a/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv4 b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv4 new file mode 100644 index 00000000..ec56ff1d --- /dev/null +++ b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv4 @@ -0,0 +1,5 @@ +rule 'FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 67 -j OUT_ONLY' +rule 'OUTPUT -p IPv4 --ip-protocol udp --ip-destination-port 67 -j OUT_ONLY' + +rule 'FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 68 -j IN_ONLY' +rule 'INPUT -p IPv4 --ip-protocol udp --ip-destination-port 68 -j IN_ONLY' diff --git a/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv6 b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv6 new file mode 100644 index 00000000..470a7648 --- /dev/null +++ b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv6 @@ -0,0 +1,5 @@ +rule 'FORWARD -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j OUT_ONLY' +rule 'OUTPUT -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j OUT_ONLY' + +rule 'FORWARD -p IPv6 --ip6-protocol udp --ip6-destination-port 546 -j IN_ONLY' +rule 'INPUT -p IPv6 --ip6-protocol udp --ip6-destination-port 546 -j IN_ONLY' diff --git a/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-radv b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-radv new file mode 100644 index 00000000..b34d4c76 --- /dev/null +++ b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-radv @@ -0,0 +1,5 @@ +rule 'FORWARD -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -j OUT_ONLY' +rule 'OUTPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -j OUT_ONLY' + +rule 'FORWARD -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j IN_ONLY' +rule 'INPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j IN_ONLY' diff --git a/package/gluon-ebtables/Makefile b/package/gluon-ebtables/Makefile new file mode 100644 index 00000000..39c654c1 --- /dev/null +++ b/package/gluon-ebtables/Makefile @@ -0,0 +1,36 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-ebtables +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-ebtables + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Ebtables support + DEPENDS:=+gluon-core +ebtables +kmod-ebtables-ipv4 +kmod-ebtables-ipv6 +kmod-ipt-core +endef + +define Package/gluon-ebtables/description + Gluon community wifi mesh firmware framework: ebtables support +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-ebtables/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-ebtables)) diff --git a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables new file mode 100755 index 00000000..5a770452 --- /dev/null +++ b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables @@ -0,0 +1,73 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2013 Project Gluon +# +# Firewall script for inserting and removing ebtables rules. +# +# Example format, for filtering any IPv4 multicast packets to the SSDP UDP port: +# rule FORWARD --logical-out br-client -d Multicast -p IPv4 --ip-protocol udp --ip-destination-port 5355 -j DROP +# +# Removing all rules: +# $ ./firewall-ebtables stop +# Inserting all rules: +# $ ./firewall-ebtables start +# Inserting a specific rule file: +# $ ./firewall-ebtables start /lib/gluon/ebtables/100-mcast-chain +# Removing a specific rule file: +# $ ./firewall-ebtables stop /lib/gluon/ebtables/100-mcast-chain + + +START=19 +STOP=91 + + +exec_file() { + local file="$1" + + /usr/bin/lua -e " + function rule(command) + os.execute($EBTABLES_RULE) + end + function chain(name, policy) + os.execute($EBTABLES_CHAIN) + end + " "$file" +} + +exec_all() { + local sort_arg="$1" + + local old_ifs="$IFS" + IFS=' +' + for file in `find /lib/gluon/ebtables -type f | sort $sort_arg`; do + exec_file "$file" + done + IFS="$old_ifs" +} + + +start() { + ( + export EBTABLES_RULE='"ebtables -A " .. command' + export EBTABLES_CHAIN='"ebtables -N " .. name .. " -P " .. policy' + + if [ -z "$1" ]; then + exec_all '' + else + exec_file "$1" + fi + ) +} + +stop() { + ( + export EBTABLES_RULE='"ebtables -D " .. command' + export EBTABLES_CHAIN='"ebtables -X " .. name' + + if [ -z "$1" ]; then + exec_all '-r' + else + exec_file "$1" + fi + ) +} diff --git a/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain b/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain new file mode 100644 index 00000000..31c19c53 --- /dev/null +++ b/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain @@ -0,0 +1,2 @@ +chain('IN_ONLY', 'RETURN') +chain('OUT_ONLY', 'RETURN') diff --git a/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules b/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules new file mode 100644 index 00000000..b1cd4e24 --- /dev/null +++ b/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules @@ -0,0 +1,2 @@ +rule 'IN_ONLY --logical-in br-client -i ! bat0 -j DROP' +rule 'OUT_ONLY --logical-out br-client -o ! bat0 -j DROP' diff --git a/package/gluon-legacy/Makefile b/package/gluon-legacy/Makefile new file mode 100644 index 00000000..7320fba6 --- /dev/null +++ b/package/gluon-legacy/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-legacy +PKG_VERSION:=2 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-legacy + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Legacy update scripts + DEPENDS:=+gluon-core +endef + +define Package/gluon-legacy/description + Gluon community wifi mesh firmware framework: legacy update scripts +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-legacy/install + $(CP) ./files/* $(1)/ +endef + +define Package/gluon-legacy/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-legacy)) diff --git a/package/gluon-legacy/check_site.lua b/package/gluon-legacy/check_site.lua new file mode 100644 index 00000000..1ec26de0 --- /dev/null +++ b/package/gluon-legacy/check_site.lua @@ -0,0 +1,8 @@ +need_string_array 'legacy.version_files' +need_string_array 'legacy.old_files' + +need_string_array 'legacy.config_mode_configs' +need_string_array 'legacy.fastd_configs' +need_string 'legacy.mesh_ifname' +need_string_array 'legacy.tc_configs' +need_string_array 'legacy.wifi_names' diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/000-legacy b/package/gluon-legacy/files/lib/gluon/upgrade/000-legacy new file mode 100755 index 00000000..78436676 --- /dev/null +++ b/package/gluon-legacy/files/lib/gluon/upgrade/000-legacy @@ -0,0 +1,11 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local sysconfig = require 'gluon.sysconfig' + +for _, file in ipairs(site.legacy.version_files) do + if os.remove(file) then + -- Set version being upgraded from to 'legacy' + sysconfig.gluon_version = 'legacy' + end +end diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/019-legacy-interfaces b/package/gluon-legacy/files/lib/gluon/upgrade/019-legacy-interfaces new file mode 100755 index 00000000..b48e42b8 --- /dev/null +++ b/package/gluon-legacy/files/lib/gluon/upgrade/019-legacy-interfaces @@ -0,0 +1,40 @@ +#!/usr/bin/lua + +local gluon_util = require 'gluon.util' +local platform = require 'gluon.platform' +local site = require 'gluon.site_config' +local sysconfig = require 'gluon.sysconfig' + +local uci = require('luci.model.uci').cursor() +local util = require 'luci.util' + + +if sysconfig.gluon_version == 'legacy' then + local function iface_exists(name) + return (gluon_util.exec('ip', 'link', 'show', 'dev', (name:gsub('%..*$', ''))) == 0) + end + + local function remove_bat0(iface) + return util.trim(string.gsub(' ' .. iface .. ' ', ' bat0 ', ' ')) + end + + + local lan_ifname = remove_bat0(uci:get('network', site.legacy.mesh_ifname, 'ifname')) + local wan_ifname = uci:get('network', 'wan', 'ifname') + + if wan_ifname and iface_exists(wan_ifname) then + sysconfig.wan_ifname = wan_ifname + sysconfig.lan_ifname = lan_ifname + else + sysconfig.wan_ifname = lan_ifname + end + + + uci:delete('network', site.legacy.mesh_ifname) + uci:delete('network', 'wan') + + uci:save('network') + uci:commit('network') +end + + diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/210-legacy-wireless b/package/gluon-legacy/files/lib/gluon/upgrade/210-legacy-wireless new file mode 100755 index 00000000..36da0632 --- /dev/null +++ b/package/gluon-legacy/files/lib/gluon/upgrade/210-legacy-wireless @@ -0,0 +1,24 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local sysconfig = require 'gluon.sysconfig' + +local uci = require('luci.model.uci').cursor() + + +if sysconfig.gluon_version == 'legacy' then + function delete_legacy_iface(iface) + for _, wifi in pairs(site.legacy.wifi_names) do + if wifi == iface['.name'] then + return true + end + end + + return false + end + + uci:delete_all('wireless', 'wifi-iface', delete_legacy_iface) + + uci:save('wireless') + uci:commit('wireless') +end diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-setup-mode b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-setup-mode new file mode 100755 index 00000000..0b97e120 --- /dev/null +++ b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-setup-mode @@ -0,0 +1,22 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local sysconfig = require 'gluon.sysconfig' + +local uci = require('luci.model.uci').cursor() + + +if sysconfig.gluon_version == 'legacy' then + for _, config in ipairs(site.legacy.config_mode_configs) do + local old = uci:get_first(config, 'wizard', 'configured') + if old == '1' then + local setup_mode = uci:get_first('gluon-setup-mode', 'setup_mode') + uci:set('gluon-setup-mode', setup_mode, 'configured', '1') + + uci:save('gluon-setup-mode') + uci:commit('gluon-setup-mode') + + break + end + end +end diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-simple-tc b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-simple-tc new file mode 100755 index 00000000..c67afe14 --- /dev/null +++ b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-simple-tc @@ -0,0 +1,29 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local sysconfig = require 'gluon.sysconfig' + +local uci = require('luci.model.uci').cursor() + + +if sysconfig.gluon_version == 'legacy' then + for _, config in ipairs(site.legacy.tc_configs) do + local s = uci:get_first(config, 'bandwidth') + if s then + old = uci:get_all(config, s) + uci:section('gluon-simple-tc', 'interface', 'mesh_vpn', + { + ifname = 'mesh-vpn', + enabled = old.enabled, + limit_ingress = old.downstream, + limit_egress = old.upstream, + } + ) + + uci:save('gluon-simple-tc') + uci:commit('gluon-simple-tc') + + break + end + end +end diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/390-legacy-mesh-vpn-fastd b/package/gluon-legacy/files/lib/gluon/upgrade/390-legacy-mesh-vpn-fastd new file mode 100755 index 00000000..468a35a6 --- /dev/null +++ b/package/gluon-legacy/files/lib/gluon/upgrade/390-legacy-mesh-vpn-fastd @@ -0,0 +1,37 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local sysconfig = require 'gluon.sysconfig' + +local uci = require('luci.model.uci').cursor() + + +if sysconfig.gluon_version == 'legacy' then + local secret + local enabled + + + for _, config in ipairs(site.legacy.fastd_configs) do + if not secret then + local s = uci:get_all('fastd', config) + if s then + secret = s.secret + enabled = s.enabled + end + end + + uci:delete('fastd', config) + end + + if secret then + uci:section('fastd', 'fastd', 'mesh_vpn', + { + secret = secret, + enabled = enabled, + } + ) + end + + uci:save('fastd') + uci:commit('fastd') +end diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/990-legacy-late b/package/gluon-legacy/files/lib/gluon/upgrade/990-legacy-late new file mode 100755 index 00000000..efb6b675 --- /dev/null +++ b/package/gluon-legacy/files/lib/gluon/upgrade/990-legacy-late @@ -0,0 +1,11 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local sysconfig = require 'gluon.sysconfig' + + +if sysconfig.gluon_version == 'legacy' then + for _, file in ipairs(site.legacy.old_files) do + os.remove(file) + end +end diff --git a/package/gluon-lock-password/Makefile b/package/gluon-lock-password/Makefile new file mode 100644 index 00000000..d0e99373 --- /dev/null +++ b/package/gluon-lock-password/Makefile @@ -0,0 +1,36 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-lock-password +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-lock-password + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Locks the root account by default + DEPENDS:=+gluon-core +endef + +define Package/gluon-lock-password/description + This packages locks the root account by default. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-lock-password/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-lock-password)) diff --git a/package/gluon-lock-password/files/lib/gluon/upgrade/100-lock-password b/package/gluon-lock-password/files/lib/gluon/upgrade/100-lock-password new file mode 100755 index 00000000..3204f638 --- /dev/null +++ b/package/gluon-lock-password/files/lib/gluon/upgrade/100-lock-password @@ -0,0 +1,13 @@ +#!/bin/sh + +has_root_pwd() { + local pwd + + pwd=$([ -f "$1" ] && cat "$1") + pwd="${pwd#*root:}" + pwd="${pwd%%:*}" + + test -n "${pwd}" +} + +has_root_pwd /etc/shadow || passwd -l root diff --git a/package/gluon-luci-admin/Makefile b/package/gluon-luci-admin/Makefile new file mode 100644 index 00000000..455bcb25 --- /dev/null +++ b/package/gluon-luci-admin/Makefile @@ -0,0 +1,39 @@ +# Copyright (C) 2013 Nils Schneider +# This is free software, licensed under the Apache 2.0 license. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-luci-admin +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-luci-admin + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Luci based simple administration interface for mesh nodes + DEPENDS:=+gluon-config-mode-core +endef + +define Package/gluon-luci-admin/description + Luci based config mode +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-luci-admin/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-luci-admin)) diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/index.lua b/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/index.lua new file mode 100644 index 00000000..284cc162 --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/index.lua @@ -0,0 +1,39 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +module("luci.controller.admin.index", package.seeall) + +function index() + local uci_state = luci.model.uci.cursor_state() + + -- Disable gluon-luci-admin when setup mode is not enabled + if uci_state:get_first('gluon-setup-mode', 'setup_mode', 'running', '0') ~= '1' then + return + end + + local root = node() + if not root.lock then + root.target = alias("admin") + root.index = true + end + + local page = entry({"admin"}, alias("admin", "index"), "Expert Mode", 10) + page.sysauth = "root" + page.sysauth_authenticator = function() return "root" end + page.index = true + + entry({"admin", "index"}, cbi("admin/info"), _("Info"), 1).ignoreindex = true + entry({"admin", "remote"}, cbi("admin/remote"), _("Remotezugriff"), 10) +end diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/upgrade.lua b/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/upgrade.lua new file mode 100644 index 00000000..868c2bbd --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/upgrade.lua @@ -0,0 +1,135 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +module("luci.controller.admin.upgrade", package.seeall) + +function index() + local has_platform = nixio.fs.access("/lib/upgrade/platform.sh") + if has_platform then + entry({"admin", "upgrade"}, call("action_upgrade"), "Firmware aktualisieren", 90) + entry({"admin", "upgrade", "reboot"}, template("admin/upgrade_reboot"), nil, nil) + end +end + +function action_upgrade() + local tmpfile = "/tmp/firmware.img" + + -- Install upload handler + local file + luci.http.setfilehandler( + function(meta, chunk, eof) + if not nixio.fs.access(tmpfile) and not file and chunk and #chunk > 0 then + file = io.open(tmpfile, "w") + end + if file and chunk then + file:write(chunk) + end + if file and eof then + file:close() + end + end + ) + + -- Determine state + local step = tonumber(luci.http.formvalue("step") or 1) + local has_image = nixio.fs.access(tmpfile) + local has_support = image_supported(tmpfile) + + -- Step 1: file upload, error on unsupported image format + if not has_image or not has_support or step == 1 then + -- If there is an image but user has requested step 1 + -- or type is not supported, then remove it. + if has_image then + nixio.fs.unlink(tmpfile) + end + + luci.template.render("admin/upgrade", { + bad_image=(has_image and not has_support or false) + } ) + + -- Step 2: present uploaded file, show checksum, confirmation + elseif step == 2 then + luci.template.render("admin/upgrade_confirm", { + checksum=image_checksum(tmpfile), + filesize=nixio.fs.stat(tmpfile).size, + flashsize=storage_size(), + keepconfig=luci.http.formvalue("keepcfg") == "1" + } ) + elseif step == 3 then + local keepcfg = luci.http.formvalue("keepcfg") == "1" + fork_exec("/sbin/sysupgrade %s %q" % { keepcfg and "" or "-n", tmpfile }) + luci.http.redirect(luci.dispatcher.build_url("admin", "upgrade", "reboot")) + end +end + +function fork_exec(command) + local pid = nixio.fork() + if pid > 0 then + return + elseif pid == 0 then + -- change to root dir + nixio.chdir("/") + + -- patch stdin, out, err to /dev/null + local null = nixio.open("/dev/null", "w+") + if null then + nixio.dup(null, nixio.stderr) + nixio.dup(null, nixio.stdout) + nixio.dup(null, nixio.stdin) + if null:fileno() > 2 then + null:close() + end + end + + -- replace with target command + nixio.exec("/bin/sh", "-c", command) + end +end + +function image_supported(tmpfile) + -- XXX: yay... + return ( 0 == os.execute( + ". /lib/functions.sh; " .. + "include /lib/upgrade; " .. + "platform_check_image %q >/dev/null" + % tmpfile + ) ) +end + +function storage_size() + local size = 0 + if nixio.fs.access("/proc/mtd") then + for l in io.lines("/proc/mtd") do + local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"') + if n == "linux" then + size = tonumber(s, 16) + break + end + end + elseif nixio.fs.access("/proc/partitions") then + for l in io.lines("/proc/partitions") do + local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)') + if b and n and not n:match('[0-9]') then + size = tonumber(b) * 1024 + break + end + end + end + return size +end + +function image_checksum(tmpfile) + return (luci.sys.exec("md5sum %q" % tmpfile):match("^([^%s]+)")) +end diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/info.lua b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/info.lua new file mode 100644 index 00000000..e9ceba70 --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/info.lua @@ -0,0 +1,4 @@ +local t = Template('admin/info') +t.pageaction = false + +return t diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/remote.lua b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/remote.lua new file mode 100644 index 00000000..dfba5e66 --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/remote.lua @@ -0,0 +1,108 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth +Copyright 2011 Jo-Philipp Wich +Copyright 2013 Nils Schneider + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +local fs = require "nixio.fs" + +local m = Map("system", "SSH-Keys") +m.submit = "Speichern" +m.reset = "Zurücksetzen" +m.pageaction = false +m.template = "admin/expertmode" + +if fs.access("/etc/config/dropbear") then + local s = m:section(TypedSection, "_dummy1", nil, + "Hier hast du die Möglichkeit SSH-Keys (einen pro Zeile) zu hinterlegen:") + + s.addremove = false + s.anonymous = true + + function s.cfgsections() + return { "_keys" } + end + + local keys + + keys = s:option(TextValue, "_data", "") + keys.wrap = "off" + keys.rows = 5 + keys.rmempty = true + + function keys.cfgvalue() + return fs.readfile("/etc/dropbear/authorized_keys") or "" + end + + function keys.write(self, section, value) + if value then + fs.writefile("/etc/dropbear/authorized_keys", value:gsub("\r\n", "\n")) + end + end + + function keys.remove(self, section) + if keys:formvalue("_keys") then + fs.remove("/etc/dropbear/authorized_keys") + end + end +end + +local m2 = Map("system", "Passwort") +m2.submit = "Speichern" +m2.reset = false +m2.pageaction = false +m2.template = "admin/expertmode" + +local s = m2:section(TypedSection, "_dummy2", nil, +[[Alternativ kannst du auch ein Passwort setzen. Wähle bitte ein sicheres Passwort, das du nirgendwo anders verwendest.

+Beim Setzen eines leeren Passworts wird der Login per Passwort gesperrt (dies ist die Standard-Einstellung).]]) + +s.addremove = false +s.anonymous = true + +local pw1 = s:option(Value, "pw1", "Passwort") +pw1.password = true + +local pw2 = s:option(Value, "pw2", "Wiederholung") +pw2.password = true + +function s.cfgsections() + return { "_pass" } +end + +function m2.on_commit(map) + local v1 = pw1:formvalue("_pass") + local v2 = pw2:formvalue("_pass") + + if v1 and v2 then + if v1 == v2 then + if #v1 > 0 then + if luci.sys.user.setpasswd(luci.dispatcher.context.authuser, v1) == 0 then + m2.message = "Passwort geändert." + else + m2.errmessage = "Das Passwort konnte nicht geändert werden." + end + else + -- We don't check the return code here as the error 'password for root is already locked' is normal... + os.execute('passwd -l root >/dev/null') + m2.message = "Passwort gelöscht." + end + else + m2.errmessage = "Die beiden Passwörter stimmen nicht überein." + end + end +end + +local c = Compound(m, m2) +c.pageaction = false +return c diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/expertmode.htm b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/expertmode.htm new file mode 100644 index 00000000..53947f3c --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/expertmode.htm @@ -0,0 +1,56 @@ +<% if not self.embedded then %> +
+
+ + +
+<% end %> +
+ <% if self.title and #self.title > 0 then %>

<%=self.title%>

<% end %> + <%- if self.message then %> +

<%=self.message%>

+ <%- end %> + <%- if self.errmessage then %> +

<%=self.errmessage%>

+ <%- end %> + <% if self.description and #self.description > 0 then %>
<%=self.description%>
<% end %> + <% self:render_children() %> +
+<% if not self.embedded then %> +
+<%- + if type(self.hidden) == "table" then + for k, v in pairs(self.hidden) do +-%> + +<%- + end + end +%> +<% if redirect then %> +
+ +
+<% end %> +<%- if self.flow and self.flow.skip then %> + +<% end %> +<%- if self.submit ~= false then %> + +<% end %> +<%- if self.reset ~= false then %> + +<% end %> +<%- if self.cancel ~= false and self.on_cancel then %> + +<% end %> + +
+
+<% end %> diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/info.htm b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/info.htm new file mode 100644 index 00000000..9c384399 --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/info.htm @@ -0,0 +1,44 @@ +<%- + local fs = require 'luci.fs' + local uci = require('luci.model.uci').cursor() + local util = require 'luci.util' + + local site = require 'gluon.site_config' + local sysconfig = require 'gluon.sysconfig' + local platform = require 'gluon.platform' + + + local keys = { + hostname = 'Hostname', + primary_mac = 'MAC-Adresse', + model = 'Hardware-Modell', + version = 'Gluon-Version', + release = 'Firmware-Release', + site = 'Site', + pubkey = 'Öffentlicher VPN-Schlüssel', + } + + local values = { + hostname = uci:get_first('system', 'system', 'hostname'), + primary_mac = sysconfig.primary_mac, + model = platform.get_model(), + version = util.trim(fs.readfile('/lib/gluon/gluon-version')), + release = util.trim(fs.readfile('/lib/gluon/release')), + site = site.site_name, + pubkey = 'n/a', + } + + local meshvpn_enabled = uci:get("fastd", "mesh_vpn", "enabled", "0") + if meshvpn_enabled == "1" then + local pubkey = util.trim(util.exec('/etc/init.d/fastd show_key mesh_vpn')) + if pubkey ~= '' then + values.pubkey = pubkey + end + end +-%> +

Info

+<% for _, key in ipairs({'hostname', 'primary_mac', 'model', 'version', 'release', 'site', 'pubkey'}) do %> +
+
<%=keys[key]%>
<%=values[key] or 'n/a'%>
+
+<% end %> diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade.htm b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade.htm new file mode 100644 index 00000000..343ed495 --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade.htm @@ -0,0 +1,55 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008-2009 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> + +<%+header%> + +

Firmware aktualisieren

+ +
+

+ Hier kannst du ein manuelles Firmwareupdate durchführen. +

+<% if bad_image then %> +

Die übermittelte Firmwaredatei kann nicht verwendet werden.

+<% end %> +
+
+ +
+ +
+
+ +
+ + +
+ + +
+
+
+ +
+ + +
+
+<%+footer%> + diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_confirm.htm b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_confirm.htm new file mode 100644 index 00000000..ac375761 --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_confirm.htm @@ -0,0 +1,67 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008-2009 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> + +<%+header%> + +

Firmware aktualisieren

+

+ Die Firmwaredatei wurde übermittelt. Bitte vergleiche MD5-Checksumme + und Dateigröße und klicke anschließend auf "Fortfahren". +

+ +<% if flashsize > 0 and filesize > flashsize then %> +

Die Firmware passt nicht in den Speicher des Gerätes.

+<% end %> + +

+

    +
  • md5sum: <%=checksum%>
  • +
  • Größe: <% + function byte_format(byte) + local suff = {"B", "KB", "MB", "GB", "TB"} + for i=1, 5 do + if byte > 1024 and i < 5 then + byte = byte / 1024 + else + return string.format("%.2f %s", byte, suff[i]) + end + end + end + + write(byte_format(filesize)) + + if flashsize > 0 then + write(luci.i18n.translatef( + " (%s available)", + w.byte_format(flashsize) + )) + end + %>
  • +
+

+
+
+ + " /> + +
+
+ + " /> + +
+
+<%+footer%> + diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_reboot.htm b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_reboot.htm new file mode 100644 index 00000000..b9baa279 --- /dev/null +++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_reboot.htm @@ -0,0 +1,21 @@ + + + + + + Firmware wird aktualisiert + + + +
+
+

+ Die Firmware wird jetzt aktualisiert. + UNTERBRICH AUF KEINEN FALL DIE STROMVERSORGUNG! + Dieser Vorgang wird einige Minuten dauern. + Anschließend startet das Gerät automatisch neu. +

+
+
+ + diff --git a/package/gluon-luci-autoupdater/Makefile b/package/gluon-luci-autoupdater/Makefile new file mode 100644 index 00000000..518bd6df --- /dev/null +++ b/package/gluon-luci-autoupdater/Makefile @@ -0,0 +1,39 @@ +# Copyright (C) 2013 Nils Schneider +# This is free software, licensed under the Apache 2.0 license. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-luci-autoupdater +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-luci-autoupdater + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Luci module for gluon-autoupdater + DEPENDS:=+gluon-luci-admin +gluon-autoupdater +endef + +define Package/gluon-luci-autoupdater/description + Luci module for gluon-autoupdater +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-luci-autoupdater/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-luci-autoupdater)) diff --git a/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/controller/admin/autoupdater.lua b/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/controller/admin/autoupdater.lua new file mode 100644 index 00000000..9e6fd79f --- /dev/null +++ b/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/controller/admin/autoupdater.lua @@ -0,0 +1,20 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2013 Nils Schneider + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +module("luci.controller.admin.autoupdater", package.seeall) + +function index() + entry({"admin", "autoupdater"}, cbi("admin/autoupdater"), _("Autoupdater"), 20) +end + diff --git a/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/model/cbi/admin/autoupdater.lua b/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/model/cbi/admin/autoupdater.lua new file mode 100644 index 00000000..6800422b --- /dev/null +++ b/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/model/cbi/admin/autoupdater.lua @@ -0,0 +1,31 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2013 Nils Schneider + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +m = Map("autoupdater", "Autoupdater") +m.submit = "Speichern" +m.reset = "Zurücksetzen" +m.pageaction = false +m.template = "admin/expertmode" + +s = m:section(TypedSection, "autoupdater", nil) +s.addremove = false +s.anonymous = true + +s:option(Flag, "enabled", "Aktivieren") +f = s:option(ListValue, "branch", "Branch") + +uci.cursor():foreach("autoupdater", "branch", function (section) f:value(section[".name"]) end) + +return m + diff --git a/package/gluon-luci-node-role/Makefile b/package/gluon-luci-node-role/Makefile new file mode 100644 index 00000000..5e0fbb00 --- /dev/null +++ b/package/gluon-luci-node-role/Makefile @@ -0,0 +1,37 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-luci-node-role +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-luci-node-role + SECTION:=gluon + CATEGORY:=Gluon + DEPENDS:=+gluon-luci-admin +gluon-node-info + TITLE:=UI for specifying node role +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-luci-node-role/install + $(CP) ./files/* $(1)/ +endef + +define Package/gluon-luci-node-role/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-luci-node-role)) diff --git a/package/gluon-luci-node-role/check_site.lua b/package/gluon-luci-node-role/check_site.lua new file mode 100644 index 00000000..cd3ca937 --- /dev/null +++ b/package/gluon-luci-node-role/check_site.lua @@ -0,0 +1,8 @@ +local function check_role(k, _) + local role = string.format('roles.list[%q]', k) + + need_string(role) +end + +need_string('roles.default') +need_table('roles.list', check_role) diff --git a/package/gluon-luci-node-role/files/usr/lib/lua/luci/controller/admin/noderole.lua b/package/gluon-luci-node-role/files/usr/lib/lua/luci/controller/admin/noderole.lua new file mode 100644 index 00000000..dcde01f1 --- /dev/null +++ b/package/gluon-luci-node-role/files/usr/lib/lua/luci/controller/admin/noderole.lua @@ -0,0 +1,5 @@ +module("luci.controller.admin.noderole", package.seeall) + +function index() + entry({"admin", "noderole"}, cbi("admin/noderole"), "Verwendungszweck", 20) +end diff --git a/package/gluon-luci-node-role/files/usr/lib/lua/luci/model/cbi/admin/noderole.lua b/package/gluon-luci-node-role/files/usr/lib/lua/luci/model/cbi/admin/noderole.lua new file mode 100644 index 00000000..1d6d1798 --- /dev/null +++ b/package/gluon-luci-node-role/files/usr/lib/lua/luci/model/cbi/admin/noderole.lua @@ -0,0 +1,36 @@ +local f, s, o +local site = require 'gluon.site_config' +local uci = luci.model.uci.cursor() +local config = 'gluon-node-info' + +-- where to read the configuration from +local role = uci:get(config, uci:get_first(config, "system"), "role") + +f = SimpleForm("role", "Verwendungszweck") +f.reset = false +f.template = "admin/expertmode" +f.submit = "Fertig" + +s = f:section(SimpleSection, nil, [[ +Wenn dein Freifunk-Router eine besondere Rolle im Freifunk Netz einnimmt, kannst du diese hier angeben. +Bringe bitte zuvor in Erfahrung welche Auswirkungen die zur Verfügung stehenden Rollen im Freifunk-Netz haben. +Setze die Rolle nur, wenn du weißt was du machst. +]]) + +o = s:option(ListValue, "role", "Rolle") +o.default = role +o.rmempty = false +for role, prettyname in pairs(site.roles.list) do + o:value(role, prettyname) +end + +function f.handle(self, state, data) + if state == FORM_VALID then + uci:set(config, uci:get_first(config, "system"), "role", data.role) + + uci:save(config) + uci:commit(config) + end +end + +return f diff --git a/package/gluon-luci-portconfig/Makefile b/package/gluon-luci-portconfig/Makefile new file mode 100644 index 00000000..92b3cbcf --- /dev/null +++ b/package/gluon-luci-portconfig/Makefile @@ -0,0 +1,39 @@ +# Copyright (C) 2013 Nils Schneider +# This is free software, licensed under the Apache 2.0 license. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-luci-portconfig +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-luci-portconfig + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Luci module for advanced ethernet port configuration + DEPENDS:=+gluon-luci-admin +gluon-mesh-batman-adv +endef + +define Package/gluon-luci-portconfig/description + Luci module for advanced ethernet port configuration +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-luci-portconfig/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-luci-portconfig)) diff --git a/package/gluon-luci-portconfig/files/usr/lib/lua/luci/controller/admin/portconfig.lua b/package/gluon-luci-portconfig/files/usr/lib/lua/luci/controller/admin/portconfig.lua new file mode 100644 index 00000000..ff0e0af2 --- /dev/null +++ b/package/gluon-luci-portconfig/files/usr/lib/lua/luci/controller/admin/portconfig.lua @@ -0,0 +1,20 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2013 Nils Schneider + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +module("luci.controller.admin.portconfig", package.seeall) + +function index() + entry({"admin", "portconfig"}, cbi("admin/portconfig"), _("Schnittstellen"), 20) +end + diff --git a/package/gluon-luci-portconfig/files/usr/lib/lua/luci/model/cbi/admin/portconfig.lua b/package/gluon-luci-portconfig/files/usr/lib/lua/luci/model/cbi/admin/portconfig.lua new file mode 100644 index 00000000..7233d5c9 --- /dev/null +++ b/package/gluon-luci-portconfig/files/usr/lib/lua/luci/model/cbi/admin/portconfig.lua @@ -0,0 +1,133 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2014 Nils Schneider + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +local uci = luci.model.uci.cursor() + +local wan = uci:get_all("network", "wan") +local wan6 = uci:get_all("network", "wan6") +local dns = uci:get_first("gluon-wan-dnsmasq", "static") + +local f = SimpleForm("portconfig", "WAN-Verbindung") +f.template = "admin/expertmode" +f.submit = "Speichern" +f.reset = "Zurücksetzen" + +local s +local o + +s = f:section(SimpleSection, nil, nil) + +o = s:option(ListValue, "ipv4", "IPv4") +o:value("dhcp", "Automatisch (DHCP)") +o:value("static", "Statisch") +o:value("none", "Deaktiviert") +o.default = wan.proto + +o = s:option(Value, "ipv4_addr", "IP-Adresse") +o:depends("ipv4", "static") +o.value = wan.ipaddr +o.datatype = "ip4addr" +o.rmempty = false + +o = s:option(Value, "ipv4_netmask", "Netzmaske") +o:depends("ipv4", "static") +o.value = wan.netmask or "255.255.255.0" +o.datatype = "ip4addr" +o.rmempty = false + +o = s:option(Value, "ipv4_gateway", "Gateway") +o:depends("ipv4", "static") +o.value = wan.gateway +o.datatype = "ip4addr" +o.rmempty = false + + +s = f:section(SimpleSection, nil, nil) + +o = s:option(ListValue, "ipv6", "IPv6") +o:value("dhcpv6", "Automatisch (RA/DHCPv6)") +o:value("static", "Statisch") +o:value("none", "Deaktiviert") +o.default = wan6.proto + +o = s:option(Value, "ipv6_addr", "IP-Adresse") +o:depends("ipv6", "static") +o.value = wan6.ip6addr +o.datatype = "ip6addr" +o.rmempty = false + +o = s:option(Value, "ipv6_gateway", "Gateway") +o:depends("ipv6", "static") +o.value = wan6.ip6gw +o.datatype = "ip6addr" +o.rmempty = false + + +if dns then + s = f:section(SimpleSection, nil, nil) + + o = s:option(DynamicList, "dns", "Statische DNS-Server") + o:write(nil, uci:get("gluon-wan-dnsmasq", dns, "server")) + o.datatype = "ipaddr" +end + +s = f:section(SimpleSection, nil, nil) + +o = s:option(Flag, "mesh_wan", "Mesh auf dem WAN-Port aktivieren") +o.default = uci:get_bool("network", "mesh_wan", "auto") and o.enabled or o.disabled +o.rmempty = false + +function f.handle(self, state, data) + if state == FORM_VALID then + uci:set("network", "wan", "proto", data.ipv4) + if data.ipv4 == "static" then + uci:set("network", "wan", "ipaddr", data.ipv4_addr) + uci:set("network", "wan", "netmask", data.ipv4_netmask) + uci:set("network", "wan", "gateway", data.ipv4_gateway) + else + uci:delete("network", "wan", "ipaddr") + uci:delete("network", "wan", "netmask") + uci:delete("network", "wan", "gateway") + end + + uci:set("network", "wan6", "proto", data.ipv6) + if data.ipv6 == "static" then + uci:set("network", "wan6", "ip6addr", data.ipv6_addr) + uci:set("network", "wan6", "ip6gw", data.ipv6_gateway) + else + uci:delete("network", "wan6", "ip6addr") + uci:delete("network", "wan6", "ip6gw") + end + + uci:set("network", "mesh_wan", "auto", data.mesh_wan) + + uci:save("network") + uci:commit("network") + + if dns then + if #data.dns > 0 then + uci:set("gluon-wan-dnsmasq", dns, "server", data.dns) + else + uci:delete("gluon-wan-dnsmasq", dns, "server") + end + + uci:save("gluon-wan-dnsmasq") + uci:commit("gluon-wan-dnsmasq") + end + end + + return true +end + +return f diff --git a/package/gluon-luci-private-wifi/Makefile b/package/gluon-luci-private-wifi/Makefile new file mode 100644 index 00000000..0d5c4619 --- /dev/null +++ b/package/gluon-luci-private-wifi/Makefile @@ -0,0 +1,32 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-luci-private-wifi +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-luci-private-wifi + SECTION:=gluon + CATEGORY:=Gluon + DEPENDS:=+gluon-luci-admin + TITLE:=UI for activating a private WLAN +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-luci-private-wifi/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-luci-private-wifi)) diff --git a/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/controller/admin/privatewifi.lua b/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/controller/admin/privatewifi.lua new file mode 100644 index 00000000..0b10b4e9 --- /dev/null +++ b/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/controller/admin/privatewifi.lua @@ -0,0 +1,5 @@ +module("luci.controller.admin.privatewifi", package.seeall) + +function index() + entry({"admin", "privatewifi"}, cbi("admin/privatewifi"), "Privates WLAN", 10) +end diff --git a/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/model/cbi/admin/privatewifi.lua b/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/model/cbi/admin/privatewifi.lua new file mode 100644 index 00000000..b9157310 --- /dev/null +++ b/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/model/cbi/admin/privatewifi.lua @@ -0,0 +1,63 @@ +local f, s, o, ssid +local uci = luci.model.uci.cursor() +local config = 'wireless' + +-- where to read the configuration from +local primary_iface = 'wan_radio0' +local ssid = uci:get(config, primary_iface, "ssid") + +f = SimpleForm("wifi", "Privates WLAN") +f.reset = false +f.template = "admin/expertmode" +f.submit = "Fertig" + +s = f:section(SimpleSection, nil, [[ +Dein Freifunk-Router kann ebenfalls die Reichweite deines privaten Netzes erweitern. +Hierfür wird der WAN-Port mit einem seperaten WLAN gebridged. +Diese Funktionalität ist völlig unabhängig von Freifunk. +Beachte, dass du nicht gleichzeitig das Meshen über den WAN Port aktiviert haben solltest. +]]) + +o = s:option(Flag, "enabled", "Aktiviert") +o.default = (ssid and not uci:get_bool(config, primary_iface, "disabled")) and o.enabled or o.disabled +o.rmempty = false + +o = s:option(Value, "ssid", "Name (SSID)") +o.default = ssid + +o = s:option(Value, "key", "Schlüssel", "8-63 Zeichen") +o.datatype = "wpakey" +o.default = uci:get(config, primary_iface, "key") + +function f.handle(self, state, data) + if state == FORM_VALID then + uci:foreach(config, "wifi-device", + function(s) + local device = s['.name'] + local name = "wan_" .. device + + if data.enabled == '1' then + -- set up WAN wifi-iface + local t = uci:get_all(config, name) or {} + + t.device = device + t.network = "wan" + t.mode = 'ap' + t.encryption = 'psk2' + t.ssid = data.ssid + t.key = data.key + t.disabled = "false" + + uci:section(config, "wifi-iface", name, t) + else + -- disable WAN wifi-iface + uci:set(config, name, "disabled", "true") + end + end) + + uci:save(config) + uci:commit(config) + end +end + +return f diff --git a/package/gluon-luci-theme/Makefile b/package/gluon-luci-theme/Makefile new file mode 100644 index 00000000..4fc947c4 --- /dev/null +++ b/package/gluon-luci-theme/Makefile @@ -0,0 +1,39 @@ +# Copyright (C) 2013 Nils Schneider +# This is free software, licensed under the Apache 2.0 license. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-luci-theme +PKG_VERSION:=0.1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-luci-theme + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Luci theme for Gluon + DEPENDS:= +endef + +define Package/gluon-luci-theme/description + Luci based config mode +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-luci-theme/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-luci-theme)) diff --git a/package/gluon-luci-theme/files/etc/uci-defaults/luci-theme-gluon b/package/gluon-luci-theme/files/etc/uci-defaults/luci-theme-gluon new file mode 100755 index 00000000..795bd186 --- /dev/null +++ b/package/gluon-luci-theme/files/etc/uci-defaults/luci-theme-gluon @@ -0,0 +1,6 @@ +#!/bin/sh +uci batch <<-EOF + set luci.themes.Gluon=/luci-static/gluon + commit luci +EOF + diff --git a/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/footer.htm b/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/footer.htm new file mode 100644 index 00000000..6b709030 --- /dev/null +++ b/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/footer.htm @@ -0,0 +1,19 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> + + + + + diff --git a/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/header.htm b/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/header.htm new file mode 100644 index 00000000..730647bc --- /dev/null +++ b/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/header.htm @@ -0,0 +1,169 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2008 Steven Barth +Copyright 2008-2010 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ + +-%> +<% + local sys = require "luci.sys" + local http = require "luci.http" + local disp = require "luci.dispatcher" + local fs = require "nixio.fs" + + local hostname = sys.hostname() + local release = fs.readfile("/lib/gluon/release") + local load1, load5, load15 = sys.loadavg() + + local request = disp.context.path + local request2 = disp.context.request + + local category = request[1] + local cattree = category and disp.node(category) + + local leaf = request2[#request2] + + local tree = disp.node() + local node = disp.context.dispatched + + local categories = disp.node_childs(tree) + + local c = tree + local i, r + + -- tag all nodes leading to this page + for i, r in ipairs(request) do + if c.nodes and c.nodes[r] then + c = c.nodes[r] + c._menu_selected = true + end + end + + http.prepare_content("application/xhtml+xml") + + local function nodeurl(prefix, name, query) + local url = controller .. prefix .. name .. "/" + if query then + url = url .. http.build_querystring(query) + end + return pcdata(url) + end + + local function subtree(prefix, node, level) + if not level then + level = 1 + end + + local childs = disp.node_childs(node) + if #childs > 0 then +%> +
+
    + <% + local selected_node + local selected_name + local i, v + + for i, v in ipairs(childs) do + local nnode = node.nodes[v] + if nnode._menu_selected then + selected_node = nnode + selected_name = v + end + %> +
  • + <%=striptags(translate(nnode.title))%> +
  • + <% + end + %> +
+
+<% + if selected_node then + subtree(prefix .. selected_name .. "/", selected_node, level + 1) + end +%> +
+<% + end + end +-%> + + + + + + + + +<% if node and node.css then %> +<% end -%> +<% if css then %> +<% end -%> + +<%=striptags( hostname .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI + + + + + +
+ <% if category then subtree("/" .. category .. "/", cattree) end %> + +
+ diff --git a/package/gluon-luci-theme/files/www/luci-static/gluon/cascade.css b/package/gluon-luci-theme/files/www/luci-static/gluon/cascade.css new file mode 100644 index 00000000..ba2fb888 --- /dev/null +++ b/package/gluon-luci-theme/files/www/luci-static/gluon/cascade.css @@ -0,0 +1 @@ +@charset "UTF-8";.lang_he{direction:RTL;unicode-bidi:embed}.hidden{display:none}html{min-height:100%;height:auto;position:relative}body,input,select,option{font-family:'Open Sans', Arial, sans-serif;font-size:12pt}body{color:#4d4e53;line-height:1.5em;margin:0;display:flex;flex-direction:column;min-height:100vh;background-color:#f3f3f3}a img{border:none;text-decoration:none}.tabmenu1{text-align:center}ul.tabmenu{list-style:none;padding:0;margin:2em 0;display:inline-flex}ul.tabmenu li{white-space:nowrap;margin:0 0.5em;padding:0;text-align:center}ul.tabmenu li a{display:block;text-decoration:none;padding:1em;margin:0;color:#333;border-radius:2em}ul.tabmenu li a:hover{background:#ffe9b3}ul.tabmenu li.active a{font-weight:bold;background:white;color:#333}abbr,acronym{font-style:normal;font-variant:normal}abbr[title],acronym[title]{border-bottom:1px dotted;cursor:help}a:link abbr[title],a:visited abbr[title],a:link acronym[title],a:visited acronym[title]{cursor:pointer}code{font-family:monospace;white-space:pre}#maincontent ul{margin-left:2em}.warning{color:red;background-color:white;font-weight:bold}.clear{clear:both}.error{color:#ff0000;background-color:white}div.hostinfo{margin:0;padding:0;font-size:80%;padding:0.5em;flex:1;font-weight:bold}#xhr_poll_status{cursor:pointer}#xhr_poll_status #xhr_poll_status_off{font-weight:bold;color:#FF0000}#xhr_poll_status #xhr_poll_status_on{font-weight:bold;color:#00FF00}#menubar{display:flex;background:#dc0067;color:#ffffff}#menubar .warning{color:red;background-color:#557788}#menubar a:link,#menubar a:visited{position:relative;display:block;padding:0.5em;text-decoration:none;font-size:80%;font-weight:normal;color:white}#menubar a:link:hover,#menubar a:visited:hover,#menubar a:link:focus,#menubar a:visited:focus{background:#ffb400;color:black}#menubar a:link.active,#menubar a:visited.active{background:#ffb400;color:black;font-weight:bold}#menubar a:link.warning,#menubar a:visited.warning{background:#000000;color:red;font-weight:bold}#modemenu{list-style:none;margin:0;padding:0}#modemenu li{display:inline-block}.lang_de #submenu_admin_uci{width:12em}.lang_ru #submenu_admin_uci{width:11.5em}textarea#syslog{width:98%;min-height:500px;border:3px solid #cccccc;padding:5px;font-family:monospace}#maincontent{padding:0 1em 2em;max-width:60em;min-width:40em;margin:1em auto}.lang_he #maincontent{direction:rtl}#maincontent p{margin-bottom:1em}.cbi-section{margin:0;padding:0;border:none}.cbi-section legend{font-size:1.4em;font-weight:bold;color:#dc0067;position:relative;padding:0;margin-bottom:0.5em}.cbi-section h2{margin:0em 0 0.5em -0.5em !important}.cbi-section h3{text-decoration:none !important;font-weight:bold !important;color:#555555 !important;margin:0.25em !important;font-size:100% !important}.cbi-section-descr{margin-bottom:2em}.cbi-title-ref{color:inherit;text-decoration:none;padding-right:18px;background:url("../resources/cbi/link.gif") no-repeat scroll right center;background-color:inherit}ul.cbi-apply{font-size:90%}input:-webkit-input-placeholder{color:#AAAAAA}input:-moz-placeholder{color:#AAAAAA}input:-ms-input-placeholder{color:#AAAAAA}input[type=checkbox]{display:none}input[type=checkbox]+label{display:inline-block;width:1em;height:1em;margin:0}input[type=checkbox]:checked+label:after{content:'✔';color:#dc0067;vertical-align:middle;position:absolute;top:50%;left:0;margin-top:-0.5em;width:100%;text-align:center;font-size:1.7em}input[type=submit],input[type=reset],input[type=image],input[type=button]{cursor:pointer}select,input,textarea,input[type=checkbox]+label{color:#003247;border:none;background:#ffe199;border-radius:3pt;padding:0.5em}input[type=image]{border:none}select,input[type=text],input[type=password]{width:20em}td select,td input[type=text],td input[type=password]{width:99%}img.cbi-image-button{cursor:pointer;margin:0 2px;vertical-align:middle}input.cbi-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:baseline;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:100%;padding:0.5em 1em;color:rgba(0,0,0,0.8);border:none transparent;background-color:#E6E6E6;text-decoration:none;border-radius:2px;-webkit-transition:0.1s linear -webkit-box-shadow;-moz-transition:0.1s linear -moz-box-shadow;-ms-transition:0.1s linear box-shadow;-o-transition:0.1s linear box-shadow;transition:0.1s linear box-shadow;background-repeat:no-repeat}input.cbi-button::-moz-focus-inner{padding:0;border:0}input.cbi-button:active{box-shadow:0 0 0 1px rgba(0,0,0,0.15) inset,0 0 6px rgba(0,0,0,0.2) inset}input.cbi-button:focus{outline:0}input.cbi-button:hover,input.cbi-button:focus{background-image:-webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0,0.05)), to(rgba(0,0,0,0.1)));background-image:-webkit-linear-gradient(transparent, rgba(0,0,0,0.05) 40%, rgba(0,0,0,0.1));background-image:-moz-linear-gradient(top, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.1));background-image:-o-linear-gradient(transparent, rgba(0,0,0,0.05) 40%, rgba(0,0,0,0.1));background-image:linear-gradient(transparent, rgba(0,0,0,0.05) 40%, rgba(0,0,0,0.1))}input.cbi-button[disabled]{border:none;background-image:none;opacity:0.40;cursor:not-allowed;box-shadow:none}input.cbi-input-user{background-image:url("../resources/cbi/user.gif");background-repeat:no-repeat;background-position:1px center;color:#000000;text-indent:17px}input.cbi-input-find,input.cbi-button-find{background-image:url("../resources/cbi/find.gif");color:#000000;padding-left:17px}input.cbi-input-reload{background-image:url("../resources/cbi/reload.gif");color:#000000;padding-left:17px}input.cbi-input-add,input.cbi-button-add{background-image:url("../resources/cbi/add.gif");color:#000000;padding-left:17px;padding-right:1px}input.cbi-input-fieldadd,input.cbi-button-fieldadd{background-image:url(../resources/cbi/fieldadd.gif);color:#000000;padding-left:17px;padding-right:1px}input.cbi-input-reset,input.cbi-button-reset{background-color:#e30;color:#fff}input.cbi-input-save,input.cbi-button-save{background-color:#009ee0;color:#fff}input.cbi-input-apply,input.cbi-button-apply{background-color:#009ee0;color:#fff}input.cbi-input-link,input.cbi-button-link{background-image:url("../resources/cbi/link.gif");color:#000000;padding-left:17px;padding-right:1px}input.cbi-input-download,input.cbi-button-download{background-image:url("../resources/cbi/download.gif");color:#000000;padding-left:17px;padding-right:1px}input.cbi-input-remove,div.cbi-section-remove input{background-image:url("../resources/cbi/remove.gif");color:#000000;padding-left:17px;padding-right:1px}input.cbi-button-up{background-image:url("../resources/cbi/up.gif");padding-left:11px;padding-right:1px}input.cbi-button-down{background-image:url("../resources/cbi/down.gif");padding-left:11px;padding-right:1px}input.cbi-button-edit{background-image:url("../resources/cbi/edit.gif");color:#000000;padding-left:17px;padding-right:1px}input.cbi-button-reload{background-image:url("../resources/cbi/reload.gif");color:#000000;padding-left:17px;padding-right:1px}input.cbi-button-remove{background-image:url("../resources/cbi/remove.gif");color:#000000;padding-left:17px;padding-right:1px}.cbi-input-invalid{background:#e30 !important;color:white}div.cbi-section-remove input{border-bottom:none}textarea{margin-left:-1px;margin-bottom:0.5em}form>div>input[type=submit],form>div>input[type=reset]{margin-left:0.5em}table td,table th{color:#000000}table.smalltext{background:#f5f5f5;color:#000000;border-top:1px solid #666666;border-right:1px solid #666666;border-bottom:1px solid #666666;font-size:90%;width:80%;margin-left:auto;margin-right:auto;border-collapse:collapse}table.smalltext tr:hover td{background-color:#bbddee;color:#000000}table.smalltext tr th{padding:0 0.25em;border-left:1px solid #666666;text-align:left}table.smalltext tr td{padding:0 0.25em;border-top:1px solid #666666;border-left:1px solid #666666}table.cbi-section-table .cbi-rowstyle-1{background-color:#eeeeff;color:#000000}table.cbi-section-table .cbi-rowstyle-1:hover,table.cbi-section-table .cbi-rowstyle-2:hover{background-color:#b2c8d4;color:#000000}table.cbi-section-table .cbi-section-table-cell{padding:3px;white-space:nowrap}.cbi-section .cbi-rowstyle-1 h3{background-color:#eeeeff;color:#555555}.cbi-rowstyle-2{color:#000000}div.cbi-value{display:flex;flex-direction:row;margin-bottom:0.5em}.cbi-value-title{flex:2;text-align:right;padding-right:1em;font-weight:bold}div.cbi-value-field{flex:3;position:relative}div.cbi-value-field input,div.cbi-value-field select,div.cbi-value-field input+label{position:relative;top:-0.39em}div.cbi-value-description{font-size:8pt}div.cbi-section-create{clear:left;white-space:nowrap;vertical-align:top}div.cbi-section-create .cbi-button{margin:0.25em}input.cbi-section-create-name{margin-right:-0.25em}div.cbi-map-descr{margin-bottom:1em}.cbi-map-descr:empty,.cbi-section-descr:empty{display:none}.cbi-map-descr,.cbi-section-descr,.cbi-page-actions{padding:1em;background:#ececec}.cbi-page-actions{text-align:right;display:flex;display:-moz-flex;-moz-flex-flow:row-reverse;flex-flow:row-reverse}div.cbi-optionals{padding:0.25em;border-bottom:1px dotted #bbbbbb}div.cbi-section-remove{float:right}.cbi-section-node{clear:both;position:relative;border:none}.cbi-section-node-tabbed{border-top-left-radius:0}.cbi-section-node .cbi-value-last{border-bottom:none}.cbi-section-node table div{padding-bottom:0;border-bottom:none}.cbi-section-node div.cbi-section-table-row{margin:0.25em}table.cbi-section-table{width:100%;font-size:95%}table.cbi-section-table th,table.cbi-section-table td{text-align:center}tr.cbi-section-table-descr th{font-weight:normal;font-size:90%;vertical-align:top}td.cbi-section-table-optionals{text-align:left !important;padding-top:1em}.cbi-value-helpicon img{display:none}div.cbi-error{font-size:95%;font-weight:bold;color:#ff0000;background-color:#ffffff}td.cbi-value-error{border-color:red}.cbi-value-error input,.cbi-value-error select{background-color:#ffcccc}.cbi-section-error{color:red;background-color:white;font-size:95%;border:1px dotted red;margin:3px;padding:3px}.cbi-value-field var{color:#2222FF}ul.cbi-tabmenu{padding:3px 0;margin-left:0 !important;list-style-type:none;position:relative;z-index:10;top:4px;line-height:20px}ul.cbi-tabmenu li.cbi-tab,ul.cbi-tabmenu li.cbi-tab-disabled{display:inline;margin:0}ul.cbi-tabmenu li.cbi-tab a,ul.cbi-tabmenu li.cbi-tab-disabled a{text-decoration:none;padding:3px 7px;margin-right:3px;border:1px solid #BBBBBB;border-bottom:none;border-radius:3px 3px 0 0;background-color:#EEEEEE;color:#BBBBBB}ul.cbi-tabmenu li.cbi-tab-highlighted a{color:#000000;background-color:#FFEEAA}ul.cbi-tabmenu li a:hover{color:#000000}ul.cbi-tabmenu li.cbi-tab a{padding-top:4px;color:#000000;background-color:#FFFFFF}div.cbi-tab-descr{background-image:url(/luci-static/resources/cbi/help.gif);background-position:0.25em 50%;background-repeat:no-repeat;border-bottom:1px solid #CCCCCC;margin:0.25em 0.25em 2em;padding:0.5em 0.5em 0.5em 2em}.left{text-align:left !important}.right{text-align:right !important}.luci{position:absolute;bottom:0;left:1em;height:1.5em;font-size:80%}.luci a:link,.luci a:visited{background-color:transparent;color:#666666;text-decoration:none;font-size:70%}.inline{display:inline}.error500{white-space:normal;border:1px dotted #ff0000;background-color:#ffffff;color:#000000;padding:0.5em}.errorbox{border:1px solid #FF0000;background-color:#FFCCCC;padding:5px;margin-bottom:5px}.errorbox a{color:#000000 !important}.ifacebox{background-color:#FFFFFF;border:1px solid #CCCCCC;margin:0 10px;text-align:center;white-space:nowrap}.ifacebox .ifacebox-head{border-bottom:1px solid #CCCCCC;padding:2px}.ifacebox .ifacebox-body{padding:2px}.ifacebadge{background-color:#FFFFFF;border:1px solid #CCCCCC;padding:2px;margin-left:2px;display:inline-block}.ifacebadge-active{border-color:#000000;font-weight:bold}.zonebadge{padding:2px;display:inline-block;white-space:nowrap;cursor:pointer}.zonebadge em,.zonebadge strong{margin:3px;display:inline-block}.zonebadge input{width:6em;height:1.5em}.zonebadge-empty{border:1px dashed #AAAAAA;color:#AAAAAA;font-style:italic;font-size:smaller}.uci-change-list{font-family:monospace}.uci-change-list ins,.uci-change-legend-label ins{text-decoration:none;border:1px solid #00FF00;background-color:#CCFFCC;display:block;padding:2px}.uci-change-list del,.uci-change-legend-label del{text-decoration:none;border:1px solid #FF0000;background-color:#FFCCCC;display:block;font-style:normal;padding:2px}.uci-change-list var,.uci-change-legend-label var{text-decoration:none;border:1px solid #CCCCCC;background-color:#EEEEEE;display:block;font-style:normal;padding:2px}.uci-change-list var ins,.uci-change-list var del{border:none;white-space:pre;font-style:normal;padding:0px}.uci-change-legend{padding:5px}.uci-change-legend-label{width:150px;float:left;font-size:80%}.uci-change-legend-label>ins,.uci-change-legend-label>del,.uci-change-legend-label>var{float:left;margin-right:4px;width:10px;height:10px;display:block}.uci-change-legend-label var ins,.uci-change-legend-label var del{line-height:6px;border:none}.cbi-input-password+img{display:none}.the-key{text-align:left;font-size:1.4em;background:#ffe9b3;border:3pt dashed #dc0067;margin-bottom:0.5em;padding:0.5em} diff --git a/package/gluon-luci-theme/sass/cascade.scss b/package/gluon-luci-theme/sass/cascade.scss new file mode 100644 index 00000000..80e407be --- /dev/null +++ b/package/gluon-luci-theme/sass/cascade.scss @@ -0,0 +1,1019 @@ +/* ATTENTION: This file is not compiled when building gluon. + The compiled version is at ../files/www/luci-static/gluon/cascade.css + + Use sass like this to update it: + + sass cascade.scss ../files/www/luci-static/gluon/cascade.css + + When commiting changes to this file make sure to commit the respective + changes to the compilid version within the same commit! + */ + +@charset "utf-8"; + +$ffyellow: #ffb400; +$ffmagenta: #dc0067; +$ffzusatz: #009ee0; +$red: #ee3300; + +@mixin button { + &::-moz-focus-inner { + padding: 0; + border: 0; + } + + display: inline-block; + zoom: 1; + line-height: normal; + white-space: nowrap; + vertical-align: baseline; + text-align: center; + cursor: pointer; + -webkit-user-drag: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + + font-size: 100%; + padding: 0.5em 1em; + color: rgba(0, 0, 0, 0.80); + border: none rgba(0, 0, 0, 0); + background-color: #E6E6E6; + text-decoration: none; + border-radius: 2px; + + /* Transitions */ + -webkit-transition: 0.1s linear -webkit-box-shadow; + -moz-transition: 0.1s linear -moz-box-shadow; + -ms-transition: 0.1s linear box-shadow; + -o-transition: 0.1s linear box-shadow; + transition: 0.1s linear box-shadow; + + &:active { + box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) inset; + } + + &:focus { + outline: 0; + } + + &:hover, &:focus { + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0,0,0, 0.05)), to(rgba(0,0,0, 0.10))); + background-image: -webkit-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); + background-image: -moz-linear-gradient(top, rgba(0,0,0, 0.05) 0%, rgba(0,0,0, 0.10)); + background-image: -o-linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); + background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); + } + + &[disabled] { + border: none; + background-image: none; + opacity: 0.40; + cursor: not-allowed; + box-shadow: none; + } +} + +@mixin button-primary { + background-color: $ffzusatz; + color: #fff; +} + +.lang_he { + direction: RTL; + unicode-bidi: embed; +} + +.hidden { + display: none; +} + +html { + min-height: 100%; + height: auto; + position:relative; +} + +body, input, select, option { + font-family: 'Open Sans', Arial, sans-serif; + font-size: 12pt; +} + +body { + color: rgb(77, 78, 83); + line-height: 1.5em; + margin: 0; + display: flex; + flex-direction: column; + min-height: 100vh; + background-color: #f3f3f3; +} + +a img { + border: none; + text-decoration: none; +} + +.tabmenu1 { + text-align: center; +} + +ul.tabmenu { + list-style: none; + padding: 0; + margin: 2em 0; + display: inline-flex; +} + +ul.tabmenu li { + white-space: nowrap; + margin: 0 0.5em; + padding: 0; + text-align: center; + + a { + display: block; + text-decoration: none; + padding: 1em; + margin: 0; + color: #333; + border-radius: 2em; + + &:hover { + background: lighten($ffyellow, 35); + } + } + + &.active a { + font-weight: bold; + background: white; + color: #333; + } +} + +abbr, +acronym { + font-style: normal; + font-variant: normal; +} + +abbr[title], +acronym[title] { + border-bottom: 1px dotted; + cursor: help; +} + +a:link abbr[title], +a:visited abbr[title], +a:link acronym[title], +a:visited acronym[title] { + cursor: pointer; +} + +code { + font-family: monospace; + white-space: pre; +} + +#maincontent ul { + margin-left: 2em; +} + +.warning { + color: red; + background-color: white; + font-weight: bold; +} + +.clear { + clear: both; +} + +.error { + color: #ff0000; + background-color: white; +} + +div.hostinfo { + margin: 0; + padding: 0; + font-size: 80%; + padding: 0.5em; + flex: 1; + font-weight: bold; +} + +#xhr_poll_status { + cursor: pointer; +} + +#xhr_poll_status #xhr_poll_status_off { + font-weight: bold; + color: #FF0000; +} + +#xhr_poll_status #xhr_poll_status_on { + font-weight: bold; + color: #00FF00; +} + +#menubar { + display: flex; + background: $ffmagenta; + color: #ffffff; +} + +#menubar .warning { + color: red; + background-color: #557788; +} + +#menubar a:link, +#menubar a:visited { + position: relative; + display: block; + padding: 0.5em; + text-decoration: none; + font-size: 80%; + font-weight: normal; + color: white; +} + +#menubar a:link:hover, +#menubar a:visited:hover, +#menubar a:link:focus, +#menubar a:visited:focus { + background: $ffyellow; + color: black; +} + +#menubar a:link.active, +#menubar a:visited.active { + background: $ffyellow; + color: black; + font-weight: bold; +} + +#menubar a:link.warning, +#menubar a:visited.warning { + background: #000000; + color: red; + font-weight: bold; +} + +#modemenu { + list-style: none; + margin: 0; + padding: 0; +} + +#modemenu li { + display: inline-block; +} + +#savemenu { +} + +.lang_he #savemenu { +} + +.lang_de #submenu_admin_uci { + width: 12em; +} + +.lang_ru #submenu_admin_uci { + width: 11.5em; +} + +textarea#syslog { + width: 98%; + min-height: 500px; + border: 3px solid #cccccc; + padding: 5px; + font-family: monospace; +} + +#maincontent { + padding: 0 1em 2em; + max-width: 60em; + min-width: 40em; + margin: 1em auto; +} + +.lang_he #maincontent { + direction: rtl; +} + +#maincontent h2 { +} + +#maincontent h3 { +} + +#maincontent p { + margin-bottom: 1em; +} + +.cbi-section { + margin: 0; + padding: 0; + border: none; +} + +.cbi-section legend { + font-size: 1.4em; + font-weight: bold; + color: $ffmagenta; + position: relative; + padding: 0; + margin-bottom: 0.5em; +} + +.cbi-section h2 { + margin: 0em 0 0.5em -0.5em !important; +} + +.cbi-section h3 { + text-decoration: none !important; + font-weight: bold !important; + color: #555555 !important; + margin: 0.25em !important; + font-size: 100% !important; +} + +.cbi-section-descr { + margin-bottom: 2em; +} + +.cbi-title-ref { + color: inherit; + text-decoration: none; + padding-right: 18px; + background: url('../resources/cbi/link.gif') no-repeat scroll right center; + background-color: inherit; +} + +ul.cbi-apply { + font-size: 90%; +} + +input:-webkit-input-placeholder { + color: #AAAAAA; +} + +input:-moz-placeholder { + color: #AAAAAA; +} + +input:-ms-input-placeholder { + color: #AAAAAA; +} + +input[type=checkbox] { + display: none; + + & + label { + display: inline-block; + width: 1em; + height: 1em; + margin: 0; + } + + &:checked + label:after { + content: '✔'; + color: $ffmagenta; + vertical-align: middle; + position: absolute; + top: 50%; + left: 0; + margin-top: -0.5em; + width: 100%; + text-align: center; + font-size: 1.7em; + } +} + +input[type=submit], +input[type=reset], +input[type=image], +input[type=button] { + cursor: pointer; +} + +select, +input, +textarea, +input[type=checkbox] + label { + color: darken($ffzusatz, 30); + border: none; + background: lighten($ffyellow, 30); + border-radius: 3pt; + padding: 0.5em; +} + +input[type=image] { + border: none; +} + +select, +input[type=text], +input[type=password] { + width: 20em; +} + +td select, +td input[type=text], +td input[type=password] { + width: 99%; +} + +img.cbi-image-button { + cursor: pointer; + margin: 0 2px; + vertical-align: middle; +} + +input.cbi-button { + @include button; + + background-repeat: no-repeat; +} + +input.cbi-input-user { + background-image: url('../resources/cbi/user.gif'); + background-repeat: no-repeat; + background-position: 1px center; + color: #000000; + text-indent: 17px; +} + +input.cbi-input-find, +input.cbi-button-find { + background-image: url('../resources/cbi/find.gif'); + color: #000000; + padding-left: 17px; +} + +input.cbi-input-reload { + background-image: url('../resources/cbi/reload.gif'); + color: #000000; + padding-left: 17px; +} + +input.cbi-input-add, +input.cbi-button-add { + background-image: url('../resources/cbi/add.gif'); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +input.cbi-input-fieldadd, +input.cbi-button-fieldadd { + background-image: url(../resources/cbi/fieldadd.gif); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +input.cbi-input-reset, +input.cbi-button-reset { + background-color: $red; + color: #fff; +} + +input.cbi-input-save, +input.cbi-button-save { + @include button-primary; +} + +input.cbi-input-apply, +input.cbi-button-apply { + @include button-primary; +} + +input.cbi-input-link, +input.cbi-button-link { + background-image: url('../resources/cbi/link.gif'); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +input.cbi-input-download, +input.cbi-button-download { + background-image: url('../resources/cbi/download.gif'); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +input.cbi-input-remove, +div.cbi-section-remove input { + background-image: url('../resources/cbi/remove.gif'); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +input.cbi-button-up { + background-image: url('../resources/cbi/up.gif'); + padding-left: 11px; + padding-right: 1px; +} + +input.cbi-button-down { + background-image: url('../resources/cbi/down.gif'); + padding-left: 11px; + padding-right: 1px; +} + +input.cbi-button-edit { + background-image: url('../resources/cbi/edit.gif'); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +input.cbi-button-reload { + background-image: url('../resources/cbi/reload.gif'); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +input.cbi-button-remove { + background-image: url('../resources/cbi/remove.gif'); + color: #000000; + padding-left: 17px; + padding-right: 1px; +} + +.cbi-input-invalid { + background: $red !important; + color: white; +} + +div.cbi-section-remove input { + border-bottom: none; +} + +textarea { + margin-left: -1px; + margin-bottom: 0.5em; +} + +form > div > input[type=submit], +form > div > input[type=reset] { + margin-left: 0.5em; +} + +table td, +table th { + color: #000000; +} + +table.smalltext { + background: #f5f5f5; + color: #000000; + border-top: 1px solid #666666; + border-right: 1px solid #666666; + border-bottom: 1px solid #666666; + font-size: 90%; + width: 80%; + margin-left: auto; + margin-right: auto; + border-collapse: collapse; +} + +table.smalltext tr:hover td { + background-color: #bbddee; + color: #000000; +} + +table.smalltext tr th { + padding: 0 0.25em; + border-left: 1px solid #666666; + text-align: left; +} + +table.smalltext tr td { + padding: 0 0.25em; + border-top: 1px solid #666666; + border-left: 1px solid #666666; +} + +table.cbi-section-table .cbi-rowstyle-1 { + background-color: #eeeeff; + color: #000000; +} + +table.cbi-section-table .cbi-rowstyle-1:hover, +table.cbi-section-table .cbi-rowstyle-2:hover { + background-color: #b2c8d4; + color: #000000; +} + +table.cbi-section-table .cbi-section-table-cell { + padding: 3px; + white-space: nowrap; +} + +.cbi-section .cbi-rowstyle-1 h3 { + background-color: #eeeeff; + color: #555555; +} + +.cbi-rowstyle-2 { + color: #000000; +} + +div.cbi-value { + display: flex; + flex-direction: row; + margin-bottom: 0.5em; +} + +.cbi-value-title { + flex: 2; + text-align: right; + padding-right: 1em; + font-weight: bold; +} + +div.cbi-value-field { + flex: 3; + position: relative; + + input, select, input + label { + position: relative; + top: -0.39em; + } +} + +div.cbi-value-description { + font-size: 8pt; +} + +div.cbi-section-create { + clear: left; + white-space: nowrap; + vertical-align: top; +} + +div.cbi-section-create .cbi-button { + margin: 0.25em; +} + +input.cbi-section-create-name { + margin-right: -0.25em; +} + +div.cbi-map-descr { + margin-bottom: 1em; +} + +.cbi-map-descr:empty, .cbi-section-descr:empty { + display: none; +} + +.cbi-map-descr, .cbi-section-descr, .cbi-page-actions { + padding: 1em; + background: #ececec; +} + +.cbi-page-actions { + text-align: right; + display: flex; + display: -moz-flex; + -moz-flex-flow: row-reverse; + flex-flow: row-reverse; +} + +div.cbi-optionals { + padding: 0.25em; + border-bottom: 1px dotted #bbbbbb; +} + +div.cbi-section-remove { + float: right; +} + +.cbi-section-node { + clear: both; + position: relative; + border: none; +} + +.cbi-section-node-tabbed { + border-top-left-radius: 0; +} + +.cbi-section-node .cbi-value-last { + border-bottom: none; +} + +.cbi-section-node table div { + padding-bottom: 0; + border-bottom: none; +} + +.cbi-section-node div.cbi-section-table-row { + margin: 0.25em; +} + +table.cbi-section-table { + width: 100%; + font-size: 95%; +} + +table.cbi-section-table th, +table.cbi-section-table td { + text-align: center; +} + +tr.cbi-section-table-descr th { + font-weight: normal; + font-size: 90%; + vertical-align: top; +} + +td.cbi-section-table-optionals { + text-align: left !important; + padding-top: 1em; +} + +.cbi-value-helpicon img { + display: none; +} + +div.cbi-error { + font-size: 95%; + font-weight: bold; + color: #ff0000; + background-color: #ffffff; +} + +td.cbi-value-error { + border-color: red; +} + +.cbi-value-error input, +.cbi-value-error select { + background-color: #ffcccc; +} + +.cbi-section-error { + color: red; + background-color: white; + font-size: 95%; + border: 1px dotted red; + margin: 3px; + padding: 3px; +} + +.cbi-value-field var { + color: #2222FF; +} + +ul.cbi-tabmenu { + padding: 3px 0; + margin-left: 0 !important; + list-style-type: none; + position: relative; + z-index: 10; + top: 4px; + line-height: 20px; +} + +ul.cbi-tabmenu li.cbi-tab, +ul.cbi-tabmenu li.cbi-tab-disabled { + display: inline; + margin: 0; +} + +ul.cbi-tabmenu li.cbi-tab a, +ul.cbi-tabmenu li.cbi-tab-disabled a { + text-decoration: none; + padding: 3px 7px; + margin-right: 3px; + border: 1px solid #BBBBBB; + border-bottom: none; + border-radius: 3px 3px 0 0; + background-color: #EEEEEE; + color: #BBBBBB; +} + +ul.cbi-tabmenu li.cbi-tab-highlighted a { + color: #000000; + background-color: #FFEEAA; +} + +ul.cbi-tabmenu li a:hover { + color: #000000; +} + +ul.cbi-tabmenu li.cbi-tab a { + padding-top: 4px; + color: #000000; + background-color: #FFFFFF; +} + +div.cbi-tab-descr { + background-image: url(/luci-static/resources/cbi/help.gif); + background-position: 0.25em 50%; + background-repeat: no-repeat; + border-bottom: 1px solid #CCCCCC; + margin: 0.25em 0.25em 2em; + padding: 0.5em 0.5em 0.5em 2em; +} + +.left { + text-align: left !important; +} + +.right { + text-align: right !important; +} + +.luci { + position: absolute; + bottom: 0; + left: 1em; + height: 1.5em; + font-size: 80%; +} + +.luci a:link, +.luci a:visited { + background-color: transparent; + color: #666666; + text-decoration: none; + font-size: 70%; +} + +.inline { + display: inline; +} + +.error500 { + white-space: normal; + border: 1px dotted #ff0000; + background-color: #ffffff; + color: #000000; + padding: 0.5em; +} + +.errorbox { + border: 1px solid #FF0000; + background-color: #FFCCCC; + padding: 5px; + margin-bottom: 5px; +} + +.errorbox a { + color: #000000 !important; +} + + +.ifacebox { + background-color: #FFFFFF; + border: 1px solid #CCCCCC; + margin: 0 10px; + text-align: center; + white-space: nowrap; +} + +.ifacebox .ifacebox-head { + border-bottom: 1px solid #CCCCCC; + padding: 2px; +} + +.ifacebox .ifacebox-body { + padding: 2px; +} + + +.ifacebadge { + background-color: #FFFFFF; + border: 1px solid #CCCCCC; + padding: 2px; + margin-left: 2px; + display: inline-block; +} + +.ifacebadge-active { + border-color: #000000; + font-weight: bold; +} + + +.zonebadge { + padding: 2px; + display: inline-block; + white-space: nowrap; + cursor: pointer; +} + +.zonebadge em, +.zonebadge strong { + margin: 3px; + display: inline-block; +} + +.zonebadge input { + width: 6em; + height: 1.5em; +} + +.zonebadge-empty { + border: 1px dashed #AAAAAA; + color: #AAAAAA; + font-style: italic; + font-size: smaller; +} + + +.uci-change-list { + font-family: monospace; +} + +.uci-change-list ins, +.uci-change-legend-label ins { + text-decoration: none; + border: 1px solid #00FF00; + background-color: #CCFFCC; + display: block; + padding: 2px; +} + +.uci-change-list del, +.uci-change-legend-label del { + text-decoration: none; + border: 1px solid #FF0000; + background-color: #FFCCCC; + display: block; + font-style: normal; + padding: 2px; +} + +.uci-change-list var, +.uci-change-legend-label var { + text-decoration: none; + border: 1px solid #CCCCCC; + background-color: #EEEEEE; + display: block; + font-style: normal; + padding: 2px; +} + +.uci-change-list var ins, +.uci-change-list var del { + /*display: inline;*/ + border: none; + white-space: pre; + font-style: normal; + padding: 0px; +} + +.uci-change-legend { + padding: 5px; +} + +.uci-change-legend-label { + width: 150px; + float: left; + font-size: 80%; +} + +.uci-change-legend-label>ins, +.uci-change-legend-label>del, +.uci-change-legend-label>var { + float: left; + margin-right: 4px; + width: 10px; + height: 10px; + display: block; +} + +.uci-change-legend-label var ins, +.uci-change-legend-label var del { + line-height: 6px; + border: none; +} + +// Hide show/hide password toggle image +.cbi-input-password + img { + display: none; +} + +.the-key { + text-align: left; + font-size: 1.4em; + background: lighten($ffyellow, 35); + border: 3pt dashed $ffmagenta; + margin-bottom: 0.5em; + padding: 0.5em +} diff --git a/package/gluon-mesh-batman-adv-14/Makefile b/package/gluon-mesh-batman-adv-14/Makefile new file mode 100644 index 00000000..be65cafa --- /dev/null +++ b/package/gluon-mesh-batman-adv-14/Makefile @@ -0,0 +1,32 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-mesh-batman-adv-14 +PKG_VERSION:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-mesh-batman-adv-14 + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Support for batman-adv meshing (compat level 14) + DEPENDS:=+gluon-mesh-batman-adv-core +kmod-batman-adv-legacy + PROVIDES:=gluon-mesh-batman-adv +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-mesh-batman-adv-14/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-mesh-batman-adv-14)) diff --git a/package/gluon-mesh-batman-adv-14/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/compat b/package/gluon-mesh-batman-adv-14/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/compat new file mode 100644 index 00000000..84137237 --- /dev/null +++ b/package/gluon-mesh-batman-adv-14/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/compat @@ -0,0 +1 @@ +return 14 diff --git a/package/gluon-mesh-batman-adv-14/files/lib/gluon/upgrade/350-gluon-mesh-batman-adv-14 b/package/gluon-mesh-batman-adv-14/files/lib/gluon/upgrade/350-gluon-mesh-batman-adv-14 new file mode 100755 index 00000000..b3ed1e88 --- /dev/null +++ b/package/gluon-mesh-batman-adv-14/files/lib/gluon/upgrade/350-gluon-mesh-batman-adv-14 @@ -0,0 +1,40 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' + +local uci = require('luci.model.uci').cursor() + + +local function configure_mtu(radio, config) + local mesh = 'mesh_' .. radio + + if config.mesh_vlan then + uci:set('network', mesh, 'mtu', 1532) + uci:set('network', mesh .. '_vlan', 'mtu', 1528) + else + uci:set('network', mesh, 'mtu', 1528) + end +end + + +local radios = {} + +uci:foreach('wireless', 'wifi-device', + function(s) + table.insert(radios, s['.name']) + end +) + +for _, radio in ipairs(radios) do + local hwmode = uci:get('wireless', radio, 'hwmode') + + if hwmode == '11g' or hwmode == '11ng' then + configure_mtu(radio, site.wifi24) + elseif hwmode == '11a' or hwmode == '11na' then + configure_mtu(radio, site.wifi5) + end +end + + +uci:save('network') +uci:commit('network') diff --git a/package/gluon-mesh-batman-adv-15/Makefile b/package/gluon-mesh-batman-adv-15/Makefile new file mode 100644 index 00000000..14a39a61 --- /dev/null +++ b/package/gluon-mesh-batman-adv-15/Makefile @@ -0,0 +1,32 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-mesh-batman-adv-15 +PKG_VERSION:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-mesh-batman-adv-15 + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Support for batman-adv meshing (compat level 15) + DEPENDS:=+gluon-mesh-batman-adv-core +kmod-batman-adv +batctl + PROVIDES:=gluon-mesh-batman-adv +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-mesh-batman-adv-15/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-mesh-batman-adv-15)) diff --git a/package/gluon-mesh-batman-adv-15/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/compat b/package/gluon-mesh-batman-adv-15/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/compat new file mode 100644 index 00000000..d44224b3 --- /dev/null +++ b/package/gluon-mesh-batman-adv-15/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/compat @@ -0,0 +1 @@ +return 15 diff --git a/package/gluon-mesh-batman-adv-15/files/lib/gluon/upgrade/350-gluon-mesh-batman-adv-15 b/package/gluon-mesh-batman-adv-15/files/lib/gluon/upgrade/350-gluon-mesh-batman-adv-15 new file mode 100755 index 00000000..96f7d310 --- /dev/null +++ b/package/gluon-mesh-batman-adv-15/files/lib/gluon/upgrade/350-gluon-mesh-batman-adv-15 @@ -0,0 +1,40 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' + +local uci = require('luci.model.uci').cursor() + + +local function configure_mtu(radio, config) + local mesh = 'mesh_' .. radio + + if config.mesh_vlan then + uci:set('network', mesh, 'mtu', 1536) + uci:set('network', mesh .. '_vlan', 'mtu', 1532) + else + uci:set('network', mesh, 'mtu', 1532) + end +end + + +local radios = {} + +uci:foreach('wireless', 'wifi-device', + function(s) + table.insert(radios, s['.name']) + end +) + +for _, radio in ipairs(radios) do + local hwmode = uci:get('wireless', radio, 'hwmode') + + if hwmode == '11g' or hwmode == '11ng' then + configure_mtu(radio, site.wifi24) + elseif hwmode == '11a' or hwmode == '11na' then + configure_mtu(radio, site.wifi5) + end +end + + +uci:save('network') +uci:commit('network') diff --git a/package/gluon-mesh-batman-adv-core/Makefile b/package/gluon-mesh-batman-adv-core/Makefile new file mode 100644 index 00000000..fc4bc224 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/Makefile @@ -0,0 +1,37 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-mesh-batman-adv-core +PKG_VERSION:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-mesh-batman-adv-core + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Support for batman-adv meshing (core) + DEPENDS:=+gluon-core +firewall +kmod-ipt-nathelper +libiwinfo-lua +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-mesh-batman-adv-core/install + $(CP) ./files/* $(1)/ +endef + +define Package/gluon-mesh-batman-adv-core/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + + +$(eval $(call BuildPackage,gluon-mesh-batman-adv-core)) diff --git a/package/gluon-mesh-batman-adv-core/check_site.lua b/package/gluon-mesh-batman-adv-core/check_site.lua new file mode 100644 index 00000000..8226d5dd --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/check_site.lua @@ -0,0 +1,13 @@ +need_string('regdom') + +for _, config in ipairs({'wifi24', 'wifi5'}) do + need_string(config .. '.ssid') + need_number(config .. '.channel') + need_string(config .. '.htmode') + need_string(config .. '.mesh_ssid') + need_string_match(config .. '.mesh_bssid', '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$') + need_number(config .. '.mesh_mcast_rate') + need_number(config .. '.mesh_vlan', false) +end + +need_boolean('mesh_on_wan', false) diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/batadv b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/batadv new file mode 100644 index 00000000..f93a11f9 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/batadv @@ -0,0 +1,41 @@ +local json = require 'luci.json' +local util = require 'luci.util' +local fs = require 'nixio.fs' + +local ifname_address_cache = {} + +function ifname2address(ifname) + local ifaddress + if ifname_address_cache[ifname] ~= nil then + ifaddress = ifname_address_cache[ifname] + else + ifaddress = util.trim(fs.readfile("/sys/class/net/" .. ifname .. "/address")) + ifname_address_cache[ifname] = ifaddress + end + + return ifaddress +end + +function batadv() + local interfaces = {} + local list = io.lines("/sys/kernel/debug/batman_adv/bat0/originators") + for line in list do + local mac1, lastseen, tq, mac2, ifname = + line:match("^([0-9a-f:]+) +(%d+%.%d+)s +%( *(%d+)%) +([0-9a-f:]+) +%[ *(.-)%]") + + if mac1 ~= nil and mac1 == mac2 then + ifaddress = ifname2address(ifname) + if interfaces[ifaddress] == nil then + interfaces[ifaddress] = { neighbours = {} } + end + + interfaces[ifaddress].neighbours[mac1] = { tq = tonumber(tq) + , lastseen = tonumber(lastseen) + } + end + end + + return interfaces +end + +return batadv() diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/wifi b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/wifi new file mode 100644 index 00000000..d3754222 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/neighbours.d/wifi @@ -0,0 +1,41 @@ +local json = require 'luci.json' +local util = require 'luci.util' +local fs = require 'nixio.fs' +local iwinfo = require 'iwinfo' + +function neighbours(iface) + local stations = {} + for k, v in pairs(iface.iw.assoclist(iface.ifname)) do + stations[k:lower()] = { signal = v.signal + , noise = v.noise + , inactive = v.inactive + } + end + + return stations +end + +function interfaces() + local interfaces = {} + for _, line in ipairs(util.split(util.exec('batctl if'))) do + ifname = line:match('^(.-): active') + if ifname ~= nil then + pcall(function() + local address = util.trim(fs.readfile('/sys/class/net/' .. ifname .. '/address')) + local wifitype = iwinfo.type(ifname) + if wifitype ~= nil then + interfaces[address] = { ifname = ifname, iw = iwinfo[wifitype] } + end + end) + end + end + + return interfaces +end + +local wifi = {} +for address, iface in pairs(interfaces()) do + wifi[address] = { neighbours = neighbours(iface) } +end + +return wifi diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/addresses b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/addresses new file mode 100644 index 00000000..d55f8340 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/addresses @@ -0,0 +1,12 @@ +local ip = require 'luci.ip' + +local addresses = {} + +for line in io.lines('/proc/net/if_inet6') do + local matches = { line:match('^' .. string.rep('(%x%x%x%x)', 8) .. string.rep(' %x%x', 4) .. '%s+([^%s]+)$') } + if matches[9] == 'br-client' then + table.insert(addresses, ip.IPv6(string.format('%s:%s:%s:%s:%s:%s:%s:%s', unpack(matches))):string():lower()) + end +end + +return addresses diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh_interfaces b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh_interfaces new file mode 100644 index 00000000..1fef5e10 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/network/mesh_interfaces @@ -0,0 +1,15 @@ +local list = util.exec('batctl if') + +local interfaces = {} +for _, line in ipairs(util.split(list)) do + local ifname = line:match('^(.-):') + if ifname ~= nil then + pcall( + function() + table.insert(interfaces, util.trim(fs.readfile('/sys/class/net/' .. ifname .. '/address'))) + end + ) + end +end + +return interfaces diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/version b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/version new file mode 100644 index 00000000..abb317e9 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/nodeinfo.d/software/batman-adv/version @@ -0,0 +1 @@ +return util.trim(fs.readfile('/sys/module/batman_adv/version')) diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/clients b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/clients new file mode 100644 index 00000000..235865ed --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/clients @@ -0,0 +1,20 @@ +local list = io.lines("/sys/kernel/debug/batman_adv/bat0/transtable_local") + +local count = 0 +local wifi = 0 +for line in list do + local mac, _, flags, lastseen = line:match("^ %* ([0-9a-f:]+) *(.- )%[(.-)%] +(%d+%.%d+)") + if mac then + if not flags:match('P') then + count = count + 1 + + if flags:match('W') then + wifi = wifi +1 + end + end + end +end + +return { total = count + , wifi = wifi + } diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/gateway b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/gateway new file mode 100644 index 00000000..a1be9ac0 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/gateway @@ -0,0 +1,5 @@ +local gateway = util.trim(util.exec("batctl -m bat0 gateways | awk '/^=>/ { print $2 }'")) + +if gateway ~= '' then + return gateway +end diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/traffic b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/traffic new file mode 100644 index 00000000..01f6b4ab --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/announce/statistics.d/traffic @@ -0,0 +1,14 @@ +local ethtool = require 'ethtool_stats' + +local fields = ethtool.interface_stats('bat0') + +local traffic = {} +for _, class in ipairs({'rx', 'tx', 'forward', 'mgmt_rx', 'mgmt_tx'}) do + traffic[class] = { + bytes = fields[class .. '_bytes'], + packets = fields[class], + } +end +traffic['tx']['dropped'] = fields['tx_dropped'] + +return traffic diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/300-gluon-mesh-batman-adv-core-wan b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/300-gluon-mesh-batman-adv-core-wan new file mode 100755 index 00000000..f84a104d --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/300-gluon-mesh-batman-adv-core-wan @@ -0,0 +1,14 @@ +#!/usr/bin/lua + +local sysconfig = require 'gluon.sysconfig' +local util = require 'gluon.util' +local uci = require('luci.model.uci').cursor() + + +if sysconfig.wan_ifname:match('%.') then + -- fix up duplicate mac addresses (for mesh-on-WAN) + uci:set('network', 'wan', 'macaddr', util.generate_mac(1, 0)) + uci:save('network') + uci:commit('network') +end + diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/310-gluon-mesh-batman-adv-core-mesh b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/310-gluon-mesh-batman-adv-core-mesh new file mode 100755 index 00000000..c9638c4b --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/310-gluon-mesh-batman-adv-core-mesh @@ -0,0 +1,97 @@ +#!/usr/bin/lua + +local sysconfig = require 'gluon.sysconfig' +local sysctl = require 'gluon.sysctl' +local uci = require('luci.model.uci').cursor() + + +uci:delete('batman-adv', 'bat0') +uci:section('batman-adv', 'mesh', 'bat0', + { + orig_interval = 5000, + gw_mode = 'client', + multicast_mode = 0, + } +) +uci:save('batman-adv') +uci:commit('batman-adv') + + +if not uci:get('network', 'client') then + local ifname + + if sysconfig.lan_ifname then + ifname = sysconfig.lan_ifname .. ' bat0' + else + ifname = 'bat0' + end + + uci:section('network', 'interface', 'client', + { + ifname = ifname, + type = 'bridge', + proto = 'dhcpv6', + reqprefix = 'no', + } + ) +end + +uci:set('network', 'client', 'igmp_snooping', 0) +uci:set('network', 'client', 'macaddr', sysconfig.primary_mac) +uci:set('network', 'client', 'peerdns', 1) + +uci:delete('network', 'bat0') +uci:section('network', 'interface', 'bat0', + { + ifname = 'bat0', + proto = 'none', + macaddr = sysconfig.primary_mac, + } +) + +uci:save('network') +uci:commit('network') + + +uci:delete('firewall', 'client') +uci:section('firewall', 'zone', 'client', + { + name = 'client', + network = {'client'}, + input = 'ACCEPT', + output = 'ACCEPT', + forward = 'REJECT', + } +) + +uci:section('firewall', 'rule', 'client_dns', + { + name = 'client_dns', + src = 'client', + dest_port = '53', + target = 'REJECT', + } +) + +uci:save('firewall') +uci:commit('firewall') + + +local dnsmasq = uci:get_first('dhcp', 'dnsmasq') +uci:set('dhcp', dnsmasq, 'boguspriv', 0) +uci:set('dhcp', dnsmasq, 'localise_queries', 0) +uci:set('dhcp', dnsmasq, 'rebind_protection', 0) + +uci:delete('dhcp', 'client') +uci:section('dhcp', 'dhcp', 'client', + { + interface = 'client', + ignore = 1, + } +) + +uci:save('dhcp') +uci:commit('dhcp') + + +sysctl.set('net.ipv6.conf.br-client.forwarding', 0) diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/320-gluon-mesh-batman-adv-core-wireless b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/320-gluon-mesh-batman-adv-core-wireless new file mode 100755 index 00000000..004483e9 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/320-gluon-mesh-batman-adv-core-wireless @@ -0,0 +1,102 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local util = require 'gluon.util' + +local uci = require('luci.model.uci').cursor() + + +local function configure_radio(radio, index, config) + uci:delete('wireless', radio, 'disabled') + + uci:set('wireless', radio, 'channel', config.channel) + uci:set('wireless', radio, 'htmode', config.htmode) + uci:set('wireless', radio, 'country', site.regdom) + + local client = 'client_' .. radio + local mesh = 'mesh_' .. radio + + local client_ifname + local mesh_ifname + local radio_suffix = radio:match('^radio(%d+)$') + if radio_suffix then + client_ifname = 'client' .. radio_suffix + mesh_ifname = 'mesh' .. radio_suffix + end + + uci:delete('wireless', client) + uci:section('wireless', 'wifi-iface', client, + { + device = radio, + network = 'client', + mode = 'ap', + ssid = config.ssid, + macaddr = util.generate_mac(2, index), + ifname = client_ifname, + } + ) + + uci:delete('network', mesh) + uci:delete('network', mesh .. '_vlan') + + if config.mesh_vlan then + uci:section('network', 'interface', mesh, + { + proto = 'none', + } + ) + uci:section('network', 'interface', mesh .. '_vlan', + { + ifname = '@' .. mesh .. '.' .. config.mesh_vlan, + proto = 'batadv', + mesh = 'bat0', + } + ) + else + uci:section('network', 'interface', mesh, + { + proto = 'batadv', + mesh = 'bat0', + } + ) + end + + uci:delete('wireless', mesh) + uci:section('wireless', 'wifi-iface', mesh, + { + device = radio, + network = mesh, + mode = 'adhoc', + ssid = config.mesh_ssid, + bssid = config.mesh_bssid, + macaddr = util.generate_mac(3, index), + mcast_rate = config.mesh_mcast_rate, + ifname = mesh_ifname, + } + ) +end + + +local radios = {} + +uci:foreach('wireless', 'wifi-device', + function(s) + table.insert(radios, s['.name']) + end +) + +for index, radio in ipairs(radios) do + local hwmode = uci:get('wireless', radio, 'hwmode') + + if hwmode == '11g' or hwmode == '11ng' then + configure_radio(radio, index, site.wifi24) + elseif hwmode == '11a' or hwmode == '11na' then + configure_radio(radio, index, site.wifi5) + end +end + + +uci:save('wireless') +uci:save('network') +uci:commit('wireless') +uci:commit('network') diff --git a/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/330-gluon-mesh-batman-adv-core-mesh-on-wan b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/330-gluon-mesh-batman-adv-core-mesh-on-wan new file mode 100755 index 00000000..d40c5729 --- /dev/null +++ b/package/gluon-mesh-batman-adv-core/files/lib/gluon/upgrade/330-gluon-mesh-batman-adv-core-mesh-on-wan @@ -0,0 +1,18 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require 'luci.model.uci' + +local c = uci.cursor() + +if not c:get('network', 'mesh_wan') then + c:section('network', 'interface', 'mesh_wan', + { ifname = 'br-wan' + , proto = 'batadv' + , mesh = 'bat0' + , auto = site.mesh_on_wan and 1 or 0 + }) +end + +c:save('network') +c:commit('network') diff --git a/package/gluon-mesh-vpn-fastd/Makefile b/package/gluon-mesh-vpn-fastd/Makefile new file mode 100644 index 00000000..1547985c --- /dev/null +++ b/package/gluon-mesh-vpn-fastd/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-mesh-vpn-fastd +PKG_VERSION:=3 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-mesh-vpn-fastd + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Support for connecting batman-adv meshes via fastd + DEPENDS:=+gluon-core +gluon-mesh-batman-adv +gluon-wan-dnsmasq +fastd +iptables-mod-extra +endef + +define Package/gluon-mesh-vpn-fastd/description + Gluon community wifi mesh firmware framework: fastd support +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-mesh-vpn-fastd/install + $(CP) ./files/* $(1)/ +endef + +define Package/gluon-mesh-vpn-fastd/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-mesh-vpn-fastd)) diff --git a/package/gluon-mesh-vpn-fastd/check_site.lua b/package/gluon-mesh-vpn-fastd/check_site.lua new file mode 100644 index 00000000..fc9d8221 --- /dev/null +++ b/package/gluon-mesh-vpn-fastd/check_site.lua @@ -0,0 +1,14 @@ +need_string_array 'fastd_mesh_vpn.methods' +need_number 'fastd_mesh_vpn.mtu' +need_number 'fastd_mesh_vpn.backbone.limit' + + +local function check_peer(k, _) + local prefix = string.format('fastd_mesh_vpn.backbone.peers[%q].', k) + + need_string(prefix .. 'key') + need_string_array(prefix .. 'remotes') +end + +need_table('fastd_mesh_vpn.backbone.peers', check_peer) +need_boolean('fastd_mesh_vpn.enabled', false) diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/nodeinfo.d/software/fastd b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/nodeinfo.d/software/fastd new file mode 100644 index 00000000..bbfbda08 --- /dev/null +++ b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/nodeinfo.d/software/fastd @@ -0,0 +1,4 @@ +return { + enabled = uci:get_bool('fastd', 'mesh_vpn', 'enabled'), + version = util.exec('fastd -v'):match('^[^%s]+%s+([^\n]+)'), +} diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/statistics.d/mesh_vpn b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/statistics.d/mesh_vpn new file mode 100644 index 00000000..5e4e6a7a --- /dev/null +++ b/package/gluon-mesh-vpn-fastd/files/lib/gluon/announce/statistics.d/mesh_vpn @@ -0,0 +1,32 @@ +local nixio = require('nixio') +local ltn12 = require('luci.ltn12') +local json = require('luci.json') +local uci = require('luci.model.uci').cursor() + +local socket_path = uci:get('fastd', 'mesh_vpn', 'status_socket') +local fastd_sock = nixio.socket('unix', 'stream') + +if not fastd_sock:connect(socket_path) then + return nil +end + +decoder = json.Decoder() +ltn12.pump.all(ltn12.source.file(fastd_sock), decoder:sink()) + +local status = decoder:get() + +local output = {} + +for key, peer in pairs(status.peers) do + local name, valid = peer.name:gsub('^mesh_vpn_backbone_peer_', '') + if valid == 1 then + if peer.connection then + output[name] = {} + output[name].established = peer.connection.established/1000 + else + output[name] = json.null + end + end +end + +return output diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn-fastd/iptables.rules b/package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn-fastd/iptables.rules new file mode 100644 index 00000000..c1a16ee1 --- /dev/null +++ b/package/gluon-mesh-vpn-fastd/files/lib/gluon/mesh-vpn-fastd/iptables.rules @@ -0,0 +1,3 @@ +*nat +-I OUTPUT -m owner --gid-owner gluon-fastd -o lo -d 127.0.0.1 -p udp --dport 53 -j DNAT --to-destination :54 +COMMIT diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/upgrade/400-mesh-vpn-fastd b/package/gluon-mesh-vpn-fastd/files/lib/gluon/upgrade/400-mesh-vpn-fastd new file mode 100755 index 00000000..0585f5a7 --- /dev/null +++ b/package/gluon-mesh-vpn-fastd/files/lib/gluon/upgrade/400-mesh-vpn-fastd @@ -0,0 +1,93 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local users = require 'gluon.users' +local util = require 'gluon.util' + +local uci = require('luci.model.uci').cursor() + + +-- The previously used user is removed, we need root privileges to use the packet_mark option +users.remove_user('gluon-fastd') + +-- Group for iptables rule +users.add_group('gluon-fastd', 800) + + +local enabled = uci:get('fastd', 'mesh_vpn', 'enabled') +if not enabled then + enabled = site.fastd_mesh_vpn.enabled and 1 or 0 +end + + +uci:section('fastd', 'fastd', 'mesh_vpn', + { + enabled = enabled, + group = 'gluon-fastd', + syslog_level = 'verbose', + interface = 'mesh-vpn', + mode = 'tap', + mtu = site.fastd_mesh_vpn.mtu, + secure_handshakes = 1, + method = site.fastd_mesh_vpn.methods, + packet_mark = 1, + status_socket = '/var/run/fastd.mesh_vpn.socket', + } +) +uci:delete('fastd', 'mesh_vpn', 'user') + +uci:delete('fastd', 'mesh_vpn_backbone') +uci:section('fastd', 'peer_group', 'mesh_vpn_backbone', + { + enabled = 1, + net = 'mesh_vpn', + peer_limit = site.fastd_mesh_vpn.backbone.limit, + } +) + +uci:delete_all('fastd', 'peer', + function(peer) + return peer.net == 'mesh_vpn' and peer.group == 'mesh_vpn_backbone' + end +) + +for name, config in pairs(site.fastd_mesh_vpn.backbone.peers) do + uci:section('fastd', 'peer', 'mesh_vpn_backbone_peer_' .. name, + { + enabled = 1, + net = 'mesh_vpn', + group = 'mesh_vpn_backbone', + key = config.key, + remote = config.remotes, + } + ) +end + +uci:save('fastd') +uci:commit('fastd') + + +uci:section('network', 'interface', 'mesh_vpn', + { + ifname = 'mesh-vpn', + proto = 'batadv', + mesh = 'bat0', + mesh_no_rebroadcast = 1, + macaddr = util.generate_mac(4, 0), + } +) + +uci:save('network') +uci:commit('network') + + +uci:section('firewall', 'include', 'mesh_vpn_dns', + { + type = 'restore', + path = '/lib/gluon/mesh-vpn-fastd/iptables.rules', + family = 'ipv4', + } +) + +uci:save('firewall') +uci:commit('firewall') diff --git a/package/gluon-mesh-vpn-fastd/files/lib/gluon/upgrade/410-mesh-vpn-fastd-generate-secret b/package/gluon-mesh-vpn-fastd/files/lib/gluon/upgrade/410-mesh-vpn-fastd-generate-secret new file mode 100755 index 00000000..68a2fe67 --- /dev/null +++ b/package/gluon-mesh-vpn-fastd/files/lib/gluon/upgrade/410-mesh-vpn-fastd-generate-secret @@ -0,0 +1,13 @@ +#!/usr/bin/lua + +local uci = require 'luci.model.uci' + +local c = uci.cursor() + +local secret = c:get("fastd", "mesh_vpn", "secret") + +if not secret or not secret:match(("%x"):rep(64)) then + c:set("fastd", "mesh_vpn", "secret", "generate") + c:save("fastd") + c:commit("fastd") +end diff --git a/package/gluon-neighbour-info/Makefile b/package/gluon-neighbour-info/Makefile new file mode 100644 index 00000000..74aa6e8f --- /dev/null +++ b/package/gluon-neighbour-info/Makefile @@ -0,0 +1,39 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-neighbour-info +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-neighbour-info + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=neighbour-info + DEPENDS:= +endef + +define Package/gluon-neighbour-info/description + Gluon community wifi mesh firmware framework: neighbour-info +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Configure +endef + +define Build/Compile + CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) +endef + +define Package/gluon-neighbour-info/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-neighbour-info $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,gluon-neighbour-info)) diff --git a/package/gluon-neighbour-info/src/Makefile b/package/gluon-neighbour-info/src/Makefile new file mode 100644 index 00000000..16a3e95f --- /dev/null +++ b/package/gluon-neighbour-info/src/Makefile @@ -0,0 +1,6 @@ +all: gluon-neighbour-info + +gluon-neighbour-info: gluon-neighbour-info.c + +clean: + rm gluon-neighbour-info diff --git a/package/gluon-neighbour-info/src/gluon-neighbour-info.c b/package/gluon-neighbour-info/src/gluon-neighbour-info.c new file mode 100644 index 00000000..d8536de6 --- /dev/null +++ b/package/gluon-neighbour-info/src/gluon-neighbour-info.c @@ -0,0 +1,191 @@ +/* + Copyright (c) 2014, Nils Schneider + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void usage() { + puts("Usage: gluon-neighbour-info [-h] [-s] [-t ] -d -p -i -r "); + puts(" -p UDP port"); + puts(" -d multicast group, e.g. ff02:0:0:0:0:0:2:1001"); + puts(" -i interface, e.g. eth0 "); + puts(" -r request, e.g. nodeinfo"); + puts(" -t timeout in seconds (default: 3)"); + puts(" -s output as server-sent events"); + puts(" -h this help\n"); +} + +void getclock(struct timeval *tv) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} + +/* Assumes a and b are normalized */ +void tv_subtract (struct timeval *r, struct timeval *a, struct timeval *b) { + r->tv_usec = a->tv_usec - b->tv_usec; + r->tv_sec = a->tv_sec - b->tv_sec; + + if (r->tv_usec < 0) { + r->tv_usec += 1000000; + r->tv_sec -= 1; + } +} + +ssize_t recvtimeout(int socket, void *buffer, size_t length, int flags, struct timeval *timeout, struct timeval *offset) { + struct timeval now, delta; + ssize_t ret; + + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout)); + ret = recv(socket, buffer, length, flags); + + getclock(&now); + tv_subtract(&delta, &now, offset); + tv_subtract(timeout, timeout, &delta); + + return ret; +} + +int request(const int sock, const struct sockaddr_in6 *client_addr, const char *request, bool sse, int timeout) { + ssize_t ret; + char buffer[8192]; + + ret = sendto(sock, request, strlen(request), 0, (struct sockaddr *)client_addr, sizeof(struct sockaddr_in6)); + + if (ret < 0) { + perror("Error in sendto()"); + exit(EXIT_FAILURE); + } + + struct timeval tv_timeout, tv_offset; + tv_timeout.tv_sec = timeout; + tv_timeout.tv_usec = 0; + + getclock(&tv_offset); + + while (1) { + ret = recvtimeout(sock, buffer, sizeof(buffer), 0, &tv_timeout, &tv_offset); + + if (ret < 0) + break; + + if (sse) + fputs("event: neighbour\ndata: ", stdout); + + fwrite(buffer, sizeof(char), ret, stdout); + + if (sse) + fputs("\n\n", stdout); + else + fputs("\n", stdout); + + fflush(stdout); + } + + return 0; +} + +int main(int argc, char **argv) { + int sock; + struct sockaddr_in6 client_addr = {}; + char *request_string = NULL; + struct in6_addr mgroup_addr; + + sock = socket(PF_INET6, SOCK_DGRAM, 0); + + if (sock < 0) { + perror("creating socket"); + exit(EXIT_FAILURE); + } + + client_addr.sin6_family = AF_INET6; + client_addr.sin6_addr = in6addr_any; + + opterr = 0; + + int port_set = 0; + int destination_set = 0; + int timeout = 3; + bool sse = false; + + int c; + while ((c = getopt(argc, argv, "p:d:r:i:t:sh")) != -1) + switch (c) { + case 'p': + client_addr.sin6_port = htons(atoi(optarg)); + break; + case 'd': + if (!inet_pton(AF_INET6, optarg, &client_addr.sin6_addr)) { + perror("Invalid IPv6 address. This message will probably confuse you"); + exit(EXIT_FAILURE); + } + break; + case 'i': + client_addr.sin6_scope_id = if_nametoindex(optarg); + if (client_addr.sin6_scope_id == 0) { + perror("Can not use interface"); + exit(EXIT_FAILURE); + } + break; + case 'r': + request_string = optarg; + break; + case 't': + timeout = atoi(optarg); + break; + case 's': + sse = true; + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + break; + default: + fprintf(stderr, "Invalid parameter %c ignored.\n", c); + } + + if (request_string == NULL) + error(EXIT_FAILURE, 0, "No request string supplied"); + + if (sse) + fputs("Content-Type: text/event-stream\n\n", stdout); + + request(sock, &client_addr, request_string, sse, timeout); + + if (sse) + fputs("event: eot\ndata: null\n\n", stdout); + + return EXIT_SUCCESS; +} diff --git a/package/gluon-next-node/Makefile b/package/gluon-next-node/Makefile new file mode 100644 index 00000000..df27ecbe --- /dev/null +++ b/package/gluon-next-node/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-next-node +PKG_VERSION:=3 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-next-node + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Next-node anycast address + DEPENDS:=+gluon-core +gluon-ebtables +gluon-mesh-batman-adv +kmod-macvlan +endef + +define Package/gluon-next-node/description + Gluon community wifi mesh firmware framework: next-node anycast address +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-next-node/install + $(CP) ./files/* $(1)/ +endef + +define Package/gluon-next-node/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-next-node)) diff --git a/package/gluon-next-node/check_site.lua b/package/gluon-next-node/check_site.lua new file mode 100644 index 00000000..83889a8e --- /dev/null +++ b/package/gluon-next-node/check_site.lua @@ -0,0 +1,4 @@ +need_string_match('next_node.ip4', '^%d+.%d+.%d+.%d+$') +need_string_match('next_node.ip6', '^[%x:]+$') + +need_string_match('next_node.mac', '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$') diff --git a/package/gluon-next-node/files/lib/gluon/ebtables/250-next-node b/package/gluon-next-node/files/lib/gluon/ebtables/250-next-node new file mode 100644 index 00000000..0df7abcc --- /dev/null +++ b/package/gluon-next-node/files/lib/gluon/ebtables/250-next-node @@ -0,0 +1,20 @@ +local site = require 'gluon.site_config' +local next_node = site.next_node + +rule('FORWARD --logical-in br-client -p ARP --arp-ip-src ' .. next_node.ip4 .. ' -j DROP') +rule('FORWARD --logical-in br-client -p ARP --arp-ip-dst ' .. next_node.ip4 .. ' -j DROP') + +rule('FORWARD --logical-out br-client -o bat0 -d ' .. next_node.mac .. ' -j DROP') +rule('OUTPUT --logical-out br-client -o bat0 -d ' .. next_node.mac .. ' -j DROP') +rule('FORWARD --logical-out br-client -o bat0 -s ' .. next_node.mac .. ' -j DROP') +rule('OUTPUT --logical-out br-client -o bat0 -s ' .. next_node.mac .. ' -j DROP') + +rule('FORWARD --logical-out br-client -o bat0 -p IPv4 --ip-destination ' .. next_node.ip4 .. ' -j DROP') +rule('OUTPUT --logical-out br-client -o bat0 -p IPv4 --ip-destination ' .. next_node.ip4 .. ' -j DROP') +rule('FORWARD --logical-out br-client -o bat0 -p IPv4 --ip-source ' .. next_node.ip4 .. ' -j DROP') +rule('OUTPUT --logical-out br-client -o bat0 -p IPv4 --ip-source ' .. next_node.ip4 .. ' -j DROP') + +rule('FORWARD --logical-out br-client -o bat0 -p IPv6 --ip6-destination ' .. next_node.ip6 .. ' -j DROP') +rule('OUTPUT --logical-out br-client -o bat0 -p IPv6 --ip6-destination ' .. next_node.ip6 .. ' -j DROP') +rule('FORWARD --logical-out br-client -o bat0 -p IPv6 --ip6-source ' .. next_node.ip6 .. ' -j DROP') +rule('OUTPUT --logical-out br-client -o bat0 -p IPv6 --ip6-source ' .. next_node.ip6 .. ' -j DROP') diff --git a/package/gluon-next-node/files/lib/gluon/upgrade/400-next-node b/package/gluon-next-node/files/lib/gluon/upgrade/400-next-node new file mode 100755 index 00000000..0ff959fe --- /dev/null +++ b/package/gluon-next-node/files/lib/gluon/upgrade/400-next-node @@ -0,0 +1,55 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require 'luci.model.uci' +local ip = require 'luci.ip' + +local c = uci.cursor() + + +c:delete('network', 'local_node_dev') +c:section('network', 'device', 'local_node_dev', + { + name = 'local-node', + ifname = 'br-client', + type = 'macvlan', + macaddr = site.next_node.mac, + } +) + +local prefix4 = ip.IPv4(site.prefix4) +c:delete('network', 'local_node') +c:section('network', 'interface', 'local_node', + { + ifname = 'local-node', + proto = 'static', + ipaddr = site.next_node.ip4, + netmask = prefix4:mask():string(), + ip6addr = site.next_node.ip6 .. '/128', + } +) + +c:delete('network', 'local_node_route6') +c:section('network', 'route6', 'local_node_route6', + { + interface = 'client', + target = site.prefix6, + gateway = '::', + } +) + +c:save('network') +c:commit('network') + +c:delete('firewall', 'local_node') +c:section('firewall', 'zone', 'local_node', + { + name = 'local_node', + network = {'local_node'}, + input = 'ACCEPT', + output = 'ACCEPT', + forward = 'REJECT', + } +) +c:save('firewall') +c:commit('firewall') diff --git a/package/gluon-node-info/Makefile b/package/gluon-node-info/Makefile new file mode 100644 index 00000000..0fe8a8dd --- /dev/null +++ b/package/gluon-node-info/Makefile @@ -0,0 +1,41 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-node-info +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-node-info + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Add /etc/config/gluon-node-info to uci + DEPENDS:=+gluon-core +endef + +define Package/gluon-node-info/description + This packages creates /etc/config/gluon-node-info. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-node-info/install + $(CP) ./files/* $(1)/ +endef + +define Package/gluon-node-info/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-node-info)) diff --git a/package/gluon-node-info/check_site.lua b/package/gluon-node-info/check_site.lua new file mode 100644 index 00000000..d8d46408 --- /dev/null +++ b/package/gluon-node-info/check_site.lua @@ -0,0 +1,8 @@ +local function check_role(k, _) + local role = string.format('roles.list[%q]', k) + + need_string(role) +end + +need_string('roles.default', false) +need_table('roles.list', check_role, false) diff --git a/package/gluon-node-info/files/etc/config/gluon-node-info b/package/gluon-node-info/files/etc/config/gluon-node-info new file mode 100644 index 00000000..8f6f472f --- /dev/null +++ b/package/gluon-node-info/files/etc/config/gluon-node-info @@ -0,0 +1,6 @@ +config location + option share_location '0' + +config owner + +config system diff --git a/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/location b/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/location new file mode 100644 index 00000000..72bf8878 --- /dev/null +++ b/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/location @@ -0,0 +1,7 @@ +if uci:get_first('gluon-node-info', 'location', 'share_location', false) then + return { + latitude = tonumber(uci:get_first('gluon-node-info', 'location', 'latitude')), + longitude = tonumber(uci:get_first('gluon-node-info', 'location', 'longitude')), + altitude = tonumber(uci:get_first('gluon-node-info', 'location', 'altitude')), + } +end diff --git a/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/owner b/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/owner new file mode 100644 index 00000000..8a2a611d --- /dev/null +++ b/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/owner @@ -0,0 +1,4 @@ +local contact = uci:get_first('gluon-node-info', 'owner', 'contact', '') +if contact ~= '' then + return { contact = contact } +end diff --git a/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/system/role b/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/system/role new file mode 100644 index 00000000..38de47d7 --- /dev/null +++ b/package/gluon-node-info/files/lib/gluon/announce/nodeinfo.d/system/role @@ -0,0 +1,4 @@ +local role = uci:get_first('gluon-node-info', 'system', 'role', '') +if role ~= '' then + return role +end diff --git a/package/gluon-node-info/files/lib/gluon/upgrade/500-node-info-system b/package/gluon-node-info/files/lib/gluon/upgrade/500-node-info-system new file mode 100755 index 00000000..a17b9461 --- /dev/null +++ b/package/gluon-node-info/files/lib/gluon/upgrade/500-node-info-system @@ -0,0 +1,11 @@ +#!/usr/bin/lua + +local uci = require('luci.model.uci').cursor() + +local config = 'gluon-node-info' + +if not uci:get_first(config, 'system') then + uci:section(config, 'system') + uci:save(config) + uci:commit(config) +end diff --git a/package/gluon-node-info/files/lib/gluon/upgrade/510-node-info-role b/package/gluon-node-info/files/lib/gluon/upgrade/510-node-info-role new file mode 100755 index 00000000..6e54a234 --- /dev/null +++ b/package/gluon-node-info/files/lib/gluon/upgrade/510-node-info-role @@ -0,0 +1,19 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require('luci.model.uci').cursor() + +local config = 'gluon-node-info' +local role = uci:get(config, uci:get_first(config, 'system'), 'role') + +if site.roles then + default_role = site.roles.default +else + default_role = '' +end + +if not role then + uci:set(config, uci:get_first(config, 'system'), 'role', default_role) + uci:save(config) + uci:commit(config) +end diff --git a/package/gluon-radvd/Makefile b/package/gluon-radvd/Makefile new file mode 100644 index 00000000..4736af60 --- /dev/null +++ b/package/gluon-radvd/Makefile @@ -0,0 +1,39 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-radvd +PKG_VERSION:=3 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-radvd + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Advertise an IPv6 prefix from the node + DEPENDS:=+gluon-core +gluon-ebtables +gluon-mesh-batman-adv +librt +endef + +define Package/gluon-radvd/description + Gluon community wifi mesh firmware framework: Advertise an IPv6 prefix from the node +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Configure +endef + +define Build/Compile + CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) +endef + +define Package/gluon-radvd/install + $(CP) ./files/* $(1)/ + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-radvd $(1)/usr/sbin/ +endef + +$(eval $(call BuildPackage,gluon-radvd)) diff --git a/package/gluon-radvd/files/etc/init.d/gluon-radvd b/package/gluon-radvd/files/etc/init.d/gluon-radvd new file mode 100755 index 00000000..14815e76 --- /dev/null +++ b/package/gluon-radvd/files/etc/init.d/gluon-radvd @@ -0,0 +1,15 @@ +#!/bin/sh /etc/rc.common + +START=50 + +SERVICE_WRITE_PID=1 +SERVICE_DAEMONIZE=1 + + +start() { + service_start /usr/sbin/gluon-radvd -i br-client -p $(lua -e 'print(require("gluon.site_config").prefix6)') +} + +stop() { + service_stop /usr/sbin/gluon-radvd +} diff --git a/package/gluon-radvd/files/lib/gluon/ebtables/300-radv-input-output b/package/gluon-radvd/files/lib/gluon/ebtables/300-radv-input-output new file mode 100644 index 00000000..377d11cd --- /dev/null +++ b/package/gluon-radvd/files/lib/gluon/ebtables/300-radv-input-output @@ -0,0 +1,2 @@ +rule 'INPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -i bat0 -j DROP' +rule 'OUTPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -o bat0 -j DROP' diff --git a/package/gluon-radvd/files/lib/gluon/upgrade/500-radvd-remove-user b/package/gluon-radvd/files/lib/gluon/upgrade/500-radvd-remove-user new file mode 100755 index 00000000..036406af --- /dev/null +++ b/package/gluon-radvd/files/lib/gluon/upgrade/500-radvd-remove-user @@ -0,0 +1,5 @@ +#!/usr/bin/lua + +local users = require 'gluon.users' + +users.remove_user('gluon-radvd') diff --git a/package/gluon-radvd/src/Makefile b/package/gluon-radvd/src/Makefile new file mode 100644 index 00000000..f0bc9034 --- /dev/null +++ b/package/gluon-radvd/src/Makefile @@ -0,0 +1,4 @@ +all: gluon-radvd + +gluon-radvd: gluon-radvd.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -lrt diff --git a/package/gluon-radvd/src/gluon-radvd.c b/package/gluon-radvd/src/gluon-radvd.c new file mode 100644 index 00000000..bd57eabc --- /dev/null +++ b/package/gluon-radvd/src/gluon-radvd.c @@ -0,0 +1,647 @@ +/* + Copyright (c) 2014, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + + +#define MAX_PREFIXES 8 + +/* These are in seconds */ +#define AdvValidLifetime 86400u +#define AdvPreferredLifetime 14400u +#define AdvDefaultLifetime 0u +#define AdvCurHopLimit 64u + +#define MinRtrAdvInterval 200u +#define MaxRtrAdvInterval 600u + +/* And these in milliseconds */ +#define MAX_RA_DELAY_TIME 500u +#define MIN_DELAY_BETWEEN_RAS 3000u + + +struct icmpv6_opt { + uint8_t type; + uint8_t length; + uint8_t data[6]; +}; + + +struct iface { + bool ok; + unsigned int ifindex; + struct in6_addr ifaddr; + uint8_t mac[6]; +}; + +static struct global { + struct iface iface; + + struct timespec time; + struct timespec next_advert; + struct timespec next_advert_earliest; + + int icmp_sock; + int rtnl_sock; + + const char *ifname; + + size_t n_prefixes; + struct in6_addr prefixes[MAX_PREFIXES]; +} G = { + .rtnl_sock = -1, + .icmp_sock = -1, +}; + + +static inline void exit_errno(const char *message) { + error(1, errno, "error: %s", message); +} + +static inline void warn_errno(const char *message) { + error(0, errno, "warning: %s", message); +} + + +static inline void update_time(void) { + clock_gettime(CLOCK_MONOTONIC, &G.time); +} + +/* Compares two timespecs and returns true if tp1 is after tp2 */ +static inline bool timespec_after(const struct timespec *tp1, const struct timespec *tp2) { + return (tp1->tv_sec > tp2->tv_sec || + (tp1->tv_sec == tp2->tv_sec && tp1->tv_nsec > tp2->tv_nsec)); +} + +/* Returns (tp1 - tp2) in milliseconds */ +static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) { + return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6; +} + +static inline void timespec_add(struct timespec *tp, unsigned int ms) { + tp->tv_sec += ms/1000; + tp->tv_nsec += (ms%1000) * 1e6; + + if (tp->tv_nsec >= 1e9) { + tp->tv_nsec -= 1e9; + tp->tv_sec++; + } +} + + +static inline int setsockopt_int(int socket, int level, int option, int value) { + return setsockopt(socket, level, option, &value, sizeof(value)); +} + + +static void init_random(void) { + unsigned int seed; + int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + exit_errno("can't open /dev/urandom"); + + if (read(fd, &seed, sizeof(seed)) != sizeof(seed)) + exit_errno("can't read from /dev/urandom"); + + close(fd); + + srandom(seed); +} + +static inline int rand_range(int min, int max) { + unsigned int r = (unsigned int)random(); + return (r%(max-min) + min); +} + +static void init_icmp(void) { + G.icmp_sock = socket(AF_INET6, SOCK_RAW|SOCK_NONBLOCK, IPPROTO_ICMPV6); + if (G.icmp_sock < 0) + exit_errno("can't open ICMP socket"); + + setsockopt_int(G.icmp_sock, IPPROTO_RAW, IPV6_CHECKSUM, 2); + + setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255); + setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 1); + + setsockopt_int(G.icmp_sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 1); + + struct icmp6_filter filter; + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter); + setsockopt(G.icmp_sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)); +} + +static void init_rtnl(void) { + G.rtnl_sock = socket(AF_NETLINK, SOCK_DGRAM|SOCK_NONBLOCK, NETLINK_ROUTE); + if (G.rtnl_sock < 0) + exit_errno("can't open RTNL socket"); + + struct sockaddr_nl snl = { + .nl_family = AF_NETLINK, + .nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR, + }; + if (bind(G.rtnl_sock, (struct sockaddr *)&snl, sizeof(snl)) < 0) + exit_errno("can't bind RTNL socket"); +} + + +static void schedule_advert(bool nodelay) { + struct timespec t = G.time; + + if (nodelay) + timespec_add(&t, rand_range(0, MAX_RA_DELAY_TIME)); + else + timespec_add(&t, rand_range(MinRtrAdvInterval*1000, MaxRtrAdvInterval*1000)); + + if (timespec_after(&G.next_advert_earliest, &t)) + t = G.next_advert_earliest; + + if (!nodelay || timespec_after(&G.next_advert, &t)) + G.next_advert = t; +} + + +static int join_multicast(void) { + struct ipv6_mreq mreq = { + .ipv6mr_multiaddr = { + .s6_addr = { + /* all-routers address */ + 0xff, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, + } + }, + .ipv6mr_interface = G.iface.ifindex, + }; + + if (setsockopt(G.icmp_sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == 0) { + return 2; + } + else if (errno != EADDRINUSE) { + warn_errno("can't join multicast group"); + return 0; + } + + return 1; +} + +static void update_interface(void) { + struct iface old; + + memcpy(&old, &G.iface, sizeof(struct iface)); + memset(&G.iface, 0, sizeof(struct iface)); + + /* Update ifindex */ + G.iface.ifindex = if_nametoindex(G.ifname); + if (!G.iface.ifindex) + return; + + /* Update MAC address */ + struct ifreq ifr = {}; + strncpy(ifr.ifr_name, G.ifname, sizeof(ifr.ifr_name)-1); + if (ioctl(G.icmp_sock, SIOCGIFHWADDR, &ifr) < 0) + return; + + memcpy(G.iface.mac, ifr.ifr_hwaddr.sa_data, sizeof(G.iface.mac)); + + struct ifaddrs *addrs, *addr; + if (getifaddrs(&addrs) < 0) { + warn_errno("getifaddrs"); + return; + } + + memset(&G.iface.ifaddr, 0, sizeof(G.iface.ifaddr)); + + for (addr = addrs; addr; addr = addr->ifa_next) { + if (!addr->ifa_addr || addr->ifa_addr->sa_family != AF_INET6) + continue; + + const struct sockaddr_in6 *in6 = (const struct sockaddr_in6 *)addr->ifa_addr; + if (!IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) + continue; + + if (strncmp(addr->ifa_name, G.ifname, IFNAMSIZ-1) != 0) + continue; + + G.iface.ifaddr = in6->sin6_addr; + } + + freeifaddrs(addrs); + + if (IN6_IS_ADDR_UNSPECIFIED(&G.iface.ifaddr)) + return; + + int joined = join_multicast(); + if (!joined) + return; + + setsockopt(G.icmp_sock, SOL_SOCKET, SO_BINDTODEVICE, G.ifname, strnlen(G.ifname, IFNAMSIZ-1)); + + G.iface.ok = true; + + if (memcmp(&old, &G.iface, sizeof(struct iface)) != 0 || joined == 2) + schedule_advert(true); +} + + +static bool handle_rtnl_link(uint16_t type, const struct ifinfomsg *msg) { + switch (type) { + case RTM_NEWLINK: + if (!G.iface.ok) + return true; + + break; + + case RTM_SETLINK: + if ((unsigned)msg->ifi_index == G.iface.ifindex) + return true; + + if (!G.iface.ok) + return true; + + break; + + case RTM_DELLINK: + if (G.iface.ok && (unsigned)msg->ifi_index == G.iface.ifindex) + return true; + } + + return false; +} + +static bool handle_rtnl_addr(uint16_t type, const struct ifaddrmsg *msg) { + switch (type) { + case RTM_NEWADDR: + if (!G.iface.ok && (unsigned)msg->ifa_index == G.iface.ifindex) + return true; + + break; + + case RTM_DELADDR: + if (G.iface.ok && (unsigned)msg->ifa_index == G.iface.ifindex) + return true; + } + + return false; +} + +static bool handle_rtnl_msg(uint16_t type, const void *data) { + switch (type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_SETLINK: + return handle_rtnl_link(type, data); + + case RTM_NEWADDR: + case RTM_DELADDR: + return handle_rtnl_addr(type, data); + + default: + return false; + } +} + +static void handle_rtnl(void) { + char buffer[4096]; + + ssize_t len = recv(G.rtnl_sock, buffer, sizeof(buffer), 0); + if (len < 0) { + warn_errno("recv"); + return; + } + + const struct nlmsghdr *nh; + for (nh = (struct nlmsghdr *)buffer; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) { + switch (nh->nlmsg_type) { + case NLMSG_DONE: + return; + + case NLMSG_ERROR: + error(1, 0, "error: netlink error"); + + default: + if (handle_rtnl_msg(nh->nlmsg_type, NLMSG_DATA(nh))) { + update_interface(); + return; + } + } + } +} + +static void add_pktinfo(struct msghdr *msg) { + struct cmsghdr *cmsg = (struct cmsghdr*)((char*)msg->msg_control + msg->msg_controllen); + + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + + msg->msg_controllen += cmsg->cmsg_len; + + struct in6_pktinfo pktinfo = { + .ipi6_addr = G.iface.ifaddr, + .ipi6_ifindex = G.iface.ifindex, + }; + + memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo)); +} + + +static void handle_solicit(void) { + struct sockaddr_in6 addr; + + uint8_t buffer[1500] __attribute__((aligned(8))); + struct iovec vec = { .iov_base = buffer, .iov_len = sizeof(buffer) }; + + uint8_t cbuf[1024] __attribute__((aligned(8))); + + + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(addr), + .msg_iov = &vec, + .msg_iovlen = 1, + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + }; + + ssize_t len = recvmsg(G.icmp_sock, &msg, 0); + if (len < (ssize_t)sizeof(struct nd_router_solicit)) { + if (len < 0) + warn_errno("recvmsg"); + + return; + } + + struct cmsghdr *cmsg; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != IPPROTO_IPV6) + continue; + + if (cmsg->cmsg_type != IPV6_HOPLIMIT) + continue; + + if (*(int*)CMSG_DATA(cmsg) != 255) + return; + + break; + } + + const struct nd_router_solicit *s = (struct nd_router_solicit *)buffer; + if (s->nd_rs_hdr.icmp6_type != ND_ROUTER_SOLICIT || s->nd_rs_hdr.icmp6_code != 0) + return; + + const struct icmpv6_opt *opt = (struct icmpv6_opt *)(buffer + sizeof(struct nd_router_solicit)), *end = (struct icmpv6_opt *)(buffer+len); + + for (; opt < end; opt += opt->length) { + if (opt+1 < end) + return; + + if (!opt->length) + return; + + if (opt+opt->length < end) + return; + + if (opt->type == ND_OPT_SOURCE_LINKADDR && IN6_IS_ADDR_UNSPECIFIED(&addr.sin6_addr)) + return; + } + + if (opt != end) + return; + + schedule_advert(true); +} + +static void send_advert(void) { + if (!G.iface.ok) + return; + + struct nd_router_advert advert = { + .nd_ra_hdr = { + .icmp6_type = ND_ROUTER_ADVERT, + .icmp6_dataun.icmp6_un_data8 = {AdvCurHopLimit, 0 /* Flags */, (AdvDefaultLifetime>>8) & 0xff, AdvDefaultLifetime & 0xff }, + }, + }; + + struct icmpv6_opt lladdr = {ND_OPT_SOURCE_LINKADDR, 1, {}}; + memcpy(lladdr.data, G.iface.mac, sizeof(G.iface.mac)); + + struct nd_opt_prefix_info prefixes[G.n_prefixes]; + + size_t i; + for (i = 0; i < G.n_prefixes; i++) { + prefixes[i] = (struct nd_opt_prefix_info){ + .nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION, + .nd_opt_pi_len = 4, + .nd_opt_pi_prefix_len = 64, + .nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_AUTO|ND_OPT_PI_FLAG_ONLINK, + .nd_opt_pi_valid_time = htonl(AdvValidLifetime), + .nd_opt_pi_preferred_time = htonl(AdvPreferredLifetime), + .nd_opt_pi_prefix = G.prefixes[i], + }; + } + + struct iovec vec[3] = { + { .iov_base = &advert, .iov_len = sizeof(advert) }, + { .iov_base = &lladdr, .iov_len = sizeof(lladdr) }, + { .iov_base = prefixes, .iov_len = sizeof(prefixes) }, + }; + + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_addr = { + .s6_addr = { + /* all-nodes address */ + 0xff, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + } + }, + .sin6_scope_id = G.iface.ifindex, + }; + + uint8_t cbuf[1024] __attribute__((aligned(8))) = {}; + + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(addr), + .msg_iov = vec, + .msg_iovlen = 3, + .msg_control = cbuf, + .msg_controllen = 0, + .msg_flags = 0, + }; + + add_pktinfo(&msg); + + if (sendmsg(G.icmp_sock, &msg, 0) < 0) { + G.iface.ok = false; + return; + } + + G.next_advert_earliest = G.time; + timespec_add(&G.next_advert_earliest, MIN_DELAY_BETWEEN_RAS); + + schedule_advert(false); +} + + +static void usage(void) { + fprintf(stderr, "Usage: gluon-radvd [-h] -i -p [ -p ... ]\n"); +} + +static void add_prefix(const char *prefix) { + if (G.n_prefixes == MAX_PREFIXES) + error(1, 0, "maximum number of prefixes is %i.", MAX_PREFIXES); + + const size_t len = strlen(prefix)+1; + char prefix2[len]; + memcpy(prefix2, prefix, len); + + char *slash = strchr(prefix2, '/'); + if (slash) { + *slash = 0; + if (strcmp(slash+1, "64") != 0) + goto error; + } + + if (inet_pton(AF_INET6, prefix2, &G.prefixes[G.n_prefixes]) != 1) + goto error; + + static const uint8_t zero[8] = {}; + if (memcmp(G.prefixes[G.n_prefixes].s6_addr + 8, zero, 8) != 0) + goto error; + + G.n_prefixes++; + return; + + error: + error(1, 0, "invalid prefix %s (only prefixes of length 64 are supported).", prefix); +} + +static void parse_cmdline(int argc, char *argv[]) { + int c; + while ((c = getopt(argc, argv, "i:p:h")) != -1) { + switch(c) { + case 'i': + if (G.ifname) + error(1, 0, "multiple interfaces are not supported."); + + G.ifname = optarg; + + break; + + case 'p': + add_prefix(optarg); + break; + + case 'h': + usage(); + exit(0); + + default: + usage(); + exit(1); + } + } +} + +int main(int argc, char *argv[]) { + parse_cmdline(argc, argv); + + if (!G.ifname || !G.n_prefixes) + error(1, 0, "interface and prefix arguments are required."); + + init_random(); + init_icmp(); + init_rtnl(); + + update_time(); + G.next_advert = G.next_advert_earliest = G.time; + + update_interface(); + + while (true) { + struct pollfd fds[2] = { + { .fd = G.icmp_sock, .events = POLLIN }, + { .fd = G.rtnl_sock, .events = POLLIN }, + }; + + int timeout = -1; + + if (G.iface.ok) { + timeout = timespec_diff(&G.next_advert, &G.time); + + if (timeout < 0) + timeout = 0; + } + + int ret = poll(fds, 2, timeout); + if (ret < 0) + exit_errno("poll"); + + update_time(); + + if (fds[0].revents & POLLIN) + handle_solicit(); + if (fds[1].revents & POLLIN) + handle_rtnl(); + + if (timespec_after(&G.time, &G.next_advert)) + send_advert(); + } +} diff --git a/package/gluon-setup-mode/Makefile b/package/gluon-setup-mode/Makefile new file mode 100644 index 00000000..99799927 --- /dev/null +++ b/package/gluon-setup-mode/Makefile @@ -0,0 +1,43 @@ +# Copyright (C) 2012 Nils Schneider +# This is free software, licensed under the Apache 2.0 license. + +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-setup-mode +PKG_VERSION:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-setup-mode + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Setup mode + DEPENDS:=+gluon-core +uhttpd +dnsmasq +endef + +define Package/gluon-setup-mode/description + Offline mode to perform basic setup in a secure manner. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-setup-mode/install + $(CP) ./files/* $(1)/ +endef + +define Package/gluon-setup-mode/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-setup-mode)) diff --git a/package/gluon-setup-mode/check_site.lua b/package/gluon-setup-mode/check_site.lua new file mode 100644 index 00000000..07baaad3 --- /dev/null +++ b/package/gluon-setup-mode/check_site.lua @@ -0,0 +1,2 @@ +need_boolean('setup_mode.skip', false) + diff --git a/package/gluon-setup-mode/files/etc/config/gluon-setup-mode b/package/gluon-setup-mode/files/etc/config/gluon-setup-mode new file mode 100644 index 00000000..c652728f --- /dev/null +++ b/package/gluon-setup-mode/files/etc/config/gluon-setup-mode @@ -0,0 +1,3 @@ +config setup_mode + option enabled '0' + option configured '0' diff --git a/package/gluon-setup-mode/files/etc/hotplug.d/button/50-gluon-setup-mode b/package/gluon-setup-mode/files/etc/hotplug.d/button/50-gluon-setup-mode new file mode 100755 index 00000000..9a1ccf2f --- /dev/null +++ b/package/gluon-setup-mode/files/etc/hotplug.d/button/50-gluon-setup-mode @@ -0,0 +1,29 @@ +#!/bin/sh + + +wait=3 + + +wait_setup_mode() { + sleep $wait + uci set 'gluon-setup-mode.@setup_mode[0].enabled=1' + uci commit gluon-setup-mode + reboot +} + + +if [ "$BUTTON" = wps -o "$BUTTON" = reset ]; then + case "$ACTION" in + pressed) + wait_setup_mode & + PID=$! + echo $PID > /tmp/.wait_setup_mode + ;; + released) + if [ -r /tmp/.wait_setup_mode ]; then + kill $(cat /tmp/.wait_setup_mode) + rm /tmp/.wait_setup_mode + fi + ;; + esac +fi diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/ash-login b/package/gluon-setup-mode/files/lib/gluon/setup-mode/ash-login new file mode 100755 index 00000000..3349c444 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/ash-login @@ -0,0 +1,3 @@ +#!/bin/sh + +exec /bin/ash --login diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K50dropbear b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K50dropbear new file mode 120000 index 00000000..066549b3 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K50dropbear @@ -0,0 +1 @@ +/etc/init.d/dropbear \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K89log b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K89log new file mode 120000 index 00000000..1e0c5ac0 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K89log @@ -0,0 +1 @@ +/etc/init.d/log \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K90network b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K90network new file mode 120000 index 00000000..0a43e66b --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K90network @@ -0,0 +1 @@ +S20network \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K98boot b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K98boot new file mode 120000 index 00000000..64aea5e8 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K98boot @@ -0,0 +1 @@ +/etc/init.d/boot \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K99umount b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K99umount new file mode 120000 index 00000000..b02f4892 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/K99umount @@ -0,0 +1 @@ +/etc/init.d/umount \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S00sysfixtime b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S00sysfixtime new file mode 120000 index 00000000..a4fb1d5b --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S00sysfixtime @@ -0,0 +1 @@ +/etc/init.d/sysfixtime \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S10boot b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S10boot new file mode 120000 index 00000000..64aea5e8 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S10boot @@ -0,0 +1 @@ +/etc/init.d/boot \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S10system b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S10system new file mode 120000 index 00000000..81e8836f --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S10system @@ -0,0 +1 @@ +/etc/init.d/system \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S11sysctl b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S11sysctl new file mode 120000 index 00000000..b4ac535e --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S11sysctl @@ -0,0 +1 @@ +/etc/init.d/sysctl \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S12log b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S12log new file mode 120000 index 00000000..1e0c5ac0 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S12log @@ -0,0 +1 @@ +/etc/init.d/log \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S13haveged b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S13haveged new file mode 100755 index 00000000..b4af677a --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S13haveged @@ -0,0 +1,5 @@ +#!/bin/sh /etc/rc.common + +if /etc/init.d/haveged enabled; then + . /etc/init.d/haveged +fi diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S15gluon-setup-mode b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S15gluon-setup-mode new file mode 100755 index 00000000..df8a0e2b --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S15gluon-setup-mode @@ -0,0 +1,21 @@ +#!/bin/sh /etc/rc.common + +START=15 + + +boot() { + local enabled="$(uci -q get 'gluon-setup-mode.@setup_mode[0].enabled')" + local configured="$(uci -q get 'gluon-setup-mode.@setup_mode[0].configured')" + + uci set 'gluon-setup-mode.@setup_mode[0].enabled=0' + uci commit gluon-setup-mode + + if [ "$enabled" = 1 -o "$configured" != 1 ]; then + lua -e 'uci_state=require("luci.model.uci").cursor_state(); uci_state:section("gluon-setup-mode", "setup_mode", nil, { running = "1" }); uci_state:save("gluon-setup-mode")' + else + # This can happen after an upgrade from a version before the config file was called gluon-setup-mode + # We'll just reboot to return to the normal mode... + /etc/init.d/done boot + reboot + fi +} diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S20network b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S20network new file mode 100755 index 00000000..8bc0ba4f --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S20network @@ -0,0 +1,86 @@ +#!/bin/sh /etc/rc.common + +SETUP_MODE_ADDR=192.168.1.1 +SETUP_MODE_NETMASK=255.255.255.0 + +START=20 +STOP=90 + +USE_PROCD=1 + + +delete_interface() { + [ "$1" = 'loopback' ] || uci_remove network "$1" +} + +prepare_config() { +( + export UCI_CONFIG_DIR=/var/gluon/setup-mode/config + + mkdir -p "$UCI_CONFIG_DIR" + + cp /etc/config/network "$UCI_CONFIG_DIR" + + config_load network + config_foreach delete_interface interface + + uci_add network interface setup + uci_set network setup ifname "$(lua -e 'print(require("gluon.sysconfig").setup_ifname)')" + uci_set network setup type 'bridge' + uci_set network setup proto 'static' + uci_set network setup ipaddr "$SETUP_MODE_ADDR" + uci_set network setup netmask "$SETUP_MODE_NETMASK" + + uci_commit network +) +} + +init_switch() { + setup_switch() { return 0; } + + include /lib/network + setup_switch +} + +start_service() { + prepare_config + init_switch + + procd_open_instance + procd_set_param command /sbin/netifd -c /var/gluon/setup-mode/config + procd_set_param respawn + procd_set_param watch network.interface + [ -e /proc/sys/kernel/core_pattern ] && { + procd_set_param limits core="unlimited" + echo '/tmp/%e.%p.%s.%t.core' > /proc/sys/kernel/core_pattern + } + procd_close_instance +} + +reload_service() { + init_switch + ubus call network reload + /sbin/wifi reload_legacy +} + +stop_service() { + /sbin/wifi down +} + +service_running() { + ubus -t 30 wait_for network.interface + /sbin/wifi reload_legacy +} + +restart() { + ifdown -a + sleep 1 + trap '' TERM + stop "$@" + start "$@" +} + +shutdown() { + ifdown -a + stop +} diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50dropbear b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50dropbear new file mode 120000 index 00000000..066549b3 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50dropbear @@ -0,0 +1 @@ +/etc/init.d/dropbear \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50telnet b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50telnet new file mode 100755 index 00000000..b524b2ce --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50telnet @@ -0,0 +1,12 @@ +#!/bin/sh /etc/rc.common + +START=50 + +USE_PROCD=1 +PROG=/usr/sbin/telnetd + +start_service() { + procd_open_instance + procd_set_param command "$PROG" -F -l /lib/gluon/setup-mode/ash-login + procd_close_instance +} diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50uhttpd b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50uhttpd new file mode 100755 index 00000000..53118704 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S50uhttpd @@ -0,0 +1,14 @@ +#!/bin/sh /etc/rc.common + +START=50 + +USE_PROCD=1 + +UHTTPD_BIN="/usr/sbin/uhttpd" + +start_service() { + procd_open_instance + procd_set_param respawn + procd_set_param command "$UHTTPD_BIN" -f -h /lib/gluon/setup-mode/www -x /cgi-bin -A 1 -R -p 0.0.0.0:80 + procd_close_instance +} diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S60dnsmasq b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S60dnsmasq new file mode 100755 index 00000000..d275b4bf --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S60dnsmasq @@ -0,0 +1,17 @@ +#!/bin/sh /etc/rc.common + +SETUP_MODE_DHCP_RANGE=192.168.1.2,192.168.1.254 + + +START=60 + +USE_PROCD=1 +PROG=/usr/sbin/dnsmasq + + +start_service() { + procd_open_instance + procd_set_param command $PROG -k -p 0 -F $SETUP_MODE_DHCP_RANGE -l /tmp/dhcp.leases -O option:router + procd_set_param respawn + procd_close_instance +} diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S95done b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S95done new file mode 120000 index 00000000..c9f30277 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S95done @@ -0,0 +1 @@ +/etc/init.d/done \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S96led b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S96led new file mode 100755 index 00000000..b01ae599 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/rc.d/S96led @@ -0,0 +1,11 @@ +#!/bin/sh /etc/rc.common + +START=96 + +start() { + /etc/init.d/led start + + . /etc/diag.sh + get_status_led + status_led_set_timer 1000 300 +} diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/cgi-bin/luci b/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/cgi-bin/luci new file mode 100755 index 00000000..c5c98473 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/cgi-bin/luci @@ -0,0 +1,5 @@ +#!/usr/bin/lua +require "luci.cacheloader" +require "luci.sgi.cgi" +luci.dispatcher.indexcache = "/tmp/luci-indexcache" +luci.sgi.cgi.run() diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/index.html b/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/index.html new file mode 100644 index 00000000..0a7238b5 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/index.html @@ -0,0 +1,10 @@ + + + + + + + +LuCI - Lua Configuration Interface + + diff --git a/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/luci-static b/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/luci-static new file mode 120000 index 00000000..aea80e05 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/setup-mode/www/luci-static @@ -0,0 +1 @@ +/www/luci-static \ No newline at end of file diff --git a/package/gluon-setup-mode/files/lib/gluon/upgrade/300-setup-mode b/package/gluon-setup-mode/files/lib/gluon/upgrade/300-setup-mode new file mode 100755 index 00000000..ae59c7aa --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/upgrade/300-setup-mode @@ -0,0 +1,16 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require 'luci.model.uci' + +local c = uci.cursor() + +if site.setup_mode + and site.setup_mode.skip + and not c:get_first('gluon-setup-mode', 'setup_mode', 'configured', false) then + local name = c:get_first("gluon-setup-mode", "setup_mode") + c:set("gluon-setup-mode", name, "configured", 1) + c:save('gluon-setup-mode') + c:commit('gluon-setup-mode') +end + diff --git a/package/gluon-setup-mode/files/lib/gluon/upgrade/310-setup-mode-migrate b/package/gluon-setup-mode/files/lib/gluon/upgrade/310-setup-mode-migrate new file mode 100755 index 00000000..9d3b9b0a --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/upgrade/310-setup-mode-migrate @@ -0,0 +1,18 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require 'luci.model.uci' + +local c = uci.cursor() + + +local old = c:get_first('gluon-config-mode', 'wizard', 'configured') +if old == '1' then + local setup_mode = c:get_first('gluon-setup-mode', 'setup_mode') + c:set('gluon-setup-mode', setup_mode, 'configured', '1') + + c:save('gluon-setup-mode') + c:commit('gluon-setup-mode') +end + +os.remove('/etc/config/gluon-config-mode') diff --git a/package/gluon-setup-mode/files/lib/gluon/upgrade/320-setup-ifname b/package/gluon-setup-mode/files/lib/gluon/upgrade/320-setup-ifname new file mode 100755 index 00000000..3c4a8afe --- /dev/null +++ b/package/gluon-setup-mode/files/lib/gluon/upgrade/320-setup-ifname @@ -0,0 +1,18 @@ +#!/usr/bin/lua + +local platform = require 'gluon.platform' +local sysconfig = require 'gluon.sysconfig' + + +if sysconfig.setup_ifname then + os.exit(0) +end + +if platform.match('ar71xx', 'generic', {'cpe510', 'nanostation-m', 'nanostation-m-xw', 'unifi-outdoor-plus'}) then + sysconfig.setup_ifname = sysconfig.config_ifname or sysconfig.wan_ifname or sysconfig.lan_ifname +else + sysconfig.setup_ifname = sysconfig.config_ifname or sysconfig.lan_ifname or sysconfig.wan_ifname +end + +-- Remove the old sysconfig setting +sysconfig.config_ifname = nil diff --git a/package/gluon-setup-mode/files/lib/preinit/90_setup_mode b/package/gluon-setup-mode/files/lib/preinit/90_setup_mode new file mode 100644 index 00000000..396b4f76 --- /dev/null +++ b/package/gluon-setup-mode/files/lib/preinit/90_setup_mode @@ -0,0 +1,13 @@ +#!/bin/sh + + +setup_mode_enable() { + local enabled="$(uci -q get 'gluon-setup-mode.@setup_mode[0].enabled')" + local configured="$(uci -q get 'gluon-setup-mode.@setup_mode[0].configured')" + + if [ "$enabled" = 1 -o "$configured" != 1 ]; then + echo '/lib/gluon/setup-mode/rc.d' > /tmp/rc_d_path + fi +} + +boot_hook_add preinit_main setup_mode_enable diff --git a/package/gluon-simple-tc/Makefile b/package/gluon-simple-tc/Makefile new file mode 100644 index 00000000..b01b9641 --- /dev/null +++ b/package/gluon-simple-tc/Makefile @@ -0,0 +1,47 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-simple-tc +PKG_VERSION:=4 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-simple-tc + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Bandwidth limit support + DEPENDS:=+gluon-core +kmod-sched +libnl-tiny +endef + +define Package/gluon-simple-tc/description + Gluon community wifi mesh firmware framework: tc support +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Configure +endef + + +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny + +define Build/Compile + CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) +endef + +define Package/gluon-simple-tc/install + $(CP) ./files/* $(1)/ + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-simple-tc $(1)/usr/sbin/ +endef + +define Package/gluon-simple-tc/postinst +#!/bin/sh +$(call GluonCheckSite,check_site.lua) +endef + +$(eval $(call BuildPackage,gluon-simple-tc)) diff --git a/package/gluon-simple-tc/check_site.lua b/package/gluon-simple-tc/check_site.lua new file mode 100644 index 00000000..95d4fd81 --- /dev/null +++ b/package/gluon-simple-tc/check_site.lua @@ -0,0 +1,10 @@ +local function check_entry(k, _) + local prefix = string.format('simple_tc[%q].', k) + + need_string(prefix .. 'ifname') + need_boolean(prefix .. 'enabled') + need_number(prefix .. 'limit_egress') + need_number(prefix .. 'limit_ingress') +end + +need_table('simple_tc', check_entry) diff --git a/package/gluon-simple-tc/files/etc/config/gluon-simple-tc b/package/gluon-simple-tc/files/etc/config/gluon-simple-tc new file mode 100644 index 00000000..c9b784c6 --- /dev/null +++ b/package/gluon-simple-tc/files/etc/config/gluon-simple-tc @@ -0,0 +1,7 @@ +# Example config + +config interface 'example' + option enabled '0' + option ifname 'eth0' + option limit_egress '1000' # 1000 Kbit/s + option limit_ingress '5000' # 5000 Kbit/s diff --git a/package/gluon-simple-tc/files/etc/hotplug.d/net/50-gluon-simple-tc b/package/gluon-simple-tc/files/etc/hotplug.d/net/50-gluon-simple-tc new file mode 100644 index 00000000..8dd82781 --- /dev/null +++ b/package/gluon-simple-tc/files/etc/hotplug.d/net/50-gluon-simple-tc @@ -0,0 +1,26 @@ +[ "$ACTION" = 'add' ] || exit 0 + +config_load gluon-simple-tc + + +tc_interface() { + local iface="$1" + + config_get ifname "$iface" ifname + + [ "$INTERFACE" = "$ifname" ] || return + + config_get_bool enabled "$iface" enabled 0 + + [ "$enabled" -eq 1 ] || return + + config_get limit_ingress "$iface" limit_ingress + config_get limit_egress "$iface" limit_egress + + [ "$limit_ingress" ] || limit_ingress=- + [ "$limit_egress" ] || limit_egress=- + + gluon-simple-tc "$INTERFACE" "$limit_ingress" "$limit_egress" +} + +config_foreach tc_interface 'interface' diff --git a/package/gluon-simple-tc/files/etc/modules-boot.d/30-gluon-simple-tc b/package/gluon-simple-tc/files/etc/modules-boot.d/30-gluon-simple-tc new file mode 120000 index 00000000..44318d6e --- /dev/null +++ b/package/gluon-simple-tc/files/etc/modules-boot.d/30-gluon-simple-tc @@ -0,0 +1 @@ +../modules.d/30-gluon-simple-tc \ No newline at end of file diff --git a/package/gluon-simple-tc/files/etc/modules.d/30-gluon-simple-tc b/package/gluon-simple-tc/files/etc/modules.d/30-gluon-simple-tc new file mode 100644 index 00000000..72b238c7 --- /dev/null +++ b/package/gluon-simple-tc/files/etc/modules.d/30-gluon-simple-tc @@ -0,0 +1,4 @@ +sch_ingress +sch_tbf +cls_basic +act_police diff --git a/package/gluon-simple-tc/files/lib/gluon/upgrade/300-simple-tc-site-defaults b/package/gluon-simple-tc/files/lib/gluon/upgrade/300-simple-tc-site-defaults new file mode 100755 index 00000000..037cb45f --- /dev/null +++ b/package/gluon-simple-tc/files/lib/gluon/upgrade/300-simple-tc-site-defaults @@ -0,0 +1,23 @@ +#!/usr/bin/lua + +local site = require 'gluon.site_config' +local uci = require 'luci.model.uci' + +local c = uci.cursor() + + +for name, config in pairs(site.simple_tc) do + if not c:get('gluon-simple-tc', name) then + c:section('gluon-simple-tc', 'interface', name, + { + ifname = config.ifname, + enabled = config.enabled and 1 or 0, + limit_egress = config.limit_egress, + limit_ingress = config.limit_ingress, + } + ) + end +end + +c:save('gluon-simple-tc') +c:commit('gluon-simple-tc') diff --git a/package/gluon-simple-tc/src/Makefile b/package/gluon-simple-tc/src/Makefile new file mode 100644 index 00000000..502c6232 --- /dev/null +++ b/package/gluon-simple-tc/src/Makefile @@ -0,0 +1,4 @@ +all: gluon-simple-tc + +gluon-simple-tc: gluon-simple-tc.c + $(CC) -Iinclude $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -lnl-tiny diff --git a/package/gluon-simple-tc/src/gluon-simple-tc.c b/package/gluon-simple-tc/src/gluon-simple-tc.c new file mode 100644 index 00000000..9e5bb206 --- /dev/null +++ b/package/gluon-simple-tc/src/gluon-simple-tc.c @@ -0,0 +1,292 @@ +/* + Copyright (c) 2014, Matthias Schiffer + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + + +#include +#include +#include + + +static struct nl_cb *cb; +static struct nl_sock *sock; +static double ticks; + +static unsigned ifindex; + +static bool nlexpect; +static int nlerror; + + +static inline void exit_errno(const char *message) { + error(1, errno, "error: %s", message); +} + +static inline void warn_errno(const char *message) { + error(0, errno, "warning: %s", message); +} + + +static void read_psched(void) { + uint32_t clock_res; + uint32_t t2us; + uint32_t us2t; + + FILE *f = fopen("/proc/net/psched", "r"); + if (!f || fscanf(f, "%08x %08x %08x", &t2us, &us2t, &clock_res) != 3) + exit_errno("error reading /proc/net/psched"); + fclose(f); + + /* compatibility hack from iproute... */ + if (clock_res == 1000000000) + t2us = us2t; + + ticks = (double)t2us / us2t * clock_res; +} + + +static struct nl_msg * prepare_tcmsg(int type, int flags, uint32_t parent, uint32_t handle, uint32_t info) { + struct nl_msg *msg = nlmsg_alloc_simple(type, flags); + if (!msg) + exit_errno("nlmsg_alloc_simple"); + + struct tcmsg tcmsg; + memset(&tcmsg, 0, sizeof(tcmsg)); + + tcmsg.tcm_family = AF_UNSPEC; + tcmsg.tcm_ifindex = ifindex; + tcmsg.tcm_parent = parent; + tcmsg.tcm_handle = handle; + tcmsg.tcm_info = info; + + nlmsg_append(msg, &tcmsg, sizeof(tcmsg), NLMSG_ALIGNTO); + + return msg; +} + + +static int error_handler(struct sockaddr_nl *nla __attribute__((unused)), struct nlmsgerr *nlerr, void *arg __attribute__((unused))) { + if (!nlexpect || (nlerr->error != -ENOENT && nlerr->error != -EINVAL)) + nlerror = -nlerr->error; + + return NL_STOP; +} + +static bool do_send(struct nl_msg *msg, bool expect) { + nlerror = 0; + nlexpect = expect; + + nl_send_auto_complete(sock, msg); + nlmsg_free(msg); + nl_wait_for_ack(sock); + + if (nlerror) { + error(0, nlerror, "netlink"); + errno = nlerror; + return false; + } + + return true; +} + + +static inline unsigned get_xmittime(double rate, unsigned size) { + return ticks * (size/rate); +} + + +static void complete_rate(struct tc_ratespec *r, uint32_t rtab[256]) { + r->linklayer = TC_LINKLAYER_ETHERNET; + r->cell_align = -1; + r->cell_log = 3; + + unsigned i; + for (i = 0; i < 256; i++) + rtab[i] = get_xmittime(r->rate, (i + 1) << 3); +} + + +static void do_ingress(double rate) { + if (!do_send(prepare_tcmsg(RTM_DELQDISC, 0, TC_H_INGRESS, 0xffff0000, 0), true)) + return; + + if (rate < 0) + return; + + + struct nl_msg *msg = prepare_tcmsg(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL, TC_H_INGRESS, 0xffff0000, 0); + nla_put_string(msg, TCA_KIND, "ingress"); + + if (!do_send(msg, false)) + return; + + + msg = prepare_tcmsg(RTM_NEWTFILTER, NLM_F_CREATE | NLM_F_EXCL, 0xffff0000, 0, TC_H_MAKE(0, htons(ETH_P_ALL))); + + const unsigned buffer = 10240; + + struct tc_police p; + memset(&p, 0, sizeof(p)); + + /* Range check has been done in main() */ + p.rate.rate = rate; + p.burst = get_xmittime(p.rate.rate, buffer); + p.action = TC_POLICE_SHOT; + + uint32_t rtab[256]; + complete_rate(&p.rate, rtab); + + nla_put_string(msg, TCA_KIND, "basic"); + + struct nlattr *opts = nla_nest_start(msg, TCA_OPTIONS); + struct nlattr *police = nla_nest_start(msg, TCA_BASIC_POLICE); + + nla_put(msg, TCA_POLICE_TBF, sizeof(p), &p); + nla_put(msg, TCA_POLICE_RATE, sizeof(rtab), rtab); + + nla_nest_end(msg, police); + nla_nest_end(msg, opts); + + do_send(msg, false); +} + +static void do_egress(double rate) { + if (!do_send(prepare_tcmsg(RTM_DELQDISC, 0, TC_H_ROOT, 0, 0), true)) + return; + + if (rate < 0) + return; + + + struct nl_msg *msg = prepare_tcmsg(RTM_NEWQDISC, NLM_F_CREATE | NLM_F_EXCL, TC_H_ROOT, 0, 0); + const unsigned buffer = 2048; + + struct tc_tbf_qopt opt; + memset(&opt, 0, sizeof(opt)); + + /* Range check has been done in main() */ + opt.rate.rate = rate; + opt.limit = 0.05*rate + buffer; + opt.buffer = get_xmittime(opt.rate.rate, buffer); + + uint32_t rtab[256]; + complete_rate(&opt.rate, rtab); + + nla_put_string(msg, TCA_KIND, "tbf"); + + struct nlattr *opts = nla_nest_start(msg, TCA_OPTIONS); + nla_put(msg, TCA_TBF_PARMS, sizeof(opt), &opt); + nla_put(msg, TCA_TBF_BURST, sizeof(buffer), &buffer); + nla_put(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); + nla_nest_end(msg, opts); + + do_send(msg, false); +} + + +static inline void usage(void) { + fprintf(stderr, "Usage: gluon-simple-tc |- |-\n"); + exit(1); +} + +static inline void maxrate(void) { + error(1, 0, "error: maximum allowed rate it about 2^25 Kbit/s"); +} + + +int main(int argc, char *argv[]) { + if (argc != 4) + usage(); + + double ingress = -1, egress = -1; + char *end; + + ifindex = if_nametoindex(argv[1]); + if (!ifindex) + error(1, 0, "invalid interface: %s", argv[1]); + + if (strcmp(argv[2], "-") != 0) { + ingress = strtod(argv[2], &end); + if (*end || ingress < 0) + usage(); + + ingress *= 125; + + if (ingress >= (1ull << 32)) + maxrate(); + } + + if (strcmp(argv[3], "-") != 0) { + egress = strtod(argv[3], &end); + if (*end || egress < 0) + usage(); + + egress *= 125; + + if (egress >= (1ull << 32)) + maxrate(); + } + + read_psched(); + + cb = nl_cb_alloc(NL_CB_DEFAULT); + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, NULL); + + sock = nl_socket_alloc_cb(cb); + if (!sock) + exit_errno("nl_socket_alloc"); + + if (nl_connect(sock, NETLINK_ROUTE)) + exit_errno("nl_connect"); + + do_ingress(ingress); + do_egress(egress); + + nl_socket_free(sock); + nl_cb_put(cb); + + return 0; +} diff --git a/package/gluon-simple-tc/src/include/linux/pkt_cls.h b/package/gluon-simple-tc/src/include/linux/pkt_cls.h new file mode 100644 index 00000000..25731dfb --- /dev/null +++ b/package/gluon-simple-tc/src/include/linux/pkt_cls.h @@ -0,0 +1,483 @@ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include +#include + +/* I think i could have done better macros ; for now this is stolen from + * some arch/mips code - jhs +*/ +#define _TC_MAKE32(x) ((x)) + +#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) +#define _TC_MAKEMASK(v,n) (_TC_MAKE32((_TC_MAKE32(1)<<(v))-1) << _TC_MAKE32(n)) +#define _TC_MAKEVALUE(v,n) (_TC_MAKE32(v) << _TC_MAKE32(n)) +#define _TC_GETVALUE(v,n,m) ((_TC_MAKE32(v) & _TC_MAKE32(m)) >> _TC_MAKE32(n)) + +/* verdict bit breakdown + * +bit 0: when set -> this packet has been munged already + +bit 1: when set -> It is ok to munge this packet + +bit 2,3,4,5: Reclassify counter - sort of reverse TTL - if exceeded +assume loop + +bit 6,7: Where this packet was last seen +0: Above the transmit example at the socket level +1: on the Ingress +2: on the Egress + +bit 8: when set --> Request not to classify on ingress. + +bits 9,10,11: redirect counter - redirect TTL. Loop avoidance + + * + * */ + +#define TC_MUNGED _TC_MAKEMASK1(0) +#define SET_TC_MUNGED(v) ( TC_MUNGED | (v & ~TC_MUNGED)) +#define CLR_TC_MUNGED(v) ( v & ~TC_MUNGED) + +#define TC_OK2MUNGE _TC_MAKEMASK1(1) +#define SET_TC_OK2MUNGE(v) ( TC_OK2MUNGE | (v & ~TC_OK2MUNGE)) +#define CLR_TC_OK2MUNGE(v) ( v & ~TC_OK2MUNGE) + +#define S_TC_VERD _TC_MAKE32(2) +#define M_TC_VERD _TC_MAKEMASK(4,S_TC_VERD) +#define G_TC_VERD(x) _TC_GETVALUE(x,S_TC_VERD,M_TC_VERD) +#define V_TC_VERD(x) _TC_MAKEVALUE(x,S_TC_VERD) +#define SET_TC_VERD(v,n) ((V_TC_VERD(n)) | (v & ~M_TC_VERD)) + +#define S_TC_FROM _TC_MAKE32(6) +#define M_TC_FROM _TC_MAKEMASK(2,S_TC_FROM) +#define G_TC_FROM(x) _TC_GETVALUE(x,S_TC_FROM,M_TC_FROM) +#define V_TC_FROM(x) _TC_MAKEVALUE(x,S_TC_FROM) +#define SET_TC_FROM(v,n) ((V_TC_FROM(n)) | (v & ~M_TC_FROM)) +#define AT_STACK 0x0 +#define AT_INGRESS 0x1 +#define AT_EGRESS 0x2 + +#define TC_NCLS _TC_MAKEMASK1(8) +#define SET_TC_NCLS(v) ( TC_NCLS | (v & ~TC_NCLS)) +#define CLR_TC_NCLS(v) ( v & ~TC_NCLS) + +#define S_TC_RTTL _TC_MAKE32(9) +#define M_TC_RTTL _TC_MAKEMASK(3,S_TC_RTTL) +#define G_TC_RTTL(x) _TC_GETVALUE(x,S_TC_RTTL,M_TC_RTTL) +#define V_TC_RTTL(x) _TC_MAKEVALUE(x,S_TC_RTTL) +#define SET_TC_RTTL(v,n) ((V_TC_RTTL(n)) | (v & ~M_TC_RTTL)) + +#define S_TC_AT _TC_MAKE32(12) +#define M_TC_AT _TC_MAKEMASK(2,S_TC_AT) +#define G_TC_AT(x) _TC_GETVALUE(x,S_TC_AT,M_TC_AT) +#define V_TC_AT(x) _TC_MAKEVALUE(x,S_TC_AT) +#define SET_TC_AT(v,n) ((V_TC_AT(n)) | (v & ~M_TC_AT)) + +/* Action attributes */ +enum { + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + TCA_ACT_STATS, + __TCA_ACT_MAX +}; + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 +#define MAX_REC_LOOP 4 +#define MAX_RED_LOOP 4 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_JUMP 0x10000000 + +/* Action type identifiers*/ +enum { + TCA_ID_UNSPEC=0, + TCA_ID_POLICE=1, + /* other actions go here */ + __TCA_ID_MAX=255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police { + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t { + __u64 install; + __u64 lastuse; + __u64 expires; +}; + +struct tc_cnt { + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum { + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + __TCA_POLICE_MAX +#define TCA_POLICE_RESULT TCA_POLICE_RESULT +}; + +#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) + +/* U32 filters */ + +#define TC_U32_HTID(h) ((h)&0xFFF00000) +#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20) +#define TC_U32_HASH(h) (((h)>>12)&0xFF) +#define TC_U32_NODE(h) ((h)&0xFFF) +#define TC_U32_KEY(h) ((h)&0xFFFFF) +#define TC_U32_UNSPEC 0 +#define TC_U32_ROOT (0xFFF00000) + +enum { + TCA_U32_UNSPEC, + TCA_U32_CLASSID, + TCA_U32_HASH, + TCA_U32_LINK, + TCA_U32_DIVISOR, + TCA_U32_SEL, + TCA_U32_POLICE, + TCA_U32_ACT, + TCA_U32_INDEV, + TCA_U32_PCNT, + TCA_U32_MARK, + __TCA_U32_MAX +}; + +#define TCA_U32_MAX (__TCA_U32_MAX - 1) + +struct tc_u32_key { + __be32 mask; + __be32 val; + int off; + int offmask; +}; + +struct tc_u32_sel { + unsigned char flags; + unsigned char offshift; + unsigned char nkeys; + + __be16 offmask; + __u16 off; + short offoff; + + short hoff; + __be32 hmask; + struct tc_u32_key keys[0]; +}; + +struct tc_u32_mark { + __u32 val; + __u32 mask; + __u32 success; +}; + +struct tc_u32_pcnt { + __u64 rcnt; + __u64 rhit; + __u64 kcnts[0]; +}; + +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum { + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + TCA_RSVP_ACT, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi { + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo { + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; + __u8 pad; +}; + +/* ROUTE filter */ + +enum { + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + TCA_ROUTE4_ACT, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum { + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */ + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + TCA_FW_MASK, + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum { + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +/* Flow filter */ + +enum { + FLOW_KEY_SRC, + FLOW_KEY_DST, + FLOW_KEY_PROTO, + FLOW_KEY_PROTO_SRC, + FLOW_KEY_PROTO_DST, + FLOW_KEY_IIF, + FLOW_KEY_PRIORITY, + FLOW_KEY_MARK, + FLOW_KEY_NFCT, + FLOW_KEY_NFCT_SRC, + FLOW_KEY_NFCT_DST, + FLOW_KEY_NFCT_PROTO_SRC, + FLOW_KEY_NFCT_PROTO_DST, + FLOW_KEY_RTCLASSID, + FLOW_KEY_SKUID, + FLOW_KEY_SKGID, + FLOW_KEY_VLAN_TAG, + FLOW_KEY_RXHASH, + __FLOW_KEY_MAX, +}; + +#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1) + +enum { + FLOW_MODE_MAP, + FLOW_MODE_HASH, +}; + +enum { + TCA_FLOW_UNSPEC, + TCA_FLOW_KEYS, + TCA_FLOW_MODE, + TCA_FLOW_BASECLASS, + TCA_FLOW_RSHIFT, + TCA_FLOW_ADDEND, + TCA_FLOW_MASK, + TCA_FLOW_XOR, + TCA_FLOW_DIVISOR, + TCA_FLOW_ACT, + TCA_FLOW_POLICE, + TCA_FLOW_EMATCHES, + TCA_FLOW_PERTURB, + __TCA_FLOW_MAX +}; + +#define TCA_FLOW_MAX (__TCA_FLOW_MAX - 1) + +/* Basic filter */ + +enum { + TCA_BASIC_UNSPEC, + TCA_BASIC_CLASSID, + TCA_BASIC_EMATCHES, + TCA_BASIC_ACT, + TCA_BASIC_POLICE, + __TCA_BASIC_MAX +}; + +#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) + + +/* Cgroup classifier */ + +enum { + TCA_CGROUP_UNSPEC, + TCA_CGROUP_ACT, + TCA_CGROUP_POLICE, + TCA_CGROUP_EMATCHES, + __TCA_CGROUP_MAX, +}; + +#define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1) + +/* BPF classifier */ + +enum { + TCA_BPF_UNSPEC, + TCA_BPF_ACT, + TCA_BPF_POLICE, + TCA_BPF_CLASSID, + TCA_BPF_OPS_LEN, + TCA_BPF_OPS, + __TCA_BPF_MAX, +}; + +#define TCA_BPF_MAX (__TCA_BPF_MAX - 1) + +/* Extended Matches */ + +struct tcf_ematch_tree_hdr { + __u16 nmatches; + __u16 progid; +}; + +enum { + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr { + __u16 matchid; + __u16 kind; + __u16 flags; + __u16 pad; /* currently unused */ +}; + +/* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-----------------------+-+-+---+ + * | Unused |S|I| R | + * +-----------------------+-+-+---+ + * + * R(2) ::= relation to next ematch + * where: 0 0 END (last ematch) + * 0 1 AND + * 1 0 OR + * 1 1 Unused (invalid) + * I(1) ::= invert result + * S(1) ::= simple payload + */ +#define TCF_EM_REL_END 0 +#define TCF_EM_REL_AND (1<<0) +#define TCF_EM_REL_OR (1<<1) +#define TCF_EM_INVERT (1<<2) +#define TCF_EM_SIMPLE (1<<3) + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK) + +enum { + TCF_LAYER_LINK, + TCF_LAYER_NETWORK, + TCF_LAYER_TRANSPORT, + __TCF_LAYER_MAX +}; +#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) + +/* Ematch type assignments + * 1..32767 Reserved for ematches inside kernel tree + * 32768..65535 Free to use, not reliable + */ +#define TCF_EM_CONTAINER 0 +#define TCF_EM_CMP 1 +#define TCF_EM_NBYTE 2 +#define TCF_EM_U32 3 +#define TCF_EM_META 4 +#define TCF_EM_TEXT 5 +#define TCF_EM_VLAN 6 +#define TCF_EM_CANID 7 +#define TCF_EM_IPSET 8 +#define TCF_EM_MAX 8 + +enum { + TCF_EM_PROG_TC +}; + +enum { + TCF_EM_OPND_EQ, + TCF_EM_OPND_GT, + TCF_EM_OPND_LT +}; + +#endif diff --git a/package/gluon-simple-tc/src/include/linux/pkt_sched.h b/package/gluon-simple-tc/src/include/linux/pkt_sched.h new file mode 100644 index 00000000..d62316ba --- /dev/null +++ b/package/gluon-simple-tc/src/include/linux/pkt_sched.h @@ -0,0 +1,846 @@ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +#include + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats { + __u64 bytes; /* Number of enqueued bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +}; + +struct tc_estimator { + signed char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) + +/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */ +enum tc_link_layer { + TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */ + TC_LINKLAYER_ETHERNET, + TC_LINKLAYER_ATM, +}; +#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */ + +struct tc_ratespec { + unsigned char cell_log; + __u8 linklayer; /* lower 4 bits */ + unsigned short overhead; + short cell_align; + unsigned short mpu; + __u32 rate; +}; + +#define TC_RTAB_SIZE 1024 + +struct tc_sizespec { + unsigned char cell_log; + unsigned char size_log; + short cell_align; + int overhead; + unsigned int linklayer; + unsigned int mpu; + unsigned int mtu; + unsigned int tsize; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + +/* FIFO section */ + +struct tc_fifo_qopt { + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 +#define TCQ_MIN_PRIO_BANDS 2 + +struct tc_prio_qopt { + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +/* MULTIQ section */ + +struct tc_multiq_qopt { + __u16 bands; /* Number of bands */ + __u16 max_bands; /* Maximum number of queues */ +}; + +/* PLUG section */ + +#define TCQ_PLUG_BUFFER 0 +#define TCQ_PLUG_RELEASE_ONE 1 +#define TCQ_PLUG_RELEASE_INDEFINITE 2 +#define TCQ_PLUG_LIMIT 3 + +struct tc_plug_qopt { + /* TCQ_PLUG_BUFFER: Inset a plug into the queue and + * buffer any incoming packets + * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head + * to beginning of the next plug. + * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue. + * Stop buffering packets until the next TCQ_PLUG_BUFFER + * command is received (just act as a pass-thru queue). + * TCQ_PLUG_LIMIT: Increase/decrease queue size + */ + int action; + __u32 limit; +}; + +/* TBF section */ + +struct tc_tbf_qopt { + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum { + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, + TCA_TBF_RATE64, + TCA_TBF_PRATE64, + TCA_TBF_BURST, + TCA_TBF_PBURST, + __TCA_TBF_MAX, +}; + +#define TCA_TBF_MAX (__TCA_TBF_MAX - 1) + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt { + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +struct tc_sfqred_stats { + __u32 prob_drop; /* Early drops, below max threshold */ + __u32 forced_drop; /* Early drops, after max threshold */ + __u32 prob_mark; /* Marked packets, below max threshold */ + __u32 forced_mark; /* Marked packets, after max threshold */ + __u32 prob_mark_head; /* Marked packets, below max threshold */ + __u32 forced_mark_head;/* Marked packets, after max threshold */ +}; + +struct tc_sfq_qopt_v1 { + struct tc_sfq_qopt v0; + unsigned int depth; /* max number of packets per flow */ + unsigned int headdrop; +/* SFQRED parameters */ + __u32 limit; /* HARD maximal flow queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; + __u32 max_P; /* probability, high resolution */ +/* SFQRED stats */ + struct tc_sfqred_stats stats; +}; + + +struct tc_sfq_xstats { + __s32 allot; +}; + +/* RED section */ + +enum { + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, + TCA_RED_MAX_P, + __TCA_RED_MAX, +}; + +#define TCA_RED_MAX (__TCA_RED_MAX - 1) + +struct tc_red_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; +#define TC_RED_ECN 1 +#define TC_RED_HARDDROP 2 +#define TC_RED_ADAPTATIVE 4 +}; + +struct tc_red_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum { + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, + TCA_GRED_MAX_P, + __TCA_GRED_MAX, +}; + +#define TCA_GRED_MAX (__TCA_GRED_MAX - 1) + +struct tc_gred_qopt { + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + __u32 DP; /* up to 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + __u8 Wlog; /* log(W) */ + __u8 Plog; /* log(P_max/(qth_max-qth_min)) */ + __u8 Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; + +/* gred setup */ +struct tc_gred_sopt { + __u32 DPs; + __u32 def_DP; + __u8 grio; + __u8 flags; + __u16 pad1; +}; + +/* CHOKe section */ + +enum { + TCA_CHOKE_UNSPEC, + TCA_CHOKE_PARMS, + TCA_CHOKE_STAB, + TCA_CHOKE_MAX_P, + __TCA_CHOKE_MAX, +}; + +#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1) + +struct tc_choke_qopt { + __u32 limit; /* Hard queue length (packets) */ + __u32 qth_min; /* Min average threshold (packets) */ + __u32 qth_max; /* Max average threshold (packets) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; /* see RED flags */ +}; + +struct tc_choke_xstats { + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ + __u32 matched; /* Drops due to flow match */ +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 8 +#define TC_HTB_MAXDEPTH 8 +#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */ + +struct tc_htb_opt { + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; + __u32 level; /* out only */ + __u32 prio; +}; +struct tc_htb_glob { + __u32 version; /* to match HTB/TC */ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 debug; /* debug flags */ + + /* stats */ + __u32 direct_pkts; /* count of non shaped packets */ +}; +enum { + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, + TCA_HTB_DIRECT_QLEN, + TCA_HTB_RATE64, + TCA_HTB_CEIL64, + __TCA_HTB_MAX, +}; + +#define TCA_HTB_MAX (__TCA_HTB_MAX - 1) + +struct tc_htb_xstats { + __u32 lends; + __u32 borrows; + __u32 giants; /* too big packets (rate will not be accurate) */ + __u32 tokens; + __u32 ctokens; +}; + +/* HFSC section */ + +struct tc_hfsc_qopt { + __u16 defcls; /* default class */ +}; + +struct tc_service_curve { + __u32 m1; /* slope of the first segment in bps */ + __u32 d; /* x-projection of the first segment in us */ + __u32 m2; /* slope of the second segment in bps */ +}; + +struct tc_hfsc_stats { + __u64 work; /* total work done */ + __u64 rtwork; /* work done by real-time criteria */ + __u32 period; /* current period */ + __u32 level; /* class level in hierarchy */ +}; + +enum { + TCA_HFSC_UNSPEC, + TCA_HFSC_RSC, + TCA_HFSC_FSC, + TCA_HFSC_USC, + __TCA_HFSC_MAX, +}; + +#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) + + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt { + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt { + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl { + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u16 pad; + __u32 penalty; +}; + +struct tc_cbq_police { + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt { + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats { + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum { + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, + __TCA_CBQ_MAX, +}; + +#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE, + __TCA_DSMARK_MAX, +}; + +#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ + __TCA_ATM_MAX, +}; + +#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) + +/* Network emulator */ + +enum { + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, + TCA_NETEM_REORDER, + TCA_NETEM_CORRUPT, + TCA_NETEM_LOSS, + TCA_NETEM_RATE, + TCA_NETEM_ECN, + TCA_NETEM_RATE64, + __TCA_NETEM_MAX, +}; + +#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1) + +struct tc_netem_qopt { + __u32 latency; /* added delay (us) */ + __u32 limit; /* fifo limit (packets) */ + __u32 loss; /* random packet loss (0=none ~0=100%) */ + __u32 gap; /* re-ordering gap (0 for none) */ + __u32 duplicate; /* random packet dup (0=none ~0=100%) */ + __u32 jitter; /* random jitter in latency (us) */ +}; + +struct tc_netem_corr { + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +struct tc_netem_reorder { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_corrupt { + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_rate { + __u32 rate; /* byte/s */ + __s32 packet_overhead; + __u32 cell_size; + __s32 cell_overhead; +}; + +enum { + NETEM_LOSS_UNSPEC, + NETEM_LOSS_GI, /* General Intuitive - 4 state model */ + NETEM_LOSS_GE, /* Gilbert Elliot models */ + __NETEM_LOSS_MAX +}; +#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1) + +/* State transition probabilities for 4 state model */ +struct tc_netem_gimodel { + __u32 p13; + __u32 p31; + __u32 p32; + __u32 p14; + __u32 p23; +}; + +/* Gilbert-Elliot models */ +struct tc_netem_gemodel { + __u32 p; + __u32 r; + __u32 h; + __u32 k1; +}; + +#define NETEM_DIST_SCALE 8192 +#define NETEM_DIST_MAX 16384 + +/* DRR */ + +enum { + TCA_DRR_UNSPEC, + TCA_DRR_QUANTUM, + __TCA_DRR_MAX +}; + +#define TCA_DRR_MAX (__TCA_DRR_MAX - 1) + +struct tc_drr_stats { + __u32 deficit; +}; + +/* MQPRIO */ +#define TC_QOPT_BITMASK 15 +#define TC_QOPT_MAX_QUEUE 16 + +struct tc_mqprio_qopt { + __u8 num_tc; + __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; + __u8 hw; + __u16 count[TC_QOPT_MAX_QUEUE]; + __u16 offset[TC_QOPT_MAX_QUEUE]; +}; + +/* SFB */ + +enum { + TCA_SFB_UNSPEC, + TCA_SFB_PARMS, + __TCA_SFB_MAX, +}; + +#define TCA_SFB_MAX (__TCA_SFB_MAX - 1) + +/* + * Note: increment, decrement are Q0.16 fixed-point values. + */ +struct tc_sfb_qopt { + __u32 rehash_interval; /* delay between hash move, in ms */ + __u32 warmup_time; /* double buffering warmup time in ms (warmup_time < rehash_interval) */ + __u32 max; /* max len of qlen_min */ + __u32 bin_size; /* maximum queue length per bin */ + __u32 increment; /* probability increment, (d1 in Blue) */ + __u32 decrement; /* probability decrement, (d2 in Blue) */ + __u32 limit; /* max SFB queue length */ + __u32 penalty_rate; /* inelastic flows are rate limited to 'rate' pps */ + __u32 penalty_burst; +}; + +struct tc_sfb_xstats { + __u32 earlydrop; + __u32 penaltydrop; + __u32 bucketdrop; + __u32 queuedrop; + __u32 childdrop; /* drops in child qdisc */ + __u32 marked; + __u32 maxqlen; + __u32 maxprob; + __u32 avgprob; +}; + +#define SFB_MAX_PROB 0xFFFF + +/* QFQ */ +enum { + TCA_QFQ_UNSPEC, + TCA_QFQ_WEIGHT, + TCA_QFQ_LMAX, + __TCA_QFQ_MAX +}; + +#define TCA_QFQ_MAX (__TCA_QFQ_MAX - 1) + +struct tc_qfq_stats { + __u32 weight; + __u32 lmax; +}; + +/* CODEL */ + +enum { + TCA_CODEL_UNSPEC, + TCA_CODEL_TARGET, + TCA_CODEL_LIMIT, + TCA_CODEL_INTERVAL, + TCA_CODEL_ECN, + __TCA_CODEL_MAX +}; + +#define TCA_CODEL_MAX (__TCA_CODEL_MAX - 1) + +struct tc_codel_xstats { + __u32 maxpacket; /* largest packet we've seen so far */ + __u32 count; /* how many drops we've done since the last time we + * entered dropping state + */ + __u32 lastcount; /* count at entry to dropping state */ + __u32 ldelay; /* in-queue delay seen by most recently dequeued packet */ + __s32 drop_next; /* time to drop next packet */ + __u32 drop_overlimit; /* number of time max qdisc packet limit was hit */ + __u32 ecn_mark; /* number of packets we ECN marked instead of dropped */ + __u32 dropping; /* are we in dropping state ? */ +}; + +/* FQ_CODEL */ + +enum { + TCA_FQ_CODEL_UNSPEC, + TCA_FQ_CODEL_TARGET, + TCA_FQ_CODEL_LIMIT, + TCA_FQ_CODEL_INTERVAL, + TCA_FQ_CODEL_ECN, + TCA_FQ_CODEL_FLOWS, + TCA_FQ_CODEL_QUANTUM, + __TCA_FQ_CODEL_MAX +}; + +#define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1) + +enum { + TCA_FQ_CODEL_XSTATS_QDISC, + TCA_FQ_CODEL_XSTATS_CLASS, +}; + +struct tc_fq_codel_qd_stats { + __u32 maxpacket; /* largest packet we've seen so far */ + __u32 drop_overlimit; /* number of time max qdisc + * packet limit was hit + */ + __u32 ecn_mark; /* number of packets we ECN marked + * instead of being dropped + */ + __u32 new_flow_count; /* number of time packets + * created a 'new flow' + */ + __u32 new_flows_len; /* count of flows in new list */ + __u32 old_flows_len; /* count of flows in old list */ +}; + +struct tc_fq_codel_cl_stats { + __s32 deficit; + __u32 ldelay; /* in-queue delay seen by most recently + * dequeued packet + */ + __u32 count; + __u32 lastcount; + __u32 dropping; + __s32 drop_next; +}; + +struct tc_fq_codel_xstats { + __u32 type; + union { + struct tc_fq_codel_qd_stats qdisc_stats; + struct tc_fq_codel_cl_stats class_stats; + }; +}; + +/* FQ */ + +enum { + TCA_FQ_UNSPEC, + + TCA_FQ_PLIMIT, /* limit of total number of packets in queue */ + + TCA_FQ_FLOW_PLIMIT, /* limit of packets per flow */ + + TCA_FQ_QUANTUM, /* RR quantum */ + + TCA_FQ_INITIAL_QUANTUM, /* RR quantum for new flow */ + + TCA_FQ_RATE_ENABLE, /* enable/disable rate limiting */ + + TCA_FQ_FLOW_DEFAULT_RATE,/* obsolete, do not use */ + + TCA_FQ_FLOW_MAX_RATE, /* per flow max rate */ + + TCA_FQ_BUCKETS_LOG, /* log2(number of buckets) */ + + TCA_FQ_FLOW_REFILL_DELAY, /* flow credit refill delay in usec */ + + __TCA_FQ_MAX +}; + +#define TCA_FQ_MAX (__TCA_FQ_MAX - 1) + +struct tc_fq_qd_stats { + __u64 gc_flows; + __u64 highprio_packets; + __u64 tcp_retrans; + __u64 throttled; + __u64 flows_plimit; + __u64 pkts_too_long; + __u64 allocation_errors; + __s64 time_next_delayed_flow; + __u32 flows; + __u32 inactive_flows; + __u32 throttled_flows; + __u32 pad; +}; + +/* Heavy-Hitter Filter */ + +enum { + TCA_HHF_UNSPEC, + TCA_HHF_BACKLOG_LIMIT, + TCA_HHF_QUANTUM, + TCA_HHF_HH_FLOWS_LIMIT, + TCA_HHF_RESET_TIMEOUT, + TCA_HHF_ADMIT_BYTES, + TCA_HHF_EVICT_TIMEOUT, + TCA_HHF_NON_HH_WEIGHT, + __TCA_HHF_MAX +}; + +#define TCA_HHF_MAX (__TCA_HHF_MAX - 1) + +struct tc_hhf_xstats { + __u32 drop_overlimit; /* number of times max qdisc packet limit + * was hit + */ + __u32 hh_overlimit; /* number of times max heavy-hitters was hit */ + __u32 hh_tot_count; /* number of captured heavy-hitters so far */ + __u32 hh_cur_count; /* number of current heavy-hitters */ +}; + +/* PIE */ +enum { + TCA_PIE_UNSPEC, + TCA_PIE_TARGET, + TCA_PIE_LIMIT, + TCA_PIE_TUPDATE, + TCA_PIE_ALPHA, + TCA_PIE_BETA, + TCA_PIE_ECN, + TCA_PIE_BYTEMODE, + __TCA_PIE_MAX +}; +#define TCA_PIE_MAX (__TCA_PIE_MAX - 1) + +struct tc_pie_xstats { + __u32 prob; /* current probability */ + __u32 delay; /* current delay in ms */ + __u32 avg_dq_rate; /* current average dq_rate in bits/pie_time */ + __u32 packets_in; /* total number of packets enqueued */ + __u32 dropped; /* packets dropped due to pie_action */ + __u32 overlimit; /* dropped due to lack of space in queue */ + __u32 maxq; /* maximum queue size */ + __u32 ecn_mark; /* packets marked with ecn*/ +}; +#endif diff --git a/package/gluon-simple-tc/src/include/linux/rtnetlink.h b/package/gluon-simple-tc/src/include/linux/rtnetlink.h new file mode 100644 index 00000000..248fdd3f --- /dev/null +++ b/package/gluon-simple-tc/src/include/linux/rtnetlink.h @@ -0,0 +1,639 @@ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include +#include +#include +#include +#include + +/* rtnetlink families. Values up to 127 are reserved for real address + * families, values above 128 may be used arbitrarily. + */ +#define RTNL_FAMILY_IPMR 128 +#define RTNL_FAMILY_IP6MR 129 +#define RTNL_FAMILY_MAX 129 + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_NEWNEIGHTBL = 64, +#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL + RTM_GETNEIGHTBL = 66, +#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL + RTM_SETNEIGHTBL, +#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + + RTM_NEWNDUSEROPT = 68, +#define RTM_NEWNDUSEROPT RTM_NEWNDUSEROPT + + RTM_NEWADDRLABEL = 72, +#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL + RTM_DELADDRLABEL, +#define RTM_DELADDRLABEL RTM_DELADDRLABEL + RTM_GETADDRLABEL, +#define RTM_GETADDRLABEL RTM_GETADDRLABEL + + RTM_GETDCB = 78, +#define RTM_GETDCB RTM_GETDCB + RTM_SETDCB, +#define RTM_SETDCB RTM_SETDCB + + RTM_NEWNETCONF = 80, +#define RTM_NEWNETCONF RTM_NEWNETCONF + RTM_GETNETCONF = 82, +#define RTM_GETNETCONF RTM_GETNETCONF + + RTM_NEWMDB = 84, +#define RTM_NEWMDB RTM_NEWMDB + RTM_DELMDB = 85, +#define RTM_DELMDB RTM_DELMDB + RTM_GETMDB = 86, +#define RTM_GETMDB RTM_GETMDB + + __RTM_MAX, +#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) +}; + +#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) +#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) +#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr { + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg { + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum { + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ +#define RTPROT_DHCP 16 /* DHCP client */ +#define RTPROT_MROUTED 17 /* Multicast daemon */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t { + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ + +/* Reserved table identifiers */ + +enum rt_class_t { + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_COMPAT=252, + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + RT_TABLE_MAX=0xFFFFFFFF +}; + + +/* Routing message attributes */ + +enum rtattr_type_t { + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, /* no longer used */ + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, /* no longer used */ + RTA_MP_ALGO, /* no longer used */ + RTA_TABLE, + RTA_MARK, + RTA_MFC_STATS, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop { + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo { + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum { + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + RTAX_RTO_MIN, +#define RTAX_RTO_MIN RTAX_RTO_MIN + RTAX_INITRWND, +#define RTAX_INITRWND RTAX_INITRWND + RTAX_QUICKACK, +#define RTAX_QUICKACK RTAX_QUICKACK + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN 0x00000001 +#define RTAX_FEATURE_SACK 0x00000002 +#define RTAX_FEATURE_TIMESTAMP 0x00000004 +#define RTAX_FEATURE_ALLFRAG 0x00000008 + +struct rta_session { + __u8 proto; + __u8 pad1; + __u16 pad2; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + +struct rta_mfc_stats { + __u64 mfcs_packets; + __u64 mfcs_bytes; + __u64 mfcs_wrong_if; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg { + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg { + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg { + unsigned char prefix_family; + unsigned char prefix_pad1; + unsigned short prefix_pad2; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; + unsigned char prefix_pad3; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo { + __u32 preferred_time; + __u32 valid_time; +}; + + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg { + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; + __u32 tcm_info; +}; + +enum { + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + TCA_STAB, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + +/******************************************************************** + * Neighbor Discovery userland options + ****/ + +struct nduseroptmsg { + unsigned char nduseropt_family; + unsigned char nduseropt_pad1; + unsigned short nduseropt_opts_len; /* Total length of options */ + int nduseropt_ifindex; + __u8 nduseropt_icmp_type; + __u8 nduseropt_icmp_code; + unsigned short nduseropt_pad2; + unsigned int nduseropt_pad3; + /* Followed by one or more ND options */ +}; + +enum { + NDUSEROPT_UNSPEC, + NDUSEROPT_SRCADDR, + __NDUSEROPT_MAX +}; + +#define NDUSEROPT_MAX (__NDUSEROPT_MAX - 1) + +/* RTnetlink multicast groups - backwards compatibility for userspace */ +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 + +/* RTnetlink multicast groups */ +enum rtnetlink_groups { + RTNLGRP_NONE, +#define RTNLGRP_NONE RTNLGRP_NONE + RTNLGRP_LINK, +#define RTNLGRP_LINK RTNLGRP_LINK + RTNLGRP_NOTIFY, +#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY + RTNLGRP_NEIGH, +#define RTNLGRP_NEIGH RTNLGRP_NEIGH + RTNLGRP_TC, +#define RTNLGRP_TC RTNLGRP_TC + RTNLGRP_IPV4_IFADDR, +#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE, +#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE, +#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR, +#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE, +#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE, +#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO, +#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO + RTNLGRP_DECnet_IFADDR, +#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR + RTNLGRP_NOP2, + RTNLGRP_DECnet_ROUTE, +#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE + RTNLGRP_DECnet_RULE, +#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE + RTNLGRP_NOP4, + RTNLGRP_IPV6_PREFIX, +#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE, +#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE + RTNLGRP_ND_USEROPT, +#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT + RTNLGRP_PHONET_IFADDR, +#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR + RTNLGRP_PHONET_ROUTE, +#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE + RTNLGRP_DCB, +#define RTNLGRP_DCB RTNLGRP_DCB + RTNLGRP_IPV4_NETCONF, +#define RTNLGRP_IPV4_NETCONF RTNLGRP_IPV4_NETCONF + RTNLGRP_IPV6_NETCONF, +#define RTNLGRP_IPV6_NETCONF RTNLGRP_IPV6_NETCONF + RTNLGRP_MDB, +#define RTNLGRP_MDB RTNLGRP_MDB + __RTNLGRP_MAX +}; +#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) + +/* TC action piece */ +struct tcamsg { + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +#define TCA_ACT_TAB 1 /* attr type must be >=1 */ +#define TCAA_MAX 1 + +/* New extended info filters for IFLA_EXT_MASK */ +#define RTEXT_FILTER_VF (1 << 0) +#define RTEXT_FILTER_BRVLAN (1 << 1) + +/* End of information exported to user level */ + + + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/package/gluon-site/Makefile b/package/gluon-site/Makefile new file mode 100644 index 00000000..fc9f58ce --- /dev/null +++ b/package/gluon-site/Makefile @@ -0,0 +1,42 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-site +PKG_VERSION:=$(if $(GLUON_SITE_CODE),$(GLUON_SITE_CODE),1) +PKG_RELEASE:=$(GLUON_RELEASE) + +PKG_FILE_DEPENDS := $(GLUON_SITEDIR)/ +PKG_BUILD_DEPENDS := luci + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(GLUONDIR)/include/package.mk + +PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG) + + +define Package/gluon-site + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Site-specific files of Gluon +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile + $(call GluonBuildI18N,gluon-site,$(GLUON_SITEDIR)/i18n) +endef + +define Package/gluon-site/install + $(INSTALL_DIR) $(1)/lib/gluon + $(CP) $(GLUON_SITEDIR)/site.conf $(1)/lib/gluon/site.conf + echo "$(GLUON_RELEASE)" > $(1)/lib/gluon/release + + $(call GluonInstallI18N,gluon-site,$(1)) +endef + +$(eval $(call BuildPackage,gluon-site)) diff --git a/package/gluon-status-page/Makefile b/package/gluon-status-page/Makefile new file mode 100644 index 00000000..4e44e2b2 --- /dev/null +++ b/package/gluon-status-page/Makefile @@ -0,0 +1,37 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-status-page +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-status-page + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Adds a status page showing information about the node. + DEPENDS:=+gluon-core +gluon-neighbour-info +uhttpd +endef + +define Package/gluon-status-page/description + Adds a status page showing information about the node. + Especially useful in combination with the next-node feature. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-status-page/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-status-page)) diff --git a/package/gluon-status-page/files/lib/gluon/status-page/www/cgi-bin/status b/package/gluon-status-page/files/lib/gluon/status-page/www/cgi-bin/status new file mode 100755 index 00000000..23e612d3 --- /dev/null +++ b/package/gluon-status-page/files/lib/gluon/status-page/www/cgi-bin/status @@ -0,0 +1,147 @@ +#!/usr/bin/lua + +local util = require("luci.util") +local fs = require("luci.fs") +local ltn12 = require 'luci.ltn12' +local sys = require("luci.sys") +local json = require("luci.json") +local nixio = require 'nixio' +local platform_info = require("platform_info") + +local hostname = sys.hostname() +local model = platform_info.get_model() +local release = util.trim(fs.readfile("/lib/gluon/release") or "") + +function escape_html(s) + return (s:gsub('&', '&'):gsub('<', '<'):gsub('>', '>'):gsub('"', '"')) +end + +function neighbours(ifname) + local info = util.exec("gluon-neighbour-info -d ff02::2:1001 -p 1001 -r nodeinfo -t 3 -i " .. ifname) + local macs = {} + for _, line in ipairs(util.split(info)) do + local data = json.decode(line) + if data then + if data["network"] and data["network"]["mesh_interfaces"] then + for _, mac in ipairs(data["network"]["mesh_interfaces"]) do + macs[mac] = data + end + end + end + end + + return macs +end + +io.write("Content-type: text/html\n\n") +io.write("\n") +io.write("") +io.write("") +io.write("") +io.write("" .. escape_html(hostname) .. "") +io.write("") +io.write("") + +io.write("

" .. escape_html(hostname) .. "

") +io.write("
")
+
+io.write("Model: " .. escape_html(model) .. "\n")
+io.write("Firmware release: " .. escape_html(release) .. "\n\n")
+
+io.write(escape_html(util.trim(sys.exec("uptime | sed 's/^ \+//'"))) .. "\n\n")
+io.write(escape_html(sys.exec("ip address show dev br-client")) .. "\n")
+io.write(escape_html(sys.exec("free -m")) .. "\n")
+io.write(escape_html(sys.exec("df /rom /overlay")))
+io.write("
") + +io.write("

Neighbours

") + +local interfaces = util.split(util.trim(util.exec("iw dev | grep IBSS -B 5 | grep Interface | cut -d' ' -f2"))) + +for _, ifname in ipairs(interfaces) do + io.write("

" .. escape_html(ifname) .. "

") + io.write("
")
+
+  io.write(escape_html(sys.exec("iw dev " .. ifname .. " link")) .. "\n")
+
+  for _, line in ipairs(util.split(util.exec("iw dev " .. ifname .. " station dump"))) do
+    local mac = line:match("^Station (.*) %(on ")
+    if mac then
+      io.write("Station " .. mac .. " (on " .. escape_html(ifname) .. ")\n")
+    else
+      io.write(escape_html(line) .. "\n")
+    end
+  end
+
+  io.write("
") +end + +local stat, fastd_status = pcall( + function() + local fastd_sock = nixio.socket('unix', 'stream') + assert(fastd_sock:connect('/var/run/fastd.mesh_vpn.socket')) + + decoder = json.Decoder() + ltn12.pump.all(ltn12.source.file(fastd_sock), decoder:sink()) + return decoder:get() + end +) + +io.write("

VPN status

") +io.write("
")
+
+if stat then
+  io.write(string.format("fastd running for %.3f seconds\n", fastd_status.uptime/1000))
+
+  local peers = 0
+  local connections = 0
+
+  for key, peer in pairs(fastd_status.peers) do
+    peers = peers+1
+
+    if peer.connection then
+      connections = connections+1
+    end
+  end
+
+  io.write(string.format("There are %i peers configured, of which %i are connected:\n\n", peers, connections))
+
+  for key, peer in pairs(fastd_status.peers) do
+    io.write(string.format("%s: ", escape_html(peer.name)))
+
+    if peer.connection then
+      io.write(string.format("connected for %.3f seconds\n", peer.connection.established/1000))
+    else
+      io.write("not connected\n")
+    end
+  end
+
+else
+  io.write("fastd not running")
+end
+
+io.write("
") + +io.write("") +io.write("") +io.write("") diff --git a/package/gluon-status-page/files/lib/gluon/status-page/www/index.html b/package/gluon-status-page/files/lib/gluon/status-page/www/index.html new file mode 100644 index 00000000..75700015 --- /dev/null +++ b/package/gluon-status-page/files/lib/gluon/status-page/www/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + Redirecting... + + diff --git a/package/gluon-status-page/files/lib/gluon/status-page/www/status.js b/package/gluon-status-page/files/lib/gluon/status-page/www/status.js new file mode 100644 index 00000000..e17102d0 --- /dev/null +++ b/package/gluon-status-page/files/lib/gluon/status-page/www/status.js @@ -0,0 +1,9 @@ +function update_node(id, ip, hostname) { + var el = document.getElementById(id); + + if (!el) + return; + + el.href = "http://[" + ip + "]/"; + el.textContent += " (" + hostname + ")"; +} diff --git a/package/gluon-status-page/files/lib/gluon/upgrade/500-status-page b/package/gluon-status-page/files/lib/gluon/upgrade/500-status-page new file mode 100755 index 00000000..ee7a58c9 --- /dev/null +++ b/package/gluon-status-page/files/lib/gluon/upgrade/500-status-page @@ -0,0 +1,13 @@ +#!/bin/sh + +uci batch <<-EOF + delete uhttpd.main.listen_http + add_list uhttpd.main.listen_http=0.0.0.0:80 + add_list uhttpd.main.listen_http=[::]:80 + + delete uhttpd.main.listen_https + + set uhttpd.main.home=/lib/gluon/status-page/www + + commit uhttpd +EOF diff --git a/package/gluon-wan-dnsmasq/Makefile b/package/gluon-wan-dnsmasq/Makefile new file mode 100644 index 00000000..3722210f --- /dev/null +++ b/package/gluon-wan-dnsmasq/Makefile @@ -0,0 +1,35 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-wan-dnsmasq +PKG_VERSION:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) + +include $(INCLUDE_DIR)/package.mk + +define Package/gluon-wan-dnsmasq + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Support for a secondary DNS server using the WAN interface + DEPENDS:=+gluon-core +dnsmasq +libpacketmark +endef + +define Package/gluon-wan-dnsmasq/description + Gluon community wifi mesh firmware framework: Support for a secondary DNS server using the WAN interface +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/gluon-wan-dnsmasq/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,gluon-wan-dnsmasq)) diff --git a/package/gluon-wan-dnsmasq/files/etc/config/gluon-wan-dnsmasq b/package/gluon-wan-dnsmasq/files/etc/config/gluon-wan-dnsmasq new file mode 100644 index 00000000..9c7e9b4c --- /dev/null +++ b/package/gluon-wan-dnsmasq/files/etc/config/gluon-wan-dnsmasq @@ -0,0 +1,2 @@ +config 'static' + # list 'server' '192.168.0.1' # Example diff --git a/package/gluon-wan-dnsmasq/files/etc/hotplug.d/iface/50-gluon-wan-dnsmasq b/package/gluon-wan-dnsmasq/files/etc/hotplug.d/iface/50-gluon-wan-dnsmasq new file mode 100644 index 00000000..d559e9b1 --- /dev/null +++ b/package/gluon-wan-dnsmasq/files/etc/hotplug.d/iface/50-gluon-wan-dnsmasq @@ -0,0 +1,3 @@ +if [ "$INTERFACE" = 'wan' -o "$INTERFACE" = 'wan6' ]; then + /lib/gluon/wan-dnsmasq/update.lua +fi diff --git a/package/gluon-wan-dnsmasq/files/etc/init.d/gluon-wan-dnsmasq b/package/gluon-wan-dnsmasq/files/etc/init.d/gluon-wan-dnsmasq new file mode 100755 index 00000000..22bed039 --- /dev/null +++ b/package/gluon-wan-dnsmasq/files/etc/init.d/gluon-wan-dnsmasq @@ -0,0 +1,26 @@ +#!/bin/sh /etc/rc.common + +START=60 + +SERVICE_NAME=gluon-wan-dnsmasq +SERVICE_USE_PID=1 +SERVICE_PID_FILE=/var/run/gluon-wan-dnsmasq.pid + + +PORT=54 +PACKET_MARK=1 + +RESOLV_CONF_DIR=/var/gluon/wan-dnsmasq +RESOLV_CONF=$RESOLV_CONF_DIR/resolv.conf + + +start() { + mkdir -p $RESOLV_CONF_DIR + /lib/gluon/wan-dnsmasq/update.lua + + LD_PRELOAD=libpacketmark.so LIBPACKETMARK_MARK=$PACKET_MARK service_start /usr/sbin/dnsmasq -x $SERVICE_PID_FILE -u root -i lo -p $PORT -h -r $RESOLV_CONF +} + +stop() { + service_stop /usr/sbin/dnsmasq +} diff --git a/package/gluon-wan-dnsmasq/files/lib/gluon/wan-dnsmasq/update.lua b/package/gluon-wan-dnsmasq/files/lib/gluon/wan-dnsmasq/update.lua new file mode 100755 index 00000000..88a86507 --- /dev/null +++ b/package/gluon-wan-dnsmasq/files/lib/gluon/wan-dnsmasq/update.lua @@ -0,0 +1,46 @@ +#!/usr/bin/lua + +local RESOLV_CONF_DIR = '/var/gluon/wan-dnsmasq' +local RESOLV_CONF = RESOLV_CONF_DIR .. '/resolv.conf' + + +local ubus = require('ubus').connect() +local uci = require('luci.model.uci').cursor() +local fs = require 'nixio.fs' + + +local new_servers = '' + + +local function append_servers(servers) + for _, server in ipairs(servers) do + new_servers = new_servers .. 'nameserver ' .. server .. '\n' + end +end + +local function append_interface_servers(iface) + append_servers(ubus:call('network.interface.' .. iface, 'status', {}).inactive['dns-server']) +end + + +local static = uci:get_first('gluon-wan-dnsmasq', 'static', 'server') + +if type(static) == 'table' and #static > 0 then + append_servers(static) +else + pcall(append_interface_servers, 'wan6') + pcall(append_interface_servers, 'wan') +end + + +fs.mkdirr(RESOLV_CONF_DIR) + +local old_servers = fs.readfile(RESOLV_CONF) + +if new_servers ~= old_servers then + local f = io.open(RESOLV_CONF .. '.tmp', 'w') + f:write(new_servers) + f:close() + + fs.rename(RESOLV_CONF .. '.tmp', RESOLV_CONF) +end