Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Christian Obersteiner 2016-02-22 19:17:52 +01:00
commit a43d6bfc6e
182 changed files with 17467 additions and 7247 deletions

View File

@ -1,5 +1,5 @@
The code of Project Gluon may be distributed under the following terms, unless
noted otherwise in indiviual files or subtrees.
noted otherwise in individual files or subtrees.
Copyright (c) 2013, Project Gluon
All rights reserved.
@ -25,7 +25,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
OpenWRT is licensed under the terms of the GNU General Public License Version 2,
OpenWrt is licensed under the terms of the GNU General Public License Version 2,
which can be found under openwrt/LICENSE after the openwrt submodule has been
obtained. This applies to the following submodules:
* openwrt

View File

@ -177,6 +177,9 @@ GLUON_$(1)_MODEL_$(2)_ALIASES += $(3)
endef
export SHA512SUM := $(GLUONDIR)/scripts/sha512sum.sh
prereq: FORCE
+$(NO_TRACE_MAKE) prereq
@ -209,7 +212,12 @@ gluon-tools: FORCE
+$(GLUONMAKE_EARLY) package/lua/host/install package/usign/host/install
early_prepared_stamp := $(GLUON_BUILDDIR)/prepared
early_prepared_stamp := $(GLUON_BUILDDIR)/prepared_$(shell \
( \
$(SHA512SUM) $(GLUONDIR)/modules; \
[ ! -r $(GLUON_SITEDIR)/modules ] || $(SHA512SUM) $(GLUON_SITEDIR)/modules \
) | $(SHA512SUM) )
prepare-early: FORCE
for dir in build_dir dl staging_dir; do \
@ -223,11 +231,12 @@ prepare-early: FORCE
touch $(early_prepared_stamp)
$(early_prepared_stamp):
rm -f $(GLUON_BUILDDIR)/prepared_*
+$(GLUONMAKE_EARLY) prepare-early
$(GLUON_OPKG_KEY): $(early_prepared_stamp) FORCE
[ -s $(GLUON_OPKG_KEY) -a -s $(GLUON_OPKG_KEY).pub ] || \
mkdir -p $$(dirname $(GLUON_OPKG_KEY)) && $(STAGING_DIR_HOST)/bin/usign -G -s $(GLUON_OPKG_KEY) -p $(GLUON_OPKG_KEY).pub -c "Gluon opkg key"
( mkdir -p $$(dirname $(GLUON_OPKG_KEY)) && $(STAGING_DIR_HOST)/bin/usign -G -s $(GLUON_OPKG_KEY) -p $(GLUON_OPKG_KEY).pub -c "Gluon opkg key" )
$(GLUON_OPKG_KEY).pub: $(GLUON_OPKG_KEY)
@ -253,8 +262,9 @@ MODULE_PREFIX = gluon-$(GLUON_SITE_CODE)-$(PREPARED_RELEASE)
include $(INCLUDE_DIR)/target.mk
build-key: FORCE
ln -sf $(GLUON_OPKG_KEY) $(BUILD_KEY)
ln -sf $(GLUON_OPKG_KEY).pub $(BUILD_KEY).pub
rm -f $(BUILD_KEY) $(BUILD_KEY).pub
cp $(GLUON_OPKG_KEY) $(BUILD_KEY)
cp $(GLUON_OPKG_KEY).pub $(BUILD_KEY).pub
config: FORCE
+$(NO_TRACE_MAKE) scripts/config/conf OPENWRT_BUILD= QUIET=0
@ -304,9 +314,6 @@ clean: FORCE
rm -f $(gluon_prepared_stamp)
export SHA512SUM := $(GLUONDIR)/scripts/sha512sum.sh
download: FORCE
+$(SUBMAKE) tools/download
+$(SUBMAKE) toolchain/download
@ -331,7 +338,7 @@ prepare-image: FORCE
+$(SUBMAKE) -C $(TOPDIR)/target/linux/$(BOARD)/image image_prepare KDIR="$(BOARD_KDIR)"
prepare: FORCE
@$(STAGING_DIR_HOST)/bin/lua $(GLUONDIR)/package/gluon-core/files/usr/lib/lua/gluon/site_config.lua \
@$(STAGING_DIR_HOST)/bin/lua $(GLUONDIR)/scripts/site_config.lua \
|| (echo 'Your site configuration did not pass validation.'; false)
mkdir -p $(GLUON_IMAGEDIR) $(BOARD_BUILDDIR)

View File

@ -1,5 +1,5 @@
Documentation (incomplete at this time, contribute if you can!) may be found at
http://gluon.readthedocs.org/
http://gluon.readthedocs.org/.
If you're new to Gluon and ready to get your feet wet, have a look at the
[Getting Started Guide](http://gluon.readthedocs.org/en/latest/user/getting_started.html).
@ -8,15 +8,15 @@ If you're new to Gluon and ready to get your feet wet, have a look at the
## Issues & Feature requests
Before opening an issue make sure to read check whether any existing issues
Before opening an issue, make sure to check whether any existing issues
(open or closed) match. If you're suggesting a new feature, drop by on IRC or
our mailinglist to discuss it first.
## Use a release!
Please refrain from using the master branch for anything else but development purposes!
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 v2015.1 && make update`.
and switch to one by running `git checkout v2016.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.

View File

@ -1,6 +1,8 @@
#!/bin/sh
if [ $# -eq 0 -o "-h" = "$1" -o "-help" = "$1" -o "--help" = "$1" ]; then
set -e
if [ $# -ne 2 -o "-h" = "$1" -o "--help" = "$1" -o ! -r "$1" -o ! -r "$2" ]; then
cat <<EOHELP
Usage: $0 <secret> <manifest>
@ -25,16 +27,18 @@ manifest="$2"
upper="$(mktemp)"
lower="$(mktemp)"
awk "BEGIN { sep=0 }
/^---\$/ { sep=1; next }
{ if(sep==0) print > \"$upper\";
else print > \"$lower\"}" \
trap 'rm -f "$upper" "$lower"' EXIT
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"
(
cat "$upper"
echo ---
cat "$lower"
) > "$manifest"

View File

@ -54,9 +54,9 @@ copyright = '2015, Project Gluon'
# built documents.
#
# The short X.Y version.
version = '2015.1+'
version = '2016.1+'
# The full version, including alpha/beta/rc tags.
release = '2015.1+'
release = '2016.1+'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@ -15,8 +15,8 @@ ecdsautils) can by found in the `contrib` directory. When creating the manifest,
be set on the command line, or it can be taken from the ``site.mk``.
The priority defines the maximum number of days that may pass between releasing an update and installation
of the images. The update probability with start at 0 after the release time mentioned in the manifest
and then slowly rise to 1 after the number of days given by the priority has passed.
of the images. The update probability will start at 0 after the release time mentioned in the manifest
and then slowly rise to 1 up to the point when the number of days given by the priority has passed.
The priority may be an integer or a decimal fraction.
@ -59,7 +59,7 @@ The server must be available via IPv6.
Command Line
------------
These commands can be used on a node.
These commands can be used on a node:
::

View File

@ -1,5 +1,5 @@
Announcing Node Information
===========================
Node monitoring
===============
Gluon is capable of announcing information about each node to the mesh
and to neighbouring nodes. This allows nodes to learn each others hostname,
@ -8,7 +8,7 @@ IP addresses, location, software versions and various other information.
Format of collected data
------------------------
Information to be announced is currently split into two categories:
Information to be announced is currently split into three categories:
nodeinfo
In this category (mostly) static information is collected. If
@ -19,15 +19,19 @@ Information to be announced is currently split into two categories:
This category holds fast changing data, like traffic counters, uptime,
system load or the selected gateway.
Both categories will have a ``node_id`` key by default. It should be used to
match data from *statistics* to *nodeinfo*.
neighbours
`neighbours` contains information about all neighbouring nodes of all
interfaces. This data can be used to determine the network topology.
All categories will have a ``node_id`` key. It should be used to
relate data of different catagories.
Accessing Node Information
--------------------------
There are two packages responsible for distribution of the information. For
one, information is distributed across the mesh using alfred_. Information
between neighbouring nodes is exchanged using `gluon-announced`.
between neighbouring nodes is exchanged using `gluon-respondd`.
.. _alfred: http://www.open-mesh.org/projects/alfred
@ -43,8 +47,13 @@ installed. Please note that at least one alfred daemon is required to run as
.. _alfred-json: https://github.com/tcatm/alfred-json
`nodeinfo` is distributed as alfred datatype `158`, while `statistics` uses
`159`. Both are compressed using GZip (alfred-json can handle the decompression).
The following datatypes are used:
* `nodeinfo`: 158
* `statistics`: 159
* `neighbours`: 160
All data is compressed using GZip (alfred-json can handle the decompression).
In order to retrieve statistics data you could run:
@ -90,18 +99,26 @@ You can find more information about alfred in its README_.
.. _README: http://www.open-mesh.org/projects/alfred/repository/revisions/master/entry/README
gluon-announced
~~~~~~~~~~~~~~~
gluon-respondd
~~~~~~~~~~~~~~
`gluon-announced` allows querying neighbouring nodes for their `nodeinfo`.
`gluon-respondd` allows querying neighbouring nodes for their information.
It is a daemon listening on the multicast address ``ff02::2:1001`` on
UDP port 1001 on the bare mesh interfaces.
UDP port 1001 on both the bare mesh interfaces and `br-client`. Unicast
requests are supported as well.
The supported requests are:
* ``nodeinfo``, ``statistics``, ``neighbours``: Returns the data of single category uncompressed.
* ``GET nodeinfo``, ...: Returns the data of one or multiple categories (separated by spaces)
compressed using the `deflate` algorithm (without a gzip header). The data may
be decompressed using zlib and many zlib bindings using -15 as the window size parameter.
gluon-neighbour-info
~~~~~~~~~~~~~~~~~~~~
A programm called `gluon-neighbour-info` has been developed to retrieve
information from neighbours.
The programm `gluon-neighbour-info` can be used to retrieve
information from other nodes.
::
@ -109,55 +126,13 @@ information from neighbours.
-p 1001 -d ff02:0:0:0:0:0:2:1001 \
-r nodeinfo
On optional timeout may be specified, e.g. `-t 5` (default: 3 seconds).
An optional timeout may be specified, e.g. `-t 5` (default: 3 seconds). See
the usage information printed by ``gluon-neighbour-info -h`` for more information
about the supported arguments.
Adding a fact
-------------
Adding a data provider
----------------------
To add a fact just add a file to either ``/lib/gluon/announce/nodeinfo.d/`` or
``/lib/gluon/announce/statistics.d/``.
The file must contain a lua script and its name will become the key for the
resulting JSON object. A simple script adding a ``hostname`` field might look
like this:
::
return uci:get_first('system', 'system', 'hostname')
The directory structure will be converted to a JSON object, i.e. you may
create subdirectories. So, if the directories look like this
::
.
├── hardware
│   └── model
├── hostname
├── network
│   └── mac
├── node_id
└── software
└── firmware
the resulting JSON would become:
::
# /lib/gluon/announce/announce.lua nodeinfo
{
"hardware" : {
"model" : "TP-Link TL-MR3420 v1"
},
"hostname" : "mr3420-test",
"network" : {
"mac" : "90:f6:52:82:06:02"
},
"node_id" : "90f652820602",
"software" : {
"firmware" : {
"base" : "gluon-v2014.2-32-ge831099",
"release" : "0.4.1+0-exp20140720"
}
}
}
To add a provider, you need to install a shared object into ``/lib/gluon/respondd``.
For more information, refer to the `respondd README <https://github.com/freifunk-gluon/packages/blob/master/net/respondd/README.md>`_
and have a look the existing providers.

View File

@ -60,6 +60,6 @@ It may be disabled by running::
done
uci commit
Please note that this configuration has changed in Gluon v2015.2. Using
the old commands on v2015.2 will break the corresponding Export Mode
Please note that this configuration has changed in Gluon v2016.1. Using
the old commands on v2016.1 will break the corresponding Expert Mode
settings.

View File

@ -0,0 +1,26 @@
WLAN configuration
==================
Gluon allows to configure 2.4GHz and 5GHz radios independently. The configuration
may include any or all of the three networks "client" (AP mode), "mesh" (802.11s
mode) and "ibss" (adhoc mode), which can be used simultaneously (using "mesh" and
"ibss" at same time should be avoided though as weaker hardware usually can't handle the additional
load). See :doc:`../user/site` for details on the configuration.
Upgrade behaviour
-----------------
For each of these networks, the site configuration may define a `disabled` flag (by
default, all configured networks are enabled). This flag is merely a default setting,
on upgrades the existing setting is always retained (as this setting may have been changed
by the user). This means that is is not possible to enable or disable an existing network
configurations during upgrades.
For the "mesh" and "ibss" networks, the default setting only has an effect if none
of the two has existed before. If a new configuration has been added for "mesh" or "ibss",
while the other of the two has already existed before, the enabled/disabled state of the
existing configuration will also be set for the new configuration.
This allows upgrades to change from IBSS to 11s and vice-versa while retaining the
"wireless meshing is enabled/disabled" property configured by the user regardless
of the used mode.

View File

@ -24,9 +24,10 @@ Features
features/configmode
features/autoupdater
features/wlan-configuration
features/private-wlan
features/wired-mesh
features/announce
features/monitoring
features/authorized-keys
features/roles
@ -50,6 +51,8 @@ Packages
:maxdepth: 1
package/gluon-client-bridge
package/gluon-ebtables-filter-multicast
package/gluon-ebtables-filter-ra-dhcp
Releases
--------
@ -57,7 +60,7 @@ Releases
.. toctree::
:maxdepth: 1
releases/v2015.2
releases/v2016.1
releases/v2015.1.2
releases/v2015.1.1
releases/v2015.1
@ -79,12 +82,14 @@ ar71xx-generic
* Buffalo
- WZR-HP-AG300H / WZR-600DHP
- WZR-HP-G300NH
- WZR-HP-G450H
* D-Link
- DIR-825 (B1)
- DIR-505 (A1)
- DIR-615 (C1)
- DIR-825 (B1)
* GL-Inet
@ -103,10 +108,10 @@ ar71xx-generic
* TP-Link
- CPE210 (v1)
- CPE220 (v1)
- CPE510 (v1)
- CPE520 (v1)
- CPE210 (v1.0, v1.1)
- CPE220 (v1.0, v1.1)
- CPE510 (v1.0, v1.1)
- CPE520 (v1.0, v1.1)
- TL-MR3020 (v1)
- TL-MR3040 (v1, v2)
- TL-MR3220 (v1, v2)
@ -117,32 +122,43 @@ ar71xx-generic
- TL-WA830RE (v1, v2)
- TL-WA850RE (v1)
- TL-WA860RE (v1)
- TL-WA901N/ND (v2, v3)
- TL-WA901N/ND (v1, v2, v3)
- TL-WDR3500 (v1)
- TL-WDR3600 (v1)
- TL-WDR4300 (v1)
- TL-WR1043N/ND (v1, v2)
- TL-WR703N (v1)
- TL-WR710N (v1)
- TL-WR710N (v1, v2)
- TL-WR740N (v1, v3, v4, v5)
- TL-WR741N/ND (v1, v2, v4, v5)
- TL-WR743N/ND (v1, v2)
- TL-WR801N/ND (v1, v2)
- TL-WR841N/ND (v3, v5, v7, v8, v9, v10)
- TL-WR842N/ND (v1, v2)
- TL-WR941N/ND (v2, v3, v4, v5)
- TL-WR843N/ND (v1)
- TL-WR940N (v1, v2, v3)
- TL-WR941ND (v2, v3, v4, v5, v6)
- TL-WR1043N/ND (v1, v2, v3)
- TL-WR2543N/ND (v1)
* Ubiquiti
- Bullet M2
- Nanostation M2
- Air Gateway
- Air Router
- Bullet M
- Nanostation M
- Nanostation M XW
- Loco M XW
- Picostation M2
- Rocket M2
- Picostation M
- Rocket M
- UniFi AP
- UniFi AP Pro
- UniFi AP Outdoor
- UniFi AP Outdoor+
* Western Digital
- My Net N600
- My Net N750
ar71xx-nand
^^^^^^^^^^^
@ -173,6 +189,20 @@ x86-kvm_guest
See also: :doc:`user/x86`
x86-xen_domu
^^^^^^^^^^^^
* x86-xen
See also: :doc:`user/x86`
x86-64
^^^^^^
* x86-64-generic
* x86-64-virtualbox
* x86-64-vmware
See also: :doc:`user/x86`
License
-------

View File

@ -0,0 +1,30 @@
gluon-ebtables-filter-multicast
===============================
The *gluon-ebtables-filter-multicast* package filters out various kinds of
non-essential multicast traffic, as this traffic often constitutes a
disproportionate burden on the mesh network. Unfortunately, this breaks many useful services
(Avahi, Bonjour chat, ...), but this seems unavoidable, as the current Avahi implementation is
optimized for small local networks and causes too much traffic in large mesh networks.
The multicast packets are filtered between the nodes' client bridge (*br-client*) and mesh
interface (*bat0*) on output.
The following packet types are considered essential and aren't filtered:
* ARP (except requests for/replies from 0.0.0.0)
* DHCP, DHCPv6
* ICMPv6 (except Echo Requests (ping) and Node Information Queries (RFC4620)
* IGMP
In addition, the following packet types are allowed to allow experimentation with
layer 3 routing protocols.
* Babel
* OSPF
* RIPng
The following packet types are also allowed:
* BitTorrent Local Peer Discovery (it seems better to have local peers for BitTorrent than sending everything through the internet)

View File

@ -0,0 +1,13 @@
gluon-ebtables-filter-ra-dhcp
=============================
The *gluon-ebtables-filter-ra-dhcp* package tries to prevent common
misconfigurations (i.e. connecting the client interface of a Gluon
node to a private network) from causing issues for either of the
networks.
The rules are the following:
* DHCP requests, DHCPv6 requests and Router Solicitations may only be sent from clients to the mesh, but aren't forwarded
from the mesh to clients
* DHCP replies, DHCPv6 replies and Router Advertisements from clients aren't forwarded to the mesh

View File

@ -1,145 +0,0 @@
Gluon 2015.2 (in development)
=============================
Added (and removed) hardware support
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
New features
~~~~~~~~~~~~
Bugfixes
~~~~~~~~
Site changes
~~~~~~~~~~~~
* ``site.conf``
- New WLAN configuration
``wifi24`` and ``wifi5`` need to be updated to a new more flexible format.
A configuration using the old format
::
{
channel = 1,
htmode = 'HT20'
ssid = 'entenhausen.freifunk.net',
mesh_ssid = 'xe:xx:xx:xx:xx:xx',
mesh_bssid = 'xe:xx:xx:xx:xx:xx',
mesh_mcast_rate = 12000,
}
would look like this in the new format::
{
channel = 1,
ap = {
ssid = 'entenhausen.freifunk.net',
},
ibss = {
ssid = 'xe:xx:xx:xx:xx:xx',
bssid = 'xe:xx:xx:xx:xx:xx',
mcast_rate = 12000,
},
}
The ``htmode`` option has been dropped, the channel width is now always set to 20MHz
(see https://github.com/freifunk-gluon/gluon/issues/487 for a discussion of this change).
In addition to the old IBSS (Adhoc) based meshing, 802.11s-based meshing can be configured
using the ``mesh`` section. Example::
{
channel = 1,
ap = {
ssid = 'entenhausen.freifunk.net',
},
mesh = {
id = 'mesh.entenhausen.freifunk.net', -- can by any string, human-readable or random
mcast_rate = 12000,
},
}
While using ``ibss`` and ``mesh`` at the same time is possible, is causes high load in
very active meshes, so it is advisable to avoid such configurations.
- Bandwidth limitation defaults
The old section ``simple_tc.mesh_vpn`` has been moved to ``fastd_mesh_vpn.bandwidth_limit`` and the ``ifname``
field isn't used anymore. What looked like this
before
::
simple_tc = {
mesh_vpn = {
ifname = 'mesh-vpn',
enabled = false,
limit_ingress = 3000,
limit_egress = 200,
}
}
needs to be changed to
::
fastd_mesh_vpn = {
-- ...
bandwidth_limit = {
enabled = false,
ingress = 3000,
egress = 200,
},
}
- opkg repository configuration
The opkg configuration has been changed to be more flexible and allow specifying custom repositories.
Example::
opkg = {
openwrt = 'http://opkg.services.ffeh/openwrt/%n/%v/%S/packages',
extra = {
modules = 'http://opkg.services.ffeh/modules/gluon-%GS-%GR/%S',
},
}
The keys of the ``extra`` table (like ``modules`` in this example) can be chosen arbitrarily.
Instead of explicitly specifying the whole URL, using patterns is recommended. The following
patterns are understood:
- ``%n`` is replaced by the OpenWrt version codename (e.g. "chaos_calmer")
- ``%v`` is replaced by the OpenWrt version number (e.g. "15.05")
- ``%S`` is replaced by the target architecture (e.g. "ar71xx/generic")
- ``%GS`` is replaced by the Gluon site code (as specified in ``site.conf``)
- ``%GV`` is replaced by the Gluon version
- ``%GR`` is replaced by the Gluon release (as specified in ``site.mk``)
* ``i18n/``
- The translations of ``gluon-config-mode:pubkey`` now have to show the fastd
public key itself if desired, making the formatting of the key and whether it is shown at
all configurable. To retain the old format, add ``<p>`` to the beginning of
your translations and append::
"</p>"
"<div class=\"the-key\">"
" # <%= hostname %>"
" <br/>"
"<%= pubkey %>"
"</div>"
Internals
~~~~~~~~~
Known Issues
~~~~~~~~~~~~

276
docs/releases/v2016.1.rst Normal file
View File

@ -0,0 +1,276 @@
Gluon 2016.1
============
Added hardware support
~~~~~~~~~~~~~~~~~~~~~~
ar71xx-generic
^^^^^^^^^^^^^^
* Buffalo
- WZR-HP-G300NH
* D-Link
- DIR-505 (A1)
* TP-Link
- CPE210/220/510/520 v1.1
- TL-WA901N/ND v1
- TL-WR710N v2
- TL-WR801N/ND v1, v2
- TL-WR841N/ND v10
- TL-WR843N/ND v1
- TL-WR940N v1, v2, v3
- TL-WR941ND v6
- TL-WR1043N/ND v3
* Ubiquiti
- airGateway
- airRouter
- UniFi AP Outdoor+
* Western Digital
- My Net N600
- My Net N750
x86-xen_domu
^^^^^^^^^^^^
New target containing the necessary drivers for use in Xen.
x86-64
^^^^^^
64bit version of `x86-generic`. The generic image can also be used in KVM with VirtIO.
New features
~~~~~~~~~~~~
Kernel module opkg repository
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We've not been able to keep ABI compatiblity with the kernel of the official OpenWrt images.
Therefore, Gluon now generates an opkg repository with modules itself.
The repository can be found at `output/modules/` by default, the image output directory has
been moved from `images/` to `output/images/`. See the updated :doc:`../user/getting_started` guide
for information on the handling of the signing keys for this repository.
The `opkg_repo` site.conf option has been replaced to allow specifying this and other additional repositories.
New status page
^^^^^^^^^^^^^^^
The new status page provides a visually pleasing experience, and displays all important information
on a node in a clear manner. It also contains a real-time signal strength graph for all neighbouring
nodes to aid with the alignment of antennas.
802.11s mesh support
^^^^^^^^^^^^^^^^^^^^
Gluon now supports using 802.11s for its mesh links instead of IBSS (Adhoc). This will allow supporting
more WLAN hardware in the future (like Ralink/Mediatek, which don't support AP and IBSS mode simultaneously).
Note that batman-adv is still used on top of 802.11s (and 802.11s forwarding is disabled), the mesh routing protocol
provided by 802.11s is not used.
Multicast filter extension
^^^^^^^^^^^^^^^^^^^^^^^^^^
The `gluon-ebtables-filter-multicast` package has been extended to filter out multicast
ICMP and ICMPv6 Echo Requests (ping) and Node Information Queries (RFC4620). This prevents
pings to multicast addresses like ff02::1 to cause traffic peaks
(as all nodes and clients would answer such a ping).
French translation
^^^^^^^^^^^^^^^^^^
A French translation for the Config Mode/Expert Mode has been added.
Bugfixes
~~~~~~~~
* Update kernel code for the QCA953x
Might improve stability of the TP-Link TL-WR841N/ND v9.
* Fix model detection on some Netgear WNDR3700v2
The broken devices will identify as "NETGEAR ".
This also breaks the autoupdater, making a manual upgrade necessary.
* Ensure that `odhcp6c` doesn't spawn multiple instances of ``dhcpv6.script``
* Fix support for Buffalo WZR-600DHP
A flashable factory image is generated now. The sysupgrade image is still shared
with the WZR-HP-AG300H.
Site changes
~~~~~~~~~~~~
* ``site.conf``
- New WLAN configuration
``wifi24`` and ``wifi5`` need to be updated to a new more flexible format.
A configuration using the old format
::
{
channel = 1,
htmode = 'HT20'
ssid = 'entenhausen.freifunk.net',
mesh_ssid = 'xe:xx:xx:xx:xx:xx',
mesh_bssid = 'xe:xx:xx:xx:xx:xx',
mesh_mcast_rate = 12000,
}
would look like this in the new format::
{
channel = 1,
ap = {
ssid = 'entenhausen.freifunk.net',
},
ibss = {
ssid = 'xe:xx:xx:xx:xx:xx',
bssid = 'xe:xx:xx:xx:xx:xx',
mcast_rate = 12000,
},
}
The ``htmode`` option has been dropped, the channel width is now always set to 20MHz
(see https://github.com/freifunk-gluon/gluon/issues/487 for a discussion of this change).
In addition to the old IBSS (Adhoc) based meshing, 802.11s-based meshing can be configured
using the ``mesh`` section. Example::
{
channel = 1,
ap = {
ssid = 'entenhausen.freifunk.net',
},
mesh = {
id = 'mesh.entenhausen.freifunk.net', -- can by any string, human-readable or random
mcast_rate = 12000,
},
}
While using ``ibss`` and ``mesh`` at the same time is possible, is causes high load in
very active meshes, so it is advisable to avoid such configurations.
- Bandwidth limitation defaults
The old section ``simple_tc.mesh_vpn`` has been moved to ``fastd_mesh_vpn.bandwidth_limit`` and the ``ifname``
field isn't used anymore. What looked like this
before
::
simple_tc = {
mesh_vpn = {
ifname = 'mesh-vpn',
enabled = false,
limit_ingress = 3000,
limit_egress = 200,
}
}
needs to be changed to
::
fastd_mesh_vpn = {
-- ...
bandwidth_limit = {
enabled = false,
ingress = 3000,
egress = 200,
},
}
- opkg repository configuration
The opkg configuration has been changed to be more flexible and allow specifying custom repositories.
Example::
opkg = {
openwrt = 'http://opkg.services.ffeh/openwrt/%n/%v/%S/packages',
extra = {
modules = 'http://opkg.services.ffeh/modules/gluon-%GS-%GR/%S',
},
}
The keys of the ``extra`` table (like ``modules`` in this example) can be chosen arbitrarily.
Instead of explicitly specifying the whole URL, using patterns is recommended. The following
patterns are understood:
- ``%n`` is replaced by the OpenWrt version codename (e.g. "chaos_calmer")
- ``%v`` is replaced by the OpenWrt version number (e.g. "15.05")
- ``%S`` is replaced by the target architecture (e.g. "ar71xx/generic")
- ``%GS`` is replaced by the Gluon site code (as specified in ``site.conf``)
- ``%GV`` is replaced by the Gluon version
- ``%GR`` is replaced by the Gluon release (as specified in ``site.mk``)
* ``site.mk``
- The packages `gluon-announce` and `gluon-announced` were merged into
the package `gluon-respondd`. If you had any of them (probably
`gluon-announced`) in your package list, you have to replace them.
* ``i18n/``
- The translations of ``gluon-config-mode:pubkey`` now have to show the fastd
public key themselves if desired, making the formatting of the key and whether it is shown at
all configurable. To retain the old format, add ``<p>`` to the beginning of
your translations and append::
"</p>"
"<div class=\"the-key\">"
" # <%= hostname %>"
" <br/>"
"<%= pubkey %>"
"</div>"
Internals
~~~~~~~~~
* OpenWrt has been updated to Chaos Calmer
* mac80211 has been backported from OpenWrt trunk r47249 (wireless-testing 2015-07-21)
This allows us to support the TL-WR940N v3/TL-WR941ND v6, which uses a TP9343 (QCA956x) SoC.
* Several packages have been moved from the Gluon repo to the packages repo, removing references to Gluon:
- gluon-cron -> micrond (the crontabs are now read from ``/usr/lib/micron.d`` instead of ``/lib/gluon/cron``)
- gluon-radvd -> uradvd
- gluon-simple-tc -> simple-tc (the config file has been renamed as well)
* Some of the Gluon-specific i18n support code in the build system has been removed, as LuCI now provides
similar facilities
* The C-based `luci-lib-jsonc` library is now used for JSON encoding/decoding instead of the pure Lua `luci-lib-json`
* The site config is now stored as JSON on the node. The Lua interface ``gluon.site_config`` is still available, and a C interface was added as part of the new package `libgluonutil`.
* The `respondd` daemon now uses C modules instead of Lua snippets, which greatly enhances response speed and reduces memory usage. The Gluon integration package has
been renamed from `gluon-announced` to `gluon-respondd`.
Known Issues
~~~~~~~~~~~~
* Default TX power on many Ubiquiti devices is too high, correct offsets are unknown (`#94 <https://github.com/freifunk-gluon/gluon/issues/94>`_)
Reducing the TX power in the Expert Mode is recommended.
* batman-adv causes stability issues for both alfred and respondd/announced (`#177 <https://github.com/freifunk-gluon/gluon/issues/177>`_)
* The MAC address of the WAN interface is modified even when Mesh-on-WAN is disabled (`#496 <https://github.com/freifunk-gluon/gluon/issues/496>`_)
This may lead to issues in environments where a fixed MAC address is expected (like VMware when promicious mode is disallowed).
* Inconsistent respondd/announced API (`#522 <https://github.com/freifunk-gluon/gluon/issues/522>`_)
The current API is inconsistent and will be replaced in the next release. The old API will still be supported for a while.

View File

@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-03-19 20:28+0100\n"
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
"PO-Revision-Date: 2016-02-04 14:28+0100\n"
"Last-Translator: David Lutz <kpanic@hirnduenger.de>\n"
"Language-Team: English\n"
"Language: en\n"
"MIME-Version: 1.0\n"
@ -12,15 +12,15 @@ msgstr ""
msgid "gluon-config-mode:welcome"
msgstr ""
"Welcome the the setup wizard of your new Freifunk Duckburg node. "
"Please fill out the following form and transmit it."
"Welcome to the setup wizard of your new Freifunk Duckburg node. "
"Please fill out the following form and submit it."
msgid "gluon-config-mode:pubkey"
msgstr ""
"<p>This is your Freifunk node's public key. The node won't be able to "
"connect to the mesh VPN until the key has been registered on the Freifunk "
"Duckburg servers. "
"To register the key send it together with your node's name (<em><%=hostname%></em>) to "
"To register, send the key together with your node's name (<em><%=hostname%></em>) to "
"<a href=\"mailto:keys@entenhausen.freifunk.net\">keys@entenhausen.freifunk.net</a>."
"</p>"
"<div class=\"the-key\">"
@ -33,10 +33,10 @@ msgstr ""
msgid "gluon-config-mode:reboot"
msgstr ""
"<p>The node is currently rebooting and will try to connect to other "
"nearby Freifunk nodes after that. "
"Your can find lots of information on the Freifunk Duckburg community on "
"nearby Freifunk nodes after that. "
"For more information on the Freifunk Duckburg community, have a look at "
"<a href=\"https://entenhausen.freifunk.net/\">our homepage</a>.</p>"
"<p>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.</p>"
"<p>Have fun with your node and exploring the Freifunk network!</p>"
"<p>Have fun with your node and exploring of the Freifunk network!</p>"

View File

@ -1,4 +1,4 @@
-- This is an example site configuration for Gluon v2015.1+
-- This is an example site configuration for Gluon v2016.1+
--
-- Take a look at the documentation located at
-- http://gluon.readthedocs.org/ for details.

View File

@ -7,7 +7,7 @@
GLUON_SITE_PACKAGES := \
gluon-mesh-batman-adv-15 \
gluon-alfred \
gluon-announced \
gluon-respondd \
gluon-autoupdater \
gluon-config-mode-autoupdater \
gluon-config-mode-contact-info \

View File

@ -8,7 +8,7 @@ Gluon's releases are managed using `Git tags`_. If you are just getting
started with Gluon we recommend to use the latest stable release of Gluon.
Take a look at the `list of gluon releases`_ and notice the latest release,
e.g. *v2014.3*. Always get Gluon using git and don't try to download it
e.g. *v2016.1*. Always get Gluon using git and don't try to download it
as a Zip archive as the archive will be missing version information.
Please keep in mind that there is no "default Gluon" build; a site configuration
@ -42,7 +42,7 @@ Building the images
-------------------
To build Gluon, first check out the repository. Replace *RELEASE* with the
version you'd like to checkout, e.g. *v2015.1*.
version you'd like to checkout, e.g. *v2016.1*.
::
@ -90,12 +90,21 @@ In case of errors read the messages carefully and try to fix the stated issues (
``ar71xx-generic`` is the most common target and will generate images for most of the supported hardware.
To see a complete list of supported targets, call ``make`` without setting ``GLUON_TARGET``.
The built images can be found in the directory `output/images`. Of these, the factory
You should reserve about 10GB of disk space for each `GLUON_TARGET`.
The built images can be found in the directory `output/images`. Of these, the `factory`
images are to be used when flashing from the original firmware a device came with,
and sysupgrade is to upgrade from other versions of Gluon or any other OpenWRT-based
and `sysupgrade` is to upgrade from other versions of Gluon or any other OpenWrt-based
system.
You should reserve about 10GB of disk space for each `GLUON_TARGET`.
**Note:** The images for some models are identical; to save disk space, symlinks are generated instead
of multiple copies of the same image. If your webserver's configuration prohibits following
symlinks, you can use the following command to resolve these links while copying the images::
cp -rL output/images /var/www
Cleaning the build tree
.......................
There are two levels of `make clean`::

View File

@ -95,14 +95,16 @@ wifi24 : optional
This will only affect new installations.
Upgrades will not changed the disabled state.
``ap`` requires a single parameter, a string, named ``ssid`` which sets the interface's ESSID.
``ap`` requires a single parameter, a string, named ``ssid`` which sets the
interface's ESSID.
``mesh`` requires a single parameter, a string, named ``id`` which sets the mesh id.
``ibss`` requires two parametersr: ``ssid`` (a string) and ``bssid`` (a MAC).
An optional parameter ``vlan`` (integer) is supported.
Both ``mesh`` and ``ibss`` accept an optional ``mcast_rate`` (kbit/s) parameter for setting the default multicast datarate.
Both ``mesh`` and ``ibss`` accept an optional ``mcast_rate`` (kbit/s) parameter for
setting the default multicast datarate.
::
wifi24 = {
@ -156,10 +158,10 @@ fastd_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
If `configurable` is set to `false` or unset, the method list will be replaced on updates
with the list from the site configuration. Setting `configurable` to `true` will allow the user to
add the method ``null`` to the beginning of the method list or remove ``null`` from it,
and make this change survive updates. Setting `configurable` is necessary for the
package `gluon-luci-mesh-vpn-fastd`, which adds a UI for this configuration.
In any case, the ``null`` method should always be the first method in the list
@ -169,19 +171,41 @@ fastd_mesh_vpn
fastd_mesh_vpn = {
methods = {'salsa2012+umac'},
-- enabled = true,
-- configurable = true,
-- enabled = true,
-- configurable = true,
mtu = 1280,
groups = {
backbone = {
-- Limit number of connected peers from this group
limit = 1,
peers = {
peer1 = {
key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
remotes = {'ipv4 "vpn1.entenhausen.freifunk.net" port 10000'},
-- Having multiple domains prevents SPOF in freifunk.net
remotes = {
'ipv4 "vpn1.entenhausen.freifunk.net" port 10000',
'ipv4 "vpn1.entenhausener-freifunk.de" port 10000',
},
},
}
}
peer2 = {
key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
-- You can also omit the ipv4 to allow both connection via ipv4 and ipv6
remotes = {'"vpn2.entenhausen.freifunk.net" port 10000'},
},
},
-- Optional: nested peer groups
-- groups = {
-- lowend_backbone = {
-- limit = 1,
-- peers = ...
-- },
-- },
},
-- Optional: additional peer groups, possibly with other limits
-- peertopeer = {
-- limit = 10,
-- peers = { ... },
-- },
},
bandwidth_limit = {
@ -207,14 +231,15 @@ autoupdater : package
::
autoupdater = {
branch = 'experimental',
branch = 'stable',
branches = {
stable = {
name = 'stable',
mirrors = {
'http://[fdca:ffee:babe:1::fec1]/firmware/stable/sysupgrade/',
'http://[fdca:ffee:babe:1::fec2]/firmware/stable/sysupgrade/',
'http://autoupdate.entenhausen.freifunk.net/firmware/stable/sysupgrade/',
},
-- Number of good signatures required
good_signatures = 2,
pubkeys = {
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', -- someguy
@ -225,9 +250,9 @@ autoupdater : package
}
roles : optional
Optional role definitions. With this nodes will announce their role inside the mesh.
In the backend this adds the facility to distinguish between normal, backbone and
service nodes or even gateways (if they advertise the role, also). It is up to
Optional role definitions. Nodes will announce their role inside the mesh.
This will allow in the backend to distinguish between normal, backbone and
service nodes or even gateways (if they advertise that role). It is up to
the community which roles to define. See the section below as an example.
``default`` takes the default role which is set initially. This value should be
part of ``list``. If you want node owners to change the role via config mode add
@ -279,7 +304,7 @@ The ``site.mk`` is a Makefile which should define constants
involved in the build process of Gluon.
GLUON_SITE_PACKAGES
Defines a list of packages which should installed in addition
Defines a list of packages which should be installed additionally
to the ``gluon-core`` package.
GLUON_RELEASE
@ -290,7 +315,7 @@ GLUON_PRIORITY
for more information).
GLUON_LANGS
List of languages (as two-letter-codes) to include for the web interface. Should always contain
List of languages (as two-letter-codes) to be included in the web interface. Should always contain
``en``.
.. _site-config-mode-texts:
@ -362,6 +387,7 @@ site-repos in the wild
This is a non-exhaustive list of site-repos from various communities:
* `site-ffa <https://github.com/tecff/site-ffa>`_ (Altdorf, Landshut & Umgebung)
* `site-ffbs <https://github.com/ffbs/site-ffbs>`_ (Braunschweig)
* `site-ffhb <https://github.com/FreifunkBremen/gluon-site-ffhb>`_ (Bremen)
* `site-ffda <https://github.com/freifunk-darmstadt/site-ffda>`_ (Darmstadt)
@ -374,7 +400,7 @@ This is a non-exhaustive list of site-repos from various communities:
* `site-ffmyk <https://github.com/FreifunkMYK/site-ffmyk>`_ (Mayen-Koblenz)
* `site-ffm <https://github.com/freifunkMUC/site-ffm>`_ (München)
* `site-ffms <https://github.com/FreiFunkMuenster/site-ffms>`_ (Münsterland)
* `site-ffnw <https://git.freifunk-ol.de/root/siteconf.git>`_ (Nordwest)
* `site-ffnw <https://git.nordwest.freifunk.net/ffnw/siteconf/tree/master>`_ (Nordwest)
* `site-ffpb <https://git.c3pb.de/freifunk-pb/site-ffpb>`_ (Paderborn)
* `site-ffka <https://github.com/ffka/site-ffka>`_ (Karlsruhe)
* `site-ffrl <https://github.com/ffrl/sites-ffrl>`_ (Rheinland)

View File

@ -2,12 +2,12 @@ x86 support
===========
Gluon can run on normal x86 systems, for example virtual machines
and VPN boxes. There is no WLAN support on x86 though.
and VPN boxes. By default, there is no WLAN support on x86 though.
Targets
^^^^^^^
There are two targets for x86 images:
The following targets for x86 images exist:
`x86-generic`
Generic x86 support with many different ethernet drivers; should run on
@ -27,3 +27,10 @@ There are two targets for x86 images:
`x86-kvm`
The `x86-kvm` image uses VirtIO as its harddisk and network driver.
`x86-xen_domu`
The `x86-xen_domu` target contains the necessary drivers for use in Xen.
`x86-64`
64bit version of `x86-generic`. Also has VirtIO support, so there's no need for an
`x86-64-kvm` target.

View File

@ -1,5 +1,6 @@
CONFIG_IMAGEOPT=y
# CONFIG_PER_FEED_REPO is not set
# CONFIG_TARGET_ROOTFS_INITRAMFS is not set
CONFIG_DEVEL=y
CONFIG_ALL_KMODS=y
@ -17,6 +18,5 @@ CONFIG_BUSYBOX_CONFIG_FEATURE_WGET_TIMEOUT=y
CONFIG_ATH_USER_REGD=y
CONFIG_PACKAGE_ATH_DEBUG=y
CONFIG_ATH10K_CT_COMMUNITY_FW=y
CONFIG_LUCI_SRCDIET=y

10
modules
View File

@ -1,18 +1,18 @@
GLUON_FEEDS='openwrt gluon routing luci'
OPENWRT_REPO=git://git.openwrt.org/15.05/openwrt.git
OPENWRT_COMMIT=363508bcabd8e9205f5fffc8ff282439e61d618f
OPENWRT_COMMIT=c698aa66043a151ac76d19849be9ee24dfd78b72
PACKAGES_OPENWRT_REPO=git://github.com/openwrt/packages.git
PACKAGES_OPENWRT_COMMIT=f8a70fc188673d0ae8739b0a3095f7f61335fc10
PACKAGES_OPENWRT_COMMIT=9622fe984bba3a4547f48bc507ebaba7637eb2b0
PACKAGES_OPENWRT_BRANCH=for-15.05
PACKAGES_GLUON_REPO=git://github.com/freifunk-gluon/packages.git
PACKAGES_GLUON_COMMIT=b4f04f51d53b151a45f0618eef6d89d32f52dae7
PACKAGES_GLUON_COMMIT=06f2a62b97a25ddd1b9919d084c626d42cef5489
PACKAGES_ROUTING_REPO=git://github.com/openwrt-routing/packages.git
PACKAGES_ROUTING_COMMIT=ae65d4fe027592652376f8dbd3ff2ef37f5a84bc
PACKAGES_ROUTING_COMMIT=2a8338559de5c4b077cde7a83f43f4700a17d5cc
PACKAGES_LUCI_REPO=git://github.com/openwrt/luci.git
PACKAGES_LUCI_COMMIT=8832d534e96d3a934bd02711884371fc78a0d506
PACKAGES_LUCI_COMMIT=cdcdfd2594634804ab09dc8105e46116edce0cd6
PACKAGES_LUCI_BRANCH=for-15.05

View File

@ -11,7 +11,7 @@ include $(INCLUDE_DIR)/package.mk
define Package/gluon-alfred
SECTION:=gluon
CATEGORY:=Gluon
DEPENDS:=+gluon-core +gluon-announced +gluon-neighbour-info +micrond +alfred
DEPENDS:=+gluon-core +gluon-respondd +gluon-neighbour-info +micrond +alfred
TITLE:=Configure alfred
endef

View File

@ -0,0 +1,6 @@
#!/bin/sh
. /lib/gluon/autoupdater/lib.sh
start_enabled alfred

View File

@ -0,0 +1,6 @@
#!/bin/sh
. /lib/gluon/autoupdater/lib.sh
stop alfred

View File

@ -1,32 +0,0 @@
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-jsonc +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))

View File

@ -1 +0,0 @@
return require('gluon.util').node_id()

View File

@ -1 +0,0 @@
return require('platform_info').get_model()

View File

@ -1,14 +0,0 @@
local n = 0
local cpus = util.readline(io.open('/sys/devices/system/cpu/online'))
for entry in cpus:gmatch('([^,]+)') 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

View File

@ -1 +0,0 @@
return uci:get_first('system', 'system', 'hostname')

View File

@ -1 +0,0 @@
return require('gluon.sysconfig').primary_mac

View File

@ -1 +0,0 @@
return require('gluon.util').node_id()

View File

@ -1,4 +0,0 @@
return {
base = 'gluon-' .. util.readline(io.open('/lib/gluon/gluon-version')),
release = util.readline(io.open('/lib/gluon/release')),
}

View File

@ -1 +0,0 @@
return require('gluon.site_config').site_code

View File

@ -1 +0,0 @@
return tonumber(util.readline(io.open('/proc/uptime')):match('^[^ ]+ ([^ ]+)'))

View File

@ -1 +0,0 @@
return tonumber(util.readline(io.open('/proc/loadavg')):match('^([^ ]+) '))

View File

@ -1,13 +0,0 @@
local data = io.open('/proc/meminfo'):read('*a')
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,
}

View File

@ -1 +0,0 @@
return require('gluon.util').node_id()

View File

@ -1,3 +0,0 @@
local running, total = util.readline(io.open('/proc/loadavg')):match('^[^ ]+ [^ ]+ [^ ]+ (%d+)/(%d+)')
return { running = tonumber(running), total = tonumber(total) }

View File

@ -1,4 +0,0 @@
local fs = require "nixio.fs"
local st = fs.statvfs("/")
return 1 - st.bfree / st.blocks

View File

@ -1 +0,0 @@
return tonumber(util.readline(io.open('/proc/uptime')):match('^([^ ]+) '))

View File

@ -1,51 +0,0 @@
#!/usr/bin/lua
module('gluon.announce', package.seeall)
fs = require 'nixio.fs'
uci = require('luci.model.uci').cursor()
util = require 'gluon.util'
local function collect_entry(entry)
if fs.stat(entry, 'type') == 'dir' then
return collect_dir(entry)
else
return loadfile(entry)
end
end
function collect_dir(dir)
local fns = {}
for entry in fs.dir(dir) do
if entry:sub(1, 1) ~= '.' then
collectgarbage()
local fn, err = collect_entry(dir .. '/' .. entry)
if fn then
fns[entry] = fn
else
io.stderr:write(err, '\n')
end
end
end
return function ()
local ret = { [{}] = true }
for k, v in pairs(fns) do
collectgarbage()
local ok, val = pcall(setfenv(v, _M))
if ok then
ret[k] = val
else
io.stderr:write(val, '\n')
end
end
collectgarbage()
return ret
end
end

View File

@ -1,32 +0,0 @@
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))

View File

@ -1,45 +0,0 @@
#!/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

View File

@ -1,18 +0,0 @@
#!/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')

View File

@ -1,50 +0,0 @@
local announce = require 'gluon.announce'
local deflate = require 'deflate'
local json = require 'luci.jsonc'
local nixio = require 'nixio'
local fs = require 'nixio.fs'
local memoize = {}
nixio.chdir('/lib/gluon/announce/')
for dir in fs.glob('*.d') do
local name = dir:sub(1, -3)
memoize[name] = announce.collect_dir(dir)
end
local function collect(type)
return memoize[type] and memoize[type]()
end
module('gluon.announced', package.seeall)
function handle_request(query)
collectgarbage()
local m = query:match('^GET ([a-z ]+)$')
local ret
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
ret = deflate.compress(json.stringify(data))
end
elseif query:match('^[a-z]+$') then
local ok, data = pcall(collect, query)
if ok then
ret = json.stringify(data)
end
end
collectgarbage()
return ret
end

View File

@ -5,29 +5,28 @@ PKG_VERSION:=4
PKG_RELEASE:=$(GLUON_BRANCH)
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
define Package/gluon-autoupdater
SECTION:=gluon
CATEGORY:=Gluon
DEPENDS:=+gluon-core +micrond +autoupdater
DEPENDS:=+gluon-core +libgluonutil +micrond +autoupdater
TITLE:=Automatically update firmware
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-autoupdater/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/autoupdater.so
if [ '$(GLUON_BRANCH)' ]; then \
$(INSTALL_DIR) $(1)/lib/gluon/autoupdater; \
echo '$(GLUON_BRANCH)' > $(1)/lib/gluon/autoupdater/default_branch; \

View File

@ -1,7 +0,0 @@
local autoupdater = uci:get_all('autoupdater', 'settings')
if autoupdater then
return {
branch = autoupdater['branch'],
enabled = uci:get_bool('autoupdater', 'settings', 'enabled'),
}
end

View File

@ -0,0 +1,16 @@
# Library to be sourced by download.d/abort.d scripts
stop() {
if [ -x /etc/init.d/$1 ]; then
echo "Stopping $1..."
/etc/init.d/$1 stop
fi
}
start_enabled() {
if [ -x /etc/init.d/$1 ] && /etc/init.d/$1 enabled; then
echo "Starting $1..."
/etc/init.d/$1 start
fi
}

View File

@ -0,0 +1,9 @@
#!/bin/sh
. /lib/gluon/autoupdater/lib.sh
start_enabled cron
start_enabled haveged
start_enabled micrond
start_enabled sysntpd

View File

@ -0,0 +1,9 @@
#!/bin/sh
. /lib/gluon/autoupdater/lib.sh
stop cron
stop haveged
stop micrond
stop sysntpd

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci

View File

@ -0,0 +1,79 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
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 <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <string.h>
static struct json_object * get_autoupdater(void) {
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "autoupdater", &p))
goto error;
struct uci_section *s = uci_lookup_section(ctx, p, "settings");
if (!s)
goto error;
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "branch", gluonutil_wrap_string(uci_lookup_option_string(ctx, s, "branch")));
const char *enabled = uci_lookup_option_string(ctx, s, "enabled");
json_object_object_add(ret, "enabled", json_object_new_boolean(enabled && !strcmp(enabled, "1")));
uci_free_context(ctx);
return ret;
error:
uci_free_context(ctx);
return NULL;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *software = json_object_new_object();
json_object_object_add(software, "autoupdater", get_autoupdater());
json_object_object_add(ret, "software", software);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{}
};

View File

@ -12,7 +12,7 @@ define Package/gluon-core
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Base files of Gluon
DEPENDS:=+gluon-site +lua-platform-info +luci-base +odhcp6c +firewall
DEPENDS:=+gluon-site +libgluonutil +lua-platform-info +luci-base +luci-lib-jsonc +odhcp6c +firewall
endef

View File

@ -1,20 +1,26 @@
local config = os.getenv('GLUON_SITE_CONFIG') or '/lib/gluon/site.conf'
local function get_site_config()
local config = '/lib/gluon/site.json'
local function loader()
coroutine.yield('return ')
coroutine.yield(io.open(config):read('*a'))
local json = require 'luci.jsonc'
local ltn12 = require 'luci.ltn12'
local file = assert(io.open(config))
local decoder = json.new()
ltn12.pump.all(ltn12.source.file(io.open(config)), decoder:sink())
file:close()
return assert(decoder:get())
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,
__index = get_site_config(),
}
)

View File

@ -1 +0,0 @@
rule 'MULTICAST_OUT -p IPv4 --ip-protocol icmp -j RETURN'

View File

@ -1,2 +1,5 @@
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol 0 -j RETURN' -- hop-by-hop
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type echo-request -j DROP'
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 139 -j DROP'
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ipv6-icmp -j RETURN'
rule 'MULTICAST_OUT -p IPv6 --ip6-protocol 0 -j RETURN' -- hop-by-hop

View File

@ -44,7 +44,7 @@ if fs.access("/etc/config/dropbear") then
function keys.write(self, section, value)
if value then
fs.writefile("/etc/dropbear/authorized_keys", value:gsub("\r\n", "\n"))
fs.writefile("/etc/dropbear/authorized_keys", value:gsub("\r\n", "\n"):trim() .. "\n")
end
end

View File

@ -11,7 +11,7 @@ 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. '
.. 'with a separate WLAN. This feature is completely independent of the mesh functionality. '
.. 'Please note that the private WLAN and meshing on the WAN interface should not be enabled '
.. 'at the same time.'
))

View File

@ -1,12 +1,12 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-08-19 23:30+0100\n"
"Last-Translator:Tobias Bernot <tqbs@airmail.cc>\n"
"Language-Team: French\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@ -25,6 +25,7 @@ msgid ""
"the mesh functionality. Please note that the private WLAN and meshing on the "
"WAN interface should not be enabled at the same time."
msgstr ""
"Votre nœud peut étendre votre réseau privé en interfaçant le WAN avec un WLAN séparé. "
"Cette fonction est complètement indépendante de les fonctions de MESH. "
"Il ne faut pas activer la fonction de MESH et de WLAN privé en même temps."
"Votre nœud peut étendre votre réseau privé en interfaçant le WAN avec un "
"WLAN séparé. Cette fonction est complètement indépendante de les fonctions "
"de MESH. Il ne faut pas activer la fonction de MESH et de WLAN privé en même "
"temps."

View File

@ -4,6 +4,7 @@ PKG_NAME:=gluon-mesh-batman-adv-core
PKG_VERSION:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
@ -11,21 +12,19 @@ define Package/gluon-mesh-batman-adv-core
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Support for batman-adv meshing (core)
DEPENDS:=+gluon-core +gluon-client-bridge +firewall +libiwinfo-lua
DEPENDS:=+gluon-core +libgluonutil +gluon-client-bridge +firewall +libiwinfo
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-mesh-batman-adv-core/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/mesh-batman-adv-core.so
endef
define Package/gluon-mesh-batman-adv-core/postinst

View File

@ -1,39 +0,0 @@
local ifname_address_cache = {}
function ifname2address(ifname)
local ifaddress
if ifname_address_cache[ifname] ~= nil then
ifaddress = ifname_address_cache[ifname]
else
ifaddress = util.readline(io.open("/sys/class/net/" .. ifname .. "/address"))
ifname_address_cache[ifname] = ifaddress
end
return ifaddress
end
function batadv()
local interfaces = {}
local list = io.lines("/sys/kernel/debug/batman_adv/bat0/originators")
for line in list do
local mac1, lastseen, tq, mac2, ifname =
line:match("^([0-9a-f:]+) +(%d+%.%d+)s +%( *(%d+)%) +([0-9a-f:]+) +%[ *(.-)%]")
if mac1 ~= nil and mac1 == mac2 then
ifaddress = ifname2address(ifname)
if interfaces[ifaddress] == nil then
interfaces[ifaddress] = { neighbours = { [{}] = true } }
end
interfaces[ifaddress].neighbours[mac1] = { tq = tonumber(tq)
, lastseen = tonumber(lastseen)
}
end
end
if next(interfaces) then
return interfaces
end
end
return batadv()

View File

@ -1,40 +0,0 @@
local batman_adv = require 'gluon.batman_adv'
local iwinfo = require 'iwinfo'
function neighbours(iface)
local stations = {}
for k, v in pairs(iface.iw.assoclist(iface.ifname)) do
stations[k:lower()] = { signal = v.signal
, noise = v.noise
, inactive = v.inactive
}
end
if next(stations) then
return stations
end
end
function interfaces()
local interfaces = {}
for ifname in batman_adv.interfaces('bat0') do
pcall(function()
local address = util.readline(io.open('/sys/class/net/' .. ifname .. '/address'))
local wifitype = iwinfo.type(ifname)
if wifitype ~= nil then
interfaces[address] = { ifname = ifname, iw = iwinfo[wifitype] }
end
end)
end
return interfaces
end
local wifi = {}
for address, iface in pairs(interfaces()) do
wifi[address] = { [{}] = true, neighbours = neighbours(iface) }
end
if next(wifi) then
return wifi
end

View File

@ -1,15 +0,0 @@
local ip = require 'luci.ip'
local bit = require 'nixio'.bit
local addresses = {}
for line in io.lines('/proc/net/if_inet6') do
local matches = { line:match('^' .. string.rep('(%x%x%x%x)', 8) .. string.rep(' %x%x', 3) .. ' (%x%x)%s+([^%s]+)$') }
-- exclude wrong interfaces and deprecated as well as tentative addresses
-- (see /include/uapi/linux/if_addr.h in linux source for flags)
if matches[10] == 'br-client' and bit.band(tonumber(matches[9], 16), 0x60) == 0 then
table.insert(addresses, ip.IPv6(string.format('%s:%s:%s:%s:%s:%s:%s:%s', unpack(matches))):string():lower())
end
end
return addresses

View File

@ -1,56 +0,0 @@
local batman_adv = require 'gluon.batman_adv'
local wireless = {}
local tunnel = {}
local other = {}
local function get_address(t, ifname)
pcall(
function()
table.insert(t, util.readline(io.open('/sys/class/net/' .. ifname .. '/address')))
end
)
end
local function file_exists(filename)
local f = io.open(filename)
if f == nil then
return false
else
f:close()
return true
end
end
local function is_wireless(ifname)
return file_exists('/sys/class/net/' .. ifname .. '/wireless')
end
local function is_tuntap(ifname)
return file_exists('/sys/class/net/' .. ifname .. '/tun_flags')
end
local function nil_table(t)
if next(t) ~= nil then
return t
else
return nil
end
end
for ifname in batman_adv.interfaces('bat0') do
if is_wireless(ifname) then
get_address(wireless, ifname)
elseif is_tuntap(ifname) then
get_address(tunnel, ifname)
else
get_address(other, ifname)
end
end
return {
wireless = nil_table(wireless),
tunnel = nil_table(tunnel),
other = nil_table(other),
[{}] = true
}

View File

@ -1,13 +0,0 @@
local batman_adv = require 'gluon.batman_adv'
local interfaces = {}
for ifname in batman_adv.interfaces('bat0') do
pcall(
function()
table.insert(interfaces, util.readline(io.open('/sys/class/net/' .. ifname .. '/address')))
end
)
end
return interfaces

View File

@ -1 +0,0 @@
return util.readline(io.open('/sys/module/batman_adv/version'))

View File

@ -1,55 +0,0 @@
local iwinfo = require 'iwinfo'
local counts = { total = 0
, wifi = 0
, wifi24 = 0
, wifi5 = 0
}
local list = io.lines("/sys/kernel/debug/batman_adv/bat0/transtable_local")
local clients = {}
for line in list do
local mac, _, flags, lastseen = line:match("^ %* ([0-9a-f:]+) *(.- )%[(.-)%] +(%d+%.%d+)")
if mac then
if not flags:match('P') then
counts.total = counts.total + 1
clients[mac:lower()] = true
if flags:match('W') then
counts.wifi = counts.wifi +1
end
end
end
end
function count_iface_stations(iface)
local wifitype = iwinfo.type(iface)
if wifitype == nil then
return
end
local freq = iwinfo[wifitype].frequency(iface)
local key
if freq >= 2400 and freq < 2500 then
key = "wifi24"
elseif freq >= 5000 and freq < 6000 then
key = "wifi5"
else
return
end
for k, v in pairs(iwinfo[wifitype].assoclist(iface)) do
if clients[k:lower()] then
counts[key] = counts[key] + 1
end
end
end
local ifaces = {}
uci:foreach("wireless", "wifi-iface", function(s)
if s.network == "client" and s.mode == "ap" then
count_iface_stations(s.ifname)
end
end)
return counts

View File

@ -1,12 +0,0 @@
local gateway = ''
for line in io.lines('/sys/kernel/debug/batman_adv/bat0/gateways') do
if line:sub(1, 3) == '=> ' then
gateway = line:sub(4, 20)
break
end
end
if gateway ~= '' then
return gateway
end

View File

@ -1,14 +0,0 @@
local ethtool = require 'ethtool_stats'
local fields = ethtool.interface_stats('bat0')
local traffic = {}
for _, class in ipairs({'rx', 'tx', 'forward', 'mgmt_rx', 'mgmt_tx'}) do
traffic[class] = {
bytes = fields[class .. '_bytes'],
packets = fields[class],
}
end
traffic['tx']['dropped'] = fields['tx_dropped']
return traffic

View File

@ -10,6 +10,7 @@ if not c:get('network', 'mesh_wan') then
{ ifname = 'br-wan'
, proto = 'batadv'
, mesh = 'bat0'
, mesh_no_rebroadcast = '1'
, auto = site.mesh_on_wan and 1 or 0
})
end

View File

@ -27,6 +27,7 @@ if sysconfig.lan_ifname and not uci:get('network', 'mesh_lan') then
{ ifname = sysconfig.lan_ifname
, proto = 'batadv'
, mesh = 'bat0'
, mesh_no_rebroadcast = '1'
, macaddr = util.generate_mac(1, 1)
, auto = enable and 1 or 0
})
@ -34,4 +35,3 @@ if sysconfig.lan_ifname and not uci:get('network', 'mesh_lan') then
uci:save('network')
uci:commit('network')
end

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -liwinfo -luci

View File

@ -0,0 +1,604 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
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 <respondd.h>
#include <iwinfo.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <alloca.h>
#include <glob.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/ethtool.h>
#include <linux/if_addr.h>
#include <linux/sockios.h>
#define _STRINGIFY(s) #s
#define STRINGIFY(s) _STRINGIFY(s)
static struct json_object * get_addresses(void) {
FILE *f = fopen("/proc/net/if_inet6", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
struct json_object *ret = json_object_new_array();
while (getline(&line, &len, f) >= 0) {
/* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */
char ifname[IF_NAMESIZE+1];
unsigned int flags;
struct in6_addr addr;
char buf[INET6_ADDRSTRLEN];
if (sscanf(line,
"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8
"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8"%2"SCNx8
" %*2x %*2x %*2x %2x %"STRINGIFY(IF_NAMESIZE)"s",
&addr.s6_addr[0], &addr.s6_addr[1], &addr.s6_addr[2], &addr.s6_addr[3],
&addr.s6_addr[4], &addr.s6_addr[5], &addr.s6_addr[6], &addr.s6_addr[7],
&addr.s6_addr[8], &addr.s6_addr[9], &addr.s6_addr[10], &addr.s6_addr[11],
&addr.s6_addr[12], &addr.s6_addr[13], &addr.s6_addr[14], &addr.s6_addr[15],
&flags, ifname) != 18)
continue;
if (strcmp(ifname, "br-client"))
continue;
if (flags & (IFA_F_TENTATIVE|IFA_F_DEPRECATED))
continue;
inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
json_object_array_add(ret, json_object_new_string(buf));
}
fclose(f);
free(line);
return ret;
}
static void add_if_not_empty(struct json_object *obj, const char *key, struct json_object *val) {
if (json_object_array_length(val))
json_object_object_add(obj, key, val);
else
json_object_put(val);
}
static bool interface_file_exists(const char *ifname, const char *name) {
const char *format = "/sys/class/net/%s/%s";
char path[strlen(format) + strlen(ifname) + strlen(name)];
snprintf(path, sizeof(path), format, ifname, name);
return !access(path, F_OK);
}
static void mesh_add_subif(const char *ifname, struct json_object *wireless,
struct json_object *tunnel, struct json_object *other) {
struct json_object *address = gluonutil_wrap_and_free_string(gluonutil_get_interface_address(ifname));
if (interface_file_exists(ifname, "wireless"))
json_object_array_add(wireless, address);
else if (interface_file_exists(ifname, "tun_flags"))
json_object_array_add(tunnel, address);
else
json_object_array_add(other, address);
}
static struct json_object * get_mesh_subifs(const char *ifname) {
struct json_object *wireless = json_object_new_array();
struct json_object *tunnel = json_object_new_array();
struct json_object *other = json_object_new_array();
const char *format = "/sys/class/net/%s/lower_*";
char pattern[strlen(format) + strlen(ifname) - 1];
snprintf(pattern, sizeof(pattern), format, ifname);
size_t pattern_len = strlen(pattern);
glob_t lower;
if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) {
size_t i;
for (i = 0; i < lower.gl_pathc; i++) {
mesh_add_subif(lower.gl_pathv[i] + pattern_len - 1,
wireless, tunnel, other);
}
globfree(&lower);
}
struct json_object *ret = json_object_new_object();
add_if_not_empty(ret, "wireless", wireless);
add_if_not_empty(ret, "tunnel", tunnel);
add_if_not_empty(ret, "other", other);
return ret;
}
static struct json_object * get_mesh(void) {
struct json_object *ret = json_object_new_object();
struct json_object *bat0_interfaces = json_object_new_object();
json_object_object_add(bat0_interfaces, "interfaces", get_mesh_subifs("bat0"));
json_object_object_add(ret, "bat0", bat0_interfaces);
return ret;
}
static struct json_object * get_batman_adv_compat(void) {
FILE *f = fopen("/lib/gluon/mesh-batman-adv-core/compat", "r");
if (!f)
return NULL;
struct json_object *ret = NULL;
int compat;
if (fscanf(f, "%i", &compat) == 1)
ret = json_object_new_int(compat);
fclose(f);
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *network = json_object_new_object();
json_object_object_add(network, "addresses", get_addresses());
json_object_object_add(network, "mesh", get_mesh());
json_object_object_add(ret, "network", network);
struct json_object *software = json_object_new_object();
struct json_object *software_batman_adv = json_object_new_object();
json_object_object_add(software_batman_adv, "version", gluonutil_wrap_and_free_string(gluonutil_read_line("/sys/module/batman_adv/version")));
json_object_object_add(software_batman_adv, "compat", get_batman_adv_compat());
json_object_object_add(software, "batman-adv", software_batman_adv);
json_object_object_add(ret, "software", software);
return ret;
}
static void add_gateway(struct json_object *obj) {
FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/gateways", "r");
if (!f)
return;
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, f) >= 0) {
char addr[18];
if (sscanf(line, "=> %17[0-9a-fA-F:]", addr) != 1)
continue;
json_object_object_add(obj, "gateway", json_object_new_string(addr));
break;
}
free(line);
fclose(f);
}
static inline bool ethtool_ioctl(int fd, struct ifreq *ifr, void *data) {
ifr->ifr_data = data;
return (ioctl(fd, SIOCETHTOOL, ifr) >= 0);
}
static uint32_t ethtool_get_stats_length(int fd, struct ifreq *ifr) {
const size_t sset_info_len = sizeof(struct ethtool_sset_info) + sizeof(uint32_t);
struct ethtool_sset_info *sset_info = alloca(sset_info_len);
memset(sset_info, 0, sset_info_len);
sset_info->cmd = ETHTOOL_GSSET_INFO;
sset_info->sset_mask = 1ull << ETH_SS_STATS;
if (!ethtool_ioctl(fd, ifr, sset_info))
return 0;
return sset_info->sset_mask ? sset_info->data[0] : 0;
}
static struct ethtool_gstrings * ethtool_get_stats_strings(int fd, struct ifreq *ifr) {
uint32_t n_stats = ethtool_get_stats_length(fd, ifr);
if (!n_stats)
return NULL;
struct ethtool_gstrings *strings = calloc(1, sizeof(*strings) + n_stats * ETH_GSTRING_LEN);
strings->cmd = ETHTOOL_GSTRINGS;
strings->string_set = ETH_SS_STATS;
strings->len = n_stats;
if (!ethtool_ioctl(fd, ifr, strings)) {
free(strings);
return NULL;
}
return strings;
}
static struct json_object * get_traffic(void) {
struct ethtool_gstrings *strings = NULL;
struct ethtool_stats *stats = NULL;
struct ifreq ifr = {};
strncpy(ifr.ifr_name, "bat0", IF_NAMESIZE);
struct json_object *ret = NULL;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return NULL;
strings = ethtool_get_stats_strings(fd, &ifr);
if (!strings)
goto out;
stats = calloc(1, sizeof(struct ethtool_stats) + strings->len * sizeof(uint64_t));
stats->cmd = ETHTOOL_GSTATS;
stats->n_stats = strings->len;
if (!ethtool_ioctl(fd, &ifr, stats))
goto out;
struct json_object *rx = json_object_new_object();
struct json_object *tx = json_object_new_object();
struct json_object *forward = json_object_new_object();
struct json_object *mgmt_rx = json_object_new_object();
struct json_object *mgmt_tx = json_object_new_object();
size_t i;
for (i = 0; i < strings->len; i++) {
if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx", ETH_GSTRING_LEN))
json_object_object_add(rx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx_bytes", ETH_GSTRING_LEN))
json_object_object_add(rx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx", ETH_GSTRING_LEN))
json_object_object_add(tx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_dropped", ETH_GSTRING_LEN))
json_object_object_add(tx, "dropped", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_bytes", ETH_GSTRING_LEN))
json_object_object_add(tx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward", ETH_GSTRING_LEN))
json_object_object_add(forward, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward_bytes", ETH_GSTRING_LEN))
json_object_object_add(forward, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx", ETH_GSTRING_LEN))
json_object_object_add(mgmt_rx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx_bytes", ETH_GSTRING_LEN))
json_object_object_add(mgmt_rx, "bytes", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx", ETH_GSTRING_LEN))
json_object_object_add(mgmt_tx, "packets", json_object_new_int64(stats->data[i]));
else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx_bytes", ETH_GSTRING_LEN))
json_object_object_add(mgmt_tx, "bytes", json_object_new_int64(stats->data[i]));
}
ret = json_object_new_object();
json_object_object_add(ret, "rx", rx);
json_object_object_add(ret, "tx", tx);
json_object_object_add(ret, "forward", forward);
json_object_object_add(ret, "mgmt_rx", mgmt_rx);
json_object_object_add(ret, "mgmt_tx", mgmt_tx);
out:
free(stats);
free(strings);
close(fd);
return ret;
}
static void count_iface_stations(size_t *wifi24, size_t *wifi5, const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return;
int freq;
if (iw->frequency(ifname, &freq) < 0)
return;
size_t *wifi;
if (freq >= 2400 && freq < 2500)
wifi = wifi24;
else if (freq >= 5000 && freq < 6000)
wifi = wifi5;
else
return;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return;
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++)
(*wifi)++;
}
static void count_stations(size_t *wifi24, size_t *wifi5) {
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "wireless", &p))
goto end;
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "wifi-iface"))
continue;
const char *network = uci_lookup_option_string(ctx, s, "network");
if (!network || strcmp(network, "client"))
continue;
const char *mode = uci_lookup_option_string(ctx, s, "mode");
if (!mode || strcmp(mode, "ap"))
continue;
const char *ifname = uci_lookup_option_string(ctx, s, "ifname");
if (!ifname)
continue;
count_iface_stations(wifi24, wifi5, ifname);
}
end:
uci_free_context(ctx);
}
static struct json_object * get_clients(void) {
size_t total = 0, wifi = 0, wifi24 = 0, wifi5 = 0;
FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/transtable_local", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, f) >= 0) {
char flags[16];
if (sscanf(line, " * %*[^[] [%15[^]]]", flags) != 1)
continue;
if (strchr(flags, 'P'))
continue;
total++;
if (strchr(flags, 'W'))
wifi++;
}
free(line);
fclose(f);
count_stations(&wifi24, &wifi5);
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "total", json_object_new_int(total));
json_object_object_add(ret, "wifi", json_object_new_int(wifi));
json_object_object_add(ret, "wifi24", json_object_new_int(wifi24));
json_object_object_add(ret, "wifi5", json_object_new_int(wifi5));
return ret;
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "clients", get_clients());
json_object_object_add(ret, "traffic", get_traffic());
add_gateway(ret);
return ret;
}
static struct json_object * ifnames2addrs(struct json_object *interfaces) {
struct json_object *ret = json_object_new_object();
json_object_object_foreach(interfaces, ifname, interface) {
char *ifaddr = gluonutil_get_interface_address(ifname);
if (!ifaddr)
continue;
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "neighbours", json_object_get(interface));
json_object_object_add(ret, ifaddr, obj);
free(ifaddr);
}
json_object_put(interfaces);
return ret;
}
static struct json_object * get_batadv(void) {
FILE *f = fopen("/sys/kernel/debug/batman_adv/bat0/originators", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
struct json_object *interfaces = json_object_new_object();
while (getline(&line, &len, f) >= 0) {
char mac1[18], mac2[18];
/* IF_NAMESIZE would be enough, but adding 1 here is simpler than subtracting 1 in the format string */
char ifname[IF_NAMESIZE+1];
double lastseen;
int tq;
if (sscanf(line,
"%17[0-9a-fA-F:] %lfs ( %i ) %17[0-9a-fA-F:] [ %"STRINGIFY(IF_NAMESIZE)"[^]] ]",
mac1, &lastseen, &tq, mac2, ifname) != 5)
continue;
if (strcmp(mac1, mac2))
continue;
struct json_object *interface;
if (!json_object_object_get_ex(interfaces, ifname, &interface)) {
interface = json_object_new_object();
json_object_object_add(interfaces, ifname, interface);
}
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "tq", json_object_new_int(tq));
json_object_object_add(obj, "lastseen", json_object_new_double(lastseen));
json_object_object_add(interface, mac1, obj);
}
fclose(f);
free(line);
return ifnames2addrs(interfaces);
}
static struct json_object * get_wifi_neighbours(const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return NULL;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return NULL;
struct json_object *neighbours = json_object_new_object();
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++) {
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "signal", json_object_new_int(entry->signal));
json_object_object_add(obj, "noise", json_object_new_int(entry->noise));
json_object_object_add(obj, "inactive", json_object_new_int(entry->inactive));
char mac[18];
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
entry->mac[0], entry->mac[1], entry->mac[2],
entry->mac[3], entry->mac[4], entry->mac[5]);
json_object_object_add(neighbours, mac, obj);
}
struct json_object *ret = json_object_new_object();
if (json_object_object_length(neighbours))
json_object_object_add(ret, "neighbours", neighbours);
else
json_object_put(neighbours);
return ret;
}
static struct json_object * get_wifi(void) {
const char *mesh = "bat0";
struct json_object *ret = json_object_new_object();
const char *format = "/sys/class/net/%s/lower_*";
char pattern[strlen(format) + strlen(mesh)];
snprintf(pattern, sizeof(pattern), format, mesh);
size_t pattern_len = strlen(pattern);
glob_t lower;
if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) {
size_t i;
for (i = 0; i < lower.gl_pathc; i++) {
const char *ifname = lower.gl_pathv[i] + pattern_len - 1;
char *ifaddr = gluonutil_get_interface_address(ifname);
if (!ifaddr)
continue;
struct json_object *neighbours = get_wifi_neighbours(ifname);
if (neighbours)
json_object_object_add(ret, ifaddr, neighbours);
free(ifaddr);
}
globfree(&lower);
}
return ret;
}
static struct json_object * respondd_provider_neighbours(void) {
struct json_object *ret = json_object_new_object();
struct json_object *batadv = get_batadv();
if (batadv)
json_object_object_add(ret, "batadv", batadv);
struct json_object *wifi = get_wifi();
if (wifi)
json_object_object_add(ret, "wifi", wifi);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{"neighbours", respondd_provider_neighbours},
{}
};

View File

@ -4,6 +4,7 @@ PKG_NAME:=gluon-mesh-vpn-fastd
PKG_VERSION:=3
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
@ -11,25 +12,19 @@ define Package/gluon-mesh-vpn-fastd
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Support for connecting batman-adv meshes via fastd
DEPENDS:=+gluon-core gluon-mesh-batman-adv +gluon-wan-dnsmasq +fastd +iptables-mod-extra +simple-tc
endef
define Package/gluon-mesh-vpn-fastd/description
Gluon community wifi mesh firmware framework: fastd support
DEPENDS:=+gluon-core +libgluonutil gluon-mesh-batman-adv +gluon-wan-dnsmasq +fastd +iptables-mod-extra +simple-tc
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-mesh-vpn-fastd/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/mesh-vpn-fastd.so
endef
define Package/gluon-mesh-vpn-fastd/postinst

View File

@ -1,5 +0,0 @@
local ret = {
enabled = uci:get('fastd', 'mesh_vpn', 'enabled') ~= 0,
version = util.readline(io.popen('exec fastd -v')):match('^[^%s]+%s+(.+)'),
}
return ret

View File

@ -1,70 +0,0 @@
local json = require 'luci.jsonc'
local ltn12 = require 'luci.ltn12'
local nixio = require 'nixio'
local site = require 'gluon.site_config'
local fastd_sock = nixio.socket('unix', 'stream')
local socket_path = uci:get('fastd', 'mesh_vpn', 'status_socket')
if not fastd_sock:connect(socket_path) then
return nil
end
local decoder = json.new()
ltn12.pump.all(ltn12.source.file(fastd_sock), decoder:sink())
local status = decoder:get()
local peer_groups
local function peer_connection(config)
local peer = status.peers[config.key]
if peer then
if peer.connection then
return {
established = peer.connection.established/1000
}
else
return function()end -- nil
end
end
end
local function peer_group(config)
local ret = {}
if config.peers then
local peers = {}
for peername, peerconfig in pairs(config.peers) do
peers[peername] = peer_connection(peerconfig)
end
if next(peers) then
ret.peers = peers
end
end
ret.groups = peer_groups(config.groups)
if next(ret) then
return ret
end
end
function peer_groups(groups)
if groups then
local ret = {}
for name, group in pairs(groups) do
ret[name] = peer_group(group)
end
if next(ret) then
return ret
end
end
end
return peer_group(site.fastd_mesh_vpn)

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci

View File

@ -0,0 +1,305 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
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 <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
static struct json_object * get_peer_groups(struct json_object *groups, struct json_object *peers);
static struct json_object * get_fastd_version(void) {
FILE *f = popen("exec fastd -v", "r");
if (!f)
return NULL;
char *line = NULL;
size_t len = 0;
ssize_t r = getline(&line, &len, f);
pclose(f);
if (r >= 0) {
len = strlen(line); /* The len given by getline is the buffer size, not the string length */
if (len && line[len-1] == '\n')
line[len-1] = 0;
}
else {
free(line);
line = NULL;
}
const char *version = line;
if (strncmp(version, "fastd ", 6) == 0)
version += 6;
struct json_object *ret = gluonutil_wrap_string(version);
free(line);
return ret;
}
static struct json_object * get_fastd(void) {
bool enabled = false;
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "fastd", &p))
goto disabled;
struct uci_section *s = uci_lookup_section(ctx, p, "mesh_vpn");
if (!s)
goto disabled;
const char *enabled_str = uci_lookup_option_string(ctx, s, "enabled");
if (!enabled_str || !strcmp(enabled_str, "1"))
enabled = true;
disabled:
uci_free_context(ctx);
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "version", get_fastd_version());
json_object_object_add(ret, "enabled", json_object_new_boolean(enabled));
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct json_object *software = json_object_new_object();
json_object_object_add(software, "fastd", get_fastd());
json_object_object_add(ret, "software", software);
return ret;
}
static const char * get_status_socket(struct uci_context *ctx, struct uci_section *s) {
return uci_lookup_option_string(ctx, s, "status_socket");
}
static struct json_object * read_status(struct uci_context *ctx, struct uci_section *s) {
const char *path = get_status_socket(ctx, s);
size_t addrlen = strlen(path);
/* Allocate enough space for arbitrary-length paths */
char addrbuf[offsetof(struct sockaddr_un, sun_path) + addrlen + 1];
memset(addrbuf, 0, sizeof(addrbuf));
struct sockaddr_un *addr = (struct sockaddr_un *)addrbuf;
addr->sun_family = AF_UNIX;
memcpy(addr->sun_path, path, addrlen+1);
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
return NULL;
if (connect(fd, (struct sockaddr*)addr, sizeof(addrbuf)) < 0) {
close(fd);
return NULL;
}
struct json_object *ret = NULL;
struct json_tokener *tok = json_tokener_new();
do {
char buf[1024];
size_t len = read(fd, buf, sizeof(buf));
if (len <= 0)
break;
ret = json_tokener_parse_ex(tok, buf, len);
} while (!ret && json_tokener_get_error(tok) == json_tokener_continue);
json_tokener_free(tok);
close(fd);
return ret;
}
static struct json_object * get_status(void) {
struct json_object *ret = NULL;
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (!uci_load(ctx, "fastd", &p)) {
struct uci_section *s = uci_lookup_section(ctx, p, "mesh_vpn");
if (s)
ret = read_status(ctx, s);
}
uci_free_context(ctx);
return ret;
}
static bool get_peer_connection(struct json_object **ret, struct json_object *config, struct json_object *peers) {
struct json_object *key_object;
if (!json_object_object_get_ex(config, "key", &key_object))
return false;
const char *key = json_object_get_string(key_object);
if (!key)
return false;
struct json_object *peer, *connection, *established;
if (!json_object_object_get_ex(peers, key, &peer) ||
!json_object_object_get_ex(peer, "connection", &connection))
return false;
if (json_object_object_get_ex(connection, "established", &established)) {
int64_t established_time = json_object_get_int64(established);
*ret = json_object_new_object();
json_object_object_add(*ret, "established", json_object_new_double(established_time/1000.0));
}
else {
*ret = NULL;
}
return true;
}
static struct json_object * get_peer_group(struct json_object *config, struct json_object *peers) {
struct json_object *ret = json_object_new_object();
struct json_object *config_peers;
if (json_object_object_get_ex(config, "peers", &config_peers) &&
json_object_is_type(config_peers, json_type_object)) {
struct json_object *ret_peers = json_object_new_object();
json_object_object_foreach(config_peers, peername, peerconfig) {
struct json_object *obj;
if (get_peer_connection(&obj, peerconfig, peers))
json_object_object_add(ret_peers, peername, obj);
}
if (json_object_object_length(ret_peers))
json_object_object_add(ret, "peers", ret_peers);
else
json_object_put(ret_peers);
}
struct json_object *config_groups;
if (json_object_object_get_ex(config, "groups", &config_groups)) {
struct json_object *obj = get_peer_groups(config_groups, peers);
if (obj)
json_object_object_add(ret, "groups", obj);
}
if (!json_object_object_length(ret)) {
json_object_put(ret);
return NULL;
}
return ret;
}
static struct json_object * get_peer_groups(struct json_object *groups, struct json_object *peers) {
if (!json_object_is_type(groups, json_type_object))
return NULL;
struct json_object *ret = json_object_new_object();
json_object_object_foreach(groups, name, group) {
struct json_object *g = get_peer_group(group, peers);
if (g)
json_object_object_add(ret, name, g);
}
if (!json_object_object_length(ret)) {
json_object_put(ret);
return NULL;
}
return ret;
}
static struct json_object * get_mesh_vpn(void) {
struct json_object *ret = NULL;
struct json_object *status = NULL;
struct json_object *site = NULL;
status = get_status();
if (!status)
goto end;
struct json_object *peers;
if (!json_object_object_get_ex(status, "peers", &peers))
goto end;
site = gluonutil_load_site_config();
if (!site)
goto end;
struct json_object *fastd_mesh_vpn;
if (!json_object_object_get_ex(site, "fastd_mesh_vpn", &fastd_mesh_vpn))
goto end;
ret = get_peer_group(fastd_mesh_vpn, peers);
end:
json_object_put(site);
json_object_put(status);
return ret;
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
struct json_object *mesh_vpn = get_mesh_vpn();
if (mesh_vpn)
json_object_object_add(ret, "mesh_vpn", mesh_vpn);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{}
};

View File

@ -32,6 +32,8 @@ define Build/Compile
endef
define Package/gluon-neighbour-info/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-neighbour-info $(1)/usr/bin/
endef

View File

@ -0,0 +1,20 @@
#!/usr/bin/lua
local uci = require('luci.model.uci').cursor()
-- Allow incoming respondd replies to queries on WAN
-- If the query was via multicast, the response isn't matched by --state RELATED
uci:section('firewall', 'rule', 'wan_respondd_reply',
{
name = 'wan_respondd_reply',
src = 'wan',
src_ip = 'fe80::/64',
src_port = '1001',
dest_port = '32768:61000', -- see /proc/sys/net/ipv4/ip_local_port_range
proto = 'udp',
target = 'ACCEPT',
}
)
uci:save('firewall')
uci:commit('firewall')

View File

@ -58,7 +58,7 @@ void getclock(struct timeval *tv) {
}
/* Assumes a and b are normalized */
void tv_subtract (struct timeval *r, struct timeval *a, struct timeval *b) {
void tv_subtract (struct timeval *r, const struct timeval *a, const struct timeval *b) {
r->tv_usec = a->tv_usec - b->tv_usec;
r->tv_sec = a->tv_sec - b->tv_sec;
@ -68,18 +68,17 @@ void tv_subtract (struct timeval *r, struct timeval *a, struct timeval *b) {
}
}
ssize_t recvtimeout(int socket, void *buffer, size_t length, int flags, struct timeval *timeout, struct timeval *offset) {
struct timeval now, delta;
ssize_t ret;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(*timeout));
ret = recv(socket, buffer, length, flags);
ssize_t recvtimeout(int socket, void *buffer, size_t length, int flags, const struct timeval *timeout) {
struct timeval now, timeout_left;
getclock(&now);
tv_subtract(&delta, &now, offset);
tv_subtract(timeout, timeout, &delta);
tv_subtract(&timeout_left, timeout, &now);
return ret;
if (timeout_left.tv_sec < 0)
return -1;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout_left, sizeof(timeout_left));
return recv(socket, buffer, length, flags);
}
int request(const int sock, const struct sockaddr_in6 *client_addr, const char *request, const char *sse, double timeout, unsigned int max_count) {
@ -94,14 +93,18 @@ int request(const int sock, const struct sockaddr_in6 *client_addr, const char *
exit(EXIT_FAILURE);
}
struct timeval tv_timeout, tv_offset;
tv_timeout.tv_sec = (int) timeout;
tv_timeout.tv_usec = ((int) (timeout * 1000000)) % 1000000;
struct timeval tv_timeout;
getclock(&tv_timeout);
getclock(&tv_offset);
tv_timeout.tv_sec += (int) timeout;
tv_timeout.tv_usec += ((int) (timeout * 1000000)) % 1000000;
if (tv_timeout.tv_usec >= 1000000) {
tv_timeout.tv_usec -= 1000000;
tv_timeout.tv_sec += 1;
}
do {
ret = recvtimeout(sock, buffer, sizeof(buffer), 0, &tv_timeout, &tv_offset);
ret = recvtimeout(sock, buffer, sizeof(buffer), 0, &tv_timeout);
if (ret < 0)
break;
@ -133,7 +136,6 @@ int main(int argc, char **argv) {
int sock;
struct sockaddr_in6 client_addr = {};
char *request_string = NULL;
struct in6_addr mgroup_addr;
sock = socket(PF_INET6, SOCK_DGRAM, 0);
@ -147,9 +149,7 @@ int main(int argc, char **argv) {
opterr = 0;
int port_set = 0;
int destination_set = 0;
unsigned int max_count = 0;
int max_count = 0;
double timeout = 3.0;
char *sse = NULL;
bool loop = false;
@ -179,6 +179,10 @@ int main(int argc, char **argv) {
break;
case 't':
timeout = atof(optarg);
if (timeout < 0) {
perror("Negative timeout not supported");
exit(EXIT_FAILURE);
}
break;
case 's':
sse = optarg;
@ -206,8 +210,10 @@ int main(int argc, char **argv) {
exit(EXIT_FAILURE);
}
if (sse)
if (sse) {
fputs("Content-Type: text/event-stream\n\n", stdout);
fflush(stdout);
}
do {
ret = request(sock, &client_addr, request_string, sse, timeout, max_count);

View File

@ -5,6 +5,7 @@ PKG_VERSION:=1
PKG_RELEASE:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
PKG_BUILD_DEPENDS := respondd
include $(GLUONDIR)/include/package.mk
@ -12,25 +13,19 @@ define Package/gluon-node-info
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Add /etc/config/gluon-node-info to uci
DEPENDS:=+gluon-core
endef
define Package/gluon-node-info/description
This packages creates /etc/config/gluon-node-info.
DEPENDS:=+gluon-core +libgluonutil
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
endef
define Build/Configure
endef
define Build/Compile
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-node-info/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/node-info.so
endef
define Package/gluon-node-info/postinst

View File

@ -1,7 +0,0 @@
if uci:get_first('gluon-node-info', 'location', 'share_location', false) then
return {
latitude = tonumber(uci:get_first('gluon-node-info', 'location', 'latitude')),
longitude = tonumber(uci:get_first('gluon-node-info', 'location', 'longitude')),
altitude = tonumber(uci:get_first('gluon-node-info', 'location', 'altitude')),
}
end

View File

@ -1,4 +0,0 @@
local contact = uci:get_first('gluon-node-info', 'owner', 'contact', '')
if contact ~= '' then
return { contact = contact }
end

View File

@ -1,4 +0,0 @@
local role = uci:get_first('gluon-node-info', 'system', 'role', '')
if role ~= '' then
return role
end

View File

@ -0,0 +1,6 @@
all: respondd.so
CFLAGS += -Wall
respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci

View File

@ -0,0 +1,144 @@
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
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 <respondd.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <stdlib.h>
#include <string.h>
static struct uci_section * get_first_section(struct uci_package *p, const char *type) {
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, type))
return s;
}
return NULL;
}
static const char * get_first_option(struct uci_context *ctx, struct uci_package *p, const char *type, const char *option) {
struct uci_section *s = get_first_section(p, type);
if (s)
return uci_lookup_option_string(ctx, s, option);
else
return NULL;
}
static struct json_object * get_number(struct uci_context *ctx, struct uci_section *s, const char *name) {
const char *val = uci_lookup_option_string(ctx, s, name);
if (!val || !*val)
return NULL;
char *end;
double d = strtod(val, &end);
if (*end)
return NULL;
return json_object_new_double(d);
}
static struct json_object * get_location(struct uci_context *ctx, struct uci_package *p) {
struct uci_section *s = get_first_section(p, "location");
if (!s)
return NULL;
const char *share = uci_lookup_option_string(ctx, s, "share_location");
if (!share || strcmp(share, "1"))
return NULL;
struct json_object *ret = json_object_new_object();
struct json_object *latitude = get_number(ctx, s, "latitude");
if (latitude)
json_object_object_add(ret, "latitude", latitude);
struct json_object *longitude = get_number(ctx, s, "longitude");
if (longitude)
json_object_object_add(ret, "longitude", longitude);
struct json_object *altitude = get_number(ctx, s, "altitude");
if (altitude)
json_object_object_add(ret, "altitude", altitude);
return ret;
}
static struct json_object * get_owner(struct uci_context *ctx, struct uci_package *p) {
const char *contact = get_first_option(ctx, p, "owner", "contact");
if (!contact || !*contact)
return NULL;
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "contact", gluonutil_wrap_string(contact));
return ret;
}
static struct json_object * get_system(struct uci_context *ctx, struct uci_package *p) {
struct json_object *ret = json_object_new_object();
const char *role = get_first_option(ctx, p, "system", "role");
if (role && *role)
json_object_object_add(ret, "role", gluonutil_wrap_string(role));
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
struct json_object *ret = json_object_new_object();
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (!uci_load(ctx, "gluon-node-info", &p)) {
struct json_object *location = get_location(ctx, p);
if (location)
json_object_object_add(ret, "location", location);
struct json_object *owner = get_owner(ctx, p);
if (owner)
json_object_object_add(ret, "owner", owner);
json_object_object_add(ret, "system", get_system(ctx, p));
}
uci_free_context(ctx);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{}
};

View File

@ -0,0 +1,6 @@
#!/bin/sh
. /lib/gluon/autoupdater/lib.sh
start_enabled gluon-radvd

View File

@ -0,0 +1,6 @@
#!/bin/sh
. /lib/gluon/autoupdater/lib.sh
stop gluon-radvd

View File

@ -0,0 +1,29 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-respondd
PKG_VERSION:=1
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/gluon-respondd
SECTION:=gluon
CATEGORY:=Gluon
TITLE:=Provides node information to the network
DEPENDS:=+gluon-core +libplatforminfo +libgluonutil +respondd
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/gluon-respondd/install
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/lib/gluon/respondd
$(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/respondd.so
endef
$(eval $(call BuildPackage,gluon-respondd))

View File

@ -0,0 +1,34 @@
#!/bin/sh
. /usr/share/libubox/jshn.sh
. /lib/functions/service.sh
DEVLIST=/var/run/gluon-respondd.devs
ifname_to_dev () {
json_load "$(ubus call network.interface.$1 status)"
json_get_var dev device
echo "$dev"
}
case "$ACTION" in
ifdown)
sed "/ $INTERFACE$/d" $DEVLIST > $DEVLIST.new
mv $DEVLIST.new $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 2>/dev/null; echo $DEVICE $INTERFACE)
echo "$DEVS" | sort -u > $DEVLIST.new
mv $DEVLIST.new $DEVLIST
/etc/init.d/gluon-respondd restart_if_running &
;;
esac

View File

@ -0,0 +1,45 @@
#!/bin/sh /etc/rc.common
EXTRA_COMMANDS='restart_if_running'
START=50
SERVICE_WRITE_PID=1
SERVICE_DAEMONIZE=1
DEVLIST=/var/run/gluon-respondd.devs
DAEMON=/usr/bin/respondd
LOCK=/var/run/gluon-respondd.lock
do_start() {
DEVS=$(cat $DEVLIST 2>/dev/null | while read dev iface; do echo -n " -i $dev"; done)
service_start $DAEMON -g ff02::2:1001 -p 1001 -d /lib/gluon/respondd $DEVS
}
do_stop() {
service_stop $DAEMON
}
start() {
lock $LOCK
do_start
lock -u $LOCK
}
stop() {
lock $LOCK
do_stop
lock -u $LOCK
}
restart_if_running() {
lock $LOCK
if service_check $DAEMON; then
do_stop
do_start
fi
lock -u $LOCK
}

View File

@ -0,0 +1 @@
10000

View File

@ -0,0 +1 @@
300000

View File

@ -0,0 +1 @@
5000

Some files were not shown because too many files have changed in this diff Show More