diff --git a/Makefile b/Makefile
index b5f4305b..4420b387 100644
--- a/Makefile
+++ b/Makefile
@@ -179,11 +179,8 @@ include $(INCLUDE_DIR)/target.mk
prereq: FORCE
+$(NO_TRACE_MAKE) prereq
-gluon-tools: FORCE
- +$(GLUONMAKE_EARLY) tools/sed/install
- +$(GLUONMAKE_EARLY) package/lua/host/install
-
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"
@@ -200,18 +197,23 @@ 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
- . $(GLUONDIR)/modules && for feed in $$GLUON_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/sed/install
+ +$(GLUONMAKE_EARLY) package/lua/host/install
+
config: FORCE
- +$(NO_TRACE_MAKE) scripts/config/conf OPENWRT_BUILD=0
+ +$(NO_TRACE_MAKE) scripts/config/conf OPENWRT_BUILD= QUIET=0
+$(GLUONMAKE) prepare-tmpinfo
( \
cat $(GLUONDIR)/include/config $(GLUONDIR)/targets/$(GLUON_TARGET)/config; \
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_GLUON_LANG_%=y,$(GLUON_LANGS))' \
+ 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
@@ -280,11 +282,14 @@ packages: $(package/stamp-compile)
prepare-image: FORCE
rm -rf $(BOARD_KDIR)
mkdir -p $(BOARD_KDIR)
- cp $(KERNEL_BUILD_DIR)/vmlinux $(KERNEL_BUILD_DIR)/vmlinux.elf $(BOARD_KDIR)/
- +$(SUBMAKE) -C $(TOPDIR)/target/linux/$(BOARD)/image -f $(GLUONDIR)/include/Makefile.image prepare KDIR="$(BOARD_KDIR)"
+ $(foreach k, vmlinux vmlinux.elf \
+ $(if $(KERNEL_IMAGES),$(KERNEL_IMAGES),$(filter-out dtbs,$(KERNELNAME))), \
+ $(CP) $(KERNEL_BUILD_DIR)/$(k) $(BOARD_KDIR)/$(k); \
+ )
+ +$(SUBMAKE) -C $(TOPDIR)/target/linux/$(BOARD)/image image_prepare KDIR="$(BOARD_KDIR)"
prepare: FORCE
- @$(STAGING_DIR_HOST)/bin/lua $(GLUONDIR)/packages/gluon/gluon/gluon-core/files/usr/lib/lua/gluon/site_config.lua \
+ @$(STAGING_DIR_HOST)/bin/lua $(GLUONDIR)/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua \
|| (echo 'Your site configuration did not pass validation.'; false)
mkdir -p $(GLUON_IMAGEDIR) $(BOARD_BUILDDIR)
@@ -345,7 +350,7 @@ $(eval $(call merge-lists,INSTALL_PACKAGES,DEFAULT_PACKAGES GLUON_DEFAULT_PACKAG
package_install: FORCE
$(OPKG) update
- $(OPKG) install $(PACKAGE_DIR)/libc_*.ipk
+ $(OPKG) install $(PACKAGE_DIR)/base-files_*.ipk $(PACKAGE_DIR)/libc_*.ipk
$(OPKG) install $(PACKAGE_DIR)/kernel_*.ipk
$(OPKG) install $(INSTALL_PACKAGES)
@@ -353,6 +358,10 @@ package_install: FORCE
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
+
+
ifeq ($(GLUON_OPKG_CONFIG),1)
include $(INCLUDE_DIR)/version.mk
endif
diff --git a/README.md b/README.md
index 0bd1306d..87750136 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ our mailinglist to discuss it first.
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 v2014.3;make update`.
+and switch to one by running `git checkout v2015.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.
diff --git a/contrib/lsupgrade.sh b/contrib/lsupgrade.sh
index ecf2f512..7fe11299 100755
--- a/contrib/lsupgrade.sh
+++ b/contrib/lsupgrade.sh
@@ -26,7 +26,7 @@ fi
pushd "$(dirname "$0")/.." >/dev/null
-find packages -name Makefile | while read makefile; do
+find ./package packages -name Makefile | while read makefile; do
dir="$(dirname "$makefile")"
pushd "$dir" >/dev/null
@@ -36,7 +36,7 @@ find packages -name Makefile | while read makefile; do
package="$(basename "$dir")"
for file in "${SUFFIX}"/*; do
- echo "${GREEN}$(basename "${file}")${RESET}" "(${BLUE}${repo}${RESET}/${dirname}/${RED}${package}${RESET}/${SUFFIX})"
+ echo "${GREEN}$(basename "${file}")${RESET}" "(${BLUE}${repo}${RESET}/${dirname}${dirname:+/}${RED}${package}${RESET}/${SUFFIX})"
done
popd >/dev/null
done | sort
diff --git a/contrib/sign.sh b/contrib/sign.sh
index a88c52df..082e967e 100755
--- a/contrib/sign.sh
+++ b/contrib/sign.sh
@@ -18,23 +18,23 @@ See also
EOHELP
exit 1
fi
-
-SECRET=$1
-
-manifest=$2
-upper=$(mktemp)
-lower=$(mktemp)
-
+
+SECRET="$1"
+
+manifest="$2"
+upper="$(mktemp)"
+lower="$(mktemp)"
+
awk "BEGIN { sep=0 }
/^---\$/ { sep=1; next }
{ if(sep==0) print > \"$upper\";
else print > \"$lower\"}" \
- $manifest
-
-ecdsasign $upper < $SECRET >> $lower
-
-cat $upper > $manifest
-echo --- >> $manifest
-cat $lower >> $manifest
-
-rm -f $upper $lower
+ "$manifest"
+
+ecdsasign "$upper" < "$SECRET" >> "$lower"
+
+cat "$upper" > "$manifest"
+echo --- >> "$manifest"
+cat "$lower" >> "$manifest"
+
+rm -f "$upper" "$lower"
diff --git a/docs/conf.py b/docs/conf.py
index 4cf0a96e..090de523 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -47,16 +47,16 @@ master_doc = 'index'
# General information about the project.
project = 'Gluon'
-copyright = '2014, Project Gluon'
+copyright = '2015, 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 = '2014.4+'
+version = '2015.1+'
# The full version, including alpha/beta/rc tags.
-release = '2014.4+'
+release = '2015.1+'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/dev/i18n.rst b/docs/dev/i18n.rst
new file mode 100644
index 00000000..b51bfdee
--- /dev/null
+++ b/docs/dev/i18n.rst
@@ -0,0 +1,88 @@
+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/features/autoupdater.rst b/docs/features/autoupdater.rst
index 983027de..fdc511d9 100644
--- a/docs/features/autoupdater.rst
+++ b/docs/features/autoupdater.rst
@@ -54,7 +54,7 @@ We suggest to have following directory tree accessible via http:
sysupgrade/
factory/
-The server should be available via IPv6.
+The server must be available via IPv6.
Command Line
------------
diff --git a/docs/index.rst b/docs/index.rst
index a53c0a8d..92fb58b7 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -13,6 +13,7 @@ User Documentation
user/getting_started
user/site
+ user/x86
user/faq
Features
@@ -40,57 +41,7 @@ Developer Documentation
dev/upgrade
dev/configmode
dev/wan
-
-Supported Devices
------------------
-
-* Buffalo
-
- - WZR-HP-AG300H / WZR-600DHP
- - WZR-HP-G450H
-
-* D-Link
-
- - DIR-825 (B1)
-
-* Linksys
-
- - WRT160NL
-
-* TP-Link
-
- - CPE210 (v1)
- - CPE220 (v1)
- - CPE510 (v1)
- - CPE520 (v1)
- - TL-MR3020 (v1)
- - TL-MR3040 (v1, v2)
- - TL-MR3220 (v1)
- - TL-MR3420 (v1, v2)
- - TL-WA750RE (v1)
- - TL-WA801N/ND (v2)
- - TL-WA850RE (v1)
- - TL-WA901N/ND (v2)
- - TL-WDR3500 (v1)
- - TL-WDR3600 (v1)
- - TL-WDR4300 (v1)
- - TL-WR1043N/ND (v1, v2)
- - TL-WR703N (v1)
- - TL-WR710N (v1)
- - TL-WR740N (v1, v3, v4)
- - TL-WR741N/ND (v1, v2, v4)
- - TL-WR841N/ND (v3, v5, v7, v8, v9)
- - TL-WR842N/ND (v1, v2)
- - TL-WR941N/ND (v2, v3, v4)
-
-* Ubiquiti
-
- - Bullet M2
- - Nanostation M2
- - Picostation M2
- - UniFi AP
- - UniFi AP Outdoor
-
+ dev/i18n
Releases
--------
@@ -104,6 +55,112 @@ Releases
releases/v2014.3
+Supported Devices & Architectures
+---------------------------------
+
+ar71xx-generic
+^^^^^^^^^^^^^^
+
+* Allnet
+
+ - ALL0315N
+
+* Buffalo
+
+ - WZR-HP-AG300H / WZR-600DHP
+ - WZR-HP-G450H
+
+* D-Link
+
+ - DIR-825 (B1)
+ - DIR-615 (C1)
+
+* GL-Inet
+
+ - 6408A (v1)
+ - 6416A (v1)
+
+* Linksys
+
+ - WRT160NL
+
+* Netgear
+
+ - WNDR3700 (v1, v2)
+ - WNDR3800
+ - WNDRMAC (v2)
+
+* TP-Link
+
+ - CPE210 (v1)
+ - CPE220 (v1)
+ - CPE510 (v1)
+ - CPE520 (v1)
+ - TL-MR3020 (v1)
+ - TL-MR3040 (v1, v2)
+ - TL-MR3220 (v1, v2)
+ - TL-MR3420 (v1, v2)
+ - TL-WA701N/ND (v1)
+ - TL-WA750RE (v1)
+ - TL-WA801N/ND (v2)
+ - TL-WA850RE (v1)
+ - TL-WA860RE (v1)
+ - TL-WA901N/ND (v2, v3)
+ - TL-WDR3500 (v1)
+ - TL-WDR3600 (v1)
+ - TL-WDR4300 (v1)
+ - TL-WR1043N/ND (v1, v2)
+ - TL-WR703N (v1)
+ - TL-WR710N (v1)
+ - TL-WR740N (v1, v3, v4)
+ - TL-WR741N/ND (v1, v2, v4)
+ - TL-WR743N/ND (v1, v2)
+ - TL-WR841N/ND (v3, v5, v7, v8, v9)
+ - TL-WR842N/ND (v1, v2)
+ - TL-WR941N/ND (v2, v3, v4, v5)
+ - TL-WR2543N/ND (v1)
+
+* Ubiquiti
+
+ - Bullet M2
+ - Nanostation M2
+ - Nanostation M XW
+ - Loco M XW
+ - Picostation M2
+ - Rocket M2
+ - UniFi AP
+ - UniFi AP Pro
+ - UniFi AP Outdoor
+
+ar71xx-nand
+^^^^^^^^^^^
+
+* Netgear
+
+ - WNDR3700 (v4)
+ - WNDR4300 (v1)
+
+mpc85xx-generic
+^^^^^^^^^^^^^^^
+
+* TP-Link
+
+ - TL-WDR4900 (v1)
+
+x86-generic
+^^^^^^^^^^^
+* x86-generic
+* x86-virtualbox
+* x86-vmware
+
+See also: :doc:`user/x86`
+
+x86-kvm_guest
+^^^^^^^^^^^^^
+* x86-kvm
+
+See also: :doc:`user/x86`
+
License
-------
@@ -116,4 +173,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`search`
-
diff --git a/docs/releases/v2015.1.rst b/docs/releases/v2015.1.rst
index 8ce3db0f..8e5c8e99 100644
--- a/docs/releases/v2015.1.rst
+++ b/docs/releases/v2015.1.rst
@@ -1,16 +1,159 @@
-Gluon 2015.1 (in development)
-=============================
+Gluon 2015.1
+============
-Added (and removed) hardware support
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Added hardware support
+~~~~~~~~~~~~~~~~~~~~~~
+Gluon v2015.1 is the first release to officially support hardware
+that is not handled by the `ar71xx-generic` OpenWrt target. This also
+means that `ar71xx-generic` isn't the default target anymore, the ``GLUON_TARGET``
+variable must be set for all runs of ``make`` and ``make clean`` now.
+
+ar71xx-generic
+^^^^^^^^^^^^^^
+
+* Allnet
+
+ - ALL0315N
+
+* D-Link
+
+ - DIR-615 (C1)
+
+* GL-Inet
+
+ - 6408A (v1)
+ - 6416A (v1)
+
+ - WRT160NL
+
+* Netgear
+
+ - WNDR3700 (v1, v2)
+ - WNDR3800
+ - WNDRMAC (v2)
+
+* TP-Link
+
+ - TL-MR3220 (v2)
+ - TL-WA701N/ND (v1)
+ - TL-WA860RE (v1)
+ - TL-WA901N/ND (v2, v3)
+ - TL-WR743N/ND (v1, v2)
+ - TL-WR941N/ND (v5)
+ - TL-WR2543N/ND (v1)
+
+* Ubiquiti
+
+ - Nanostation M XW
+ - Loco M XW
+ - UniFi AP Pro
+
+ar71xx-nand
+^^^^^^^^^^^
+
+* Netgear
+
+ - WNDR3700 (v4)
+ - WNDR4300 (v1)
+
+mpc85xx-generic
+^^^^^^^^^^^^^^^
+
+* TP-Link
+
+ - TL-WDR4900 (v1)
+
+x86-generic
+^^^^^^^^^^^
+* x86-generic
+* x86-virtualbox
+* x86-vmware
+
+x86-kvm_guest
+^^^^^^^^^^^^^
+* x86-kvm
New features
~~~~~~~~~~~~
+Multilingual config mode
+^^^^^^^^^^^^^^^^^^^^^^^^
+All config and expert mode modules contain both English and German texts now. The English
+locale should always be enabled in ``site.mk`` (as English is the fallback language),
+German can be enabled in addition using the ``GLUON_LANGS`` setting.
+The language shown is autmatically determined from the headers sent by the user's
+browser.
+
+Mesh-on-LAN
+^^^^^^^^^^^
+Gluon now supports meshing using a node's LAN ports. It can be enabled by
+default in `site.conf`, and configured by the user using the `gluon-luci-portconfig`
+expert mode package.
+
+Please note that nodes without the `mesh-on-lan` feature enabled must never be connected
+via their LAN ports.
+
+Extended WLAN configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The new ``client_disabled`` and ``mesh_disabled`` keys in the ``wifi24`` and ``wifi5`` sections allow
+to disable the client and mesh networks by default, which may make sense for images for
+special installations.
+
+The new package `gluon-luci-wifi-config` allows the user to change these settings; in addition,
+the WLAN adapters' transmission power can be changed in this package.
+
+fastd "performance mode"
+^^^^^^^^^^^^^^^^^^^^^^^^
+The new package `gluon-luci-mesh-vpn-fastd` allows the user to switch between the `security` and
+`performance` VPN settions. In `performance mode`, the method `null` will be prepended to the
+method list.
+
+The new option ``configurable`` in the ``fastd_mesh_vpn`` section of ``site.conf`` must be set to `true`
+so firmware upgrades don't overwrite the method list completely (non-`null` methods will still
+be overwritten). Adding the `gluon-luci-mesh-vpn-fastd` package enforces this setting.
+
+Altitude setting in `gluon-config-mode-geo-location`
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The `gluon-config-mode-geo-location` config mode module
+now contains an optional altitude field.
+
+`gluon-announced` rework
+^^^^^^^^^^^^^^^^^^^^^^^^
+The `gluon-announced` package has been reworked to allow querying it from anywhere in the mesh.
+In contrast to `gluon-alfred`, it is based on a query-response model (the master multicasts a query,
+the nodes respond), while `gluon-alfred` uses periodic announcements.
+
+For now, we recommend including both `gluon-alfred` and `gluon-announced` in Gluon-based firmwares,
+until `gluon-announced` is ready to replace `gluon-alfred` completely, and software like the
+ffmap backend has been adjusted accordingly.
+
+Nested peer groups
+^^^^^^^^^^^^^^^^^^
+Nested peer groups for the `fastd-mesh-vpn-fastd` package can now be configured in ``site.conf``,
+each with its own peer limit. This allows to add additional constaints, for example to connect
+to 2 peers altogether, but only 1 peer in each data center.
+
+Autoupdater manual branch override
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+When running the updater manually on the command line, the branch to use can now be
+overridden using the ``-b`` option.
Bugfixes
~~~~~~~~
+Accidental factory reset fix
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Pressing a node's reset button for more than 5 seconds would completely reset a node's
+configuration under certain conditions.
+
+WAN IPv6 issues
+^^^^^^^^^^^^^^^
+The WAN port would stop to respond to IPv6 packets sometimes, also breaking IPv6 VPN connectivity.
+
+WDR4900 WAN MAC address
+^^^^^^^^^^^^^^^^^^^^^^^
+The MAC address on the WAN port of the WDR4900 was broken, making this device unusable for `mesh-on-wan`
+configurations.
Site changes
@@ -22,9 +165,88 @@ Site changes
to keep the old behaviour, you have to append the hyphen to the
``hostname_prefix`` field of your ``site.conf``.
+ - ``mesh_vpn_fastd``: The default peer group name ``backbone`` isn't hardcoded anymore, any
+ group name can be used. Instead, the ``fastd_mesh_vpn`` table must now contain an element ``groups``,
+ for example::
+
+ fastd_mesh_vpn = {
+ methods = {'salsa2012+umac'},
+ mtu = 1426,
+ groups = {
+ backbone = {
+ limit = 2,
+ peers = {
+ -- ...
+ }
+ }
+ }
+ }
+
+ - ``config_mode``: The config mode messages aren't configured in ``site.conf`` anymore. Instead, they are
+ defined language-specific gettext files in the ``i18n`` subdirectory of the site configuration (see
+ :ref:`site-config-mode-texts`).
+
+ - ``roles``: The display strings for the node roles aren't configured in the ``site.conf`` anymore, but
+ in the site i18n files. The ``site.conf`` section becomes::
+
+ roles = {
+ default = 'foo',
+ list = {
+ 'foo',
+ 'bar',
+ }
+ }
+
+ The display string use i18n message IDs like ``gluon-luci-node-role:role:foo`` and ``gluon-luci-node-role:role:bar``.
+
+* ``site.mk``
+
+ - ``gluon-mesh-batman-adv-15`` is now the recommended batman-adv version for new Gluon deployments.
+
+ - The packages ``gluon-setup-mode`` and ``gluon-config-mode-core`` must now be
+ added to ``GLUON_SITE_PACKAGES`` explicitly (to allow replacing them with
+ community-specific implementations).
+
+ - The new ``GLUON_LANGS`` variable selects the config mode languages to include. It defaults to ``en``,
+ setting it to ``en de`` will select both the English and German locales. ``en`` must always be
+ included.
+
Internals
~~~~~~~~~
+New upgrade script directory
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The distinction between `initial` and `invariant` scripts has been removed,
+all scripts are now run on each upgrade. Instead of having one script directory
+per package, all upgrade scripts lie in ``/lib/gluon/upgrade`` now, so it is
+possible to define the run order across packages.
+Merged package repository
+^^^^^^^^^^^^^^^^^^^^^^^^^
+The Gluon-specific packages have been moved to the ``package`` directory of the Gluon
+main repository. The ``packages`` repository now only contains packages that will be
+submitted to the OpenWrt upstream eventually.
Known Issues
~~~~~~~~~~~~
+
+Alfred/respondd crashes
+^^^^^^^^^^^^^^^^^^^^^^^
+
+https://github.com/freifunk-gluon/gluon/issues/177
+
+Occasional alfred crashes may still occur. As this is caused by a kernel issue,
+we suspect that respondd, which gluon-announced is based on, is affected
+in the same way.
+
+
+Ignored TX power offset on Ubiquiti AirMax devices
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+https://github.com/freifunk-gluon/gluon/issues/94
+
+The default transmission power setting on many of these devices
+is too high. It may be necessary to make manual adjustments, for example
+using the ``gluon-luci-wifi-config`` package. The values shown by
+``gluon-luci-wifi-config`` generally include the TX power offset
+(amplifier and antenna gain) where available, but on many devices
+the offset is inaccurate or unavailable.
diff --git a/docs/site-example/i18n/de.po b/docs/site-example/i18n/de.po
index 686813bf..2bb29726 100644
--- a/docs/site-example/i18n/de.po
+++ b/docs/site-example/i18n/de.po
@@ -34,4 +34,7 @@ msgstr ""
"verbinden. Weitere Informationen zur "
"Entenhausener Freifunk-Community findest du auf "
"unserer Webseite.
"
+"
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!
"
diff --git a/docs/site-example/i18n/en.po b/docs/site-example/i18n/en.po
index c00f82c4..2e963ba3 100644
--- a/docs/site-example/i18n/en.po
+++ b/docs/site-example/i18n/en.po
@@ -31,4 +31,7 @@ msgstr ""
"nearby Freifunk nodes after that. "
"Your can find lots of information on the Freifunk Duckburg community on "
"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 the Freifunk network!
"
diff --git a/docs/site-example/site.conf b/docs/site-example/site.conf
index 48e651e9..6cffded8 100644
--- a/docs/site-example/site.conf
+++ b/docs/site-example/site.conf
@@ -1,4 +1,4 @@
--- This is an example site configuration for Gluon v2014.4
+-- This is an example site configuration for Gluon v2015.1+
--
-- Take a look at the documentation located at
-- http://gluon.readthedocs.org/ for details.
@@ -54,6 +54,8 @@
-- (optional) mesh VLAN on 802.11 ad-hoc interface (1-4095)
-- mesh_vlan = 14,
+ -- client_disabled = true,
+ -- mesh_disabled = false,
},
-- Wireless configuration for 5 GHz interfaces.
@@ -67,6 +69,8 @@
mesh_bssid = 'xx:xx:xx:xx:xx:xx',
mesh_mcast_rate = 12000,
-- mesh_vlan = 14,
+ -- client_disabled = true,
+ -- mesh_disabled = false,
},
-- The next node feature allows clients to always reach the node it is
@@ -85,25 +89,42 @@
fastd_mesh_vpn = {
-- List of crypto-methods to use.
methods = {'salsa2012+umac'},
+ -- enabled = true,
+ -- configurable = true,
+
mtu = 1426,
- backbone = {
- -- Limit number of connected peers to reduce bandwidth.
- limit = 2,
+ groups = {
+ backbone = {
+ -- Limit number of connected peers to reduce bandwidth.
+ limit = 2,
- -- List of peers.
- peers = {
- peer1 = {
- key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ -- List of peers.
+ peers = {
+ peer1 = {
+ key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
- -- This is a list, so you might add multiple entries.
- remotes = {'ipv4 "xxx.somehost.invalid" port xxxxxx'},
- },
- peer2 = {
- key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
- -- You can also omit the ipv4 to allow both connection via ipv4 and ipv6
- remotes = {'"xxx.somehost2.invalid" port xxxxx'},
+ -- This is a list, so you might add multiple entries.
+ remotes = {'ipv4 "xxx.somehost.invalid" port xxxxxx'},
+ },
+ peer2 = {
+ key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+ -- You can also omit the ipv4 to allow both connection via ipv4 and ipv6
+ remotes = {'"xxx.somehost2.invalid" port xxxxx'},
+ },
},
+
+ -- Optional: nested peer groups
+ -- groups = {
+ -- backbone_sub = {
+ -- ...
+ -- },
+ -- ...
+ -- },
},
+ -- Optional: additional peer groups, possibly with other limits
+ -- backbone2 = {
+ -- ...
+ -- },
},
},
@@ -136,13 +157,13 @@
},
-- Node roles
- -- roles {
+ -- roles = {
-- default = 'node',
-- list = {
- -- node = 'Normal Node',
- -- test = 'Test Node',
- -- backbone = 'Backbone Node',
- -- service = 'Service Node',
+ -- 'node',
+ -- 'test',
+ -- 'backbone',
+ -- 'service',
-- },
-- },
diff --git a/docs/site-example/site.mk b/docs/site-example/site.mk
index e03be578..6a16eb53 100644
--- a/docs/site-example/site.mk
+++ b/docs/site-example/site.mk
@@ -5,12 +5,13 @@
# The gluon-mesh-batman-adv-* package must come first because of the dependency resolution
GLUON_SITE_PACKAGES := \
- gluon-mesh-batman-adv-14 \
+ gluon-mesh-batman-adv-15 \
gluon-alfred \
gluon-announced \
gluon-autoupdater \
gluon-config-mode-autoupdater \
gluon-config-mode-contact-info \
+ gluon-config-mode-core \
gluon-config-mode-geo-location \
gluon-config-mode-hostname \
gluon-config-mode-mesh-vpn \
@@ -19,9 +20,11 @@ GLUON_SITE_PACKAGES := \
gluon-luci-admin \
gluon-luci-autoupdater \
gluon-luci-portconfig \
+ gluon-luci-wifi-config \
gluon-next-node \
gluon-mesh-vpn-fastd \
gluon-radvd \
+ gluon-setup-mode \
gluon-status-page \
haveged \
iptables \
diff --git a/docs/user/getting_started.rst b/docs/user/getting_started.rst
index adbec026..885a3887 100644
--- a/docs/user/getting_started.rst
+++ b/docs/user/getting_started.rst
@@ -38,7 +38,7 @@ Building the images
-------------------
To build Gluon, first check out the repository. Replace *RELEASE* with the
-version you'd like to checkout, e.g. *v2014.4*.
+version you'd like to checkout, e.g. *v2015.1*.
::
diff --git a/docs/user/site.rst b/docs/user/site.rst
index 2c4c6e8b..bf6c1650 100644
--- a/docs/user/site.rst
+++ b/docs/user/site.rst
@@ -67,12 +67,14 @@ wifi24
``htmode``, the adhoc ssid ``mesh_ssid`` used between devices, the adhoc
bssid ``mesh_bssid`` and the adhoc multicast rate ``mesh_mcast_rate``.
Optionally ``mesh_vlan`` can be used to setup VLAN on top of the 802.11
- ad-hoc interface.
+ ad-hoc interface. The options ``mesh_disabled`` and ``client_disabled``
+ are optional, too. They allow to disable the SSID by default, e.g. for
+ preconfigured node. This only affects first configuraton.
Combined in an dictionary, e.g.:
::
wifi24 = {
- ssid = 'http://kiel.freifunk.net/',
+ ssid = 'entenhausen.freifunk.net',
channel = 11,
htmode = 'HT40-',
mesh_ssid = 'ff:ff:ff:ee:ba:be',
@@ -90,24 +92,40 @@ next_node : package
next_node = {
ip4 = '10.23.42.1',
ip6 = 'fdca:ffee:babe:1::1',
- mac = 'ca:ff:ee:ba:be'
+ mac = 'ca:ff:ee:ba:be:00'
}
fastd_mesh_vpn
- Remote server setup for vpn.
+ Remote server setup for the fastd-based mesh VPN.
+
+ The `enabled` option can be set to true to enable the VPN by default.
+
+ If `configurable` is `false` or unset, the method list will be replaced on updates
+ with the list in the site configuration. Setting `configurable` to `true` will allow the user to
+ add the method ``null`` to the front of the method list or remove ``null`` from it,
+ and make this change survive updates. Settings configurable is necessary for the
+ package `gluon-luci-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.
::
fastd_mesh_vpn = {
- methods = {'salsa2012+gmac'},
+ methods = {'salsa2012+umac'},
+ -- enabled = true,
+ -- configurable = true,
mtu = 1426,
- backbone = {
- limit = 2,
- peers = {
- ffki_rz = {
- key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
- remotes = {'ipv4 "vpn1.entenhausen.freifunk.net" port 10000'},
- },
+ groups = {
+ backbone = {
+ limit = 2,
+ peers = {
+ peer1 = {
+ key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
+ remotes = {'ipv4 "vpn1.entenhausen.freifunk.net" port 10000'},
+ },
+ }
}
}
}
@@ -115,6 +133,9 @@ fastd_mesh_vpn
mesh_on_wan : optional
Enables the mesh on the WAN port (``true`` or ``false``).
+mesh_on_lan : optional
+ Enables the mesh on the LAN port (``true`` or ``false``).
+
autoupdater : package
Configuration for the autoupdater feature of Gluon.
::
@@ -146,15 +167,19 @@ roles : optional
``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 strings to display in the LuCI interface can be 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``.
::
roles = {
default = 'node',
list = {
- node = 'Normal Node',
- test = 'Test Node',
- backbone = 'Backbone Node',
- service = 'Service Node',
+ 'node',
+ 'test',
+ 'backbone',
+ 'service',
},
},
@@ -176,7 +201,7 @@ setup_mode : package
``skip`` is set to ``true``. This is optional and may be left out.
::
- setup_mode {
+ setup_mode = {
skip = true,
},
@@ -216,6 +241,8 @@ GLUON_LANGS
List of languages (as two-letter-codes) to include for the web interface. Should always contain
``en``.
+.. _site-config-mode-texts:
+
Config mode texts
-----------------
@@ -280,7 +307,7 @@ This is a non-exhaustive list of site-repos from various communities:
* `site-ffgoe `_ (Göttingen)
* `site-ffhh `_ (Hamburg)
* `site-ffhgw `_ (Greifswald)
-* `site-ffhl `_ (Lübeck)
+* `site-ffhl `_ (Lübeck)
* `site-ffmd `_ (Magdeburg)
* `site-ffmwu `_ (Mainz, Wiesbaden & Umgebung)
* `site-ffmyk `_ (Mayen-Koblenz)
diff --git a/docs/user/x86.rst b/docs/user/x86.rst
new file mode 100644
index 00000000..b985d6fc
--- /dev/null
+++ b/docs/user/x86.rst
@@ -0,0 +1,29 @@
+x86 support
+===========
+
+Gluon can run on normal x86 systems, for example virtual machines
+and VPN boxes. There is no WLAN support on x86 though.
+
+Targets
+^^^^^^^
+
+There are two targets for x86 images:
+
+`x86-generic`
+ Generic x86 support with many different ethernet drivers; should run on
+ most x86 systems.
+
+ There are three images:
+
+ * `generic` (compressed "raw" image, can written to a disk directly or booted with qemu)
+ * `virtualbox` (VDI image)
+ * `vmware` (VMDK image)
+
+ 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.
diff --git a/include/Makefile.image b/include/Makefile.image
deleted file mode 100644
index d6a627c6..00000000
--- a/include/Makefile.image
+++ /dev/null
@@ -1,7 +0,0 @@
-override define BuildImage
-prepare: FORCE
- $(call Image/Prepare)
-endef
-
-# The Makefile included here is $(TOPDIR)/target/linux/$(BOARD)/image/Makefile
-include Makefile
diff --git a/include/Makefile.target b/include/Makefile.target
index 19370e52..a78a4044 100644
--- a/include/Makefile.target
+++ b/include/Makefile.target
@@ -7,8 +7,7 @@ override define Kernel/Configure
echo "# CONFIG_KALLSYMS_EXTRA_PASS is not set" >> $(LINUX_DIR)/.config.target
echo "# CONFIG_KALLSYMS_ALL is not set" >> $(LINUX_DIR)/.config.target
echo "# CONFIG_KALLSYMS_UNCOMPRESSED is not set" >> $(LINUX_DIR)/.config.target
- echo "# CONFIG_KPROBES is not set" >> $(LINUX_DIR)/.config.target
- $(SCRIPT_DIR)/metadata.pl kconfig $(TMP_DIR)/.packageinfo $(BOARD_BUILDDIR)/config-allmods > $(LINUX_DIR)/.config.override
+ $(SCRIPT_DIR)/metadata.pl kconfig $(TMP_DIR)/.packageinfo $(BOARD_BUILDDIR)/config-allmods $(KERNEL_PATCHVER) > $(LINUX_DIR)/.config.override
$(SCRIPT_DIR)/kconfig.pl 'm+' '+' $(LINUX_DIR)/.config.target /dev/null $(LINUX_DIR)/.config.override > $(LINUX_DIR)/.config
$(call Kernel/SetNoInitramfs)
rm -rf $(KERNEL_BUILD_DIR)/modules
diff --git a/include/config b/include/config
index faab30c8..ac1a4f8d 100644
--- a/include/config
+++ b/include/config
@@ -19,4 +19,4 @@ CONFIG_ATH_USER_REGD=y
CONFIG_PACKAGE_ATH_DEBUG=y
CONFIG_ATH10K_CT_COMMUNITY_FW=y
-CONFIG_PACKAGE_luci-base_srcdiet=y
+CONFIG_LUCI_SRCDIET=y
diff --git a/include/gluon.mk b/include/gluon.mk
index 88db6f42..fdccef20 100644
--- a/include/gluon.mk
+++ b/include/gluon.mk
@@ -35,7 +35,7 @@ export GLUON_LANGS
ifeq ($(OPENWRT_BUILD),1)
ifeq ($(GLUON_TOOLS),1)
-CONFIG_VERSION_REPO := $(shell $(GLUONDIR)/scripts/site.sh opkg_repo || echo http://downloads.openwrt.org/barrier_breaker/14.07/%S/packages)
+CONFIG_VERSION_REPO := $(shell $(GLUONDIR)/scripts/site.sh opkg_repo || echo http://downloads.openwrt.org/chaos_calmer/15.05-rc1/%S/packages)
export CONFIG_VERSION_REPO
GLUON_SITE_CODE := $(shell $(GLUONDIR)/scripts/site.sh site_code)
@@ -65,7 +65,7 @@ GLUON_TARGET_$$(gluon_target)_BOARD := $(1)
GLUON_TARGET_$$(gluon_target)_SUBTARGET := $(2)
endef
-GLUON_DEFAULT_PACKAGES := gluon-core kmod-ipv6 firewall ip6tables -uboot-envtools
+GLUON_DEFAULT_PACKAGES := gluon-core kmod-ipv6 firewall ip6tables -uboot-envtools -wpad-mini hostapd-mini
override DEFAULT_PACKAGES.router :=
diff --git a/include/package.mk b/include/package.mk
index 271d1de0..76e109e2 100644
--- a/include/package.mk
+++ b/include/package.mk
@@ -7,45 +7,20 @@ $(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_LANG_ca := catalan
-GLUON_LANG_zh_cn := chinese
-GLUON_LANG_en := english
-GLUON_LANG_fr := french
-GLUON_LANG_de := german
-GLUON_LANG_el := greek
-GLUON_LANG_he := hebrew
-GLUON_LANG_hu := hungarian
-GLUON_LANG_it := italian
-GLUON_LANG_ja := japanese
-GLUON_LANG_ms := malay
-GLUON_LANG_no := norwegian
-GLUON_LANG_pl := polish
-GLUON_LANG_pt_br := portuguese-brazilian
-GLUON_LANG_pt := portuguese
-GLUON_LANG_ro := romanian
-GLUON_LANG_ru := russian
-GLUON_LANG_es := spanish
-GLUON_LANG_sv := swedish
-GLUON_LANG_uk := ukrainian
-GLUON_LANG_vi := vietnamese
+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)))
-GLUON_I18N_PACKAGES := $(foreach lang,$(GLUON_SUPPORTED_LANGS),+GLUON_LANG_$(lang):luci-i18n-$(GLUON_LANG_$(lang)))
-GLUON_I18N_CONFIG := $(foreach lang,$(GLUON_SUPPORTED_LANGS),CONFIG_GLUON_LANG_$(lang))
-GLUON_ENABLED_LANGS := $(foreach lang,$(GLUON_SUPPORTED_LANGS),$(if $(CONFIG_GLUON_LANG_$(lang)),$(lang)))
-
-
-GLUON_PO2LMO := $(BUILD_DIR)/luci/build/po2lmo
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; \
- $(GLUON_PO2LMO) $(2)/$$$$lang.po $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo; \
+ po2lmo $(2)/$$$$lang.po $$(PKG_BUILD_DIR)/i18n/$(1).$$$$lang.lmo; \
fi; \
done
endef
diff --git a/include/toplevel.mk b/include/toplevel.mk
index 74a95997..72522da5 100644
--- a/include/toplevel.mk
+++ b/include/toplevel.mk
@@ -48,10 +48,6 @@ else
export HOSTCC_WRAPPER:=$(HOSTCC)
endif
-ifeq ($(FORCE),)
- .config scripts/config/conf scripts/config/mconf: tmp/.prereq-build
-endif
-
SCAN_COOKIE?=$(shell echo $$$$)
export SCAN_COOKIE
diff --git a/modules b/modules
index 04b9b5b2..d0c43d05 100644
--- a/modules
+++ b/modules
@@ -1,18 +1,19 @@
GLUON_FEEDS='openwrt gluon routing luci'
-OPENWRT_REPO=git://git.openwrt.org/14.07/openwrt.git
-OPENWRT_COMMIT=64ae631f20eb349b47dae30c461ab33b5c4ac5c2
+OPENWRT_REPO=git://git.openwrt.org/openwrt.git
+OPENWRT_COMMIT=53a178af743eb1a463c806ee79d1e622969ad6ef
PACKAGES_OPENWRT_REPO=git://github.com/openwrt/packages.git
-PACKAGES_OPENWRT_COMMIT=01fcd1f29174a56d6ddb59901ed8c67ea42c3a8f
-PACKAGES_OPENWRT_BRANCH=for-14.07
+PACKAGES_OPENWRT_COMMIT=914beae80e848c161214464fba7a9826c66bbf26
+PACKAGES_OPENWRT_BRANCH=for-15.05
PACKAGES_GLUON_REPO=git://github.com/freifunk-gluon/packages.git
-PACKAGES_GLUON_COMMIT=f5c0865d5025a7e6ad3ff6c21ca5206ac972ba75
+PACKAGES_GLUON_COMMIT=dcc5a5ab74822492877eadcba5fc3ad845ee65db
PACKAGES_ROUTING_REPO=git://github.com/openwrt-routing/packages.git
-PACKAGES_ROUTING_COMMIT=5d4ad63897b435d5df0f39a49bd58962c22c33b8
-PACKAGES_ROUTING_BRANCH=for-14.07
+PACKAGES_ROUTING_COMMIT=b1c3bdfbf47003088198bf8ef699a94cc29e3eca
+PACKAGES_ROUTING_BRANCH=for-15.05
PACKAGES_LUCI_REPO=git://github.com/openwrt/luci.git
-PACKAGES_LUCI_COMMIT=f81be49ae756dab82e1758a6c9de145f0d36960e
+PACKAGES_LUCI_COMMIT=7a54785ea616df1e72f20b9cad2f1a6b3097f7b5
+PACKAGES_LUCI_BRANCH=for-15.05
diff --git a/package/gluon-alfred/Makefile b/package/gluon-alfred/Makefile
new file mode 100644
index 00000000..bb6926a5
--- /dev/null
+++ b/package/gluon-alfred/Makefile
@@ -0,0 +1,32 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-alfred
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-alfred
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ DEPENDS:=+gluon-core +gluon-announce +gluon-cron +alfred
+ TITLE:=Configure alfred
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-alfred/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-alfred))
diff --git a/package/gluon-alfred/files/lib/gluon/cron/alfred b/package/gluon-alfred/files/lib/gluon/cron/alfred
new file mode 100644
index 00000000..9f79bc64
--- /dev/null
+++ b/package/gluon-alfred/files/lib/gluon/cron/alfred
@@ -0,0 +1 @@
+* * * * * /lib/gluon/announce/collect.lua nodeinfo | gzip | alfred -s 158; /lib/gluon/announce/collect.lua statistics | gzip | alfred -s 159; /lib/gluon/announce/collect.lua neighbours | gzip | alfred -s 160
diff --git a/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred b/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred
new file mode 100755
index 00000000..8c52d8a6
--- /dev/null
+++ b/package/gluon-alfred/files/lib/gluon/upgrade/500-enable-alfred
@@ -0,0 +1,19 @@
+#!/usr/bin/lua
+
+local uci = require 'luci.model.uci'
+local c = uci.cursor()
+
+
+c:delete('alfred', 'alfred')
+c:section('alfred', 'alfred', 'alfred',
+ {
+ interface = 'br-client',
+ mode = 'slave',
+ batmanif = 'bat0',
+ start_vis = '1',
+ run_facters = '0',
+ }
+)
+
+c:save('alfred')
+c:commit('alfred')
diff --git a/package/gluon-announce/Makefile b/package/gluon-announce/Makefile
new file mode 100644
index 00000000..3f62c889
--- /dev/null
+++ b/package/gluon-announce/Makefile
@@ -0,0 +1,32 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-announce
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-announce
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ DEPENDS:=+gluon-core +luci-lib-json +lua-ethtool-stats
+ TITLE:=Lua scripts announcing various information
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-announce/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-announce))
diff --git a/package/gluon-announce/files/lib/gluon/announce/collect.lua b/package/gluon-announce/files/lib/gluon/announce/collect.lua
new file mode 100755
index 00000000..e2974b49
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/collect.lua
@@ -0,0 +1,10 @@
+#!/usr/bin/lua
+
+local announce = require 'gluon.announce'
+local json = require 'luci.json'
+local ltn12 = require 'luci.ltn12'
+
+local announce_dir = '/lib/gluon/announce/' .. arg[1] .. '.d'
+
+encoder = json.Encoder(announce.collect_dir(announce_dir))
+ltn12.pump.all(encoder:source(), ltn12.sink.file(io.stdout))
diff --git a/package/gluon-announce/files/lib/gluon/announce/neighbours.d/node_id b/package/gluon-announce/files/lib/gluon/announce/neighbours.d/node_id
new file mode 100644
index 00000000..66303f4c
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/neighbours.d/node_id
@@ -0,0 +1 @@
+return require('gluon.util').node_id()
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/model b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/model
new file mode 100644
index 00000000..aee3cd81
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/model
@@ -0,0 +1 @@
+return require('platform_info').get_model()
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/nproc b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/nproc
new file mode 100644
index 00000000..3072f8f8
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hardware/nproc
@@ -0,0 +1,14 @@
+local n = 0
+
+local cpus = util.trim(fs.readfile('/sys/devices/system/cpu/online'))
+
+for _, entry in ipairs(cpus:split(',')) do
+ local x, y = entry:match('(%d+)-(%d+)')
+ if x then
+ n = n + tonumber(y) - tonumber(x) + 1
+ else
+ n = n + 1
+ end
+end
+
+return n
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hostname b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hostname
new file mode 100644
index 00000000..7d4f0521
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/hostname
@@ -0,0 +1 @@
+return uci:get_first('system', 'system', 'hostname')
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/network/mac b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/network/mac
new file mode 100644
index 00000000..049eea58
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/network/mac
@@ -0,0 +1 @@
+return require('gluon.sysconfig').primary_mac
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/node_id b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/node_id
new file mode 100644
index 00000000..66303f4c
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/node_id
@@ -0,0 +1 @@
+return require('gluon.util').node_id()
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware
new file mode 100644
index 00000000..cf50f79f
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/software/firmware
@@ -0,0 +1,4 @@
+return {
+ base = 'gluon-' .. util.trim(fs.readfile('/lib/gluon/gluon-version')),
+ release = util.trim(fs.readfile('/lib/gluon/release')),
+}
diff --git a/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code
new file mode 100644
index 00000000..876fb6d8
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/nodeinfo.d/system/site_code
@@ -0,0 +1,3 @@
+local site = require 'gluon.site_config'
+
+return site.site_code
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime b/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime
new file mode 100644
index 00000000..845de268
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/idletime
@@ -0,0 +1 @@
+return tonumber(fs.readfile('/proc/uptime'):match('^[^ ]+ ([^ ]+)'))
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg b/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg
new file mode 100644
index 00000000..d79973aa
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/loadavg
@@ -0,0 +1 @@
+return tonumber(fs.readfile('/proc/loadavg'):match('^([^ ]+) '))
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory b/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory
new file mode 100644
index 00000000..7b07a107
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/memory
@@ -0,0 +1,13 @@
+local data = fs.readfile('/proc/meminfo')
+
+local fields = {}
+for k, v in data:gmatch('([^\n:]+):%s*(%d+) kB') do
+ fields[k] = tonumber(v)
+end
+
+return {
+ total = fields.MemTotal,
+ free = fields.MemFree,
+ buffers = fields.Buffers,
+ cached = fields.Cached,
+}
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/node_id b/package/gluon-announce/files/lib/gluon/announce/statistics.d/node_id
new file mode 100644
index 00000000..66303f4c
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/node_id
@@ -0,0 +1 @@
+return require('gluon.util').node_id()
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes b/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes
new file mode 100644
index 00000000..33ecff66
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/processes
@@ -0,0 +1,3 @@
+local running, total = fs.readfile('/proc/loadavg'):match('^[^ ]+ [^ ]+ [^ ]+ (%d+)/(%d+)')
+
+return { running = tonumber(running), total = tonumber(total) }
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/rootfs_usage b/package/gluon-announce/files/lib/gluon/announce/statistics.d/rootfs_usage
new file mode 100644
index 00000000..8426e9e1
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/rootfs_usage
@@ -0,0 +1,4 @@
+local fs = require "nixio.fs"
+
+local st = fs.statvfs("/")
+return 1 - st.bfree / st.blocks
diff --git a/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime b/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime
new file mode 100644
index 00000000..0bc45bea
--- /dev/null
+++ b/package/gluon-announce/files/lib/gluon/announce/statistics.d/uptime
@@ -0,0 +1 @@
+return tonumber(fs.readfile('/proc/uptime'):match('^([^ ]+) '))
diff --git a/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua b/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua
new file mode 100644
index 00000000..17109785
--- /dev/null
+++ b/package/gluon-announce/files/usr/lib/lua/gluon/announce.lua
@@ -0,0 +1,33 @@
+#!/usr/bin/lua
+
+module('gluon.announce', package.seeall)
+
+fs = require 'nixio.fs'
+uci = require('luci.model.uci').cursor()
+util = require 'luci.util'
+
+local function collect_entry(entry)
+ if fs.stat(entry, 'type') == 'dir' then
+ return collect_dir(entry)
+ else
+ return setfenv(loadfile(entry), _M)()
+ end
+end
+
+function collect_dir(dir)
+ local ret = {}
+
+ for entry in fs.dir(dir) do
+ if entry:sub(1, 1) ~= '.' then
+ local ok, val = pcall(collect_entry, dir .. '/' .. entry)
+ if ok then
+ ret[entry] = val
+ else
+ io.stderr:write(val, '\n')
+ end
+ end
+ end
+
+ return ret
+end
+
diff --git a/package/gluon-announced/Makefile b/package/gluon-announced/Makefile
new file mode 100644
index 00000000..d5b06dce
--- /dev/null
+++ b/package/gluon-announced/Makefile
@@ -0,0 +1,32 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-announced
+PKG_VERSION:=2
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-announced
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Provides node information to the network
+ DEPENDS:=+gluon-announce +respondd +lua-deflate
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-announced/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-announced))
diff --git a/package/gluon-announced/files/etc/hotplug.d/iface/10-gluon-announced b/package/gluon-announced/files/etc/hotplug.d/iface/10-gluon-announced
new file mode 100644
index 00000000..a1e2c45f
--- /dev/null
+++ b/package/gluon-announced/files/etc/hotplug.d/iface/10-gluon-announced
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+. /usr/share/libubox/jshn.sh
+. /lib/functions/service.sh
+
+DEVLIST=/var/run/gluon-announced.devs
+DAEMON=/usr/bin/respondd
+
+ifname_to_dev () {
+ json_load "$(ubus call network.interface.$1 status)"
+ json_get_var dev device
+
+ echo "$dev"
+}
+
+restart_announced () {
+ SERVICE_USE_PID=1
+ SERVICE_WRITE_PID=1
+ SERVICE_DAEMONIZE=1
+
+ DEVS=$(cat $DEVLIST | while read dev iface; do echo -n " -i $dev"; done)
+
+ service_stop $DAEMON
+ service_start $DAEMON -g ff02::2:1001 -p 1001 -c 'return require("gluon.announced").handle_request' $DEVS
+}
+
+case "$ACTION" in
+ ifdown)
+ sed -i "/$INTERFACE/d" $DEVLIST
+ ;;
+ ifup)
+ DEVICE="$(ifname_to_dev "$INTERFACE")"
+ MESH="$(cat "/sys/class/net/$DEVICE/batman_adv/mesh_iface" 2>/dev/null)"
+
+ [ "$MESH" = "bat0" -o "$INTERFACE" = "client" ] || exit 0
+
+ DEVS=$(cat $DEVLIST; echo $DEVICE $INTERFACE)
+
+ echo "$DEVS" | sort -u > $DEVLIST
+
+ restart_announced
+
+ ;;
+esac
+
diff --git a/package/gluon-announced/files/lib/gluon/upgrade/400-announced-firewall b/package/gluon-announced/files/lib/gluon/upgrade/400-announced-firewall
new file mode 100755
index 00000000..77acb1b9
--- /dev/null
+++ b/package/gluon-announced/files/lib/gluon/upgrade/400-announced-firewall
@@ -0,0 +1,18 @@
+#!/usr/bin/lua
+
+local uci = require('luci.model.uci').cursor()
+
+-- Allow announced port on WAN to allow resolving neighbours over mesh-on-wan
+uci:section('firewall', 'rule', 'wan_announced',
+ {
+ name = 'wan_announced',
+ src = 'wan',
+ src_ip = 'fe80::/64',
+ dest_port = '1001',
+ proto = 'udp',
+ target = 'ACCEPT',
+ }
+)
+
+uci:save('firewall')
+uci:commit('firewall')
diff --git a/package/gluon-announced/files/usr/lib/lua/gluon/announced.lua b/package/gluon-announced/files/usr/lib/lua/gluon/announced.lua
new file mode 100644
index 00000000..99116b81
--- /dev/null
+++ b/package/gluon-announced/files/usr/lib/lua/gluon/announced.lua
@@ -0,0 +1,33 @@
+local announce = require 'gluon.announce'
+local deflate = require 'deflate'
+local json = require 'luci.json'
+
+
+local function collect(type)
+ return announce.collect_dir('/lib/gluon/announce/' .. type .. '.d')
+end
+
+
+module('gluon.announced', package.seeall)
+
+function handle_request(query)
+ if query:match('^nodeinfo$') then
+ return json.encode(collect('nodeinfo'))
+ end
+
+ local m = query:match('^GET ([a-z ]+)$')
+ if m then
+ local data = {}
+
+ for q in m:gmatch('([a-z]+)') do
+ local ok, val = pcall(collect, q)
+ if ok then
+ data[q] = val
+ end
+ end
+
+ if next(data) then
+ return deflate.compress(json.encode(data))
+ end
+ end
+end
diff --git a/package/gluon-authorized-keys/Makefile b/package/gluon-authorized-keys/Makefile
new file mode 100644
index 00000000..6ef90da3
--- /dev/null
+++ b/package/gluon-authorized-keys/Makefile
@@ -0,0 +1,36 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-authorized-keys
+PKG_VERSION:=2
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(GLUONDIR)/include/package.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))
diff --git a/package/gluon-authorized-keys/check_site.lua b/package/gluon-authorized-keys/check_site.lua
new file mode 100644
index 00000000..d1acfabe
--- /dev/null
+++ b/package/gluon-authorized-keys/check_site.lua
@@ -0,0 +1 @@
+need_string_array 'authorized_keys'
diff --git a/package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys b/package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys
new file mode 100755
index 00000000..643fa076
--- /dev/null
+++ b/package/gluon-authorized-keys/files/lib/gluon/upgrade/100-authorized-keys
@@ -0,0 +1,22 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local file = '/etc/dropbear/authorized_keys'
+
+local keys = {}
+
+function load_keys()
+ for line in io.lines(file) do
+ keys[line] = true
+ end
+end
+
+pcall(load_keys)
+
+local f = io.open(file, 'a')
+for _, key in ipairs(site.authorized_keys) do
+ if not keys[key] then
+ f:write(key .. '\n')
+ end
+end
+f:close()
diff --git a/package/gluon-autoupdater/Makefile b/package/gluon-autoupdater/Makefile
new file mode 100644
index 00000000..b33bada9
--- /dev/null
+++ b/package/gluon-autoupdater/Makefile
@@ -0,0 +1,42 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-autoupdater
+PKG_VERSION:=4
+PKG_RELEASE:=$(GLUON_BRANCH)
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(GLUONDIR)/include/package.mk
+
+define Package/gluon-autoupdater
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ DEPENDS:=+gluon-core +gluon-cron +autoupdater
+ TITLE:=Automatically update firmware
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-autoupdater/install
+ $(CP) ./files/* $(1)/
+
+ if [ '$(GLUON_BRANCH)' ]; then \
+ $(INSTALL_DIR) $(1)/lib/gluon/autoupdater; \
+ echo '$(GLUON_BRANCH)' > $(1)/lib/gluon/autoupdater/default_branch; \
+ fi
+endef
+
+define Package/gluon-autoupdater/postinst
+#!/bin/sh
+$(call GluonCheckSite,check_site.lua)
+endef
+
+$(eval $(call BuildPackage,gluon-autoupdater))
diff --git a/package/gluon-autoupdater/check_site.lua b/package/gluon-autoupdater/check_site.lua
new file mode 100644
index 00000000..1d8996f0
--- /dev/null
+++ b/package/gluon-autoupdater/check_site.lua
@@ -0,0 +1,12 @@
+need_string 'autoupdater.branch'
+
+local function check_branch(k, _)
+ 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)
diff --git a/package/gluon-autoupdater/files/lib/gluon/announce/nodeinfo.d/software/autoupdater b/package/gluon-autoupdater/files/lib/gluon/announce/nodeinfo.d/software/autoupdater
new file mode 100644
index 00000000..75a67578
--- /dev/null
+++ b/package/gluon-autoupdater/files/lib/gluon/announce/nodeinfo.d/software/autoupdater
@@ -0,0 +1,7 @@
+local autoupdater = uci:get_all('autoupdater', 'settings')
+if autoupdater then
+ return {
+ branch = autoupdater['branch'],
+ enabled = uci:get_bool('autoupdater', 'settings', 'enabled'),
+ }
+end
diff --git a/package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater b/package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater
new file mode 100755
index 00000000..fec313b3
--- /dev/null
+++ b/package/gluon-autoupdater/files/lib/gluon/upgrade/500-autoupdater
@@ -0,0 +1,57 @@
+#!/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')
+c:commit('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('/lib/gluon/cron/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-config-mode-autoupdater/Makefile b/package/gluon-config-mode-autoupdater/Makefile
new file mode 100644
index 00000000..4303940b
--- /dev/null
+++ b/package/gluon-config-mode-autoupdater/Makefile
@@ -0,0 +1,40 @@
+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)
+
+
+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
+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))
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
new file mode 100644
index 00000000..3bbe0390
--- /dev/null
+++ b/package/gluon-config-mode-autoupdater/files/lib/gluon/config-mode/wizard/0050-autoupdater-info.lua
@@ -0,0 +1,19 @@
+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
new file mode 100644
index 00000000..5a55e349
--- /dev/null
+++ b/package/gluon-config-mode-autoupdater/i18n/de.po
@@ -0,0 +1,17 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-03-18 16:03+0100\n"
+"Last-Translator: Matthias Schiffer \n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid ""
+"This node will automatically update its firmware when a new version is "
+"available."
+msgstr "Dieser Knoten aktualisiert seine Firmware automatisch, sobald "
+"eine neue Version vorliegt."
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
new file mode 100644
index 00000000..9e6272cd
--- /dev/null
+++ b/package/gluon-config-mode-autoupdater/i18n/gluon-config-mode-autoupdater.pot
@@ -0,0 +1,7 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid ""
+"This node will automatically update its firmware when a new version is "
+"available."
+msgstr ""
diff --git a/package/gluon-config-mode-contact-info/Makefile b/package/gluon-config-mode-contact-info/Makefile
new file mode 100644
index 00000000..ff9cb57f
--- /dev/null
+++ b/package/gluon-config-mode-contact-info/Makefile
@@ -0,0 +1,36 @@
+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)
+
+
+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
+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))
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
new file mode 100644
index 00000000..a2182f95
--- /dev/null
+++ b/package/gluon-config-mode-contact-info/files/lib/gluon/config-mode/wizard/0500-contact-info.lua
@@ -0,0 +1,34 @@
+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
new file mode 100644
index 00000000..719246f4
--- /dev/null
+++ b/package/gluon-config-mode-contact-info/i18n/de.po
@@ -0,0 +1,27 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-03-19 01:32+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 "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."
+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."
+
+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/gluon-config-mode-contact-info.pot b/package/gluon-config-mode-contact-info/i18n/gluon-config-mode-contact-info.pot
new file mode 100644
index 00000000..63939a83
--- /dev/null
+++ b/package/gluon-config-mode-contact-info/i18n/gluon-config-mode-contact-info.pot
@@ -0,0 +1,14 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+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."
+msgstr ""
+
+msgid "e.g. E-mail or phone number"
+msgstr ""
diff --git a/package/gluon-config-mode-core/Makefile b/package/gluon-config-mode-core/Makefile
new file mode 100644
index 00000000..5d700f7f
--- /dev/null
+++ b/package/gluon-config-mode-core/Makefile
@@ -0,0 +1,40 @@
+# Copyright (C) 2012 Nils Schneider
+# This is free software, licensed under the Apache 2.0 license.
+
+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)
+
+
+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)
+endef
+
+define Package/gluon-config-mode-core/install
+ $(CP) ./files/* $(1)/
+ $(call GluonInstallI18N,gluon-config-mode-core,$(1))
+endef
+
+$(eval $(call BuildPackage,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
new file mode 100644
index 00000000..bf27c07f
--- /dev/null
+++ b/package/gluon-config-mode-core/files/lib/gluon/config-mode/reboot/0900-msg-reboot.lua
@@ -0,0 +1,3 @@
+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/usr/lib/lua/luci/controller/gluon-config-mode/index.lua b/package/gluon-config-mode-core/files/usr/lib/lua/luci/controller/gluon-config-mode/index.lua
new file mode 100644
index 00000000..39ba00a9
--- /dev/null
+++ b/package/gluon-config-mode-core/files/usr/lib/lua/luci/controller/gluon-config-mode/index.lua
@@ -0,0 +1,90 @@
+--[[
+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.gluon-config-mode.index", package.seeall)
+
+function index()
+ local uci_state = luci.model.uci.cursor_state()
+
+ if uci_state:get_first("gluon-setup-mode", "setup_mode", "running", "0") == "1" then
+ local root = node()
+ if not root.target then
+ root.target = alias("gluon-config-mode")
+ root.index = true
+ end
+
+ page = node()
+ page.lock = true
+ page.target = alias("gluon-config-mode")
+ page.subindex = true
+ page.index = false
+
+ page = node("gluon-config-mode")
+ page.title = _("Wizard")
+ page.target = alias("gluon-config-mode", "wizard")
+ page.order = 5
+ page.setuser = "root"
+ page.setgroup = "root"
+ page.index = true
+
+ entry({"gluon-config-mode", "wizard"}, form("gluon-config-mode/wizard")).index = true
+ entry({"gluon-config-mode", "reboot"}, call("action_reboot"))
+ end
+end
+
+function action_reboot()
+ local uci = luci.model.uci.cursor()
+
+ uci:set("gluon-setup-mode", uci:get_first("gluon-setup-mode", "setup_mode"), "configured", "1")
+ uci:save("gluon-setup-mode")
+ uci:commit("gluon-setup-mode")
+
+ if nixio.fork() ~= 0 then
+ 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))
+
+ table.sort(files)
+
+ local parts = {}
+
+ for _, entry in ipairs(files) do
+ if entry:sub(1, 1) ~= '.' then
+ local f = dofile(parts_dir .. '/' .. entry)
+ if f ~= nil then
+ table.insert(parts, f)
+ end
+ end
+ end
+
+ local hostname = uci:get_first("system", "system", "hostname")
+
+ luci.template.render("gluon/config-mode/reboot", { parts=parts
+ , hostname=hostname
+ })
+ else
+ debug.setfenv(io.stdout, debug.getfenv(io.open '/dev/null'))
+ io.stdout:close()
+
+ -- Sleep a little so the browser can fetch everything required to
+ -- display the reboot page, then reboot the device.
+ nixio.nanosleep(2)
+
+ -- Run reboot with popen so it gets its own std filehandles.
+ io.popen("reboot")
+
+ -- Prevent any further execution in this child.
+ os.exit()
+ end
+end
diff --git a/package/gluon-config-mode-core/files/usr/lib/lua/luci/model/cbi/gluon-config-mode/wizard.lua b/package/gluon-config-mode-core/files/usr/lib/lua/luci/model/cbi/gluon-config-mode/wizard.lua
new file mode 100644
index 00000000..dda5e58c
--- /dev/null
+++ b/package/gluon-config-mode-core/files/usr/lib/lua/luci/model/cbi/gluon-config-mode/wizard.lua
@@ -0,0 +1,39 @@
+local wizard_dir = "/lib/gluon/config-mode/wizard/"
+local i18n = luci.i18n
+local uci = luci.model.uci.cursor()
+local fs = require "nixio.fs"
+local util = require "nixio.util"
+local f, s
+
+local wizard = {}
+local files = util.consume(fs.dir(wizard_dir))
+
+table.sort(files)
+
+for _, entry in ipairs(files) do
+ if entry:sub(1, 1) ~= '.' then
+ table.insert(wizard, dofile(wizard_dir .. '/' .. entry))
+ end
+end
+
+f = SimpleForm("wizard")
+f.reset = false
+f.template = "gluon/cbi/config-mode"
+
+for _, s in ipairs(wizard) do
+ s.section(f)
+end
+
+function f.handle(self, state, data)
+ if state == FORM_VALID then
+ for _, s in ipairs(wizard) do
+ s.handle(data)
+ end
+
+ luci.http.redirect(luci.dispatcher.build_url("gluon-config-mode", "reboot"))
+ end
+
+ return true
+end
+
+return f
diff --git a/package/gluon-config-mode-core/files/usr/lib/lua/luci/view/gluon/cbi/config-mode.htm b/package/gluon-config-mode-core/files/usr/lib/lua/luci/view/gluon/cbi/config-mode.htm
new file mode 100644
index 00000000..d2210eca
--- /dev/null
+++ b/package/gluon-config-mode-core/files/usr/lib/lua/luci/view/gluon/cbi/config-mode.htm
@@ -0,0 +1,46 @@
+<%-
+ local sysconfig = require 'gluon.sysconfig'
+ local i18n = require 'luci.i18n'
+ local template = require 'luci.template'
+-%>
+
+
]]
+
+ return function ()
+ luci.template.render_string(msg, { pubkey=pubkey
+ , hostname=hostname
+ , site=site
+ , sysconfig=sysconfig
+ })
+ end
+end
diff --git a/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua b/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
new file mode 100644
index 00000000..669a7bc3
--- /dev/null
+++ b/package/gluon-config-mode-mesh-vpn/files/lib/gluon/config-mode/wizard/0300-mesh-vpn.lua
@@ -0,0 +1,64 @@
+local cbi = require "luci.cbi"
+local i18n = require "luci.i18n"
+local uci = luci.model.uci.cursor()
+
+local M = {}
+
+function M.section(form)
+ local msg = i18n.translate('Your internet connection can be used to establish an ' ..
+ 'encrypted connection with other nodes. ' ..
+ 'Enable this option if there are no other nodes reachable ' ..
+ 'over WLAN in your vicinity or you want to make a part of ' ..
+ 'your connection\'s bandwidth available for the network. You can limit how ' ..
+ 'much bandwidth the node will use at most.')
+ local s = form:section(cbi.SimpleSection, nil, msg)
+
+ local o
+
+ o = s:option(cbi.Flag, "_meshvpn", i18n.translate("Use internet connection (mesh VPN)"))
+ o.default = uci:get_bool("fastd", "mesh_vpn", "enabled") and o.enabled or o.disabled
+ o.rmempty = false
+
+ o = s:option(cbi.Flag, "_limit_enabled", i18n.translate("Limit bandwidth"))
+ o:depends("_meshvpn", "1")
+ o.default = uci:get_bool("gluon-simple-tc", "mesh_vpn", "enabled") and o.enabled or o.disabled
+ o.rmempty = false
+
+ o = s:option(cbi.Value, "_limit_ingress", i18n.translate("Downstream (kbit/s)"))
+ o:depends("_limit_enabled", "1")
+ o.value = uci:get("gluon-simple-tc", "mesh_vpn", "limit_ingress")
+ o.rmempty = false
+ o.datatype = "integer"
+
+ o = s:option(cbi.Value, "_limit_egress", i18n.translate("Upstream (kbit/s)"))
+ o:depends("_limit_enabled", "1")
+ o.value = uci:get("gluon-simple-tc", "mesh_vpn", "limit_egress")
+ o.rmempty = false
+ o.datatype = "integer"
+end
+
+function M.handle(data)
+ uci:set("fastd", "mesh_vpn", "enabled", data._meshvpn)
+ uci:save("fastd")
+ uci:commit("fastd")
+
+ -- checks for nil needed due to o:depends(...)
+ if data._limit_enabled ~= nil then
+ uci:set("gluon-simple-tc", "mesh_vpn", "interface")
+ uci:set("gluon-simple-tc", "mesh_vpn", "enabled", data._limit_enabled)
+ uci:set("gluon-simple-tc", "mesh_vpn", "ifname", "mesh-vpn")
+
+ if data._limit_ingress ~= nil then
+ uci:set("gluon-simple-tc", "mesh_vpn", "limit_ingress", data._limit_ingress)
+ end
+
+ if data._limit_egress ~= nil then
+ uci:set("gluon-simple-tc", "mesh_vpn", "limit_egress", data._limit_egress)
+ end
+
+ uci:commit("gluon-simple-tc")
+ uci:commit("gluon-simple-tc")
+ end
+end
+
+return M
diff --git a/package/gluon-config-mode-mesh-vpn/i18n/de.po b/package/gluon-config-mode-mesh-vpn/i18n/de.po
new file mode 100644
index 00000000..8c613aa0
--- /dev/null
+++ b/package/gluon-config-mode-mesh-vpn/i18n/de.po
@@ -0,0 +1,36 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-03-19 22:05+0100\n"
+"Last-Translator: Matthias Schiffer \n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Downstream (kbit/s)"
+msgstr "Downstream (kbit/s)"
+
+msgid "Limit bandwidth"
+msgstr "Bandbreite begrenzen"
+
+msgid "Upstream (kbit/s)"
+msgstr "Upstream (kbit/s)"
+
+msgid "Use internet connection (mesh VPN)"
+msgstr "Internetverbindung nutzen (Mesh-VPN)"
+
+msgid ""
+"Your internet connection can be used to establish an encrypted connection "
+"with other nodes. Enable this option if there are no other nodes reachable "
+"over WLAN in your vicinity or you want to make a part of your connection's "
+"bandwidth available for the network. You can limit how much bandwidth the "
+"node will use at most."
+msgstr ""
+"Dein Knoten kann deine Internetverbindung nutzen um darüber eine "
+"verschlüsselte Verbindung zu anderen Knoten aufzubauen. Die dafür "
+"genutzte Bandbreite kannst du beschränken. Aktiviere die Option, falls keine "
+"per WLAN erreichbaren Nachbarknoten in deiner Nähe sind oder du deine "
+"Internetverbindung für das Mesh-Netzwerk zur Verfügung stellen möchtest."
diff --git a/package/gluon-config-mode-mesh-vpn/i18n/gluon-config-mode-mesh-vpn.pot b/package/gluon-config-mode-mesh-vpn/i18n/gluon-config-mode-mesh-vpn.pot
new file mode 100644
index 00000000..52e2eef8
--- /dev/null
+++ b/package/gluon-config-mode-mesh-vpn/i18n/gluon-config-mode-mesh-vpn.pot
@@ -0,0 +1,22 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "Downstream (kbit/s)"
+msgstr ""
+
+msgid "Limit bandwidth"
+msgstr ""
+
+msgid "Upstream (kbit/s)"
+msgstr ""
+
+msgid "Use internet connection (mesh VPN)"
+msgstr ""
+
+msgid ""
+"Your internet connection can be used to establish an encrypted connection "
+"with other nodes. Enable this option if there are no other nodes reachable "
+"over WLAN in your vicinity or you want to make a part of your connection's "
+"bandwidth available for the network. You can limit how much bandwidth the "
+"node will use at most."
+msgstr ""
diff --git a/package/gluon-core/Makefile b/package/gluon-core/Makefile
new file mode 100644
index 00000000..bf9768e2
--- /dev/null
+++ b/package/gluon-core/Makefile
@@ -0,0 +1,45 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-core
+PKG_VERSION:=3
+PKG_RELEASE:=$(GLUON_VERSION)
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(GLUONDIR)/include/package.mk
+
+define Package/gluon-core
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Base files of Gluon
+ DEPENDS:=+gluon-site +lua-platform-info +luci-base +odhcp6c +firewall
+endef
+
+
+define Package/gluon-core/description
+ Gluon community wifi mesh firmware framework: core
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-core/install
+ $(CP) ./files/* $(1)/
+
+ $(INSTALL_DIR) $(1)/lib/gluon
+ echo "$(GLUON_VERSION)" > $(1)/lib/gluon/gluon-version
+endef
+
+define Package/gluon-core/postinst
+#!/bin/sh
+$(call GluonCheckSite,check_site.lua)
+endef
+
+$(eval $(call BuildPackage,gluon-core))
diff --git a/package/gluon-core/check_site.lua b/package/gluon-core/check_site.lua
new file mode 100644
index 00000000..1a6987a0
--- /dev/null
+++ b/package/gluon-core/check_site.lua
@@ -0,0 +1,10 @@
+need_string 'site_code'
+need_string 'site_name'
+
+need_string('hostname_prefix', false)
+need_string 'timezone'
+
+need_string_array('ntp_servers', false)
+
+need_string_match('prefix4', '^%d+.%d+.%d+.%d+/%d+$')
+need_string_match('prefix6', '^[%x:]+/%d+$')
diff --git a/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade b/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade
new file mode 100755
index 00000000..a12ce78e
--- /dev/null
+++ b/package/gluon-core/files/etc/uci-defaults/zzz-gluon-upgrade
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+for script in /lib/gluon/upgrade/*; do
+ "$script"
+done
diff --git a/package/gluon-core/files/lib/gluon/core/sysconfig/.keep b/package/gluon-core/files/lib/gluon/core/sysconfig/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/package/gluon-core/files/lib/gluon/upgrade/001-upgrade b/package/gluon-core/files/lib/gluon/upgrade/001-upgrade
new file mode 100755
index 00000000..5d53398c
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/001-upgrade
@@ -0,0 +1,10 @@
+#!/usr/bin/lua
+
+local fs = require 'nixio.fs'
+local sysconfig = require 'gluon.sysconfig'
+
+
+if fs.stat('/lib/gluon/version/core') and not sysconfig.gluon_version then
+ -- This isn't an initial upgrade, so set gluon_version
+ sysconfig.gluon_version = ''
+end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac b/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac
new file mode 100755
index 00000000..d7c6e968
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/010-primary-mac
@@ -0,0 +1,42 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+
+
+if sysconfig.primary_mac then
+ os.exit(0)
+end
+
+
+local platform = require 'gluon.platform'
+
+local fs = require 'nixio.fs'
+local util = require 'luci.util'
+
+
+local try_files = {
+ '/sys/class/ieee80211/phy0/macaddress',
+ '/sys/class/net/eth0/address',
+}
+
+if platform.match('ar71xx', 'generic', {'tl-wdr3600', 'tl-wdr4300'}) then
+ table.insert(try_files, 1, '/sys/class/ieee80211/phy1/macaddress')
+end
+
+if platform.match('ar71xx', 'generic', {'unifi-outdoor-plus'}) then
+ table.insert(try_files, 1, '/sys/class/net/eth0/address')
+end
+
+if platform.match('ar71xx', 'generic', {'archer-c5', 'archer-c7'}) then
+ table.insert(try_files, 1, '/sys/class/net/eth1/address')
+end
+
+
+for _, file in ipairs(try_files) do
+ local addr = fs.readfile(file)
+
+ if addr then
+ sysconfig.primary_mac = util.trim(addr)
+ break
+ end
+end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/020-interfaces b/package/gluon-core/files/lib/gluon/upgrade/020-interfaces
new file mode 100755
index 00000000..a051c738
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/020-interfaces
@@ -0,0 +1,36 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+local gluon_util = require 'gluon.util'
+local platform = require 'gluon.platform'
+
+local uci = require('luci.model.uci').cursor()
+
+
+if not (sysconfig.lan_ifname or sysconfig.wan_ifname) then
+ local function iface_exists(name)
+ return (gluon_util.exec('ip', 'link', 'show', 'dev', (name:gsub('%..*$', ''))) == 0)
+ end
+
+
+ local lan_ifname = uci:get('network', 'lan', 'ifname')
+ local wan_ifname = uci:get('network', 'wan', 'ifname')
+
+ if platform.match('ar71xx', 'generic', {'cpe510', 'nanostation-m', 'nanostation-m-xw', 'unifi-outdoor-plus'}) then
+ lan_ifname, wan_ifname = wan_ifname, lan_ifname
+ end
+
+ if wan_ifname and iface_exists(wan_ifname) then
+ sysconfig.wan_ifname = wan_ifname
+ sysconfig.lan_ifname = lan_ifname
+ else
+ sysconfig.wan_ifname = lan_ifname
+ end
+
+
+ uci:delete('network', 'lan')
+ uci:delete('network', 'wan')
+
+ uci:save('network')
+ uci:commit('network')
+end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/030-system b/package/gluon-core/files/lib/gluon/upgrade/030-system
new file mode 100755
index 00000000..d7a66605
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/030-system
@@ -0,0 +1,18 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+
+-- Initial
+if not sysconfig.gluon_version then
+ local site = require 'gluon.site_config'
+ local util = require 'gluon.util'
+ local uci = require('luci.model.uci').cursor()
+
+ local system = uci:get_first('system', 'system')
+
+ uci:set('system', system, 'hostname', (site.hostname_prefix or '') .. util.node_id())
+ uci:set('system', system, 'timezone', site.timezone)
+
+ uci:save('system')
+ uci:commit('system')
+end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq b/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq
new file mode 100755
index 00000000..3636fbf6
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/100-dnsmasq
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ -e /etc/dnsmasq.conf ]; then
+ sed -i -e '/^conf-dir=\/lib\/gluon\/dnsmasq\.d$/d' -e '/^conf-dir=\/var\/gluon\/dnsmasq\.d$/d' /etc/dnsmasq.conf
+fi
diff --git a/package/gluon-core/files/lib/gluon/upgrade/110-network b/package/gluon-core/files/lib/gluon/upgrade/110-network
new file mode 100755
index 00000000..07961788
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/110-network
@@ -0,0 +1,59 @@
+#!/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,
+ }
+)
+
+if not uci:get('network', 'wan6', 'proto') then
+ uci:set('network', 'wan6', 'proto', 'dhcpv6')
+end
+
+
+uci:section('network', 'rule6', 'wan6_lookup',
+ {
+ mark = '0x01/0x01',
+ lookup = 1,
+ }
+)
+
+uci:section('network', 'route6', 'wan6_unreachable',
+ {
+ type = 'unreachable',
+ interface = 'loopback',
+ target = '::/0',
+ gateway = '::',
+ table = 1,
+ metric = 65535,
+ }
+)
+
+uci:save('network')
+uci:commit('network')
+
+
+sysctl.set('net.ipv6.conf.all.accept_ra', 0)
+sysctl.set('net.ipv6.conf.default.accept_ra', 0)
diff --git a/package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers b/package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers
new file mode 100755
index 00000000..2b3a2df6
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/120-ntp-servers
@@ -0,0 +1,14 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local uci = require 'luci.model.uci'
+
+if not site.ntp_servers or #site.ntp_servers == 0 then
+ os.exit(0)
+end
+
+local c = uci.cursor()
+c:delete('system', 'ntp', 'server')
+c:set_list('system', 'ntp', 'server', site.ntp_servers)
+c:save('system')
+c:commit('system')
diff --git a/package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom b/package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom
new file mode 100755
index 00000000..48cfc5a8
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/130-reboot-on-oom
@@ -0,0 +1,5 @@
+#!/usr/bin/lua
+
+local sysctl = require 'gluon.sysctl'
+
+sysctl.set('vm.panic_on_oom', 1)
diff --git a/package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules b/package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules
new file mode 100755
index 00000000..792e06a2
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/140-firewall-rules
@@ -0,0 +1,30 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local uci = require 'luci.model.uci'
+
+local c = uci.cursor()
+
+
+local function reject_input_on_wan(zone)
+ if zone.name == 'wan' then
+ c:set('firewall', zone['.name'], 'input', 'REJECT')
+ c:set('firewall', zone['.name'], 'conntrack', '1')
+ end
+
+ return true
+end
+c:foreach('firewall', 'zone', reject_input_on_wan)
+
+c:section('firewall', 'rule', 'wan_ssh',
+ {
+ name = 'wan_ssh',
+ src = 'wan',
+ dest_port = '22',
+ proto = 'tcp',
+ target = 'ACCEPT',
+ }
+)
+
+c:save('firewall')
+c:commit('firewall')
diff --git a/package/gluon-core/files/lib/gluon/upgrade/200-wireless b/package/gluon-core/files/lib/gluon/upgrade/200-wireless
new file mode 100755
index 00000000..219e505d
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/200-wireless
@@ -0,0 +1,12 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+
+-- Initial
+if not sysconfig.gluon_version then
+ local uci = require('luci.model.uci').cursor()
+
+ uci:delete_all('wireless', 'wifi-iface')
+ uci:save('wireless')
+ uci:commit('wireless')
+end
diff --git a/package/gluon-core/files/lib/gluon/upgrade/999-version b/package/gluon-core/files/lib/gluon/upgrade/999-version
new file mode 100755
index 00000000..9731692e
--- /dev/null
+++ b/package/gluon-core/files/lib/gluon/upgrade/999-version
@@ -0,0 +1,11 @@
+#!/usr/bin/lua
+
+local sysconfig = require 'gluon.sysconfig'
+
+local fs = require 'nixio.fs'
+local util = require 'luci.util'
+
+
+-- Save the Gluon version in the sysconfig so we know which version we
+-- upgraded from after the next upgrade
+sysconfig.gluon_version = util.trim(fs.readfile('/lib/gluon/gluon-version'))
diff --git a/package/gluon-core/files/lib/upgrade/keep.d/gluon b/package/gluon-core/files/lib/upgrade/keep.d/gluon
new file mode 100644
index 00000000..bc82c775
--- /dev/null
+++ b/package/gluon-core/files/lib/upgrade/keep.d/gluon
@@ -0,0 +1 @@
+/lib/gluon/core/sysconfig/
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/platform.lua b/package/gluon-core/files/usr/lib/lua/gluon/platform.lua
new file mode 100644
index 00000000..3d56f081
--- /dev/null
+++ b/package/gluon-core/files/usr/lib/lua/gluon/platform.lua
@@ -0,0 +1,31 @@
+local platform_info = require 'platform_info'
+local util = require 'luci.util'
+
+local setmetatable = setmetatable
+
+
+module 'gluon.platform'
+
+setmetatable(_M,
+ {
+ __index = platform_info,
+ }
+)
+
+function match(target, subtarget, boards)
+ if get_target() ~= target then
+ return false
+ end
+
+ if get_subtarget() ~= subtarget then
+ return false
+ end
+
+ if not util.contains(boards, get_board_name()) then
+ return false
+ end
+
+ return true
+end
+
+
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua b/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua
new file mode 100644
index 00000000..cf151483
--- /dev/null
+++ b/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua
@@ -0,0 +1,21 @@
+local config = os.getenv('GLUON_SITE_CONFIG') or '/lib/gluon/site.conf'
+
+local function loader()
+ coroutine.yield('return ')
+ coroutine.yield(io.open(config):read('*a'))
+end
+
+-- setfenv doesn't work with Lua 5.2 anymore, but we're using 5.1
+local site_config = setfenv(assert(load(coroutine.wrap(loader), 'site.conf')), {})()
+
+local setmetatable = setmetatable
+
+module 'gluon.site_config'
+
+setmetatable(_M,
+ {
+ __index = site_config,
+ }
+)
+
+return _M
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua b/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua
new file mode 100644
index 00000000..ff61f05b
--- /dev/null
+++ b/package/gluon-core/files/usr/lib/lua/gluon/sysconfig.lua
@@ -0,0 +1,34 @@
+local sysconfigdir = '/lib/gluon/core/sysconfig/'
+
+local function get(_, name)
+ local f = io.open(sysconfigdir .. name)
+ if f then
+ local ret = f:read('*line')
+ f:close()
+ return (ret or '')
+ end
+ return nil
+end
+
+local function set(_, name, val)
+ if val then
+ local f = io.open(sysconfigdir .. name, 'w+')
+ f:write(val)
+ f:close()
+ else
+ os.remove(sysconfigdir .. name)
+ end
+end
+
+local setmetatable = setmetatable
+
+module 'gluon.sysconfig'
+
+setmetatable(_M,
+ {
+ __index = get,
+ __newindex = set,
+ }
+)
+
+return _M
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua b/package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua
new file mode 100644
index 00000000..44b0c217
--- /dev/null
+++ b/package/gluon-core/files/usr/lib/lua/gluon/sysctl.lua
@@ -0,0 +1,8 @@
+local util = require 'gluon.util'
+
+
+module 'gluon.sysctl'
+
+function set(name, value)
+ util.replace_prefix('/etc/sysctl.conf', name .. '=', name .. '=' .. value .. '\n')
+end
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/users.lua b/package/gluon-core/files/usr/lib/lua/gluon/users.lua
new file mode 100644
index 00000000..8e618d88
--- /dev/null
+++ b/package/gluon-core/files/usr/lib/lua/gluon/users.lua
@@ -0,0 +1,33 @@
+local util = require 'gluon.util'
+
+local os = os
+local string = string
+
+
+module 'gluon.users'
+
+function add_user(username, uid, gid)
+ util.lock('/var/lock/passwd')
+ util.replace_prefix('/etc/passwd', username .. ':', string.format('%s:*:%u:%u::/var:/bin/false\n', username, uid, gid))
+ util.replace_prefix('/etc/shadow', username .. ':', string.format('%s:*:0:0:99999:7:::\n', username))
+ util.unlock('/var/lock/passwd')
+end
+
+function remove_user(username)
+ util.lock('/var/lock/passwd')
+ util.replace_prefix('/etc/passwd', username .. ':')
+ util.replace_prefix('/etc/shadow', username .. ':')
+ util.unlock('/var/lock/passwd')
+end
+
+function add_group(groupname, gid)
+ util.lock('/var/lock/group')
+ util.replace_prefix('/etc/group', groupname .. ':', string.format('%s:x:%u:\n', groupname, gid))
+ util.unlock('/var/lock/group')
+end
+
+function remove_group(groupname)
+ util.lock('/var/lock/group')
+ util.replace_prefix('/etc/group', groupname .. ':')
+ util.unlock('/var/lock/group')
+end
diff --git a/package/gluon-core/files/usr/lib/lua/gluon/util.lua b/package/gluon-core/files/usr/lib/lua/gluon/util.lua
new file mode 100644
index 00000000..cf3677cb
--- /dev/null
+++ b/package/gluon-core/files/usr/lib/lua/gluon/util.lua
@@ -0,0 +1,79 @@
+-- Writes all lines from the file input to the file output except those starting with prefix
+-- Doesn't close the output file, but returns the file object
+local function do_filter_prefix(input, output, prefix)
+ local f = io.open(output, 'w+')
+ local l = prefix:len()
+
+ for line in io.lines(input) do
+ if line:sub(1, l) ~= prefix then
+ f:write(line, '\n')
+ end
+ end
+
+ return f
+end
+
+
+local function escape_args(ret, arg0, ...)
+ if not arg0 then
+ return ret
+ end
+
+ return escape_args(ret .. "'" .. string.gsub(arg0, "'", "'\\''") .. "' ", ...)
+end
+
+
+local os = os
+local string = string
+local tonumber = tonumber
+
+local nixio = require 'nixio'
+local sysconfig = require 'gluon.sysconfig'
+
+
+module 'gluon.util'
+
+function exec(...)
+ return os.execute(escape_args('', ...))
+end
+
+-- Removes all lines starting with a prefix from a file, optionally adding a new one
+function replace_prefix(file, prefix, add)
+ local tmp = file .. '.tmp'
+ local f = do_filter_prefix(file, tmp, prefix)
+ if add then
+ f:write(add)
+ end
+ f:close()
+ os.rename(tmp, file)
+end
+
+function lock(file)
+ exec('lock', file)
+end
+
+function unlock(file)
+ exec('lock', '-u', file)
+end
+
+function node_id()
+ return string.gsub(sysconfig.primary_mac, ':', '')
+end
+
+-- Generates a (hopefully) unique MAC address
+-- The first parameter defines the function and the second
+-- parameter an ID to add to the MAC address
+-- Functions and IDs defined so far:
+-- (1, 0): WAN (for mesh-on-WAN)
+-- (1, 1): LAN (for mesh-on-LAN)
+-- (2, n): client interface for the n'th radio
+-- (3, n): adhoc interface for n'th radio
+-- (4, 0): mesh VPN
+function generate_mac(f, i)
+ local m1, m2, m3, m4, m5, m6 = string.match(sysconfig.primary_mac, '(%x%x):(%x%x):(%x%x):(%x%x):(%x%x):(%x%x)')
+ m1 = nixio.bit.bor(tonumber(m1, 16), 0x02)
+ m2 = (tonumber(m2, 16)+f) % 0x100
+ m3 = (tonumber(m3, 16)+i) % 0x100
+
+ return string.format('%02x:%02x:%02x:%s:%s:%s', m1, m2, m3, m4, m5, m6)
+end
diff --git a/package/gluon-cron/Makefile b/package/gluon-cron/Makefile
new file mode 100644
index 00000000..ac92a92d
--- /dev/null
+++ b/package/gluon-cron/Makefile
@@ -0,0 +1,40 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-cron
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-cron
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Cron support
+ DEPENDS:=+gluon-core
+endef
+
+define Package/gluon-cron/description
+ Gluon community wifi mesh firmware framework: cron support
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+ $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+ CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
+endef
+
+define Package/gluon-cron/install
+ $(CP) ./files/* $(1)/
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-crond $(1)/usr/sbin/
+endef
+
+$(eval $(call BuildPackage,gluon-cron))
diff --git a/package/gluon-cron/files/etc/init.d/gluon-cron b/package/gluon-cron/files/etc/init.d/gluon-cron
new file mode 100755
index 00000000..27a05e1d
--- /dev/null
+++ b/package/gluon-cron/files/etc/init.d/gluon-cron
@@ -0,0 +1,18 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2013 Project Gluon
+
+START=50
+
+SERVICE_USE_PID=1
+SERVICE_WRITE_PID=1
+SERVICE_DAEMONIZE=1
+
+CRONDIR=/lib/gluon/cron
+
+start () {
+ service_start /usr/sbin/gluon-crond "$CRONDIR"
+}
+
+stop() {
+ service_stop /usr/sbin/gluon-crond
+}
diff --git a/package/gluon-cron/files/lib/gluon/cron/.keep b/package/gluon-cron/files/lib/gluon/cron/.keep
new file mode 100644
index 00000000..e69de29b
diff --git a/package/gluon-cron/src/Makefile b/package/gluon-cron/src/Makefile
new file mode 100644
index 00000000..3f4c7a50
--- /dev/null
+++ b/package/gluon-cron/src/Makefile
@@ -0,0 +1,3 @@
+all: gluon-crond
+
+gluon-crond: gluon-crond.c
diff --git a/package/gluon-cron/src/gluon-crond.c b/package/gluon-cron/src/gluon-crond.c
new file mode 100644
index 00000000..11a87c42
--- /dev/null
+++ b/package/gluon-cron/src/gluon-crond.c
@@ -0,0 +1,316 @@
+/*
+ Copyright (c) 2013, Matthias Schiffer
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+typedef struct job {
+ struct job *next;
+
+ uint64_t minutes;
+ uint32_t hours;
+ uint32_t doms;
+ uint16_t months;
+ uint8_t dows;
+
+ char *command;
+} job_t;
+
+
+static const char const *const MONTHS[12] = {
+ "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"
+};
+
+static const char const *const WEEKDAYS[7] = {
+ "sun", "mon", "tue", "wed", "thu", "fri", "sat"
+};
+
+
+static const char *crondir;
+
+static job_t *jobs = NULL;
+
+
+static void usage(void) {
+ fprintf(stderr, "Usage: gluon-crond \n");
+}
+
+
+static inline uint64_t bit(unsigned b) {
+ return ((uint64_t)1) << b;
+}
+
+static int strict_atoi(const char *s) {
+ char *end;
+ int ret = strtol(s, &end, 10);
+
+ if (*end)
+ return -1;
+ else
+ return ret;
+}
+
+static uint64_t parse_strings(const char *input, const char *const *strings, size_t n) {
+ size_t i;
+ for (i = 0; i < n; i++) {
+ if (strcasecmp(input, strings[i]) == 0)
+ return bit(i);
+ }
+
+ return 0;
+}
+
+static uint64_t parse_times(char *input, int min, int n) {
+ uint64_t ret = 0;
+ int step = 1;
+
+ char *comma = strchr(input, ',');
+ if (comma) {
+ *comma = 0;
+ ret = parse_times(comma+1, min, n);
+
+ if (!ret)
+ return 0;
+ }
+
+ char *slash = strchr(input, '/');
+ if (slash) {
+ *slash = 0;
+ step = strict_atoi(slash+1);
+
+ if (step <= 0)
+ return 0;
+ }
+
+ int begin, end;
+ char *minus = strchr(input, '-');
+ if (minus) {
+ *minus = 0;
+ begin = strict_atoi(input);
+ end = strict_atoi(minus+1);
+ }
+ else if (strcmp(input, "*") == 0) {
+ begin = min;
+ end = min+n-1;
+ }
+ else {
+ begin = end = strict_atoi(input);
+ }
+
+ if (begin < min || end < min)
+ return 0;
+
+ int i;
+ for (i = begin-min; i <= end-min; i += step)
+ ret |= bit(i % n);
+
+ return ret;
+}
+
+static int handle_line(const char *line) {
+ job_t job = {};
+ int ret = -1;
+ char *columns[5];
+ int i;
+ int len;
+
+ int matches = sscanf(line, "%ms %ms %ms %ms %ms %n", &columns[0], &columns[1], &columns[2], &columns[3], &columns[4], &len);
+ if (matches != 5 && matches != 6) {
+ if (matches <= 0)
+ ret = 0;
+
+ goto end;
+ }
+
+ job.minutes = parse_times(columns[0], 0, 60);
+ if (!job.minutes)
+ goto end;
+
+ job.hours = parse_times(columns[1], 0, 24);
+ if (!job.hours)
+ goto end;
+
+ job.doms = parse_times(columns[2], 1, 31);
+ if (!job.doms)
+ goto end;
+
+
+ job.months = parse_strings(columns[3], MONTHS, 12);
+
+ if (!job.months)
+ job.months = parse_times(columns[3], 1, 12);
+ if (!job.months)
+ goto end;
+
+ job.dows = parse_strings(columns[4], WEEKDAYS, 7);
+ if (!job.dows)
+ job.dows = parse_times(columns[4], 0, 7);
+ if (!job.dows)
+ goto end;
+
+ job.command = strdup(line+len);
+
+ job_t *jobp = malloc(sizeof(job_t));
+ *jobp = job;
+
+ jobp->next = jobs;
+ jobs = jobp;
+
+ ret = 0;
+
+ end:
+ for (i = 0; i < matches && i < 5; i++)
+ free(columns[i]);
+
+ return ret;
+}
+
+
+static void read_crontab(const char *name) {
+ FILE *file = fopen(name, "r");
+ if (!file) {
+ syslog(LOG_WARNING, "unable to read crontab `%s'", name);
+ return;
+ }
+
+ char line[16384];
+ unsigned lineno = 0;
+
+ while (fgets(line, sizeof(line), file)) {
+ lineno++;
+
+ char *comment = strchr(line, '#');
+ if (comment)
+ *comment = 0;
+
+ if (handle_line(line))
+ syslog(LOG_WARNING, "syntax error in `%s', line %u", name, lineno);
+ }
+
+ fclose(file);
+}
+
+
+static void read_crondir(void) {
+ DIR *dir;
+
+ if (chdir(crondir) || ((dir = opendir(".")) == NULL)) {
+ fprintf(stderr, "Unable to read crondir `%s'\n", crondir);
+ usage();
+ exit(1);
+ }
+
+ struct dirent *ent;
+ while ((ent = readdir(dir)) != NULL) {
+ if (ent->d_name[0] == '.')
+ continue;
+
+ read_crontab(ent->d_name);
+ }
+
+ closedir(dir);
+}
+
+
+static void run_job(const job_t *job) {
+ pid_t pid = fork();
+ if (pid == 0) {
+ execl("/bin/sh", "/bin/sh", "-c", job->command, (char*)NULL);
+ syslog(LOG_ERR, "unable to run job: exec failed");
+ _exit(1);
+ }
+ else if (pid < 0) {
+ syslog(LOG_ERR, "unable to run job: fork failed");
+ }
+}
+
+
+static void check_job(const job_t *job, const struct tm *tm) {
+ if (!(job->minutes & bit(tm->tm_min)))
+ return;
+
+ if (!(job->hours & bit(tm->tm_hour)))
+ return;
+
+ if (!(job->doms & bit(tm->tm_mday-1)))
+ return;
+
+ if (!(job->months & bit(tm->tm_mon)))
+ return;
+
+ if (!(job->dows & bit(tm->tm_wday)))
+ return;
+
+ run_job(job);
+}
+
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ usage();
+
+ exit(argc < 2 ? 0 : 1);
+ }
+
+ crondir = argv[1];
+
+ signal(SIGCHLD, SIG_IGN);
+
+ read_crondir();
+
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+ int minute = tm->tm_min;
+
+ while (1) {
+ sleep(60 - t%60);
+
+ t = time(NULL);
+ tm = localtime(&t);
+
+ minute = (minute+1)%60;
+ if (tm->tm_min != minute) {
+ /* clock has moved, don't execute jobs */
+ minute = tm->tm_min;
+ continue;
+ }
+
+ job_t *job;
+ for (job = jobs; job; job = job->next)
+ check_job(job, tm);
+ }
+}
diff --git a/package/gluon-ebtables-filter-multicast/Makefile b/package/gluon-ebtables-filter-multicast/Makefile
new file mode 100644
index 00000000..93b7f9a5
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/Makefile
@@ -0,0 +1,40 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-ebtables-filter-multicast
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-ebtables-filter-multicast
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Ebtables filters for multicast packets
+ DEPENDS:=+gluon-core +gluon-ebtables
+endef
+
+define Package/gluon-ebtables-filter-multicast/description
+ Gluon community wifi mesh firmware framework: Ebtables filters for multicast packets
+
+ These filters drop non-essential multicast traffic before it enters the mesh.
+
+ Allowed protocols are: DHCP, DHCPv6, ARP, ICMP, ICMPv6, BitTorrent local peer discovery, BABEL and OSPF
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-ebtables-filter-multicast/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-ebtables-filter-multicast))
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain
new file mode 100644
index 00000000..ec0013a3
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/100-mcast-chain
@@ -0,0 +1 @@
+chain('MULTICAST_OUT', 'DROP')
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-arp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-arp
new file mode 100644
index 00000000..8af1900a
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-arp
@@ -0,0 +1,3 @@
+rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-ip-src 0.0.0.0 -j DROP'
+rule 'MULTICAST_OUT -p ARP --arp-opcode Request --arp-ip-dst 0.0.0.0 -j DROP'
+rule 'MULTICAST_OUT -p ARP -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-babel b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-babel
new file mode 100644
index 00000000..d5b81771
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-babel
@@ -0,0 +1 @@
+rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 6696 -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-btlpd b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-btlpd
new file mode 100644
index 00000000..20b709f8
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-btlpd
@@ -0,0 +1 @@
+rule 'MULTICAST_OUT -p IPv4 --ip-destination 239.192.152.143 --ip-protocol udp --ip-destination-port 6771 -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv4 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv4
new file mode 100644
index 00000000..2fca2223
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv4
@@ -0,0 +1 @@
+rule 'MULTICAST_OUT -p IPv4 --ip-protocol udp --ip-destination-port 67 -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv6 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv6
new file mode 100644
index 00000000..6d7f0f55
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-dhcpv6
@@ -0,0 +1 @@
+rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmp
new file mode 100644
index 00000000..25a95f39
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmp
@@ -0,0 +1 @@
+rule 'MULTICAST_OUT -p IPv4 --ip-protocol icmp -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6 b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6
new file mode 100644
index 00000000..a7b67414
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-icmpv6
@@ -0,0 +1,2 @@
+rule 'MULTICAST_OUT -p IPv6 --ip6-protocol 0 -j RETURN' -- hop-by-hop
+rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp
new file mode 100644
index 00000000..2d3814ae
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-igmp
@@ -0,0 +1 @@
+rule 'MULTICAST_OUT -p IPv4 --ip-protocol igmp -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ospf b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ospf
new file mode 100644
index 00000000..da928d4b
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ospf
@@ -0,0 +1,2 @@
+rule 'MULTICAST_OUT -p IPv4 --ip-protocol ospf -j RETURN'
+rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ospf -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ripng b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ripng
new file mode 100644
index 00000000..37d31877
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/110-mcast-allow-ripng
@@ -0,0 +1 @@
+rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination ff02::9 --ip6-destination-port 521 -j RETURN'
diff --git a/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast
new file mode 100644
index 00000000..c52f122f
--- /dev/null
+++ b/package/gluon-ebtables-filter-multicast/files/lib/gluon/ebtables/300-mcast
@@ -0,0 +1,2 @@
+rule 'FORWARD --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT'
+rule 'OUTPUT --logical-out br-client -o bat0 -d Multicast -j MULTICAST_OUT'
diff --git a/package/gluon-ebtables-filter-ra-dhcp/Makefile b/package/gluon-ebtables-filter-ra-dhcp/Makefile
new file mode 100644
index 00000000..ea6a737d
--- /dev/null
+++ b/package/gluon-ebtables-filter-ra-dhcp/Makefile
@@ -0,0 +1,39 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-ebtables-filter-ra-dhcp
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-ebtables-filter-ra-dhcp
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Ebtables filters for Router Advertisement and DHCP packets
+ DEPENDS:=+gluon-core +gluon-ebtables
+endef
+
+define Package/gluon-ebtables-filter-ra-dhcp/description
+ Gluon community wifi mesh firmware framework: Ebtables filters for Router Advertisement and DHCP packets
+
+ These filters ensure that RA and DHCP packets are only forwarded from the mesh into the
+ client network, and not vice-versa.
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-ebtables-filter-ra-dhcp/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-ebtables-filter-ra-dhcp))
diff --git a/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv4 b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv4
new file mode 100644
index 00000000..ec56ff1d
--- /dev/null
+++ b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv4
@@ -0,0 +1,5 @@
+rule 'FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 67 -j OUT_ONLY'
+rule 'OUTPUT -p IPv4 --ip-protocol udp --ip-destination-port 67 -j OUT_ONLY'
+
+rule 'FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 68 -j IN_ONLY'
+rule 'INPUT -p IPv4 --ip-protocol udp --ip-destination-port 68 -j IN_ONLY'
diff --git a/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv6 b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv6
new file mode 100644
index 00000000..470a7648
--- /dev/null
+++ b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-dhcpv6
@@ -0,0 +1,5 @@
+rule 'FORWARD -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j OUT_ONLY'
+rule 'OUTPUT -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j OUT_ONLY'
+
+rule 'FORWARD -p IPv6 --ip6-protocol udp --ip6-destination-port 546 -j IN_ONLY'
+rule 'INPUT -p IPv6 --ip6-protocol udp --ip6-destination-port 546 -j IN_ONLY'
diff --git a/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-radv b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-radv
new file mode 100644
index 00000000..b34d4c76
--- /dev/null
+++ b/package/gluon-ebtables-filter-ra-dhcp/files/lib/gluon/ebtables/200-dir-radv
@@ -0,0 +1,5 @@
+rule 'FORWARD -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -j OUT_ONLY'
+rule 'OUTPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -j OUT_ONLY'
+
+rule 'FORWARD -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j IN_ONLY'
+rule 'INPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j IN_ONLY'
diff --git a/package/gluon-ebtables/Makefile b/package/gluon-ebtables/Makefile
new file mode 100644
index 00000000..39c654c1
--- /dev/null
+++ b/package/gluon-ebtables/Makefile
@@ -0,0 +1,36 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-ebtables
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-ebtables
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Ebtables support
+ DEPENDS:=+gluon-core +ebtables +kmod-ebtables-ipv4 +kmod-ebtables-ipv6 +kmod-ipt-core
+endef
+
+define Package/gluon-ebtables/description
+ Gluon community wifi mesh firmware framework: ebtables support
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-ebtables/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-ebtables))
diff --git a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables
new file mode 100755
index 00000000..5a770452
--- /dev/null
+++ b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables
@@ -0,0 +1,73 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2013 Project Gluon
+#
+# Firewall script for inserting and removing ebtables rules.
+#
+# Example format, for filtering any IPv4 multicast packets to the SSDP UDP port:
+# rule FORWARD --logical-out br-client -d Multicast -p IPv4 --ip-protocol udp --ip-destination-port 5355 -j DROP
+#
+# Removing all rules:
+# $ ./firewall-ebtables stop
+# Inserting all rules:
+# $ ./firewall-ebtables start
+# Inserting a specific rule file:
+# $ ./firewall-ebtables start /lib/gluon/ebtables/100-mcast-chain
+# Removing a specific rule file:
+# $ ./firewall-ebtables stop /lib/gluon/ebtables/100-mcast-chain
+
+
+START=19
+STOP=91
+
+
+exec_file() {
+ local file="$1"
+
+ /usr/bin/lua -e "
+ function rule(command)
+ os.execute($EBTABLES_RULE)
+ end
+ function chain(name, policy)
+ os.execute($EBTABLES_CHAIN)
+ end
+ " "$file"
+}
+
+exec_all() {
+ local sort_arg="$1"
+
+ local old_ifs="$IFS"
+ IFS='
+'
+ for file in `find /lib/gluon/ebtables -type f | sort $sort_arg`; do
+ exec_file "$file"
+ done
+ IFS="$old_ifs"
+}
+
+
+start() {
+ (
+ export EBTABLES_RULE='"ebtables -A " .. command'
+ export EBTABLES_CHAIN='"ebtables -N " .. name .. " -P " .. policy'
+
+ if [ -z "$1" ]; then
+ exec_all ''
+ else
+ exec_file "$1"
+ fi
+ )
+}
+
+stop() {
+ (
+ export EBTABLES_RULE='"ebtables -D " .. command'
+ export EBTABLES_CHAIN='"ebtables -X " .. name'
+
+ if [ -z "$1" ]; then
+ exec_all '-r'
+ else
+ exec_file "$1"
+ fi
+ )
+}
diff --git a/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain b/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain
new file mode 100644
index 00000000..31c19c53
--- /dev/null
+++ b/package/gluon-ebtables/files/lib/gluon/ebtables/100-dir-chain
@@ -0,0 +1,2 @@
+chain('IN_ONLY', 'RETURN')
+chain('OUT_ONLY', 'RETURN')
diff --git a/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules b/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules
new file mode 100644
index 00000000..b1cd4e24
--- /dev/null
+++ b/package/gluon-ebtables/files/lib/gluon/ebtables/101-dir-rules
@@ -0,0 +1,2 @@
+rule 'IN_ONLY --logical-in br-client -i ! bat0 -j DROP'
+rule 'OUT_ONLY --logical-out br-client -o ! bat0 -j DROP'
diff --git a/package/gluon-legacy/Makefile b/package/gluon-legacy/Makefile
new file mode 100644
index 00000000..7320fba6
--- /dev/null
+++ b/package/gluon-legacy/Makefile
@@ -0,0 +1,40 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-legacy
+PKG_VERSION:=2
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(GLUONDIR)/include/package.mk
+
+define Package/gluon-legacy
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Legacy update scripts
+ DEPENDS:=+gluon-core
+endef
+
+define Package/gluon-legacy/description
+ Gluon community wifi mesh firmware framework: legacy update scripts
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-legacy/install
+ $(CP) ./files/* $(1)/
+endef
+
+define Package/gluon-legacy/postinst
+#!/bin/sh
+$(call GluonCheckSite,check_site.lua)
+endef
+
+$(eval $(call BuildPackage,gluon-legacy))
diff --git a/package/gluon-legacy/check_site.lua b/package/gluon-legacy/check_site.lua
new file mode 100644
index 00000000..1ec26de0
--- /dev/null
+++ b/package/gluon-legacy/check_site.lua
@@ -0,0 +1,8 @@
+need_string_array 'legacy.version_files'
+need_string_array 'legacy.old_files'
+
+need_string_array 'legacy.config_mode_configs'
+need_string_array 'legacy.fastd_configs'
+need_string 'legacy.mesh_ifname'
+need_string_array 'legacy.tc_configs'
+need_string_array 'legacy.wifi_names'
diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/000-legacy b/package/gluon-legacy/files/lib/gluon/upgrade/000-legacy
new file mode 100755
index 00000000..78436676
--- /dev/null
+++ b/package/gluon-legacy/files/lib/gluon/upgrade/000-legacy
@@ -0,0 +1,11 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local sysconfig = require 'gluon.sysconfig'
+
+for _, file in ipairs(site.legacy.version_files) do
+ if os.remove(file) then
+ -- Set version being upgraded from to 'legacy'
+ sysconfig.gluon_version = 'legacy'
+ end
+end
diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/019-legacy-interfaces b/package/gluon-legacy/files/lib/gluon/upgrade/019-legacy-interfaces
new file mode 100755
index 00000000..b48e42b8
--- /dev/null
+++ b/package/gluon-legacy/files/lib/gluon/upgrade/019-legacy-interfaces
@@ -0,0 +1,40 @@
+#!/usr/bin/lua
+
+local gluon_util = require 'gluon.util'
+local platform = require 'gluon.platform'
+local site = require 'gluon.site_config'
+local sysconfig = require 'gluon.sysconfig'
+
+local uci = require('luci.model.uci').cursor()
+local util = require 'luci.util'
+
+
+if sysconfig.gluon_version == 'legacy' then
+ local function iface_exists(name)
+ return (gluon_util.exec('ip', 'link', 'show', 'dev', (name:gsub('%..*$', ''))) == 0)
+ end
+
+ local function remove_bat0(iface)
+ return util.trim(string.gsub(' ' .. iface .. ' ', ' bat0 ', ' '))
+ end
+
+
+ local lan_ifname = remove_bat0(uci:get('network', site.legacy.mesh_ifname, 'ifname'))
+ local wan_ifname = uci:get('network', 'wan', 'ifname')
+
+ if wan_ifname and iface_exists(wan_ifname) then
+ sysconfig.wan_ifname = wan_ifname
+ sysconfig.lan_ifname = lan_ifname
+ else
+ sysconfig.wan_ifname = lan_ifname
+ end
+
+
+ uci:delete('network', site.legacy.mesh_ifname)
+ uci:delete('network', 'wan')
+
+ uci:save('network')
+ uci:commit('network')
+end
+
+
diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/210-legacy-wireless b/package/gluon-legacy/files/lib/gluon/upgrade/210-legacy-wireless
new file mode 100755
index 00000000..36da0632
--- /dev/null
+++ b/package/gluon-legacy/files/lib/gluon/upgrade/210-legacy-wireless
@@ -0,0 +1,24 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local sysconfig = require 'gluon.sysconfig'
+
+local uci = require('luci.model.uci').cursor()
+
+
+if sysconfig.gluon_version == 'legacy' then
+ function delete_legacy_iface(iface)
+ for _, wifi in pairs(site.legacy.wifi_names) do
+ if wifi == iface['.name'] then
+ return true
+ end
+ end
+
+ return false
+ end
+
+ uci:delete_all('wireless', 'wifi-iface', delete_legacy_iface)
+
+ uci:save('wireless')
+ uci:commit('wireless')
+end
diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-setup-mode b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-setup-mode
new file mode 100755
index 00000000..0b97e120
--- /dev/null
+++ b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-setup-mode
@@ -0,0 +1,22 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local sysconfig = require 'gluon.sysconfig'
+
+local uci = require('luci.model.uci').cursor()
+
+
+if sysconfig.gluon_version == 'legacy' then
+ for _, config in ipairs(site.legacy.config_mode_configs) do
+ local old = uci:get_first(config, 'wizard', 'configured')
+ if old == '1' then
+ local setup_mode = uci:get_first('gluon-setup-mode', 'setup_mode')
+ uci:set('gluon-setup-mode', setup_mode, 'configured', '1')
+
+ uci:save('gluon-setup-mode')
+ uci:commit('gluon-setup-mode')
+
+ break
+ end
+ end
+end
diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-simple-tc b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-simple-tc
new file mode 100755
index 00000000..c67afe14
--- /dev/null
+++ b/package/gluon-legacy/files/lib/gluon/upgrade/290-legacy-simple-tc
@@ -0,0 +1,29 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local sysconfig = require 'gluon.sysconfig'
+
+local uci = require('luci.model.uci').cursor()
+
+
+if sysconfig.gluon_version == 'legacy' then
+ for _, config in ipairs(site.legacy.tc_configs) do
+ local s = uci:get_first(config, 'bandwidth')
+ if s then
+ old = uci:get_all(config, s)
+ uci:section('gluon-simple-tc', 'interface', 'mesh_vpn',
+ {
+ ifname = 'mesh-vpn',
+ enabled = old.enabled,
+ limit_ingress = old.downstream,
+ limit_egress = old.upstream,
+ }
+ )
+
+ uci:save('gluon-simple-tc')
+ uci:commit('gluon-simple-tc')
+
+ break
+ end
+ end
+end
diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/390-legacy-mesh-vpn-fastd b/package/gluon-legacy/files/lib/gluon/upgrade/390-legacy-mesh-vpn-fastd
new file mode 100755
index 00000000..468a35a6
--- /dev/null
+++ b/package/gluon-legacy/files/lib/gluon/upgrade/390-legacy-mesh-vpn-fastd
@@ -0,0 +1,37 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local sysconfig = require 'gluon.sysconfig'
+
+local uci = require('luci.model.uci').cursor()
+
+
+if sysconfig.gluon_version == 'legacy' then
+ local secret
+ local enabled
+
+
+ for _, config in ipairs(site.legacy.fastd_configs) do
+ if not secret then
+ local s = uci:get_all('fastd', config)
+ if s then
+ secret = s.secret
+ enabled = s.enabled
+ end
+ end
+
+ uci:delete('fastd', config)
+ end
+
+ if secret then
+ uci:section('fastd', 'fastd', 'mesh_vpn',
+ {
+ secret = secret,
+ enabled = enabled,
+ }
+ )
+ end
+
+ uci:save('fastd')
+ uci:commit('fastd')
+end
diff --git a/package/gluon-legacy/files/lib/gluon/upgrade/990-legacy-late b/package/gluon-legacy/files/lib/gluon/upgrade/990-legacy-late
new file mode 100755
index 00000000..efb6b675
--- /dev/null
+++ b/package/gluon-legacy/files/lib/gluon/upgrade/990-legacy-late
@@ -0,0 +1,11 @@
+#!/usr/bin/lua
+
+local site = require 'gluon.site_config'
+local sysconfig = require 'gluon.sysconfig'
+
+
+if sysconfig.gluon_version == 'legacy' then
+ for _, file in ipairs(site.legacy.old_files) do
+ os.remove(file)
+ end
+end
diff --git a/package/gluon-lock-password/Makefile b/package/gluon-lock-password/Makefile
new file mode 100644
index 00000000..d0e99373
--- /dev/null
+++ b/package/gluon-lock-password/Makefile
@@ -0,0 +1,36 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-lock-password
+PKG_VERSION:=1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-lock-password
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Locks the root account by default
+ DEPENDS:=+gluon-core
+endef
+
+define Package/gluon-lock-password/description
+ This packages locks the root account by default.
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-lock-password/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-lock-password))
diff --git a/package/gluon-lock-password/files/lib/gluon/upgrade/100-lock-password b/package/gluon-lock-password/files/lib/gluon/upgrade/100-lock-password
new file mode 100755
index 00000000..3204f638
--- /dev/null
+++ b/package/gluon-lock-password/files/lib/gluon/upgrade/100-lock-password
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+has_root_pwd() {
+ local pwd
+
+ pwd=$([ -f "$1" ] && cat "$1")
+ pwd="${pwd#*root:}"
+ pwd="${pwd%%:*}"
+
+ test -n "${pwd}"
+}
+
+has_root_pwd /etc/shadow || passwd -l root
diff --git a/package/gluon-luci-admin/Makefile b/package/gluon-luci-admin/Makefile
new file mode 100644
index 00000000..3e20d179
--- /dev/null
+++ b/package/gluon-luci-admin/Makefile
@@ -0,0 +1,39 @@
+# Copyright (C) 2013 Nils Schneider
+# This is free software, licensed under the Apache 2.0 license.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-luci-admin
+PKG_VERSION:=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
new file mode 100644
index 00000000..55c0a248
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/index.lua
@@ -0,0 +1,39 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth
+Copyright 2008 Jo-Philipp Wich
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+module("luci.controller.admin.index", package.seeall)
+
+function index()
+ local uci_state = luci.model.uci.cursor_state()
+
+ -- Disable gluon-luci-admin when setup mode is not enabled
+ if uci_state:get_first('gluon-setup-mode', 'setup_mode', 'running', '0') ~= '1' then
+ return
+ end
+
+ local root = node()
+ if not root.lock then
+ root.target = alias("admin")
+ root.index = true
+ end
+
+ local page = entry({"admin"}, alias("admin", "index"), _("Expert Mode"), 10)
+ page.sysauth = "root"
+ page.sysauth_authenticator = function() return "root" end
+ page.index = true
+
+ entry({"admin", "index"}, cbi("admin/info"), _("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
new file mode 100644
index 00000000..29aecb95
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/controller/admin/upgrade.lua
@@ -0,0 +1,135 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2008 Steven Barth
+Copyright 2008 Jo-Philipp Wich
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+]]--
+
+module("luci.controller.admin.upgrade", package.seeall)
+
+function index()
+ local has_platform = nixio.fs.access("/lib/upgrade/platform.sh")
+ if has_platform then
+ entry({"admin", "upgrade"}, call("action_upgrade"), _("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(
+ ". /lib/functions.sh; " ..
+ "include /lib/upgrade; " ..
+ "platform_check_image %q >/dev/null"
+ % tmpfile
+ ) )
+end
+
+function storage_size()
+ local size = 0
+ if nixio.fs.access("/proc/mtd") then
+ for l in io.lines("/proc/mtd") do
+ local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
+ if n == "linux" then
+ size = tonumber(s, 16)
+ break
+ end
+ end
+ elseif nixio.fs.access("/proc/partitions") then
+ for l in io.lines("/proc/partitions") do
+ local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
+ if b and n and not n:match('[0-9]') then
+ size = tonumber(b) * 1024
+ break
+ end
+ end
+ end
+ return size
+end
+
+function image_checksum(tmpfile)
+ return (luci.sys.exec("md5sum %q" % tmpfile):match("^([^%s]+)"))
+end
diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/info.lua b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/info.lua
new file mode 100644
index 00000000..e9ceba70
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/info.lua
@@ -0,0 +1,4 @@
+local t = Template('admin/info')
+t.pageaction = false
+
+return t
diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/remote.lua b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/remote.lua
new file mode 100644
index 00000000..5797b623
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/model/cbi/admin/remote.lua
@@ -0,0 +1,105 @@
+--[[
+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"))
+ 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(luci.dispatcher.context.authuser, 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
new file mode 100644
index 00000000..53947f3c
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/expertmode.htm
@@ -0,0 +1,56 @@
+<% if not self.embedded then %>
+
+<% end %>
diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/info.htm b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/info.htm
new file mode 100644
index 00000000..da403c1b
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/info.htm
@@ -0,0 +1,45 @@
+<%-
+ 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
new file mode 100644
index 00000000..a2b82235
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade.htm
@@ -0,0 +1,55 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth
+Copyright 2008-2009 Jo-Philipp Wich
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+-%>
+
+<%+header%>
+
+
<%:Upgrade firmware%>
+
+
+<%+footer%>
+
diff --git a/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_confirm.htm b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_confirm.htm
new file mode 100644
index 00000000..5618728f
--- /dev/null
+++ b/package/gluon-luci-admin/files/usr/lib/lua/luci/view/admin/upgrade_confirm.htm
@@ -0,0 +1,67 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth
+Copyright 2008-2009 Jo-Philipp Wich
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+-%>
+
+<%+header%>
+
+
<%:Upgrade firmware%>
+
+
+ <%:The firmware image has been transmitted. Please ensure the MD5 checksum and image size are correct and click "continue".%>
+
+
+<% if flashsize > 0 and filesize > flashsize then %>
+
<%:The firmware is too big for your device's storage.%>
+<% end %>
+
+
+
+
md5sum: <%=checksum%>
+
<%:Size%>: <%
+ function byte_format(byte)
+ local suff = {"B", "KB", "MB", "GB", "TB"}
+ for i=1, 5 do
+ if byte > 1024 and i < 5 then
+ byte = byte / 1024
+ else
+ return string.format("%.2f %s", byte, suff[i])
+ end
+ end
+ end
+
+ write(byte_format(filesize))
+
+ if flashsize > 0 then
+ write(luci.i18n.translatef(
+ " (%s available)",
+ w.byte_format(flashsize)
+ ))
+ end
+ %>
+ <%: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-admin/i18n/de.po b/package/gluon-luci-admin/i18n/de.po
new file mode 100644
index 00000000..ec109ae0
--- /dev/null
+++ b/package/gluon-luci-admin/i18n/de.po
@@ -0,0 +1,111 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-05-04 00:34+0200\n"
+"Last-Translator: \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 ""
+"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."
+msgstr ""
+"Alternativ kannst du auch ein Passwort setzen. Wähle bitte ein sicheres "
+"Passwort, das du nirgendwo anders verwendest.
Beim Setzen eines "
+"leeren Passworts wird der Login per Passwort gesperrt (dies ist die Standard-"
+"Einstellung)."
+
+msgid "Continue"
+msgstr "Fortfahren"
+
+msgid "Don't switch off the device in any circumstance!"
+msgstr "Unterbrich auf keinen Fall die Stromversorgung!"
+
+msgid "Expert Mode"
+msgstr "Expert Mode"
+
+msgid "Firmware image"
+msgstr "Firmware-Datei"
+
+msgid "Firmware release"
+msgstr "Firmware-Release"
+
+msgid "Gluon version"
+msgstr "Gluon-Version"
+
+msgid "Hardware model"
+msgstr "Hardware-Modell"
+
+msgid "Information"
+msgstr "Info"
+
+msgid "MAC address"
+msgstr "MAC-Adresse"
+
+msgid "Password changed."
+msgstr "Passwort geändert."
+
+msgid "Password removed."
+msgstr "Passwort gelöscht."
+
+msgid "Public VPN key"
+msgstr "Öffentlicher VPN-Schlüssel"
+
+msgid "Remote access"
+msgstr "Remotezugriff"
+
+msgid "SSH keys"
+msgstr "SSH-Schlüssel"
+
+msgid "Site"
+msgstr "Site"
+
+msgid ""
+"The firmware image has been transmitted. Please ensure the MD5 checksum and "
+"image size are correct and click \"continue\"."
+msgstr ""
+"Die Firmwaredatei wurde übermittelt. Bitte vergleiche MD5-Checksumme und "
+"Dateigröße und klicke anschließend auf \"fortfahren\"."
+
+msgid "The firmware is currently being upgraded."
+msgstr "Die Firmware wird jetzt aktualisiert."
+
+msgid "The firmware is too big for your device's storage."
+msgstr "Die Firmware passt nicht in den Speicher des Gerätes."
+
+msgid "The password and the confirmation differ."
+msgstr "Die beiden Passwörter stimmen nicht überein."
+
+msgid "The provided firmware image is not valid for this device."
+msgstr "Die übermittelte Datei ist keine gültige Firmware für dieses Gerät."
+
+msgid ""
+"The upgrade will take a few minutes. When it is finished, your node will "
+"reboot automatically."
+msgstr ""
+"Dieser Vorgang wird einige Minuten dauern. Anschließend startet das Gerät "
+"automatisch neu."
+
+msgid "Unable to change the password."
+msgstr "Das Passwort konnte nicht geändert werden."
+
+msgid "Upgrade firmware"
+msgstr "Firmware aktualisieren"
+
+msgid "Upgrading firmware"
+msgstr "Firmware wird aktualisiert"
+
+msgid "Upload image"
+msgstr "Datei hochladen"
+
+msgid "You can manually upgrade your firmware here."
+msgstr "Hier kannst du ein manuelles Firmwareupdate durchführen."
+
+msgid "You can provide your SSH keys here (one per line):"
+msgstr ""
+"Hier hast du die Möglichkeit, SSH-Keys zu hinterlegen (einen pro Zeile):"
diff --git a/package/gluon-luci-admin/i18n/gluon-luci-admin.pot b/package/gluon-luci-admin/i18n/gluon-luci-admin.pot
new file mode 100644
index 00000000..197b6367
--- /dev/null
+++ b/package/gluon-luci-admin/i18n/gluon-luci-admin.pot
@@ -0,0 +1,93 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid ""
+"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."
+msgstr ""
+
+msgid "Continue"
+msgstr ""
+
+msgid "Don't switch off the device in any circumstance!"
+msgstr ""
+
+msgid "Expert Mode"
+msgstr ""
+
+msgid "Firmware image"
+msgstr ""
+
+msgid "Firmware release"
+msgstr ""
+
+msgid "Gluon version"
+msgstr ""
+
+msgid "Hardware model"
+msgstr ""
+
+msgid "Information"
+msgstr ""
+
+msgid "MAC address"
+msgstr ""
+
+msgid "Password changed."
+msgstr ""
+
+msgid "Password removed."
+msgstr ""
+
+msgid "Public VPN key"
+msgstr ""
+
+msgid "Remote access"
+msgstr ""
+
+msgid "SSH keys"
+msgstr ""
+
+msgid "Site"
+msgstr ""
+
+msgid ""
+"The firmware image has been transmitted. Please ensure the MD5 checksum and "
+"image size are correct and click \"continue\"."
+msgstr ""
+
+msgid "The firmware is currently being upgraded."
+msgstr ""
+
+msgid "The firmware is too big for your device's storage."
+msgstr ""
+
+msgid "The password and the confirmation differ."
+msgstr ""
+
+msgid "The provided firmware image is not valid for this device."
+msgstr ""
+
+msgid ""
+"The upgrade will take a few minutes. When it is finished, your node will "
+"reboot automatically."
+msgstr ""
+
+msgid "Unable to change the password."
+msgstr ""
+
+msgid "Upgrade firmware"
+msgstr ""
+
+msgid "Upgrading firmware"
+msgstr ""
+
+msgid "Upload image"
+msgstr ""
+
+msgid "You can manually upgrade your firmware here."
+msgstr ""
+
+msgid "You can provide your SSH keys here (one per line):"
+msgstr ""
diff --git a/package/gluon-luci-autoupdater/Makefile b/package/gluon-luci-autoupdater/Makefile
new file mode 100644
index 00000000..650c4692
--- /dev/null
+++ b/package/gluon-luci-autoupdater/Makefile
@@ -0,0 +1,39 @@
+# Copyright (C) 2013 Nils Schneider
+# This is free software, licensed under the Apache 2.0 license.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-luci-autoupdater
+PKG_VERSION:=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
new file mode 100644
index 00000000..64e1acbd
--- /dev/null
+++ b/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/controller/admin/autoupdater.lua
@@ -0,0 +1,19 @@
+--[[
+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
new file mode 100644
index 00000000..a8f9d3b3
--- /dev/null
+++ b/package/gluon-luci-autoupdater/files/usr/lib/lua/luci/model/cbi/admin/autoupdater.lua
@@ -0,0 +1,29 @@
+--[[
+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-autoupdater/i18n/de.po b/package/gluon-luci-autoupdater/i18n/de.po
new file mode 100644
index 00000000..9fd4cf53
--- /dev/null
+++ b/package/gluon-luci-autoupdater/i18n/de.po
@@ -0,0 +1,17 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-05-04 01:55+0200\n"
+"Last-Translator: \n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Automatic updates"
+msgstr "Automatische Updates"
+
+msgid "Branch"
+msgstr "Branch"
diff --git a/package/gluon-luci-autoupdater/i18n/gluon-luci-autoupdater.pot b/package/gluon-luci-autoupdater/i18n/gluon-luci-autoupdater.pot
new file mode 100644
index 00000000..678bdbaf
--- /dev/null
+++ b/package/gluon-luci-autoupdater/i18n/gluon-luci-autoupdater.pot
@@ -0,0 +1,8 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "Automatic updates"
+msgstr ""
+
+msgid "Branch"
+msgstr ""
diff --git a/package/gluon-luci-mesh-vpn-fastd/Makefile b/package/gluon-luci-mesh-vpn-fastd/Makefile
new file mode 100644
index 00000000..1be12ccc
--- /dev/null
+++ b/package/gluon-luci-mesh-vpn-fastd/Makefile
@@ -0,0 +1,41 @@
+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
new file mode 100644
index 00000000..42ff5a50
--- /dev/null
+++ b/package/gluon-luci-mesh-vpn-fastd/check_site.lua
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 00000000..8141c44a
--- /dev/null
+++ b/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/controller/admin/mesh_vpn_fastd.lua
@@ -0,0 +1,5 @@
+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
new file mode 100644
index 00000000..14bb5783
--- /dev/null
+++ b/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/model/cbi/admin/mesh_vpn_fastd.lua
@@ -0,0 +1,41 @@
+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
new file mode 100644
index 00000000..0899fa1d
--- /dev/null
+++ b/package/gluon-luci-mesh-vpn-fastd/files/usr/lib/lua/luci/view/gluon/cbi/mesh-vpn-fastd-mode.htm
@@ -0,0 +1,32 @@
+
+
+ />
+
+
+
+
+ <%= 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-mesh-vpn-fastd/i18n/de.po b/package/gluon-luci-mesh-vpn-fastd/i18n/de.po
new file mode 100644
index 00000000..12e48439
--- /dev/null
+++ b/package/gluon-luci-mesh-vpn-fastd/i18n/de.po
@@ -0,0 +1,38 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-05-03 20:39+0200\n"
+"Last-Translator: \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 ""
+"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."
+msgstr ""
+"Im Modus „Hohe Geschwindigkeit“ wird auf Verschlüsselung "
+"verzichtet. Dies erlaubt häufig eine höhere Bandbreite als mit "
+"Verschlüsselung, aber die Verbindung ist nicht gegen Abhören geschützt."
+
+msgid ""
+"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."
+msgstr ""
+"Im Modus „Hohe Sicherheit“ wird ein verschlüsselter Tunnel "
+"verwendet. Dies schließt aus, dass dein Internetzugangsprovider herausfinden "
+"kann, was für Daten über deinen Knoten übertragen werden."
+
+msgid "Mesh VPN"
+msgstr "Mesh-VPN"
+
+msgid "Performance mode"
+msgstr "Hohe Geschwindigkeit"
+
+msgid "Security mode"
+msgstr "Hohe Sicherheit"
diff --git a/package/gluon-luci-mesh-vpn-fastd/i18n/gluon-luci-mesh-vpn-fastd.pot b/package/gluon-luci-mesh-vpn-fastd/i18n/gluon-luci-mesh-vpn-fastd.pot
new file mode 100644
index 00000000..878a1959
--- /dev/null
+++ b/package/gluon-luci-mesh-vpn-fastd/i18n/gluon-luci-mesh-vpn-fastd.pot
@@ -0,0 +1,23 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid ""
+"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."
+msgstr ""
+
+msgid ""
+"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."
+msgstr ""
+
+msgid "Mesh VPN"
+msgstr ""
+
+msgid "Performance mode"
+msgstr ""
+
+msgid "Security mode"
+msgstr ""
diff --git a/package/gluon-luci-node-role/Makefile b/package/gluon-luci-node-role/Makefile
new file mode 100644
index 00000000..ded7bd3f
--- /dev/null
+++ b/package/gluon-luci-node-role/Makefile
@@ -0,0 +1,41 @@
+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
new file mode 100644
index 00000000..ab01eeb5
--- /dev/null
+++ b/package/gluon-luci-node-role/check_site.lua
@@ -0,0 +1,2 @@
+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
new file mode 100644
index 00000000..cfca1275
--- /dev/null
+++ b/package/gluon-luci-node-role/files/usr/lib/lua/luci/controller/admin/noderole.lua
@@ -0,0 +1,5 @@
+module("luci.controller.admin.noderole", package.seeall)
+
+function index()
+ entry({"admin", "noderole"}, cbi("admin/noderole"), "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
new file mode 100644
index 00000000..e7832ad9
--- /dev/null
+++ b/package/gluon-luci-node-role/files/usr/lib/lua/luci/model/cbi/admin/noderole.lua
@@ -0,0 +1,34 @@
+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/de.po b/package/gluon-luci-node-role/i18n/de.po
new file mode 100644
index 00000000..e3037b26
--- /dev/null
+++ b/package/gluon-luci-node-role/i18n/de.po
@@ -0,0 +1,27 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-04-26 17:18+0200\n"
+"Last-Translator: Tobias Hachmer \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 role"
+msgstr "Verwendungszweck"
+
+msgid "Role"
+msgstr "Rolle"
+
+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 ""
+"Wenn dein Freifunk-Knoten eine besondere Rolle im Freifunk Netz einnimmt, "
+"kannst du diese hier angeben. Bringe bitte zuvor in Erfahrung, welche "
+"Auswirkungen die zur Verfügung stehenden Rollen im Freifunk-Netz haben. "
+"Setze die Rolle nur, wenn du weißt was du machst."
diff --git a/package/gluon-luci-node-role/i18n/gluon-luci-node-role.pot b/package/gluon-luci-node-role/i18n/gluon-luci-node-role.pot
new file mode 100644
index 00000000..acf6dd79
--- /dev/null
+++ b/package/gluon-luci-node-role/i18n/gluon-luci-node-role.pot
@@ -0,0 +1,14 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "Node role"
+msgstr ""
+
+msgid "Role"
+msgstr ""
+
+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 ""
diff --git a/package/gluon-luci-portconfig/Makefile b/package/gluon-luci-portconfig/Makefile
new file mode 100644
index 00000000..007c5fea
--- /dev/null
+++ b/package/gluon-luci-portconfig/Makefile
@@ -0,0 +1,39 @@
+# Copyright (C) 2013 Nils Schneider
+# This is free software, licensed under the Apache 2.0 license.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-luci-portconfig
+PKG_VERSION:=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
new file mode 100644
index 00000000..037b56c8
--- /dev/null
+++ b/package/gluon-luci-portconfig/files/usr/lib/lua/luci/controller/admin/portconfig.lua
@@ -0,0 +1,19 @@
+--[[
+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
new file mode 100644
index 00000000..8166539c
--- /dev/null
+++ b/package/gluon-luci-portconfig/files/usr/lib/lua/luci/model/cbi/admin/portconfig.lua
@@ -0,0 +1,149 @@
+--[[
+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 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)
+ uci:set("network", "wan", "netmask", data.ipv4_netmask)
+ uci:set("network", "wan", "gateway", data.ipv4_gateway)
+ else
+ uci:delete("network", "wan", "ipaddr")
+ uci:delete("network", "wan", "netmask")
+ uci:delete("network", "wan", "gateway")
+ end
+
+ uci:set("network", "wan6", "proto", data.ipv6)
+ if data.ipv6 == "static" then
+ uci:set("network", "wan6", "ip6addr", data.ipv6_addr)
+ uci:set("network", "wan6", "ip6gw", data.ipv6_gateway)
+ else
+ uci:delete("network", "wan6", "ip6addr")
+ uci:delete("network", "wan6", "ip6gw")
+ end
+
+ uci:set("network", "mesh_wan", "auto", data.mesh_wan)
+
+ if sysconfig.lan_ifname then
+ uci:set("network", "mesh_lan", "auto", data.mesh_lan)
+
+ if data.mesh_lan == '1' then
+ uci:set("network", "client", "ifname", "bat0")
+ else
+ uci:set("network", "client", "ifname", sysconfig.lan_ifname .. " bat0")
+ 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-portconfig/i18n/de.po b/package/gluon-luci-portconfig/i18n/de.po
new file mode 100644
index 00000000..2197fdbe
--- /dev/null
+++ b/package/gluon-luci-portconfig/i18n/de.po
@@ -0,0 +1,32 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-05-04 02:08+0200\n"
+"Last-Translator: \n"
+"Language-Team: German\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgid "Automatic (DHCP)"
+msgstr "Automatisch (DHCP)"
+
+msgid "Automatic (RA/DHCPv6)"
+msgstr "Automatisch (RA/DHCPv6)"
+
+msgid "Enable meshing on the WAN interface"
+msgstr "Mesh auf dem WAN-Port aktivieren"
+
+msgid "Enable meshing on the LAN interface"
+msgstr "Mesh auf dem LAN-Port aktivieren"
+
+msgid "Static"
+msgstr "Statisch"
+
+msgid "Static DNS servers"
+msgstr "Statische DNS-Server"
+
+msgid "WAN connection"
+msgstr "WAN-Verbindung"
diff --git a/package/gluon-luci-portconfig/i18n/gluon-luci-portconfig.pot b/package/gluon-luci-portconfig/i18n/gluon-luci-portconfig.pot
new file mode 100644
index 00000000..31ac71d3
--- /dev/null
+++ b/package/gluon-luci-portconfig/i18n/gluon-luci-portconfig.pot
@@ -0,0 +1,23 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "Automatic (DHCP)"
+msgstr ""
+
+msgid "Automatic (RA/DHCPv6)"
+msgstr ""
+
+msgid "Enable meshing on the WAN interface"
+msgstr ""
+
+msgid "Enable meshing on the LAN interface"
+msgstr ""
+
+msgid "Static"
+msgstr ""
+
+msgid "Static DNS servers"
+msgstr ""
+
+msgid "WAN connection"
+msgstr ""
diff --git a/package/gluon-luci-private-wifi/Makefile b/package/gluon-luci-private-wifi/Makefile
new file mode 100644
index 00000000..604929dd
--- /dev/null
+++ b/package/gluon-luci-private-wifi/Makefile
@@ -0,0 +1,36 @@
+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
new file mode 100644
index 00000000..e11ba58b
--- /dev/null
+++ b/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/controller/admin/privatewifi.lua
@@ -0,0 +1,5 @@
+module("luci.controller.admin.privatewifi", package.seeall)
+
+function index()
+ entry({"admin", "privatewifi"}, cbi("admin/privatewifi"), _("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
new file mode 100644
index 00000000..d3ddcd72
--- /dev/null
+++ b/package/gluon-luci-private-wifi/files/usr/lib/lua/luci/model/cbi/admin/privatewifi.lua
@@ -0,0 +1,63 @@
+local f, s, o, ssid
+local uci = luci.model.uci.cursor()
+local config = 'wireless'
+
+-- where to read the configuration from
+local primary_iface = 'wan_radio0'
+local ssid = uci:get(config, primary_iface, "ssid")
+
+f = SimpleForm("wifi", 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 seperate 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-private-wifi/i18n/de.po b/package/gluon-luci-private-wifi/i18n/de.po
new file mode 100644
index 00000000..ccf51341
--- /dev/null
+++ b/package/gluon-luci-private-wifi/i18n/de.po
@@ -0,0 +1,32 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"PO-Revision-Date: 2015-05-04 02:25+0200\n"
+"Last-Translator: \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 "8-63 characters"
+msgstr "8-63 Zeichen"
+
+msgid "Name (SSID)"
+msgstr "Name (SSID)"
+
+msgid "Private WLAN"
+msgstr "Privates WLAN"
+
+msgid ""
+"Your node can additionally extend your private network by bridging the WAN "
+"interface with a seperate 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."
+msgstr ""
+"Dein Knoten kann zusätzlich die Reichweite deines privaten Netzes erweitern. "
+"Hierfür wird der WAN-Port mit einem seperaten WLAN gebridged. Diese "
+"Funktionalität ist völlig unabhängig von den Mesh-Funktionen des Knotens. "
+"Beachte, dass du nicht gleichzeitig das Meshen über den WAN-Port aktiviert "
+"haben solltest."
diff --git a/package/gluon-luci-private-wifi/i18n/gluon-luci-private-wifi.pot b/package/gluon-luci-private-wifi/i18n/gluon-luci-private-wifi.pot
new file mode 100644
index 00000000..4051319f
--- /dev/null
+++ b/package/gluon-luci-private-wifi/i18n/gluon-luci-private-wifi.pot
@@ -0,0 +1,18 @@
+msgid ""
+msgstr "Content-Type: text/plain; charset=UTF-8"
+
+msgid "8-63 characters"
+msgstr ""
+
+msgid "Name (SSID)"
+msgstr ""
+
+msgid "Private WLAN"
+msgstr ""
+
+msgid ""
+"Your node can additionally extend your private network by bridging the WAN "
+"interface with a seperate 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."
+msgstr ""
diff --git a/package/gluon-luci-theme/Makefile b/package/gluon-luci-theme/Makefile
new file mode 100644
index 00000000..4fc947c4
--- /dev/null
+++ b/package/gluon-luci-theme/Makefile
@@ -0,0 +1,39 @@
+# Copyright (C) 2013 Nils Schneider
+# This is free software, licensed under the Apache 2.0 license.
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=gluon-luci-theme
+PKG_VERSION:=0.1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/gluon-luci-theme
+ SECTION:=gluon
+ CATEGORY:=Gluon
+ TITLE:=Luci theme for Gluon
+ DEPENDS:=
+endef
+
+define Package/gluon-luci-theme/description
+ Luci based config mode
+endef
+
+define Build/Prepare
+ mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/gluon-luci-theme/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,gluon-luci-theme))
diff --git a/package/gluon-luci-theme/files/etc/uci-defaults/luci-theme-gluon b/package/gluon-luci-theme/files/etc/uci-defaults/luci-theme-gluon
new file mode 100755
index 00000000..795bd186
--- /dev/null
+++ b/package/gluon-luci-theme/files/etc/uci-defaults/luci-theme-gluon
@@ -0,0 +1,6 @@
+#!/bin/sh
+uci batch <<-EOF
+ set luci.themes.Gluon=/luci-static/gluon
+ commit luci
+EOF
+
diff --git a/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/footer.htm b/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/footer.htm
new file mode 100644
index 00000000..6b709030
--- /dev/null
+++ b/package/gluon-luci-theme/files/usr/lib/lua/luci/view/themes/gluon/footer.htm
@@ -0,0 +1,19 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth
+Copyright 2008 Jo-Philipp Wich
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+$Id$
+
+-%>
+
+
+
+