diff --git a/.gitignore b/.gitignore
index 2b6cabec..ab2b03cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
*~
-/build
+/lede
/output
/site
-/openwrt
+/tmp
/packages
-/modules.local
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 824362d7..1c827fb2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -46,24 +46,8 @@ fix some bug, detail in the remaining commit message exactly how it could be
triggered and what you did to fix it. If in question, have a glance at the
existing commit messages to get the idea.
-Squash commits
---------------
-Most changes are trivial enough to fit in one single commit in order to not
-clutter the history. While developing a new feature, you are free to use
-multiple commits, but if your feature is to be merged, reduce the number of
-commits to a minimum. Even huge feature introductions like the 802.11s mesh
-(commit [2a93c58]) fit into a single commit.
-
-If you developed your change in multiple smaller commits, you can easily
-[squash] those before opening the pull request. While discussing, it is okay to
-do your changes using `git commit --amend` and force-push them to your head of
-the pull request. This way, your change always consists of only one commit and
-can be merged in the instant everybody is content with the whole thing.
-
[packages]: http://gluon.readthedocs.org/en/latest/user/site.html#packages
[#gluon]: https://webirc.hackint.org/#gluon
[mailing list]: mailto:gluon@luebeck.freifunk.net
[list of rejected features]: https://github.com/freifunk-gluon/gluon/issues?q=label%3Arejected
-[2a93c58]: https://github.com/freifunk-gluon/gluon/commit/2a93c580428d10724116b0d2d1238e2745715a14
-[squash]: https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Squashing-Commits
diff --git a/LICENSE b/LICENSE
index 81ed5a23..a9adc369 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
The code of Project Gluon may be distributed under the following terms, unless
noted otherwise in individual files or subtrees.
-Copyright (c) 2013, Project Gluon
+Copyright (c) 2013-2017, Project Gluon
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -25,10 +25,10 @@ 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.
-OpenWrt is licensed under the terms of the GNU General Public License Version 2,
-which can be found under openwrt/LICENSE after the openwrt submodule has been
-obtained. This applies to the following submodules:
- * openwrt
+LEDE and OpenWrt are licensed under the terms of the GNU General Public License
+Version 2, which can be found at lede/LICENSE after the lede repository has been
+obtained. This applies to the following repositories:
+ * lede
* packages/openwrt
* packages/routing
* packages/luci
diff --git a/Makefile b/Makefile
index 90ea36f3..9593fd91 100644
--- a/Makefile
+++ b/Makefile
@@ -4,489 +4,180 @@ LC_ALL:=C
LANG:=C
export LC_ALL LANG
-export SHELL:=/usr/bin/env bash
-GLUONPATH ?= $(PATH)
-export GLUONPATH := $(GLUONPATH)
+# initialize (possibly already user set) directory variables
+GLUON_SITEDIR ?= site
+GLUON_TMPDIR ?= tmp
+GLUON_OUTPUTDIR ?= output
-empty:=
-space:= $(empty) $(empty)
+GLUON_IMAGEDIR ?= $(GLUON_OUTPUTDIR)/images
+GLUON_PACKAGEDIR ?= $(GLUON_OUTPUTDIR)/packages
-GLUONMAKE_EARLY = PATH=$(GLUONPATH) $(SUBMAKE) -C $(GLUON_ORIGOPENWRTDIR) -f $(GLUONDIR)/Makefile GLUON_TOOLS=0 QUILT=
-GLUONMAKE = PATH=$(GLUONPATH) $(SUBMAKE) -C $(GLUON_OPENWRTDIR) -f $(GLUONDIR)/Makefile
+# check for spaces & resolve possibly relative paths
+define mkabspath
+ ifneq (1,$(words [$($(1))]))
+ $$(error $(1) must not contain spaces)
+ endif
+ override $(1) := $(abspath $($(1)))
+endef
-ifneq ($(OPENWRT_BUILD),1)
+$(eval $(call mkabspath,GLUON_SITEDIR))
+$(eval $(call mkabspath,GLUON_TMPDIR))
+$(eval $(call mkabspath,GLUON_OUTPUTDIR))
+$(eval $(call mkabspath,GLUON_IMAGEDIR))
+$(eval $(call mkabspath,GLUON_PACKAGEDIR))
-GLUONDIR:=${CURDIR}
+export GLUON_TMPDIR GLUON_IMAGEDIR GLUON_PACKAGEDIR DEVICES
-include $(GLUONDIR)/include/gluon.mk
-TOPDIR:=$(GLUON_ORIGOPENWRTDIR)
-export TOPDIR
+$(GLUON_SITEDIR)/site.mk:
+ $(error No site configuration was found. Please check out a site configuration to $(GLUON_SITEDIR))
+
+include $(GLUON_SITEDIR)/site.mk
+
+GLUON_RELEASE ?= $(error GLUON_RELEASE not set. GLUON_RELEASE can be set in site.mk or on the command line)
+
+GLUON_MULTIDOMAIN ?= 0
+GLUON_WLAN_MESH ?= 11s
+GLUON_DEBUG ?= 0
+
+export GLUON_RELEASE GLUON_REGION GLUON_MULTIDOMAIN GLUON_WLAN_MESH GLUON_DEBUG
+
+show-release:
+ @echo '$(GLUON_RELEASE)'
update: FORCE
- $(GLUONDIR)/scripts/update.sh
- $(GLUONDIR)/scripts/patch.sh
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update.sh
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/patch.sh
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/feeds.sh
update-patches: FORCE
- $(GLUONDIR)/scripts/update.sh
- $(GLUONDIR)/scripts/update-patches.sh
- $(GLUONDIR)/scripts/patch.sh
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update.sh
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/update-patches.sh
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/patch.sh
--include $(TOPDIR)/include/host.mk
-
-_SINGLE=export MAKEFLAGS=$(space);
-
-override OPENWRT_BUILD=1
-override GLUON_TOOLS=1
-GREP_OPTIONS=
-export OPENWRT_BUILD GLUON_TOOLS GREP_OPTIONS
-
--include $(TOPDIR)/include/debug.mk
--include $(TOPDIR)/include/depends.mk
-include $(GLUONDIR)/include/toplevel.mk
+update-feeds: FORCE
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/feeds.sh
-include $(GLUONDIR)/targets/targets.mk
+GLUON_TARGETS :=
+
+define GluonTarget
+gluon_target := $(1)$$(if $(2),-$(2))
+GLUON_TARGETS += $$(gluon_target)
+GLUON_TARGET_$$(gluon_target)_BOARD := $(1)
+GLUON_TARGET_$$(gluon_target)_SUBTARGET := $(if $(3),$(3),$(2))
+endef
+
+include targets/targets.mk
-CheckTarget := [ -n '$(GLUON_TARGET)' -a -n '$(GLUON_TARGET_$(GLUON_TARGET)_BOARD)' ] \
- || (echo -e 'Please set GLUON_TARGET to a valid target. Gluon supports the following targets:$(subst $(space),\n * ,$(GLUON_TARGETS))'; false)
+LEDEMAKE = $(MAKE) -C lede
+
+BOARD := $(GLUON_TARGET_$(GLUON_TARGET)_BOARD)
+SUBTARGET := $(GLUON_TARGET_$(GLUON_TARGET)_SUBTARGET)
+
+GLUON_CONFIG_VARS := \
+ GLUON_SITEDIR='$(GLUON_SITEDIR)' \
+ GLUON_RELEASE='$(GLUON_RELEASE)' \
+ GLUON_BRANCH='$(GLUON_BRANCH)' \
+ GLUON_LANGS='$(GLUON_LANGS)' \
+ BOARD='$(BOARD)' \
+ SUBTARGET='$(SUBTARGET)'
+
+LEDE_TARGET := $(BOARD)$(if $(SUBTARGET),-$(SUBTARGET))
+
+export LEDE_TARGET
-CheckExternal := test -d $(GLUON_ORIGOPENWRTDIR) || (echo 'You don'"'"'t seem to have obtained the external repositories needed by Gluon; please call `make update` first!'; false)
+CheckTarget := [ '$(LEDE_TARGET)' ] \
+ || (echo 'Please set GLUON_TARGET to a valid target. Gluon supports the following targets:'; $(foreach target,$(GLUON_TARGETS),echo ' * $(target)';) false)
+
+CheckExternal := test -d lede || (echo 'You don'"'"'t seem to have obtained the external repositories needed by Gluon; please call `make update` first!'; false)
+
+define CheckSite
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' GLUON_SITE_CONFIG='$(1).conf' $(LUA) scripts/site_config.lua \
+ || (echo 'Your site configuration ($(1).conf) did not pass validation.'; false)
+
+endef
+
+list-targets: FORCE
+ @$(foreach target,$(GLUON_TARGETS),echo '$(target)';)
-create-key: FORCE
- @$(CheckExternal)
- +@$(GLUONMAKE_EARLY) create-key
+GLUON_FEATURE_PACKAGES := $(shell scripts/features.sh '$(GLUON_FEATURES)' || echo '__ERROR__')
+ifneq ($(filter __ERROR__,$(GLUON_FEATURE_PACKAGES)),)
+$(error Error while evaluating GLUON_FEATURES)
+endif
-prepare-target: FORCE
+
+GLUON_PACKAGES :=
+define merge_packages
+ $(foreach pkg,$(1),
+ GLUON_PACKAGES := $$(strip $$(filter-out -$$(patsubst -%,%,$(pkg)) $$(patsubst -%,%,$(pkg)),$$(GLUON_PACKAGES)) $(pkg))
+ )
+endef
+$(eval $(call merge_packages,$(GLUON_FEATURE_PACKAGES) $(GLUON_SITE_PACKAGES)))
+
+config: FORCE
@$(CheckExternal)
@$(CheckTarget)
- +@$(GLUONMAKE_EARLY) prepare-target
+
+ @$(GLUON_CONFIG_VARS) \
+ scripts/target_config.sh '$(GLUON_TARGET)' '$(GLUON_PACKAGES)' \
+ > lede/.config
+ +@$(LEDEMAKE) defconfig
+
+ @$(GLUON_CONFIG_VARS) \
+ scripts/target_config_check.sh '$(GLUON_TARGET)' '$(GLUON_PACKAGES)'
+
+
+LUA := lede/staging_dir/hostpkg/bin/lua
+
+$(LUA):
+ @$(CheckExternal)
+
+ +@[ -e lede/.config ] || $(LEDEMAKE) defconfig
+ +@$(LEDEMAKE) tools/install
+ +@$(LEDEMAKE) package/lua/host/install
+
+prepare-target: config $(LUA) ;
all: prepare-target
- +@$(GLUONMAKE) build-key
- +@$(GLUONMAKE) prepare
- +@$(GLUONMAKE) images
- +@$(GLUONMAKE) modules
+ $(foreach conf,site $(patsubst $(GLUON_SITEDIR)/%.conf,%,$(wildcard $(GLUON_SITEDIR)/domains/*.conf)),$(call CheckSite,$(conf)))
-prepare: prepare-target
- +@$(GLUONMAKE) build-key
- +@$(GLUONMAKE) $@
+ @scripts/clean_output.sh
+ +@$(LEDEMAKE)
+ @GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/copy_output.sh '$(GLUON_TARGET)'
-clean download images modules: FORCE
- @$(CheckExternal)
- @$(CheckTarget)
- +@$(GLUONMAKE_EARLY) maybe-prepare-target
- +@$(GLUONMAKE) build-key
- +@$(GLUONMAKE) $@
+clean download: config
+ +@$(LEDEMAKE) $@
-toolchain/% package/% target/% image/%: FORCE
- @$(CheckExternal)
- @$(CheckTarget)
- +@$(GLUONMAKE_EARLY) maybe-prepare-target
- +@$(GLUONMAKE) build-key
- +@$(GLUONMAKE) $@
+dirclean: FORCE
+ +@[ -e lede/.config ] || $(LEDEMAKE) defconfig
+ +@$(LEDEMAKE) dirclean
+ @rm -rf $(GLUON_TMPDIR) $(GLUON_OUTPUTDIR)
-manifest: FORCE
- @[ -n '$(GLUON_BRANCH)' ] || (echo 'Please set GLUON_BRANCH to create a manifest.'; false)
+manifest: $(LUA) FORCE
+ @[ '$(GLUON_BRANCH)' ] || (echo 'Please set GLUON_BRANCH to create a manifest.'; false)
@echo '$(GLUON_PRIORITY)' | grep -qE '^([0-9]*\.)?[0-9]+$$' || (echo 'Please specify a numeric value for GLUON_PRIORITY to create a manifest.'; false)
@$(CheckExternal)
- ( \
+ @( \
echo 'BRANCH=$(GLUON_BRANCH)' && \
- echo 'DATE=$(shell $(GLUON_ORIGOPENWRTDIR)/staging_dir/host/bin/lua $(GLUONDIR)/scripts/rfc3339date.lua)' && \
+ echo "DATE=$$($(LUA) scripts/rfc3339date.lua)" && \
echo 'PRIORITY=$(GLUON_PRIORITY)' && \
- echo \
- ) > $(GLUON_BUILDDIR)/$(GLUON_BRANCH).manifest.tmp
-
- +($(foreach GLUON_TARGET,$(GLUON_TARGETS), \
- ( [ ! -e $(BOARD_BUILDDIR)/prepared ] || ( $(GLUONMAKE) manifest GLUON_TARGET='$(GLUON_TARGET)' V=s$(OPENWRT_VERBOSE) ) ) && \
- ) :)
-
- mkdir -p $(GLUON_IMAGEDIR)/sysupgrade
- mv $(GLUON_BUILDDIR)/$(GLUON_BRANCH).manifest.tmp $(GLUON_IMAGEDIR)/sysupgrade/$(GLUON_BRANCH).manifest
-
-dirclean : FORCE
- for dir in build_dir dl staging_dir tmp; do \
- rm -rf $(GLUON_ORIGOPENWRTDIR)/$$dir; \
- done
- rm -rf $(GLUON_BUILDDIR) $(GLUON_OUTPUTDIR)
-
-else
-
-TOPDIR=${CURDIR}
-export TOPDIR
-
-include rules.mk
-
-include $(GLUONDIR)/include/gluon.mk
-
-include $(INCLUDE_DIR)/host.mk
-include $(INCLUDE_DIR)/depends.mk
-include $(INCLUDE_DIR)/subdir.mk
-
-include package/Makefile
-include tools/Makefile
-include toolchain/Makefile
-include target/Makefile
-
-
-PROFILES :=
-PROFILE_PACKAGES :=
-
-define Profile
- $(eval $(call Profile/Default))
- $(eval $(call Profile/$(1)))
-endef
-
-define GluonProfile
-PROFILES += $(1)
-PROFILE_PACKAGES += $(filter-out -%,$(2) $(GLUON_$(1)_SITE_PACKAGES))
-GLUON_$(1)_PROFILE := $(if $(3),$(3),$(1))
-GLUON_$(1)_DEFAULT_PACKAGES := $(2)
-GLUON_$(1)_FACTORY_SUFFIX := -squashfs-factory
-GLUON_$(1)_SYSUPGRADE_SUFFIX := -squashfs-sysupgrade
-GLUON_$(1)_FACTORY_EXT := .bin
-GLUON_$(1)_SYSUPGRADE_EXT := .bin
-GLUON_$(1)_MODELS :=
-endef
-
-define GluonProfileFactorySuffix
-GLUON_$(1)_FACTORY_SUFFIX := $(2)
-GLUON_$(1)_FACTORY_EXT := $(3)
-endef
-
-define GluonProfileSysupgradeSuffix
-GLUON_$(1)_SYSUPGRADE_SUFFIX := $(2)
-GLUON_$(1)_SYSUPGRADE_EXT := $(3)
-endef
-
-define GluonModel
-GLUON_$(1)_MODELS += $(3)
-GLUON_$(1)_MODEL_$(3) := $(2)
-GLUON_$(1)_MODEL_$(3)_ALIASES :=
-endef
-
-define GluonModelAlias
-GLUON_$(1)_MODEL_$(2)_ALIASES += $(3)
-endef
-
-
-export SHA512SUM := $(GLUONDIR)/scripts/sha512sum.sh
-
-
-prereq: FORCE
- +$(NO_TRACE_MAKE) prereq
-
-prepare-tmpinfo: FORCE
- @+$(MAKE) -r -s staging_dir/host/.prereq-build OPENWRT_BUILD= QUIET=0
- mkdir -p tmp/info
- $(_SINGLE)$(NO_TRACE_MAKE) -j1 -r -s -f include/scan.mk SCAN_TARGET="packageinfo" SCAN_DIR="package" SCAN_NAME="package" SCAN_DEPS="$(TOPDIR)/include/package*.mk $(TOPDIR)/overlay/*/*.mk" SCAN_EXTRA=""
- $(_SINGLE)$(NO_TRACE_MAKE) -j1 -r -s -f include/scan.mk SCAN_TARGET="targetinfo" SCAN_DIR="target/linux" SCAN_NAME="target" SCAN_DEPS="profiles/*.mk $(TOPDIR)/include/kernel*.mk $(TOPDIR)/include/target.mk" SCAN_DEPTH=2 SCAN_EXTRA="" SCAN_MAKEOPTS="TARGET_BUILD=1"
- for type in package target; do \
- f=tmp/.$${type}info; t=tmp/.config-$${type}.in; \
- [ "$$t" -nt "$$f" ] || ./scripts/metadata.pl $${type}_config "$$f" > "$$t" || { rm -f "$$t"; echo "Failed to build $$t"; false; break; }; \
- done
- [ tmp/.config-feeds.in -nt tmp/.packagefeeds ] || ./scripts/feeds feed_config > tmp/.config-feeds.in
- ./scripts/metadata.pl package_mk tmp/.packageinfo > tmp/.packagedeps || { rm -f tmp/.packagedeps; false; }
- ./scripts/metadata.pl package_feeds tmp/.packageinfo > tmp/.packagefeeds || { rm -f tmp/.packagefeeds; false; }
- touch $(TOPDIR)/tmp/.build
-
-feeds: FORCE
- rm -rf $(TOPDIR)/package/feeds
- mkdir $(TOPDIR)/package/feeds
- [ ! -f $(GLUON_SITEDIR)/modules ] || . $(GLUON_SITEDIR)/modules && for feed in $$GLUON_SITE_FEEDS; do ln -s ../../../packages/$$feed $(TOPDIR)/package/feeds/$$feed; done
- ln -s ../../../package $(TOPDIR)/package/feeds/gluon
- . $(GLUONDIR)/modules && for feed in $$GLUON_FEEDS; do ln -s ../../../packages/$$feed $(TOPDIR)/package/feeds/module_$$feed; done
- +$(GLUONMAKE_EARLY) prepare-tmpinfo
-
-gluon-tools: FORCE
- +$(GLUONMAKE_EARLY) tools/patch/install
- +$(GLUONMAKE_EARLY) tools/sed/install
- +$(GLUONMAKE_EARLY) tools/cmake/install
- +$(GLUONMAKE_EARLY) package/lua/host/install package/usign/host/install
-
-
-
-early_prepared_stamp := $(GLUON_BUILDDIR)/prepared_$(shell \
- ( \
- $(SHA512SUM) $(GLUONDIR)/modules; \
- [ ! -r $(GLUON_SITEDIR)/modules ] || $(SHA512SUM) $(GLUON_SITEDIR)/modules \
- ) | $(SHA512SUM) )
-
-prepare-early: FORCE
- for dir in build_dir dl staging_dir; do \
- mkdir -p $(GLUON_ORIGOPENWRTDIR)/$$dir; \
- done
-
- +$(GLUONMAKE_EARLY) feeds
- +$(GLUONMAKE_EARLY) gluon-tools
-
- mkdir -p $$(dirname $(early_prepared_stamp))
- touch $(early_prepared_stamp)
-
-$(early_prepared_stamp):
- rm -f $(GLUON_BUILDDIR)/prepared_*
- +$(GLUONMAKE_EARLY) prepare-early
-
-$(GLUON_OPKG_KEY): $(early_prepared_stamp) FORCE
- [ -s $(GLUON_OPKG_KEY) -a -s $(GLUON_OPKG_KEY).pub ] || \
- ( mkdir -p $$(dirname $(GLUON_OPKG_KEY)) && $(STAGING_DIR_HOST)/bin/usign -G -s $(GLUON_OPKG_KEY) -p $(GLUON_OPKG_KEY).pub -c "Gluon opkg key" )
-
-$(GLUON_OPKG_KEY).pub: $(GLUON_OPKG_KEY)
-
-create-key: $(GLUON_OPKG_KEY).pub
-
-include $(GLUONDIR)/targets/targets.mk
-
-ifneq ($(GLUON_TARGET),)
-
-include $(GLUONDIR)/targets/$(GLUON_TARGET)/profiles.mk
-
-BOARD := $(GLUON_TARGET_$(GLUON_TARGET)_BOARD)
-override SUBTARGET := $(GLUON_TARGET_$(GLUON_TARGET)_SUBTARGET)
-
-target_prepared_stamp := $(BOARD_BUILDDIR)/target-prepared
-gluon_prepared_stamp := $(BOARD_BUILDDIR)/prepared
-
-PREPARED_RELEASE = $$(cat $(gluon_prepared_stamp))
-IMAGE_PREFIX = gluon-$(GLUON_SITE_CODE)-$(PREPARED_RELEASE)
-MODULE_PREFIX = gluon-$(GLUON_SITE_CODE)-$(PREPARED_RELEASE)
-
-
-include $(INCLUDE_DIR)/target.mk
-
-build-key: FORCE
- rm -f $(BUILD_KEY) $(BUILD_KEY).pub
- cp $(GLUON_OPKG_KEY) $(BUILD_KEY)
- cp $(GLUON_OPKG_KEY).pub $(BUILD_KEY).pub
-
-config: FORCE
- +$(NO_TRACE_MAKE) scripts/config/conf OPENWRT_BUILD= QUIET=0
- +$(GLUONMAKE) prepare-tmpinfo
- ( \
- cat $(GLUONDIR)/include/config; \
- echo 'CONFIG_TARGET_$(GLUON_TARGET_$(GLUON_TARGET)_BOARD)=y'; \
- $(if $(GLUON_TARGET_$(GLUON_TARGET)_SUBTARGET), \
- echo 'CONFIG_TARGET_$(GLUON_TARGET_$(GLUON_TARGET)_BOARD)_$(GLUON_TARGET_$(GLUON_TARGET)_SUBTARGET)=y'; \
- ) \
- cat $(GLUONDIR)/targets/$(GLUON_TARGET)/config 2>/dev/null; \
- echo 'CONFIG_BUILD_SUFFIX="gluon-$(GLUON_TARGET)"'; \
- echo '$(patsubst %,CONFIG_PACKAGE_%=m,$(sort $(filter-out -%,$(GLUON_DEFAULT_PACKAGES) $(GLUON_SITE_PACKAGES) $(PROFILE_PACKAGES))))' \
- | sed -e 's/ /\n/g'; \
- echo '$(patsubst %,CONFIG_LUCI_LANG_%=y,$(GLUON_LANGS))' \
- | sed -e 's/ /\n/g'; \
- ) > $(BOARD_BUILDDIR)/config.tmp
- scripts/config/conf --defconfig=$(BOARD_BUILDDIR)/config.tmp Config.in
- +$(SUBMAKE) tools/install
-
-prepare-target: $(GLUON_OPKG_KEY).pub
- rm $(GLUON_OPENWRTDIR)/tmp || true
- mkdir -p $(GLUON_OPENWRTDIR)/tmp
-
- for link in build_dir config Config.in dl include Makefile package rules.mk scripts staging_dir target toolchain tools; do \
- ln -sf $(GLUON_ORIGOPENWRTDIR)/$$link $(GLUON_OPENWRTDIR); \
- done
-
- +$(GLUONMAKE) config
- touch $(target_prepared_stamp)
-
-$(target_prepared_stamp):
- +$(GLUONMAKE_EARLY) prepare-target
-
-maybe-prepare-target: $(target_prepared_stamp)
- +$(GLUONMAKE_EARLY) $(GLUON_OPKG_KEY).pub
-
-$(BUILD_DIR)/.prepared: Makefile
- @mkdir -p $$(dirname $@)
- @touch $@
-
-$(toolchain/stamp-install): $(tools/stamp-install)
-$(package/stamp-compile): $(package/stamp-cleanup)
-
-
-clean: FORCE
- +$(SUBMAKE) clean
- rm -f $(gluon_prepared_stamp)
-
-
-download: FORCE
- +$(SUBMAKE) tools/download
- +$(SUBMAKE) toolchain/download
- +$(SUBMAKE) package/download
- +$(SUBMAKE) target/download
-
-toolchain: $(toolchain/stamp-install) $(tools/stamp-install)
-
-include $(INCLUDE_DIR)/kernel.mk
-
-kernel: FORCE
- +$(NO_TRACE_MAKE) -C $(TOPDIR)/target/linux/$(BOARD) $(LINUX_DIR)/.image TARGET_BUILD=1
- +$(NO_TRACE_MAKE) -C $(TOPDIR)/target/linux/$(BOARD) $(LINUX_DIR)/.modules TARGET_BUILD=1
-
-packages: $(package/stamp-compile)
- $(_SINGLE)$(SUBMAKE) -r package/index
-
-prepare-image: FORCE
- rm -rf $(BOARD_KDIR)
- mkdir -p $(BOARD_KDIR)
- -cp $(KERNEL_BUILD_DIR)/* $(BOARD_KDIR)/
- +$(SUBMAKE) -C $(TOPDIR)/target/linux/$(BOARD)/image image_prepare KDIR="$(BOARD_KDIR)"
-
-prepare: FORCE
- @$(STAGING_DIR_HOST)/bin/lua $(GLUONDIR)/scripts/site_config.lua \
- || (echo 'Your site configuration did not pass validation.'; false)
-
- mkdir -p $(GLUON_IMAGEDIR) $(BOARD_BUILDDIR)
- echo 'src packages file:../openwrt/bin/$(BOARD)/packages' > $(BOARD_BUILDDIR)/opkg.conf
-
- +$(GLUONMAKE) toolchain
- +$(GLUONMAKE) kernel
- +$(GLUONMAKE) packages
- +$(GLUONMAKE) prepare-image
-
- echo "$(GLUON_RELEASE)" > $(gluon_prepared_stamp)
-
-$(gluon_prepared_stamp):
- +$(GLUONMAKE) prepare
-
-modules: FORCE $(gluon_prepared_stamp)
- -rm -f $(GLUON_MODULEDIR)/*/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic)/*
- -rmdir -p $(GLUON_MODULEDIR)/*/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic)
- mkdir -p $(GLUON_MODULEDIR)/$(MODULE_PREFIX)/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic)
- cp $(PACKAGE_DIR)/kmod-*.ipk $(GLUON_MODULEDIR)/$(MODULE_PREFIX)/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic)
-
- $(_SINGLE)$(SUBMAKE) -r package/index PACKAGE_DIR=$(GLUON_MODULEDIR)/$(MODULE_PREFIX)/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic)
-
-
-include $(INCLUDE_DIR)/package-ipkg.mk
-
-# override variables from rules.mk
-PACKAGE_DIR = $(GLUON_OPENWRTDIR)/bin/$(BOARD)/packages
-
-PROFILE_BUILDDIR = $(BOARD_BUILDDIR)/profiles/$(PROFILE)
-PROFILE_KDIR = $(PROFILE_BUILDDIR)/kernel
-BIN_DIR = $(PROFILE_BUILDDIR)/images
-
-TARGET_DIR = $(PROFILE_BUILDDIR)/root
-
-OPKG:= \
- IPKG_TMP="$(TMP_DIR)/ipkgtmp" \
- IPKG_INSTROOT="$(TARGET_DIR)" \
- IPKG_CONF_DIR="$(TMP_DIR)" \
- IPKG_OFFLINE_ROOT="$(TARGET_DIR)" \
- $(STAGING_DIR_HOST)/bin/opkg \
- -f $(BOARD_BUILDDIR)/opkg.conf \
- --cache $(TMP_DIR)/dl \
- --offline-root $(TARGET_DIR) \
- --force-postinstall \
- --add-dest root:/ \
- --add-arch all:100 \
- --add-arch $(ARCH_PACKAGES):200
-
-EnableInitscript = ! grep -q '\#!/bin/sh /etc/rc.common' $(1) || bash ./etc/rc.common $(1) enable
-
-
-enable_initscripts: FORCE
- cd $(TARGET_DIR) && ( export IPKG_INSTROOT=$(TARGET_DIR); \
- $(foreach script,$(wildcard $(TARGET_DIR)/etc/init.d/*), \
- $(call EnableInitscript,$(script)); \
+ echo && \
+ $(foreach GLUON_TARGET,$(GLUON_TARGETS), \
+ GLUON_SITEDIR='$(GLUON_SITEDIR)' scripts/generate_manifest.sh '$(GLUON_TARGET)' && \
) : \
- )
+ ) > 'tmp/$(GLUON_BRANCH).manifest.tmp'
+ @mkdir -p '$(GLUON_IMAGEDIR)/sysupgrade'
+ @mv 'tmp/$(GLUON_BRANCH).manifest.tmp' '$(GLUON_IMAGEDIR)/sysupgrade/$(GLUON_BRANCH).manifest'
-# Generate package list
-$(eval $(call merge-lists,INSTALL_PACKAGES,DEFAULT_PACKAGES GLUON_DEFAULT_PACKAGES GLUON_SITE_PACKAGES GLUON_$(PROFILE)_DEFAULT_PACKAGES GLUON_$(PROFILE)_SITE_PACKAGES))
+FORCE: ;
-package_install: FORCE
- $(OPKG) update
- $(OPKG) install $(PACKAGE_DIR)/base-files_*.ipk $(PACKAGE_DIR)/libc_*.ipk
- $(OPKG) install $(PACKAGE_DIR)/kernel_*.ipk
-
- $(OPKG) install $(INSTALL_PACKAGES)
- +$(GLUONMAKE) enable_initscripts
-
- rm -f $(TARGET_DIR)/usr/lib/opkg/lists/* $(TARGET_DIR)/tmp/opkg.lock
-
-# Remove opkg database when opkg is not intalled
- if [ ! -x $(TARGET_DIR)/bin/opkg ]; then rm -rf $(TARGET_DIR)/usr/lib/opkg; fi
-
-
-include $(INCLUDE_DIR)/version.mk
-
-opkg_config: FORCE
- for d in base packages luci routing telephony management; do \
- echo "src/gz %n_$$d %U/$$d"; \
- done > $(TARGET_DIR)/etc/opkg/distfeeds.conf
- $(VERSION_SED) $(TARGET_DIR)/etc/opkg/distfeeds.conf
-
-
-image: FORCE
- rm -rf $(TARGET_DIR) $(BIN_DIR) $(PROFILE_KDIR)
- mkdir -p $(TARGET_DIR) $(BIN_DIR) $(TARGET_DIR)/tmp $(GLUON_IMAGEDIR)/factory $(GLUON_IMAGEDIR)/sysupgrade
- cp -r $(BOARD_KDIR) $(PROFILE_KDIR)
-
- +$(GLUONMAKE) package_install
- +$(GLUONMAKE) opkg_config
-
- $(call Image/mkfs/prepare)
- $(_SINGLE)$(NO_TRACE_MAKE) -C $(TOPDIR)/target/linux/$(BOARD)/image install TARGET_BUILD=1 IMG_PREFIX=gluon \
- PROFILE="$(GLUON_$(PROFILE)_PROFILE)" KDIR="$(PROFILE_KDIR)" TARGET_DIR="$(TARGET_DIR)" BIN_DIR="$(BIN_DIR)" TMP_DIR="$(TMP_DIR)"
-
- $(foreach model,$(GLUON_$(PROFILE)_MODELS), \
- $(if $(GLUON_$(PROFILE)_SYSUPGRADE_EXT), \
- rm -f $(GLUON_IMAGEDIR)/sysupgrade/gluon-*-$(model)-sysupgrade$(GLUON_$(PROFILE)_SYSUPGRADE_EXT) && \
- cp $(BIN_DIR)/gluon-$(GLUON_$(PROFILE)_MODEL_$(model))$(GLUON_$(PROFILE)_SYSUPGRADE_SUFFIX)$(GLUON_$(PROFILE)_SYSUPGRADE_EXT) $(GLUON_IMAGEDIR)/sysupgrade/$(IMAGE_PREFIX)-$(model)-sysupgrade$(GLUON_$(PROFILE)_SYSUPGRADE_EXT) && \
- ) \
- $(if $(GLUON_$(PROFILE)_FACTORY_EXT), \
- rm -f $(GLUON_IMAGEDIR)/factory/gluon-*-$(model)$(GLUON_$(PROFILE)_FACTORY_EXT) && \
- cp $(BIN_DIR)/gluon-$(GLUON_$(PROFILE)_MODEL_$(model))$(GLUON_$(PROFILE)_FACTORY_SUFFIX)$(GLUON_$(PROFILE)_FACTORY_EXT) $(GLUON_IMAGEDIR)/factory/$(IMAGE_PREFIX)-$(model)$(GLUON_$(PROFILE)_FACTORY_EXT) && \
- ) \
- \
- $(foreach alias,$(GLUON_$(PROFILE)_MODEL_$(model)_ALIASES), \
- $(if $(GLUON_$(PROFILE)_SYSUPGRADE_EXT), \
- rm -f $(GLUON_IMAGEDIR)/sysupgrade/gluon-*-$(alias)-sysupgrade$(GLUON_$(PROFILE)_SYSUPGRADE_EXT) && \
- ln -s $(IMAGE_PREFIX)-$(model)-sysupgrade$(GLUON_$(PROFILE)_SYSUPGRADE_EXT) $(GLUON_IMAGEDIR)/sysupgrade/$(IMAGE_PREFIX)-$(alias)-sysupgrade$(GLUON_$(PROFILE)_SYSUPGRADE_EXT) && \
- ) \
- $(if $(GLUON_$(PROFILE)_FACTORY_EXT), \
- rm -f $(GLUON_IMAGEDIR)/factory/gluon-*-$(alias)$(GLUON_$(PROFILE)_FACTORY_EXT) && \
- ln -s $(IMAGE_PREFIX)-$(model)$(GLUON_$(PROFILE)_FACTORY_EXT) $(GLUON_IMAGEDIR)/factory/$(IMAGE_PREFIX)-$(alias)$(GLUON_$(PROFILE)_FACTORY_EXT) && \
- ) \
- ) \
- ) :
-
-
-image/%: $(gluon_prepared_stamp)
- +$(GLUONMAKE) image PROFILE="$(patsubst image/%,%,$@)" V=s$(OPENWRT_VERBOSE)
-
-call_image/%: FORCE
- +$(GLUONMAKE) $(patsubst call_image/%,image/%,$@)
-
-images: $(patsubst %,call_image/%,$(PROFILES)) ;
-
-manifest: FORCE
- ( \
- cd $(GLUON_IMAGEDIR)/sysupgrade; \
- $(foreach profile,$(PROFILES), \
- $(if $(GLUON_$(profile)_SYSUPGRADE_EXT), \
- $(foreach model,$(GLUON_$(profile)_MODELS), \
- file="$(IMAGE_PREFIX)-$(model)-sysupgrade$(GLUON_$(profile)_SYSUPGRADE_EXT)"; \
- [ -e "$$file" ] && echo '$(model)' "$(PREPARED_RELEASE)" "$$($(SHA512SUM) "$$file")" "$$file"; \
- \
- $(foreach alias,$(GLUON_$(profile)_MODEL_$(model)_ALIASES), \
- file="$(IMAGE_PREFIX)-$(alias)-sysupgrade$(GLUON_$(profile)_SYSUPGRADE_EXT)"; \
- [ -e "$$file" ] && echo '$(alias)' "$(PREPARED_RELEASE)" "$$($(SHA512SUM) "$$file")" "$$file"; \
- ) \
- ) \
- ) \
- ) : \
- ) >> $(GLUON_BUILDDIR)/$(GLUON_BRANCH).manifest.tmp
-
-.PHONY: all create-key prepare images modules clean gluon-tools manifest
-
-endif
-endif
+.PHONY: FORCE
+.NOTPARALLEL:
diff --git a/README.md b/README.md
index 3ef1f420..e5ac6029 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
Documentation (incomplete at this time, contribute if you can!) may be found at
-http://gluon.readthedocs.org/.
+https://gluon.readthedocs.org/.
If you're new to Gluon and ready to get your feet wet, have a look at the
-[Getting Started Guide](http://gluon.readthedocs.org/en/latest/user/getting_started.html).
+[Getting Started Guide](https://gluon.readthedocs.org/en/latest/user/getting_started.html).
-**Gluon IRC channel: `#gluon` in [hackint](http://hackint.org/)**
+**Gluon IRC channel: `#gluon` in [hackint](https://hackint.org/)**
## Issues & Feature requests
@@ -18,8 +18,8 @@ the future development of Gluon.
## Use a release!
Please refrain from using the `master` branch for anything else but development purposes!
-Use the most recent release instead. You can list all relaseses by running `git branch -a`
-and switch to one by running `git checkout v2016.1.5 && make update`.
+Use the most recent release instead. You can list all releases by running `git tag`
+and switch to one by running `git checkout v2018.1 && make update`.
If you're using the autoupdater, do not autoupdate nodes with anything but releases.
If you upgrade using random master commits the nodes *will break* eventually.
@@ -28,11 +28,11 @@ If you upgrade using random master commits the nodes *will break* eventually.
To subscribe to the list, send a message to:
- gluon-subscribe@luebeck.freifunk.net
+ gluon+subscribe@luebeck.freifunk.net
To remove your address from the list, just send a message to
the address in the `List-Unsubscribe` header of any list
message. If you haven't changed addresses since subscribing,
you can also send a message to:
- gluon-unsubscribe@luebeck.freifunk.net
+ gluon+unsubscribe@luebeck.freifunk.net
diff --git a/contrib/i18n-scan.pl b/contrib/i18n-scan.pl
new file mode 100755
index 00000000..29abefcc
--- /dev/null
+++ b/contrib/i18n-scan.pl
@@ -0,0 +1,128 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Text::Balanced qw(extract_bracketed extract_delimited extract_tagged);
+
+@ARGV >= 1 || die "Usage: $0 \n";
+
+
+my %stringtable;
+
+sub dec_lua_str
+{
+ my $s = shift;
+ $s =~ s/[\s\n]+/ /g;
+ $s =~ s/\\n/\n/g;
+ $s =~ s/\\t/\t/g;
+ $s =~ s/\\(.)/$1/g;
+ $s =~ s/^ //;
+ $s =~ s/ $//;
+ return $s;
+}
+
+sub dec_tpl_str
+{
+ my $s = shift;
+ $s =~ s/-$//;
+ $s =~ s/[\s\n]+/ /g;
+ $s =~ s/^ //;
+ $s =~ s/ $//;
+ $s =~ s/\\/\\\\/g;
+ return $s;
+}
+
+
+if( open F, "find @ARGV -type f '(' -name '*.html' -o -name '*.lua' ')' |" )
+{
+ while( defined( my $file = readline F ) )
+ {
+ chomp $file;
+
+ if( open S, "< $file" )
+ {
+ local $/ = undef;
+ my $raw = ;
+ close S;
+
+
+ my $text = $raw;
+
+ while( $text =~ s/ ^ .*? (?:translate|translatef|_) [\n\s]* \( /(/sgx )
+ {
+ ( my $code, $text ) = extract_bracketed($text, q{('")});
+
+ $code =~ s/\\\n/ /g;
+ $code =~ s/^\([\n\s]*//;
+ $code =~ s/[\n\s]*\)$//;
+
+ my $res = "";
+ my $sub = "";
+
+ if( $code =~ /^['"]/ )
+ {
+ while( defined $sub )
+ {
+ ( $sub, $code ) = extract_delimited($code, q{'"}, q{\s*(?:\.\.\s*)?});
+
+ if( defined $sub && length($sub) > 2 )
+ {
+ $res .= substr $sub, 1, length($sub) - 2;
+ }
+ else
+ {
+ undef $sub;
+ }
+ }
+ }
+ elsif( $code =~ /^(\[=*\[)/ )
+ {
+ my $stag = quotemeta $1;
+ my $etag = $stag;
+ $etag =~ s/\[/]/g;
+
+ ( $res ) = extract_tagged($code, $stag, $etag);
+
+ $res =~ s/^$stag//;
+ $res =~ s/$etag$//;
+ }
+
+ $res = dec_lua_str($res);
+ $stringtable{$res}++ if $res;
+ }
+
+
+ $text = $raw;
+
+ while( $text =~ s/ ^ .*? <% -? [:_] /<%/sgx )
+ {
+ ( my $code, $text ) = extract_tagged($text, '<%', '%>');
+
+ if( defined $code )
+ {
+ $code = dec_tpl_str(substr $code, 2, length($code) - 4);
+ $stringtable{$code}++;
+ }
+ }
+ }
+ }
+
+ close F;
+}
+
+
+if( open C, "| msgcat -" )
+{
+ printf C "msgid \"\"\nmsgstr \"Content-Type: text/plain; charset=UTF-8\"\n\n";
+
+ foreach my $key ( sort keys %stringtable )
+ {
+ if( length $key )
+ {
+ $key =~ s/"/\\"/g;
+ printf C "msgid \"%s\"\nmsgstr \"\"\n\n", $key;
+ }
+ }
+
+ close C;
+}
diff --git a/contrib/lsupgrade.sh b/contrib/lsupgrade.sh
index 7fe11299..eca7a852 100755
--- a/contrib/lsupgrade.sh
+++ b/contrib/lsupgrade.sh
@@ -2,10 +2,11 @@
# Script to list all upgrade scripts in a clear manner
# Limitations:
-# * Does only show scripts of packages whose `files' directory represent the whole image filesystem (which are all Gluon packages)
+# * Does only show scripts of packages whose `files'/`luasrc' directories represent the whole image filesystem (which are all Gluon packages)
-SUFFIX=files/lib/gluon/upgrade
+SUFFIX1=files/lib/gluon/upgrade
+SUFFIX2=luasrc/lib/gluon/upgrade
shopt -s nullglob
@@ -35,8 +36,11 @@ find ./package packages -name Makefile | while read makefile; do
dirname="$(dirname "$dir" | cut -d/ -f 3-)"
package="$(basename "$dir")"
- for file in "${SUFFIX}"/*; do
- echo "${GREEN}$(basename "${file}")${RESET}" "(${BLUE}${repo}${RESET}/${dirname}${dirname:+/}${RED}${package}${RESET}/${SUFFIX})"
+ for file in "${SUFFIX1}"/*; do
+ echo "${GREEN}$(basename "${file}")${RESET}" "(${BLUE}${repo}${RESET}/${dirname}${dirname:+/}${RED}${package}${RESET}/${SUFFIX1})"
+ done
+ for file in "${SUFFIX2}"/*; do
+ echo "${GREEN}$(basename "${file}")${RESET}" "(${BLUE}${repo}${RESET}/${dirname}${dirname:+/}${RED}${package}${RESET}/${SUFFIX2})"
done
popd >/dev/null
done | sort
diff --git a/contrib/sign.sh b/contrib/sign.sh
index 0923b2dc..57b08490 100755
--- a/contrib/sign.sh
+++ b/contrib/sign.sh
@@ -15,7 +15,7 @@ The script may be performed multiple times to the same document
to indicate an approval by multiple developers.
See also
- * edcsautils on https://github.com/tcatm/ecdsautils
+ * ecdsautils on https://github.com/tcatm/ecdsautils
EOHELP
exit 1
diff --git a/docs/conf.py b/docs/conf.py
index fd329bda..d035119b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -47,16 +47,16 @@ master_doc = 'index'
# General information about the project.
project = 'Gluon'
-copyright = '2015-2016, Project Gluon'
+copyright = '2015-2018, Project Gluon'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = '2016.1+'
+version = '2018.1'
# The full version, including alpha/beta/rc tags.
-release = '2016.1+'
+release = '2018.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/dev/basics.rst b/docs/dev/basics.rst
index 7e7d9f58..2f4d1d89 100644
--- a/docs/dev/basics.rst
+++ b/docs/dev/basics.rst
@@ -34,21 +34,7 @@ rerun
`patches`; the resulting branch will be called `patched`, while the commit specified in `modules`
can be refered to by the branch `base`.
-::
-
- make unpatch
-
-sets the repositories to the `base` branch,
-
-::
-
- make patch
-
-re-applies the patches by resetting the `patched` branch to `base` and calling `git am`
-for the patch files. Calling `make` or a similar command after calling `make unpatch`
-is generally not a good idea.
-
-After new patches have been commited on top of the patched branch (or existing commits
+After new patches have been commited on top of the `patched` branch (or existing commits
since the base commit have been edited or removed), the patch directories can be regenerated
using
@@ -61,3 +47,19 @@ and you can try rebasing it onto the new `base` branch yourself and after that c
Always call `make update-patches` after making changes to a module repository as `make update` will overwrite your
commits, making `git reflog` the only way to recover them!
+
+Development Guidelines
+----------------------
+lua should be used instead of sh whenever sensible. The following criteria
+should be considered:
+
+- Is the script doing more than just executing external commands? if so, use lua
+- Is the script parsing/editing json-data? If so, use lua for speed
+- When using sh, use jsonfilter instead of json_* functions for speed
+
+Code formatting may sound like a topic for the pedantic, however it helps if
+the code in the project is formatted in the same way. The following rules
+apply:
+
+- use tabs instead of spaces
+- trailing whitespaces must be eliminated
diff --git a/docs/dev/configmode.rst b/docs/dev/configmode.rst
deleted file mode 100644
index 9a174d4c..00000000
--- a/docs/dev/configmode.rst
+++ /dev/null
@@ -1,92 +0,0 @@
-Config Mode
-===========
-
-As of 2014.4 `gluon-config-mode` consists of several modules.
-
-gluon-config-mode-core
- This modules provides the core functionality for the config mode.
- All modules must depend on it.
-
-gluon-config-mode-hostname
- Provides a hostname field.
-
-gluon-config-mode-autoupdater
- Informs whether the autoupdater is enabled.
-
-gluon-config-mode-mesh-vpn
- Allows toggling of mesh-vpn-fastd and setting a bandwidth limit.
-
-gluon-config-mode-geo-location
- Enables the user to set the geographical location of the node.
-
-gluon-config-mode-contact-info
- Adds a field where the user can provide contact information.
-
-In order to get a config mode close to the one found in 2014.3.x you may add
-these modules to your `site.mk`:
-gluon-config-mode-hostname,
-gluon-config-mode-autoupdater,
-gluon-config-mode-mesh-vpn,
-gluon-config-mode-geo-location,
-gluon-config-mode-contact-info
-
-Writing Config Mode Modules
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Config mode modules are located at `/lib/gluon/config-mode/wizard` and
-`/lib/gluon/config-mode/reboot`. Modules are named like `0000-name.lua` and
-are executed in lexical order. If you take the standard set of modules, the
-order is, for wizard modules:
-
- - 0050-autoupdater-info
- - 0100-hostname
- - 0300-mesh-vpn
- - 0400-geo-location
- - 0500-contact-info
-
-While for reboot modules it is:
-
- - 0100-mesh-vpn
- - 0900-msg-reboot
-
-Wizards
--------
-
-Wizard modules return a UCI section. A simple module capable of changing the
-hostname might look like this::
-
- local cbi = require "luci.cbi"
- 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", "Hostname")
- 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
-
-Reboot page
------------
-
-Reboot modules return a function that will be called when the page is to be
-rendered or nil (i.e. the module is skipped)::
-
- if no_hello_world_today then
- return nil
- else
- return function ()
- luci.template.render_string("Hello World!")
- end
- end
-
diff --git a/docs/dev/hardware.rst b/docs/dev/hardware.rst
index 7f04d93d..aa0462a1 100644
--- a/docs/dev/hardware.rst
+++ b/docs/dev/hardware.rst
@@ -7,78 +7,138 @@ Hardware requirements
---------------------
Having an ath9k (or ath10k) based WLAN adapter is highly recommended,
although other chipsets may also work. VAP (multiple SSID) support
-is a requirement. At the moment, Gluon's scripts can't handle devices
-without WLAN adapters (although such environments may also be interesting,
-e.g. for automated testing in virtual machines).
+is a requirement.
+
.. _hardware-adding-profiles:
Adding profiles
---------------
-The vast majority of devices with ath9k WLAN uses the ar71xx target of OpenWrt.
-If the hardware you want to add support for is also ar71xx, adding a new profile
-is enough.
+The vast majority of devices with ath9k WLAN is based on the ar71xx target of LEDE.
+If the hardware you want to add support for is ar71xx, adding a new profile
+is sufficient.
-Profiles are defined in ``targets/-/profiles.mk``. There are two macros
-used to define which images are generated: ``GluonProfile`` and ``GluonModel``. The following examples
-are taken from ``profiles.mk`` of the ``ar71xx-generic`` target::
+Profiles are defined in ``targets/*`` in a shell-based DSL (so common shell
+command syntax like ``if`` can be used).
- $(eval $(call GluonProfile,TLWR1043))
- $(eval $(call GluonModel,TLWR1043,tl-wr1043nd-v1-squashfs,tp-link-tl-wr1043n-nd-v1))
- $(eval $(call GluonModel,TLWR1043,tl-wr1043nd-v2-squashfs,tp-link-tl-wr1043n-nd-v2))
+The ``device`` command is used to define an image build for a device. It takes
+two or three parameters.
-The ``GluonProfile`` macro takes at least one parameter, the profile name as it is
-defined in the Makefiles of OpenWrt (``openwrt/target/linux///profiles/*``
-and ``openwrt/target/linux//image/Makefile``). If the target you are on doesn't define
-profiles (e.g. on x86), just add a single profile called ``Generic`` or similar.
-
-It may optionally take a second parameter which defines additional packages to include for the profile
-(e.g. ath10k). The additional packages defined in ``openwrt/target/linux///profiles/*``
-aren't used.
-
-The ``GluonModel`` macro takes three parameters: The profile name, the suffix of the image file
-generated by OpenWrt (without the file extension), and the final image name of the Gluon image.
-The final image name must be the same that is returned by the following command.
-
-::
+The first parameter defines the Gluon profile name, which is used to refer to the
+device and is part of the generated image name. The profile name must be same as
+the output of the following command (on the target device), so the autoupdater
+can work::
lua -e 'print(require("platform_info").get_image_name())'
+While porting Gluon to a new device, it might happen that the profile name is un-
+known. Best practise is to generate an image first by using an arbitrary value
+and then executing the lua command on the device and use its output from then on.
-This is just so the autoupdater can work. The command has to be executed _on_ the target (eg. the hardware router with a flashed image). So you'll first have to build an image with a guessed name, and afterwards build a new, correctly named image. On targets which aren't supported by the autoupdater,
-``require("platform_info").get_image_name()`` will just return ``nil`` and the final image name
-may be defined arbitrarily.
+The second parameter defines the name of the image files generated by LEDE. Usually,
+it is also the LEDE profile name; for devices that still use the old image build
+code, a third parameter with the LEDE profile name can be passed. The profile names
+can be found in the image Makefiles in ``lede/target/linux//image/Makefile``.
+
+Examples::
+
+ device tp-link-tl-wr1043n-nd-v1 tl-wr1043nd-v1
+ device alfa-network-hornet-ub hornet-ub HORNETUB
+
+Suffixes and extensions
+'''''''''''''''''''''''
+
+By default, image files are expected to have the extension ``.bin``. In addition,
+the images generated by LEDE have a suffix before the extension that defaults to
+``-squashfs-factory`` and ``-squashfs-sysupgrade``.
+
+This can be changed using the ``factory`` and ``sysupgrade`` commands, either at
+the top of the file to set the defaults for all images, or for a single image. There
+are three forms with 0 to 2 arguments (all work with ``sysupgrade`` as well)::
+
+ factory SUFFIX .EXT
+ factory .EXT
+ factory
+
+When only an extension is given, the default suffix is retained. When no arguments
+are given, this signals that no factory (or sysupgrade) image exists.
+
+Aliases
+'''''''
+
+Sometimes multiple models use the same LEDE images. In this case, the ``alias``
+command can be used to create symlinks and additional entries in the autoupdater
+manifest for the alternative models.
+
+Standalone images
+'''''''''''''''''
+
+On targets without *per-device rootfs* support in LEDE, the commands described above
+can't be used. Instead, ``factory_image`` and ``sysupgrade_image`` are used::
+
+ factory_image PROFILE IMAGE .EXT
+ sysupgrade_image PROFILE IMAGE .EXT
+
+Again, the profile name must match the value printed by the aforementioned Lua
+command. The image name must match the part between the target name and the extension
+as generated by LEDE and is to be omitted when no such part exists.
+
+Packages
+''''''''
+
+The ``packages`` command takes an arbitrary number of arguments. Each argument
+defines an additional package to include in the images in addition to the default
+package sets defined by LEDE. When a package name is prefixed by a minus sign, the
+packages are excluded instead.
+
+The ``packages`` command may be used at the top of a target definition to modify
+the default package list for all images, or just for a single device (when the
+target supports *per-default rootfs*).
+
+
+Configuration
+'''''''''''''
+
+The ``config`` command allows to add arbitary target-specific LEDE configuration
+to be emitted to ``.config``.
+
+Notes
+'''''
On devices with multiple WLAN adapters, care must also be taken that the primary MAC address is
configured correctly. ``/lib/gluon/core/sysconfig/primary_mac`` should contain the MAC address which
can be found on a label on most hardware; if it does not, ``/lib/gluon/upgrade/010-primary-mac``
in ``gluon-core`` might need a fix. (There have also been cases in which the address was incorrect
-even on devices with only one WLAN adapter, in these cases an OpenWrt bug was the cause).
+even on devices with only one WLAN adapter, in these cases a LEDE bug was the cause).
+
Adding support for new hardware targets
---------------------------------------
+
Adding a new target is much more complex than adding a new profile. There are two basic steps
required for adding a new target:
-Adjust packages
-'''''''''''''''
-One package that definitely needs adjustments for every new target added is ``lua-platform-info``. Just
-start with a copy of an existing platform info script, adjust it for the new target, and add the new target
-to the list of supported targets in the package Makefile.
+Package adjustments
+'''''''''''''''''''
-On many targets, Gluon's network setup scripts (mainly in the packages ``gluon-core`` and ``gluon-mesh-batman-adv-core``)
+One package that may need adjustments for new targets is ``libplatforminfo`` (to be found in
+`packages/gluon/libs/libplatforminfo `_).
+If the new platform works fine with the definitions found in ``default.c``, nothing needs to be done. Otherwise,
+create a definition for the added target or subtarget, either by symlinking one of the files in the ``templates``
+directory, or adding a new source file.
+
+On many targets, Gluon's network setup scripts (mainly in the package ``gluon-core``)
won't run correctly without some adjustments, so better double check that everything is fine there (and the files
``primary_mac``, ``lan_ifname`` and ``wan_ifname`` in ``/lib/gluon/core/sysconfig/`` contain sensible values).
-Add support to the build system
-'''''''''''''''''''''''''''''''
-A directory for the new target must be created under ``targets``, and it must be added
-to ``targets/targets.mk``. In the new target directory, the following files must be created:
+Build system support
+''''''''''''''''''''
-* profiles.mk
-* config (optional)
-
-For ``profiles.mk``, see :ref:`hardware-adding-profiles`.
-The file ``config`` can be used to add additional, target-specific options to the OpenWrt config.
+A definition for the new target must be created under ``targets``, and it must be added
+to ``targets/targets.mk``. The ``GluonTarget`` macro takes one to three arguments:
+the target name, the Gluon subtarget name (if the target has subtargets), and the
+LEDE subtarget name (if it differs from the Gluon subtarget). The third argument
+can be used to define multiple Gluon targets with different configuration for the
+same LEDE target, like it is done for the ``ar71xx-tiny`` target.
After this, is should be sufficient to call ``make GLUON_TARGET=`` to build the images for the new target.
diff --git a/docs/dev/i18n.rst b/docs/dev/i18n.rst
deleted file mode 100644
index b51bfdee..00000000
--- a/docs/dev/i18n.rst
+++ /dev/null
@@ -1,88 +0,0 @@
-Internationalization support
-============================
-
-General guidelines
-------------------
-
-* All config mode packages must be fully translatable, with complete English and German texts.
-* All new expert mode packages be fully translatable. English texts are required, German texts recommended.
-* Existing expert mode packages should be made translatable as soon as possible.
-* The "message IDs" (which are the arguments to the ``translate`` function) should be the
- English texts.
-
-i18n support in LuCI
---------------------
-
-Internationalization support can be found in the ``luci.i18n`` package.
-Strings are translated using the ``i18n.translate`` and ``i18n.translatef`` functions
-(``translate`` for static strings, ``translatef`` for printf-like formatted string).
-
-Example from the ``gluon-config-mode-geo-location`` package::
-
- local i18n = require "luci.i18n"
- o = s:option(cbi.Flag, "_location", i18n.translate("Show node on the map"))
-
-Adding translation templates to Gluon packages
-----------------------------------------------
-
-The i18n support is based on the standard gettext system. For each translatable package,
-a translation template with extension ``.pot`` can be created using the ``i18n-scan.pl``
-script from the LuCI repository::
-
- cd package/gluon-config-mode-geo-location
- mkdir i18n
- cd i18n
- ../../../packages/luci/build/i18n-scan.pl ../files > gluon-config-mode-geo-location.pot
-
-The entries in the template can be reordered after the generation if desirable. Lots of standard
-translations like "Cancel" are already available in the LuCI base translation file (see
-``packages/luci/po/templates/base.pot``) and can be removed from the template.
-
-In addition, some additions to the Makefile must be made. Instead of OpenWrt's default package.mk,
-the Gluon version ``$(GLUONDIR)/include/package.mk`` must be used. The i18n files must be installed
-and PKG_CONFIG_DEPENDS must be added::
-
- ...
- include $(GLUONDIR)/include/package.mk
-
- PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
- ...
- define Build/Compile
- $(call GluonBuildI18N,gluon-config-mode-geo-location,i18n)
- endef
-
- define Package/gluon-config-mode-geo-location/install
- ...
- $(call GluonInstallI18N,gluon-config-mode-geo-location,$(1))
- endef
- ...
-
-
-Adding translations
--------------------
-
-A new translation file for a template can be added using the ``msginit`` command::
-
- cd package/gluon-config-mode-geo-location/i18n
- msginit -l de
-
-This will create the file ``de.po`` in which the translations can be added.
-
-The translation file can be updated to a new template version using the ``msgmerge`` command::
-
- msgmerge -U de.po gluon-config-mode-geo-location.pot
-
-After the merge, the translation file should be checked for "fuzzy matched" entries where
-the original English texts have changed. All entries from the the translation file should be
-translated in the ``.po`` file (or removed from it, so the original English texts are displayed
-instead).
-
-Adding support for new languages
---------------------------------
-
-A list of all languages supported by LuCI can be found in the ``packages/luci/luci.mk`` file after
-Gluon's dependencies have been downloaded using ``make update``. Adding translations for these
-languages is straightforward using the ``msginit`` command.
-
-For other languages, support must be added tu LuCI first, which constitutes completely translating
-the ``base.pot``. Please contact the upstream LuCI maintainers if you'd like to do this.
diff --git a/docs/dev/mac_addresses.rst b/docs/dev/mac_addresses.rst
new file mode 100644
index 00000000..b221a302
--- /dev/null
+++ b/docs/dev/mac_addresses.rst
@@ -0,0 +1,18 @@
+MAC addresses
+=============
+
+Many devices don't have enough unique MAC addresses assigned by the vendor
+(in batman-adv, each mesh interface needs an own MAC address that must be unique
+mesh-wide).
+
+Gluon tries to solve this issue by using a hash of the primary MAC address as a
+45 bit MAC address prefix. The resulting 8 addresses are used as follows:
+
+* 0: client0; WAN
+* 1: mesh0
+* 2: ibss0
+* 3: wan_radio0 (private WLAN); batman-adv primary address
+* 4: client1; LAN
+* 5: mesh1
+* 6: ibss1
+* 7: wan_radio1 (private WLAN); mesh VPN
diff --git a/docs/dev/packages.rst b/docs/dev/packages.rst
new file mode 100644
index 00000000..f89e83d4
--- /dev/null
+++ b/docs/dev/packages.rst
@@ -0,0 +1,114 @@
+Package development
+###################
+
+Gluon packages are OpenWrt packages and follow the same rules described at https://openwrt.org/docs/guide-developer/packages.
+
+
+Gluon package makefiles
+=======================
+
+As many packages share the same or a similar structure, Gluon provides a ``package/gluon.mk`` that
+can be included for common definitions. This file replaces OpenWrt's ``$(INCLUDE_DIR)/package.mk``;
+it is usually included as ``include ../gluon.mk`` from Gluon core packages, or as
+``include $(TOPDIR)../package/gluon.mk`` from feeds.
+
+Provided macros
+***************
+
+* *GluonBuildI18N* (arguments: **)
+
+ Converts the *.po* files for all enabled languages from the given source directory to
+ the binary *.lmo* format and stores them in ``$(PKG_BUILD_DIR)/i18n``.
+
+* *GluonInstallI18N*
+
+ Install *.lmo* files from ``$(PKG_BUILD_DIR)/i18n`` to ``/lib/gluon/web/i18n`` in the
+ package install directory.
+
+* *GluonSrcDiet* (arguments: **, **)
+
+ Copies a directory tree, processing all files in it using *LuaSrcDiet*. The directory
+ tree should only contain Lua files.
+
+* *GluonCheckSite* (arguments: **)
+
+ Intended to be used in a package postinst script. It will use the passed Lua
+ snippet to verify package-specific site configuration.
+
+* *BuildPackageGluon* (replaces *BuildPackage*)
+
+ Extends the *Package/* definition with common defaults, sets the package
+ install script to the common *Gluon/Build/Install*, and automatically creates
+ a postinst script using *GluonCheckSite* if a ``check_site.lua`` is found in the
+ package directory.
+
+Default build steps
+*******************
+
+These defaults greatly reduce the boilerplate in each package, but they can also
+be confusing because of the many implicit behaviors depending on files in the
+package directory. If any part of *Gluon/Build/Compile* or *Gluon/Build/Install*
+does not work as intended for a package, the compile and install steps can
+always be replaced or extended.
+
+*Build/Compile* is set to *Gluon/Build/Compile* by default, which will
+
+* run OpenWrt standard default commands (*Build/Compile/Default*) if a ``src/Makefile``
+ or ``src/CMakeLists.txt`` is found
+* run *GluonSrcDiet* on all files in the ``luasrc`` directory
+* run *GluonBuildI18N* if a ``i18n`` directory is found
+
+*Package/* defaults to *Gluon/Build/Install* for packages defined using
+*BuildPackageGluon*, which will
+
+* copy all files from ``$(PKG_INSTALL_DIR)`` into the package if ``$(PKG_INSTALL)`` is 1
+* copy all files from ``files`` into the package
+* copy all Lua files built from ``luasrc`` into the package
+* installs ``$(PKG_BUILD_DIR)/respondd.so`` to ``/usr/lib/respondd/$(PKG_NAME).so`` if ``src/respondd.c`` exists
+* installs compiled i18n *.lmo* files
+
+Feature flags
+=============
+
+Feature flags provide a convenient way to define package selections without
+making it necessary to list each package explicitly.
+
+The main feature flag definition file is ``package/features``, but each package
+feed can provide additional defintions in a file called ``features`` at the root
+of the feed repository.
+
+Each flag *$flag* without any explicit definition will simply include the package
+with the name *gluon-$flag* by default. The feature definition file can modify
+the package selection in two ways:
+
+* The *nodefault* function suppresses default of including the *gluon-$flag*
+ package
+* The *packages* function adds a list of packages (or removes, when package
+ names are prepended with minus signs) when a given logical expression
+ is satisfied
+
+Example::
+
+ nodefault 'web-wizard'
+
+ packages 'web-wizard' \
+ 'gluon-config-mode-hostname' \
+ 'gluon-config-mode-geo-location' \
+ 'gluon-config-mode-contact-info'
+
+ packages 'web-wizard & (mesh-vpn-fastd | mesh-vpn-tunneldigger)' \
+ 'gluon-config-mode-mesh-vpn'
+
+This will
+
+* disable the inclusion of a (non-existent) package called *gluon-web-wizard*
+* enable three config mode packages when the *web-wizard* feature is enabled
+* enable *gluon-config-mode-mesh-vpn* when both *web-wizard* and one
+ of *mesh-vpn-fastd* and *mesh-vpn-tunneldigger* are enabled
+
+Supported syntax elements of logical expressions are:
+
+* \& (and)
+* \| (or)
+* \! (not)
+* parentheses
diff --git a/docs/dev/site_library.rst b/docs/dev/site_library.rst
new file mode 100644
index 00000000..991e3cb5
--- /dev/null
+++ b/docs/dev/site_library.rst
@@ -0,0 +1,36 @@
+gluon.site library
+==================
+
+The *gluon.site* library allows convenient access to the site configuration
+from Lua scripts. Example:
+
+.. code-block:: lua
+
+ local site = require 'gluon.site'
+ print(site.wifi24.ap.ssid())
+
+The *site* object in this example does not directly represent the *site.conf* data structure;
+instead, it is wrapped in a way that makes it more convenient to access deeply nested elements.
+To access the the underlying values, they must be unwrapped using the function call notation
+(the ``()`` after ``site.wifi24.ap.ssid`` in the example).
+
+The wrapper objects have two advantages over simple Lua tables:
+
+* Accessing non-existing values is never an error: ``site.wifi24.ap.ssid()`` will simply
+ return *nil* if ``site.wifi24`` or ``site.wifi24.ap`` do not exist
+* Default values: A default value can be passed to the unwrapping function call:
+
+ .. code-block:: lua
+
+ print(site.wifi24.ap.ssid('Default'))
+
+ will return *'Default'* instead of *nil* when the value is unset.
+
+ Note that *nil* values and unset values are equivalent in Lua.
+
+A simple way to access the whole site configuration as a simple table
+is to unwrap the top-level site object:
+
+.. code-block:: lua
+
+ local site_table = site()
diff --git a/docs/dev/upgrade.rst b/docs/dev/upgrade.rst
index 28377235..2c18604d 100644
--- a/docs/dev/upgrade.rst
+++ b/docs/dev/upgrade.rst
@@ -6,7 +6,7 @@ Basics
After each sysupgrade (including the initial installation), Gluon will execute all scripts
under ``/lib/gluon/upgrade``. These scripts' filenames usually begin with a 3-digit number
-specifying the order of execution.
+specifying the order of execution. Note that the script files need to be executable.
To get an overview of the ordering of all scripts of all packages, the helper script ``contrib/lsupgrade.sh``
in the Gluon repository can be used, which will print all upgrade scripts' filenames and directories. If executed
@@ -16,17 +16,15 @@ Best practices
--------------
* Most upgrade scripts are written in Lua. This allows using lots of helper functions provided
- by LuCi and Gluon, e.g. to access the site configuration or edit UCI configuration files.
+ by Gluon, e.g. to access the site configuration or edit UCI configuration files.
* Whenever possible, scripts shouldn't check if they are running for the first time, but just edit configuration
- files to achive a valid configuration (without overwriting configuration changes made by the user where desirable).
+ files to achieve a valid configuration (without overwriting configuration changes made by the user where desirable).
This allows using the same code to create the initial configuration and upgrade configurations on upgrades.
* If it is unavoidable to run different code during the initial installation, the ``sysconfig.gluon_version`` variable
can be checked. This variable is ``nil`` during the initial installation and contains the previously install Gluon
- version otherwise. The package ``gluon-legacy`` (which is responsible for upgrades from the old firmwares of
- Hamburg/Kiel/Lübeck) uses the special value ``legacy``; other packages should handle this value just as any other
- string.
+ version otherwise.
Script ordering
---------------
diff --git a/docs/dev/wan.rst b/docs/dev/wan.rst
index a430df31..9742bf2b 100644
--- a/docs/dev/wan.rst
+++ b/docs/dev/wan.rst
@@ -46,4 +46,4 @@ so the WAN DNS servers aren't leaked to the primary DNS daemon.
*libpacketmark* is used to make the secondary DNS daemon send its requests over the WAN interface.
The package ``gluon-mesh-vpn-fastd`` provides an iptables rule which will redirect all DNS requests from processes running
-with the primary group ``gluon-fastd`` to ``127.0.0.1:54``, thus making fastd use the secondary DNS daemon.
+with the primary group ``gluon-mesh-vpn`` to ``127.0.0.1:54``, thus making fastd use the secondary DNS daemon.
diff --git a/docs/dev/web/config-mode.rst b/docs/dev/web/config-mode.rst
new file mode 100644
index 00000000..9c0c2430
--- /dev/null
+++ b/docs/dev/web/config-mode.rst
@@ -0,0 +1,82 @@
+Config Mode
+===========
+
+The `Config Mode` consists of several modules that provide a range of different
+condiguration options:
+
+gluon-config-mode-core
+ This modules provides the core functionality for the config mode.
+ All modules must depend on it.
+
+gluon-config-mode-hostname
+ Provides a hostname field.
+
+gluon-config-mode-autoupdater
+ Informs whether the autoupdater is enabled.
+
+gluon-config-mode-mesh-vpn
+ Allows toggling of mesh-vpn-fastd and setting a bandwidth limit.
+
+gluon-config-mode-geo-location
+ Enables the user to set the geographical location of the node.
+
+gluon-config-mode-contact-info
+ Adds a field where the user can provide contact information.
+
+
+Writing Config Mode modules
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Config mode modules are located at ``/lib/gluon/config-mode/wizard`` and
+``/lib/gluon/config-mode/reboot``. Modules are named like ``0000-name.lua`` and
+are executed in lexical order. In the standard package set, the
+order is, for wizard modules:
+
+ - 0050-autoupdater-info
+ - 0100-hostname
+ - 0300-mesh-vpn
+ - 0400-geo-location
+ - 0500-contact-info
+
+The reboot module order is:
+
+ - 0100-mesh-vpn
+ - 0900-msg-reboot
+
+All modules are run in the gluon-web model context and have access to the same
+variables as "full" gluon-web modules.
+
+Wizards
+-------
+
+Wizard modules must return a function that is provided with the wizard form and an
+UCI cursor. The function can create configuration sections in the form:
+
+.. code-block:: lua
+
+ return function(form, uci)
+ local s = form:section(Section)
+ local o = s:option(Value, "hostname", "Hostname")
+ o.default = uci:get_first("system", "system", "hostname")
+ o.datatype = "hostname"
+
+ function o:write(data)
+ uci:set("system", uci:get_first("system", "system"), "hostname", data)
+ end
+
+ return {'system'}
+ end
+
+The function may return a table of UCI packages to commit after the individual
+fields' `write` methods have been executed. This is done to avoid committing the
+packages repeatedly when multiple wizard modules modify the same package.
+
+Reboot page
+-----------
+
+Reboot modules are simply executed when the reboot page is
+rendered:
+
+.. code-block:: lua
+
+ renderer.render_string("Hello World!")
diff --git a/docs/dev/web/controller.rst b/docs/dev/web/controller.rst
new file mode 100644
index 00000000..b3adb288
--- /dev/null
+++ b/docs/dev/web/controller.rst
@@ -0,0 +1,123 @@
+Controllers
+===========
+
+Controllers live in ``/lib/gluon/web/controller``. They define which pages ("routes")
+exist under the ``/cgi-bin/gluon`` path, and what code is run when these pages are requested.
+
+Controller scripts usually start with a *package* declaration, followed by calls
+to the *entry* function, which each define one route:
+
+.. code-block:: lua
+
+ package 'gluon-web-admin'
+
+ entry({"admin"}, alias("admin", "info"), _("Advanced settings"), 10)
+ entry({"admin", "info"}, template("admin/info"), _("Information"), 1)
+
+*package* defines the translation namespace for the titles of the defined
+pages as well as the referenced views and models. The entry function expects 4
+arguments:
+
+ - `path`: Components of the path to define a route for.
+
+ The above example defines routes for the paths ``admin`` and ``admin/info``.
+
+ - `target`: Dispatcher for the route. See the following section for details.
+ - `title`: Page title (also used in navigation). The underscore function is used
+ to mark the strings as translatable for ``i18n-scan.pl``.
+
+ - `order`: Sort index in navigation (defaults to 100)
+
+Navigation indexes are automatically generated for each path level. Pages can be
+hidden from the navigation by setting the `hidden` property of the node object
+returned by `entry`:
+
+.. code-block:: lua
+
+ entry({"hidden"}, alias("foo"), _("I'm hidden!")).hidden = true
+
+
+Dispatchers
+-----------
+
+ - *alias* (*path*, ...): Redirects to a different page. The path components are
+ passed as individual arguments.
+ - *call* (*func*, ...): Runs a Lua function for custom request handling. The given
+ function is called with the HTTP object and the template renderer as first
+ two arguments, followed by all additional arguments passed to `call`.
+ - *template* (*view*): Renders the given view. See :doc:`view`.
+ - *model* (*name*): Displays and evaluates a form as defined by the given model. See the
+ :doc:`model` page for an explanation of gluon-web models.
+
+
+.. _web-controller-http:
+
+The HTTP object
+---------------
+
+The HTTP object provides information about the HTTP requests and allows to add
+data to the reply. Using it directly is rarely necessary when gluon-web
+models and views are used.
+
+Useful functions:
+
+ - *getenv* (*key*): Returns a value from the CGI environment passed by the webserver.
+ - *formvalue* (*key*): Returns a value passed in a query string or in the content
+ of a POST request. If multiple values with the same name have been passed, only
+ the first is returned.
+ - *formvaluetable* (*key*): Similar to *formvalue*, but returns a table of all
+ values for the given key.
+ - *status* (*code*, *message*): Writes the HTTP status to the reply. Has no effect
+ if a status has already been sent or non-header data has been written.
+ - *header* (*key*, *value*): Adds an HTTP header to the reply to be sent to to
+ the client. Has no effect when non-header data has already been written.
+ - *prepare_content* (*mime*): Sets the *Content-Type* header to the given MIME
+ type, potentially setting additional headers or modifying the MIME type to
+ accommodate browser quirks
+ - *write* (*data*, ...): Sends the given data to the client. If headers have not
+ been sent, it will be done before the data is written.
+
+
+HTTP functions are called in method syntax, for example:
+
+.. code-block:: lua
+
+ http:write('Output!')
+
+
+.. _web-controller-template-renderer:
+
+The template renderer
+---------------------
+
+The template renderer allows to render templates (views). The most useful functions
+are:
+
+ - *render* (*view*, *scope*, *pkg*): Renders the given view, optionally passing a table
+ with additional variables to make available in the template. The passed package
+ defines the translation namespace.
+ - *render_string* (*str*, *scope*, *pkg*): Same as *render*, but the template is passed
+ directly instead of being loaded from the view directory.
+
+The renderer functions are called in property syntax, for example:
+
+.. code-block:: lua
+
+ renderer.render('layout')
+
+
+Differences from LuCI
+---------------------
+
+ - Controllers must not use the *module* function to define a Lua module (*gluon-web*
+ will set up a proper environment for each controller itself)
+ - Entries are defined at top level, not inside an *index* function
+ - The *alias* dispatcher triggers an HTTP redirect instead of directly running
+ the dispatcher of the aliased route.
+ - The *call* dispatcher is passed a function instead of a string with a function
+ name.
+ - The *cbi* dispatcher of LuCI has been renamed to *model*.
+ - The HTTP POST handler support the multipart/form-data encoding only, so
+ ``enctype="multipart/form-data"`` must be included in all *
"
"
Um zu dieser Konfigurationsseite zurückzugelangen, drücke im normalen "
"Betrieb für drei Sekunden den Reset-Button. Das Gerät wird dann im Config "
"Mode neustarten.
"
"
Viel Spaß mit deinem Knoten und der Erkundung von Freifunk!
"
+
+msgid "gluon-config-mode:altitude-label"
+msgstr "Höhe"
+
+msgid "gluon-config-mode:altitude-help"
+msgstr ""
+"Die Höhenangabe ist optional und sollte nur gesetzt werden, wenn ein "
+"exakter Wert bekannt ist."
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-hostname/i18n/
+msgid "gluon-config-mode:hostname-help"
+msgstr ""
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-geo-location/i18n/
+msgid "gluon-config-mode:geo-location-help"
+msgstr ""
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-contact-info/i18n/
+msgid "gluon-config-mode:contact-help"
+msgstr ""
+
+msgid "gluon-config-mode:contact-note"
+msgstr ""
diff --git a/docs/site-example/i18n/en.po b/docs/site-example/i18n/en.po
index 13273230..c31043a2 100644
--- a/docs/site-example/i18n/en.po
+++ b/docs/site-example/i18n/en.po
@@ -12,31 +12,71 @@ msgstr ""
msgid "gluon-config-mode:welcome"
msgstr ""
-"Welcome to the setup wizard of your new Freifunk Duckburg node. "
+"Welcome to the setup wizard of your new Freifunk Alpha Centauri node. "
"Please fill out the following form and submit it."
+msgid "gluon-config-mode:domain"
+msgstr "Domain"
+
+msgid "gluon-config-mode:domain-select"
+msgstr ""
+"Here you have the possibility of selecting the mesh domain in which your node "
+"is placed. Please keep in mind that your router only connects with the nodes "
+"of the selected domain"
+
msgid "gluon-config-mode:pubkey"
msgstr ""
"
This is your Freifunk node's public key. The node won't be able to "
-"connect to the mesh VPN until the key has been registered on the Freifunk "
-"Duckburg servers. "
-"To register, send the key together with your node's name (<%=hostname%>) to "
-"keys@entenhausen.freifunk.net."
+"connect to the mesh VPN until the key has been registered on the Freifunk servers. "
+"To register, send the key together with your node's name (<%=pcdata(hostname)%>) to "
+"&"
+"body=<%= urlencode('# ' .. hostname .. '\n' .. pubkey) %>\">keys@alpha-centauri.freifunk.net."
"
You have selected not to use the mesh VPN. "
+"Your node will only be able to connect to the Freifunk network if other nodes in reach "
+"already have a connection.
"
msgid "gluon-config-mode:reboot"
msgstr ""
-"
The node is currently rebooting and will try to connect to other "
-"nearby Freifunk nodes after that. "
-"For more information on the Freifunk Duckburg community, have a look at "
-"our homepage.
"
+"
Your node <%= pcdata(hostname) %> is currently rebooting and will "
+"try to connect to other nearby Freifunk nodes after that. For more "
+"information about the Freifunk community on Alpha Centauri, have a look at "
+"our homepage.
"
"
To get back to this configuration interface, press the reset button for "
"3 seconds during normal operation. The device will then reboot into config "
"mode.
"
"
Have fun with your node and exploring of the Freifunk network!
"
+
+msgid "gluon-config-mode:altitude-label"
+msgstr "Altitude"
+
+msgid "gluon-config-mode:altitude-help"
+msgstr ""
+"Specifying the altitude is optional and should only be done if a proper "
+"value is known."
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-hostname/i18n/
+msgid "gluon-config-mode:hostname-help"
+msgstr ""
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-geo-location/i18n/
+msgid "gluon-config-mode:geo-location-help"
+msgstr ""
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-contact-info/i18n/
+msgid "gluon-config-mode:contact-help"
+msgstr ""
+
+msgid "gluon-config-mode:contact-note"
+msgstr ""
diff --git a/docs/site-example/i18n/fr.po b/docs/site-example/i18n/fr.po
index 30e21c50..5a591ee5 100644
--- a/docs/site-example/i18n/fr.po
+++ b/docs/site-example/i18n/fr.po
@@ -14,7 +14,13 @@ msgid "gluon-config-mode:welcome"
msgstr ""
"Bienvenue dans l'assistant de configuration pour votre nouveau nœud "
"Freifunk. Remplissez le formulaire suivant en fonction de vos besoins "
-"et enregistrez le"
+"et enregistrez le"
+
+msgid "gluon-config-mode:domain"
+msgstr "Domaine"
+
+msgid "gluon-config-mode:domain-select"
+msgstr ""
msgid "gluon-config-mode:pubkey"
msgstr ""
@@ -22,22 +28,55 @@ msgstr ""
"entrée sur les serveurs de votre groupe de Freifunk votre nœud pourra se connecter "
"sur les serveur Mesh-VPN de votre groupe Freifunk. Veuillez envoyer la clé avec le "
"nom de votre nœud "
-"(<%=hostname%>) à "
-"keys@entenhausen.freifunk.net."
+"(<%=pcdata(hostname)%>) à "
+"&"
+"body=<%= urlencode('# ' .. hostname .. '\n' .. pubkey) %>\">keys@alpha-centauri.freifunk.net."
""
"
Vous avez choisi de ne pas utiliser "
+"le réseau VPN. Votre nœud ne pourra se connecter au réseau Freifunk que si "
+"d'autres nœuds à portée ont déjà une connexion.
"
+
msgid "gluon-config-mode:reboot"
msgstr ""
-"
Votre nœud es en train de redémarrer et va ensuite éssayer de se connecter "
-"avec les autres nœuds du réseau Freifunk "
+"
Votre nœud <%= pcdata(hostname) %> es en train de redémarrer et "
+"va ensuite éssayer de se connecter avec les autres nœuds du réseau Freifunk "
"Vous pourrez trouver plus d'informations sur votre groupe Freifunk sur la page "
-" de ton groupe .
Pour retrouver cette page de configuration veuillier appuyez pendant le "
"fonctionement normal pendant 3 Secondes sur le bouton reset. L'appareil va ensuite "
"redémarer en mode configuration.
"
"
Profitez votre de nœud et amusez vous à découvrir le réseau Freifunk!
"
+
+msgid "gluon-config-mode:altitude-label"
+msgstr "Hauteur"
+
+msgid "gluon-config-mode:altitude-help"
+msgstr ""
+"La altitude est optionelle et ne devrait que être ajoutée si la valeur "
+"exacte est connue."
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-hostname/i18n/
+msgid "gluon-config-mode:hostname-help"
+msgstr ""
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-geo-location/i18n/
+msgid "gluon-config-mode:geo-location-help"
+msgstr ""
+
+# Leave empty to use the default text, which can be found in:
+# package/gluon-config-mode-contact-info/i18n/
+msgid "gluon-config-mode:contact-help"
+msgstr ""
+
+msgid "gluon-config-mode:contact-note"
+msgstr ""
diff --git a/docs/site-example/i18n/gluon-site.pot b/docs/site-example/i18n/gluon-site.pot
index 670de410..032c6931 100644
--- a/docs/site-example/i18n/gluon-site.pot
+++ b/docs/site-example/i18n/gluon-site.pot
@@ -4,8 +4,35 @@ msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "gluon-config-mode:welcome"
msgstr ""
+msgid "gluon-config-mode:domain"
+msgstr ""
+
+msgid "gluon-config-mode:domain-select"
+msgstr ""
+
msgid "gluon-config-mode:pubkey"
msgstr ""
+msgid "gluon-config-mode:novpn"
+msgstr ""
+
msgid "gluon-config-mode:reboot"
msgstr ""
+
+msgid "gluon-config-mode:altitude-label"
+msgstr ""
+
+msgid "gluon-config-mode:altitude-help"
+msgstr ""
+
+msgid "gluon-config-mode:contact-help"
+msgstr ""
+
+msgid "gluon-config-mode:contact-note"
+msgstr ""
+
+msgid "gluon-config-mode:hostname-help"
+msgstr ""
+
+msgid "gluon-config-mode:geo-location-help"
+msgstr ""
diff --git a/docs/site-example/site.conf b/docs/site-example/site.conf
index 45719b94..6b52d53a 100644
--- a/docs/site-example/site.conf
+++ b/docs/site-example/site.conf
@@ -1,21 +1,28 @@
--- This is an example site configuration for Gluon v2016.1+
+-- This is an example site configuration for Gluon v2018.1
--
-- Take a look at the documentation located at
-- http://gluon.readthedocs.org/ for details.
--
--- This configuration will not work as it. You're required to make
+-- This configuration will not work as is. You're required to make
-- community specific changes to it!
{
-- Used for generated hostnames, e.g. freifunk-abcdef123456. (optional)
-- hostname_prefix = 'freifunk-',
-- Name of the community.
- site_name = 'Freifunk Entenhausen',
+ site_name = 'Freifunk Alpha Centauri',
-- Shorthand of the community.
site_code = 'ffxx',
- -- Prefixes used within the mesh. Both are required.
+ -- 32 bytes of random data, encoded in hexadecimal
+ -- This data must be unique among all sites and domains!
+ -- Can be generated using: echo $(hexdump -v -n 32 -e '1/1 "%02x"' `.
@@ -54,23 +67,27 @@ opkg \: optional
There are two optional fields in the ``opkg`` section:
- - ``openwrt`` overrides the default OpenWrt repository URL
+ - ``lede`` overrides the default LEDE repository URL. The default URL would
+ correspond to ``http://downloads.lede-project.org/snapshots/packages/%A``
+ and usually doesn't need to be changed when nodes are expected to have IPv6
+ internet connectivity.
- ``extra`` specifies a table of additional repositories (with arbitrary keys)
::
opkg = {
- openwrt = 'http://opkg.services.ffeh/openwrt/%n/%v/%S/packages',
+ lede = 'http://opkg.services.ffac/lede/snapshots/packages/%A',
extra = {
- modules = 'http://opkg.services.ffeh/modules/gluon-%GS-%GR/%S',
+ gluon = 'http://opkg.services.ffac/modules/gluon-%GS-%GR/%S',
},
}
There are various patterns which can be used in the URLs:
- - ``%n`` is replaced by the OpenWrt version codename (e.g. "chaos_calmer")
- - ``%v`` is replaced by the OpenWrt version number (e.g. "15.05")
- - ``%S`` is replaced by the target architecture (e.g. "ar71xx/generic")
+ - ``%n`` is replaced by the LEDE version codename
+ - ``%v`` is replaced by the LEDE version number (e.g. "17.01")
+ - ``%S`` is replaced by the target board (e.g. "ar71xx/generic")
+ - ``%A`` is replaced by the target architecture (e.g. "mips_24kc")
- ``%GS`` is replaced by the Gluon site code (as specified in ``site.conf``)
- ``%GV`` is replaced by the Gluon version
- ``%GR`` is replaced by the Gluon release (as specified in ``site.mk``)
@@ -81,13 +98,13 @@ regdom \: optional
regdom = 'DE'
- Setting ``regdom`` in mandatory if ``wifi24`` or ``wifi5`` is defined.
+ Setting ``regdom`` is mandatory if ``wifi24`` or ``wifi5`` is defined.
wifi24 \: optional
WLAN configuration for 2.4 GHz devices.
``channel`` must be set to a valid wireless channel for your radio.
- There are currently three interface types available. You many choose to
+ There are currently three interface types available. You may choose to
configure any subset of them:
- ``ap`` creates a master interface where clients may connect
@@ -96,27 +113,39 @@ wifi24 \: optional
Each interface may be disabled by setting ``disabled`` to ``true``.
This will only affect new installations.
- Upgrades will not changed the disabled state.
+ Upgrades will not change the disabled state.
+
+ Additionally it is possible to configure the ``supported_rates`` and ``basic_rate``
+ of each radio. Both are optional, by default hostapd/driver dictate the rates.
+ If ``supported_rates`` is set, ``basic_rate`` is required, because ``basic_rate``
+ has to be a subset of ``supported_rates``.
+ The example below disables 802.11b rates.
``ap`` requires a single parameter, a string, named ``ssid`` which sets the
- interface's ESSID.
+ interface's ESSID. This is the WiFi the clients connect to.
- ``mesh`` requires a single parameter, a string, named ``id`` which sets the mesh id.
+ ``mesh`` requires a single parameter, a string, named ``id`` which sets the
+ mesh id, also visible as an open WiFi in some network managers. Usually you
+ don't want users to connect to this mesh-SSID, so use a cryptic id that no
+ one will accidentally mistake for the client WiFi.
``ibss`` requires two parametersr: ``ssid`` (a string) and ``bssid`` (a MAC).
An optional parameter ``vlan`` (integer) is supported.
Both ``mesh`` and ``ibss`` accept an optional ``mcast_rate`` (kbit/s) parameter for
- setting the default multicast datarate.
+ setting the multicast bitrate. Increasing the default value of 1000 to something
+ like 12000 is recommended.
::
wifi24 = {
channel = 11,
+ supported_rates = {6000, 9000, 12000, 18000, 24000, 36000, 48000, 54000},
+ basic_rate = {6000, 9000, 18000, 36000, 54000},
ap = {
- ssid = 'entenhausen.freifunk.net',
+ ssid = 'alpha-centauri.freifunk.net',
},
mesh = {
- id = 'entenhausen-mesh',
+ id = 'ueH3uXjdp',
mcast_rate = 12000,
},
ibss = {
@@ -134,81 +163,161 @@ next_node \: package
::
next_node = {
+ name = { 'nextnode.location.community.example.org', 'nextnode', 'nn' },
ip4 = '10.23.42.1',
ip6 = 'fdca:ffee:babe:1::1',
- mac = 'ca:ff:ee:ba:be:00'
+ mac = '16:41:95:40:f7:dc'
}
-mesh \: optional
- Options specific to routing protocols.
+ All values of this section are optional. If the IPv4 or IPv6 address is
+ omitted, there will be no IPv4 or IPv6 anycast address. The MAC address
+ defaults to ``16:41:95:40:f7:dc``; this value usually doesn't need to be
+ changed, but it can be adjusted to match existing deployments that use a
+ different value.
- At the moment, only the ``batman_adv`` routing protocol has such options:
+ When the nodes' next-node address is used as a DNS resolver by clients
+ (by passing it via DHCP or router advertisements), it may be useful to
+ allow resolving a next-node hostname without referring to an upstream DNS
+ server (e.g. to allow reaching the node using such a hostname via HTTP or SSH
+ in isolated mesh segments). This is possible by providing one or more names
+ in the ``name`` field.
- The optional value ``gw_sel_class`` sets the gateway selection class. The default
- class 20 is based on the link quality (TQ) only, class 1 is calculated from
- both the TQ and the announced bandwidth.
+.. _user-site-mesh:
+
+mesh
+ Configuration of general mesh functionality.
+
+ To avoid inter-mesh links, Gluon can encapsulate the mesh protocol in VXLAN
+ for Mesh-on-LAN/WAN. It is recommended to set *mesh.vxlan* to ``true`` to
+ enable VXLAN in new setups. Setting it to ``false`` disables this
+ encapsulation to allow meshing with other nodes that don't support VXLAN
+ (Gluon 2017.1.x and older). In multi-domain setups, *mesh.vxlan* is optional
+ and defaults to ``true``.
+
+ Gluon generally segments layer-2 meshes so that each node becomes IGMP/MLD
+ querier for its own local clients. This is necessary for reliable multicast
+ snooping. The segmentation is realized by preventing IGMP/MLD queries from
+ passing through the mesh.
+
+ By default, not only queries are filtered, but also membership report and
+ leave packets, as they add to the background noise of the mesh. As a
+ consequence, snooping switches outside the mesh that are connected to a
+ Gluon node need to be configured to forward all multicast traffic towards
+ the mesh; this is usually not a problem, as such setups are unusual. If
+ you run a special-purpose mesh that requires membership reports to be
+ working, this filtering can be disabled by setting the
+ optional *filter_membership_reports* value to ``false``.
+
+ In addition, options specific to the batman-adv routing protocol can be set
+ in the *batman_adv* section:
+
+ The optional value *gw_sel_class* sets the gateway selection class. The
+ default is class 20, which is based on the link quality (TQ) only; class 1
+ is calculated from both the TQ and the announced bandwidth.
::
- mesh = {
- batman_adv = {
- gw_sel_class = 1,
- },
- }
+ mesh = {
+ vxlan = true,
+ filter_membership_reports = false,
+ batman_adv = {
+ gw_sel_class = 1,
+ },
+ }
-fastd_mesh_vpn
- Remote server setup for the fastd-based mesh VPN.
+mesh_vpn
+ Remote server setup for the mesh VPN.
- The `enabled` option can be set to true to enable the VPN by default.
+ The `enabled` option can be set to true to enable the VPN by default. `mtu`
+ defines the MTU of the VPN interface, determining a proper MTU value is described
+ in the :ref:`FAQ `.
+
+ By default the public key of a node's VPN daemon is not added to announced respondd
+ data; this prevents malicious ISPs from correlating VPN sessions with specific mesh
+ nodes via public respondd data. If this is of no concern in your threat model,
+ this behaviour can be disabled (and thus announcing the public key be enabled) by
+ setting `pubkey_privacy` to `false`. At the moment, this option only affects fastd.
+
+ The `fastd` section configures settings specific to the *fastd* VPN
+ implementation.
If `configurable` is set to `false` or unset, the method list will be replaced on updates
with the list from the site configuration. Setting `configurable` to `true` will allow the user to
add the method ``null`` to the beginning of the method list or remove ``null`` from it,
and make this change survive updates. Setting `configurable` is necessary for the
- package `gluon-luci-mesh-vpn-fastd`, which adds a UI for this configuration.
+ package `gluon-web-mesh-vpn-fastd`, which adds a UI for this configuration.
In any case, the ``null`` method should always be the first method in the list
if it is supported at all. You should only set `configurable` to `true` if the
configured peers support both the ``null`` method and methods with encryption.
+
+ You can set syslog_level from verbose (default) to warn to reduce syslog output.
+
+ The `tunneldigger` section is used to define the *tunneldigger* broker list.
+
+ **Note:** It doesn't make sense to include both `fastd` and `tunneldigger`
+ sections in the same configuration file, as only one of the packages *gluon-mesh-vpn-fastd*
+ and *gluon-mesh-vpn-tunneldigger* should be installed with the current
+ implementation.
+
::
- fastd_mesh_vpn = {
- methods = {'salsa2012+umac'},
- -- enabled = true,
- -- configurable = true,
- mtu = 1280,
- groups = {
- backbone = {
- -- Limit number of connected peers from this group
- limit = 1,
- peers = {
- peer1 = {
- key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
- -- Having multiple domains prevents SPOF in freifunk.net
- remotes = {
- 'ipv4 "vpn1.entenhausen.freifunk.net" port 10000',
- 'ipv4 "vpn1.entenhausener-freifunk.de" port 10000',
+ mesh_vpn = {
+ -- enabled = true,
+ mtu = 1312,
+ -- pubkey_privacy = true,
+
+ fastd = {
+ methods = {'salsa2012+umac'},
+ -- configurable = true,
+ -- syslog_level = 'warn',
+ groups = {
+ backbone = {
+ -- Limit number of connected peers from this group
+ limit = 1,
+ peers = {
+ peer1 = {
+ key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ -- Having multiple domains prevents SPOF in freifunk.net
+ remotes = {
+ 'ipv4 "vpn1.alpha-centauri.freifunk.net" port 10000',
+ 'ipv4 "vpn1.alpha-centauri-freifunk.de" port 10000',
+ },
+ },
+ peer2 = {
+ key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ -- You can also omit the ipv4 to allow both connection via ipv4 and ipv6
+ remotes = {'"vpn2.alpha-centauri.freifunk.net" port 10000'},
+ },
+ peer3 = {
+ key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ -- In addition to domains you can also add ip addresses, which provides
+ -- resilience in case of dns outages
+ remotes = {
+ '"vpn3.alpha-centauri.freifunk.net" port 10000',
+ '[2001:db8::3:1]:10000',
+ '192.0.2.3:10000',
+ },
},
},
- peer2 = {
- key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
- -- You can also omit the ipv4 to allow both connection via ipv4 and ipv6
- remotes = {'"vpn2.entenhausen.freifunk.net" port 10000'},
- },
+ -- Optional: nested peer groups
+ -- groups = {
+ -- lowend_backbone = {
+ -- limit = 1,
+ -- peers = ...
+ -- },
+ -- },
},
- -- Optional: nested peer groups
- -- groups = {
- -- lowend_backbone = {
- -- limit = 1,
- -- peers = ...
- -- },
+ -- Optional: additional peer groups, possibly with other limits
+ -- peertopeer = {
+ -- limit = 10,
+ -- peers = { ... },
-- },
},
- -- Optional: additional peer groups, possibly with other limits
- -- peertopeer = {
- -- limit = 10,
- -- peers = { ... },
- -- },
+ },
+
+ tunneldigger = {
+ brokers = {'vpn1.alpha-centauri.freifunk.net'}
},
bandwidth_limit = {
@@ -225,12 +334,24 @@ fastd_mesh_vpn
mesh_on_wan \: optional
Enables the mesh on the WAN port (``true`` or ``false``).
+ ::
+
+ mesh_on_wan = true,
mesh_on_lan \: optional
Enables the mesh on the LAN port (``true`` or ``false``).
+ ::
+
+ mesh_on_lan = true,
+
+poe_passthrough \: optional
+ Enable PoE passthrough by default on hardware with such a feature.
autoupdater \: package
Configuration for the autoupdater feature of Gluon.
+
+ The mirrors are checked in random order until the manifest could be downloaded
+ successfully or all mirrors have been tried.
::
autoupdater = {
@@ -240,7 +361,7 @@ autoupdater \: package
name = 'stable',
mirrors = {
'http://[fdca:ffee:babe:1::fec1]/firmware/stable/sysupgrade/',
- 'http://autoupdate.entenhausen.freifunk.net/firmware/stable/sysupgrade/',
+ 'http://autoupdate.alpha-centauri.freifunk.net/firmware/stable/sysupgrade/',
},
-- Number of good signatures required
good_signatures = 2,
@@ -255,6 +376,39 @@ autoupdater \: package
All configured mirrors must be reachable from the nodes via IPv6. If you don't want to set an IPv6 address
explicitly, but use a hostname (which is recommended), see also the :ref:`FAQ `.
+config_mode \: optional
+ Additional configuration for the configuration web interface. All values are
+ optional.
+
+ When no hostname is specified, a default hostname based on the *hostname_prefix*
+ and the node's primary MAC address is assigned. Manually setting a hostname
+ can be enforced by setting *hostname.optional* to *false*.
+
+ By default, no altitude fields are shown by the *gluon-config-mode-geo-location*
+ package. If *geo_location.show_altitude* is set to *true*, the *gluon-config-mode:altitude-label*
+ and *gluon-config-mode:altitude-help* strings must be provided in the site i18n
+ data as well.
+
+ The remote login page only shows SSH key configuration by default. A
+ password form can be displayed by setting *remote_login.show_password_form*
+ to true; in this case, *remote_login.min_password_length* defines the
+ minimum password length.
+ ::
+
+ config_mode = {
+ hostname = {
+ optional = false,
+ },
+ geo_location = {
+ show_altitude = true,
+ },
+ remote_login = {
+ show_password_form = true,
+ min_password_length = 10,
+ },
+ },
+
+
roles \: optional
Optional role definitions. Nodes will announce their role inside the mesh.
This will allow in the backend to distinguish between normal, backbone and
@@ -262,11 +416,11 @@ roles \: optional
the community which roles to define. See the section below as an example.
``default`` takes the default role which is set initially. This value should be
part of ``list``. If you want node owners to change the role via config mode add
- the package ``gluon-luci-node-role`` to ``site.mk``.
+ the package ``gluon-web-node-role`` to ``site.mk``.
- The strings to display in the LuCI interface can be configured per language in the
+ The strings to display in the web interface are configured per language in the
``i18n/en.po``, ``i18n/de.po``, etc. files of the site repository using message IDs like
- ``gluon-luci-node-role:role:node`` and ``gluon-luci-node-role:role:backbone``.
+ ``gluon-web-node-role:role:node`` and ``gluon-web-node-role:role:backbone``.
::
roles = {
@@ -288,30 +442,20 @@ setup_mode \: package
skip = true,
},
-legacy \: package
- Configuration for the legacy upgrade path.
- This is only required in communities upgrading from Lübeck's LFF-0.3.x.
- ::
+Build configuration
+-------------------
- legacy = {
- version_files = {'/etc/.freifunk_version_keep', '/etc/.eff_version_keep'},
- old_files = {'/etc/config/config_mode', '/etc/config/ffeh', '/etc/config/freifunk'},
- config_mode_configs = {'config_mode', 'ffeh', 'freifunk'},
- fastd_configs = {'ffeh_mesh_vpn', 'mesh_vpn'},
- mesh_ifname = 'freifunk',
- tc_configs = {'ffki', 'freifunk'},
- wifi_names = {'wifi_freifunk', 'wifi_freifunk5', 'wifi_mesh', 'wifi_mesh5'},
- }
-
-Packages
---------
-
-The ``site.mk`` is a Makefile which should define constants
+The ``site.mk`` is a Makefile which defines various values
involved in the build process of Gluon.
+GLUON_FEATURES
+ Defines a list of features to include. The feature list is used to generate
+ the default package set.
+
GLUON_SITE_PACKAGES
- Defines a list of packages which should be installed additionally
- to the ``gluon-core`` package.
+ Defines a list of packages which should be installed in addition to the
+ default package set. It is also possible to remove packages from the
+ default set by prepending a minus sign to the package name.
GLUON_RELEASE
The current release version Gluon should use.
@@ -320,10 +464,86 @@ GLUON_PRIORITY
The default priority for the generated manifests (see the autoupdater documentation
for more information).
+GLUON_REGION
+ Region code to build into images where necessary. Valid values are the empty string,
+ ``us`` and ``eu``.
+
GLUON_LANGS
List of languages (as two-letter-codes) to be included in the web interface. Should always contain
``en``.
+GLUON_WLAN_MESH
+ Setting this to ``11s`` or ``ibss`` will enable generation of matching images for devices which don't
+ support both meshing modes, either at all (e.g. ralink and mediatek don't support AP+IBSS) or in the
+ same firmware (ath10k-based 5GHz). Defaults to ``11s``.
+
+.. _user-site-feature-flags:
+
+Feature flags
+^^^^^^^^^^^^^
+
+With the addition of more and more features that interact in complex ways, it
+has become necessary to split certain packages into multiple parts, so it is
+possible to install just what is needed for a specific usecase. One example
+is the package *gluon-status-page-mesh-batman-adv*: There are batman-adv-specific
+status page components; they should only be installed when both batman-adv and
+the status page are enabled, making the addition of a specific package for this
+combination necessary.
+
+With the ongoing modularization, e.g. for the purpose of supporting new
+routing protocols, specifying all such split packages in *site.mk* would
+soon become very cumbersome: In the future, further components like
+respondd support or languages might be split off as separate packages,
+leading to entangled package names like *gluon-mesh-vpn-fastd-respondd* or
+*gluon-status-page-mesh-batman-adv-i18n-de*.
+
+For this reason, we have introduced *feature flags*, which can be specified
+in the *GLUON_FEATURES* variable. These flags allow to specify a set of features
+on a higher level than individual package names.
+
+Most Gluon packages can simply be specified as feature flags by removing the ``gluon-``
+prefix: The feature flag corresponding to the package *gluon-mesh-batman-adv-15* is
+*mesh-batman-adv-15*.
+
+The file ``package/features`` in the Gluon repository (or
+``features`` in site feeds) can specify additional rules for deriving package lists
+from feature flags, e.g. specifying both *status-page* and either *mesh-batman-adv-14*
+or *mesh-batman-adv-15* will automatically select the additional package
+*gluon-status-page-mesh-batman-adv*. In the future, selecting the flags
+*mesh-vpn-fastd* and *respondd* might automatically enable the additional
+package *gluon-mesh-vpn-fastd-respondd*, and enabling *status-page* and
+*mesh-batman-adv-15* (or *-14*) with ``de`` in *GLUON_LANGS* could
+add the package *gluon-status-page-mesh-batman-adv-i18n-de*.
+
+In short, it is not necessary anymore to list all the individual packages that are
+relevant for a firmware; instead, the package list is derived from a list of feature
+flags using a flexible ruleset defined in the Gluon repo or site package feeds.
+To some extent, it will even allow us to further modularize existing Gluon packages,
+without necessitating changes to existing site configurations.
+
+It is still possible to override such automatic rules using *GLUON_SITE_PACKAGES*
+(e.g., ``-gluon-status-page-mesh-batman-adv`` to remove the automatically added
+package *gluon-status-page-mesh-batman-adv*).
+
+For convenience, there are two feature flags that do not directly correspond to a Gluon
+package:
+
+* web-wizard
+
+ Includes the *gluon-config-mode-...* base packages (hostname, geolocation and contact info),
+ as well as the *gluon-config-mode-autoupdater* (when *autoupdater* is in *GLUON_FEATURES*),
+ and *gluon-config-mode-mesh-vpn* (when *mesh-vpn-fastd* or *mesh-vpn-tunneldigger* are in
+ *GLUON_FEATURES*)
+
+* web-advanced
+
+ Includes the *gluon-web-...* base packages (admin, network, WiFi config),
+ as well as the *gluon-web-autoupdater* (when *autoupdater* is in *GLUON_FEATURES*)
+
+We recommend to use *GLUON_SITE_PACKAGES* for non-Gluon OpenWrt packages only and
+completely rely on *GLUON_FEATURES* for Gluon packages, as it is shown in the
+example *site.mk*.
+
.. _site-config-mode-texts:
Config mode texts
@@ -338,6 +558,27 @@ gluon-config-mode:welcome
gluon-config-mode:pubkey
Information about the public VPN key on the reboot page.
+gluon-config-mode:novpn
+ Information shown on the reboot page, if the mesh VPN was not selected.
+
+gluon-config-mode:altitude-label
+ Label for the ``altitude`` field
+
+gluon-config-mode:altitude-help
+ Description for the usage of the ``altitude`` field
+
+gluon-config-mode:contact-help
+ Description for the usage of the ``contact`` field
+
+gluon-config-mode:contact-note
+ Note shown (in small font) below the ``contact`` field
+
+gluon-config-mode:hostname-help
+ Description for the usage of the ``hostname`` field
+
+gluon-config-mode:geo-location-help
+ Description for the usage of the longitude/latitude fields
+
gluon-config-mode:reboot
General information shown on the reboot page.
@@ -355,6 +596,41 @@ utilities are installed.
Depending on the context, you might be able to use comments like
```` as translations to effectively hide the text.
+Site modules
+------------
+
+The file ``modules`` in the site repository is completely optional and can be used
+to supply additional package feeds from which packages are built. The git repositories
+specified here are retrieved in addition to the default feeds when ``make update``
+it called.
+
+This file's format is very similar to the toplevel ``modules`` file of the Gluon
+tree, with the important different that the list of feeds must be assigned to
+the variable ``GLUON_SITE_FEEDS``. Multiple feed names must be separated by spaces,
+for example::
+
+ GLUON_SITE_FEEDS='foo bar'
+
+The feed names may only contain alphanumerical characters, underscores and slashes.
+For each of the feeds, the following variables are used to specify how to update
+the feed:
+
+PACKAGES_${feed}_REPO
+ The URL of the git repository to clone (usually ``git://`` or ``http(s)://``)
+
+PACKAGES_${feed}_COMMIT
+ The commit ID of the repository to use
+
+PACKAGES_${feed}_BRANCH
+ Optional: The branch of the repository the given commit ID can be found in.
+ Defaults to the default branch of the repository (usually ``master``)
+
+These variables are always all uppercase, so for an entry ``foo`` in GLUON_SITE_FEEDS,
+the corresponding configuration variables would be ``PACKAGES_FOO_REPO``,
+``PACKAGES_FOO_COMMIT`` and ``PACKAGES_FOO_BRANCH``. Slashes in feed names are
+replaced by underscores to get valid shell variable identifiers.
+
+
Examples
--------
@@ -394,23 +670,38 @@ site-repos in the wild
This is a non-exhaustive list of site-repos from various communities:
* `site-ffa `_ (Altdorf, Landshut & Umgebung)
+* `site-ffac `_ (Regio Aachen)
* `site-ffbs `_ (Braunschweig)
* `site-ffhb `_ (Bremen)
-* `site-ffda `_ (Darmstadt)
+* `site-ffda `_ (Darmstadt)
+* `site-ff3l `_ (Dreiländereck)
+* `site-ffeh `_ (Ehingen)
+* `site-fffl `_ (Flensburg)
* `site-ffgoe `_ (Göttingen)
+* `site-ffgt-rhw `_ (Guetersloh)
* `site-ffhh `_ (Hamburg)
-* `site-ffho `_ (Hochstift)
+* `site-ffho `_ (Hochstift)
* `site-ffhgw `_ (Greifswald)
+* `site-ffka `_ (Karlsruhe)
+* `site-ffki `_ (Kiel)
+* `site-fflz `_ (Lausitz)
* `site-ffl `_ (Leipzig)
* `site-ffhl `_ (Lübeck)
+* `site-fflg `_ (Lüneburg)
* `site-ffmd `_ (Magdeburg)
-* `site-ffmwu `_ (Mainz, Wiesbaden & Umgebung)
+* `site-ffmwu `_ (Mainz, Wiesbaden & Umgebung)
* `site-ffmyk `_ (Mayen-Koblenz)
+* `site-ffmo `_ (Moers)
+* `site-ffmg `_ (Mönchengladbach)
* `site-ffm `_ (München)
+* `site-ffhmue `_ (Münden)
* `site-ffms `_ (Münsterland)
+* `site-neuss `_ (Neuss)
+* `site-ffniers `_ (Niersufer)
+* `site-ffndh `_ (Nordheide)
* `site-ffnw `_ (Nordwest)
-* `site-ffka `_ (Karlsruhe)
-* `site-ffrl `_ (Rheinland)
-* `site-ffrg `_ (Ruhrgebiet)
+* `site-ffrgb `_ (Regensburg)
+* `site-ffrn `_ (Rhein-Neckar)
+* `site-ffruhr `_ (Ruhrgebiet, Multi-Communities)
* `site-ffs `_ (Stuttgart)
* `site-fftr `_ (Trier)
diff --git a/docs/user/x86.rst b/docs/user/x86.rst
index 9364b7ed..a65edfcc 100644
--- a/docs/user/x86.rst
+++ b/docs/user/x86.rst
@@ -22,15 +22,8 @@ The following targets for x86 images exist:
These images only differ in the image file format, the content is the same. Therefore there is
only a single `x86-generic` sysupgrade image instead of three.
- Please note that the `x86-generic` image doesn't include VirtIO support, so another virtual NIC
- like `pcnet32` must be chosen when using VirtualBox.
-
-`x86-kvm`
- The `x86-kvm` image uses VirtIO as its harddisk and network driver.
-
-`x86-xen_domu`
- The `x86-xen_domu` target contains the necessary drivers for use in Xen.
+`x86-geode`
+ x86 image for Geode CPUs.
`x86-64`
- 64bit version of `x86-generic`. Also has VirtIO support, so there's no need for an
- `x86-64-kvm` target.
+ 64bit version of `x86-generic`.
diff --git a/include/config b/include/config
deleted file mode 100644
index 12f8a7d4..00000000
--- a/include/config
+++ /dev/null
@@ -1,22 +0,0 @@
-CONFIG_IMAGEOPT=y
-# CONFIG_PER_FEED_REPO is not set
-# CONFIG_TARGET_ROOTFS_INITRAMFS is not set
-CONFIG_DEVEL=y
-CONFIG_ALL_KMODS=y
-
-CONFIG_BUSYBOX_CUSTOM=y
-CONFIG_BUSYBOX_CONFIG_SHA512SUM=y
-# CONFIG_BUSYBOX_CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set
-CONFIG_BUSYBOX_CONFIG_IP=y
-CONFIG_BUSYBOX_CONFIG_FEATURE_IP_ADDRESS=y
-CONFIG_BUSYBOX_CONFIG_FEATURE_IP_LINK=y
-CONFIG_BUSYBOX_CONFIG_FEATURE_IP_ROUTE=y
-CONFIG_BUSYBOX_CONFIG_FEATURE_IP_TUNNEL=y
-CONFIG_BUSYBOX_CONFIG_FEATURE_IP_RULE=y
-CONFIG_BUSYBOX_CONFIG_FEATURE_IP_SHORT_FORMS=y
-CONFIG_BUSYBOX_CONFIG_FEATURE_WGET_TIMEOUT=y
-
-CONFIG_ATH_USER_REGD=y
-CONFIG_PACKAGE_ATH_DEBUG=y
-
-CONFIG_LUCI_SRCDIET=y
diff --git a/include/gluon.mk b/include/gluon.mk
deleted file mode 100644
index d49dc13f..00000000
--- a/include/gluon.mk
+++ /dev/null
@@ -1,82 +0,0 @@
-ifneq ($(__gluon_inc),1)
-__gluon_inc=1
-
-GLUON_SITEDIR ?= $(GLUONDIR)/site
-GLUON_BUILDDIR ?= $(GLUONDIR)/build
-
-GLUON_ORIGOPENWRTDIR := $(GLUONDIR)/openwrt
-GLUON_SITE_CONFIG := $(GLUON_SITEDIR)/site.conf
-
-GLUON_OUTPUTDIR ?= $(GLUONDIR)/output
-GLUON_IMAGEDIR ?= $(GLUON_OUTPUTDIR)/images
-GLUON_MODULEDIR ?= $(GLUON_OUTPUTDIR)/modules
-
-GLUON_OPKG_KEY ?= $(GLUON_BUILDDIR)/gluon-opkg-key
-
-export GLUONDIR GLUON_SITEDIR GLUON_BUILDDIR GLUON_SITE_CONFIG GLUON_OUTPUTDIR GLUON_IMAGEDIR GLUON_MODULEDIR
-
-
-BOARD_BUILDDIR = $(GLUON_BUILDDIR)/$(GLUON_TARGET)
-BOARD_KDIR = $(BOARD_BUILDDIR)/kernel
-
-export BOARD_BUILDDIR
-
-
-LINUX_RELEASE := 2
-export LINUX_RELEASE
-
-
-GLUON_OPENWRTDIR = $(BOARD_BUILDDIR)/openwrt
-
-
-$(GLUON_SITEDIR)/site.mk:
- $(error There was no site configuration found. Please check out a site configuration to $(GLUON_SITEDIR))
-
--include $(GLUON_SITEDIR)/site.mk
-
-
-GLUON_VERSION := $(shell cd $(GLUONDIR) && git describe --always 2>/dev/null || echo unknown)
-export GLUON_VERSION
-
-GLUON_LANGS ?= en
-export GLUON_LANGS
-
-
-ifeq ($(OPENWRT_BUILD),1)
-ifeq ($(GLUON_TOOLS),1)
-
-GLUON_OPENWRT_FEEDS := base packages luci routing telephony management
-export GLUON_OPENWRT_FEEDS
-
-GLUON_SITE_CODE := $(shell $(GLUONDIR)/scripts/site.sh site_code)
-export GLUON_SITE_CODE
-
-ifeq ($(GLUON_RELEASE),)
-$(error GLUON_RELEASE not set. GLUON_RELEASE can be set in site.mk or on the command line.)
-endif
-export GLUON_RELEASE
-
-endif
-endif
-
-
-define merge-lists
-$(1) :=
-$(foreach var,$(2),$(1) := $$(filter-out -% $$(patsubst -%,%,$$(filter -%,$$($(var)))),$$($(1)) $$($(var)))
-)
-endef
-
-GLUON_TARGETS :=
-
-define GluonTarget
-gluon_target := $(1)$$(if $(2),-$(2))
-GLUON_TARGETS += $$(gluon_target)
-GLUON_TARGET_$$(gluon_target)_BOARD := $(1)
-GLUON_TARGET_$$(gluon_target)_SUBTARGET := $(2)
-endef
-
-GLUON_DEFAULT_PACKAGES := gluon-core kmod-ipv6 firewall ip6tables -uboot-envtools -wpad-mini hostapd-mini
-
-override DEFAULT_PACKAGES.router :=
-
-endif #__gluon_inc
diff --git a/include/package.mk b/include/package.mk
deleted file mode 100644
index 76e109e2..00000000
--- a/include/package.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-include $(INCLUDE_DIR)/package.mk
-
-# Annoyingly, make's shell function replaces all newlines with spaces, so we have to do some escaping work. Yuck.
-define GluonCheckSite
-[ -z "$$GLUONDIR" ] || sed -e 's/-@/\n/g' -e 's/+@/@/g' <<'END__GLUON__CHECK__SITE' | "$$GLUONDIR"/scripts/check_site.sh
-$(shell cat $(1) | sed -ne '1h; 1!H; $$ {g; s/@/+@/g; s/\n/-@/g; p}')
-END__GLUON__CHECK__SITE
-endef
-
-# Languages supported by LuCi
-GLUON_SUPPORTED_LANGS := ca zh_cn en fr de el he hu it ja ms no pl pt_br pt ro ru es sv uk vi
-
-GLUON_I18N_PACKAGES := $(foreach lang,$(GLUON_SUPPORTED_LANGS),+LUCI_LANG_$(lang):luci-i18n-base-$(lang))
-GLUON_I18N_CONFIG := $(foreach lang,$(GLUON_SUPPORTED_LANGS),CONFIG_LUCI_LANG_$(lang))
-GLUON_ENABLED_LANGS := $(foreach lang,$(GLUON_SUPPORTED_LANGS),$(if $(CONFIG_LUCI_LANG_$(lang)),$(lang)))
-
-
-define GluonBuildI18N
- mkdir -p $$(PKG_BUILD_DIR)/i18n
- for lang in $$(GLUON_ENABLED_LANGS); do \
- if [ -e $(2)/$$$$lang.po ]; then \
- rm -f $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo; \
- po2lmo $(2)/$$$$lang.po $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo; \
- fi; \
- done
-endef
-
-define GluonInstallI18N
- $$(INSTALL_DIR) $(2)/usr/lib/lua/luci/i18n
- for lang in $$(GLUON_ENABLED_LANGS); do \
- if [ -e $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo ]; then \
- $$(INSTALL_DATA) $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo $(2)/usr/lib/lua/luci/i18n/$(1).$$$$lang.lmo; \
- fi; \
- done
-endef
diff --git a/include/toplevel.mk b/include/toplevel.mk
deleted file mode 100644
index 087a7a19..00000000
--- a/include/toplevel.mk
+++ /dev/null
@@ -1,62 +0,0 @@
-# Makefile for OpenWrt
-#
-# Copyright (C) 2007-2012 OpenWrt.org
-# Copyright (C) 2013-2014 Project Gluon
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-RELEASE:=Chaos Calmer
-PREP_MK= OPENWRT_BUILD= QUIET=0
-
-export IS_TTY=$(shell tty -s && echo 1 || echo 0)
-
-include $(GLUONDIR)/include/verbose.mk
-
-REVISION:=$(shell [ -d $(TOPDIR) ] && cd $(TOPDIR) && ./scripts/getver.sh 2>/dev/null)
-
-HOSTCC ?= gcc
-OPENWRTVERSION:=$(RELEASE)$(if $(REVISION), ($(REVISION)))
-export RELEASE
-export REVISION
-export OPENWRTVERSION
-export IS_TTY=$(shell tty -s && echo 1 || echo 0)
-export LD_LIBRARY_PATH:=$(subst ::,:,$(if $(LD_LIBRARY_PATH),$(LD_LIBRARY_PATH):)$(STAGING_DIR_HOST)/lib)
-export DYLD_LIBRARY_PATH:=$(subst ::,:,$(if $(DYLD_LIBRARY_PATH),$(DYLD_LIBRARY_PATH):)$(STAGING_DIR_HOST)/lib)
-export GIT_CONFIG_PARAMETERS='core.autocrlf=false'
-export MAKE_JOBSERVER=$(filter --jobserver%,$(MAKEFLAGS))
-
-# prevent perforce from messing with the patch utility
-unexport P4PORT P4USER P4CONFIG P4CLIENT
-
-# prevent user defaults for quilt from interfering
-unexport QUILT_PATCHES QUILT_PATCH_OPTS
-
-unexport C_INCLUDE_PATH CROSS_COMPILE ARCH
-
-# prevent distro default LPATH from interfering
-unexport LPATH
-
-# make sure that a predefined CFLAGS variable does not disturb packages
-export CFLAGS=
-
-ifneq ($(shell $(HOSTCC) 2>&1 | grep clang),)
- export HOSTCC_REAL?=$(HOSTCC)
- export HOSTCC_WRAPPER:=$(TOPDIR)/scripts/clang-gcc-wrapper
-else
- export HOSTCC_WRAPPER:=$(HOSTCC)
-endif
-
-SCAN_COOKIE?=$(shell echo $$$$)
-export SCAN_COOKIE
-
-SUBMAKE:=umask 022; $(SUBMAKE)
-
-ULIMIT_FIX=_limit=`ulimit -n`; [ "$$_limit" = "unlimited" -o "$$_limit" -ge 1024 ] || ulimit -n 1024;
-
-FORCE: ;
-
-.PHONY: FORCE
-.NOTPARALLEL:
-
diff --git a/include/verbose.mk b/include/verbose.mk
deleted file mode 100644
index d34f55fc..00000000
--- a/include/verbose.mk
+++ /dev/null
@@ -1,67 +0,0 @@
-#
-# Copyright (C) 2006 OpenWrt.org
-#
-# This is free software, licensed under the GNU General Public License v2.
-# See /LICENSE for more information.
-#
-
-ifndef OPENWRT_VERBOSE
- OPENWRT_VERBOSE:=
-endif
-ifeq ("$(origin V)", "command line")
- OPENWRT_VERBOSE:=$(V)
-endif
-
-ifeq ($(OPENWRT_VERBOSE),1)
- OPENWRT_VERBOSE:=w
-endif
-ifeq ($(OPENWRT_VERBOSE),99)
- OPENWRT_VERBOSE:=s
-endif
-
-ifeq ($(NO_TRACE_MAKE),)
-NO_TRACE_MAKE := $(MAKE) V=s$(OPENWRT_VERBOSE)
-export NO_TRACE_MAKE
-endif
-
-ifeq ($(IS_TTY),1)
- ifneq ($(strip $(NO_COLOR)),1)
- _Y:=\\033[33m
- _R:=\\033[31m
- _N:=\\033[m
- endif
-endif
-
-ifeq ($(findstring s,$(OPENWRT_VERBOSE)),)
- define MESSAGE
- printf "$(_Y)%s$(_N)\n" "$(1)" >&8
- endef
-
- define ERROR_MESSAGE
- printf "$(_R)%s$(_N)\n" "$(1)" >&8
- endef
-
- ifeq ($(QUIET),1)
- ifneq ($(CURDIR),$(TOPDIR))
- _DIR:=$(patsubst $(TOPDIR)/%,%,${CURDIR})
- else
- _DIR:=
- endif
- _NULL:=$(if $(MAKECMDGOALS),$(shell \
- $(call MESSAGE, make[$(MAKELEVEL)]$(if $(_DIR), -C $(_DIR)) $(MAKECMDGOALS)); \
- ))
- SUBMAKE=$(MAKE)
- else
- SILENT:=>/dev/null $(if $(findstring w,$(OPENWRT_VERBOSE)),,2>&1)
- export QUIET:=1
- SUBMAKE=cmd() { $(SILENT) $(MAKE) -s $$* < /dev/null || { echo "make $$*: build failed. Please re-run make with V=s to see what's going on"; false; } } 8>&1 9>&2; cmd
- endif
-
- .SILENT: $(MAKECMDGOALS)
-else
- SUBMAKE=$(MAKE) -w
- define MESSAGE
- printf "%s\n" "$(1)"
- endef
- ERROR_MESSAGE=$(MESSAGE)
-endif
diff --git a/modules b/modules
index 1336e245..fefeb87e 100644
--- a/modules
+++ b/modules
@@ -1,18 +1,20 @@
GLUON_FEEDS='openwrt gluon routing luci'
-OPENWRT_REPO=git://git.openwrt.org/15.05/openwrt.git
-OPENWRT_COMMIT=eadf19c0b43d2f75f196ea8d875a08c7c348530c
+LEDE_REPO=https://git.openwrt.org/openwrt/openwrt.git
+LEDE_BRANCH=lede-17.01
+LEDE_COMMIT=0a2c984222d03d940c52c3675849dd6398b34e64
-PACKAGES_OPENWRT_REPO=git://github.com/openwrt/packages.git
-PACKAGES_OPENWRT_COMMIT=9622fe984bba3a4547f48bc507ebaba7637eb2b0
-PACKAGES_OPENWRT_BRANCH=for-15.05
+PACKAGES_OPENWRT_REPO=https://github.com/openwrt/packages.git
+PACKAGES_OPENWRT_BRANCH=lede-17.01
+PACKAGES_OPENWRT_COMMIT=338690b2f79e2c7090be4e9adbb19b452c9e3c36
-PACKAGES_GLUON_REPO=git://github.com/freifunk-gluon/packages.git
-PACKAGES_GLUON_COMMIT=63376e23c81e53c21d3c5250c3fb7444a90dc019
+PACKAGES_GLUON_REPO=https://github.com/freifunk-gluon/packages.git
+PACKAGES_GLUON_COMMIT=be2c35785994e443d895225c7240474a46f64f5e
-PACKAGES_ROUTING_REPO=git://github.com/openwrt-routing/packages.git
-PACKAGES_ROUTING_COMMIT=2a8338559de5c4b077cde7a83f43f4700a17d5cc
+PACKAGES_ROUTING_REPO=https://github.com/openwrt-routing/packages.git
+PACKAGES_ROUTING_BRANCH=openwrt-18.06
+PACKAGES_ROUTING_COMMIT=1b9d1c419f0ecefda51922a7845ab2183d6acd76
-PACKAGES_LUCI_REPO=git://github.com/openwrt/luci.git
-PACKAGES_LUCI_COMMIT=cdcdfd2594634804ab09dc8105e46116edce0cd6
-PACKAGES_LUCI_BRANCH=for-15.05
+PACKAGES_LUCI_REPO=https://github.com/openwrt/luci.git
+PACKAGES_LUCI_BRANCH=lede-17.01
+PACKAGES_LUCI_COMMIT=1f014bd2180b364bec4c3f6457f72a0621884f9a
diff --git a/overlay/opkg.mk b/overlay/opkg.mk
new file mode 100644
index 00000000..ccbd2573
--- /dev/null
+++ b/overlay/opkg.mk
@@ -0,0 +1,10 @@
+# LEDE doesn't have a nice way to set the list of feeds in
+# /etc/opkg/distfeeds.conf, so we use this overlay file (which is included by
+# the opkg package Makefile though LEDE's IncludeOverlay mechanism).
+
+# The following definitions make /etc/opkg/distfeeds.conf match the one included
+# in official LEDE builds (by default, FEEDS_DISABLED contains the original list
+# of feeds (which are unused by Gluon), and FEEDS_ENABLED our own feed list).
+
+FEEDS_ENABLED := $(FEEDS_DISABLED)
+FEEDS_DISABLED :=
diff --git a/package/features b/package/features
new file mode 100644
index 00000000..210581d5
--- /dev/null
+++ b/package/features
@@ -0,0 +1,26 @@
+nodefault 'web-wizard'
+
+packages 'web-wizard' \
+ 'gluon-config-mode-hostname' \
+ 'gluon-config-mode-geo-location' \
+ 'gluon-config-mode-contact-info'
+
+packages 'web-wizard & autoupdater' \
+ 'gluon-config-mode-autoupdater'
+
+packages 'web-wizard & (mesh-vpn-fastd | mesh-vpn-tunneldigger)' \
+ 'gluon-config-mode-mesh-vpn'
+
+
+nodefault 'web-advanced'
+
+packages 'web-advanced' \
+ 'gluon-web-admin' \
+ 'gluon-web-network' \
+ 'gluon-web-wifi-config'
+
+packages 'web-advanced & autoupdater' \
+ 'gluon-web-autoupdater'
+
+packages 'status-page & (mesh-batman-adv-14 | mesh-batman-adv-15)' \
+ 'gluon-status-page-mesh-batman-adv'
diff --git a/package/gluon-alfred/Makefile b/package/gluon-alfred/Makefile
index ca3ae327..7c173a77 100644
--- a/package/gluon-alfred/Makefile
+++ b/package/gluon-alfred/Makefile
@@ -4,29 +4,11 @@ PKG_NAME:=gluon-alfred
PKG_VERSION:=1
PKG_RELEASE:=1
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(INCLUDE_DIR)/package.mk
+include ../gluon.mk
define Package/gluon-alfred
- SECTION:=gluon
- CATEGORY:=Gluon
- DEPENDS:=+gluon-core +gluon-respondd +gluon-neighbour-info +micrond +alfred
+ DEPENDS:=+gluon-core +gluon-respondd +gluon-neighbour-info gluon-mesh-batman-adv +micrond +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))
+$(eval $(call BuildPackageGluon,gluon-alfred))
diff --git a/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred b/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred
deleted file mode 100755
index d3802d8a..00000000
--- a/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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')
diff --git a/package/gluon-alfred/luasrc/lib/gluon/upgrade/400-alfred-firewall b/package/gluon-alfred/luasrc/lib/gluon/upgrade/400-alfred-firewall
new file mode 100755
index 00000000..eaf727cd
--- /dev/null
+++ b/package/gluon-alfred/luasrc/lib/gluon/upgrade/400-alfred-firewall
@@ -0,0 +1,14 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+
+uci:section('firewall', 'rule', 'mesh_respondd', {
+ name = 'mesh_respondd',
+ src = 'mesh',
+ src_ip = 'fe80::/64',
+ dest_port = '16962',
+ proto = 'udp',
+ target = 'ACCEPT',
+})
+
+uci:save('firewall')
diff --git a/package/gluon-alfred/luasrc/lib/gluon/upgrade/500-enable-alfred b/package/gluon-alfred/luasrc/lib/gluon/upgrade/500-enable-alfred
new file mode 100755
index 00000000..e1a28080
--- /dev/null
+++ b/package/gluon-alfred/luasrc/lib/gluon/upgrade/500-enable-alfred
@@ -0,0 +1,14 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+
+uci:delete('alfred', 'alfred')
+uci:section('alfred', 'alfred', 'alfred', {
+ interface = 'br-client',
+ mode = 'slave',
+ batmanif = 'bat0',
+ start_vis = true,
+ run_facters = false,
+})
+
+uci:save('alfred')
diff --git a/package/gluon-authorized-keys/Makefile b/package/gluon-authorized-keys/Makefile
index 6ef90da3..2995107a 100644
--- a/package/gluon-authorized-keys/Makefile
+++ b/package/gluon-authorized-keys/Makefile
@@ -3,34 +3,11 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-authorized-keys
PKG_VERSION:=2
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
+include ../gluon.mk
define Package/gluon-authorized-keys
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Fill /etc/dropbear/authorized_keys from site.conf
DEPENDS:=+gluon-core
endef
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
-endef
-
-define Package/gluon-authorized-keys/install
- $(CP) ./files/* $(1)/
-endef
-
-define Package/gluon-authorized-keys/postinst
-#!/bin/sh
-$(call GluonCheckSite,check_site.lua)
-endef
-
-$(eval $(call BuildPackage,gluon-authorized-keys))
+$(eval $(call BuildPackageGluon,gluon-authorized-keys))
diff --git a/package/gluon-authorized-keys/check_site.lua b/package/gluon-authorized-keys/check_site.lua
index d1acfabe..7daf2115 100644
--- a/package/gluon-authorized-keys/check_site.lua
+++ b/package/gluon-authorized-keys/check_site.lua
@@ -1 +1 @@
-need_string_array 'authorized_keys'
+need_string_array(in_site({'authorized_keys'}))
diff --git a/package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys b/package/gluon-authorized-keys/luasrc/lib/gluon/upgrade/100-authorized-keys
similarity index 76%
rename from package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys
rename to package/gluon-authorized-keys/luasrc/lib/gluon/upgrade/100-authorized-keys
index 643fa076..34efd0b8 100755
--- a/package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys
+++ b/package/gluon-authorized-keys/luasrc/lib/gluon/upgrade/100-authorized-keys
@@ -1,6 +1,6 @@
#!/usr/bin/lua
-local site = require 'gluon.site_config'
+local site = require 'gluon.site'
local file = '/etc/dropbear/authorized_keys'
local keys = {}
@@ -14,7 +14,7 @@ end
pcall(load_keys)
local f = io.open(file, 'a')
-for _, key in ipairs(site.authorized_keys) do
+for _, key in ipairs(site.authorized_keys()) do
if not keys[key] then
f:write(key .. '\n')
end
diff --git a/package/gluon-autoupdater/Makefile b/package/gluon-autoupdater/Makefile
index 40120fa9..d6e87499 100644
--- a/package/gluon-autoupdater/Makefile
+++ b/package/gluon-autoupdater/Makefile
@@ -2,40 +2,30 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-autoupdater
PKG_VERSION:=4
-PKG_RELEASE:=$(GLUON_BRANCH)
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-PKG_BUILD_DEPENDS := respondd
+PKG_CONFIG_DEPENDS := CONFIG_GLUON_BRANCH
-include $(GLUONDIR)/include/package.mk
+include ../gluon.mk
define Package/gluon-autoupdater
- SECTION:=gluon
- CATEGORY:=Gluon
DEPENDS:=+gluon-core +libgluonutil +micrond +autoupdater
TITLE:=Automatically update firmware
endef
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
- $(CP) ./src/* $(PKG_BUILD_DIR)/
+define Package/gluon-autoupdater/config
+config GLUON_BRANCH
+ string "Gluon autoupdater branch"
+ depends on PACKAGE_gluon-autoupdater
+ default ""
endef
define Package/gluon-autoupdater/install
- $(CP) ./files/* $(1)/
+ $(Gluon/Build/Install)
- $(INSTALL_DIR) $(1)/lib/gluon/respondd
- $(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/autoupdater.so
-
- if [ '$(GLUON_BRANCH)' ]; then \
- $(INSTALL_DIR) $(1)/lib/gluon/autoupdater; \
- echo '$(GLUON_BRANCH)' > $(1)/lib/gluon/autoupdater/default_branch; \
- fi
+ifneq ($(CONFIG_GLUON_BRANCH),"")
+ $(INSTALL_DIR) $(1)/lib/gluon/autoupdater
+ echo '$(call qstrip,$(CONFIG_GLUON_BRANCH))' > $(1)/lib/gluon/autoupdater/default_branch
+endif
endef
-define Package/gluon-autoupdater/postinst
-#!/bin/sh
-$(call GluonCheckSite,check_site.lua)
-endef
-
-$(eval $(call BuildPackage,gluon-autoupdater))
+$(eval $(call BuildPackageGluon,gluon-autoupdater))
diff --git a/package/gluon-autoupdater/check_site.lua b/package/gluon-autoupdater/check_site.lua
index 9f443fec..de9beef0 100644
--- a/package/gluon-autoupdater/check_site.lua
+++ b/package/gluon-autoupdater/check_site.lua
@@ -1,14 +1,10 @@
-need_string 'autoupdater.branch'
+need_string(in_site({'autoupdater', 'branch'}))
-local function check_branch(k, _)
- assert_uci_name(k)
+need_table({'autoupdater', 'branches'}, function(branch)
+ need_alphanumeric_key(branch)
- local prefix = string.format('autoupdater.branches[%q].', k)
-
- need_string(prefix .. 'name')
- need_string_array(prefix .. 'mirrors')
- need_number(prefix .. 'good_signatures')
- need_string_array(prefix .. 'pubkeys')
-end
-
-need_table('autoupdater.branches', check_branch)
+ need_string(in_site(extend(branch, {'name'})))
+ need_string_array_match(extend(branch, {'mirrors'}), '^http://')
+ need_number(in_site(extend(branch, {'good_signatures'})))
+ need_string_array_match(in_site(extend(branch, {'pubkeys'})), '^%x+$')
+end)
diff --git a/package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh b/package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh
index 36ddd971..24ef2f90 100644
--- a/package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh
+++ b/package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh
@@ -2,15 +2,15 @@
stop() {
- if [ -x /etc/init.d/$1 ]; then
+ if [ -x /etc/init.d/"$1" ]; then
echo "Stopping $1..."
- /etc/init.d/$1 stop
+ /etc/init.d/"$1" stop
fi
}
start_enabled() {
- if [ -x /etc/init.d/$1 ] && /etc/init.d/$1 enabled; then
+ if [ -x /etc/init.d/"$1" ] && /etc/init.d/"$1" enabled; then
echo "Starting $1..."
- /etc/init.d/$1 start
+ /etc/init.d/"$1" start
fi
}
diff --git a/package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater b/package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater
deleted file mode 100755
index 17006b70..00000000
--- a/package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/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.autoupdater.branches) do
- c:delete('autoupdater', name)
- c:section('autoupdater', 'branch', name,
- {
- name = config.name,
- mirror = config.mirrors,
- good_signatures = config.good_signatures,
- pubkey = config.pubkeys,
- }
- )
-end
-
-if not c:get('autoupdater', 'settings') then
- local enabled = 0
- local branch = site.autoupdater.branch
-
- local f = io.open('/lib/gluon/autoupdater/default_branch')
- if f then
- enabled = 1
- branch = f:read('*line')
- f:close()
- end
-
- c:section('autoupdater', 'autoupdater', 'settings',
- {
- enabled = enabled,
- branch = branch,
- }
- )
-end
-
-c:set('autoupdater', 'settings', 'version_file', '/lib/gluon/release')
-
-c:save('autoupdater')
-
-
-local autoupdater_util = require 'autoupdater.util'
-autoupdater_util.randomseed()
-
-
--- Perform updates at a random time between 04:00 and 05:00, and once an hour
--- a fallback update (used after the regular updates haven't
-local minute = math.random(0, 59)
-
-local f = io.open('/usr/lib/micron.d/autoupdater', 'w')
-f:write(string.format('%i 4 * * * /usr/sbin/autoupdater\n', minute))
-f:write(string.format('%i 0-3,5-23 * * * /usr/sbin/autoupdater --fallback\n', minute))
-f:close()
diff --git a/package/gluon-autoupdater/luasrc/lib/gluon/upgrade/500-autoupdater b/package/gluon-autoupdater/luasrc/lib/gluon/upgrade/500-autoupdater
new file mode 100755
index 00000000..88214a86
--- /dev/null
+++ b/package/gluon-autoupdater/luasrc/lib/gluon/upgrade/500-autoupdater
@@ -0,0 +1,53 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site'
+local uci = require('simple-uci').cursor()
+
+
+for name, config in pairs(site.autoupdater.branches()) do
+ uci:delete('autoupdater', name)
+ uci:section('autoupdater', 'branch', name, {
+ name = config.name,
+ mirror = config.mirrors,
+ good_signatures = config.good_signatures,
+ pubkey = config.pubkeys,
+ })
+end
+
+if not uci:get('autoupdater', 'settings') then
+ local enabled = false
+ local branch = site.autoupdater.branch()
+
+ local f = io.open('/lib/gluon/autoupdater/default_branch')
+ if f then
+ enabled = true
+ branch = f:read('*line')
+ f:close()
+ end
+
+ uci:section('autoupdater', 'autoupdater', 'settings', {
+ enabled = enabled,
+ branch = branch,
+ })
+end
+
+uci:set('autoupdater', 'settings', 'version_file', '/lib/gluon/release')
+
+uci:save('autoupdater')
+
+
+local urandom = io.open('/dev/urandom', 'r')
+local seed1, seed2 = urandom:read(2):byte(1, 2)
+math.randomseed(seed1*0x100 + seed2)
+urandom:close()
+
+-- Perform updates at a random time between 04:00 and 05:00, and once an hour
+-- a fallback update (used after the regular updates haven't worked for
+-- (priority+1) days after a firmware release, for example because the node
+-- is always offline at night)
+local minute = math.random(0, 59)
+
+local f = io.open('/usr/lib/micron.d/autoupdater', 'w')
+f:write(string.format('%i 4 * * * /usr/sbin/autoupdater\n', minute))
+f:write(string.format('%i 0-3,5-23 * * * /usr/sbin/autoupdater --fallback\n', minute))
+f:close()
diff --git a/package/gluon-autoupdater/src/respondd.c b/package/gluon-autoupdater/src/respondd.c
index 66b7a928..74468fb1 100644
--- a/package/gluon-autoupdater/src/respondd.c
+++ b/package/gluon-autoupdater/src/respondd.c
@@ -36,6 +36,8 @@
static struct json_object * get_autoupdater(void) {
struct uci_context *ctx = uci_alloc_context();
+ if (!ctx)
+ return NULL;
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
diff --git a/package/gluon-client-bridge/Makefile b/package/gluon-client-bridge/Makefile
index 6bbfed64..a2daf34b 100644
--- a/package/gluon-client-bridge/Makefile
+++ b/package/gluon-client-bridge/Makefile
@@ -3,34 +3,11 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-client-bridge
PKG_VERSION:=1
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
+include ../gluon.mk
define Package/gluon-client-bridge
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Provides a bridge and a wireless interface for clients to connect to
- DEPENDS:=+gluon-core
+ DEPENDS:=+gluon-core +kmod-veth +@GLUON_SPECIALIZE_KERNEL:KERNEL_VETH
endef
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
-endef
-
-define Package/gluon-client-bridge/install
- $(CP) ./files/* $(1)/
-endef
-
-define Package/gluon-client-bridge/postinst
-#!/bin/sh
-$(call GluonCheckSite,check_site.lua)
-endef
-
-$(eval $(call BuildPackage,gluon-client-bridge))
+$(eval $(call BuildPackageGluon,gluon-client-bridge))
diff --git a/package/gluon-client-bridge/check_site.lua b/package/gluon-client-bridge/check_site.lua
index 3a7d81ee..7922fae5 100644
--- a/package/gluon-client-bridge/check_site.lua
+++ b/package/gluon-client-bridge/check_site.lua
@@ -1,6 +1,15 @@
-for _, config in ipairs({'wifi24', 'wifi5'}) do
- if need_table(config .. '.ap', nil, false) then
- need_string(config .. '.ap.ssid')
- need_boolean(config .. '.ap.disabled', false)
- end
+need_string_match(in_domain({'next_node', 'mac'}), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$', false)
+
+if need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', false) then
+ need_string_match(in_domain({'prefix4'}), '^%d+.%d+.%d+.%d+/%d+$')
+end
+
+need_string_match(in_domain({'next_node', 'ip6'}), '^[%x:]+$', false)
+
+
+for _, config in ipairs({'wifi24', 'wifi5'}) do
+ if need_table({config, 'ap'}, nil, false) then
+ need_string_match(in_domain({config, 'ap', 'ssid'}), '^' .. ('.?'):rep(32) .. '$')
+ need_boolean({config, 'ap', 'disabled'}, false)
+ end
end
diff --git a/package/gluon-client-bridge/files/lib/gluon/upgrade/300-gluon-client-bridge-network b/package/gluon-client-bridge/files/lib/gluon/upgrade/300-gluon-client-bridge-network
deleted file mode 100755
index 4a897f74..00000000
--- a/package/gluon-client-bridge/files/lib/gluon/upgrade/300-gluon-client-bridge-network
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/lua
-
-local sysconfig = require 'gluon.sysconfig'
-local uci = require('luci.model.uci').cursor()
-
-
-if not uci:get('network', 'client') then
- uci:section('network', 'interface', 'client',
- {
- type = 'bridge',
- }
- )
-end
-
-local ifname = uci:get('network', 'client', 'ifname')
-
-if type(ifname) == 'string' then
- uci:delete('network', 'client', 'ifname')
- for x in ifname:gmatch("[^%s]+") do
- uci:add_to_set('network', 'client', 'ifname', x)
- end
-end
-
-uci:set('network', 'client', 'macaddr', sysconfig.primary_mac)
-
-uci:save('network')
diff --git a/package/gluon-client-bridge/files/lib/gluon/upgrade/320-gluon-client-bridge-wireless b/package/gluon-client-bridge/files/lib/gluon/upgrade/320-gluon-client-bridge-wireless
deleted file mode 100755
index 3edb39d8..00000000
--- a/package/gluon-client-bridge/files/lib/gluon/upgrade/320-gluon-client-bridge-wireless
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/lua
-
-local site = require 'gluon.site_config'
-local util = require 'gluon.util'
-
-local uci = require('luci.model.uci').cursor()
-
-
-local function is_disabled(config, name)
- local disabled = config and config.disabled
- if uci:get('wireless', name) then
- disabled = uci:get_bool('wireless', name, 'disabled')
- end
-
- return disabled and 1 or 0
-end
-
-local function configure_client(config, radio, index, suffix)
- local name = 'client_' .. radio
- local disabled = is_disabled(config, name)
-
- uci:delete('wireless', name)
-
- macaddr = util.generate_mac(3*(index-1))
-
- if config and macaddr then
- uci:section('wireless', 'wifi-iface', name,
- {
- device = radio,
- network = 'client',
- mode = 'ap',
- ssid = config.ssid,
- macaddr = macaddr,
- ifname = suffix and 'client' .. suffix,
- disabled = disabled,
- }
- )
- end
-end
-
-local function configure_radio(radio, index, config)
- local suffix = radio:match('^radio(%d+)$')
-
- configure_client(config.ap, radio, index, suffix)
-end
-
-util.iterate_radios(configure_radio)
-
-uci:save('wireless')
diff --git a/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/300-gluon-client-bridge-network b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/300-gluon-client-bridge-network
new file mode 100755
index 00000000..c79bc3c7
--- /dev/null
+++ b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/300-gluon-client-bridge-network
@@ -0,0 +1,73 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+local util = require 'gluon.util'
+
+local uci = require('simple-uci').cursor()
+
+
+local interfaces = uci:get('network', 'client', 'ifname') or {}
+
+if type(interfaces) == 'string' then
+ local ifname = interfaces
+ interfaces = {}
+ for iface in ifname:gmatch('%S+') do
+ util.add_to_set(interfaces, iface)
+ end
+end
+
+if sysconfig.lan_ifname and uci:get_bool('network', 'mesh_lan', 'disabled') then
+ for lanif in sysconfig.lan_ifname:gmatch('%S+') do
+ util.add_to_set(interfaces, lanif)
+ end
+end
+
+util.add_to_set(interfaces, 'local-port')
+
+
+uci:delete('network', 'client')
+uci:section('network', 'interface', 'client', {
+ type = 'bridge',
+ ifname = interfaces,
+ proto = 'none',
+ auto = true,
+ ipv6 = false,
+ macaddr = sysconfig.primary_mac,
+ igmp_snooping = true,
+ multicast_querier = true,
+})
+
+uci:save('network')
+
+-- TODO: remove this line and the next in 2019. Firewall zones have been renamed in 2017.
+uci:delete('firewall', 'client')
+
+uci:section('firewall', 'zone', 'drop', {
+ name = 'drop',
+ network = {'client'},
+ input = 'DROP',
+ output = 'DROP',
+ forward = 'DROP',
+})
+
+local networks = uci:get_list('firewall', 'local_client', 'network')
+util.add_to_set(networks, 'local_node')
+uci:set_list('firewall', 'local_client', 'network', networks)
+
+
+local dnsmasq = uci:get_first('dhcp', 'dnsmasq')
+uci:set('dhcp', dnsmasq, 'boguspriv', false)
+uci:set('dhcp', dnsmasq, 'localise_queries', false)
+uci:set('dhcp', dnsmasq, 'rebind_protection', false)
+
+-- TODO: remove this line and the next two in 2019 the zones were removed in 2017
+uci:delete('dhcp', 'client')
+uci:delete('firewall', 'local_node')
+
+uci:section('dhcp', 'dhcp', 'local_client', {
+ interface = 'client',
+ ignore = true,
+})
+
+uci:save('dhcp')
+uci:save('firewall')
diff --git a/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/310-gluon-client-bridge-local-node b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/310-gluon-client-bridge-local-node
new file mode 100755
index 00000000..824d8d40
--- /dev/null
+++ b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/310-gluon-client-bridge-local-node
@@ -0,0 +1,52 @@
+#!/usr/bin/lua
+
+local client_bridge = require 'gluon.client_bridge'
+local site = require 'gluon.site'
+local sysconfig = require 'gluon.sysconfig'
+
+local uci = require('simple-uci').cursor()
+
+
+local next_node = site.next_node({})
+
+
+uci:delete('network', 'local_node_dev')
+uci:section('network', 'device', 'local_node_dev', {
+ type = 'veth',
+ name = 'local-node',
+ macaddr = client_bridge.next_node_macaddr(),
+ peer_name = 'local-port',
+ peer_macaddr = sysconfig.primary_mac,
+})
+
+
+local ip4, ip6
+
+if next_node.ip4 then
+ local plen = site.prefix4():match('/%d+$')
+ ip4 = next_node.ip4 .. plen
+end
+
+if next_node.ip6 then
+ ip6 = next_node.ip6 .. '/128'
+end
+
+uci:delete('network', 'local_node')
+uci:section('network', 'interface', 'local_node', {
+ ifname = 'local-node',
+ proto = 'static',
+ ipaddr = ip4,
+ ip6addr = ip6,
+ ip6deprecated = true,
+})
+
+uci:save('network')
+
+
+uci:delete('dhcp', 'local_node')
+uci:section('dhcp', 'dhcp', 'local_node', {
+ interface = 'local_node',
+ ignore = true,
+})
+
+uci:save('dhcp')
diff --git a/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/320-gluon-client-bridge-wireless b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/320-gluon-client-bridge-wireless
new file mode 100755
index 00000000..d9ac83f3
--- /dev/null
+++ b/package/gluon-client-bridge/luasrc/lib/gluon/upgrade/320-gluon-client-bridge-wireless
@@ -0,0 +1,47 @@
+#!/usr/bin/lua
+
+local util = require 'gluon.util'
+
+local uci = require('simple-uci').cursor()
+
+
+local function is_disabled(config, name)
+ if uci:get('wireless', name) then
+ return uci:get_bool('wireless', name, 'disabled')
+ end
+
+ return config.disabled(false)
+end
+
+util.foreach_radio(uci, function(radio, index, config)
+ local radio_name = radio['.name']
+
+ local name = 'client_' .. radio_name
+ local suffix = radio_name:match('^radio(%d+)$')
+
+ local ap = config.ap
+ local disabled = is_disabled(ap, name)
+
+ uci:delete('wireless', name)
+
+ if not ap() then
+ return
+ end
+
+ local macaddr = util.get_wlan_mac(uci, radio, index, 1)
+ if not macaddr then
+ return
+ end
+
+ uci:section('wireless', 'wifi-iface', name, {
+ device = radio_name,
+ network = 'client',
+ mode = 'ap',
+ ssid = ap.ssid(),
+ macaddr = macaddr,
+ ifname = suffix and 'client' .. suffix,
+ disabled = disabled or false,
+ })
+end)
+
+uci:save('wireless')
diff --git a/package/gluon-client-bridge/luasrc/usr/lib/lua/gluon/client_bridge.lua b/package/gluon-client-bridge/luasrc/usr/lib/lua/gluon/client_bridge.lua
new file mode 100644
index 00000000..f3af7504
--- /dev/null
+++ b/package/gluon-client-bridge/luasrc/usr/lib/lua/gluon/client_bridge.lua
@@ -0,0 +1,8 @@
+local site = require 'gluon.site'
+
+
+module 'gluon.client_bridge'
+
+function next_node_macaddr()
+ return site.next_node.mac('16:41:95:40:f7:dc')
+end
diff --git a/package/gluon-config-mode-autoupdater/Makefile b/package/gluon-config-mode-autoupdater/Makefile
index 4303940b..b94d3b45 100644
--- a/package/gluon-config-mode-autoupdater/Makefile
+++ b/package/gluon-config-mode-autoupdater/Makefile
@@ -3,38 +3,11 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-config-mode-autoupdater
PKG_VERSION:=1
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
+include ../gluon.mk
define Package/gluon-config-mode-autoupdater
- SECTION:=gluon
- CATEGORY:=Gluon
- TITLE:=Let the user know whether the autoupdater is enabled or not.
- DEPENDS:=gluon-config-mode-core-virtual +gluon-autoupdater
+ TITLE:=Config Mode: Let the user know whether the autoupdater is enabled or not
+ DEPENDS:=+gluon-config-mode-core +gluon-autoupdater
endef
-define Package/gluon-config-mode-autoupdater/description
- Luci based config mode
-endef
-
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
- $(call GluonBuildI18N,gluon-config-mode-autoupdater,i18n)
-endef
-
-define Package/gluon-config-mode-autoupdater/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-config-mode-autoupdater,$(1))
-endef
-
-$(eval $(call BuildPackage,gluon-config-mode-autoupdater))
+$(eval $(call BuildPackageGluon,gluon-config-mode-autoupdater))
diff --git a/package/gluon-config-mode-autoupdater/files/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua b/package/gluon-config-mode-autoupdater/files/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua
deleted file mode 100644
index 3bbe0390..00000000
--- a/package/gluon-config-mode-autoupdater/files/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua
+++ /dev/null
@@ -1,19 +0,0 @@
-local cbi = require "luci.cbi"
-local i18n = require "luci.i18n"
-local uci = luci.model.uci.cursor()
-
-local M = {}
-
-function M.section(form)
- local enabled = uci:get_bool("autoupdater", "settings", "enabled")
- if enabled then
- local s = form:section(cbi.SimpleSection, nil,
- i18n.translate('This node will automatically update its firmware when a new version is available.'))
- end
-end
-
-function M.handle(data)
- return
-end
-
-return M
diff --git a/package/gluon-config-mode-autoupdater/i18n/de.po b/package/gluon-config-mode-autoupdater/i18n/de.po
index 5a55e349..c555b721 100644
--- a/package/gluon-config-mode-autoupdater/i18n/de.po
+++ b/package/gluon-config-mode-autoupdater/i18n/de.po
@@ -13,5 +13,13 @@ msgstr ""
msgid ""
"This node will automatically update its firmware when a new version is "
"available."
-msgstr "Dieser Knoten aktualisiert seine Firmware automatisch, sobald "
+msgstr ""
+"Dieser Knoten aktualisiert seine Firmware automatisch, sobald "
"eine neue Version vorliegt."
+
+msgid ""
+"Automatic updates are disabled. They can be enabled in Advanced "
+"settings."
+msgstr ""
+"Automatische Updates sind deaktiviert. Sie können in den Erweiterten "
+"Einstellungen aktiviert werden."
diff --git a/package/gluon-config-mode-autoupdater/i18n/fr.po b/package/gluon-config-mode-autoupdater/i18n/fr.po
index 7ee64f46..13cf9637 100644
--- a/package/gluon-config-mode-autoupdater/i18n/fr.po
+++ b/package/gluon-config-mode-autoupdater/i18n/fr.po
@@ -15,3 +15,10 @@ msgid ""
"available."
msgstr "Ce nœud s'actualisera automatiquement quand une nouvelle "
"version sera disponible."
+
+msgid ""
+"Automatic updates are disabled. They can be enabled in Advanced "
+"settings."
+msgstr ""
+"Mises à jour automatiques sont désactivées. Ils peuvent être activés dans "
+"Paramètres avancés."
diff --git a/package/gluon-config-mode-autoupdater/i18n/gluon-config-mode-autoupdater.pot b/package/gluon-config-mode-autoupdater/i18n/gluon-config-mode-autoupdater.pot
index 9e6272cd..aec7cdc5 100644
--- a/package/gluon-config-mode-autoupdater/i18n/gluon-config-mode-autoupdater.pot
+++ b/package/gluon-config-mode-autoupdater/i18n/gluon-config-mode-autoupdater.pot
@@ -5,3 +5,8 @@ msgid ""
"This node will automatically update its firmware when a new version is "
"available."
msgstr ""
+
+msgid ""
+"Automatic updates are disabled. They can be enabled in Advanced "
+"settings."
+msgstr ""
diff --git a/package/gluon-config-mode-autoupdater/luasrc/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua b/package/gluon-config-mode-autoupdater/luasrc/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua
new file mode 100644
index 00000000..856938f1
--- /dev/null
+++ b/package/gluon-config-mode-autoupdater/luasrc/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua
@@ -0,0 +1,16 @@
+return function(form, uci)
+ local pkg_i18n = i18n 'gluon-config-mode-autoupdater'
+
+ local enabled = uci:get_bool("autoupdater", "settings", "enabled")
+ if enabled then
+ form:section(
+ Section, nil,
+ pkg_i18n.translate('This node will automatically update its firmware when a new version is available.')
+ )
+ else
+ form:section(
+ Section, nil,
+ pkg_i18n.translate('Automatic updates are disabled. They can be enabled in Advanced settings.')
+ )
+ end
+end
diff --git a/package/gluon-config-mode-contact-info/Makefile b/package/gluon-config-mode-contact-info/Makefile
index ff9cb57f..ad27ffa2 100644
--- a/package/gluon-config-mode-contact-info/Makefile
+++ b/package/gluon-config-mode-contact-info/Makefile
@@ -3,34 +3,11 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-config-mode-contact-info
PKG_VERSION:=1
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
+include ../gluon.mk
define Package/gluon-config-mode-contact-info
- SECTION:=gluon
- CATEGORY:=Gluon
- TITLE:=Set a custom string that will be distributed in the mesh.
- DEPENDS:=gluon-config-mode-core-virtual +gluon-node-info
+ TITLE:=Allows the user to provide contact information to be distributed in the mesh
+ 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-contact-info,i18n)
-endef
-
-define Package/gluon-config-mode-contact-info/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-config-mode-contact-info,$(1))
-endef
-
-$(eval $(call BuildPackage,gluon-config-mode-contact-info))
+$(eval $(call BuildPackageGluon,gluon-config-mode-contact-info))
diff --git a/package/gluon-config-mode-contact-info/check_site.lua b/package/gluon-config-mode-contact-info/check_site.lua
new file mode 100644
index 00000000..e69de29b
diff --git a/package/gluon-config-mode-contact-info/files/lib/gluon/config-mode/wizard/0500-contact-info.lua b/package/gluon-config-mode-contact-info/files/lib/gluon/config-mode/wizard/0500-contact-info.lua
deleted file mode 100644
index a2182f95..00000000
--- a/package/gluon-config-mode-contact-info/files/lib/gluon/config-mode/wizard/0500-contact-info.lua
+++ /dev/null
@@ -1,34 +0,0 @@
-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(
- 'You can provide your contact information here to '
- .. 'allow others to contact you. Please note that '
- .. 'this information will be visible publicly '
- .. 'on the internet together with your node\'s coordinates.'
- )
- )
-
- local o = s:option(cbi.Value, "_contact", i18n.translate("Contact info"))
- o.default = uci:get_first("gluon-node-info", "owner", "contact", "")
- o.rmempty = true
- o.datatype = "string"
- o.description = i18n.translate("e.g. E-mail or phone number")
- o.maxlen = 140
-end
-
-function M.handle(data)
- if data._contact ~= nil then
- uci:set("gluon-node-info", uci:get_first("gluon-node-info", "owner"), "contact", data._contact)
- else
- uci:delete("gluon-node-info", uci:get_first("gluon-node-info", "owner"), "contact")
- end
- uci:save("gluon-node-info")
- uci:commit("gluon-node-info")
-end
-
-return M
diff --git a/package/gluon-config-mode-contact-info/i18n/de.po b/package/gluon-config-mode-contact-info/i18n/de.po
index 719246f4..651fdde4 100644
--- a/package/gluon-config-mode-contact-info/i18n/de.po
+++ b/package/gluon-config-mode-contact-info/i18n/de.po
@@ -14,14 +14,20 @@ msgid "Contact info"
msgstr "Kontakt"
msgid ""
-"You can provide your contact information here to allow others to contact "
-"you. Please note that this information will be visible publicly on "
-"the internet together with your node's coordinates."
+"Please provide your contact information here to allow others to contact "
+"you. Note that this information will be visible publicly on "
+"the internet together with your node's coordinates. This means it can "
+"be downloaded and processed by anyone. This information is "
+"not required to operate a node. If you chose to enter data, it will be "
+"stored on this node and can be deleted by yourself at any time."
msgstr ""
-"Hier kannst du einen öffentlichen Hinweis hinterlegen, um anderen "
-"zu ermöglichen, Kontakt mit dir aufzunehmen. Bitte beachte, dass "
-"dieser Hinweis auch öffentlich im Internet, zusammen mit den Koordinaten "
-"deines Knotens, einsehbar sein wird."
+"Bitte hinterlege hier einen Hinweis, um anderen zu ermöglichen, "
+"Kontakt mit dir aufzunehmen. Beachte, dass dieser Hinweis auch "
+"öffentlich im Internet, zusammen mit den Koordinaten "
+"deines Knotens, einsehbar sein wird. Das bedeutet, dass diese Informationen "
+"von jedem heruntergeladen und verarbeitet werden können. Für den Betrieb sind diese "
+"Informationen nicht erforderlich. Eine Speicherung erfolgt auf diesem Knoten. "
+"Die Daten können durch dich in diesem Menü eigenständig gelöscht werden."
msgid "e.g. E-mail or phone number"
msgstr "z.B. E-Mail oder Telefonnummer"
diff --git a/package/gluon-config-mode-contact-info/i18n/fr.po b/package/gluon-config-mode-contact-info/i18n/fr.po
index 779e15fe..f20e06cb 100644
--- a/package/gluon-config-mode-contact-info/i18n/fr.po
+++ b/package/gluon-config-mode-contact-info/i18n/fr.po
@@ -14,12 +14,20 @@ msgid "Contact info"
msgstr "Informations de Contact"
msgid ""
-"You can provide your contact information here to allow others to contact "
-"you. Please note that this information will be visible publicly on "
-"the internet together with your node's coordinates."
+"Please provide your contact information here to allow others to contact "
+"you. Note that this information will be visible publicly on "
+"the internet together with your node's coordinates. This means it can "
+"be downloaded and processed by anyone. This information is "
+"not required to operate a node. If you chose to enter data, it will be "
+"stored on this node and can be deleted by yourself at any time."
msgstr ""
-"Ici vous pouvez donner des informations publiques pour permettre aux autres de vous contacter. "
-"Ces informations seront affichées en ligne, avec les coordonnées du nœud."
+"Entrez vos coordonnées ici si vous souhaitez permettre à d'autres "
+"personnes de communiquer avec vous. Ces informations seront affichées "
+"en ligne avec les coordonnées du nœud. Il peut être téléchargé "
+"et traité par tous. Il n'est pas obligatoire "
+"de fournir ces informations pour pouvoir opérer un nœud. Si vous avez "
+"choisi d'entrer des données, elles seront stockées sur ce nœud et vous "
+"pourrez les effacer par vous-même à tout moment."
msgid "e.g. E-mail or phone number"
msgstr "Ex : E-mail ou numéro de téléphone"
diff --git a/package/gluon-config-mode-contact-info/i18n/gluon-config-mode-contact-info.pot b/package/gluon-config-mode-contact-info/i18n/gluon-config-mode-contact-info.pot
index 63939a83..913f38a5 100644
--- a/package/gluon-config-mode-contact-info/i18n/gluon-config-mode-contact-info.pot
+++ b/package/gluon-config-mode-contact-info/i18n/gluon-config-mode-contact-info.pot
@@ -5,9 +5,12 @@ msgid "Contact info"
msgstr ""
msgid ""
-"You can provide your contact information here to allow others to contact "
-"you. Please note that this information will be visible publicly on "
-"the internet together with your node's coordinates."
+"Please provide your contact information here to allow others to contact "
+"you. Note that this information will be visible publicly on "
+"the internet together with your node's coordinates. This means it can be "
+"downloaded and processed by anyone. This information is "
+"not required to operate a node. If you chose to enter data, it will be "
+"stored on this node and can be deleted by yourself at any time."
msgstr ""
msgid "e.g. E-mail or phone number"
diff --git a/package/gluon-config-mode-contact-info/luasrc/lib/gluon/config-mode/wizard/0500-contact-info.lua b/package/gluon-config-mode-contact-info/luasrc/lib/gluon/config-mode/wizard/0500-contact-info.lua
new file mode 100644
index 00000000..8309d87a
--- /dev/null
+++ b/package/gluon-config-mode-contact-info/luasrc/lib/gluon/config-mode/wizard/0500-contact-info.lua
@@ -0,0 +1,29 @@
+return function(form, uci)
+ local pkg_i18n = i18n 'gluon-config-mode-contact-info'
+ local site_i18n = i18n 'gluon-site'
+
+ local site = require 'gluon.site'
+
+ local owner = uci:get_first("gluon-node-info", "owner")
+
+ local help = site_i18n._translate("gluon-config-mode:contact-help") or pkg_i18n.translate(
+ 'Please provide your contact information here to allow others to contact '
+ .. 'you. Note that this information will be visible publicly on '
+ .. 'the internet together with your node\'s coordinates. This means it can '
+ .. 'be downloaded and processed by anyone. This information is '
+ .. 'not required to operate a node. If you chose to enter data, it will be '
+ .. 'stored on this node and can be deleted by yourself at any time.'
+ )
+ local s = form:section(Section, nil, help)
+
+ local o = s:option(Value, "contact", pkg_i18n.translate("Contact info"),
+ site_i18n._translate("gluon-config-mode:contact-note") or pkg_i18n.translate("e.g. E-mail or phone number"))
+ o.default = uci:get("gluon-node-info", owner, "contact")
+ o.datatype = 'minlength(1)'
+ o.optional = true
+ function o:write(data)
+ uci:set("gluon-node-info", owner, "contact", data)
+ end
+
+ return {'gluon-node-info'}
+end
diff --git a/package/gluon-config-mode-core/Makefile b/package/gluon-config-mode-core/Makefile
index 5d700f7f..b9c39487 100644
--- a/package/gluon-config-mode-core/Makefile
+++ b/package/gluon-config-mode-core/Makefile
@@ -6,35 +6,20 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-config-mode-core
PKG_VERSION:=2
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
+include ../gluon.mk
define Package/gluon-config-mode-core
- SECTION:=gluon
- CATEGORY:=Gluon
- TITLE:=Luci based config mode for user friendly setup of new mesh nodes
- DEPENDS:=gluon-setup-mode-virtual +gluon-luci-theme +gluon-lock-password $(GLUON_I18N_PACKAGES)
- PROVIDES:=gluon-config-mode-core-virtual
-endef
-
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
- $(call GluonBuildI18N,gluon-config-mode-core,i18n)
+ TITLE:=Configuration wizard for user friendly setup of new mesh nodes
+ DEPENDS:=+gluon-setup-mode +gluon-web-model +gluon-config-mode-theme +uhttpd
endef
define Package/gluon-config-mode-core/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-config-mode-core,$(1))
+ $(Gluon/Build/Install)
+
+ $(INSTALL_DIR) $(1)/lib/gluon/config-mode/view/ $(1)/lib/gluon/config-mode/www/static/
+ $(LN) /lib/gluon/web/i18n $(1)/lib/gluon/config-mode/
+ $(LN) /lib/gluon/web/view/{error,model} $(1)/lib/gluon/config-mode/view/
+ $(LN) /lib/gluon/web/www/static/gluon-web-model.js $(1)/lib/gluon/config-mode/www/static/
endef
-$(eval $(call BuildPackage,gluon-config-mode-core))
+$(eval $(call BuildPackageGluon,gluon-config-mode-core))
diff --git a/package/gluon-config-mode-core/files/lib/gluon/config-mode/reboot/0900-msg-reboot.lua b/package/gluon-config-mode-core/files/lib/gluon/config-mode/reboot/0900-msg-reboot.lua
deleted file mode 100644
index bf27c07f..00000000
--- a/package/gluon-config-mode-core/files/lib/gluon/config-mode/reboot/0900-msg-reboot.lua
+++ /dev/null
@@ -1,3 +0,0 @@
-local i18n = require 'luci.i18n'
-
-return function () luci.template.render_string(i18n.translate('gluon-config-mode:reboot')) end
diff --git a/package/gluon-config-mode-core/files/lib/gluon/config-mode/view/wizard/reboot.html b/package/gluon-config-mode-core/files/lib/gluon/config-mode/view/wizard/reboot.html
new file mode 100644
index 00000000..6c65010b
--- /dev/null
+++ b/package/gluon-config-mode-core/files/lib/gluon/config-mode/view/wizard/reboot.html
@@ -0,0 +1,22 @@
+
<%:Your node's setup is now complete.%>
+<%
+ local fs = require "nixio.fs"
+ local util = require "nixio.util"
+
+ local parts_dir = "/lib/gluon/config-mode/reboot/"
+ local files = util.consume(fs.dir(parts_dir) or function() end)
+ table.sort(files)
+
+ local parts = {}
+ for _, entry in ipairs(files) do
+ if entry:sub(1, 1) ~= '.' then
+ local p = assert(loadfile(parts_dir .. entry))
+ setfenv(p, getfenv())
+ table.insert(parts, p)
+ end
+ end
+
+ for _, p in ipairs(parts) do
+ p()
+ end
+%>
diff --git a/package/gluon-config-mode-core/files/lib/gluon/config-mode/view/wizard/welcome.html b/package/gluon-config-mode-core/files/lib/gluon/config-mode/view/wizard/welcome.html
new file mode 100644
index 00000000..00beaa04
--- /dev/null
+++ b/package/gluon-config-mode-core/files/lib/gluon/config-mode/view/wizard/welcome.html
@@ -0,0 +1,16 @@
+<%-
+ local site_i18n = i18n 'gluon-site'
+
+ local sysconfig = require 'gluon.sysconfig'
+
+ local msg = site_i18n._translate('gluon-config-mode:welcome')
+ if not msg then return end
+-%>
+
-
-<% if not self.embedded then %>
-
-<% 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
deleted file mode 100644
index e8f32d99..00000000
--- a/package/gluon-config-mode-core/files/usr/lib/lua/luci/view/gluon/config-mode/reboot.htm
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
- <%=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
index 58d23894..dd335a47 100644
--- a/package/gluon-config-mode-core/i18n/de.po
+++ b/package/gluon-config-mode-core/i18n/de.po
@@ -10,7 +10,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#, fuzzy
msgid "Save & restart"
msgstr "Speichern & Neustarten"
diff --git a/package/gluon-config-mode-core/i18n/fr.po b/package/gluon-config-mode-core/i18n/fr.po
index f4f988cc..4988e251 100644
--- a/package/gluon-config-mode-core/i18n/fr.po
+++ b/package/gluon-config-mode-core/i18n/fr.po
@@ -9,7 +9,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#, fuzzy
+
msgid "Save & restart"
msgstr "Enregistrer & Redémarer"
diff --git a/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/controller/gluon-config-mode/index.lua b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/controller/gluon-config-mode/index.lua
new file mode 100644
index 00000000..8801948a
--- /dev/null
+++ b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/controller/gluon-config-mode/index.lua
@@ -0,0 +1,4 @@
+package 'gluon-config-mode-core'
+
+entry({}, alias("wizard"))
+entry({"wizard"}, model("gluon-config-mode/wizard"), _("Wizard"), 5)
diff --git a/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/model/gluon-config-mode/wizard.lua b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/model/gluon-config-mode/wizard.lua
new file mode 100644
index 00000000..ca400a9e
--- /dev/null
+++ b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/model/gluon-config-mode/wizard.lua
@@ -0,0 +1,75 @@
+local fs = require "nixio.fs"
+local util = require "gluon.util"
+local nixio_util = require "nixio.util"
+
+local uci = require("simple-uci").cursor()
+
+local wizard_dir = "/lib/gluon/config-mode/wizard/"
+
+local files = nixio_util.consume(fs.dir(wizard_dir) or function() end)
+table.sort(files)
+
+local wizard = {}
+for _, entry in ipairs(files) do
+ if entry:sub(1, 1) ~= '.' then
+ local f = assert(loadfile(wizard_dir .. entry))
+ setfenv(f, getfenv())
+ local w = f()
+ table.insert(wizard, w)
+ end
+end
+
+local f = Form(translate("Welcome!"))
+f.submit = translate('Save & restart')
+f.reset = false
+
+local s = f:section(Section)
+s.template = "wizard/welcome"
+s.package = "gluon-config-mode-core"
+
+local commit = {'gluon-setup-mode'}
+local run = {}
+
+for _, w in ipairs(wizard) do
+ for _, c in ipairs(w(f, uci) or {}) do
+ if type(c) == 'string' then
+ if not util.contains(commit, c) then
+ table.insert(commit, c)
+ end
+ elseif type(c) == 'function' then
+ table.insert(run, c)
+ else
+ error('invalid wizard module return')
+ end
+ end
+end
+
+function f:write()
+ local nixio = require "nixio"
+
+ uci:set("gluon-setup-mode", uci:get_first("gluon-setup-mode", "setup_mode"), "configured", true)
+
+ for _, c in ipairs(commit) do
+ uci:commit(c)
+ end
+ for _, r in ipairs(run) do
+ r()
+ end
+
+ f.template = "wizard/reboot"
+ f.package = "gluon-config-mode-core"
+ f.hidenav = true
+
+ if nixio.fork() == 0 then
+ -- Replace stdout with /dev/null
+ nixio.dup(nixio.open('/dev/null', 'w'), nixio.stdout)
+
+ -- Sleep a little so the browser can fetch everything required to
+ -- display the reboot page, then reboot the device.
+ nixio.nanosleep(1)
+
+ nixio.execp("reboot")
+ end
+end
+
+return f
diff --git a/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/reboot/0900-msg-reboot.lua b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/reboot/0900-msg-reboot.lua
new file mode 100644
index 00000000..d73a9e0b
--- /dev/null
+++ b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/reboot/0900-msg-reboot.lua
@@ -0,0 +1,20 @@
+local site_i18n = i18n 'gluon-site'
+
+local site = require 'gluon.site'
+local sysconfig = require 'gluon.sysconfig'
+local pretty_hostname = require 'pretty_hostname'
+
+local uci = require("simple-uci").cursor()
+
+local hostname = pretty_hostname.get(uci)
+local contact = uci:get_first('gluon-node-info', 'owner', 'contact')
+
+local msg = site_i18n._translate('gluon-config-mode:reboot')
+if not msg then return end
+
+renderer.render_string(msg, {
+ hostname = hostname,
+ site = site,
+ sysconfig = sysconfig,
+ contact = contact,
+})
diff --git a/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/www/cgi-bin/config b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/www/cgi-bin/config
new file mode 100755
index 00000000..7f7ead23
--- /dev/null
+++ b/package/gluon-config-mode-core/luasrc/lib/gluon/config-mode/www/cgi-bin/config
@@ -0,0 +1,8 @@
+#!/usr/bin/lua
+
+require 'gluon.web.cgi' {
+ base_path = '/lib/gluon/config-mode',
+
+ layout_package = 'gluon-config-mode-theme',
+ layout_template = 'theme/layout',
+}
diff --git a/package/gluon-config-mode-domain-select/Makefile b/package/gluon-config-mode-domain-select/Makefile
new file mode 100644
index 00000000..b1ded939
--- /dev/null
+++ b/package/gluon-config-mode-domain-select/Makefile
@@ -0,0 +1,13 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-config-mode-domain-select
+PKG_VERSION:=1
+
+include ../gluon.mk
+
+define Package/gluon-config-mode-domain-select
+ TITLE:=UI for changing the node-config
+ DEPENDS:=+gluon-config-mode-core @GLUON_MULTIDOMAIN
+endef
+
+$(eval $(call BuildPackageGluon,gluon-config-mode-domain-select))
diff --git a/package/gluon-config-mode-domain-select/check_site.lua b/package/gluon-config-mode-domain-select/check_site.lua
new file mode 100644
index 00000000..6a6fd47b
--- /dev/null
+++ b/package/gluon-config-mode-domain-select/check_site.lua
@@ -0,0 +1,7 @@
+local valid_domain_codes = table_keys(need_table(in_domain({'domain_names'})))
+
+alternatives(function()
+ need_boolean(in_domain({'hide_domain'}), false)
+end, function()
+ need_array_of(in_domain({'hide_domain'}), valid_domain_codes, false)
+end)
diff --git a/package/gluon-config-mode-domain-select/luasrc/lib/gluon/config-mode/wizard/0200-domain-select.lua b/package/gluon-config-mode-domain-select/luasrc/lib/gluon/config-mode/wizard/0200-domain-select.lua
new file mode 100644
index 00000000..9496e99f
--- /dev/null
+++ b/package/gluon-config-mode-domain-select/luasrc/lib/gluon/config-mode/wizard/0200-domain-select.lua
@@ -0,0 +1,68 @@
+
+return function(form, uci)
+ local site_i18n = i18n 'gluon-site'
+
+ local fs = require 'nixio.fs'
+ local json = require 'jsonc'
+ local site = require 'gluon.site'
+ local util = require 'gluon.util'
+
+ local selected_domain = uci:get('gluon', 'core', 'domain')
+ local configured = uci:get_first('gluon-setup-mode','setup_mode', 'configured') == '1' or (selected_domain ~= site.default_domain())
+
+ local function hide_domain_code(domain, code)
+ if configured and code == selected_domain then
+ return false
+ elseif type(domain.hide_domain) == 'table' then
+ return util.contains(domain.hide_domain, code)
+ else
+ return domain.hide_domain
+ end
+ end
+
+ local function get_domain_list()
+ local list = {}
+ for domain_path in fs.glob('/lib/gluon/domains/*.json') do
+ local domain_code = domain_path:match('([^/]+)%.json$')
+ local domain = assert(json.load(domain_path))
+
+ if not hide_domain_code(domain, domain_code) then
+ table.insert(list, {
+ domain_code = domain_code,
+ domain_name = domain.domain_names[domain_code],
+ })
+ end
+ end
+
+ table.sort(list, function(a, b) return a.domain_name < b.domain_name end)
+ return list
+ end
+
+ local s = form:section(Section, nil, site_i18n.translate('gluon-config-mode:domain-select'))
+ local o = s:option(ListValue, 'domain', site_i18n.translate('gluon-config-mode:domain'))
+
+ if configured then
+ o.default = selected_domain
+ end
+
+ for _, domain in ipairs(get_domain_list()) do
+ o:value(domain.domain_code, domain.domain_name)
+ end
+
+ local domain_changed = false
+
+ function o:write(data)
+ if data ~= selected_domain then
+ domain_changed = true
+ uci:set('gluon', 'core', 'domain', data)
+ end
+ end
+
+ local function reconfigure()
+ if domain_changed then
+ os.execute('gluon-reconfigure')
+ end
+ end
+
+ return {'gluon', reconfigure}
+end
diff --git a/package/gluon-config-mode-geo-location/Makefile b/package/gluon-config-mode-geo-location/Makefile
index 03812e54..a020feb9 100644
--- a/package/gluon-config-mode-geo-location/Makefile
+++ b/package/gluon-config-mode-geo-location/Makefile
@@ -3,39 +3,11 @@ 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)
-
+include ../gluon.mk
define Package/gluon-config-mode-geo-location
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Set geographic location of a node
- DEPENDS:=gluon-config-mode-core-virtual +gluon-node-info
+ 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
-
-define Package/gluon-config-mode-geo-location/postinst
-#!/bin/sh
-$(call GluonCheckSite,check_site.lua)
-endef
-
-$(eval $(call BuildPackage,gluon-config-mode-geo-location))
+$(eval $(call BuildPackageGluon,gluon-config-mode-geo-location))
diff --git a/package/gluon-config-mode-geo-location/check_site.lua b/package/gluon-config-mode-geo-location/check_site.lua
index 509226fe..9d7c26cb 100644
--- a/package/gluon-config-mode-geo-location/check_site.lua
+++ b/package/gluon-config-mode-geo-location/check_site.lua
@@ -1,3 +1 @@
-if need_table('config_mode', nil, false) and need_table('config_mode.geo_location', nil, false) then
- need_boolean('config_mode.geo_location.show_altitude', false)
-end
+need_boolean(in_site({'config_mode', 'geo_location', 'show_altitude'}), false)
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
deleted file mode 100644
index 9bc70301..00000000
--- a/package/gluon-config-mode-geo-location/files/lib/gluon/config-mode/wizard/0400-geo-location.lua
+++ /dev/null
@@ -1,76 +0,0 @@
-local cbi = require "luci.cbi"
-local i18n = require "luci.i18n"
-local uci = luci.model.uci.cursor()
-local site = require 'gluon.site_config'
-
-local M = {}
-
-local function show_altitude()
- if ((site.config_mode or {}).geo_location or {}).show_altitude ~= false then
- return true
- end
- if uci:get_first("gluon-node-info", "location", "altitude") then
- return true
- end
- return false
-end
-
-function M.section(form)
- local text = i18n.translate('If you want the location of your node to '
- .. 'be displayed on the map, you can enter its coordinates here.')
- if show_altitude() then
- text = text .. ' ' .. i18n.translate('Specifying the altitude is '
- .. 'optional and should only be done if a proper value is known.')
- end
- local s = form:section(cbi.SimpleSection, nil, text)
-
-
- 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")
-
- if show_altitude() then
- 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
-
-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:trim())
- uci:set("gluon-node-info", sname, "longitude", data._longitude:trim())
- if data._altitude ~= nil then
- uci:set("gluon-node-info", sname, "altitude", data._altitude:trim())
- 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
index 3580732b..c8bc906a 100644
--- a/package/gluon-config-mode-geo-location/i18n/de.po
+++ b/package/gluon-config-mode-geo-location/i18n/de.po
@@ -17,22 +17,12 @@ 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."
-msgid ""
-"Specifying the altitude is optional and should only be done if a proper "
-"value is known."
-msgstr ""
-"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öhe"
-
msgid "Show node on the map"
msgstr "Knoten auf der Karte anzeigen"
diff --git a/package/gluon-config-mode-geo-location/i18n/fr.po b/package/gluon-config-mode-geo-location/i18n/fr.po
index b239c84c..d3d7c6e8 100644
--- a/package/gluon-config-mode-geo-location/i18n/fr.po
+++ b/package/gluon-config-mode-geo-location/i18n/fr.po
@@ -17,22 +17,12 @@ msgstr ""
"Pour Afficher votre nœud sur la Carte nous avons besoin de ses coordonnées. "
"Ici vous pouvez entrer sa position."
-msgid ""
-"Specifying the altitude is optional and should only be done if a proper "
-"value is known."
-msgstr ""
-"La altitude est optionelle et ne devrait que être ajoutée si la valeur "
-"exacte est connue."
-
msgid "Latitude"
msgstr "Latitude"
msgid "Longitude"
msgstr "Longitude"
-msgid "Altitude"
-msgstr "Hauteur"
-
msgid "Show node on the map"
msgstr "Afficher le nœud sur la carte"
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
index 48f26f70..7acf7f28 100644
--- 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
@@ -6,20 +6,12 @@ msgid ""
"enter its coordinates here."
msgstr ""
-msgid ""
-"Specifying the altitude is optional and should only be done if a proper "
-"value is known."
-msgstr ""
-
msgid "Latitude"
msgstr ""
msgid "Longitude"
msgstr ""
-msgid "Altitude"
-msgstr ""
-
msgid "Show node on the map"
msgstr ""
diff --git a/package/gluon-config-mode-geo-location/luasrc/lib/gluon/config-mode/wizard/0400-geo-location.lua b/package/gluon-config-mode-geo-location/luasrc/lib/gluon/config-mode/wizard/0400-geo-location.lua
new file mode 100644
index 00000000..718e59ee
--- /dev/null
+++ b/package/gluon-config-mode-geo-location/luasrc/lib/gluon/config-mode/wizard/0400-geo-location.lua
@@ -0,0 +1,63 @@
+return function(form, uci)
+ local pkg_i18n = i18n 'gluon-config-mode-geo-location'
+ local site_i18n = i18n 'gluon-site'
+
+ local site = require 'gluon.site'
+
+ local location = uci:get_first("gluon-node-info", "location")
+
+ local function show_altitude()
+ if site.config_mode.geo_location.show_altitude(true) then
+ return true
+ end
+
+ return uci:get_bool("gluon-node-info", location, "altitude")
+ end
+
+ local text = site_i18n._translate("gluon-config-mode:geo-location-help") or pkg_i18n.translate(
+ 'If you want the location of your node to ' ..
+ 'be displayed on the map, you can enter its coordinates here.'
+ )
+ if show_altitude() then
+ text = text .. ' ' .. site_i18n.translate("gluon-config-mode:altitude-help")
+ end
+
+ local s = form:section(Section, nil, text)
+
+ local o
+
+ local share_location = s:option(Flag, "location", pkg_i18n.translate("Show node on the map"))
+ share_location.default = uci:get_bool("gluon-node-info", location, "share_location")
+ function share_location:write(data)
+ uci:set("gluon-node-info", location, "share_location", data)
+ end
+
+ o = s:option(Value, "latitude", pkg_i18n.translate("Latitude"), pkg_i18n.translatef("e.g. %s", "53.873621"))
+ o.default = uci:get("gluon-node-info", location, "latitude")
+ o:depends(share_location, true)
+ o.datatype = "float"
+ function o:write(data)
+ uci:set("gluon-node-info", location, "latitude", data)
+ end
+
+ o = s:option(Value, "longitude", pkg_i18n.translate("Longitude"), pkg_i18n.translatef("e.g. %s", "10.689901"))
+ o.default = uci:get("gluon-node-info", location, "longitude")
+ o:depends(share_location, true)
+ o.datatype = "float"
+ function o:write(data)
+ uci:set("gluon-node-info", location, "longitude", data)
+ end
+
+ if show_altitude() then
+ o = s:option(Value, "altitude", site_i18n.translate("gluon-config-mode:altitude-label"), pkg_i18n.translatef("e.g. %s", "11.51"))
+ o.default = uci:get("gluon-node-info", location, "altitude")
+ o:depends(share_location, true)
+ o.datatype = "float"
+ o.optional = true
+ function o:write(data)
+ uci:set("gluon-node-info", location, "altitude", data)
+ end
+ end
+
+ return {'gluon-node-info'}
+end
diff --git a/package/gluon-config-mode-hostname/Makefile b/package/gluon-config-mode-hostname/Makefile
index 803f018f..fdf3acd9 100644
--- a/package/gluon-config-mode-hostname/Makefile
+++ b/package/gluon-config-mode-hostname/Makefile
@@ -3,34 +3,11 @@ 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)
-
+include ../gluon.mk
define Package/gluon-config-mode-hostname
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Set the hostname
- DEPENDS:=gluon-config-mode-core-virtual
+ 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))
+$(eval $(call BuildPackageGluon,gluon-config-mode-hostname))
diff --git a/package/gluon-config-mode-hostname/check_site.lua b/package/gluon-config-mode-hostname/check_site.lua
new file mode 100644
index 00000000..29b5edd6
--- /dev/null
+++ b/package/gluon-config-mode-hostname/check_site.lua
@@ -0,0 +1 @@
+need_boolean(in_site({'config_mode', 'hostname', 'optional'}), false)
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
deleted file mode 100644
index cf8bbf89..00000000
--- a/package/gluon-config-mode-hostname/files/lib/gluon/config-mode/wizard/0100-hostname.lua
+++ /dev/null
@@ -1,21 +0,0 @@
-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
index f1d2a765..fa5a6e0f 100644
--- a/package/gluon-config-mode-hostname/i18n/de.po
+++ b/package/gluon-config-mode-hostname/i18n/de.po
@@ -1,14 +1,22 @@
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-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
msgid "Node name"
msgstr "Name dieses Knotens"
+
+msgid ""
+"The node name is used solely for identification of your node, e.g. on a node "
+"map. It does not affect the name (SSID) of the broadcasted WLAN."
+msgstr ""
+"Der Knotenname dient ausschließlich zur Identifikation deines Knotens, z.B. "
+"auf einer Knotenkarte. Er hat keinen Einfluss auf den Namen (SSID) des "
+"ausgestrahlten WLANs."
diff --git a/package/gluon-config-mode-hostname/i18n/fr.po b/package/gluon-config-mode-hostname/i18n/fr.po
index 79702c06..133992d9 100644
--- a/package/gluon-config-mode-hostname/i18n/fr.po
+++ b/package/gluon-config-mode-hostname/i18n/fr.po
@@ -1,14 +1,22 @@
msgid ""
msgstr ""
-"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-08-12 23:30+0100\n"
"Last-Translator:Tobias Bernot \n"
"Language-Team: French\n"
"Language: fr\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 "Node name"
msgstr "Nom du nœud"
+
+msgid ""
+"The node name is used solely for identification of your node, e.g. on a node "
+"map. It does not affect the name (SSID) of the broadcasted WLAN."
+msgstr ""
+"Le nom du nœud est utilisé uniquement pour l'identification de votre nœud, "
+"par ex. sur un carte des nœuds. Il n'a aucune influence sur le nom du Wi-"
+"Fi(SSID) rayonné."
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
index dbaafc0b..eabf0a28 100644
--- a/package/gluon-config-mode-hostname/i18n/gluon-config-mode-hostname.pot
+++ b/package/gluon-config-mode-hostname/i18n/gluon-config-mode-hostname.pot
@@ -3,3 +3,8 @@ msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Node name"
msgstr ""
+
+msgid ""
+"The node name is used solely for identification of your node, e.g. on a node "
+"map. It does not affect the name (SSID) of the broadcasted WLAN."
+msgstr ""
diff --git a/package/gluon-config-mode-hostname/luasrc/lib/gluon/config-mode/wizard/0100-hostname.lua b/package/gluon-config-mode-hostname/luasrc/lib/gluon/config-mode/wizard/0100-hostname.lua
new file mode 100644
index 00000000..d078b4de
--- /dev/null
+++ b/package/gluon-config-mode-hostname/luasrc/lib/gluon/config-mode/wizard/0100-hostname.lua
@@ -0,0 +1,35 @@
+return function(form, uci)
+ local pkg_i18n = i18n 'gluon-config-mode-hostname'
+ local site_i18n = i18n 'gluon-site'
+
+ local pretty_hostname = require 'pretty_hostname'
+ local site = require 'gluon.site'
+ local util = require 'gluon.util'
+
+ local help = site_i18n._translate("gluon-config-mode:hostname-help") or pkg_i18n.translate(
+ 'The node name is used solely for identification of your node, e.g. on a '
+ .. 'node map. It does not affect the name (SSID) of the broadcasted WLAN.'
+ )
+ form:section(Section, nil, help)
+
+ local current_hostname = pretty_hostname.get(uci)
+ local default_hostname = util.default_hostname()
+ local configured = uci:get_first('gluon-setup-mode', 'setup_mode', 'configured', false) or (current_hostname ~= default_hostname)
+
+ local s = form:section(Section)
+ local o = s:option(Value, "hostname", pkg_i18n.translate("Node name"))
+ o.datatype = 'minlength(1)'
+ if site.config_mode.hostname.optional(true) then
+ o.optional = true
+ o.placeholder = default_hostname
+ end
+ if configured then
+ o.default = current_hostname
+ end
+
+ function o:write(data)
+ pretty_hostname.set(uci, data or default_hostname)
+ end
+
+ return {'system'}
+end
diff --git a/package/gluon-config-mode-mesh-vpn/Makefile b/package/gluon-config-mode-mesh-vpn/Makefile
index 35d53ea7..0c3a752d 100644
--- a/package/gluon-config-mode-mesh-vpn/Makefile
+++ b/package/gluon-config-mode-mesh-vpn/Makefile
@@ -3,34 +3,11 @@ 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)
-
+include ../gluon.mk
define Package/gluon-config-mode-mesh-vpn
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Toggle mesh-vpn and bandwidth limit
- DEPENDS:=gluon-config-mode-core-virtual +gluon-mesh-vpn-fastd
+ DEPENDS:=+gluon-config-mode-core +gluon-mesh-vpn-core
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))
+$(eval $(call BuildPackageGluon,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
deleted file mode 100644
index c6e06975..00000000
--- a/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua
+++ /dev/null
@@ -1,26 +0,0 @@
-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 contact = uci:get_first("gluon-node-info", "owner", "contact")
-
- local msg = i18n.translate('gluon-config-mode:pubkey')
-
- return function ()
- luci.template.render_string(msg, { pubkey=pubkey
- , hostname=hostname
- , site=site
- , sysconfig=sysconfig
- , contact=contact
- })
- 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
deleted file mode 100644
index f0172421..00000000
--- a/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
+++ /dev/null
@@ -1,64 +0,0 @@
-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("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("simple-tc", "mesh_vpn", "limit_ingress")
- o.rmempty = false
- o.datatype = "uinteger"
-
- o = s:option(cbi.Value, "_limit_egress", i18n.translate("Upstream (kbit/s)"))
- o:depends("_limit_enabled", "1")
- o.value = uci:get("simple-tc", "mesh_vpn", "limit_egress")
- o.rmempty = false
- o.datatype = "uinteger"
-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("simple-tc", "mesh_vpn", "interface")
- uci:set("simple-tc", "mesh_vpn", "enabled", data._limit_enabled)
- uci:set("simple-tc", "mesh_vpn", "ifname", "mesh-vpn")
-
- if data._limit_ingress ~= nil then
- uci:set("simple-tc", "mesh_vpn", "limit_ingress", data._limit_ingress:trim())
- end
-
- if data._limit_egress ~= nil then
- uci:set("simple-tc", "mesh_vpn", "limit_egress", data._limit_egress:trim())
- end
-
- uci:save("simple-tc")
- uci:commit("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
index 8c613aa0..c2e6b35c 100644
--- a/package/gluon-config-mode-mesh-vpn/i18n/de.po
+++ b/package/gluon-config-mode-mesh-vpn/i18n/de.po
@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
-"PO-Revision-Date: 2015-03-19 22:05+0100\n"
-"Last-Translator: Matthias Schiffer \n"
+"PO-Revision-Date: 2015-10-07 00:32+0100\n"
+"Last-Translator: Cyrus Fox \n"
"Language-Team: German\n"
"Language: de\n"
"MIME-Version: 1.0\n"
@@ -23,14 +23,14 @@ msgid "Use internet connection (mesh VPN)"
msgstr "Internetverbindung nutzen (Mesh-VPN)"
msgid ""
-"Your internet connection can be used to establish an encrypted connection "
+"Your internet connection can be used to establish a VPN 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 "
+"Dein Knoten kann deine Internetverbindung nutzen, um darüber eine "
+"VPN-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/fr.po b/package/gluon-config-mode-mesh-vpn/i18n/fr.po
index fc854613..0c324f32 100644
--- a/package/gluon-config-mode-mesh-vpn/i18n/fr.po
+++ b/package/gluon-config-mode-mesh-vpn/i18n/fr.po
@@ -1,12 +1,12 @@
msgid ""
msgstr ""
-"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-08-12 23:30+0100\n"
"Last-Translator:Tobias Bernot \n"
"Language-Team: French\n"
"Language: fr\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"
@@ -23,14 +23,9 @@ msgid "Use internet connection (mesh VPN)"
msgstr "Utiliser la connection internet (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 "
+"Your internet connection can be used to establish a VPN 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 ""
-"Votre nœud peut partager votre accès internet grâce une communication encryptée "
-"avec d'autres nœuds. Vous pouvez limiter la bande passante utilisée par ceci. "
-"Active cette option, si vous n'avez pas d'autres nœuds atteignables par WLAN "
-"ou si vous voulez partager votre bande passante pour le réseau MESH."
-
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
index 52e2eef8..b6722919 100644
--- 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
@@ -14,7 +14,7 @@ msgid "Use internet connection (mesh VPN)"
msgstr ""
msgid ""
-"Your internet connection can be used to establish an encrypted connection "
+"Your internet connection can be used to establish a VPN 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 "
diff --git a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua
new file mode 100644
index 00000000..16282eba
--- /dev/null
+++ b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/reboot/0100-mesh-vpn.lua
@@ -0,0 +1,49 @@
+local site_i18n = i18n 'gluon-site'
+
+local uci = require("simple-uci").cursor()
+local fs = require "nixio.fs"
+
+local platform = require 'gluon.platform'
+local site = require 'gluon.site'
+local sysconfig = require 'gluon.sysconfig'
+local util = require "gluon.util"
+
+local pretty_hostname = require 'pretty_hostname'
+
+
+local has_fastd = fs.access('/lib/gluon/mesh-vpn/fastd')
+local has_tunneldigger = fs.access('/lib/gluon/mesh-vpn/tunneldigger')
+
+
+local hostname = pretty_hostname.get(uci)
+local contact = uci:get_first("gluon-node-info", "owner", "contact")
+
+local pubkey
+local msg
+
+
+if has_tunneldigger then
+ local tunneldigger_enabled = uci:get_bool("tunneldigger", "mesh_vpn", "enabled")
+ if not tunneldigger_enabled then
+ msg = site_i18n._translate('gluon-config-mode:novpn')
+ end
+elseif has_fastd then
+ local fastd_enabled = uci:get_bool("fastd", "mesh_vpn", "enabled")
+ if fastd_enabled then
+ pubkey = util.trim(util.exec("/etc/init.d/fastd show_key mesh_vpn"))
+ msg = site_i18n._translate('gluon-config-mode:pubkey')
+ else
+ msg = site_i18n._translate('gluon-config-mode:novpn')
+ end
+end
+
+if not msg then return end
+
+renderer.render_string(msg, {
+ pubkey = pubkey,
+ hostname = hostname,
+ site = site,
+ platform = platform,
+ sysconfig = sysconfig,
+ contact = contact,
+})
diff --git a/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
new file mode 100644
index 00000000..1ad50794
--- /dev/null
+++ b/package/gluon-config-mode-mesh-vpn/luasrc/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
@@ -0,0 +1,63 @@
+local fs = require "nixio.fs"
+
+local has_fastd = fs.access('/lib/gluon/mesh-vpn/fastd')
+local has_tunneldigger = fs.access('/lib/gluon/mesh-vpn/tunneldigger')
+
+return function(form, uci)
+ if not (has_fastd or has_tunneldigger) then
+ return
+ end
+
+ local pkg_i18n = i18n 'gluon-config-mode-mesh-vpn'
+
+ local msg = pkg_i18n.translate(
+ 'Your internet connection can be used to establish a ' ..
+ 'VPN 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(Section, nil, msg)
+
+ local o
+
+ local meshvpn = s:option(Flag, "meshvpn", pkg_i18n.translate("Use internet connection (mesh VPN)"))
+ meshvpn.default = uci:get_bool("fastd", "mesh_vpn", "enabled") or uci:get_bool("tunneldigger", "mesh_vpn", "enabled")
+ function meshvpn:write(data)
+ if has_fastd then
+ uci:set("fastd", "mesh_vpn", "enabled", data)
+ end
+ if has_tunneldigger then
+ uci:set("tunneldigger", "mesh_vpn", "enabled", data)
+ end
+ end
+
+ local limit = s:option(Flag, "limit_enabled", pkg_i18n.translate("Limit bandwidth"))
+ limit:depends(meshvpn, true)
+ limit.default = uci:get_bool("simple-tc", "mesh_vpn", "enabled")
+ function limit:write(data)
+ uci:set("simple-tc", "mesh_vpn", "interface")
+ uci:set("simple-tc", "mesh_vpn", "enabled", data)
+ uci:set("simple-tc", "mesh_vpn", "ifname", "mesh-vpn")
+ end
+
+ o = s:option(Value, "limit_ingress", pkg_i18n.translate("Downstream (kbit/s)"))
+ o:depends(limit, true)
+ o.default = uci:get("simple-tc", "mesh_vpn", "limit_ingress")
+ o.datatype = "uinteger"
+ function o:write(data)
+ uci:set("simple-tc", "mesh_vpn", "limit_ingress", data)
+ end
+
+ o = s:option(Value, "limit_egress", pkg_i18n.translate("Upstream (kbit/s)"))
+ o:depends(limit, true)
+ o.default = uci:get("simple-tc", "mesh_vpn", "limit_egress")
+ o.datatype = "uinteger"
+ function o:write(data)
+ uci:set("simple-tc", "mesh_vpn", "limit_egress", data)
+ end
+
+ return {'fastd', 'tunneldigger', 'simple-tc'}
+end
diff --git a/package/gluon-config-mode-theme/Makefile b/package/gluon-config-mode-theme/Makefile
new file mode 100644
index 00000000..38858f0c
--- /dev/null
+++ b/package/gluon-config-mode-theme/Makefile
@@ -0,0 +1,16 @@
+# Copyright (C) 2013 Nils Schneider
+# This is free software, licensed under the Apache 2.0 license.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-config-mode-theme
+PKG_VERSION:=1
+
+include ../gluon.mk
+
+define Package/gluon-config-mode-theme
+ TITLE:=Gluon config mode theme for gluon-web
+ DEPENDS:=+gluon-core +gluon-web
+endef
+
+$(eval $(call BuildPackageGluon,gluon-config-mode-theme))
diff --git a/package/gluon-config-mode-theme/files/lib/gluon/config-mode/view/theme/layout.html b/package/gluon-config-mode-theme/files/lib/gluon/config-mode/view/theme/layout.html
new file mode 100644
index 00000000..874419a7
--- /dev/null
+++ b/package/gluon-config-mode-theme/files/lib/gluon/config-mode/view/theme/layout.html
@@ -0,0 +1,152 @@
+<%#
+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
+-%>
+<%
+ local uci = require("simple-uci").cursor()
+ local fs = require "nixio.fs"
+ local pretty_hostname = require "pretty_hostname"
+
+ local hostname = pretty_hostname.get(uci)
+ local release = fs.readfile("/lib/gluon/release")
+
+ local root = node()
+ local rnode = node(unpack(request))
+
+ local category = request[1]
+ local cattree = category and node(category)
+
+ local function node_visible(node)
+ return (
+ node.title and
+ node.target and
+ (not node.hidden)
+ )
+ end
+
+ local function node_children(node)
+ if not node then return {} end
+
+ local ret = {}
+ for k, v in pairs(node.nodes) do
+ if node_visible(v) then
+ table.insert(ret, k)
+ end
+ end
+
+ table.sort(ret,
+ function(a, b)
+ return (node.nodes[a].order or 100)
+ < (node.nodes[b].order or 100)
+ end
+ )
+ return ret
+ end
+
+ local categories = node_children(root)
+
+ local function append(xs, x)
+ local r = {unpack(xs)}
+ r[#r+1] = x
+ return r
+ end
+
+ local function title(node)
+ return i18n(node.pkg).translate(node.title)
+ end
+
+ local function subtree(prefix, node, name, ...)
+ if not node then return end
+
+ local children = node_children(node)
+ if #children == 0 then return end
+
+ %>
+
+
+ <%
+ for i, v in ipairs(children) do
+ local child = node.nodes[v]
+ local active = (v == name)
+ %>
+
+ <%
+ if not hidenav then
+ menutree(unpack(request))
+ end
+ %>
+
+
+
+
+ <%
+ ok, err = pcall(renderer.render, content, scope, pkg)
+ if not ok then
+ renderer.render('error/500', {message = err}, 'gluon-web')
+ end
+ %>
+
+
+
+
+
diff --git a/package/gluon-config-mode-theme/files/lib/gluon/config-mode/www/static/gluon.css b/package/gluon-config-mode-theme/files/lib/gluon/config-mode/www/static/gluon.css
new file mode 100644
index 00000000..e270b30d
--- /dev/null
+++ b/package/gluon-config-mode-theme/files/lib/gluon/config-mode/www/static/gluon.css
@@ -0,0 +1 @@
+.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}.clear{clear:both}.error{color:#ff0000;background-color:white}#menubar{display:flex;background:#dc0067;color:#ffffff}#menubar a:link.topcat,#menubar a:visited.topcat{position:relative;display:block;padding:0.5em;text-decoration:none;font-size:80%;font-weight:normal;color:white}#menubar a:link.topcat:hover,#menubar a:link.topcat:focus,#menubar a:visited.topcat:hover,#menubar a:visited.topcat:focus{background:#ffb400;color:black}#menubar a:link.topcat.active,#menubar a:visited.topcat.active{background:#ffb400;color:black;font-weight:bold}#menubar div.hostinfo{position:relative;margin:0;padding:0.5em;flex:1;font-weight:bold;font-size:80%}#menubar div.hostinfo a:link,#menubar div.hostinfo a:visited{text-decoration:none;font-weight:bold;color:white}#menubar div.hostinfo a:link:hover,#menubar div.hostinfo a:link:focus,#menubar div.hostinfo a:visited:hover,#menubar div.hostinfo a:visited:focus{text-decoration:underline}#topmenu{list-style:none;margin:0;padding:0}#topmenu li{display:inline-block}#maincontent{padding:0 1em 2em;max-width:60em;min-width:40em;margin:1em auto}#maincontent p{margin-bottom:1em}.gluon-section{margin:0;padding:0;border:none;margin-bottom:1.3em}.gluon-section:last-child{margin-bottom:0.7em}.gluon-section legend{font-size:1.4em;font-weight:bold;position:relative;padding:0;margin-bottom:0.5em}.gluon-section h2{margin:0em 0 0.5em -0.5em !important}.gluon-section h3{text-decoration:none !important;font-weight:bold !important;color:#555555 !important;margin:0.25em !important;font-size:100% !important}.gluon-section-descr{margin-bottom:2em}input:placeholder{color:#aaaaaa}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;position:relative;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=radio]{display:none}input[type=radio]+label{display:inline-block;position:relative;width:0.8em;height:0.8em;padding:0.5em;margin:0.2em 0.2em 0.2em 0.1em;border:none;background:#ffe199;vertical-align:middle;border-radius:50%}input[type=radio]:checked+label:after{content:'•';color:#dc0067;vertical-align:middle;position:absolute;top:50%;left:0;margin-top:-0.4em;width:100%;text-align:center;font-size:2em}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;margin-top:1px;margin-bottom:2px;box-sizing:content-box;outline:0}option{color:#003247;background:#ffe199}input[type=image]{border:none}select,input[type=text],input[type=password]{min-width:20em}input.gluon-button{display:inline-block;zoom:1;line-height:normal;white-space:nowrap;vertical-align:baseline;text-align:center;cursor:pointer;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;transition:0.1s linear box-shadow;margin-left:0.5em;background-repeat:no-repeat}input.gluon-button::-moz-focus-inner{padding:0;border:0}input.gluon-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.gluon-button:focus{outline:0}input.gluon-button:hover,input.gluon-button:focus{background-image:linear-gradient(transparent, rgba(0,0,0,0.05) 40%, rgba(0,0,0,0.1))}input.gluon-button[disabled]{border:none;background-image:none;opacity:0.40;cursor:not-allowed;box-shadow:none}input.gluon-button-reset{background-color:#e30;color:#fff}input.gluon-button-submit{background-color:#009ee0;color:#fff}.gluon-input-invalid{background:#e30 !important;color:white}div.gluon-section-remove input{border-bottom:none}textarea{margin-left:-1px;margin-bottom:0.5em}.gluon-section .gluon-rowstyle-1 h3{background-color:#eeeeff;color:#555555}.gluon-rowstyle-2{color:#000000}div.gluon-value{display:flex;flex-direction:row;margin-bottom:0.5em}.gluon-section-node .gluon-value:last-child{margin-bottom:0}.gluon-value-title{flex:2;text-align:right;padding-top:0.39em;padding-right:1em;font-weight:bold}div.gluon-value-field{flex:3;position:relative}div.gluon-value-field input,div.gluon-value-field select,div.gluon-value-field input+label{position:relative}div.gluon-value-field-text{flex:3;padding-top:0.39em}div.gluon-value-field-long{flex:10;position:relative;margin-top:0.65em}div.gluon-value-field-long input,div.gluon-value-field-long select,div.gluon-value-field-long input+label{position:relative}div.gluon-value-field-long-after{flex:2}div.gluon-value-description{font-size:8pt}div.gluon-section-create{clear:left;white-space:nowrap;vertical-align:top}div.gluon-section-create .gluon-button{margin:0.25em}input.gluon-section-create-name{margin-right:-0.25em}div.gluon-map-descr{margin-bottom:1em}.gluon-map-descr:empty,.gluon-section-descr:empty{display:none}.gluon-map-descr,.gluon-section-descr,.gluon-page-actions{padding:1em;background:#ececec}.gluon-page-actions{text-align:right;display:flex;flex-flow:row-reverse}div.gluon-optionals{padding:0.25em;border-bottom:1px dotted #bbbbbb}div.gluon-section-remove{float:right}.gluon-section-node{clear:both;position:relative;border:none}.gluon-section-node-tabbed{border-top-left-radius:0}div.gluon-error{font-size:95%;font-weight:bold;color:#ff0000;background-color:#ffffff}.gluon-value-error input,.gluon-value-error select{background-color:#ffcccc}.gluon-section-error{color:red;background-color:white;font-size:95%;border:1px dotted red;margin:3px;padding:3px}.gluon-value-field var{color:#2222FF}.gluon-add:after,.gluon-remove:after{cursor:pointer;display:inline-block;text-align:center;vertical-align:middle;font-size:180%;width:1.2em;height:1em}.gluon-add{color:#008000;position:relative;left:21em}input+.gluon-add{left:0;top:0.04em}.gluon-add:first-child{top:0.53em;left:-0.08em}.gluon-add:after{content:'+'}.gluon-remove{color:#800000;position:relative;top:-0.03em}.gluon-remove:after{content:'–'}.left{text-align:left !important}.right{text-align:right !important}.inline{display:inline}.error500{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}.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-config-mode-theme/i18n/de.po b/package/gluon-config-mode-theme/i18n/de.po
new file mode 100644
index 00000000..b18a346d
--- /dev/null
+++ b/package/gluon-config-mode-theme/i18n/de.po
@@ -0,0 +1,20 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2013-03-29 12:13+0200\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 "JavaScript required!"
+msgstr "JavaScript benötigt!"
+
+msgid ""
+"You must enable JavaScript in your browser or the web interface will not "
+"work properly."
+msgstr "Bitte aktiviere JavaScript in deinem Browser, damit das Webinterface "
+"korrekt funktionieren kann."
diff --git a/package/gluon-config-mode-theme/i18n/fr.po b/package/gluon-config-mode-theme/i18n/fr.po
new file mode 100644
index 00000000..95e73b1a
--- /dev/null
+++ b/package/gluon-config-mode-theme/i18n/fr.po
@@ -0,0 +1,19 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2013-12-22 17:11+0200\n"
+"Last-Translator: goofy \n"
+"Language-Team: French\n"
+"Language: fr\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 "JavaScript required!"
+msgstr ""
+
+msgid ""
+"You must enable JavaScript in your browser or the web interface will not "
+"work properly."
+msgstr ""
diff --git a/package/gluon-config-mode-theme/i18n/gluon-config-mode-theme.pot b/package/gluon-config-mode-theme/i18n/gluon-config-mode-theme.pot
new file mode 100644
index 00000000..1676d857
--- /dev/null
+++ b/package/gluon-config-mode-theme/i18n/gluon-config-mode-theme.pot
@@ -0,0 +1,10 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "JavaScript required!"
+msgstr ""
+
+msgid ""
+"You must enable JavaScript in your browser or the web interface will not "
+"work properly."
+msgstr ""
diff --git a/package/gluon-config-mode-theme/sass/gluon.scss b/package/gluon-config-mode-theme/sass/gluon.scss
new file mode 100644
index 00000000..8b548e8e
--- /dev/null
+++ b/package/gluon-config-mode-theme/sass/gluon.scss
@@ -0,0 +1,640 @@
+/*
+ ATTENTION: This file is not compiled when building gluon.
+ The compiled version is at ../files/lib/gluon/config-mode/www/static/gluon.css
+
+ Use sass like this to update it:
+
+ sass --sourcemap=none -C -t compressed sass/gluon.scss files/lib/gluon/config-mode/www/static/gluon.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;
+ 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 */
+ 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: 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;
+}
+
+.clear {
+ clear: both;
+}
+
+.error {
+ color: #ff0000;
+ background-color: white;
+}
+
+#menubar {
+ display: flex;
+ background: $ffmagenta;
+ color: #ffffff;
+}
+
+#menubar a:link.topcat,
+#menubar a:visited.topcat {
+ position: relative;
+ display: block;
+ padding: 0.5em;
+ text-decoration: none;
+ font-size: 80%;
+ font-weight: normal;
+ color: white;
+
+ &:hover, &:focus {
+ background: $ffyellow;
+ color: black;
+ }
+
+ &.active {
+ background: $ffyellow;
+ color: black;
+ font-weight: bold;
+ }
+}
+
+#menubar div.hostinfo {
+ position: relative;
+ margin: 0;
+ padding: 0.5em;
+ flex: 1;
+ font-weight: bold;
+ font-size: 80%;
+}
+
+#menubar div.hostinfo a {
+ &:link, &:visited {
+ text-decoration: none;
+ font-weight: bold;
+ color: white;
+
+ &:hover, &:focus {
+ text-decoration: underline;
+ }
+ }
+}
+
+#topmenu {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+#topmenu li {
+ display: inline-block;
+}
+
+#maincontent {
+ padding: 0 1em 2em;
+ max-width: 60em;
+ min-width: 40em;
+ margin: 1em auto;
+}
+
+#maincontent p {
+ margin-bottom: 1em;
+}
+
+.gluon-section {
+ margin: 0;
+ padding: 0;
+ border: none;
+ margin-bottom: 1.3em;
+}
+
+.gluon-section:last-child {
+ margin-bottom: 0.7em;
+}
+
+.gluon-section legend {
+ font-size: 1.4em;
+ font-weight: bold;
+ position: relative;
+ padding: 0;
+ margin-bottom: 0.5em;
+}
+
+.gluon-section h2 {
+ margin: 0em 0 0.5em -0.5em !important;
+}
+
+.gluon-section h3 {
+ text-decoration: none !important;
+ font-weight: bold !important;
+ color: #555555 !important;
+ margin: 0.25em !important;
+ font-size: 100% !important;
+}
+
+.gluon-section-descr {
+ margin-bottom: 2em;
+}
+
+input:placeholder {
+ color: #aaaaaa;
+}
+
+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;
+ position: relative;
+ 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=radio] {
+ display: none;
+
+ & + label {
+ display: inline-block;
+ position: relative;
+ width: 0.8em;
+ height: 0.8em;
+ padding: 0.5em;
+ margin: 0.2em 0.2em 0.2em 0.1em;
+ border: none;
+ background: lighten($ffyellow, 30);
+ vertical-align: middle;
+ border-radius: 50%;
+ }
+
+ &:checked + label:after {
+ content: '•';
+ color: $ffmagenta;
+ vertical-align: middle;
+ position: absolute;
+ top: 50%;
+ left: 0;
+ margin-top: -0.4em;
+ width: 100%;
+ text-align: center;
+ font-size: 2em;
+ }
+}
+
+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;
+ margin-top: 1px;
+ margin-bottom: 2px;
+ box-sizing: content-box;
+ outline: 0;
+}
+
+option {
+ color: darken($ffzusatz, 30);
+ background: lighten($ffyellow, 30);
+}
+
+input[type=image] {
+ border: none;
+}
+
+select,
+input[type=text],
+input[type=password] {
+ min-width: 20em;
+}
+
+input.gluon-button {
+ @include button;
+
+ margin-left: 0.5em;
+ background-repeat: no-repeat;
+}
+
+input.gluon-button-reset {
+ background-color: $red;
+ color: #fff;
+}
+input.gluon-button-submit {
+ @include button-primary;
+}
+
+.gluon-input-invalid {
+ background: $red !important;
+ color: white;
+}
+
+div.gluon-section-remove input {
+ border-bottom: none;
+}
+
+textarea {
+ margin-left: -1px;
+ margin-bottom: 0.5em;
+}
+
+.gluon-section .gluon-rowstyle-1 h3 {
+ background-color: #eeeeff;
+ color: #555555;
+}
+
+.gluon-rowstyle-2 {
+ color: #000000;
+}
+
+div.gluon-value {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 0.5em;
+}
+
+.gluon-section-node .gluon-value:last-child {
+ margin-bottom: 0;
+}
+
+.gluon-value-title {
+ flex: 2;
+ text-align: right;
+ padding-top: 0.39em;
+ padding-right: 1em;
+ font-weight: bold;
+}
+
+div.gluon-value-field {
+ flex: 3;
+ position: relative;
+
+ input, select, input + label {
+ position: relative;
+ }
+}
+
+div.gluon-value-field-text {
+ flex: 3;
+ padding-top: 0.39em;
+}
+
+div.gluon-value-field-long {
+ flex: 10;
+ position: relative;
+ margin-top: 0.65em;
+
+ input, select, input + label {
+ position: relative;
+ }
+}
+
+div.gluon-value-field-long-after {
+ flex: 2;
+}
+
+div.gluon-value-description {
+ font-size: 8pt;
+}
+
+div.gluon-section-create {
+ clear: left;
+ white-space: nowrap;
+ vertical-align: top;
+}
+
+div.gluon-section-create .gluon-button {
+ margin: 0.25em;
+}
+
+input.gluon-section-create-name {
+ margin-right: -0.25em;
+}
+
+div.gluon-map-descr {
+ margin-bottom: 1em;
+}
+
+.gluon-map-descr:empty, .gluon-section-descr:empty {
+ display: none;
+}
+
+.gluon-map-descr, .gluon-section-descr, .gluon-page-actions {
+ padding: 1em;
+ background: #ececec;
+}
+
+.gluon-page-actions {
+ text-align: right;
+ display: flex;
+ flex-flow: row-reverse;
+}
+
+div.gluon-optionals {
+ padding: 0.25em;
+ border-bottom: 1px dotted #bbbbbb;
+}
+
+div.gluon-section-remove {
+ float: right;
+}
+
+.gluon-section-node {
+ clear: both;
+ position: relative;
+ border: none;
+}
+
+.gluon-section-node-tabbed {
+ border-top-left-radius: 0;
+}
+
+div.gluon-error {
+ font-size: 95%;
+ font-weight: bold;
+ color: #ff0000;
+ background-color: #ffffff;
+}
+
+.gluon-value-error input,
+.gluon-value-error select {
+ background-color: #ffcccc;
+}
+
+.gluon-section-error {
+ color: red;
+ background-color: white;
+ font-size: 95%;
+ border: 1px dotted red;
+ margin: 3px;
+ padding: 3px;
+}
+
+.gluon-value-field var {
+ color: #2222FF;
+}
+
+.gluon-add:after, .gluon-remove:after {
+ cursor: pointer;
+ display: inline-block;
+ text-align: center;
+ vertical-align: middle;
+ font-size: 180%;
+ width: 1.2em;
+ height: 1em;
+}
+
+.gluon-add {
+ color: #008000;
+
+ position: relative;
+ left: 21em;
+
+ input + & {
+ left: 0;
+ top: 0.04em;
+ }
+
+ &:first-child {
+ top: 0.53em;
+ left: -0.08em;
+ }
+
+ &:after {
+ content: '+';
+ }
+}
+
+.gluon-remove {
+ color: #800000;
+
+ position: relative;
+ top: -0.03em;
+
+ &:after {
+ content: '–';
+ }
+}
+
+.left {
+ text-align: left !important;
+}
+
+.right {
+ text-align: right !important;
+}
+
+.inline {
+ display: inline;
+}
+
+.error500 {
+ 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;
+}
+
+
+.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-core/Config.in b/package/gluon-core/Config.in
new file mode 100644
index 00000000..9def3c29
--- /dev/null
+++ b/package/gluon-core/Config.in
@@ -0,0 +1,338 @@
+config KERNEL_NET_IP_TUNNEL
+ bool
+
+config KERNEL_NET_UDP_TUNNEL
+ bool
+
+config KERNEL_VXLAN
+ bool
+ select KERNEL_NET_IP_TUNNEL
+ select KERNEL_NET_UDP_TUNNEL
+
+config KERNEL_VETH
+ bool
+
+
+# for fastd
+
+config KERNEL_TUN
+ bool
+
+
+# for tunneldigger
+
+config KERNEL_L2TP_V3
+ bool
+
+config KERNEL_L2TP_IP
+ bool
+
+config KERNEL_L2TP_ETH
+ bool
+
+config KERNEL_L2TP
+ bool
+ select KERNEL_L2TP_V3
+ select KERNEL_L2TP_IP
+ select KERNEL_L2TP_ETH
+
+
+# for simple-tc
+
+config KERNEL_NET_SCHED
+ bool
+
+config KERNEL_NET_CLS
+ bool
+ select KERNEL_NET_SCHED
+
+config KERNEL_NET_CLS_ACT
+ bool
+ select KERNEL_NET_CLS
+
+config KERNEL_NET_CLS_BASIC
+ bool
+ select KERNEL_NET_CLS
+
+config KERNEL_NET_SCH_TBF
+ bool
+ select KERNEL_NET_SCHED
+
+config KERNEL_NET_SCH_INGRESS
+ bool
+ select KERNEL_NET_CLS_ACT
+
+
+# for batman-adv
+
+config KERNEL_DUMMY
+ bool
+
+config KERNEL_CRC16
+ bool
+
+config KERNEL_LIBCRC32C
+ bool
+
+
+# Netfilter modules used by ebtables and fw3
+
+config KERNEL_NETFILTER
+ bool
+
+config KERNEL_NETFILTER_ADVANCED
+ bool
+ select KERNEL_NETFILTER
+
+config KERNEL_NETFILTER_XTABLES
+ bool
+ select KERNEL_NETFILTER_ADVANCED
+
+config KERNEL_BRIDGE_NF_EBTABLES
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_BRIDGE_EBT_T_FILTER
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+config KERNEL_BRIDGE_EBT_T_NAT
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+config KERNEL_BRIDGE_EBT_ARP
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+config KERNEL_BRIDGE_EBT_IP
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+config KERNEL_BRIDGE_EBT_IP6
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+config KERNEL_BRIDGE_EBT_LIMIT
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+config KERNEL_BRIDGE_EBT_MARK
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+config KERNEL_BRIDGE_EBT_MARK_T
+ bool
+ select KERNEL_BRIDGE_NF_EBTABLES
+
+
+# Not all of the following modules are really required for Gluon, but fw3 pulls
+# them in, so we add them to the kernel config to reduce the number of loaded
+# modules
+
+# kmod-nf-conntrack
+
+config KERNEL_NF_CONNTRACK
+ bool
+ select KERNEL_NETFILTER_ADVANCED
+
+config KERNEL_NF_CONNTRACK_RTCACHE
+ bool
+ select KERNEL_NF_CONNTRACK
+
+config KERNEL_NF_CONNTRACK_IPV4
+ bool
+ select KERNEL_NF_CONNTRACK
+
+
+# kmod-nf-conntrack6
+
+config KERNEL_NF_CONNTRACK_IPV6
+ bool
+ select KERNEL_NF_CONNTRACK
+
+
+# kmod-nf-ipt
+
+config KERNEL_NF_REJECT_IPV4
+ bool
+ select KERNEL_NETFILTER_ADVANCED
+
+config KERNEL_IP_NF_IPTABLES
+ bool
+ select KERNEL_NETFILTER_XTABLES
+ select KERNEL_NF_REJECT_IPV4
+
+
+# kmod-nf-ipt6
+
+config KERNEL_NF_REJECT_IPV6
+ bool
+ select KERNEL_NETFILTER_ADVANCED
+
+config KERNEL_IP6_NF_IPTABLES
+ bool
+ select KERNEL_NETFILTER_XTABLES
+ select KERNEL_NF_REJECT_IPV6
+
+
+# kmod-nf-nat
+
+config KERNEL_NF_NAT
+ bool
+ select KERNEL_NF_CONNTRACK
+
+config KERNEL_NF_NAT_REDIRECT
+ bool
+ select KERNEL_NF_NAT
+
+config KERNEL_NF_NAT_IPV4
+ bool
+ select KERNEL_NETFILTER_ADVANCED
+ select KERNEL_NF_NAT
+
+config KERNEL_NF_NAT_MASQUERADE_IPV4
+ bool
+ select KERNEL_NF_NAT_IPV4
+
+
+# kmod-ipt-core
+
+config KERNEL_IP_NF_FILTER
+ bool
+ select KERNEL_IP_NF_IPTABLES
+
+config KERNEL_IP_NF_MANGLE
+ bool
+ select KERNEL_IP_NF_IPTABLES
+
+config KERNEL_IP_NF_TARGET_REJECT
+ bool
+ select KERNEL_IP_NF_FILTER
+
+config KERNEL_NETFILTER_XT_MATCH_LIMIT
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MATCH_MAC
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MATCH_MULTIPORT
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MATCH_COMMENT
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MATCH_TIME
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MARK
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_TARGET_LOG
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_TARGET_TCPMSS
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+# kmod-ip6tables
+
+config KERNEL_IP6_NF_FILTER
+ bool
+ select KERNEL_IP6_NF_IPTABLES
+
+config KERNEL_IP6_NF_MANGLE
+ bool
+ select KERNEL_IP6_NF_IPTABLES
+
+config KERNEL_IP6_NF_TARGET_REJECT
+ bool
+ select KERNEL_IP6_NF_IPTABLES
+
+
+# kmod-ipt-conntrack
+
+config KERNEL_NETFILTER_XT_MATCH_STATE
+ bool
+ select KERNEL_NETFILTER_XTABLES
+ select KERNEL_NF_CONNTRACK
+
+config KERNEL_NETFILTER_XT_MATCH_CONNTRACK
+ bool
+ select KERNEL_NETFILTER_XTABLES
+ select KERNEL_NF_CONNTRACK
+
+
+# kmod-ipt-nat
+
+config KERNEL_IP_NF_NAT
+ bool
+ select KERNEL_NF_NAT
+ select KERNEL_IP_NF_IPTABLES
+
+config KERNEL_IP_NF_TARGET_MASQUERADE
+ bool
+ select KERNEL_NF_NAT_MASQUERADE_IPV4
+ select KERNEL_IP_NF_NAT
+
+config KERNEL_IP_NF_TARGET_REDIRECT
+ bool
+ select KERNEL_NF_NAT
+ select KERNEL_IP_NF_NAT
+
+
+# kmod-ipt-extra
+
+config KERNEL_NETFILTER_XT_MATCH_ADDRTYPE
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MATCH_OWNER
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MATCH_PKTTYPE
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+config KERNEL_NETFILTER_XT_MATCH_QUOTA
+ bool
+ select KERNEL_NETFILTER_XTABLES
+
+
+config GLUON_SPECIALIZE_KERNEL
+ bool "Specialize kernel for Gluon"
+ select KERNEL_VXLAN
+ select KERNEL_NF_CONNTRACK_RTCACHE
+ select KERNEL_NF_CONNTRACK_IPV4
+ select KERNEL_NF_CONNTRACK_IPV6
+ select KERNEL_IP_NF_IPTABLES
+ select KERNEL_IP6_NF_IPTABLES
+ select KERNEL_NF_NAT_REDIRECT
+ select KERNEL_NF_NAT_IPV4
+ select KERNEL_NF_NAT_MASQUERADE_IPV4
+ select KERNEL_IP_NF_FILTER
+ select KERNEL_IP_NF_MANGLE
+ select KERNEL_IP_NF_TARGET_REJECT
+ select KERNEL_IP6_NF_FILTER
+ select KERNEL_IP6_NF_MANGLE
+ select KERNEL_IP6_NF_TARGET_REJECT
+ select KERNEL_NETFILTER_XT_MATCH_LIMIT
+ select KERNEL_NETFILTER_XT_MATCH_MAC
+ select KERNEL_NETFILTER_XT_MATCH_MULTIPORT
+ select KERNEL_NETFILTER_XT_MATCH_COMMENT
+ select KERNEL_NETFILTER_XT_MATCH_TIME
+ select KERNEL_NETFILTER_XT_MARK
+ select KERNEL_NETFILTER_XT_TARGET_LOG
+ select KERNEL_NETFILTER_XT_TARGET_TCPMSS
+ select KERNEL_NETFILTER_XT_MATCH_STATE
+ select KERNEL_NETFILTER_XT_MATCH_CONNTRACK
+ select KERNEL_IP_NF_NAT
+ select KERNEL_IP_NF_TARGET_MASQUERADE
+ select KERNEL_IP_NF_TARGET_REDIRECT
diff --git a/package/gluon-core/Makefile b/package/gluon-core/Makefile
index 725fb737..c0600c1f 100644
--- a/package/gluon-core/Makefile
+++ b/package/gluon-core/Makefile
@@ -1,46 +1,35 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-core
-PKG_VERSION:=3
-PKG_RELEASE:=$(GLUON_VERSION)
+
+GLUON_VERSION = $(shell git describe --always --dirty=+ 2>/dev/null || echo unknown)
+PKG_VERSION:=$(if $(DUMP),x,$(GLUON_VERSION))
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-include $(GLUONDIR)/include/package.mk
+include ../gluon.mk
+include $(INCLUDE_DIR)/cmake.mk
define Package/gluon-core
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Base files of Gluon
- DEPENDS:=+gluon-site +libgluonutil +lua-platform-info +lua-hash +luci-base +luci-lib-jsonc +odhcp6c +firewall
+ DEPENDS:= \
+ +gluon-site +libgluonutil +libiwinfo-lua +lua-platform-info +lua-simple-uci +lua-hash +lua-jsonc \
+ +luci-lib-nixio +vxlan +odhcp6c +firewall +pretty-hostname
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
+define Package/gluon-core/config
+ source "$(SOURCE)/Config.in"
endef
define Package/gluon-core/install
- $(CP) ./files/* $(1)/
- $(SED) 's/__GLUON_OPENWRT_FEEDS__/{$(GLUON_OPENWRT_FEEDS:%="%",)}/' $(1)/lib/gluon/upgrade/500-opkg
+ $(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon
- echo "$(GLUON_VERSION)" > $(1)/lib/gluon/gluon-version
+ 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))
+$(eval $(call BuildPackageGluon,gluon-core))
diff --git a/package/gluon-core/check_site.lua b/package/gluon-core/check_site.lua
index 1647d778..0977b086 100644
--- a/package/gluon-core/check_site.lua
+++ b/package/gluon-core/check_site.lua
@@ -1,32 +1,80 @@
-need_string 'site_code'
-need_string 'site_name'
+need_string(in_site({'site_code'}))
+need_string(in_site({'site_name'}))
-if need_table('opkg', nil, false) then
- need_string('opkg.openwrt', false)
+-- this_domain() returns nil when multidomain support is disabled
+if this_domain() then
+ function need_domain_name(path)
+ need_string(path)
+ need(path, function(default_domain)
+ local f = io.open(os.getenv('IPKG_INSTROOT') .. '/lib/gluon/domains/' .. default_domain .. '.json')
+ if not f then return false end
+ f:close()
+ return true
+ end, nil, 'be a valid domain name')
+ end
+ need_domain_name(in_site({'default_domain'}))
- function check_repo(k, _)
- -- this is not actually a uci name, but using the same naming rules here is fine
- assert_uci_name(k)
-
- need_string(string.format('opkg.extra[%q]', k))
- end
-
- need_table('opkg.extra', check_repo, false)
+ need_table(in_domain({'domain_names'}), function(domain)
+ need_alphanumeric_key(domain)
+ need_string(domain)
+ end)
+ need_string(in_domain({'domain_names', this_domain()}))
end
-need_string('hostname_prefix', false)
-need_string 'timezone'
+need_string_match(in_domain({'domain_seed'}), '^' .. ('%x'):rep(64) .. '$')
-need_string_array('ntp_servers', false)
+need_string({'opkg', 'lede'}, false)
+need_table({'opkg', 'extra'}, function(extra_repo)
+ need_alphanumeric_key(extra_repo)
+ need_string(extra_repo)
+end, false)
-need_string_match('prefix4', '^%d+.%d+.%d+.%d+/%d+$')
-need_string_match('prefix6', '^[%x:]+/%d+$')
+need_string(in_site({'hostname_prefix'}), false)
+need_string(in_site({'timezone'}))
+
+need_string_array({'ntp_servers'}, false)
+
+need_string_match(in_domain({'prefix6'}), '^[%x:]+/64$')
for _, config in ipairs({'wifi24', 'wifi5'}) do
- if need_table(config, nil, false) then
- need_string('regdom') -- regdom is only required when wifi24 or wifi5 is configured
+ if need_table({config}, nil, false) then
+ need_string(in_site({'regdom'})) -- regdom is only required when wifi24 or wifi5 is configured
- need_number(config .. '.channel')
- end
+ need_number({config, 'channel'})
+
+ local rates = {1000, 2000, 5500, 6000, 9000, 11000, 12000, 18000, 24000, 36000, 48000, 54000}
+ local supported_rates = need_array_of(in_site({config, 'supported_rates'}), rates, false)
+ need_array_of({config, 'basic_rate'}, supported_rates or rates, supported_rates ~= nil)
+
+ if need_table({config, 'ibss'}, nil, false) then
+ need_string_match(in_domain({config, 'ibss', 'ssid'}), '^' .. ('.?'):rep(32) .. '$')
+ need_string_match(in_domain({config, 'ibss', 'bssid'}), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$')
+ need_one_of({config, 'ibss', 'mcast_rate'}, supported_rates or rates, false)
+ need_number({config, 'ibss', 'vlan'}, false)
+ need_boolean({config, 'ibss', 'disabled'}, false)
+ end
+
+ if need_table({config, 'mesh'}, nil, false) then
+ need_string_match(in_domain({config, 'mesh', 'id'}), '^' .. ('.?'):rep(32) .. '$')
+ need_one_of({config, 'mesh', 'mcast_rate'}, supported_rates or rates, false)
+ need_boolean({config, 'mesh', 'disabled'}, false)
+ end
+ end
end
+
+need_boolean(in_site({'poe_passthrough'}), false)
+
+if need_table({'dns'}, nil, false) then
+ need_string_array_match({'dns', 'servers'}, '^[%x:]+$')
+end
+
+need_string_array(in_domain({'next_node', 'name'}), false)
+need_string_match(in_domain({'next_node', 'ip6'}), '^[%x:]+$', false)
+need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', false)
+
+need_boolean(in_domain({'mesh', 'vxlan'}), false)
+
+need_boolean(in_site({'mesh_on_wan'}), false)
+need_boolean(in_site({'mesh_on_lan'}), false)
+need_boolean(in_site({'single_as_lan'}), false)
diff --git a/package/gluon-core/files/etc/config/gluon b/package/gluon-core/files/etc/config/gluon
new file mode 100644
index 00000000..270547ac
--- /dev/null
+++ b/package/gluon-core/files/etc/config/gluon
@@ -0,0 +1 @@
+config core 'core'
diff --git a/package/gluon-core/files/etc/hotplug.d/ieee80211/01-gluon-core-codel-memusage b/package/gluon-core/files/etc/hotplug.d/ieee80211/01-gluon-core-codel-memusage
new file mode 100755
index 00000000..07da10c9
--- /dev/null
+++ b/package/gluon-core/files/etc/hotplug.d/ieee80211/01-gluon-core-codel-memusage
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+if [ "${ACTION}" = 'add' ]; then
+ RAM=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
+ if [ "$RAM" -le $((32*1024)) ]; then
+ echo 'fq_memory_limit 262144' > "/sys/kernel/debug/ieee80211/$DEVICENAME/aqm"
+ fi
+fi
diff --git a/package/gluon-core/files/etc/sysctl.d/30-gluon-core.conf b/package/gluon-core/files/etc/sysctl.d/30-gluon-core.conf
new file mode 100644
index 00000000..12d569a0
--- /dev/null
+++ b/package/gluon-core/files/etc/sysctl.d/30-gluon-core.conf
@@ -0,0 +1 @@
+vm.panic_on_oom=1
diff --git a/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade b/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade
index a12ce78e..bd3a51d7 100755
--- a/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade
+++ b/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade
@@ -1,5 +1,5 @@
#!/bin/sh
-for script in /lib/gluon/upgrade/*; do
- "$script"
-done
+gluon-reconfigure
+
+exit 0
diff --git a/package/gluon-core/files/lib/gluon/core/mesh/setup.d/.keep b/package/gluon-core/files/lib/gluon/core/mesh/setup.d/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/package/gluon-core/files/lib/gluon/core/mesh/teardown.d/.keep b/package/gluon-core/files/lib/gluon/core/mesh/teardown.d/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/package/gluon-core/files/lib/gluon/upgrade/020-interfaces b/package/gluon-core/files/lib/gluon/upgrade/020-interfaces
deleted file mode 100755
index 34e1c8bb..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/020-interfaces
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/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', 'uap-pro', 'unifiac-pro'}) 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')
-end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/030-system b/package/gluon-core/files/lib/gluon/upgrade/030-system
deleted file mode 100755
index 0086a5dc..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/030-system
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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')
-end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/100-core-reset-sysctl b/package/gluon-core/files/lib/gluon/upgrade/100-core-reset-sysctl
new file mode 100755
index 00000000..6f6f4316
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/100-core-reset-sysctl
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# This script can be removed after Gluon v2018.2
+
+# Check for a random line that always was in /etc/sysctl.conf
+if grep -qxF 'net.ipv4.ip_forward=1' /etc/sysctl.conf; then
+ echo '# Defaults are configured in /etc/sysctl.d/* and can be customized in this file' >/etc/sysctl.conf
+fi
diff --git a/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq b/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq
deleted file mode 100755
index 3636fbf6..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/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
deleted file mode 100755
index bf87d47f..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/110-network
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/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',
- igmp_snooping = 0,
- 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,
- sourcefilter = 0,
- }
-)
-
-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:section('firewall', 'rule', 'wan_igmp',
- {
- name = 'Allow-IGMP',
- src = 'wan',
- proto = 'igmp',
- family = 'ipv4',
- target = 'ACCEPT',
- }
-)
-
-uci:section('firewall', 'rule', 'wan_mld',
- {
- name = 'Allow-MLD',
- src = 'wan',
- proto = 'icmp',
- src_ip = 'fe80::/10',
- icmp_type = { '130/0', '131/0', '132/0', '143/0', },
- family = 'ipv6',
- target = 'ACCEPT',
- }
-)
-
-uci:save('firewall')
-
-
-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
deleted file mode 100755
index f38c1f5d..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/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')
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
deleted file mode 100755
index 48cfc5a8..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/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
deleted file mode 100755
index abd45381..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/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')
diff --git a/package/gluon-core/files/lib/gluon/upgrade/200-wireless b/package/gluon-core/files/lib/gluon/upgrade/200-wireless
deleted file mode 100755
index 5a4ec0a4..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/200-wireless
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/lua
-
-local util = require 'gluon.util'
-local uci = require('luci.model.uci').cursor()
-local site = require 'gluon.site_config'
-local sysconfig = require 'gluon.sysconfig'
-
--- Initial
-if not sysconfig.gluon_version then
- uci:delete_all('wireless', 'wifi-iface')
-end
-
-local function get_channel(radio, config)
- if uci:get_first('gluon-core', 'wireless', 'preserve_channels') then
- return uci:get('wireless', radio, 'channel') or config.channel
- else
- return config.channel
- end
-end
-
-local function configure_radio(radio, index, config)
- if config then
- local channel = get_channel(radio, config)
-
- uci:delete('wireless', radio, 'disabled')
-
- uci:set('wireless', radio, 'channel', channel)
- uci:set('wireless', radio, 'htmode', 'HT20')
- uci:set('wireless', radio, 'country', site.regdom)
- end
-end
-
-util.iterate_radios(configure_radio)
-
-uci:save('wireless')
diff --git a/package/gluon-core/files/lib/gluon/upgrade/500-opkg b/package/gluon-core/files/lib/gluon/upgrade/500-opkg
deleted file mode 100755
index 4380050c..00000000
--- a/package/gluon-core/files/lib/gluon/upgrade/500-opkg
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/lua
-
-local fs = require 'nixio.fs'
-local site = require 'gluon.site_config'
-local util = require 'luci.util'
-
-
-local subst = {}
-
-subst['%%v'] = util.trim(fs.readfile('/etc/openwrt_version'))
-subst['%%n'], subst['%%S'] = util.exec('. /etc/openwrt_release; echo $DISTRIB_CODENAME; echo $DISTRIB_TARGET'):match('([^\n]*)\n([^\n]*)')
-subst['%%GS'] = site.site_code
-subst['%%GV'] = util.trim(fs.readfile('/lib/gluon/gluon-version'))
-subst['%%GR'] = util.trim(fs.readfile('/lib/gluon/release'))
-
-function replace_patterns(url)
- for k, v in pairs(subst) do
- url = url:gsub(k, v)
- end
-
- return url
-end
-
-
-if site.opkg then
- if site.opkg.openwrt then
- local url = replace_patterns(site.opkg.openwrt)
- local f = io.open('/etc/opkg/distfeeds.conf', 'w')
-
- for _, v in ipairs(__GLUON_OPENWRT_FEEDS__) do
- f:write(replace_patterns(string.format('src/gz %%n_%s %s/%s\n', v, site.opkg.openwrt, v)))
- end
-
- f:close()
- end
-
- if site.opkg.extra and next(site.opkg.extra) then
- local f = io.open('/etc/opkg/gluon.conf', 'w')
-
- for k, v in pairs(site.opkg.extra) do
- f:write(string.format('src/gz %s %s\n', k, replace_patterns(v)))
- end
-
- f:close()
-
- else
- os.remove('/etc/opkg/gluon.conf')
- end
-end
diff --git a/package/gluon-core/files/lib/netifd/proto/gluon_mesh.sh b/package/gluon-core/files/lib/netifd/proto/gluon_mesh.sh
new file mode 100755
index 00000000..52d6abec
--- /dev/null
+++ b/package/gluon-core/files/lib/netifd/proto/gluon_mesh.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+. /lib/functions.sh
+. ../netifd-proto.sh
+init_proto "$@"
+
+proto_gluon_mesh_init_config() {
+ proto_config_add_boolean fixed_mtu
+ proto_config_add_boolean transitive
+}
+
+proto_gluon_mesh_setup() {
+ export CONFIG="$1"
+ export IFNAME="$2"
+
+ local fixed_mtu transitive
+ json_get_vars fixed_mtu transitive
+
+ export FIXED_MTU="${fixed_mtu:-0}"
+ export TRANSITIVE="${transitive:-0}"
+
+ for script in /lib/gluon/core/mesh/setup.d/*; do
+ [ ! -x "$script" ] || "$script"
+ done
+
+ proto_init_update "$IFNAME" 1
+
+ proto_add_data
+ json_add_boolean fixed_mtu "$FIXED_MTU"
+ json_add_boolean transitive "$TRANSITIVE"
+ [ "$IFNAME" != 'br-wan' ] && json_add_string zone 'mesh'
+ proto_close_data
+ proto_send_update "$CONFIG"
+
+ for script in /lib/gluon/core/mesh/post-setup.d/*; do
+ [ ! -x "$script" ] || "$script"
+ done
+}
+
+proto_gluon_mesh_teardown() {
+ export CONFIG="$1"
+ export IFNAME="$2"
+
+ for script in /lib/gluon/core/mesh/teardown.d/*; do
+ [ ! -x "$script" ] || "$script"
+ done
+}
+
+add_protocol gluon_mesh
diff --git a/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
new file mode 100755
index 00000000..4a44a30e
--- /dev/null
+++ b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+. /lib/functions.sh
+. ../netifd-proto.sh
+init_proto "$@"
+
+proto_gluon_wired_init_config() {
+ proto_config_add_boolean transitive
+ proto_config_add_int index
+}
+
+xor2() {
+ echo -n "${1:0:1}"
+ echo -n "${1:1:1}" | tr '0123456789abcdef' '23016745ab89efcd'
+}
+
+interface_linklocal() {
+ local macaddr="$(ubus call network.device status '{"name": "'"$1"'"}' | jsonfilter -e '@.macaddr')"
+ local oldIFS="$IFS"; IFS=':'; set -- $macaddr; IFS="$oldIFS"
+
+ echo "fe80::$(xor2 "$1")$2:$3ff:fe$4:$5$6"
+}
+
+proto_gluon_wired_setup() {
+ local config="$1"
+ local ifname="$2"
+
+ local meshif="$config"
+
+ local vxlan="$(lua -e 'print(require("gluon.site").mesh.vxlan(true))')"
+
+ local transitive index
+ json_get_vars transitive index
+
+ proto_init_update "$ifname" 1
+ proto_send_update "$config"
+
+ if [ "$vxlan" = 'true' ]; then
+ meshif="vx_$config"
+
+ json_init
+ json_add_string name "$meshif"
+ [ -n "$index" ] && json_add_string macaddr "$(lua -lgluon.util -e "print(gluon.util.generate_mac($index))")"
+ json_add_string proto 'vxlan6'
+ json_add_string tunlink "$config"
+ json_add_string ip6addr "$(interface_linklocal "$ifname")"
+ json_add_string peer6addr 'ff02::15c'
+ json_add_int vid "$(lua -lgluon.util -e 'print(tonumber(gluon.util.domain_seed_bytes("gluon-mesh-vxlan", 3), 16))')"
+ json_add_boolean rxcsum '0'
+ json_add_boolean txcsum '0'
+ json_close_object
+ ubus call network add_dynamic "$(json_dump)"
+ fi
+
+ json_init
+ json_add_string name "${config}_mesh"
+ json_add_string ifname "@${meshif}"
+ json_add_string proto 'gluon_mesh'
+ json_add_boolean fixed_mtu 1
+ [ -n "$transitive" ] && json_add_boolean transitive "$transitive"
+ json_close_object
+ ubus call network add_dynamic "$(json_dump)"
+}
+
+proto_gluon_wired_teardown() {
+ local config="$1"
+
+ proto_init_update "*" 0
+ proto_send_update "$config"
+}
+
+add_protocol gluon_wired
diff --git a/package/gluon-core/files/usr/bin/gluon-list-mesh-interfaces b/package/gluon-core/files/usr/bin/gluon-list-mesh-interfaces
new file mode 100755
index 00000000..9b450f90
--- /dev/null
+++ b/package/gluon-core/files/usr/bin/gluon-list-mesh-interfaces
@@ -0,0 +1,2 @@
+#!/bin/sh
+ubus call network.interface dump | jsonfilter -e "@.interface[@.proto='gluon_mesh' && @.up=true].device"
diff --git a/package/gluon-core/files/usr/bin/gluon-reconfigure b/package/gluon-core/files/usr/bin/gluon-reconfigure
new file mode 100755
index 00000000..6e7add98
--- /dev/null
+++ b/package/gluon-core/files/usr/bin/gluon-reconfigure
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+cd /lib/gluon/upgrade || exit 1
+
+err=0
+
+for script in *; do
+ echo "Configuring: ${script}"
+ ./"$script" || err=1
+done
+
+if [ $err -eq 1 ]; then
+ echo 'One or more upgrade scripts failed. Please review the above error messages.'
+ exit 1
+fi
+
+exit 0
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
deleted file mode 100644
index 4c0a2d22..00000000
--- a/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua
+++ /dev/null
@@ -1,27 +0,0 @@
-local function get_site_config()
- local config = '/lib/gluon/site.json'
-
- local json = require 'luci.jsonc'
- local ltn12 = require 'luci.ltn12'
-
- local file = assert(io.open(config))
-
- local decoder = json.new()
- ltn12.pump.all(ltn12.source.file(file), decoder:sink())
-
- file:close()
-
- return assert(decoder:get())
-end
-
-local setmetatable = setmetatable
-
-module 'gluon.site_config'
-
-setmetatable(_M,
- {
- __index = get_site_config(),
- }
-)
-
-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
deleted file mode 100644
index 44b0c217..00000000
--- a/package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua
+++ /dev/null
@@ -1,8 +0,0 @@
-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
deleted file mode 100644
index 8e618d88..00000000
--- a/package/gluon-core/files/usr/lib/lua/gluon/users.lua
+++ /dev/null
@@ -1,33 +0,0 @@
-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
deleted file mode 100644
index 72b6220d..00000000
--- a/package/gluon-core/files/usr/lib/lua/gluon/util.lua
+++ /dev/null
@@ -1,129 +0,0 @@
--- 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 ipairs = ipairs
-local table = table
-
-local nixio = require 'nixio'
-local hash = require 'hash'
-local sysconfig = require 'gluon.sysconfig'
-local site = require 'gluon.site_config'
-local uci = require('luci.model.uci').cursor()
-
-
-module 'gluon.util'
-
-function exec(...)
- return os.execute(escape_args('', 'exec', ...))
-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 readline(fd)
- local line = fd:read('*l')
- fd:close()
- return line
-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 parameter defines the ID to add to the mac addr
---
--- IDs defined so far:
--- 0: client0; mesh-vpn
--- 1: mesh0
--- 2: ibss0
--- 3: client1; mesh-on-wan
--- 4: mesh1
--- 5: ibss1
--- 6: mesh-on-lan
--- 7: unused
-function generate_mac(i)
- if i > 7 or i < 0 then return nil end -- max allowed id (0b111)
-
- local hashed = string.sub(hash.md5(sysconfig.primary_mac), 0, 12)
- local m1, m2, m3, m4, m5, m6 = string.match(hashed, '(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)')
-
- m1 = tonumber(m1, 16)
- m6 = tonumber(m6, 16)
-
- m1 = nixio.bit.bor(m1, 0x02) -- set locally administered bit
- m1 = nixio.bit.band(m1, 0xFE) -- unset the multicast bit
-
- -- It's necessary that the first 45 bits of the mac do
- -- not vary on a single hardware interface, since some chips are using
- -- a hardware mac filter. (e.g 'ramips-rt305x')
-
- m6 = nixio.bit.band(m6, 0xF8) -- zero the last three bits (space needed for counting)
- m6 = m6 + i -- add virtual interface id
-
- return string.format('%02x:%s:%s:%s:%s:%02x', m1, m2, m3, m4, m5, m6)
-end
-
--- Iterate over all radios defined in UCI calling
--- f(radio, index, site.wifiX) for each radio found while passing
--- site.wifi24 for 2.4 GHz devices and site.wifi5 for 5 GHz ones.
-function iterate_radios(f)
- 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
- f(radio, index, site.wifi24)
- elseif hwmode == '11a' or hwmode == '11na' then
- f(radio, index, site.wifi5)
- end
- end
-end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/001-upgrade b/package/gluon-core/luasrc/lib/gluon/upgrade/001-upgrade
similarity index 100%
rename from package/gluon-core/files/lib/gluon/upgrade/001-upgrade
rename to package/gluon-core/luasrc/lib/gluon/upgrade/001-upgrade
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/005-site-domain b/package/gluon-core/luasrc/lib/gluon/upgrade/005-site-domain
new file mode 100755
index 00000000..04c81292
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/005-site-domain
@@ -0,0 +1,27 @@
+#!/usr/bin/lua
+
+local fs = require 'nixio.fs'
+
+
+if not fs.access('/lib/gluon/domains/') then
+ return
+end
+
+
+local uci = require('simple-uci').cursor()
+
+local domain = uci:get('gluon', 'core', 'domain')
+if domain and not fs.access('/lib/gluon/domains/' .. domain .. '.json') then
+ io.stderr:write(string.format("Warning: invalid mesh domain '%s' configured, resetting to default...\n", domain))
+ domain = nil
+end
+
+if domain then return end
+
+
+-- We can't use gluon.site yet, as it depends on gluon.core.domain to be set
+local json = require 'jsonc'
+local site = assert(json.load('/lib/gluon/site.json'))
+
+uci:set('gluon', 'core', 'domain', site.default_domain)
+uci:commit('gluon')
diff --git a/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac b/package/gluon-core/luasrc/lib/gluon/upgrade/010-primary-mac
similarity index 61%
rename from package/gluon-core/files/lib/gluon/upgrade/010-primary-mac
rename to package/gluon-core/luasrc/lib/gluon/upgrade/010-primary-mac
index 144ee700..9ad9b820 100755
--- a/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/010-primary-mac
@@ -8,33 +8,43 @@ if sysconfig.primary_mac then
end
+local util = require 'gluon.util'
local platform = require 'gluon.platform'
local fs = require 'nixio.fs'
-local util = require 'luci.util'
local try_files = {
'/sys/class/net/eth0/address'
}
-if not util.contains({'x86', 'brcm2708'}, platform.get_target()) then
+if not (
+ util.contains({'x86', 'brcm2708'}, platform.get_target()) or
+ platform.match('ar71xx', 'mikrotik')
+) then
table.insert(try_files, 1, '/sys/class/ieee80211/phy0/macaddress')
end
if platform.match('ar71xx', 'generic', {'tl-wdr3600', 'tl-wdr4300'}) then
table.insert(try_files, 1, '/sys/class/ieee80211/phy1/macaddress')
+elseif platform.match('ramips', 'mt7621', {'dir-860l-b1'}) then
+ table.insert(try_files, 1, '/sys/class/ieee80211/phy1/macaddress')
elseif platform.match('ar71xx', 'generic', {'unifi-outdoor-plus', 'carambola2',
+ 'a40', 'a60',
'mr600', 'mr600v2',
'mr900', 'mr900v2',
- 'mr1750',
- 'om2p', 'om2pv2',
- 'om2p-hs', 'om2p-hsv2',
+ 'mr1750', 'mr1750v2',
+ 'om2p', 'om2pv2', 'om2pv4',
+ 'om2p-hs', 'om2p-hsv2', 'om2p-hsv3',
+ 'om2p-hsv4',
'om2p-lc',
'om5p', 'om5p-an',
- 'om5p-ac', 'om5p-acv2'}) then
+ 'om5p-ac', 'om5p-acv2',
+ 'archer-c7-v4'}) then
table.insert(try_files, 1, '/sys/class/net/eth0/address')
-elseif platform.match('ar71xx', 'generic', {'archer-c5', 'archer-c7'}) then
+elseif platform.match('ar71xx', 'generic', {'archer-c5', 'archer-c58-v1',
+ 'archer-c59-v1', 'archer-c60-v1',
+ 'archer-c7'}) then
table.insert(try_files, 1, '/sys/class/net/eth1/address')
end
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/020-interfaces b/package/gluon-core/luasrc/lib/gluon/upgrade/020-interfaces
new file mode 100755
index 00000000..47757699
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/020-interfaces
@@ -0,0 +1,64 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+
+-- Are we already set up?
+if sysconfig.lan_ifname or sysconfig.wan_ifname then
+ os.exit(0)
+end
+
+
+local util = require 'gluon.util'
+local platform = require 'gluon.platform'
+local site = require 'gluon.site'
+
+local fs = require 'nixio.fs'
+local uci = require('simple-uci').cursor()
+
+
+local function iface_exists(ifaces)
+ if not ifaces then return nil end
+
+ for iface in ifaces:gmatch('%S+') do
+ if fs.access('/sys/class/net/' .. iface:gsub('%..*$', '')) then
+ return ifaces
+ end
+ end
+end
+
+
+local lan_ifname = iface_exists(uci:get('network', 'lan', 'ifname'))
+local wan_ifname = iface_exists(uci:get('network', 'wan', 'ifname'))
+
+if platform.match('ar71xx', 'generic', {
+ 'cpe210',
+ 'cpe510',
+ 'wbs210',
+ 'wbs510',
+ 'airgateway',
+ 'nanostation-m',
+ 'nanostation-m-xw',
+ 'unifi-outdoor-plus',
+ 'uap-pro',
+ 'unifiac-pro',
+}) then
+ lan_ifname, wan_ifname = wan_ifname, lan_ifname
+end
+
+if wan_ifname and lan_ifname then
+ sysconfig.wan_ifname = wan_ifname
+ sysconfig.lan_ifname = lan_ifname
+else
+ local single_ifname = lan_ifname or wan_ifname
+ if site.single_as_lan(false) then
+ sysconfig.lan_ifname = single_ifname
+ else
+ sysconfig.wan_ifname = single_ifname
+ end
+end
+
+
+uci:delete('network', 'lan')
+uci:delete('network', 'wan')
+
+uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/030-system b/package/gluon-core/luasrc/lib/gluon/upgrade/030-system
new file mode 100755
index 00000000..29432e4c
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/030-system
@@ -0,0 +1,21 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+
+-- Initial
+if sysconfig.gluon_version then
+ os.exit(0)
+end
+
+
+local site = require 'gluon.site'
+local util = require 'gluon.util'
+local uci = require('simple-uci').cursor()
+local pretty_hostname = require 'pretty_hostname'
+
+local system = uci:get_first('system', 'system')
+
+pretty_hostname.set(uci, util.default_hostname())
+uci:set('system', system, 'timezone', site.timezone())
+
+uci:save('system')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/110-network b/package/gluon-core/luasrc/lib/gluon/upgrade/110-network
new file mode 100755
index 00000000..79c50d27
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/110-network
@@ -0,0 +1,69 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+local sysconfig = require 'gluon.sysconfig'
+
+
+uci:section('network', 'interface', 'wan', {
+ ifname = sysconfig.wan_ifname,
+ type = 'bridge',
+ igmp_snooping = true,
+ multicast_querier = false,
+ peerdns = false,
+ auto = true,
+})
+
+if not uci:get('network', 'wan', 'proto') then
+ uci:set('network', 'wan', 'proto', 'dhcp')
+end
+
+
+uci:section('network', 'interface', 'wan6', {
+ ifname = 'br-wan',
+ peerdns = false,
+ ip6table = 1,
+ sourcefilter = false,
+ reqprefix = 'no',
+})
+
+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:section('firewall', 'rule', 'wan_igmp', {
+ name = 'Allow-IGMP',
+ src = 'wan',
+ proto = 'igmp',
+ family = 'ipv4',
+ target = 'ACCEPT',
+})
+
+uci:section('firewall', 'rule', 'wan_mld', {
+ name = 'Allow-MLD',
+ src = 'wan',
+ proto = 'icmp',
+ src_ip = 'fe80::/10',
+ icmp_type = { '130/0', '131/0', '132/0', '143/0', },
+ family = 'ipv6',
+ target = 'ACCEPT',
+})
+
+uci:save('firewall')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/120-ntp-servers b/package/gluon-core/luasrc/lib/gluon/upgrade/120-ntp-servers
new file mode 100755
index 00000000..e3718b68
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/120-ntp-servers
@@ -0,0 +1,13 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site'
+
+if #site.ntp_servers({}) == 0 then
+ os.exit(0)
+end
+
+
+local uci = require('simple-uci').cursor()
+uci:delete('system', 'ntp', 'server')
+uci:set_list('system', 'ntp', 'server', site.ntp_servers({}))
+uci:save('system')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/140-firewall-rules b/package/gluon-core/luasrc/lib/gluon/upgrade/140-firewall-rules
new file mode 100755
index 00000000..ae374e33
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/140-firewall-rules
@@ -0,0 +1,102 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+
+
+local defaults = uci:get_first('firewall', 'defaults')
+uci:set('firewall', defaults, 'input', 'REJECT')
+
+
+local function reject_input_on_wan(zone)
+ if zone.name == 'wan' then
+ uci:set('firewall', zone['.name'], 'input', 'REJECT')
+ uci:set('firewall', zone['.name'], 'conntrack', true)
+ end
+
+ return true
+end
+
+uci:foreach('firewall', 'zone', reject_input_on_wan)
+
+for _, zone in ipairs({'mesh', 'local_client', 'wired_mesh'}) do
+ -- Other packages assign interfaces to these zones
+ uci:section('firewall', 'zone', zone, {
+ name = zone,
+ network = {},
+ input = 'REJECT',
+ output = 'ACCEPT',
+ forward = 'REJECT',
+ })
+
+ uci:section('firewall', 'rule', zone .. '_ICMPv6_in', {
+ src = zone,
+ proto = 'icmp',
+ icmp_type = {
+ 'echo-request',
+ 'echo-reply',
+ 'destination-unreachable',
+ 'packet-too-big',
+ 'time-exceeded',
+ 'bad-header',
+ 'unknown-header-type',
+ 'router-solicitation',
+ 'neighbour-solicitation',
+ 'router-advertisement',
+ 'neighbour-advertisement',
+ '130/0', -- Multicast Listener Query
+ '131/0', -- Multicast Listener Report
+ '132/0', -- Multicast Listener Done
+ '143/0', -- MLDv2
+ },
+ limit = '1000/sec',
+ family = 'ipv6',
+ target = 'ACCEPT',
+ })
+
+ -- Can be removed soon: was never in a release
+ uci:delete('firewall', zone .. '_ICMPv6_out')
+end
+
+uci:section('firewall', 'rule', 'local_client_ICMPv4_in', {
+ src = 'local_client',
+ proto = 'icmp',
+ icmp_type = {
+ 'echo-request',
+ },
+ family = 'ipv4',
+ target = 'ACCEPT',
+})
+
+
+-- allow inbound SSH from anywhere
+for _, zone in ipairs({ 'wan', 'local_client', 'mesh' }) do
+ uci:section('firewall', 'rule', zone .. '_ssh', {
+ name = zone .. '_ssh',
+ src = zone,
+ dest_port = '22',
+ proto = 'tcp',
+ target = 'ACCEPT',
+ })
+end
+
+
+-- We can't put mesh_wan into this zone, as mesh_wan is the same
+-- interface as wan, which has its own zone
+uci:set('firewall', 'wired_mesh', 'network', {'mesh_lan'})
+
+-- VXLAN for wired meshing
+for _, zone in ipairs({'wired_mesh', 'wan'}) do
+ uci:section('firewall', 'rule', zone .. '_vxlan', {
+ name = zone .. '_vxlan',
+ src = zone,
+ family = 'ipv6',
+ src_ip = 'fe80::/64',
+ proto = 'udp',
+ dest_port = '4789',
+ target = 'ACCEPT',
+ })
+ uci:reorder('firewall', zone .. '_vxlan', 0)
+end
+
+
+uci:save('firewall')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/150-poe-passthrough b/package/gluon-core/luasrc/lib/gluon/upgrade/150-poe-passthrough
new file mode 100755
index 00000000..e8737259
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/150-poe-passthrough
@@ -0,0 +1,16 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+local site = require 'gluon.site'
+
+if sysconfig.gluon_version or not site.poe_passthrough(false) then
+ os.exit(0)
+end
+
+
+local uci = require('simple-uci').cursor()
+
+if uci:get('system', 'poe_passthrough') then
+ uci:set('system', 'poe_passthrough', 'value', true)
+ uci:save('system')
+end
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless b/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless
new file mode 100755
index 00000000..72abfde4
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless
@@ -0,0 +1,210 @@
+#!/usr/bin/lua
+
+local util = require 'gluon.util'
+local site = require 'gluon.site'
+local sysconfig = require 'gluon.sysconfig'
+local iwinfo = require 'iwinfo'
+
+local uci = require('simple-uci').cursor()
+
+-- Initial
+if not sysconfig.gluon_version then
+ uci:delete_all('wireless', 'wifi-iface')
+end
+
+local function get_channel(radio, config)
+ local channel
+ if uci:get_first('gluon-core', 'wireless', 'preserve_channels') then
+ channel = radio.channel
+ end
+
+ return channel or config.channel()
+end
+
+local function get_htmode(radio)
+ local phy = util.find_phy(radio)
+ if iwinfo.nl80211.hwmodelist(phy).ac then
+ return 'VHT20'
+ else
+ return 'HT20'
+ end
+end
+
+local function is_disabled(name)
+ if uci:get('wireless', name) then
+ return uci:get_bool('wireless', name, 'disabled')
+ else
+ return false
+ end
+end
+
+-- Returns the first argument that is not nil; don't call without any non-nil arguments!
+local function first_non_nil(first, ...)
+ if first ~= nil then
+ return first
+ else
+ return first_non_nil(...)
+ end
+end
+
+
+local function configure_ibss(config, radio, index, suffix, disabled)
+ local radio_name = radio['.name']
+ local name = 'ibss_' .. radio_name
+
+ uci:delete('network', name)
+ uci:delete('network', name .. '_vlan')
+ uci:delete('wireless', name)
+
+ if not config then
+ return
+ end
+
+ local macaddr = util.get_wlan_mac(uci, radio, index, 3)
+ if not macaddr then
+ return
+ end
+
+ if config.vlan then
+ uci:section('network', 'interface', name, {
+ proto = 'none',
+ })
+
+ uci:section('network', 'interface', name .. '_vlan', {
+ ifname = '@' .. name .. '.' .. config.vlan,
+ proto = 'gluon_mesh',
+ })
+ else
+ uci:section('network', 'interface', name, {
+ proto = 'gluon_mesh',
+ })
+ end
+
+ uci:section('wireless', 'wifi-iface', name, {
+ device = radio_name,
+ network = name,
+ mode = 'adhoc',
+ ssid = config.ssid,
+ bssid = config.bssid,
+ macaddr = macaddr,
+ mcast_rate = config.mcast_rate,
+ ifname = suffix and 'ibss' .. suffix,
+ disabled = disabled,
+ })
+end
+
+local function configure_mesh(config, radio, index, suffix, disabled)
+ local radio_name = radio['.name']
+ local name = 'mesh_' .. radio_name
+
+ local macfilter = uci:get('wireless', name, 'macfilter')
+ local maclist = uci:get('wireless', name, 'maclist')
+
+ uci:delete('network', name)
+ uci:delete('network', name .. '_vlan')
+ uci:delete('wireless', name)
+
+ if not config then
+ return
+ end
+
+ local macaddr = util.get_wlan_mac(uci, radio, index, 2)
+ if not macaddr then
+ return
+ end
+
+ uci:section('network', 'interface', name, {
+ proto = 'gluon_mesh',
+ })
+
+ uci:section('wireless', 'wifi-iface', name, {
+ device = radio_name,
+ network = name,
+ mode = 'mesh',
+ mesh_id = config.id,
+ mesh_fwding = false,
+ macaddr = macaddr,
+ mcast_rate = config.mcast_rate,
+ ifname = suffix and 'mesh' .. suffix,
+ disabled = disabled,
+ macfilter = macfilter,
+ maclist = maclist,
+ })
+end
+
+local function fixup_wan(radio, index)
+ local radio_name = radio['.name']
+ local name = 'wan_' .. radio_name
+
+ if not uci:get('wireless', name) then
+ return
+ end
+
+ local macaddr = util.get_wlan_mac(uci, radio, index, 4)
+ if not macaddr then
+ return
+ end
+
+ uci:set('wireless', name, 'macaddr', macaddr)
+end
+
+util.foreach_radio(uci, function(radio, index, config)
+ local radio_name = radio['.name']
+
+ if not config() then
+ uci:set('wireless', radio_name, 'disabled', true)
+ return
+ end
+
+ local suffix = radio_name:match('^radio(%d+)$')
+ if not suffix then
+ return
+ end
+
+ local channel = get_channel(radio, config)
+ local htmode = get_htmode(radio)
+
+ uci:delete('wireless', radio_name, 'disabled')
+
+ uci:set('wireless', radio_name, 'channel', channel)
+ uci:set('wireless', radio_name, 'htmode', htmode)
+ uci:set('wireless', radio_name, 'country', site.regdom())
+
+ uci:set_list('wireless', radio_name, 'supported_rates', config.supported_rates())
+ uci:set_list('wireless', radio_name, 'basic_rate', config.basic_rate())
+
+
+ local ibss_disabled = is_disabled('ibss_' .. radio_name)
+ local mesh_disabled = is_disabled('mesh_' .. radio_name)
+
+ configure_ibss(config.ibss(), radio, index, suffix,
+ first_non_nil(
+ ibss_disabled,
+ mesh_disabled,
+ config.ibss.disabled(false)
+ )
+ )
+ configure_mesh(config.mesh(), radio, index, suffix,
+ first_non_nil(
+ mesh_disabled,
+ ibss_disabled,
+ config.mesh.disabled(false)
+ )
+ )
+
+ fixup_wan(radio, index)
+end)
+
+
+if uci:get('system', 'rssid_wlan0') then
+ if uci:get('wireless', 'mesh_radio0') then
+ uci:set('system', 'rssid_wlan0', 'dev', 'mesh0')
+ else
+ uci:set('system', 'rssid_wlan0', 'dev', 'ibss0')
+ end
+
+ uci:save('system')
+end
+
+uci:save('wireless')
+uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan b/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan
new file mode 100755
index 00000000..e8600ee8
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/210-interface-wan
@@ -0,0 +1,30 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site'
+local uci = require('simple-uci').cursor()
+
+local old_proto = uci:get('network', 'mesh_wan', 'proto')
+
+uci:section('network', 'interface', 'mesh_wan', {
+ ifname = 'br-wan',
+ proto = 'gluon_wired',
+ index = 0,
+})
+
+local enable = site.mesh_on_wan(false)
+local old_auto = uci:get('network', 'mesh_wan', 'auto')
+local old_disabled = uci:get('network', 'mesh_wan', 'disabled')
+if old_auto ~= nil or old_disabled ~= nil then
+ enable = old_auto ~= '0' and old_disabled ~= '1'
+end
+uci:set('network', 'mesh_wan', 'disabled', not enable)
+
+if uci:get('network', 'mesh_wan', 'transitive') == nil then
+ uci:set('network', 'mesh_wan', 'transitive', true)
+end
+
+uci:delete('network', 'mesh_wan', 'auto')
+uci:delete('network', 'mesh_wan', 'fixed_mtu')
+uci:delete('network', 'mesh_wan', 'legacy')
+
+uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan b/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan
new file mode 100755
index 00000000..a6fb4127
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/220-interface-lan
@@ -0,0 +1,58 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site'
+local util = require 'gluon.util'
+local sysconfig = require 'gluon.sysconfig'
+
+local uci = require('simple-uci').cursor()
+
+if not sysconfig.lan_ifname then
+ os.exit(0)
+end
+
+local old_proto = uci:get('network', 'mesh_lan', 'proto')
+
+uci:section('network', 'interface', 'mesh_lan', {
+ ifname = sysconfig.lan_ifname,
+ igmp_snooping = false,
+ proto = 'gluon_wired',
+ index = 4,
+})
+
+if sysconfig.lan_ifname:match(' ') then
+ uci:set('network', 'mesh_lan', 'type', 'bridge')
+else
+ uci:delete('network', 'mesh_lan', 'type')
+end
+
+local enable = site.mesh_on_lan(false)
+local old_auto = uci:get('network', 'mesh_lan', 'auto')
+local old_disabled = uci:get('network', 'mesh_lan', 'disabled')
+if old_auto ~= nil or old_disabled ~= nil then
+ enable = old_auto ~= '0' and old_disabled ~= '1'
+end
+
+if enable then
+ local interfaces = uci:get_list('network', 'client', 'ifname')
+
+ if interfaces then
+ for lanif in sysconfig.lan_ifname:gmatch('%S+') do
+ if util.contains(interfaces, lanif) then
+ enable = false
+ break
+ end
+ end
+ end
+end
+
+uci:set('network', 'mesh_lan', 'disabled', not enable)
+
+if uci:get('network', 'mesh_lan', 'transitive') == nil then
+ uci:set('network', 'mesh_lan', 'transitive', true)
+end
+
+uci:delete('network', 'mesh_lan', 'auto')
+uci:delete('network', 'mesh_lan', 'fixed_mtu')
+uci:delete('network', 'mesh_lan', 'legacy')
+
+uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/500-opkg b/package/gluon-core/luasrc/lib/gluon/upgrade/500-opkg
new file mode 100755
index 00000000..81e437b5
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/500-opkg
@@ -0,0 +1,75 @@
+#!/usr/bin/lua
+
+local fs = require 'nixio.fs'
+
+if not fs.access('/etc/opkg/distfeeds.conf') then
+ os.exit(0)
+end
+
+
+local site = require 'gluon.site'
+local util = require 'gluon.util'
+
+
+local subst = {}
+
+
+local f = io.popen('. /etc/openwrt_release; echo "$DISTRIB_CODENAME"; echo "$DISTRIB_RELEASE"; echo "$DISTRIB_TARGET"; echo "$DISTRIB_ARCH"')
+subst['%%n'] = f:read()
+subst['%%v'] = f:read():gsub('-SNAPSHOT', '')
+subst['%%S'] = f:read()
+subst['%%A'] = f:read()
+f:close()
+
+subst['%%GS'] = site.site_code()
+subst['%%GV'] = util.trim(fs.readfile('/lib/gluon/gluon-version'))
+subst['%%GR'] = util.trim(fs.readfile('/lib/gluon/release'))
+
+local prefix = subst['%%n'] .. '_'
+
+
+local function replace_patterns(url)
+ for k, v in pairs(subst) do
+ url = url:gsub(k, v)
+ end
+
+ return url
+end
+
+
+local lede = site.opkg.lede()
+local extra = site.opkg.extra({})
+
+
+local distfeeds = {}
+for line in io.lines('/etc/opkg/distfeeds.conf') do
+ table.insert(distfeeds, line)
+end
+
+local f = io.open('/etc/opkg/distfeeds.conf', 'w')
+
+for _, line in ipairs(distfeeds) do
+ local name = line:match('^src/gz%s' .. prefix .. '(%S+)%s')
+ if name == 'core' then
+ f:write('# ' .. line .. '\n')
+ elseif name and lede then
+ f:write(string.format('src/gz %s %s/%s\n', prefix .. name, replace_patterns(lede), name))
+ else
+ f:write(line .. '\n')
+ end
+end
+
+f:close()
+
+if next(extra) then
+ local f = io.open('/etc/opkg/gluon.conf', 'w')
+
+ for k, v in pairs(extra) do
+ f:write(string.format('src/gz %s %s\n', k, replace_patterns(v)))
+ end
+
+ f:close()
+
+else
+ os.remove('/etc/opkg/gluon.conf')
+end
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/800-migrate-batadv b/package/gluon-core/luasrc/lib/gluon/upgrade/800-migrate-batadv
new file mode 100755
index 00000000..ddd03c0a
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/800-migrate-batadv
@@ -0,0 +1,24 @@
+#!/usr/bin/lua
+
+local uci = require('simple-uci').cursor()
+
+local function migrate_iface(iface)
+ if iface.proto ~= 'batadv' or iface.mesh ~= 'bat0' then
+ return
+ end
+
+ local s = iface['.name']
+
+ uci:set('network', s, 'proto', 'gluon_mesh')
+ uci:set('network', s, 'fixed_mtu', true)
+
+ if iface.mesh_no_rebroadcast then
+ uci:set('network', s, 'transitive', iface.mesh_no_rebroadcast)
+ end
+
+ uci:delete('network', s, 'mesh')
+ uci:delete('network', s, 'mesh_no_rebroadcast')
+end
+
+uci:foreach('network', 'interface', migrate_iface)
+uci:save('network')
diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/820-dns-config b/package/gluon-core/luasrc/lib/gluon/upgrade/820-dns-config
new file mode 100755
index 00000000..18b44d3f
--- /dev/null
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/820-dns-config
@@ -0,0 +1,46 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site'
+local uci = require('simple-uci').cursor()
+
+local dns = site.dns({})
+local next_node = site.next_node({})
+
+local dnsmasq = uci:get_first("dhcp", "dnsmasq")
+
+uci:set('dhcp', dnsmasq, 'localise_queries', true)
+uci:set('dhcp', dnsmasq, 'localservice', false)
+
+uci:set('dhcp', dnsmasq, 'server', dns.servers)
+uci:delete('dhcp', dnsmasq, 'cachesize')
+
+uci:delete('firewall', 'client_dns')
+if dns.servers then
+ -- allow inbound traffic for dns from client zone
+ uci:section('firewall', 'rule', 'client_dns', {
+ src = 'local_client',
+ dest_port = '53',
+ proto = 'tcpudp',
+ target = 'ACCEPT',
+ })
+end
+
+local function set_dns_record(name, ip, sectionname)
+ if not ip then return end
+ uci:section('dhcp', 'domain', sectionname, {
+ name = name,
+ ip = ip,
+ })
+end
+
+uci:delete_all('dhcp', 'domain', function(s)
+ return (s['.name'] and string.match(s['.name'], "^nextnode[46]"))
+end)
+
+for i, name in ipairs(next_node.name or {}) do
+ set_dns_record(name, next_node.ip4, 'nextnode4_' .. i)
+ set_dns_record(name, next_node.ip6, 'nextnode6_' .. i)
+end
+
+uci:save('dhcp')
+uci:save('firewall')
diff --git a/package/gluon-core/files/lib/gluon/upgrade/999-version b/package/gluon-core/luasrc/lib/gluon/upgrade/999-version
similarity index 89%
rename from package/gluon-core/files/lib/gluon/upgrade/999-version
rename to package/gluon-core/luasrc/lib/gluon/upgrade/999-version
index 9731692e..608545b7 100755
--- a/package/gluon-core/files/lib/gluon/upgrade/999-version
+++ b/package/gluon-core/luasrc/lib/gluon/upgrade/999-version
@@ -3,7 +3,7 @@
local sysconfig = require 'gluon.sysconfig'
local fs = require 'nixio.fs'
-local util = require 'luci.util'
+local util = require 'gluon.util'
-- Save the Gluon version in the sysconfig so we know which version we
diff --git a/package/gluon-core/luasrc/usr/bin/gluon-show-site b/package/gluon-core/luasrc/usr/bin/gluon-show-site
new file mode 100755
index 00000000..df3b56fa
--- /dev/null
+++ b/package/gluon-core/luasrc/usr/bin/gluon-show-site
@@ -0,0 +1,6 @@
+#!/usr/bin/lua
+
+local json = require 'jsonc'
+local site = require 'gluon.site'
+
+print(json.stringify(site(), true))
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/platform.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua
similarity index 79%
rename from package/gluon-core/files/usr/lib/lua/gluon/platform.lua
rename to package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua
index 3d56f081..5b4f559a 100644
--- a/package/gluon-core/files/usr/lib/lua/gluon/platform.lua
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua
@@ -1,5 +1,5 @@
local platform_info = require 'platform_info'
-local util = require 'luci.util'
+local util = require 'gluon.util'
local setmetatable = setmetatable
@@ -21,11 +21,9 @@ function match(target, subtarget, boards)
return false
end
- if not util.contains(boards, get_board_name()) then
+ if boards and not util.contains(boards, get_board_name()) then
return false
end
return true
end
-
-
diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/site_config.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/site_config.lua
new file mode 100644
index 00000000..533fdf71
--- /dev/null
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/site_config.lua
@@ -0,0 +1,11 @@
+local site = require 'gluon.site'
+
+local setmetatable = setmetatable
+
+module 'gluon.site_config'
+
+setmetatable(_M, {
+ __index = site(),
+})
+
+return _M
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/sysconfig.lua
similarity index 96%
rename from package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua
rename to package/gluon-core/luasrc/usr/lib/lua/gluon/sysconfig.lua
index ff61f05b..fabfe05f 100644
--- a/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/sysconfig.lua
@@ -13,7 +13,7 @@ end
local function set(_, name, val)
if val then
local f = io.open(sysconfigdir .. name, 'w+')
- f:write(val)
+ f:write(val, '\n')
f:close()
else
os.remove(sysconfigdir .. name)
diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/users.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/users.lua
new file mode 100644
index 00000000..1dbc3728
--- /dev/null
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/users.lua
@@ -0,0 +1,20 @@
+local util = require 'gluon.util'
+
+local os = os
+local string = string
+
+
+module 'gluon.users'
+
+function remove_user(username)
+ os.execute('exec lock /var/lock/passwd')
+ util.replace_prefix('/etc/passwd', username .. ':')
+ util.replace_prefix('/etc/shadow', username .. ':')
+ os.execute('exec lock -u /var/lock/passwd')
+end
+
+function remove_group(groupname)
+ os.execute('exec lock /var/lock/group')
+ util.replace_prefix('/etc/group', groupname .. ':')
+ os.execute('exec lock -u /var/lock/group')
+end
diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
new file mode 100644
index 00000000..1becb9c1
--- /dev/null
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
@@ -0,0 +1,246 @@
+-- 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 io = io
+local os = os
+local string = string
+local tonumber = tonumber
+local ipairs = ipairs
+local pairs = pairs
+local table = table
+
+local nixio = require 'nixio'
+local hash = require 'hash'
+local sysconfig = require 'gluon.sysconfig'
+local site = require 'gluon.site'
+local fs = require 'nixio.fs'
+
+
+module 'gluon.util'
+
+function trim(str)
+ return str:gsub("^%s*(.-)%s*$", "%1")
+end
+
+function contains(table, value)
+ for k, v in pairs(table) do
+ if value == v then
+ return k
+ end
+ end
+ return false
+end
+
+function add_to_set(t, itm)
+ for _,v in ipairs(t) do
+ if v == itm then return false end
+ end
+ table.insert(t, itm)
+ return true
+end
+
+function remove_from_set(t, itm)
+ local i = 1
+ local changed = false
+ while i <= #t do
+ if t[i] == itm then
+ table.remove(t, i)
+ changed = true
+ else
+ i = i + 1
+ end
+ end
+ return changed
+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 readline(fd)
+ local line = fd:read('*l')
+ fd:close()
+ return line
+end
+
+function exec(command)
+ local pp = io.popen(command)
+ local data = pp:read("*a")
+ pp:close()
+
+ return data
+end
+
+function node_id()
+ return string.gsub(sysconfig.primary_mac, ':', '')
+end
+
+function default_hostname()
+ return site.hostname_prefix('') .. node_id()
+end
+
+function domain_seed_bytes(key, length)
+ local ret = ''
+ local v = ''
+ local i = 0
+
+ -- Inspired by HKDF key expansion, but much simpler, as we don't need
+ -- cryptographic strength
+ while ret:len() < 2*length do
+ i = i + 1
+ v = hash.md5(v .. key .. site.domain_seed():lower() .. i)
+ ret = ret .. v
+ end
+
+ return ret:sub(0, 2*length)
+end
+
+function get_mesh_devices(uconn)
+ local dump = uconn:call("network.interface", "dump", {})
+ local devices = {}
+ for _, interface in ipairs(dump.interface) do
+ if ( (interface.proto == "gluon_mesh") and interface.up ) then
+ table.insert(devices, interface.device)
+ end
+ end
+ return devices
+end
+
+local function find_phy_by_path(path)
+ for phy in fs.glob('/sys/devices/' .. path .. '/ieee80211/phy*') do
+ return phy:match('([^/]+)$')
+ end
+
+ for phy in fs.glob('/sys/devices/platform/' .. path .. '/ieee80211/phy*') do
+ return phy:match('([^/]+)$')
+ end
+end
+
+local function find_phy_by_macaddr(macaddr)
+ local addr = macaddr:lower()
+ for file in fs.glob('/sys/class/ieee80211/*/macaddress') do
+ if trim(fs.readfile(file)) == addr then
+ return file:match('([^/]+)/macaddress$')
+ end
+ end
+end
+
+function find_phy(config)
+ if not config or config.type ~= 'mac80211' then
+ return nil
+ elseif config.path then
+ return find_phy_by_path(config.path)
+ elseif config.macaddr then
+ return find_phy_by_macaddr(config.macaddr)
+ else
+ return nil
+ end
+end
+
+local function get_addresses(uci, radio)
+ local phy = find_phy(radio)
+ if not phy then
+ return function() end
+ end
+
+ return io.lines('/sys/class/ieee80211/' .. phy .. '/addresses')
+end
+
+-- Generates a (hopefully) unique MAC address
+-- The parameter defines the ID to add to the MAC address
+--
+-- IDs defined so far:
+-- 0: client0; WAN
+-- 1: mesh0
+-- 2: ibss0
+-- 3: wan_radio0 (private WLAN); batman-adv primary address
+-- 4: client1; LAN
+-- 5: mesh1
+-- 6: ibss1
+-- 7: wan_radio1 (private WLAN); mesh VPN
+function generate_mac(i)
+ if i > 7 or i < 0 then return nil end -- max allowed id (0b111)
+
+ local hashed = string.sub(hash.md5(sysconfig.primary_mac), 0, 12)
+ local m1, m2, m3, m4, m5, m6 = string.match(hashed, '(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)')
+
+ m1 = tonumber(m1, 16)
+ m6 = tonumber(m6, 16)
+
+ m1 = nixio.bit.bor(m1, 0x02) -- set locally administered bit
+ m1 = nixio.bit.band(m1, 0xFE) -- unset the multicast bit
+
+ -- It's necessary that the first 45 bits of the MAC address don't
+ -- vary on a single hardware interface, since some chips are using
+ -- a hardware MAC filter. (e.g 'rt305x')
+
+ m6 = nixio.bit.band(m6, 0xF8) -- zero the last three bits (space needed for counting)
+ m6 = m6 + i -- add virtual interface id
+
+ return string.format('%02x:%s:%s:%s:%s:%02x', m1, m2, m3, m4, m5, m6)
+end
+
+local function get_wlan_mac_from_driver(uci, radio, vif)
+ local primary = sysconfig.primary_mac:lower()
+
+ local i = 1
+ for addr in get_addresses(uci, radio) do
+ if addr:lower() ~= primary then
+ if i == vif then
+ return addr
+ end
+
+ i = i + 1
+ end
+ end
+end
+
+function get_wlan_mac(uci, radio, index, vif)
+ local addr = get_wlan_mac_from_driver(uci, radio, vif)
+ if addr then
+ return addr
+ end
+
+ return generate_mac(4*(index-1) + (vif-1))
+end
+
+-- Iterate over all radios defined in UCI calling
+-- f(radio, index, site.wifiX) for each radio found while passing
+-- site.wifi24 for 2.4 GHz devices and site.wifi5 for 5 GHz ones.
+function foreach_radio(uci, f)
+ local radios = {}
+
+ uci:foreach('wireless', 'wifi-device', function(radio)
+ table.insert(radios, radio)
+ end)
+
+ for index, radio in ipairs(radios) do
+ local hwmode = radio.hwmode
+
+ if hwmode == '11g' or hwmode == '11ng' then
+ f(radio, index, site.wifi24)
+ elseif hwmode == '11a' or hwmode == '11na' then
+ f(radio, index, site.wifi5)
+ end
+ end
+end
diff --git a/package/gluon-core/src/CMakeLists.txt b/package/gluon-core/src/CMakeLists.txt
new file mode 100644
index 00000000..aee77452
--- /dev/null
+++ b/package/gluon-core/src/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.0)
+
+project(gluon-core C)
+
+add_library(site MODULE site.c)
+set_property(TARGET site PROPERTY PREFIX "")
+set_property(TARGET site PROPERTY COMPILE_FLAGS "-Wall -std=c99")
+target_link_libraries(site gluonutil lua lua-jsonc)
+
+install(TARGETS site
+ LIBRARY DESTINATION lib/lua/gluon
+)
diff --git a/package/gluon-core/src/site.c b/package/gluon-core/src/site.c
new file mode 100644
index 00000000..971d44cf
--- /dev/null
+++ b/package/gluon-core/src/site.c
@@ -0,0 +1,113 @@
+#include "libgluonutil.h"
+#include "lua-jsonc.h"
+
+#include
+#include
+#include
+
+
+#define UDATA "gluon.site"
+
+
+static struct json_object * gluon_site_udata(lua_State *L, int narg) {
+ return *(struct json_object **)luaL_checkudata(L, narg, UDATA);
+}
+
+static void gluon_site_push_none(lua_State *L) {
+ lua_pushlightuserdata(L, gluon_site_push_none);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+}
+
+static void gluon_site_do_wrap(lua_State *L, struct json_object *obj) {
+ struct json_object **objp = lua_newuserdata(L, sizeof(struct json_object *));
+ *objp = json_object_get(obj);
+ luaL_getmetatable(L, UDATA);
+ lua_setmetatable(L, -2);
+}
+
+static void gluon_site_wrap(lua_State *L, struct json_object *obj) {
+ if (obj)
+ gluon_site_do_wrap(L, obj);
+ else
+ gluon_site_push_none(L);
+}
+
+
+static int gluon_site_index(lua_State *L) {
+ struct json_object *obj = gluon_site_udata(L, 1);
+ const char *key;
+ lua_Number lua_index;
+ size_t index;
+ struct json_object *v = NULL;
+
+ switch (json_object_get_type(obj)) {
+ case json_type_object:
+ key = lua_tostring(L, 2);
+ if (key)
+ json_object_object_get_ex(obj, key, &v);
+ break;
+
+ case json_type_array:
+ index = lua_index = lua_tonumber(L, 2);
+ if (lua_index == (lua_Number)index && index >= 1)
+ v = json_object_array_get_idx(obj, index-1);
+ break;
+
+ case json_type_string:
+ case json_type_null:
+ break;
+
+ case json_type_boolean:
+ case json_type_int:
+ case json_type_double:
+ luaL_error(L, "attempt to index a number or boolean value");
+ __builtin_unreachable();
+ }
+
+ gluon_site_wrap(L, v);
+ return 1;
+}
+
+static int gluon_site_call(lua_State *L) {
+ struct json_object *obj = gluon_site_udata(L, 1);
+
+ if (obj) {
+ lua_jsonc_push_json(L, obj);
+ } else {
+ if (lua_isnone(L, 2))
+ lua_pushnil(L);
+ else
+ lua_pushvalue(L, 2);
+ }
+
+ return 1;
+}
+
+static int gluon_site_gc(lua_State *L) {
+ json_object_put(gluon_site_udata(L, 1));
+ return 0;
+}
+
+static const luaL_reg R[] = {
+ { "__index", gluon_site_index },
+ { "__call", gluon_site_call },
+ { "__gc", gluon_site_gc },
+ {}
+};
+
+int luaopen_gluon_site(lua_State *L) {
+ luaL_newmetatable(L, UDATA);
+ luaL_register(L, NULL, R);
+ lua_pop(L, 1);
+
+ /* Create "none" object */
+ lua_pushlightuserdata(L, gluon_site_push_none);
+ gluon_site_do_wrap(L, NULL);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ struct json_object *site = gluonutil_load_site_config();
+ gluon_site_wrap(L, site);
+ json_object_put(site);
+
+ return 1;
+}
diff --git a/package/gluon-ebtables-filter-multicast/Makefile b/package/gluon-ebtables-filter-multicast/Makefile
index 93b7f9a5..dc579a2a 100644
--- a/package/gluon-ebtables-filter-multicast/Makefile
+++ b/package/gluon-ebtables-filter-multicast/Makefile
@@ -4,15 +4,11 @@ PKG_NAME:=gluon-ebtables-filter-multicast
PKG_VERSION:=1
PKG_RELEASE:=1
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(INCLUDE_DIR)/package.mk
+include ../gluon.mk
define Package/gluon-ebtables-filter-multicast
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Ebtables filters for multicast packets
- DEPENDS:=+gluon-core +gluon-ebtables
+ DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv
endef
define Package/gluon-ebtables-filter-multicast/description
@@ -23,18 +19,4 @@ define Package/gluon-ebtables-filter-multicast/description
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))
+$(eval $(call BuildPackageGluon,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
deleted file mode 100644
index ec0013a3..00000000
--- a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain
+++ /dev/null
@@ -1 +0,0 @@
-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
index 8af1900a..bbc621c6 100644
--- 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
@@ -1,3 +1,7 @@
+# Bridge loop avoidance
+rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-gratuitous --arp-mac-dst ff:43:05:00:00:00/ff:ff:ff:fc:00:00 -j RETURN'
+rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-gratuitous --arp-mac-dst ff:43:05:05:00:00/ff:ff:ff:ff:00:00 -j RETURN'
+
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-icmpv6 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6
index 8d3b7ec2..0058ed86 100644
--- 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
@@ -1,5 +1,3 @@
-rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type echo-request -j DROP'
-rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 139 -j DROP'
-rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp -j RETURN'
-
-rule 'MULTICAST_OUT -p IPv6 --ip6-protocol 0 -j RETURN' -- hop-by-hop
+rule 'MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type echo-request -j RETURN'
+rule 'MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 139 -j RETURN' -- ICMP Node Information Query
+rule 'MULTICAST_OUT_ICMPV6 -j ACCEPT'
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
deleted file mode 100644
index c52f122f..00000000
--- a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast
+++ /dev/null
@@ -1,2 +0,0 @@
-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-multicast/files/lib/gluon/ebtables/355-mcast-drop b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/355-mcast-drop
new file mode 100644
index 00000000..46ac01a5
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/355-mcast-drop
@@ -0,0 +1 @@
+rule ('MULTICAST_OUT -j DROP')
diff --git a/package/gluon-ebtables-filter-ra-dhcp/Makefile b/package/gluon-ebtables-filter-ra-dhcp/Makefile
index ea6a737d..c650bb89 100644
--- a/package/gluon-ebtables-filter-ra-dhcp/Makefile
+++ b/package/gluon-ebtables-filter-ra-dhcp/Makefile
@@ -4,15 +4,11 @@ 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
+include ../gluon.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
+ DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv
endef
define Package/gluon-ebtables-filter-ra-dhcp/description
@@ -22,18 +18,4 @@ define Package/gluon-ebtables-filter-ra-dhcp/description
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))
+$(eval $(call BuildPackageGluon,gluon-ebtables-filter-ra-dhcp))
diff --git a/package/gluon-ebtables-limit-arp/Makefile b/package/gluon-ebtables-limit-arp/Makefile
new file mode 100644
index 00000000..f7bdc48d
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/Makefile
@@ -0,0 +1,47 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-ebtables-limit-arp
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+include ../gluon.mk
+
+define Package/gluon-ebtables-limit-arp
+ TITLE:=Ebtables limiter for ARP packets
+ DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_LIMIT \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_MARK \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_MARK_T
+endef
+
+define Package/gluon-ebtables-limit-arp/description
+ Gluon community wifi mesh firmware framework: Ebtables rules to
+ rate-limit ARP packets.
+
+ This package adds filters to limit the amount of ARP Requests
+ devices are allowed to send into the mesh. The limits are 6 packets
+ per minute per client device, by MAC address, and 1 per second per
+ node in total.
+
+ A burst of up to 50 ARP Requests is allowed until the rate-limiting
+ takes effect (see --limit-burst in the ebtables manpage).
+
+ Furthermore, ARP Requests with a target IP already present in the
+ batman-adv DAT Cache are excluded from the rate-limiting,
+ both regarding counting and filtering, as batman-adv will respond
+ locally with no burden for the mesh. Therefore, this limiter
+ should not affect popular target IPs, like gateways.
+
+ However it should mitigate the problem of curious people or
+ smart devices scanning the whole IP range. Which could create
+ a significant amount of overhead for all participants so far.
+endef
+
+define Package/gluon-ebtables-limit-arp/install
+ $(Gluon/Build/Install)
+
+ $(INSTALL_DIR) $(1)/usr/sbin/
+ $(CP) $(PKG_BUILD_DIR)/gluon-arp-limiter $(1)/usr/sbin/gluon-arp-limiter
+endef
+
+$(eval $(call BuildPackageGluon,gluon-ebtables-limit-arp))
diff --git a/package/gluon-ebtables-limit-arp/files/etc/init.d/gluon-arp-limiter b/package/gluon-ebtables-limit-arp/files/etc/init.d/gluon-arp-limiter
new file mode 100755
index 00000000..553b4e20
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/files/etc/init.d/gluon-arp-limiter
@@ -0,0 +1,14 @@
+#!/bin/sh /etc/rc.common
+
+USE_PROCD=1
+START=20
+STOP=90
+
+start_service() {
+ procd_open_instance
+ procd_set_param command /usr/sbin/gluon-arp-limiter
+ procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
+
+ procd_set_param stderr 1
+ procd_close_instance
+}
diff --git a/package/gluon-ebtables-limit-arp/files/lib/gluon/ebtables/100-arp-limit-chains b/package/gluon-ebtables-limit-arp/files/lib/gluon/ebtables/100-arp-limit-chains
new file mode 100644
index 00000000..b39b35c8
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/files/lib/gluon/ebtables/100-arp-limit-chains
@@ -0,0 +1,3 @@
+chain('ARP_LIMIT', 'DROP')
+chain('ARP_LIMIT_DATCHECK', 'RETURN')
+chain('ARP_LIMIT_TLCHECK', 'RETURN')
diff --git a/package/gluon-ebtables-limit-arp/files/lib/gluon/ebtables/320-arp-limit-rules b/package/gluon-ebtables-limit-arp/files/lib/gluon/ebtables/320-arp-limit-rules
new file mode 100644
index 00000000..416bdd96
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/files/lib/gluon/ebtables/320-arp-limit-rules
@@ -0,0 +1,6 @@
+rule('ARP_LIMIT -j ARP_LIMIT_DATCHECK')
+rule('ARP_LIMIT --mark 0x2/0x2 -j RETURN')
+rule('ARP_LIMIT -j ARP_LIMIT_TLCHECK')
+rule('ARP_LIMIT --limit 1/sec --limit-burst 50 -j RETURN')
+
+rule('FORWARD -p ARP --logical-out br-client -o bat0 --arp-op Request -j ARP_LIMIT')
diff --git a/package/gluon-ebtables-limit-arp/src/LICENSE b/package/gluon-ebtables-limit-arp/src/LICENSE
new file mode 100644
index 00000000..d159169d
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/package/gluon-ebtables-limit-arp/src/Makefile b/package/gluon-ebtables-limit-arp/src/Makefile
new file mode 100644
index 00000000..8d361c77
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/Makefile
@@ -0,0 +1,14 @@
+# Copyright (c) 2017 Linus Lüssing
+#
+# SPDX-License-Identifier: GPL-2.0+
+# License-Filename: LICENSE
+
+all: gluon-arp-limiter
+
+CFLAGS += -Wall
+
+gluon-arp-limiter: gluon-arp-limiter.c addr_store.c lookup3.c mac.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS)
+
+clean:
+ rm -f gluon-arp-limiter
diff --git a/package/gluon-ebtables-limit-arp/src/addr_store.c b/package/gluon-ebtables-limit-arp/src/addr_store.c
new file mode 100644
index 00000000..1eb4aef6
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/addr_store.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017 Linus Lüssing
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ * License-Filename: LICENSE
+ */
+
+#include
+#include
+#include
+#include
+#include "addr_store.h"
+#include "gluon-arp-limiter.h"
+#include "lookup3.h"
+
+static struct addr_list *addr_node_alloc(void *addr,
+ struct addr_store *store)
+{
+ struct addr_list *node;
+ size_t addr_len = store->addr_len;
+
+ node = malloc(sizeof(struct addr_list) + addr_len);
+ if (!node)
+ return NULL;
+
+ memcpy(node->addr, addr, addr_len);
+ node->next = NULL;
+ node->tic = clock;
+
+ return node;
+}
+
+static struct addr_list *addr_list_search(void *addr,
+ size_t addr_len,
+ struct addr_list *list)
+{
+ struct addr_list *node = list;
+ struct addr_list *ret = NULL;
+
+ if (!node)
+ goto out;
+
+ do {
+ // Found it!
+ if (!memcmp(node->addr, addr, addr_len)) {
+ ret = node;
+ break;
+ }
+
+ node = node->next;
+ } while (node);
+
+out:
+ return ret;
+}
+
+static void addr_list_add(struct addr_list *node, struct addr_list **list)
+{
+ node->next = *list;
+ *list = node;
+}
+
+static struct addr_list **addr_store_get_bucket(void *addr,
+ struct addr_store *store)
+{
+ int len = store->addr_len / sizeof(uint32_t);
+ int idx;
+ uint32_t ret;
+
+ ret = hashword(addr, len, 0);
+ idx = ret % ADDR_STORE_NUM_BUCKETS;
+
+ return &store->buckets[idx];
+}
+
+int addr_store_add(void *addr, struct addr_store *store)
+{
+ struct addr_list **bucket = addr_store_get_bucket(addr, store);
+ struct addr_list *node = addr_list_search(addr, store->addr_len,
+ *bucket);
+
+ if (node) {
+ node->tic = clock;
+ return -EEXIST;
+ }
+
+ node = addr_node_alloc(addr, store);
+ if (!node) {
+ printf("Error: Out of memory\n");
+ return -ENOMEM;
+ }
+
+ addr_list_add(node, bucket);
+ return 0;
+}
+
+int addr_store_init(size_t addr_len,
+ void (*destructor)(struct addr_list *),
+ char *(*ntoa)(void *),
+ struct addr_store *store)
+{
+ int i;
+
+ store->addr_len = addr_len;
+ store->destructor = destructor;
+ store->ntoa = ntoa;
+
+ for (i = 0; i < ADDR_STORE_NUM_BUCKETS; i++)
+ store->buckets[i] = NULL;
+
+ return 0;
+}
+
+static char *addr_ntoa(void *addr, struct addr_store *store)
+{
+ return store->ntoa(addr);
+}
+
+static void addr_store_dump(struct addr_store *store)
+{
+ int i;
+ struct addr_list *node;
+
+ for (i = 0; i < ADDR_STORE_NUM_BUCKETS; i++) {
+ node = store->buckets[i];
+
+ if (node)
+ printf("Bucket #%i:\n", i);
+
+ while (node) {
+ printf("\t%s\n", addr_ntoa(node->addr, store));
+ node = node->next;
+ }
+ }
+}
+
+void addr_store_cleanup(struct addr_store *store)
+{
+ struct addr_list *node, *prev;
+ int i;
+
+ for (i = 0; i < ADDR_STORE_NUM_BUCKETS; i++) {
+ node = store->buckets[i];
+ prev = NULL;
+
+ while (node) {
+ if (node->tic != clock) {
+ store->destructor(node);
+
+ if (prev) {
+ prev->next = node->next;
+ free(node);
+ node = prev->next;
+ } else {
+ store->buckets[i] = node->next;
+ free(node);
+ node = store->buckets[i];
+ }
+ } else {
+ prev = node;
+ node = node->next;
+ }
+ }
+ }
+
+ addr_store_dump(store);
+}
diff --git a/package/gluon-ebtables-limit-arp/src/addr_store.h b/package/gluon-ebtables-limit-arp/src/addr_store.h
new file mode 100644
index 00000000..d3eaa78d
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/addr_store.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 Linus Lüssing
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ * License-Filename: LICENSE
+ */
+
+#ifndef _ADDR_STORE_H_
+#define _ADDR_STORE_H_
+
+#define ADDR_STORE_NUM_BUCKETS 32
+
+struct addr_list {
+ struct addr_list *next;
+ int tic;
+ char addr[0];
+};
+
+struct addr_store {
+ struct addr_list *buckets[ADDR_STORE_NUM_BUCKETS];
+ size_t addr_len;
+ void (*destructor)(struct addr_list *);
+ char *(*ntoa)(void *);
+};
+
+int addr_store_init(size_t addr_len,
+ void (*destructor)(struct addr_list *),
+ char *(*ntoa)(void *),
+ struct addr_store *store);
+int addr_store_add(void *addr, struct addr_store *store);
+void addr_store_cleanup(struct addr_store *store);
+
+#endif /* _ADDR_STORE_H_ */
diff --git a/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.c b/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.c
new file mode 100644
index 00000000..9c764006
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2017 Linus Lüssing
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ * License-Filename: LICENSE
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "addr_store.h"
+#include "gluon-arp-limiter.h"
+#include "mac.h"
+
+#define BATCTL_DC "/usr/sbin/batctl dc -H -n"
+#define BATCTL_TL "/usr/sbin/batctl tl -H -n"
+#define EBTABLES "/usr/sbin/ebtables-tiny"
+
+#define BUILD_BUG_ON(check) ((void)sizeof(int[1-2*!!(check)]))
+
+static struct addr_store ip_store;
+static struct addr_store mac_store;
+
+char *addr_mac_ntoa(void *addr)
+{
+ return mac_ntoa((struct mac_addr *)addr);
+}
+
+char *addr_inet_ntoa(void *addr)
+{
+ return inet_ntoa(*((struct in_addr *)addr));
+}
+
+static void ebt_ip_call(char *mod, struct in_addr ip)
+{
+ char str[196];
+ int ret;
+
+ snprintf(str, sizeof(str),
+ EBTABLES " %s ARP_LIMIT_DATCHECK -p ARP --arp-ip-dst %s -j mark --mark-or 0x2 --mark-target RETURN",
+ mod, inet_ntoa(ip));
+
+ ret = system(str);
+ if (ret)
+ fprintf(stderr,
+ "%i: Calling ebtables for DAT failed with status %i\n",
+ clock, ret);
+}
+
+static void ip_node_destructor(struct addr_list *node)
+{
+ struct in_addr *ip = (struct in_addr *)node->addr;
+
+ ebt_ip_call("-D", *ip);
+}
+
+static void ebt_mac_limit_call(char *mod, struct mac_addr *mac)
+{
+ char str[128];
+ int ret;
+
+ snprintf(str, sizeof(str),
+ EBTABLES " %s ARP_LIMIT_TLCHECK --source %s --limit 6/min --limit-burst 50 -j RETURN",
+ mod, mac_ntoa(mac));
+
+ ret = system(str);
+ if (ret)
+ fprintf(stderr,
+ "%i: Calling ebtables for TL failed with status %i\n",
+ clock, ret);
+}
+
+static void ebt_mac_ret_call(char *mod, struct mac_addr *mac, int add)
+{
+ char str[128];
+ int ret;
+
+ snprintf(str, sizeof(str),
+ EBTABLES " %s ARP_LIMIT_TLCHECK %s --source %s -j DROP",
+ mod, add ? "2" : "", mac_ntoa(mac));
+
+ ret = system(str);
+ if (ret)
+ fprintf(stderr,
+ "%i: Calling ebtables for TL failed with status %i\n",
+ clock, ret);
+}
+
+static void ebt_mac_call(char *mod, struct mac_addr *mac)
+{
+ if (!strncmp(mod, "-D", strlen(mod))) {
+ ebt_mac_ret_call(mod, mac, 0);
+ ebt_mac_limit_call(mod, mac);
+ } else {
+ ebt_mac_limit_call(mod, mac);
+ ebt_mac_ret_call(mod, mac, 1);
+ }
+}
+
+static void mac_node_destructor(struct addr_list *node)
+{
+ struct mac_addr *mac = (struct mac_addr *)node->addr;
+
+ ebt_mac_call("-D", mac);
+}
+
+static int dat_parse_line(const char *line, struct in_addr *ip)
+{
+ int ret;
+ char *p;
+ char *tok;
+
+ p = strpbrk(line, "0123456789");
+ if (!p) {
+ fprintf(stderr, "Error: Can't find integer in: %s\n", line);
+ return -EINVAL;
+ }
+
+ tok = strtok(p, " ");
+ if (!tok) {
+ fprintf(stderr, "Error: Can't find end of string': %s\n", line);
+ return -EINVAL;
+ }
+
+ ret = inet_aton(p, ip);
+ if (!ret) {
+ fprintf(stderr, "Error: inet_aton failed on: %s\n", p);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ebt_add_ip(struct in_addr ip)
+{
+ int ret = addr_store_add(&ip, &ip_store);
+
+ /* already stored or out-of-memory */
+ if (ret)
+ return;
+
+ ebt_ip_call("-I", ip);
+}
+
+static void ebt_add_mac(struct mac_addr *mac)
+{
+ int ret = addr_store_add(mac, &mac_store);
+
+ /* already stored or out-of-memory */
+ if (ret)
+ return;
+
+ ebt_mac_call("-I", mac);
+}
+
+static void ebt_dat_update(void)
+{
+ FILE *fp;
+ char line[256];
+ char *pline;
+ int ret;
+ struct in_addr ip;
+
+ fp = popen(BATCTL_DC, "r");
+ if (!fp) {
+ fprintf(stderr, "%i: Error: Could not call batctl dc\n", clock);
+ return;
+ }
+
+ while (1) {
+ pline = fgets(line, sizeof(line), fp);
+ if (!pline) {
+ if (!feof(fp))
+ fprintf(stderr, "%i: Error: fgets() failed\n", clock);
+ break;
+ }
+
+ ret = dat_parse_line(line, &ip);
+ if (ret < 0) {
+ fprintf(stderr, "%i: Error: Parsing line failed\n",
+ clock);
+ break;
+ }
+
+ ebt_add_ip(ip);
+ }
+
+ pclose(fp);
+}
+
+static int tl_parse_line(char *line, struct mac_addr *mac)
+{
+ int ret;
+ char *p;
+ char *tok;
+
+ p = strpbrk(line, "0123456789abcdef");
+ if (!p) {
+ fprintf(stderr, "Error: Can't find hex in: %s\n", line);
+ return -EINVAL;
+ }
+
+ tok = strtok(p, " ");
+ if (!tok) {
+ fprintf(stderr, "Error: Can't find end of string': %s\n", line);
+ return -EINVAL;
+ }
+
+ ret = mac_aton(p, mac);
+ if (!ret) {
+ fprintf(stderr, "Error: mac_aton failed on: %s\n", p);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void ebt_tl_update(void)
+{
+ FILE *fp;
+ char line[256];
+ char *pline;
+ int ret;
+ struct mac_addr mac;
+
+ fp = popen(BATCTL_TL, "r");
+ if (!fp) {
+ fprintf(stderr, "%i: Error: Could not call batctl tl\n", clock);
+ return;
+ }
+
+ while (1) {
+ pline = fgets(line, sizeof(line), fp);
+ if (!pline) {
+ if (!feof(fp))
+ fprintf(stderr, "%i: Error: fgets() failed\n", clock);
+ break;
+ }
+
+ ret = tl_parse_line(line, &mac);
+ if (ret < 0) {
+ fprintf(stderr, "%i: Error: Parsing line failed\n",
+ clock);
+ break;
+ }
+
+ if (mac_is_multicast(&mac))
+ continue;
+
+ ebt_add_mac(&mac);
+ }
+
+ pclose(fp);
+}
+
+static void ebt_dat_flush(void)
+{
+ int ret = system(EBTABLES " -F ARP_LIMIT_DATCHECK");
+
+ if (ret)
+ fprintf(stderr, "Error flushing ARP_LIMIT_DATCHECK\n");
+}
+
+static void ebt_tl_flush(void)
+{
+ int ret = system(EBTABLES " -F ARP_LIMIT_TLCHECK");
+
+ if (ret)
+ fprintf(stderr, "Error flushing ARP_LIMIT_TLCHECK\n");
+}
+
+int main(int argc, char *argv[])
+{
+ ebt_dat_flush();
+ ebt_tl_flush();
+
+ /* necessary alignment for hashword() */
+ BUILD_BUG_ON(sizeof(struct in_addr) % sizeof(uint32_t) != 0);
+ BUILD_BUG_ON(sizeof(struct mac_addr) % sizeof(uint32_t) != 0);
+
+ addr_store_init(sizeof(struct in_addr), &ip_node_destructor,
+ addr_inet_ntoa, &ip_store);
+ addr_store_init(sizeof(struct mac_addr), &mac_node_destructor,
+ addr_mac_ntoa, &mac_store);
+
+ while (1) {
+ ebt_dat_update();
+ addr_store_cleanup(&ip_store);
+
+ ebt_tl_update();
+ addr_store_cleanup(&mac_store);
+
+ sleep(30);
+ clock++;
+ }
+
+ return 0;
+}
diff --git a/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.h b/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.h
new file mode 100644
index 00000000..203ab217
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2017 Linus Lüssing
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ * License-Filename: LICENSE
+ */
+
+#ifndef _GLUON_ARP_LIMITER_H_
+#define _GLUON_ARP_LIMITER_H_
+
+int clock;
+
+#endif /* _GLUON_ARP_LIMITER_H_ */
diff --git a/package/gluon-ebtables-limit-arp/src/lookup3.c b/package/gluon-ebtables-limit-arp/src/lookup3.c
new file mode 100644
index 00000000..a6252a92
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/lookup3.c
@@ -0,0 +1,998 @@
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+These are functions for producing 32-bit hashes for hash table lookup.
+hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+are externally useful functions. Routines to test the hash are included
+if SELF_TEST is defined. You can use this free for any purpose. It's in
+the public domain. It has no warranty.
+
+You probably want to use hashlittle(). hashlittle() and hashbig()
+hash byte arrays. hashlittle() is is faster than hashbig() on
+little-endian machines. Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+then use c as the hash value. If you have a variable length array of
+4-byte integers to hash, use hashword(). If you have a byte array (like
+a character string), use hashlittle(). If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().
+
+Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+then mix those integers. This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+-------------------------------------------------------------------------------
+*/
+//#define SELF_TEST 1
+
+#include /* defines printf for tests */
+#include /* defines time_t for timings in the test */
+#include /* defines uint32_t etc */
+#include /* attempt to define endianness */
+#ifdef linux
+# include /* attempt to define endianness */
+#endif
+
+/*
+ * My best guess at if you are big-endian or little-endian. This may
+ * need adjustment.
+ */
+#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN) || \
+ (defined(i386) || defined(__i386__) || defined(__i486__) || \
+ defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
+# define HASH_LITTLE_ENDIAN 1
+# define HASH_BIG_ENDIAN 0
+#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
+ __BYTE_ORDER == __BIG_ENDIAN) || \
+ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 1
+#else
+# define HASH_LITTLE_ENDIAN 0
+# define HASH_BIG_ENDIAN 0
+#endif
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta. I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche. There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a. The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism. Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism. I did what I could. Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different. This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+--------------------------------------------------------------------
+ This works on all machines. To be useful, it requires
+ -- that the key be an array of uint32_t's, and
+ -- that the length be the number of uint32_t's in the key
+
+ The function hashword() is identical to hashlittle() on little-endian
+ machines, and identical to hashbig() on big-endian machines,
+ except that the length has to be measured in uint32_ts rather than in
+ bytes. hashlittle() is more complicated than hashword() only because
+ hashlittle() has to dance around fitting the key bytes into registers.
+--------------------------------------------------------------------
+*/
+uint32_t hashword(
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t initval) /* the previous hash, or an arbitrary value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ return c;
+}
+
+
+/*
+--------------------------------------------------------------------
+hashword2() -- same as hashword(), but take two seeds and return two
+32-bit values. pc and pb must both be nonnull, and *pc and *pb must
+both be initialized with seeds. If you pass in (*pb)==0, the output
+(*pc) will be the same as the return value from hashword().
+--------------------------------------------------------------------
+*/
+void hashword2 (
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t *pc, /* IN: seed OUT: primary hash value */
+uint32_t *pb) /* IN: more seed OUT: secondary hash value */
+{
+ uint32_t a,b,c;
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc;
+ c += *pb;
+
+ /*------------------------------------------------- handle most of the key */
+ while (length > 3)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 3;
+ k += 3;
+ }
+
+ /*------------------------------------------- handle the last 3 uint32_t's */
+ switch(length) /* all the case statements fall through */
+ {
+ case 3 : c+=k[2];
+ case 2 : b+=k[1];
+ case 1 : a+=k[0];
+ final(a,b,c);
+ case 0: /* case 0: nothing left to add */
+ break;
+ }
+ /*------------------------------------------------------ report the result */
+ *pc=c; *pb=b;
+}
+
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Two keys differing by one or two bits will have
+totally different hash values.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return c;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return c; /* zero length requires no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
+
+/*
+ * hashlittle2: return 2 32-bit hash values
+ *
+ * This is identical to hashlittle(), except it returns two 32-bit hash
+ * values instead of just one. This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key. *pc is better mixed than *pb, so use *pc first. If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ */
+void hashlittle2(
+ const void *key, /* the key to hash */
+ size_t length, /* length of the key */
+ uint32_t *pc, /* IN: primary initval, OUT: primary hash */
+ uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc;
+ c += *pb;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */
+ }
+ }
+
+ final(a,b,c);
+ *pc=c; *pb=b;
+}
+
+
+
+/*
+ * hashbig():
+ * This is the same as hashword() on big-endian machines. It is different
+ * from hashlittle() on all machines. hashbig() takes advantage of
+ * big-endian byte ordering.
+ */
+uint32_t hashbig( const void *key, size_t length, uint32_t initval)
+{
+ uint32_t a,b,c;
+ union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]<<8" actually reads beyond the end of the string, but
+ * then shifts out the part it's not allowed to read. Because the
+ * string is aligned, the illegal read is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff000000; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff00; break;
+ case 2 : a+=k[0]&0xffff0000; break;
+ case 1 : a+=k[0]&0xff000000; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<8; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */
+ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */
+ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */
+ case 1 : a+=((uint32_t)k8[0])<<24; break;
+ case 0 : return c;
+ }
+
+#endif /* !VALGRIND */
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += ((uint32_t)k[0])<<24;
+ a += ((uint32_t)k[1])<<16;
+ a += ((uint32_t)k[2])<<8;
+ a += ((uint32_t)k[3]);
+ b += ((uint32_t)k[4])<<24;
+ b += ((uint32_t)k[5])<<16;
+ b += ((uint32_t)k[6])<<8;
+ b += ((uint32_t)k[7]);
+ c += ((uint32_t)k[8])<<24;
+ c += ((uint32_t)k[9])<<16;
+ c += ((uint32_t)k[10])<<8;
+ c += ((uint32_t)k[11]);
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[11];
+ case 11: c+=((uint32_t)k[10])<<8;
+ case 10: c+=((uint32_t)k[9])<<16;
+ case 9 : c+=((uint32_t)k[8])<<24;
+ case 8 : b+=k[7];
+ case 7 : b+=((uint32_t)k[6])<<8;
+ case 6 : b+=((uint32_t)k[5])<<16;
+ case 5 : b+=((uint32_t)k[4])<<24;
+ case 4 : a+=k[3];
+ case 3 : a+=((uint32_t)k[2])<<8;
+ case 2 : a+=((uint32_t)k[1])<<16;
+ case 1 : a+=((uint32_t)k[0])<<24;
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ return c;
+}
+
+
+#ifdef SELF_TEST
+
+/* used for timings */
+void driver1()
+{
+ uint8_t buf[256];
+ uint32_t i;
+ uint32_t h=0;
+ time_t a,z;
+
+ time(&a);
+ for (i=0; i<256; ++i) buf[i] = 'x';
+ for (i=0; i<1; ++i)
+ {
+ h = hashlittle(&buf[0],1,h);
+ }
+ time(&z);
+ if (z-a > 0) printf("time %d %.8x\n", z-a, h);
+}
+
+/* check that every input bit changes every output bit half the time */
+#define HASHSTATE 1
+#define HASHLEN 1
+#define MAXPAIR 60
+#define MAXLEN 70
+void driver2()
+{
+ uint8_t qa[MAXLEN+1], qb[MAXLEN+2], *a = &qa[0], *b = &qb[1];
+ uint32_t c[HASHSTATE], d[HASHSTATE], i=0, j=0, k, l, m=0, z;
+ uint32_t e[HASHSTATE],f[HASHSTATE],g[HASHSTATE],h[HASHSTATE];
+ uint32_t x[HASHSTATE],y[HASHSTATE];
+ uint32_t hlen;
+
+ printf("No more than %d trials should ever be needed \n",MAXPAIR/2);
+ for (hlen=0; hlen < MAXLEN; ++hlen)
+ {
+ z=0;
+ for (i=0; i>(8-j));
+ c[0] = hashlittle(a, hlen, m);
+ b[i] ^= ((k+1)<>(8-j));
+ d[0] = hashlittle(b, hlen, m);
+ /* check every bit is 1, 0, set, and not set at least once */
+ for (l=0; lz) z=k;
+ if (k==MAXPAIR)
+ {
+ printf("Some bit didn't change: ");
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x ",
+ e[0],f[0],g[0],h[0],x[0],y[0]);
+ printf("i %d j %d m %d len %d\n", i, j, m, hlen);
+ }
+ if (z==MAXPAIR) goto done;
+ }
+ }
+ }
+ done:
+ if (z < MAXPAIR)
+ {
+ printf("Mix success %2d bytes %2d initvals ",i,m);
+ printf("required %d trials\n", z/2);
+ }
+ }
+ printf("\n");
+}
+
+/* Check for reading beyond the end of the buffer and alignment problems */
+void driver3()
+{
+ uint8_t buf[MAXLEN+20], *b;
+ uint32_t len;
+ uint8_t q[] = "This is the time for all good men to come to the aid of their country...";
+ uint32_t h;
+ uint8_t qq[] = "xThis is the time for all good men to come to the aid of their country...";
+ uint32_t i;
+ uint8_t qqq[] = "xxThis is the time for all good men to come to the aid of their country...";
+ uint32_t j;
+ uint8_t qqqq[] = "xxxThis is the time for all good men to come to the aid of their country...";
+ uint32_t ref,x,y;
+ uint8_t *p;
+
+ printf("Endianness. These lines should all be the same (for values filled in):\n");
+ printf("%.8x %.8x %.8x\n",
+ hashword((const uint32_t *)q, (sizeof(q)-1)/4, 13),
+ hashword((const uint32_t *)q, (sizeof(q)-5)/4, 13),
+ hashword((const uint32_t *)q, (sizeof(q)-9)/4, 13));
+ p = q;
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qq[1];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qqq[2];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ p = &qqqq[3];
+ printf("%.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x %.8x\n",
+ hashlittle(p, sizeof(q)-1, 13), hashlittle(p, sizeof(q)-2, 13),
+ hashlittle(p, sizeof(q)-3, 13), hashlittle(p, sizeof(q)-4, 13),
+ hashlittle(p, sizeof(q)-5, 13), hashlittle(p, sizeof(q)-6, 13),
+ hashlittle(p, sizeof(q)-7, 13), hashlittle(p, sizeof(q)-8, 13),
+ hashlittle(p, sizeof(q)-9, 13), hashlittle(p, sizeof(q)-10, 13),
+ hashlittle(p, sizeof(q)-11, 13), hashlittle(p, sizeof(q)-12, 13));
+ printf("\n");
+
+ /* check that hashlittle2 and hashlittle produce the same results */
+ i=47; j=0;
+ hashlittle2(q, sizeof(q), &i, &j);
+ if (hashlittle(q, sizeof(q), 47) != i)
+ printf("hashlittle2 and hashlittle mismatch\n");
+
+ /* check that hashword2 and hashword produce the same results */
+ len = 0xdeadbeef;
+ i=47, j=0;
+ hashword2(&len, 1, &i, &j);
+ if (hashword(&len, 1, 47) != i)
+ printf("hashword2 and hashword mismatch %x %x\n",
+ i, hashword(&len, 1, 47));
+
+ /* check hashlittle doesn't read before or after the ends of the string */
+ for (h=0, b=buf+1; h<8; ++h, ++b)
+ {
+ for (i=0; i /* defines uint32_t etc */
+
+uint32_t hashword(
+const uint32_t *k, /* the key, an array of uint32_t values */
+size_t length, /* the length of the key, in uint32_ts */
+uint32_t initval); /* the previous hash, or an arbitrary value */
diff --git a/package/gluon-ebtables-limit-arp/src/mac.c b/package/gluon-ebtables-limit-arp/src/mac.c
new file mode 100644
index 00000000..863c540e
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/mac.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Linus Lüssing
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ * License-Filename: LICENSE
+ */
+
+#include
+#include
+#include
+#include "mac.h"
+
+#define ETH_STRLEN (sizeof("aa:bb:cc:dd:ee:ff") - 1)
+
+char mntoa_buf[ETH_STRLEN+1];
+
+int mac_aton(const char *cp, struct mac_addr *mac)
+{
+ struct mac_addr m;
+ int ret;
+
+ if (strlen(cp) != ETH_STRLEN)
+ return 0;
+
+ memset(&m, 0, sizeof(m));
+
+ ret = sscanf(cp, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ &m.storage[0], &m.storage[1], &m.storage[2],
+ &m.storage[3], &m.storage[4], &m.storage[5]);
+
+ if (ret != ETH_ALEN)
+ return 0;
+
+ *mac = m;
+ return 1;
+}
+
+char *mac_ntoa(struct mac_addr *mac)
+{
+ unsigned char *m = mac->storage;
+
+ snprintf(mntoa_buf, sizeof(mntoa_buf),
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+
+ return mntoa_buf;
+}
diff --git a/package/gluon-ebtables-limit-arp/src/mac.h b/package/gluon-ebtables-limit-arp/src/mac.h
new file mode 100644
index 00000000..11567ade
--- /dev/null
+++ b/package/gluon-ebtables-limit-arp/src/mac.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2017 Linus Lüssing
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ * License-Filename: LICENSE
+ */
+
+#ifndef _MAC_H_
+#define _MAC_H_
+
+struct mac_addr {
+ /* 8 instead of 6 for multiples of uint32_t for hashword() */
+ unsigned char storage[8];
+};
+
+int mac_aton(const char *cp, struct mac_addr *mac);
+char *mac_ntoa(struct mac_addr *mac);
+
+static inline int mac_is_multicast(struct mac_addr *addr)
+{
+ return addr->storage[0] & 0x01;
+}
+
+#endif /* _MAC_H_ */
diff --git a/package/gluon-ebtables-source-filter/Makefile b/package/gluon-ebtables-source-filter/Makefile
new file mode 100644
index 00000000..2c14a793
--- /dev/null
+++ b/package/gluon-ebtables-source-filter/Makefile
@@ -0,0 +1,19 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-ebtables-source-filter
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+include ../gluon.mk
+
+define Package/gluon-ebtables-source-filter
+ TITLE:=Ebtables rules to filter unreasonable L2 traffic.
+ DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv
+endef
+
+define Package/gluon-ebtables-source-filter/description
+ This package adds an additional layer-2 filter-ruleset to prevent unreasonable
+ traffic entering the network via the nodes.
+endef
+
+$(eval $(call BuildPackageGluon,gluon-ebtables-source-filter))
diff --git a/package/gluon-ebtables-source-filter/check_site.lua b/package/gluon-ebtables-source-filter/check_site.lua
new file mode 100644
index 00000000..56d229c0
--- /dev/null
+++ b/package/gluon-ebtables-source-filter/check_site.lua
@@ -0,0 +1,2 @@
+need_string_match(in_domain({'prefix4'}), '^%d+.%d+.%d+.%d+/%d+$', false)
+need_string_array_match(in_domain({'extra_prefixes6'}), '^[%x:]+/%d+$', false)
diff --git a/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/100-local-forward-chain b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/100-local-forward-chain
new file mode 100644
index 00000000..b9f4467d
--- /dev/null
+++ b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/100-local-forward-chain
@@ -0,0 +1 @@
+chain('LOCAL_FORWARD', 'DROP')
diff --git a/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-arp b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-arp
new file mode 100644
index 00000000..06436cf2
--- /dev/null
+++ b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-arp
@@ -0,0 +1,6 @@
+local prefix4 = require('gluon.site').prefix4()
+
+if prefix4 then
+ rule('LOCAL_FORWARD -p ARP --arp-ip-src ' .. prefix4 .. ' --arp-ip-dst ' .. prefix4 .. ' -j RETURN')
+ rule('LOCAL_FORWARD -p ARP --arp-ip-src 0.0.0.0 --arp-ip-dst ' .. prefix4 .. ' -j RETURN')
+end
diff --git a/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-ipv4 b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-ipv4
new file mode 100644
index 00000000..e712c5fb
--- /dev/null
+++ b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-ipv4
@@ -0,0 +1,6 @@
+local prefix4 = require('gluon.site').prefix4()
+
+if prefix4 then
+ rule('LOCAL_FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 67 -j RETURN')
+ rule('LOCAL_FORWARD -p IPv4 --ip-src ' .. prefix4 .. ' -j RETURN')
+end
diff --git a/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-ipv6 b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-ipv6
new file mode 100644
index 00000000..6be0de70
--- /dev/null
+++ b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/110-local-forward-allow-ipv6
@@ -0,0 +1,9 @@
+site = require('gluon.site')
+
+rule('LOCAL_FORWARD -p IPv6 --ip6-src fe80::/64 -j RETURN')
+rule('LOCAL_FORWARD -p IPv6 --ip6-src ::/128 --ip6-proto ipv6-icmp -j RETURN')
+rule('LOCAL_FORWARD -p IPv6 --ip6-src ' .. site.prefix6() .. ' -j RETURN')
+
+for _, prefix in ipairs(site.extra_prefixes6({})) do
+ rule('LOCAL_FORWARD -p IPv6 --ip6-src ' .. prefix .. ' -j RETURN')
+end
diff --git a/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/300-local-forward-rules b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/300-local-forward-rules
new file mode 100644
index 00000000..6c5a9257
--- /dev/null
+++ b/package/gluon-ebtables-source-filter/files/lib/gluon/ebtables/300-local-forward-rules
@@ -0,0 +1 @@
+rule('FORWARD --logical-in br-client -i ! bat0 -j LOCAL_FORWARD')
diff --git a/package/gluon-ebtables/Makefile b/package/gluon-ebtables/Makefile
index 39c654c1..ca83fcf4 100644
--- a/package/gluon-ebtables/Makefile
+++ b/package/gluon-ebtables/Makefile
@@ -4,33 +4,25 @@ PKG_NAME:=gluon-ebtables
PKG_VERSION:=1
PKG_RELEASE:=1
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+PKG_CONFIG_DEPENDS := CONFIG_GLUON_SPECIALIZE_KERNEL
-include $(INCLUDE_DIR)/package.mk
+include ../gluon.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
+ DEPENDS:=+gluon-core +ebtables-tiny \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_T_FILTER \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_T_NAT \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_ARP \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_IP \
+ +@GLUON_SPECIALIZE_KERNEL:KERNEL_BRIDGE_EBT_IP6 \
+ +!GLUON_SPECIALIZE_KERNEL:kmod-ebtables \
+ +!GLUON_SPECIALIZE_KERNEL:kmod-ebtables-ipv4 \
+ +!GLUON_SPECIALIZE_KERNEL:kmod-ebtables-ipv6
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))
+$(eval $(call BuildPackageGluon,gluon-ebtables))
diff --git a/package/gluon-ebtables/check_site.lua b/package/gluon-ebtables/check_site.lua
new file mode 100644
index 00000000..68747b78
--- /dev/null
+++ b/package/gluon-ebtables/check_site.lua
@@ -0,0 +1 @@
+need_boolean({'mesh', 'filter_membership_reports'}, false)
diff --git a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables
index 5a770452..60add180 100755
--- a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables
+++ b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables
@@ -7,13 +7,13 @@
# 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
+# $ /etc/init.d/gluon-ebtables stop
# Inserting all rules:
-# $ ./firewall-ebtables start
+# $ /etc/init.d/gluon-ebtables start
# Inserting a specific rule file:
-# $ ./firewall-ebtables start /lib/gluon/ebtables/100-mcast-chain
+# $ /etc/init.d/gluon-ebtables start /lib/gluon/ebtables/100-mcast-chain
# Removing a specific rule file:
-# $ ./firewall-ebtables stop /lib/gluon/ebtables/100-mcast-chain
+# $ /etc/init.d/gluon-ebtables stop /lib/gluon/ebtables/100-mcast-chain
START=19
@@ -24,12 +24,15 @@ exec_file() {
local file="$1"
/usr/bin/lua -e "
- function rule(command)
+ function rule(command, table)
+ table = table or 'filter'
os.execute($EBTABLES_RULE)
end
- function chain(name, policy)
+ function chain(name, policy, table)
+ table = table or 'filter'
os.execute($EBTABLES_CHAIN)
end
+
" "$file"
}
@@ -48,8 +51,12 @@ exec_all() {
start() {
(
- export EBTABLES_RULE='"ebtables -A " .. command'
- export EBTABLES_CHAIN='"ebtables -N " .. name .. " -P " .. policy'
+ export EBTABLES_RULE='"ebtables-tiny -t " .. table .. " -A " .. command'
+ export EBTABLES_CHAIN='"ebtables-tiny -t " .. table .. " -N " .. name .. " -P " .. policy'
+
+ # Contains /var/lib/ebtables/lock for '--concurrent'
+ [ ! -d "/var/lib/ebtables" ] && \
+ mkdir -p /var/lib/ebtables
if [ -z "$1" ]; then
exec_all ''
@@ -61,8 +68,8 @@ start() {
stop() {
(
- export EBTABLES_RULE='"ebtables -D " .. command'
- export EBTABLES_CHAIN='"ebtables -X " .. name'
+ export EBTABLES_RULE='"ebtables-tiny -t " .. table .. " -D " .. command'
+ export EBTABLES_CHAIN='"ebtables-tiny -t " .. table .. " -X " .. name'
if [ -z "$1" ]; then
exec_all '-r'
diff --git a/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain b/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain
index 31c19c53..62b92947 100644
--- a/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain
+++ b/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain
@@ -1,2 +1,9 @@
chain('IN_ONLY', 'RETURN')
chain('OUT_ONLY', 'RETURN')
+
+-- nat chain runs early, so we can drop IGMP/MLD
+chain('MULTICAST_IN', 'RETURN', 'nat')
+chain('MULTICAST_IN_ICMPV6', 'RETURN', 'nat')
+
+chain('MULTICAST_OUT', 'RETURN')
+chain('MULTICAST_OUT_ICMPV6', '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
index b1cd4e24..74486ae5 100644
--- a/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules
+++ b/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules
@@ -1,2 +1,7 @@
-rule 'IN_ONLY --logical-in br-client -i ! bat0 -j DROP'
-rule 'OUT_ONLY --logical-out br-client -o ! bat0 -j DROP'
+rule 'IN_ONLY --logical-in br-client -i bat0 -j RETURN'
+rule 'IN_ONLY --logical-in br-client -i local-port -j RETURN'
+rule 'IN_ONLY --logical-in br-client -j DROP'
+
+rule 'OUT_ONLY --logical-out br-client -o bat0 -j RETURN'
+rule 'OUT_ONLY --logical-out br-client -o local-port -j RETURN'
+rule 'OUT_ONLY --logical-out br-client -j DROP'
diff --git a/package/gluon-ebtables/files/lib/gluon/ebtables/105-mcast-drop-igmp-mld b/package/gluon-ebtables/files/lib/gluon/ebtables/105-mcast-drop-igmp-mld
new file mode 100644
index 00000000..3b1ecab3
--- /dev/null
+++ b/package/gluon-ebtables/files/lib/gluon/ebtables/105-mcast-drop-igmp-mld
@@ -0,0 +1,20 @@
+local site = require 'gluon.site'
+
+rule('MULTICAST_IN -p IPv4 --ip-protocol igmp --ip-igmp-type membership-query -j DROP', 'nat')
+rule('MULTICAST_OUT -p IPv4 --ip-protocol igmp --ip-igmp-type membership-query -j DROP')
+
+rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 130 -j DROP') -- MLD Query
+rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 130 -j DROP', 'nat') -- MLD Query
+
+if site.mesh.filter_membership_reports(true) then
+ rule('MULTICAST_IN -p IPv4 --ip-protocol igmp -j DROP', 'nat')
+ rule('MULTICAST_OUT -p IPv4 --ip-protocol igmp -j DROP')
+
+ rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 131 -j DROP') -- MLDv1 Report
+ rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 132 -j DROP') -- MLDv1 Done
+ rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 143 -j DROP') -- MLDv2 Report
+
+ rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 131 -j DROP', 'nat') -- MLDv1 Report
+ rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 132 -j DROP', 'nat') -- MLDv1 Done
+ rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 143 -j DROP', 'nat') -- MLDv2 Report
+end
diff --git a/package/gluon-ebtables/files/lib/gluon/ebtables/350-mcast-dir-rules b/package/gluon-ebtables/files/lib/gluon/ebtables/350-mcast-dir-rules
new file mode 100644
index 00000000..5b625f99
--- /dev/null
+++ b/package/gluon-ebtables/files/lib/gluon/ebtables/350-mcast-dir-rules
@@ -0,0 +1,6 @@
+rule ('PREROUTING -d Multicast --logical-in br-client -i bat0 -j MULTICAST_IN', 'nat')
+rule 'OUTPUT -d Multicast --logical-out br-client -o bat0 -j MULTICAST_OUT'
+rule 'FORWARD -d Multicast --logical-out br-client -o bat0 -j MULTICAST_OUT'
+
+rule ('MULTICAST_IN -p IPv6 --ip6-protocol ipv6-icmp -j MULTICAST_IN_ICMPV6', 'nat')
+rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp -j MULTICAST_OUT_ICMPV6'
diff --git a/package/gluon-l3roamd/Makefile b/package/gluon-l3roamd/Makefile
new file mode 100644
index 00000000..9cae9bb1
--- /dev/null
+++ b/package/gluon-l3roamd/Makefile
@@ -0,0 +1,13 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-l3roamd
+PKG_VERSION:=1
+
+include ../gluon.mk
+
+define Package/gluon-l3roamd
+ TITLE:=Configure l3roamd for babel
+ DEPENDS:=+gluon-core +l3roamd
+endef
+
+$(eval $(call BuildPackageGluon,gluon-l3roamd))
diff --git a/package/gluon-l3roamd/files/etc/init.d/gluon-l3roamd b/package/gluon-l3roamd/files/etc/init.d/gluon-l3roamd
new file mode 100755
index 00000000..d40e1a18
--- /dev/null
+++ b/package/gluon-l3roamd/files/etc/init.d/gluon-l3roamd
@@ -0,0 +1,29 @@
+#!/bin/sh /etc/rc.common
+
+START=55
+
+USE_PROCD=1
+PROG=/usr/sbin/l3roamd
+
+start_service () {
+ local interfaces=$(
+ for dev in $(gluon-list-mesh-interfaces); do echo " -m $dev"; done
+ [ "$(ifstatus local_node | jsonfilter -e '@.up')" = 'true' ] && echo ' -i local-node'
+ )
+
+ procd_open_instance
+ procd_set_param stdout 1
+ procd_set_param stderr 1
+ procd_set_param respawn ${respawn_threshold:-3660} ${respawn_timeout:-5} ${respawn_retry:-0}
+ procd_set_param command "$PROG" -s /var/run/l3roamd.sock -p $(lua -e 'print(require("gluon.site").prefix6())') $interfaces -t 254 -a $(uci get network.loopback.ip6addr | cut -d/ -f1) -4 0:0:0:0:0:ffff::/96 -b br-client
+ procd_close_instance
+}
+
+service_triggers() {
+ local script=$(readlink "$initscript")
+ local name=$(basename "${script:-$initscript}")
+
+ procd_open_trigger
+ procd_add_raw_trigger 'interface.*' 0 "/etc/init.d/$name" reload
+ procd_close_trigger
+}
diff --git a/package/gluon-l3roamd/luasrc/lib/gluon/upgrade/150-firewall-l3roamd b/package/gluon-l3roamd/luasrc/lib/gluon/upgrade/150-firewall-l3roamd
new file mode 100755
index 00000000..b1b28844
--- /dev/null
+++ b/package/gluon-l3roamd/luasrc/lib/gluon/upgrade/150-firewall-l3roamd
@@ -0,0 +1,12 @@
+#!/usr/bin/lua
+local uci = require('simple-uci').cursor()
+
+uci:section('firewall', 'rule', 'mesh_l3roamd', {
+ name = 'mesh_l3roamd',
+ src = 'mesh',
+ dest_port = '5523',
+ proto = 'udp',
+ target = 'ACCEPT',
+})
+
+uci:save('firewall')
diff --git a/package/gluon-l3roamd/luasrc/lib/gluon/upgrade/380-gluon-l3roamd-route b/package/gluon-l3roamd/luasrc/lib/gluon/upgrade/380-gluon-l3roamd-route
new file mode 100755
index 00000000..5d0fb42e
--- /dev/null
+++ b/package/gluon-l3roamd/luasrc/lib/gluon/upgrade/380-gluon-l3roamd-route
@@ -0,0 +1,6 @@
+#!/usr/bin/lua
+
+uci:delete('network', 'l3roam')
+uci:delete('network', 'l3roamd_client')
+
+uci:save('network')
diff --git a/package/gluon-legacy/Makefile b/package/gluon-legacy/Makefile
deleted file mode 100644
index 7320fba6..00000000
--- a/package/gluon-legacy/Makefile
+++ /dev/null
@@ -1,40 +0,0 @@
-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
deleted file mode 100644
index 1ec26de0..00000000
--- a/package/gluon-legacy/check_site.lua
+++ /dev/null
@@ -1,8 +0,0 @@
-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
deleted file mode 100755
index 78436676..00000000
--- a/package/gluon-legacy/files/lib/gluon/upgrade/000-legacy
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/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
deleted file mode 100755
index 691e4ad7..00000000
--- a/package/gluon-legacy/files/lib/gluon/upgrade/019-legacy-interfaces
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/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')
-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
deleted file mode 100755
index d8fd1720..00000000
--- a/package/gluon-legacy/files/lib/gluon/upgrade/210-legacy-wireless
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/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')
-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
deleted file mode 100755
index d2a67484..00000000
--- a/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-setup-mode
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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')
-
- 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
deleted file mode 100755
index 652c53c9..00000000
--- a/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-simple-tc
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/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('simple-tc', 'interface', 'mesh_vpn',
- {
- ifname = 'mesh-vpn',
- enabled = old.enabled,
- limit_ingress = old.downstream,
- limit_egress = old.upstream,
- }
- )
-
- uci:save('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
deleted file mode 100755
index 21f95e07..00000000
--- a/package/gluon-legacy/files/lib/gluon/upgrade/390-legacy-mesh-vpn-fastd
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/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')
-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
deleted file mode 100755
index efb6b675..00000000
--- a/package/gluon-legacy/files/lib/gluon/upgrade/990-legacy-late
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/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
index d0e99373..366fd7b8 100644
--- a/package/gluon-lock-password/Makefile
+++ b/package/gluon-lock-password/Makefile
@@ -4,13 +4,9 @@ PKG_NAME:=gluon-lock-password
PKG_VERSION:=1
PKG_RELEASE:=1
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(INCLUDE_DIR)/package.mk
+include ../gluon.mk
define Package/gluon-lock-password
- SECTION:=gluon
- CATEGORY:=Gluon
TITLE:=Locks the root account by default
DEPENDS:=+gluon-core
endef
@@ -19,18 +15,4 @@ 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))
+$(eval $(call BuildPackageGluon,gluon-lock-password))
diff --git a/package/gluon-luci-admin/Makefile b/package/gluon-luci-admin/Makefile
deleted file mode 100644
index 3e20d179..00000000
--- a/package/gluon-luci-admin/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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:=1
-PKG_RELEASE:=1
-
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
-define Package/gluon-luci-admin
- SECTION:=gluon
- CATEGORY:=Gluon
- TITLE:=Luci based simple administration interface for mesh nodes
- DEPENDS:=gluon-config-mode-core-virtual
-endef
-
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
- $(call GluonBuildI18N,gluon-luci-admin,i18n)
-endef
-
-define Package/gluon-luci-admin/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-luci-admin,$(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
deleted file mode 100644
index 55c0a248..00000000
--- a/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/index.lua
+++ /dev/null
@@ -1,39 +0,0 @@
---[[
-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"), _("Information"), 1).ignoreindex = true
- entry({"admin", "remote"}, cbi("admin/remote"), _("Remote access"), 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
deleted file mode 100644
index 2365f02c..00000000
--- a/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/upgrade.lua
+++ /dev/null
@@ -1,133 +0,0 @@
---[[
-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"), _("Upgrade firmware"), 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(
- "/sbin/sysupgrade -T %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
deleted file mode 100644
index e9ceba70..00000000
--- a/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/info.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-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
deleted file mode 100644
index c79c0195..00000000
--- a/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/remote.lua
+++ /dev/null
@@ -1,105 +0,0 @@
---[[
-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", translate("SSH keys"))
-m.pageaction = false
-m.template = "admin/expertmode"
-
-if fs.access("/etc/config/dropbear") then
- local s = m:section(TypedSection, "_dummy1", nil,
- translate("You can provide your SSH keys here (one per line):"))
-
- 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"):trim() .. "\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", translate("Password"))
-m2.reset = false
-m2.pageaction = false
-m2.template = "admin/expertmode"
-
-local s = m2:section(TypedSection, "_dummy2", nil, translate(
- "Alternatively, you can set a password to access you node. Please choose a secure password you don't use anywhere else.
"
- .. "If you set an empty password, login via password will be disabled. This is the default."))
-
-s.addremove = false
-s.anonymous = true
-
-local pw1 = s:option(Value, "pw1", translate("Password"))
-pw1.password = true
-
-local pw2 = s:option(Value, "pw2", translate("Confirmation"))
-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('root', v1) == 0 then
- m2.message = translate("Password changed.")
- else
- m2.errmessage = translate("Unable to change the password.")
- 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 = translate("Password removed.")
- end
- else
- m2.errmessage = translate("The password and the confirmation differ.")
- 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
deleted file mode 100644
index 53947f3c..00000000
--- a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/expertmode.htm
+++ /dev/null
@@ -1,56 +0,0 @@
-<% if not self.embedded then %>
-
-<% 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
deleted file mode 100644
index da403c1b..00000000
--- a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/info.htm
+++ /dev/null
@@ -1,45 +0,0 @@
-<%-
- local fs = require 'nixio.fs'
- local uci = require('luci.model.uci').cursor()
- local util = require 'luci.util'
- local i18n = require 'luci.i18n'
-
- local site = require 'gluon.site_config'
- local sysconfig = require 'gluon.sysconfig'
- local platform = require 'gluon.platform'
-
-
- local keys = {
- hostname = i18n.translate('Hostname'),
- primary_mac = i18n.translate('MAC address'),
- model = i18n.translate('Hardware model'),
- version = i18n.translate('Gluon version'),
- release = i18n.translate('Firmware release'),
- site = i18n.translate('Site'),
- pubkey = i18n.translate('Public VPN key'),
- }
-
- 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
--%>
-
<%:Information%>
-<% 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
deleted file mode 100644
index a2b82235..00000000
--- a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade.htm
+++ /dev/null
@@ -1,55 +0,0 @@
-<%#
-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%>
-
-
- <%:The firmware is currently being upgraded.%>
- <%:Don't switch off the device in any circumstance!%>
- <%:The upgrade will take a few minutes. When it is finished, your node will reboot automatically.%>
-
-
-
-
-
diff --git a/package/gluon-luci-autoupdater/Makefile b/package/gluon-luci-autoupdater/Makefile
deleted file mode 100644
index 650c4692..00000000
--- a/package/gluon-luci-autoupdater/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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:=1
-PKG_RELEASE:=1
-
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
-define Package/gluon-luci-autoupdater
- SECTION:=gluon
- CATEGORY:=Gluon
- TITLE:=Luci module for gluon-autoupdater
- DEPENDS:=+gluon-luci-admin +gluon-autoupdater
-endef
-
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
- $(call GluonBuildI18N,gluon-luci-autoupdater,i18n)
-endef
-
-define Package/gluon-luci-autoupdater/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-luci-autoupdater,$(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
deleted file mode 100644
index 64e1acbd..00000000
--- a/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/controller/admin/autoupdater.lua
+++ /dev/null
@@ -1,19 +0,0 @@
---[[
-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"), _("Automatic updates"), 80)
-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
deleted file mode 100644
index a8f9d3b3..00000000
--- a/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/model/cbi/admin/autoupdater.lua
+++ /dev/null
@@ -1,29 +0,0 @@
---[[
-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", translate("Automatic updates"))
-m.pageaction = false
-m.template = "admin/expertmode"
-
-s = m:section(TypedSection, "autoupdater", nil)
-s.addremove = false
-s.anonymous = true
-
-s:option(Flag, "enabled", translate("Enable"))
-f = s:option(ListValue, "branch", translate("Branch"))
-
-uci.cursor():foreach("autoupdater", "branch", function (section) f:value(section[".name"]) end)
-
-return m
-
diff --git a/package/gluon-luci-mesh-vpn-fastd/Makefile b/package/gluon-luci-mesh-vpn-fastd/Makefile
deleted file mode 100644
index 1be12ccc..00000000
--- a/package/gluon-luci-mesh-vpn-fastd/Makefile
+++ /dev/null
@@ -1,41 +0,0 @@
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=gluon-luci-mesh-vpn-fastd
-PKG_VERSION:=1
-PKG_RELEASE:=1
-
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
-define Package/gluon-luci-mesh-vpn-fastd
- SECTION:=gluon
- CATEGORY:=Gluon
- TITLE:=Luci module to enable and disable encryption for the mesh VPN
- DEPENDS:=+gluon-luci-admin +gluon-mesh-vpn-fastd
-endef
-
-define Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
- $(call GluonBuildI18N,gluon-mesh-vpn-fastd,i18n)
-endef
-
-define Package/gluon-luci-mesh-vpn-fastd/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-mesh-vpn-fastd,$(1))
-endef
-
-define Package/gluon-luci-mesh-vpn-fastd/postinst
-#!/bin/sh
-$(call GluonCheckSite,check_site.lua)
-endef
-
-$(eval $(call BuildPackage,gluon-luci-mesh-vpn-fastd))
diff --git a/package/gluon-luci-mesh-vpn-fastd/check_site.lua b/package/gluon-luci-mesh-vpn-fastd/check_site.lua
deleted file mode 100644
index 42ff5a50..00000000
--- a/package/gluon-luci-mesh-vpn-fastd/check_site.lua
+++ /dev/null
@@ -1,2 +0,0 @@
-assert(need_boolean('fastd_mesh_vpn.configurable') == true,
- "site.conf error: expected `fastd_mesh_vpn.configurable' to be true")
diff --git a/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/controller/admin/mesh_vpn_fastd.lua b/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/controller/admin/mesh_vpn_fastd.lua
deleted file mode 100644
index 8141c44a..00000000
--- a/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/controller/admin/mesh_vpn_fastd.lua
+++ /dev/null
@@ -1,5 +0,0 @@
-module("luci.controller.admin.mesh_vpn_fastd", package.seeall)
-
-function index()
- entry({"admin", "mesh_vpn_fastd"}, cbi("admin/mesh_vpn_fastd"), _("Mesh VPN"), 20)
-end
diff --git a/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/model/cbi/admin/mesh_vpn_fastd.lua b/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/model/cbi/admin/mesh_vpn_fastd.lua
deleted file mode 100644
index 14bb5783..00000000
--- a/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/model/cbi/admin/mesh_vpn_fastd.lua
+++ /dev/null
@@ -1,41 +0,0 @@
-local uci = luci.model.uci.cursor()
-local util = luci.util
-
-local f = SimpleForm('mesh_vpn', translate('Mesh VPN'))
-f.template = "admin/expertmode"
-
-local s = f:section(SimpleSection)
-
-local o = s:option(Value, 'mode')
-o.template = "gluon/cbi/mesh-vpn-fastd-mode"
-
-local methods = uci:get('fastd', 'mesh_vpn', 'method')
-if util.contains(methods, 'null') then
- o.default = 'performance'
-else
- o.default = 'security'
-end
-
-function f.handle(self, state, data)
- if state == FORM_VALID then
- local site = require 'gluon.site_config'
-
- local methods = {}
- if data.mode == 'performance' then
- table.insert(methods, 'null')
- end
-
- for _, method in ipairs(site.fastd_mesh_vpn.methods) do
- if method ~= 'null' then
- table.insert(methods, method)
- end
- end
-
- uci:set('fastd', 'mesh_vpn', 'method', methods)
-
- uci:save('fastd')
- uci:commit('fastd')
- end
-end
-
-return f
diff --git a/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/view/gluon/cbi/mesh-vpn-fastd-mode.htm b/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/view/gluon/cbi/mesh-vpn-fastd-mode.htm
deleted file mode 100644
index 0899fa1d..00000000
--- a/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/view/gluon/cbi/mesh-vpn-fastd-mode.htm
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
- />
-
-
-
-
- <%= translate(
- 'In security mode, the mesh VPN uses an encrypted tunnel to connect to the VPN servers. ' ..
- 'The encryption ensures that it is impossible for your internet access provider to see what ' ..
- 'data is exchanged over your node.'
- ) %>
-
-
-
-
-
-
-
- />
-
-
-
-
- <%= translate(
- 'In performance mode, no encryption is used. This usually allows for higher throughput, but the data exchanged over your node is not ' ..
- 'protected against eavesdropping.'
- ) %>
-
-
-
-
diff --git a/package/gluon-luci-node-role/Makefile b/package/gluon-luci-node-role/Makefile
deleted file mode 100644
index ded7bd3f..00000000
--- a/package/gluon-luci-node-role/Makefile
+++ /dev/null
@@ -1,41 +0,0 @@
-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
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
-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
- $(call GluonBuildI18N,gluon-luci-node-role,i18n)
-endef
-
-define Package/gluon-luci-node-role/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-luci-node-role,$(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
deleted file mode 100644
index ab01eeb5..00000000
--- a/package/gluon-luci-node-role/check_site.lua
+++ /dev/null
@@ -1,2 +0,0 @@
-need_string 'roles.default'
-need_string_array 'roles.list'
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
deleted file mode 100644
index cfca1275..00000000
--- a/package/gluon-luci-node-role/files/usr/lib/lua/luci/controller/admin/noderole.lua
+++ /dev/null
@@ -1,5 +0,0 @@
-module("luci.controller.admin.noderole", package.seeall)
-
-function index()
- entry({"admin", "noderole"}, cbi("admin/noderole"), "Node role", 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
deleted file mode 100644
index e7832ad9..00000000
--- a/package/gluon-luci-node-role/files/usr/lib/lua/luci/model/cbi/admin/noderole.lua
+++ /dev/null
@@ -1,34 +0,0 @@
-local f, s, o
-local site = require 'gluon.site_config'
-local i18n = require "luci.i18n"
-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", i18n.translate("Node role"))
-f.template = "admin/expertmode"
-
-s = f:section(SimpleSection, nil, i18n.translate(
- "If this node has a special role within the freifunk network you can specify this role here. "
- .. "Please find out about the available roles and their impact first. "
- .. "Only change the role if you know what you are doing."))
-
-o = s:option(ListValue, "role", i18n.translate("Role"))
-o.default = role
-o.rmempty = false
-for _, role in ipairs(site.roles.list) do
- o:value(role, i18n.translate('gluon-luci-node-role:role:' .. role))
-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-node-role/i18n/fr.po b/package/gluon-luci-node-role/i18n/fr.po
deleted file mode 100644
index a571f568..00000000
--- a/package/gluon-luci-node-role/i18n/fr.po
+++ /dev/null
@@ -1,26 +0,0 @@
-msgid ""
-msgstr ""
-"Content-Type: text/plain; charset=UTF-8\n"
-"Project-Id-Version: PACKAGE VERSION\n"
-"PO-Revision-Date: 2015-08-19 23:30+0100\n"
-"Last-Translator:Tobias Bernot \n"
-"Language-Team: French\n"
-"Language: fr\n"
-"MIME-Version: 1.0\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-msgid "Node role"
-msgstr "Rôle du nœud"
-
-msgid "Role"
-msgstr "Rôle"
-
-msgid ""
-"If this node has a special role within the freifunk network you can specify "
-"this role here. Please find out about the available roles and their impact "
-"first. Only change the role if you know what you are doing."
-msgstr ""
-"Si votre nœud a un rôle spécial dans le réseau freifunk, vous pouvez spécifier ce "
-"rôle ici. Avant de changer, informez vous sur les rôles disponibles et sur leur impacts. "
-"Changez de rôle uniquement si vous comprenez ce que vous faites."
diff --git a/package/gluon-luci-portconfig/Makefile b/package/gluon-luci-portconfig/Makefile
deleted file mode 100644
index 007c5fea..00000000
--- a/package/gluon-luci-portconfig/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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:=1
-PKG_RELEASE:=1
-
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
-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 Build/Prepare
- mkdir -p $(PKG_BUILD_DIR)
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
- $(call GluonBuildI18N,gluon-luci-portconfig,i18n)
-endef
-
-define Package/gluon-luci-portconfig/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-luci-portconfig,$(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
deleted file mode 100644
index 037b56c8..00000000
--- a/package/gluon-luci-portconfig/files/usr/lib/lua/luci/controller/admin/portconfig.lua
+++ /dev/null
@@ -1,19 +0,0 @@
---[[
-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"), _("Network"), 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
deleted file mode 100644
index 994f3077..00000000
--- a/package/gluon-luci-portconfig/files/usr/lib/lua/luci/model/cbi/admin/portconfig.lua
+++ /dev/null
@@ -1,155 +0,0 @@
---[[
-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 lutil = require 'luci.util'
-local sysconfig = require 'gluon.sysconfig'
-
-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", translate("WAN connection"))
-f.template = "admin/expertmode"
-
-local s
-local o
-
-s = f:section(SimpleSection, nil, nil)
-
-o = s:option(ListValue, "ipv4", translate("IPv4"))
-o:value("dhcp", translate("Automatic (DHCP)"))
-o:value("static", translate("Static"))
-o:value("none", translate("Disabled"))
-o.default = wan.proto
-
-o = s:option(Value, "ipv4_addr", translate("IP address"))
-o:depends("ipv4", "static")
-o.value = wan.ipaddr
-o.datatype = "ip4addr"
-o.rmempty = false
-
-o = s:option(Value, "ipv4_netmask", translate("Netmask"))
-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", translate("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", translate("IPv6"))
-o:value("dhcpv6", translate("Automatic (RA/DHCPv6)"))
-o:value("static", translate("Static"))
-o:value("none", translate("Disabled"))
-o.default = wan6.proto
-
-o = s:option(Value, "ipv6_addr", translate("IP address"))
-o:depends("ipv6", "static")
-o.value = wan6.ip6addr
-o.datatype = "ip6addr"
-o.rmempty = false
-
-o = s:option(Value, "ipv6_gateway", translate("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", translate("Static DNS servers"))
- 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", translate("Enable meshing on the WAN interface"))
-o.default = uci:get_bool("network", "mesh_wan", "auto") and o.enabled or o.disabled
-o.rmempty = false
-
-if sysconfig.lan_ifname then
- o = s:option(Flag, "mesh_lan", translate("Enable meshing on the LAN interface"))
- o.default = uci:get_bool("network", "mesh_lan", "auto") and o.enabled or o.disabled
- o.rmempty = false
-end
-
-
-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:trim())
- uci:set("network", "wan", "netmask", data.ipv4_netmask:trim())
- uci:set("network", "wan", "gateway", data.ipv4_gateway:trim())
- 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:trim())
- uci:set("network", "wan6", "ip6gw", data.ipv6_gateway:trim())
- else
- uci:delete("network", "wan6", "ip6addr")
- uci:delete("network", "wan6", "ip6gw")
- end
-
- uci:set("network", "mesh_wan", "auto", data.mesh_wan)
-
- if sysconfig.lan_ifname then
- uci:set("network", "mesh_lan", "auto", data.mesh_lan)
-
- local doit
- if data.mesh_lan == '1' then
- doit = uci.remove_from_set
- else
- doit = uci.add_to_set
- end
-
- for _, lanif in ipairs(lutil.split(sysconfig.lan_ifname, ' ')) do
- doit(uci, "network", "client", "ifname", lanif)
- end
- end
-
- 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
deleted file mode 100644
index 604929dd..00000000
--- a/package/gluon-luci-private-wifi/Makefile
+++ /dev/null
@@ -1,36 +0,0 @@
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=gluon-luci-private-wifi
-PKG_VERSION:=1
-PKG_RELEASE:=1
-
-PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
-
-include $(GLUONDIR)/include/package.mk
-
-PKG_CONFIG_DEPENDS += $(GLUON_I18N_CONFIG)
-
-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
- $(call GluonBuildI18N,gluon-luci-private-wifi,i18n)
-endef
-
-define Package/gluon-luci-private-wifi/install
- $(CP) ./files/* $(1)/
- $(call GluonInstallI18N,gluon-luci-private-wifi,$(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
deleted file mode 100644
index e11ba58b..00000000
--- a/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/controller/admin/privatewifi.lua
+++ /dev/null
@@ -1,5 +0,0 @@
-module("luci.controller.admin.privatewifi", package.seeall)
-
-function index()
- entry({"admin", "privatewifi"}, cbi("admin/privatewifi"), _("Private 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
deleted file mode 100644
index 788ff430..00000000
--- a/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/model/cbi/admin/privatewifi.lua
+++ /dev/null
@@ -1,63 +0,0 @@
-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", translate("Private WLAN"))
-f.template = "admin/expertmode"
-
-s = f:section(SimpleSection, nil, translate(
- 'Your node can additionally extend your private network by bridging the WAN interface '
- .. 'with a separate WLAN. This feature is completely independent of the mesh functionality. '
- .. 'Please note that the private WLAN and meshing on the WAN interface should not be enabled '
- .. 'at the same time.'
-))
-
-o = s:option(Flag, "enabled", translate("Enabled"))
-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", translate("Name (SSID)"))
-o:depends("enabled", '1')
-o.default = ssid
-
-o = s:option(Value, "key", translate("Key"), translate("8-63 characters"))
-o:depends("enabled", '1')
-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
- uci:section(config, "wifi-iface", name,
- {
- device = device,
- network = "wan",
- mode = 'ap',
- encryption = 'psk2',
- ssid = data.ssid,
- key = data.key,
- disabled = 0,
- }
- )
- else
- -- disable WAN wifi-iface
- uci:set(config, name, "disabled", 1)
- 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
deleted file mode 100644
index 4fc947c4..00000000
--- a/package/gluon-luci-theme/Makefile
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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
deleted file mode 100755
index 795bd186..00000000
--- a/package/gluon-luci-theme/files/etc/uci-defaults/luci-theme-gluon
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/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
deleted file mode 100644
index 6b709030..00000000
--- a/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/footer.htm
+++ /dev/null
@@ -1,19 +0,0 @@
-<%#
-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$
-
--%>
-
-
-
-