diff --git a/.github/labeler.yml b/.github/labeler.yml index b1efaf70cf..292a433073 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -28,7 +28,7 @@ - package/gluon-mesh-vpn-fastd/** "3. topic: firewall": - package/**/*-firewall - - package/gluon-ebtables-*/** + - package/gluon-nftables-*/** "3. topic: hardware": - package/gluon-core/luasrc/lib/gluon/upgrade/010-primary-mac - package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 6e32d9889e..24bb125cce 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Create backport PRs - uses: korthout/backport-action@v1.2.0 + uses: korthout/backport-action@v1.3.0 with: # Config README: https://github.com/korthout/backport-action#backport-action pull_description: |- diff --git a/.luacheckrc b/.luacheckrc index 2f491ca253..3cdc71c2e9 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -29,6 +29,7 @@ files["package/**/check_site.lua"] = { "need", "need_alphanumeric_key", "need_array", + "need_array_elements_exclusive", "need_array_of", "need_boolean", "need_chanlist", @@ -50,6 +51,7 @@ files["package/**/check_site.lua"] = { files["package/**/luasrc/lib/gluon/config-mode/*"] = { globals = { + "MultiListValue", "DynamicList", "Flag", "Form", @@ -79,10 +81,17 @@ files["package/**/luasrc/lib/gluon/**/controller/*"] = { }, } -files["package/**/luasrc/lib/gluon/ebtables/*"] = { +files["package/**/luasrc/lib/gluon/nftables/*"] = { read_globals = { - "chain", + "path", + "include", "rule", + + "bridge_rule", + "bridge_chain", + "bridge_table", + "bridge_include_rule", + "bridge_include_table", }, max_line_length = false, } diff --git a/Makefile b/Makefile index 695b1bc73c..4c845bec92 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,7 @@ GLUON_PACKAGEDIR ?= $(GLUON_OUTPUTDIR)/packages GLUON_DEBUGDIR ?= $(GLUON_OUTPUTDIR)/debug GLUON_TARGETSDIR ?= targets GLUON_PATCHESDIR ?= patches +GLUON_PREFIX ?= openwrt $(eval $(call mkabspath,GLUON_TMPDIR)) $(eval $(call mkabspath,GLUON_OUTPUTDIR)) @@ -60,6 +61,7 @@ GLUON_MULTIDOMAIN ?= 0 GLUON_AUTOREMOVE ?= 0 GLUON_DEBUG ?= 0 GLUON_MINIFY ?= 1 +GLUON_BUILDTYPE ?= gluon # Can be overridden via environment/command line/... to use the Gluon # build system for non-Gluon builds @@ -71,7 +73,7 @@ GLUON_VARS = \ GLUON_VERSION GLUON_SITE_VERSION \ GLUON_RELEASE GLUON_REGION GLUON_MULTIDOMAIN GLUON_AUTOREMOVE GLUON_DEBUG GLUON_MINIFY GLUON_DEPRECATED \ GLUON_DEVICES GLUON_TARGETSDIR GLUON_PATCHESDIR GLUON_TMPDIR GLUON_IMAGEDIR GLUON_PACKAGEDIR GLUON_DEBUGDIR \ - GLUON_SITEDIR GLUON_AUTOUPDATER_BRANCH GLUON_AUTOUPDATER_ENABLED GLUON_LANGS GLUON_BASE_FEEDS \ + GLUON_SITEDIR GLUON_BUILDTYPE GLUON_AUTOUPDATER_BRANCH GLUON_AUTOUPDATER_ENABLED GLUON_LANGS GLUON_BASE_FEEDS GLUON_PREFIX \ GLUON_TARGET BOARD SUBTARGET unexport $(GLUON_VARS) diff --git a/README.md b/README.md index c0bd27c29a..8560d62c9a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ [![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg)](https://opensource.org/license/bsd-2-clause/) [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/freifunk-gluon/gluon?sort=semver)](https://github.com/freifunk-gluon/gluon/releases/latest) +# FFGraz Fork + +This is a fork of gluon to add olsrd and L3 mesh support. + +This is intended to be eventually upstreamed. Currently it's a bit messy and the best documentation of the site config would be the [ffgraz site](https://github.com/mkg20001/funkfeuer-graz-gluon-site/blob/master/site.conf) + +See [the github project](https://github.com/ffgraz/gluon/projects/1?query=is%3Aopen+sort%3Aupdated-desc) for more details + # Gluon Gluon is a firmware framework to build preconfigured OpenWrt images for public mesh networks. diff --git a/contrib/actions/install-dependencies.sh b/contrib/actions/install-dependencies.sh index 3f21e056c5..4f2d28c35c 100755 --- a/contrib/actions/install-dependencies.sh +++ b/contrib/actions/install-dependencies.sh @@ -3,6 +3,6 @@ set -e apt-get -y update -apt-get -y install git subversion build-essential python3 gawk unzip libncurses5-dev zlib1g-dev libssl-dev wget time qemu-utils +apt-get -y install git build-essential python3 gawk unzip libncurses5-dev zlib1g-dev libssl-dev libelf-dev wget rsync time qemu-utils apt-get -y clean rm -rf /var/lib/apt/lists/* diff --git a/contrib/ci/olsr-site/site.conf b/contrib/ci/olsr-site/site.conf index 868d3d569d..cc0955eaba 100644 --- a/contrib/ci/olsr-site/site.conf +++ b/contrib/ci/olsr-site/site.conf @@ -23,10 +23,14 @@ -- Prefixes used by clients within the mesh. -- prefix6 is required, prefix4 can be omitted if next_node.ip4 -- is not set. + prefix4 = '10.0.0.0/20', prefix6 = 'fdff:cafe:cafe:cafe::/64', -- Prefixes used by nodes within the mesh node_prefix6 = 'fdff:cafe:cafe:cafe::/64', + node_prefix4 = '10.12.0.0/16', + node_prefix4_range = 24, + node_prefix4_temporary = true, -- Timezone of your community. -- See https://openwrt.org/docs/guide-user/base-system/system_configuration#time_zones @@ -96,7 +100,16 @@ -- Options specific to routing protocols (optional) mesh = { vxlan = true, - olsrd = {}, + -- [olsr] OLSR configuration with v1/v2 parallel mesh + olsrd = { + v1 = { + enable = true, + }, + v2 = { + enable = true, + ip6_exclusive_mode = true, + } + }, }, mesh_vpn = { diff --git a/contrib/ci/olsr-site/site.mk b/contrib/ci/olsr-site/site.mk index 8b4a5a434a..7a1afa807a 100644 --- a/contrib/ci/olsr-site/site.mk +++ b/contrib/ci/olsr-site/site.mk @@ -7,9 +7,6 @@ GLUON_FEATURES := \ autoupdater \ - ebtables-filter-multicast \ - ebtables-filter-ra-dhcp \ - ebtables-limit-arp \ mesh-olsrd \ mesh-vpn-fastd \ respondd \ diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index 8cfa0d542f..84c19ab00b 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -5,8 +5,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ file \ git \ - subversion \ python3 \ + python3-distutils \ build-essential \ gawk \ unzip \ diff --git a/contrib/push_pkg.sh b/contrib/push_pkg.sh index df431e08bf..4e03675a21 100755 --- a/contrib/push_pkg.sh +++ b/contrib/push_pkg.sh @@ -7,7 +7,6 @@ topdir="$(realpath "$(dirname "${0}")/../openwrt")" # defaults to qemu run script ssh_host=localhost build_only=0 -preserve_config=1 print_help() { echo "$0 [OPTIONS] PACAKGE_DIR [PACKAGE_DIR] ..." @@ -18,10 +17,6 @@ print_help() { echo " running qemu instance started by run_qemu.sh." echo " -p PORT use PORT as ssh port (default is 22)" echo " -b build only, do not push" - echo " -P do not preserve /etc/config. By default, if a package" - echo " defines a config file in /etc/config, this config file" - echo " will be preserved. If you specify this flag, the package" - echo " default will be installed instead." echo "" echo ' To change gluon variables, run e.g. "make config GLUON_MINIFY=0"' echo ' because then the gluon logic will be triggered, and openwrt/.config' @@ -33,7 +28,6 @@ print_help() { while getopts "p:r:hbP" opt do case $opt in - P) preserve_config=0;; p) ssh_port="${OPTARG}";; r) ssh_host="${OPTARG}"; [ -z "$ssh_port" ] && ssh_port=22;; b) build_only=1;; @@ -121,18 +115,16 @@ while [ $# -gt 0 ]; do fi done - if [ "$preserve_config" -eq 0 ]; then - opkg_flags=" --force-maintainer" - fi - # shellcheck disable=SC2029 if [ -n "$filename" ]; then - scp -O -P "${ssh_port}" "$feed/$filename" "root@${BL}${ssh_host}${BR}:/tmp/${filename}" + scp -P "${ssh_port}" "$feed/$filename" "root@${BL}${ssh_host}${BR}:/tmp/${filename}" ssh -p "${ssh_port}" "root@${ssh_host}" " set -e - echo Running opkg: - opkg install --force-reinstall ${opkg_flags} '/tmp/${filename}' + echo Extracting: + tar xvfz '/tmp/${filename}' -C /tmp + tar xvfz '/tmp/data.tar.gz' -C / rm '/tmp/${filename}' + rm /tmp/*.tar.gz gluon-reconfigure " else diff --git a/docs/conf.py b/docs/conf.py index 7756759ecc..801bb4beb0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,6 +71,13 @@ # Don't highlight code blocks unless requested explicitly highlight_language = 'none' +# Ignore links to the config mode, as well as anchors on on hackint, which are +# used to mark channel names and do not exist. Regular links are not effected. +linkcheck_ignore = [ + 'http://192.168.1.1', + 'https://webirc.hackint.org/#' +] + # -- Options for HTML output ------------------------------------------------- diff --git a/docs/dev/packages.rst b/docs/dev/packages.rst index 7c239675bd..62abb9ea19 100644 --- a/docs/dev/packages.rst +++ b/docs/dev/packages.rst @@ -29,10 +29,10 @@ the workflow using these scripts: contrib/run_qemu.sh output/images/factory/[...]-x86-64.img # apply changes to the desired package - vi package/gluon-ebtables/files/etc/init.d/gluon-ebtables + vi package/gluon-nftables/files/etc/init.d/gluon-nftables # rebuild and push the package to the qemu instance - contrib/push_pkg.sh package/gluon-ebtables/ + contrib/push_pkg.sh package/gluon-nftables/ # test your changes ... @@ -41,7 +41,7 @@ the workflow using these scripts: ... # rebuild and push the package to the qemu instance - contrib/push_pkg.sh package/gluon-ebtables/ + contrib/push_pkg.sh package/gluon-nftables/ # test your changes ... @@ -83,7 +83,7 @@ Note that: * If you add new packages, you must run ``make update config GLUON_TARGET=...``. * You can change the gluon target of the target machine via ``make config GLUON_TARGET=...``. * If you want to update the ``site.conf`` of the target machine, use ``push_pkg.sh package/gluon-site/``. -* Sometimes when things break, you can heal them by compiling a package with its dependencies: ``cd openwrt; make package/gluon-ebtables/clean; make package/gluon-ebtables/compile; cd ..``. +* Sometimes when things break, you can heal them by compiling a package with its dependencies: ``cd openwrt; make package/gluon-nftables/clean; make package/gluon-nftables/compile; cd ..``. * You can exit qemu by pressing ``CTRL + a`` and ``c`` afterwards. Gluon package makefiles diff --git a/docs/features/olsrd.rst b/docs/features/olsrd.rst new file mode 100644 index 0000000000..c4b93f0f75 --- /dev/null +++ b/docs/features/olsrd.rst @@ -0,0 +1,57 @@ +OLSRD +=========== + +[todo: re-work for upstream] + +Gluon supports OLSRD, both version 1 and 2 in the following modes: + +- olsrd + - v4 only +- olsrd2 + - v4 only + - v6 only + - dual-stack + +olsrdv1 support is intended mostly for migration purposes +and as such v1 IPv6 support is not going to be added + +Configuration +------------- + +The LAN will automatically be determined by the specified prefix and prefix6 + +The following options exist + +.. code-block:: lua + { + mesh { + olsrd = { + v1 = { + -- Enable v1 + -- enable = true, + + -- Set additional olsrd configuration + -- config = { + -- DebugLevel = 0, + -- IpVersion = 4, + -- AllowNoInt = yes, + -- }, + }, + v2 = { + -- Enable v2 + enable = true, + + -- Make v2 IPv6 exclusive + -- ip6_exclusive_mode = true, + + -- Make v2 IPv4 exclusive (useful for v1 co-existence) + -- ip4_exclusive_mode = true, + + -- Set additional olsrd2 configuration + -- config = { + -- + -- } + } + } + } + } diff --git a/docs/features/static-ip.rst b/docs/features/static-ip.rst new file mode 100644 index 0000000000..7e77509620 --- /dev/null +++ b/docs/features/static-ip.rst @@ -0,0 +1,21 @@ +Static IP managment +------------------- + +A hack for graz + +Static IP managment has the following options + +.. code-block:: lua + { + -- Auto-assign addresses from an IPv4 range + node_prefix4 = '10.12.23.0/16', + node_prefix4_range = 24, -- range of node_prefix4 that should be randomized with mac + node_prefix4_temporary = true, -- (def: true) flag to indicate whether or not this is a temporary range that will need manual change for permanent assignments or not + + -- Auto-assign addresses from an IPv6 range + node_prefix6 = 'fdff:cafe:cafe:cafe:23::/128', + node_prefix6_range = 84, -- (def: 64) range of node_prefix6 that should be randomized with mac + node_prefix6_temporary = true, -- (def: false) flag to indicate whether or not this is a temporary range that will need manual change for permanent assignments or not + } + +Note that these addresses are intended to be temporary diff --git a/docs/features/vpn.rst b/docs/features/vpn.rst index 6db2ecc652..88a84cbdb8 100644 --- a/docs/features/vpn.rst +++ b/docs/features/vpn.rst @@ -191,6 +191,16 @@ negative effects. Only when a previously connected node reboots the effect comes into play, as the gateway still knows about the old timestamp of the gluon node. +gluon-mesh-vpn-key-translate +"""""""""""""""""""""""""""" + +Many communities already possess a collection of active fastd-keys when they +plan migrating their community to WireGuard. +These public keys known on the server-side can be derived into their WireGuard +equivalent using `gluon-mesh-vpn-key-translate `__. +The routers do the necessary reencoding of the private key seamlessly +when updating firmware from fastd to the WireGuard variant. + Gateway / Supernode Configuration """"""""""""""""""""""""""""""""" diff --git a/docs/index.rst b/docs/index.rst index 99fc8535f7..e904500838 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -28,8 +28,10 @@ Several Freifunk communities in Germany use Gluon as the foundation of their Fre features/dns-cache features/monitoring features/multidomain + features/olsrd features/authorized-keys features/roles + features/static-ip features/vpn .. toctree:: @@ -62,10 +64,10 @@ Several Freifunk communities in Germany use Gluon as the foundation of their Fre package/gluon-client-bridge package/gluon-config-mode-domain-select - package/gluon-ebtables-filter-multicast - package/gluon-ebtables-filter-ra-dhcp - package/gluon-ebtables-limit-arp - package/gluon-ebtables-source-filter + package/gluon-nftables-filter-multicast + package/gluon-nftables-filter-ra-dhcp + package/gluon-nftables-limit-arp + package/gluon-nftables-source-filter package/gluon-hoodselector package/gluon-logging package/gluon-mesh-batman-adv diff --git a/docs/multidomain-site-example/site.mk b/docs/multidomain-site-example/site.mk index 64ce6fa136..41ae8e564f 100644 --- a/docs/multidomain-site-example/site.mk +++ b/docs/multidomain-site-example/site.mk @@ -7,9 +7,9 @@ GLUON_FEATURES := \ autoupdater \ - ebtables-filter-multicast \ - ebtables-filter-ra-dhcp \ - ebtables-limit-arp \ + nftables-filter-multicast \ + nftables-filter-ra-dhcp \ + nftables-limit-arp \ mesh-batman-adv-15 \ mesh-vpn-fastd \ respondd \ diff --git a/docs/package/gluon-mesh-batman-adv.rst b/docs/package/gluon-mesh-batman-adv.rst index cd362ede93..5776666c42 100644 --- a/docs/package/gluon-mesh-batman-adv.rst +++ b/docs/package/gluon-mesh-batman-adv.rst @@ -13,7 +13,7 @@ domain and will see each other "as if they were connected to one giant switch". This comes with a set of advantages (like quick and economical client device roaming, layer 3 protocol agnosticism, broadcast/multicast). But also impediments, especially layer 2 multicast overhead - which Gluon tries to mitigate to achieve a certain degree -of scalability. See :doc:`gluon-ebtables-filter-multicast` and +of scalability. See :doc:`gluon-nftables-filter-multicast` and :ref:`batman-adv-multicast-architecture` for details. B.A.T.M.A.N. Advanced project homepage: @@ -53,9 +53,9 @@ While generally broadcast capability is a nice feature of a layer 2 mesh protocol, it quickly reaches its limit. For meshes with about **50 nodes / 100 clients, or more** it is therefore highly -recommended to add the :doc:`gluon-ebtables-filter-multicast` +recommended to add the :doc:`gluon-nftables-filter-multicast` package. Also, with the *mesh-batman-adv-15* feature, -:doc:`gluon-ebtables-limit-arp` is selected by default. +:doc:`gluon-nftables-limit-arp` is selected by default. Furthermore, by default IGMP and MLD messages are filtered. See :ref:`site.conf mesh section ` and diff --git a/docs/package/gluon-ebtables-filter-multicast.rst b/docs/package/gluon-nftables-filter-multicast.rst similarity index 91% rename from docs/package/gluon-ebtables-filter-multicast.rst rename to docs/package/gluon-nftables-filter-multicast.rst index eca9c6c742..b8790fc4aa 100644 --- a/docs/package/gluon-ebtables-filter-multicast.rst +++ b/docs/package/gluon-nftables-filter-multicast.rst @@ -1,7 +1,7 @@ -gluon-ebtables-filter-multicast +gluon-nftables-filter-multicast =============================== -The *gluon-ebtables-filter-multicast* package filters out various kinds of +The *gluon-nftables-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 diff --git a/docs/package/gluon-ebtables-filter-ra-dhcp.rst b/docs/package/gluon-nftables-filter-ra-dhcp.rst similarity index 82% rename from docs/package/gluon-ebtables-filter-ra-dhcp.rst rename to docs/package/gluon-nftables-filter-ra-dhcp.rst index 539fbc0d1a..8e365cb029 100644 --- a/docs/package/gluon-ebtables-filter-ra-dhcp.rst +++ b/docs/package/gluon-nftables-filter-ra-dhcp.rst @@ -1,7 +1,7 @@ -gluon-ebtables-filter-ra-dhcp +gluon-nftables-filter-ra-dhcp ============================= -The *gluon-ebtables-filter-ra-dhcp* package tries to prevent common +The *gluon-nftables-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. diff --git a/docs/package/gluon-ebtables-limit-arp.rst b/docs/package/gluon-nftables-limit-arp.rst similarity index 84% rename from docs/package/gluon-ebtables-limit-arp.rst rename to docs/package/gluon-nftables-limit-arp.rst index 9431f0046a..9969012e49 100644 --- a/docs/package/gluon-ebtables-limit-arp.rst +++ b/docs/package/gluon-nftables-limit-arp.rst @@ -1,14 +1,14 @@ -gluon-ebtables-limit-arp +gluon-nftables-limit-arp ======================== -The *gluon-ebtables-limit-arp* package adds filters to limit the +The *gluon-nftables-limit-arp* package adds filters to limit the amount of ARP requests client devices are allowed to send into the mesh. The limits per client device, identified by its MAC address, are 6 packets per minute and 1 per second per node in total. A burst of up to 50 ARP requests is allowed until the rate-limiting -takes effect (see ``--limit-burst`` in ``ebtables(8)``). +takes effect (see ``--limit-burst`` in ``nftables(8)``). Furthermore, ARP requests for a target IP already present in the batman-adv DAT cache are excluded from rate-limiting, in regard @@ -26,4 +26,4 @@ feature is *mesh-batman-adv-15*. It can be unselected via:: GLUON_SITE_PACKAGES := \ - -gluon-ebtables-limit-arp + -gluon-nftables-limit-arp diff --git a/docs/package/gluon-ebtables-source-filter.rst b/docs/package/gluon-nftables-source-filter.rst similarity index 89% rename from docs/package/gluon-ebtables-source-filter.rst rename to docs/package/gluon-nftables-source-filter.rst index 1bbb2e07e0..cfe6f443df 100644 --- a/docs/package/gluon-ebtables-source-filter.rst +++ b/docs/package/gluon-nftables-source-filter.rst @@ -1,7 +1,7 @@ -gluon-ebtables-source-filter +gluon-nftables-source-filter ============================ -The *gluon-ebtables-source-filter* package adds an additional layer-2 filter +The *gluon-nftables-source-filter* package adds an additional layer-2 filter ruleset to prevent unreasonable traffic entering the network via the nodes. Unreasonable means traffic entering the mesh via a node which source IP does not belong to the configured IP space. diff --git a/docs/package/gluon-radv-filterd.rst b/docs/package/gluon-radv-filterd.rst index 7b07ff9c01..55fa9e50a6 100644 --- a/docs/package/gluon-radv-filterd.rst +++ b/docs/package/gluon-radv-filterd.rst @@ -35,7 +35,7 @@ connected to the client interface via cable or WLAN instead of via the mesh fake TQ of 512, so that they are always preferred. Be aware of problems if you plan to use local routers together with the -:doc:`gluon-ebtables-filter-ra-dhcp` package. These router advertisements are +:doc:`gluon-nftables-filter-ra-dhcp` package. These router advertisements are filtered anyway and reach neither the node nor any other client. Therefore the use of local routers is not possible as long as the package ``gluon-radv-filterd`` is used. diff --git a/docs/releases/v2017.1.rst b/docs/releases/v2017.1.rst index 5319aae3c0..eec3922033 100644 --- a/docs/releases/v2017.1.rst +++ b/docs/releases/v2017.1.rst @@ -105,7 +105,7 @@ New features The new package *gluon-ebtables-source-filter* can be used to prevent traffic using unexpected IP addresses or packet types from entering the mesh. - See also: :doc:`../package/gluon-ebtables-source-filter` + See also: :doc:`../package/gluon-nftables-source-filter` Bugfixes ~~~~~~~~ diff --git a/docs/releases/v2018.2.rst b/docs/releases/v2018.2.rst index e365c95348..b3c22cf971 100644 --- a/docs/releases/v2018.2.rst +++ b/docs/releases/v2018.2.rst @@ -120,7 +120,7 @@ trying it out, please contact us on our mailing list or in our IRC channel. gluon-ebtables-limit-arp enabled by default ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The :doc:`../package/gluon-ebtables-limit-arp` package, introduced in Gluon +The :doc:`../package/gluon-nftables-limit-arp` package, introduced in Gluon 2018.1, is now included by default. In case of issues, it can be removed by adding ``-gluon-ebtables-limit-arp`` to *GLUON_SITE_PACKAGES*. diff --git a/docs/site-example/site.mk b/docs/site-example/site.mk index 30671b181d..91624cf44f 100644 --- a/docs/site-example/site.mk +++ b/docs/site-example/site.mk @@ -7,9 +7,9 @@ GLUON_FEATURES := \ autoupdater \ - ebtables-filter-multicast \ - ebtables-filter-ra-dhcp \ - ebtables-limit-arp \ + nftables-filter-multicast \ + nftables-filter-ra-dhcp \ + nftables-limit-arp \ mesh-batman-adv-15 \ mesh-vpn-fastd \ respondd \ diff --git a/docs/user/getting_started.rst b/docs/user/getting_started.rst index f2bbdd3dec..283550a306 100644 --- a/docs/user/getting_started.rst +++ b/docs/user/getting_started.rst @@ -25,18 +25,20 @@ An example configuration can be found in the Gluon repository at *docs/site-exam Dependencies ------------ To build Gluon, several packages need to be installed on the system. On a -freshly installed Debian Stretch system the following packages are required: +freshly installed Debian Bullseye system the following packages are required: * `git` (to get Gluon and other dependencies) -* `subversion` * `python3` * `build-essential` +* `ecdsautils` (to sign firmware, see `contrib/sign.sh`) * `gawk` * `unzip` * `libncurses-dev` (actually `libncurses5-dev`) * `libz-dev` (actually `zlib1g-dev`) * `libssl-dev` +* `libelf-dev` (to build x86-64) * `wget` +* `rsync` * `time` (built-in `time` doesn't work) * `qemu-utils` diff --git a/docs/user/site.rst b/docs/user/site.rst index fc82f314d2..96bb5eacff 100644 --- a/docs/user/site.rst +++ b/docs/user/site.rst @@ -448,13 +448,8 @@ interfaces \: optional The ``client`` role requires exclusive control over an interface. When the ``client`` role is assigned to an interface at the same time as other roles (like ``'client', 'mesh'`` in the above example), the other roles take - precedence (enabling ``mesh``, but not ``client`` in the example). - - Such a default configuration still fulfills a purpose (and is in fact the - recommended way to enable "Mesh-on-LAN" by default): The "LAN interface - meshing" checkbox in the advanced network settings will only add or remove - the ``mesh`` role, so the ``client`` role must already be in the configuration - to make the LAN port a regular client interface when the checkbox is disabled. + precedence (enabling ``mesh``, but not ``client`` in the example). In that + case, the ``client`` role is removed from the config of the interface. All interface settings are optional. If unset, the following defaults are used: diff --git a/docs/user/supported_devices.rst b/docs/user/supported_devices.rst index 3a136427ce..77f91711f5 100644 --- a/docs/user/supported_devices.rst +++ b/docs/user/supported_devices.rst @@ -115,6 +115,7 @@ ath79-generic - CPE510 (v1.0, v1.1, v2.0, v3.0) - CPE710 (v1.0) - EAP225-Outdoor (v1) + - EAP225-Outdoor (v3) - RE450 (v1) - TL-WDR3500 (v1) - TL-WDR3600 (v1) diff --git a/master-modules.sh b/master-modules.sh new file mode 100755 index 0000000000..8f85cd2fce --- /dev/null +++ b/master-modules.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +set -eo pipefail + +echo "Consider your warranty void now. Upgrading to openwrt master, temporarly." + +# move to basedir, in case the script is not executed via `make update-modules` +cd "$(dirname "$0")" || exit 1 + +git checkout -- modules + +sed "s|openwrt-22.03|master|g" -i modules + +# shellcheck source=./modules +source ./modules + +LOCAL_BRANCH=$(git branch --show-current) +[[ $LOCAL_BRANCH != *-updates ]] && LOCAL_BRANCH+=-updates + +for MODULE in "OPENWRT" "PACKAGES_PACKAGES" "PACKAGES_ROUTING" "PACKAGES_GLUON"; do + _REMOTE_URL=${MODULE}_REPO + _REMOTE_BRANCH=${MODULE}_BRANCH + _LOCAL_HEAD=${MODULE}_COMMIT + + REMOTE_URL="${!_REMOTE_URL}" + REMOTE_BRANCH="${!_REMOTE_BRANCH}" + LOCAL_HEAD="${!_LOCAL_HEAD}" + + # get default branch name if none is set + [ -z "${REMOTE_BRANCH}" ] && { + REMOTE_BRANCH=$(git ls-remote --symref "${REMOTE_URL}" HEAD | awk '/^ref:/ { sub(/refs\/heads\//, "", $2); print $2 }') + } + + # fetch the commit id for the HEAD of the module + REMOTE_HEAD=$(git ls-remote "${REMOTE_URL}" "${REMOTE_BRANCH}" | awk '{ print $1 }') + + # skip ahead if the commit id did not change + [ "$LOCAL_HEAD" == "$REMOTE_HEAD" ] && continue 1 + + echo "$REMOTE_URL $REMOTE_HEAD" + + # modify modules file + sed -i "s/${LOCAL_HEAD}/${REMOTE_HEAD}/" ./modules +done + diff --git a/modules b/modules index e06658aaad..c21974b3ea 100644 --- a/modules +++ b/modules @@ -1,16 +1,17 @@ GLUON_FEEDS='packages routing gluon' OPENWRT_REPO=https://github.com/openwrt/openwrt.git -OPENWRT_BRANCH=openwrt-22.03 -OPENWRT_COMMIT=deafcf91627921b2044b2e96de1067cdbaf23d31 +OPENWRT_BRANCH=master +OPENWRT_COMMIT=0dd5505a17959c0b3eabac2827031487444708fb PACKAGES_PACKAGES_REPO=https://github.com/openwrt/packages.git -PACKAGES_PACKAGES_BRANCH=openwrt-22.03 -PACKAGES_PACKAGES_COMMIT=1fd9cbcf93068765c6cb89fa2c39506596011830 +PACKAGES_PACKAGES_BRANCH=master +PACKAGES_PACKAGES_COMMIT=4ec0459fd3e6c3d1ef446337671b6da5bb30d006 PACKAGES_ROUTING_REPO=https://github.com/openwrt/routing.git -PACKAGES_ROUTING_BRANCH=openwrt-22.03 -PACKAGES_ROUTING_COMMIT=1cc7676b9f32acc30ec47f15fcb70380d5d6ef01 +PACKAGES_ROUTING_BRANCH=master +PACKAGES_ROUTING_COMMIT=c9a8f2d03226191111b95890302cb7e1ff18c11e -PACKAGES_GLUON_REPO=https://github.com/freifunk-gluon/packages.git -PACKAGES_GLUON_COMMIT=04d2b6ffbb6ee02012f2733b7752d8db0d12eaff +PACKAGES_GLUON_REPO=https://github.com/mkg20001/packages-2.git +PACKAGES_GLUON_COMMIT=7e34a5cc6102cc85307f3036a658c0e8572eaab8 +PACKAGES_GLUON_BRANCH=funkfeuer diff --git a/package/features b/package/features index da68b369e0..b74442150e 100644 --- a/package/features +++ b/package/features @@ -38,7 +38,7 @@ when(_'web-advanced' and _'autoupdater', { when(_'mesh-batman-adv-15', { - 'gluon-ebtables-limit-arp', + 'gluon-nftables-limit-arp', 'gluon-radvd', }) @@ -46,12 +46,15 @@ when(_'status-page' and _'mesh-batman-adv-15', { 'gluon-status-page-mesh-batman-adv', }) +when(_'status-page' and _'mesh-olsrd', { + 'gluon-status-page-mesh-olsrd' +}) when(_'mesh-babel', { 'gluon-radvd', }) -when(not _'wireless-encryption-wpa3', { +when(not _'wireless-encryption-wpa3' and not _'wireless-encryption-wpa3-openssl', { 'hostapd-mini', }) diff --git a/package/gluon-authorized-keys/check_site.lua b/package/gluon-authorized-keys/check_site.lua index 7daf2115f6..c92fc3e9a6 100644 --- a/package/gluon-authorized-keys/check_site.lua +++ b/package/gluon-authorized-keys/check_site.lua @@ -1 +1,2 @@ need_string_array(in_site({'authorized_keys'})) +need_string_array(in_site({'unauthorized_keys'}), false) diff --git a/package/gluon-authorized-keys/luasrc/lib/gluon/upgrade/100-authorized-keys b/package/gluon-authorized-keys/luasrc/lib/gluon/upgrade/100-authorized-keys index 9eb00dec09..62aa49a12d 100755 --- a/package/gluon-authorized-keys/luasrc/lib/gluon/upgrade/100-authorized-keys +++ b/package/gluon-authorized-keys/luasrc/lib/gluon/upgrade/100-authorized-keys @@ -3,7 +3,13 @@ local site = require 'gluon.site' local file = '/etc/dropbear/authorized_keys' +local uci = require('simple-uci').cursor() +if uci:get_bool('gluon', 'core', 'disable_site_keys') then + return +end + local keys = {} +local rm_keys = {} local function load_keys() for line in io.lines(file) do @@ -11,12 +17,21 @@ local function load_keys() end end +for _, key in ipairs(site.unauthorized_keys({})) do + rm_keys[key] = true +end + pcall(load_keys) -local f = io.open(file, 'a') +local f = io.open(file, 'w') for _, key in ipairs(site.authorized_keys()) do if not keys[key] then f:write(key .. '\n') end end +for key, _ in pairs(keys) do + if not rm_keys[key] then + f:write(key .. '\n') + end +end f:close() diff --git a/package/gluon-config-mode-theme/files/lib/gluon/config-mode/www/static/gluon.css b/package/gluon-config-mode-theme/files/lib/gluon/config-mode/www/static/gluon.css index c84ac01bed..d239fd8f1e 100644 --- a/package/gluon-config-mode-theme/files/lib/gluon/config-mode/www/static/gluon.css +++ b/package/gluon-config-mode-theme/files/lib/gluon/config-mode/www/static/gluon.css @@ -1 +1 @@ -html{min-height:100%;height:auto;position:relative}body,input,select,option{font-family:'Open Sans', Arial, sans-serif;font-size:12pt}body{color:#4d4e53;line-height:1.5em;margin:0;display:flex;flex-direction:column;min-height:100vh;background-color:#f3f3f3}.tabmenu1{text-align:center}ul.tabmenu{list-style:none;padding:0;margin:2em 0;display:inline-flex}ul.tabmenu li{white-space:nowrap;margin:0 0.5em;padding:0;text-align:center}ul.tabmenu li a{display:block;text-decoration:none;padding:1em;margin:0;color:#333;border-radius:2em}ul.tabmenu li a:hover{background:#ffe9b3}ul.tabmenu li.active a{font-weight:bold;background:white;color:#333}#maincontent ul{margin-left:2em}.error{color:#ff0000;background-color:white}#menubar{display:flex;background:#dc0067;color:#ffffff}#menubar a:link.topcat,#menubar a:visited.topcat{position:relative;display:block;padding:0.5em;text-decoration:none;font-size:80%;font-weight:normal;color:white}#menubar a:link.topcat:hover,#menubar a:link.topcat:focus,#menubar a:visited.topcat:hover,#menubar a:visited.topcat:focus{background:#ffb400;color:black}#menubar a:link.topcat.active,#menubar a:visited.topcat.active{background:#ffb400;color:black;font-weight:bold}#menubar .hostinfo{position:relative;margin:0;padding:0.5em;flex:1;font-weight:bold;font-size:80%}#menubar .hostinfo a:link,#menubar .hostinfo a:visited{text-decoration:none;font-weight:bold;color:white}#menubar .hostinfo a:link:hover,#menubar .hostinfo a:link:focus,#menubar .hostinfo a:visited:hover,#menubar .hostinfo a:visited:focus{text-decoration:underline}#topmenu{list-style:none;margin:0;padding:0}#topmenu li{display:inline-block}#maincontent{padding:0 1em 2em;max-width:60em;min-width:40em;margin:1em auto}#maincontent p{margin-bottom:1em}.gluon-section{margin:0;padding:0;border:none;margin-bottom:1.3em}.gluon-section:last-child{margin-bottom:0.7em}.gluon-section legend{font-size:1.4em;font-weight:bold;position:relative;padding:0;margin-bottom:0.5em}.gluon-section h2{margin:0em 0 0.5em -0.5em}.gluon-section h3{text-decoration:none;font-weight:bold;color:#555555;margin:0.25em;font-size:100%}.gluon-section-descr,.gluon-warning{margin-bottom:2em}.gluon-osm-map{width:100%;height:40em;margin-bottom:1em}input::placeholder{color:#aaaaaa}input::-webkit-input-placeholder{color:#aaaaaa}input[type=checkbox]{display:none}input[type=checkbox]+label{display:inline-block;position:relative;width:1em;height:1em;margin:0}input[type=checkbox]:checked+label::after{content:'✔';color:#dc0067;vertical-align:middle;position:absolute;top:50%;left:0;margin-top:-0.5em;width:100%;text-align:center;font-size:1.7em}input[type=radio]{display:none}input[type=radio]+label{display:inline-block;position:relative;width:0.8em;height:0.8em;padding:0.5em;margin:0.2em 0.2em 0.2em 0.1em;border:none;background:#ffe199;vertical-align:middle;border-radius:50%}input[type=radio]:checked+label::after{content:'•';color:#dc0067;vertical-align:middle;position:absolute;top:50%;left:0;margin-top:-0.4em;width:100%;text-align:center;font-size:2em}input[type=submit],input[type=reset],input[type=button]{cursor:pointer}select,input,textarea,input[type=checkbox]+label{color:#003247;border:none;background:#ffe199;border-radius:3pt;padding:0.5em;margin-top:1px;margin-bottom:2px;box-sizing:content-box;outline:0}.select-wrapper{position:relative;display:inline-block}.select-wrapper::before{position:absolute;z-index:1;right:0.05em;top:calc(2px + 0.1em);bottom:calc(2px + 0.1em);width:1.4em;border-left:0.05em solid rgba(0,0,0,0.25);pointer-events:none;background:url('data:image/svg+xml;utf8,') center/0.8em 0.5em no-repeat;content:''}.select-wrapper select{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer}option{color:#003247;background:#ffe199}select,input[type=text],input[type=password]{min-width:20em}.gluon-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:baseline;text-align:center;cursor:pointer;user-select:none;font-size:100%;padding:0.5em 1em;color:rgba(0,0,0,0.8);background-color:#E6E6E6;border:none;text-decoration:none;border-radius:2px;transition:0.1s linear box-shadow;margin-left:0.5em;background-repeat:no-repeat}.gluon-button::-moz-focus-inner{padding:0;border:0}.gluon-button:active{box-shadow:0 0 0 1px rgba(0,0,0,0.15) inset,0 0 6px rgba(0,0,0,0.2) inset}.gluon-button:focus{outline:0}.gluon-button:hover,.gluon-button:focus{background-image:linear-gradient(transparent, rgba(0,0,0,0.05) 40%, rgba(0,0,0,0.1))}.gluon-button[disabled]{border:none;background-image:none;opacity:0.40;cursor:not-allowed;box-shadow:none}.gluon-button-reset{background-color:#e30;color:#fff}.gluon-button-submit{background-color:#009ee0;color:#fff}.gluon-button-submit:active{background:grey}.gluon-input-invalid{background:#e30 !important;color:white}textarea{margin-left:-1px;margin-bottom:0.5em}.gluon-value{display:flex;flex-direction:row;margin-bottom:0.5em}.gluon-section-node .gluon-value:last-child{margin-bottom:0}.gluon-value-title{flex:2;text-align:right;padding-top:0.39em;padding-right:1em;font-weight:bold}.gluon-value-field{flex:3;position:relative}.gluon-value-field input,.gluon-value-field select,.gluon-value-field input+label{position:relative}.gluon-value-field-text{flex:3;padding-top:0.39em}.gluon-value-field-long{flex:10;position:relative;margin-top:0.65em}.gluon-value-field-long input,.gluon-value-field-long select,.gluon-value-field-long input+label{position:relative}.gluon-value-field-long-after{flex:2}.gluon-value-description{font-size:8pt}.gluon-form-descr{margin-bottom:1em}.gluon-form-descr:empty,.gluon-section-descr:empty,.gluon-warning:empty{display:none}.gluon-form-descr,.gluon-section-descr,.gluon-warning,.gluon-page-actions{padding:1em;background:#ececec}.gluon-page-actions{text-align:right;display:flex;flex-flow:row-reverse}.gluon-section-node{clear:both;position:relative;border:none}.gluon-value-error input,.gluon-value-error select{background-color:#ffcccc}.gluon-add::after,.gluon-remove::after{cursor:pointer;display:inline-block;text-align:center;vertical-align:middle;font-size:180%;width:1.2em;height:1em}.gluon-add{color:#008000;position:relative;left:21em}input+.gluon-add{left:0;top:0.04em}.gluon-add:first-child{top:0.53em;left:-0.08em}.gluon-add::after{content:'+'}.gluon-remove{color:#800000;position:relative;top:-0.03em}.gluon-remove::after{content:'–'}.gluon-warning{background:#ffe9b3}.error500{border:1px dotted #ff0000;background-color:#ffffff;color:#000000;padding:0.5em}.errorbox{border:1px solid #FF0000;background-color:#FFCCCC;padding:5px;margin-bottom:5px}.errorbox a{color:#000000 !important}.the-key{text-align:left;font-size:1.4em;background:#ffe9b3;border:3pt dashed #dc0067;margin-bottom:0.5em;padding:0.5em} +html{min-height:100%;height:auto;position:relative}body,input,select,option{font-family:'Open Sans', Arial, sans-serif;font-size:12pt}body{color:#4d4e53;line-height:1.5em;margin:0;display:flex;flex-direction:column;min-height:100vh;background-color:#f3f3f3}.tabmenu1{text-align:center}ul.tabmenu{list-style:none;padding:0;margin:2em 0;display:inline-flex}ul.tabmenu li{white-space:nowrap;margin:0 0.5em;padding:0;text-align:center}ul.tabmenu li a{display:block;text-decoration:none;padding:1em;margin:0;color:#333;border-radius:2em}ul.tabmenu li a:hover{background:#ffe9b3}ul.tabmenu li.active a{font-weight:bold;background:white;color:#333}#maincontent ul{margin-left:2em}.error{color:#ff0000;background-color:white}#menubar{display:flex;background:#dc0067;color:#ffffff}#menubar a:link.topcat,#menubar a:visited.topcat{position:relative;display:block;padding:0.5em;text-decoration:none;font-size:80%;font-weight:normal;color:white}#menubar a:link.topcat:hover,#menubar a:link.topcat:focus,#menubar a:visited.topcat:hover,#menubar a:visited.topcat:focus{background:#ffb400;color:black}#menubar a:link.topcat.active,#menubar a:visited.topcat.active{background:#ffb400;color:black;font-weight:bold}#menubar .hostinfo{position:relative;margin:0;padding:0.5em;flex:1;font-weight:bold;font-size:80%}#menubar .hostinfo a:link,#menubar .hostinfo a:visited{text-decoration:none;font-weight:bold;color:white}#menubar .hostinfo a:link:hover,#menubar .hostinfo a:link:focus,#menubar .hostinfo a:visited:hover,#menubar .hostinfo a:visited:focus{text-decoration:underline}#topmenu{list-style:none;margin:0;padding:0}#topmenu li{display:inline-block}#maincontent{padding:0 1em 2em;max-width:60em;min-width:40em;margin:1em auto}#maincontent p{margin-bottom:1em}.gluon-section{margin:0;padding:0;border:none;margin-bottom:1.3em}.gluon-section:last-child{margin-bottom:0.7em}.gluon-section legend{font-size:1.4em;font-weight:bold;position:relative;padding:0;margin-bottom:0.5em}.gluon-section h2{margin:0em 0 0.5em -0.5em}.gluon-section h3{text-decoration:none;font-weight:bold;color:#555555;margin:0.25em;font-size:100%}.gluon-section-descr,.gluon-warning{margin-bottom:2em}.gluon-osm-map{width:100%;height:40em;margin-bottom:1em}input::placeholder{color:#aaaaaa}input::-webkit-input-placeholder{color:#aaaaaa}input[type=checkbox]{display:none}input[type=checkbox]+label{display:inline-block;position:relative;width:1em;height:1em;margin:0}input[type=checkbox]:checked+label::after{content:'✔';color:#dc0067;vertical-align:middle;position:absolute;top:50%;left:0;margin-top:-0.5em;width:100%;text-align:center;font-size:1.7em}input[type=checkbox][disabled]+label{background-color:#dcdcdc !important}input[type=radio]{display:none}input[type=radio]+label{display:inline-block;position:relative;width:0.8em;height:0.8em;padding:0.5em;margin:0.2em 0.2em 0.2em 0.1em;border:none;background:#ffe199;vertical-align:middle;border-radius:50%}input[type=radio]:checked+label::after{content:'•';color:#dc0067;vertical-align:middle;position:absolute;top:50%;left:0;margin-top:-0.4em;width:100%;text-align:center;font-size:2em}input[type=submit],input[type=reset],input[type=button]{cursor:pointer}select,input,textarea,input[type=checkbox]+label{color:#003247;border:none;background:#ffe199;border-radius:3pt;padding:0.5em;margin-top:1px;margin-bottom:2px;box-sizing:content-box;outline:0}.select-wrapper{position:relative;display:inline-block}.select-wrapper::before{position:absolute;z-index:1;right:0.05em;top:calc(2px + 0.1em);bottom:calc(2px + 0.1em);width:1.4em;border-left:0.05em solid rgba(0,0,0,0.25);pointer-events:none;background:url('data:image/svg+xml;utf8,') center/0.8em 0.5em no-repeat;content:''}.select-wrapper select{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer}option{color:#003247;background:#ffe199}select,input[type=text],input[type=password]{min-width:20em}.gluon-multi-list-option-descr{display:inline-block;vertical-align:top;margin-top:0.35em;margin-left:0.4em}.gluon-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:baseline;text-align:center;cursor:pointer;user-select:none;font-size:100%;padding:0.5em 1em;color:rgba(0,0,0,0.8);background-color:#E6E6E6;border:none;text-decoration:none;border-radius:2px;transition:0.1s linear box-shadow;margin-left:0.5em;background-repeat:no-repeat}.gluon-button::-moz-focus-inner{padding:0;border:0}.gluon-button:active{box-shadow:0 0 0 1px rgba(0,0,0,0.15) inset,0 0 6px rgba(0,0,0,0.2) inset}.gluon-button:focus{outline:0}.gluon-button:hover,.gluon-button:focus{background-image:linear-gradient(transparent, rgba(0,0,0,0.05) 40%, rgba(0,0,0,0.1))}.gluon-button[disabled]{border:none;background-image:none;opacity:0.40;cursor:not-allowed;box-shadow:none}.gluon-button-reset{background-color:#e30;color:#fff}.gluon-button-submit{background-color:#009ee0;color:#fff}.gluon-button-submit:active{background:grey}.gluon-input-invalid{background:#e30 !important;color:white}textarea{margin-left:-1px;margin-bottom:0.5em}.gluon-value{display:flex;flex-direction:row;margin-bottom:0.5em}.gluon-section-node .gluon-value:last-child{margin-bottom:0}.gluon-value-title{flex:2;text-align:right;padding-top:0.39em;padding-right:1em;font-weight:bold}.gluon-value-field{flex:3;position:relative}.gluon-value-field input,.gluon-value-field select,.gluon-value-field input+label{position:relative}.gluon-value-field-text{flex:3;padding-top:0.39em}.gluon-value-field-long{flex:10;position:relative;margin-top:0.65em}.gluon-value-field-long input,.gluon-value-field-long select,.gluon-value-field-long input+label{position:relative}.gluon-value-field-long-after{flex:2}.gluon-value-description{font-size:8pt}.gluon-form-descr{margin-bottom:1em}.gluon-form-descr:empty,.gluon-section-descr:empty,.gluon-warning:empty{display:none}.gluon-form-descr,.gluon-section-descr,.gluon-warning,.gluon-page-actions{padding:1em;background:#ececec}.gluon-page-actions{text-align:right;display:flex;flex-flow:row-reverse}.gluon-section-node{clear:both;position:relative;border:none}.gluon-value-error input,.gluon-value-error select{background-color:#ffcccc}.gluon-add::after,.gluon-remove::after{cursor:pointer;display:inline-block;text-align:center;vertical-align:middle;font-size:180%;width:1.2em;height:1em}.gluon-add{color:#008000;position:relative;left:21em}input+.gluon-add{left:0;top:0.04em}.gluon-add:first-child{top:0.53em;left:-0.08em}.gluon-add::after{content:'+'}.gluon-remove{color:#800000;position:relative;top:-0.03em}.gluon-remove::after{content:'–'}.gluon-warning{background:#ffe9b3}.error500{border:1px dotted #ff0000;background-color:#ffffff;color:#000000;padding:0.5em}.errorbox{border:1px solid #FF0000;background-color:#FFCCCC;padding:5px;margin-bottom:5px}.errorbox a{color:#000000 !important}.the-key{text-align:left;font-size:1.4em;background:#ffe9b3;border:3pt dashed #dc0067;margin-bottom:0.5em;padding:0.5em} diff --git a/package/gluon-config-mode-theme/sass/gluon.scss b/package/gluon-config-mode-theme/sass/gluon.scss index 575af4f926..a63156e430 100644 --- a/package/gluon-config-mode-theme/sass/gluon.scss +++ b/package/gluon-config-mode-theme/sass/gluon.scss @@ -273,6 +273,10 @@ input[type=checkbox] { text-align: center; font-size: 1.7em; } + + &[disabled] + label { + background-color: #dcdcdc !important; + } } input[type=radio] { @@ -366,6 +370,13 @@ input[type=password] { min-width: 20em; } +.gluon-multi-list-option-descr { + display: inline-block; + vertical-align: top; + margin-top: 0.35em; + margin-left: 0.4em; +} + .gluon-button { @include button; diff --git a/package/gluon-core/Makefile b/package/gluon-core/Makefile index 93b2d599b4..93a9a9b69a 100644 --- a/package/gluon-core/Makefile +++ b/package/gluon-core/Makefile @@ -11,7 +11,7 @@ define Package/gluon-core TITLE:=Base files of Gluon DEPENDS:= \ +gluon-site +libgluonutil +libiwinfo-lua +lua-platform-info +lua-simple-uci +lua-hash +lua-jsonc \ - +luabitop +luaposix +vxlan +odhcp6c +firewall +pretty-hostname + +luabitop +luaposix +vxlan +odhcp6c +firewall4 +pretty-hostname endef define Package/gluon-core/description @@ -27,6 +27,10 @@ config GLUON_VERSION config GLUON_MINIFY bool "Minify Gluon scripts" default y + +config GLUON_BASE + bool "Exclude everything except the very most basic" + default n endef define Package/gluon-core/conffiles @@ -38,6 +42,10 @@ define Package/gluon-core/install $(INSTALL_DIR) $(1)/lib/gluon echo '$(call qstrip,$(CONFIG_GLUON_VERSION))' > $(1)/lib/gluon/gluon-version + +ifdef CONFIG_GLUON_BASE + find $(1)/lib/gluon/upgrade/ -type f -and ! -name "998-commit" -and ! -name "005-set-domain" -and ! -name "010-primary-mac" -and ! -name "030-system" -and ! -name "120-ntp-servers" -and ! -name "150-poe-passthrough" -and ! -name "300-firewall-rules" -and ! -name "820-dns-config" -and ! -name "999-version" -delete +endif endef $(eval $(call BuildPackageGluon,gluon-core)) diff --git a/package/gluon-core/check_site.lua b/package/gluon-core/check_site.lua index 06b9ac59d0..4c6d39bc2a 100644 --- a/package/gluon-core/check_site.lua +++ b/package/gluon-core/check_site.lua @@ -27,7 +27,7 @@ need_string(in_site({'timezone'})) need_string_array({'ntp_servers'}, false) need_string_match(in_domain({'prefix4'}), '^%d+.%d+.%d+.%d+/%d+$', false) -need_string_match(in_domain({'prefix6'}), '^[%x:]+/64$') +need_string_match(in_domain({'prefix6'}), '^[%x:]+/%d+$') need_string_array_match(in_domain({'extra_prefixes6'}), '^[%x:]+/%d+$', false) local supported_rates = {6000, 9000, 12000, 18000, 24000, 36000, 48000, 54000} @@ -52,7 +52,14 @@ for _, config in ipairs({'wifi24', 'wifi5'}) do obsolete({config, 'supported_rates'}, '802.11b rates are disabled by default.') obsolete({config, 'basic_rate'}, '802.11b rates are disabled by default.') - obsolete({config, 'ibss'}, 'IBSS support has been dropped.') + + if need_table({config, 'ibss'}, nil, false) then + need_string_match(in_domain({config, 'ibss', 'ssid'}), '^' .. ('.?'):rep(32) .. '$') + -- need_string_match(in_domain({config, 'ibss', 'bssid'}), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$') + need_one_of({config, 'ibss', 'mcast_rate'}, supported_rates, false) + need_number({config, 'ibss', 'vlan'}, false) + need_boolean({config, 'ibss', 'disabled'}, false) + end if need_table({config, 'mesh'}, nil, false) then need_string_match(in_domain({config, 'mesh', 'id'}), '^' .. ('.?'):rep(32) .. '$') @@ -65,7 +72,7 @@ end need_boolean(in_site({'poe_passthrough'}), false) if need_table({'dns'}, nil, false) then - need_string_array_match({'dns', 'servers'}, '^[%x:]+$') + -- need_string_array_match({'dns', 'servers'}, '^[%x:]+$') need_number({'dns', 'cacheentries'}, false) end @@ -77,7 +84,11 @@ need_boolean(in_domain({'mesh', 'vxlan'}), false) local interfaces_roles = {'client', 'uplink', 'mesh'} for _, config in ipairs({'wan', 'lan', 'single'}) do - need_array_of(in_site({'interfaces', config, 'default_roles'}), interfaces_roles, false) + local default_roles = in_site({'interfaces', config, 'default_roles'}) + + need_array_of(default_roles, interfaces_roles, false) + need_array_elements_exclusive(default_roles, 'client', 'mesh', false) + need_array_elements_exclusive(default_roles, 'client', 'uplink', false) end obsolete({'mesh_on_wan'}, 'Use interfaces.wan.default_roles.') diff --git a/package/gluon-core/files/lib/netifd/proto/gluon_mesh.sh b/package/gluon-core/files/lib/netifd/proto/gluon_mesh.sh index 52d6abec7d..15ee94c2f4 100755 --- a/package/gluon-core/files/lib/netifd/proto/gluon_mesh.sh +++ b/package/gluon-core/files/lib/netifd/proto/gluon_mesh.sh @@ -7,14 +7,16 @@ init_proto "$@" proto_gluon_mesh_init_config() { proto_config_add_boolean fixed_mtu proto_config_add_boolean transitive + proto_config_add_string ipaddr + proto_config_add_string ip6addr } proto_gluon_mesh_setup() { export CONFIG="$1" export IFNAME="$2" - local fixed_mtu transitive - json_get_vars fixed_mtu transitive + local fixed_mtu transitive ipaddr ip6addr + json_get_vars fixed_mtu transitive ipaddr ip6addr export FIXED_MTU="${fixed_mtu:-0}" export TRANSITIVE="${transitive:-0}" @@ -28,10 +30,23 @@ proto_gluon_mesh_setup() { proto_add_data json_add_boolean fixed_mtu "$FIXED_MTU" json_add_boolean transitive "$TRANSITIVE" + if [ ! -z "$ipaddr" ]; then + json_add_string ipaddr "$ipaddr" + fi + if [ ! -z "$ip6addr" ]; then + json_add_string ip6addr "$ip6addr" + fi [ "$IFNAME" != 'br-wan' ] && json_add_string zone 'mesh' proto_close_data proto_send_update "$CONFIG" + if [ ! -z "$ipaddr" ]; then + ip addr add "$ipaddr" dev "$IFNAME" + fi + if [ ! -z "$ip6addr" ]; then + ip addr add "$ip6addr" dev "$IFNAME" + fi + for script in /lib/gluon/core/mesh/post-setup.d/*; do [ ! -x "$script" ] || "$script" done @@ -41,6 +56,16 @@ proto_gluon_mesh_teardown() { export CONFIG="$1" export IFNAME="$2" + local ipaddr ip6addr + json_get_vars ipaddr ip6addr + + if [ ! -z "$ipaddr" ]; then + ip addr del "$ipaddr" dev "$IFNAME" + fi + if [ ! -z "$ip6addr" ]; then + ip addr del "$ip6addr" dev "$IFNAME" + fi + for script in /lib/gluon/core/mesh/teardown.d/*; do [ ! -x "$script" ] || "$script" done diff --git a/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh index d33c8a2741..8d9ab2b7e9 100755 --- a/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh +++ b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh @@ -9,6 +9,8 @@ proto_gluon_wired_init_config() { proto_config_add_int index proto_config_add_boolean vxlan proto_config_add_string vxpeer6addr + proto_config_add_string ipaddr + proto_config_add_string ip6addr } xor2() { @@ -46,8 +48,8 @@ proto_gluon_wired_setup() { local meshif="$config" - local transitive index vxlan vxpeer6addr - json_get_vars transitive index vxlan vxpeer6addr + local transitive index vxlan vxpeer6addr ipaddr ip6addr + json_get_vars transitive index vxlan vxpeer6addr ipaddr ip6addr # default args [ -z "$vxlan" ] && vxlan=1 @@ -79,6 +81,12 @@ proto_gluon_wired_setup() { json_add_string ifname "@${meshif}" json_add_string proto 'gluon_mesh' json_add_boolean fixed_mtu 1 + if [ ! -z "$ipaddr" ]; then + json_add_string ipaddr "$ipaddr" + fi + if [ ! -z "$ip6addr" ]; then + json_add_string ip6addr "$ip6addr" + fi [ -n "$transitive" ] && json_add_boolean transitive "$transitive" json_close_object ubus call network add_dynamic "$(json_dump)" diff --git a/package/gluon-core/luasrc/lib/gluon/check-site.lua b/package/gluon-core/luasrc/lib/gluon/check-site.lua index 148f4968f7..cde55f442d 100644 --- a/package/gluon-core/luasrc/lib/gluon/check-site.lua +++ b/package/gluon-core/luasrc/lib/gluon/check-site.lua @@ -55,6 +55,14 @@ local function merge(a, b) return m end +local function contains(table, val) + for i=1,#table do + if table[i] == val then + return true + end + end + return false +end local function path_to_string(path) if path.is_value then @@ -370,6 +378,21 @@ function M.need_array_of(path, array, required) return M.need_array(path, function(e) M.need_one_of(e, array) end, required) end +function M.need_array_elements_exclusive(path, a, b, required) + local val = need_type(path, 'table', required, 'be an array') + if not val then + return nil + end + + if contains(val, a) and contains(val, b) then + config_error(conf_src(path), + 'expected %s to contain only one of the elements %s and %s, but not both.', + path_to_string(path), format(a), format(b)) + end + + return val +end + function M.need_chanlist(path, channels, required) local valid_chanlist = check_chanlist(channels) return M.need(path, valid_chanlist, required, diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/021-interface-roles b/package/gluon-core/luasrc/lib/gluon/upgrade/021-interface-roles index 5d27fb708b..6e39219a69 100755 --- a/package/gluon-core/luasrc/lib/gluon/upgrade/021-interface-roles +++ b/package/gluon-core/luasrc/lib/gluon/upgrade/021-interface-roles @@ -63,4 +63,19 @@ for iface in pairs(interfaces) do end end +-- Fix invalid role configurations + +uci:foreach('gluon', 'interface', function(interface) + + local function has_role(role) + return util.contains(interface.role, role) + end + + if has_role('client') and (has_role('mesh') or has_role('uplink')) then + -- remove 'client' role + util.remove_from_set(interface.role, 'client') + uci:set('gluon', interface['.name'], 'role', interface.role) + end +end) + uci:save('gluon') diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/110-network b/package/gluon-core/luasrc/lib/gluon/upgrade/110-network index d1493d26a7..c51735df69 100755 --- a/package/gluon-core/luasrc/lib/gluon/upgrade/110-network +++ b/package/gluon-core/luasrc/lib/gluon/upgrade/110-network @@ -46,9 +46,24 @@ uci:section('network', 'interface', 'wan', { igmp_snooping = true, multicast_querier = false, peerdns = false, + ip4table = 1, auto = true, }) +uci:section('network', 'route4', 'wan4_unreachable', { + type = 'unreachable', + interface = 'loopback', + target = '0.0.0.0/0', + gateway = '0.0.0.0', + table = 1, + metric = 65535, +}) + +uci:section('network', 'rule', 'wan4_lookup', { + mark = '0x01/0x01', + lookup = 1, +}) + uci:section('network', 'interface', 'wan6', { proto = wan6.proto or 'dhcpv6', ip6addr = wan6.ip6addr, diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless b/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless index 9550a887d3..4d1309dbd2 100755 --- a/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless +++ b/package/gluon-core/luasrc/lib/gluon/upgrade/200-wireless @@ -16,7 +16,7 @@ if not sysconfig.gluon_version then -- This is needed to distribute devices which have radios -- capable of operating in the 2.4 GHz and 5 GHz band need -- to be distributed evenly. - local radio_band_count = {band24=0, band5=0} + local radio_band_count = {band24=0, band5=0, band60=0} wireless.foreach_radio(uci, function(radio) local hwmodes = iwinfo.nl80211.hwmodelist(wireless.find_phy(radio)) if hwmodes.g and not (hwmodes.a or hwmodes.ac) then @@ -25,6 +25,8 @@ if not sysconfig.gluon_version then elseif (hwmodes.a or hwmodes.ac) and not hwmodes.g then -- 5 GHz radio_band_count["band5"] = radio_band_count["band5"] + 1 + elseif (hwmodes.ad) then + radio_band_count["band60"] = radio_band_count["band60"] + 1 end end) @@ -53,7 +55,16 @@ local function is_outdoor() end local function get_channel(radio, config) - if radio.band == '5g' and is_outdoor() then + local channel + if config.channel_adjustable(false) and uci:get_bool('wireless', radio['.name'], 'configured') and not (radio.band == '5g' and is_outdoor()) then + -- preserved channel always wins + if radio.channel == 'auto' then + -- if channel was auto and is now channel_adjustable use the default from config + return config.channel() + end + + return radio.channel + elseif radio.band == '5g' and is_outdoor() then -- actual channel will be picked and probed from chanlist return 'auto' end @@ -102,13 +113,64 @@ local function first_non_nil(first, ...) return first_non_nil(...) end - local function delete_ibss(radio_name) local name = 'ibss_' .. radio_name uci:delete('wireless', name) end +local function configure_ibss(config, radio, index, suffix, disabled) + local radio_name = radio['.name'] + local name = 'ibss_' .. radio_name + + uci:delete('wireless', name) + + if not config then + return + end + + local macaddr = wireless.get_wlan_mac(uci, radio, index, 3) + if not macaddr then + return + end + + local ssid = config.ssid + local bssid = config.bssid + + if radio.wireless_config.include_channel_in_id then + ssid = string.format(ssid, radio.current_channel) + bssid = string.format(bssid, radio.current_channel) + end + + if config.vlan then + uci:section('network', 'interface', name, { + ifname = suffix and 'ibss' .. suffix, + proto = 'none', + }) + + uci:section('network', 'interface', name .. '_vlan', { + ifname = '@' .. name .. '.' .. config.vlan, + proto = 'gluon_mesh', + }) + else + uci:section('network', 'interface', name, { + proto = 'gluon_mesh', + ifname = suffix and 'ibss' .. suffix, + }) + end + + uci:section('wireless', 'wifi-iface', name, { + device = radio_name, + network = name, + mode = 'adhoc', + macaddr = macaddr, + ssid = ssid, + bssid = bssid, + ifname = suffix and 'ibss' .. suffix, + disabled = disabled, + }) +end + local function configure_mesh(config, radio, index, suffix, disabled) local radio_name = radio['.name'] local name = 'mesh_' .. radio_name @@ -127,15 +189,22 @@ local function configure_mesh(config, radio, index, suffix, disabled) return end + local id = config.id + + if radio.wireless_config.include_channel_in_id then + id = string.format(id, radio.current_channel) + end + uci:section('network', 'interface', name, { proto = 'gluon_mesh', + ifname = suffix and 'mesh' .. suffix, }) uci:section('wireless', 'wifi-iface', name, { device = radio_name, network = name, mode = 'mesh', - mesh_id = config.id, + mesh_id = id, mesh_fwding = false, macaddr = macaddr, mcast_rate = config.mcast_rate, @@ -146,6 +215,49 @@ local function configure_mesh(config, radio, index, suffix, disabled) }) end +local function configure_p2p(config, radio, index, suffix, disabled) + local radio_name = radio['.name'] + local name = 'p2p_' .. radio_name + + local macfilter = uci:get('wireless', name, 'macfilter') + local maclist = uci:get('wireless', name, 'maclist') + + local ssid = uci:get('wireless', name, 'ssid') or 'gluon-' .. radio.band .. '-p2p-' .. string.sub(string.gsub(sysconfig.primary_mac, ':', ''), 8) + local mode = uci:get('wireless', name, 'mode') or 'ap' + + uci:delete('wireless', name) + + if not config then + return + end + + local macaddr = wireless.get_wlan_mac(uci, radio, index, 2) + if not macaddr then + return + end + + -- FIXME: static ip stuffs + + uci:section('network', 'interface', name, { + proto = 'gluon_mesh', + ifname = suffix and 'p2p' .. suffix, + }) + + uci:section('wireless', 'wifi-iface', name, { + device = radio_name, + network = name, + ssid = ssid, + mode = mode, + macaddr = macaddr, + -- mcast_rate = config.mcast_rate, + ifname = suffix and 'p2p' .. suffix, + encryption = 'none', + disabled = disabled, + macfilter = macfilter, + maclist = maclist, + }) +end + local function fixup_wan(radio, index) local radio_name = radio['.name'] local name = 'wan_' .. radio_name @@ -166,19 +278,44 @@ local function configure_mesh_wireless(radio, index, config, disabled) local radio_name = radio['.name'] local suffix = radio_name:match('^radio(%d+)$') - configure_mesh(config.mesh(), radio, index, suffix, + local ibss_disabled = is_disabled('ibss_' .. radio_name) + local p2p_disabled = is_disabled('p2p_' .. radio_name) + local mesh_disabled = is_disabled('mesh_' .. radio_name) + + configure_p2p(config.p2p(), radio, index, suffix, first_non_nil( - disabled, - is_disabled('mesh_' .. radio_name), - config.mesh.disabled(false) + p2p_disabled, + config.p2p.disabled(true) ) ) + + if radio.band == '2g' then + configure_ibss(config.ibss(), radio, index, suffix, + first_non_nil( + ibss_disabled, + config.ibss.disabled(false) + ) + ) + else + delete_ibss(radio['.name']) + end + + if radio.band ~= '60g' then + configure_mesh(config.mesh(), radio, index, suffix, + first_non_nil( + disabled, + mesh_disabled, + config.mesh.disabled(false) + ) + ) + end end local function set_channels(radio, radio_name, config) if wireless.preserve_channels(uci) then return end + local channel = get_channel(radio, config) uci:set('wireless', radio_name, 'channel', channel) @@ -192,8 +329,6 @@ end wireless.foreach_radio(uci, function(radio, index, config) local radio_name = radio['.name'] - delete_ibss(radio_name) - if not config() then uci:set('wireless', radio_name, 'disabled', true) return @@ -207,6 +342,9 @@ wireless.foreach_radio(uci, function(radio, index, config) local htmode = get_htmode(radio) local beacon_interval = config.beacon_interval() + radio.current_channel = get_channel(radio, config) + radio.wireless_config = config + uci:delete('wireless', radio_name, 'disabled') set_channels(radio, radio_name, config) @@ -235,16 +373,24 @@ wireless.foreach_radio(uci, function(radio, index, config) uci:delete('wireless', radio_name, 'country3') configure_mesh_wireless(radio, index, config) end + elseif (band == '60g') then + configure_mesh_wireless(radio, index, config, true) end uci:set('wireless', radio_name, 'beacon_int', beacon_interval) + uci:set('wireless', radio_name, 'configured', true) fixup_wan(radio, index) end) if uci:get('system', 'rssid_wlan0') then - uci:set('system', 'rssid_wlan0', 'dev', 'mesh0') + if uci:get('wireless', 'mesh_radio0') then + uci:set('system', 'rssid_wlan0', 'dev', 'mesh0') + else + uci:set('system', 'rssid_wlan0', 'dev', 'ibss0') + end + uci:save('system') end diff --git a/package/gluon-core/luasrc/lib/gluon/upgrade/500-opkg b/package/gluon-core/luasrc/lib/gluon/upgrade/500-opkg index bf0d15dcb7..5b2da5255d 100755 --- a/package/gluon-core/luasrc/lib/gluon/upgrade/500-opkg +++ b/package/gluon-core/luasrc/lib/gluon/upgrade/500-opkg @@ -37,7 +37,6 @@ local function replace_patterns(url) end -local openwrt = site.opkg.openwrt() local extra = site.opkg.extra({}) @@ -52,8 +51,6 @@ for _, line in ipairs(distfeeds) do local name = line:match('^src/gz%s' .. prefix .. '(%S+)%s') if name == 'core' then f:write('# ' .. line .. '\n') - elseif name and openwrt then - f:write(string.format('src/gz %s %s/%s\n', prefix .. name, replace_patterns(openwrt), name)) else f:write(line .. '\n') end diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua index 6debe8c5dd..6f5c217448 100644 --- a/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua +++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/platform.lua @@ -37,6 +37,7 @@ function M.is_outdoor_device() 'tplink,cpe510-v3', 'tplink,cpe710-v1', 'tplink,eap225-outdoor-v1', + 'tplink,eap225-outdoor-v3', 'tplink,wbs210-v1', 'tplink,wbs210-v2', 'tplink,wbs510-v1', diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua index d516106677..dd66739575 100644 --- a/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua +++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua @@ -147,13 +147,26 @@ function M.get_role_interfaces(uci, role, exclusive) local ret = {} local function add(name) + local subindex = nil + -- Interface names with a / prefix refer to sysconfig interfaces -- (lan_ifname/wan_ifname/single_ifname) if string.sub(name, 1, 1) == '/' then + subindex = tonumber(string.match(name, "%[(%d+)%]")) + if subindex then + -- handle something like "/lan[10]": + name = string.gsub(name, "%[%d+%]", "") + end + name = sysconfig[string.sub(name, 2) .. '_ifname'] or '' end + local i = 0 for iface in string.gmatch(name, '%S+') do - M.add_to_set(ret, iface) + if not subindex or subindex == i then + M.add_to_set(ret, iface) + end + + i = i + 1 end end @@ -186,7 +199,7 @@ end -- 6: owe1 -- 7: wan_radio1 (private WLAN); mesh VPN function M.generate_mac(i) - if i > 7 or i < 0 then return nil end -- max allowed id (0b111) + if i > 15 or i < 0 then return nil end -- max allowed id (0b111) local hashed = string.sub(hash.md5(sysconfig.primary_mac), 0, 12) local m1, m2, m3, m4, m5, m6 = string.match(hashed, '(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)') @@ -201,7 +214,7 @@ function M.generate_mac(i) -- vary on a single hardware interface, since some chips are using -- a hardware MAC filter. (e.g 'rt305x') - m6 = bit.band(m6, 0xF8) -- zero the last three bits (space needed for counting) + m6 = bit.band(m6, 0xF0) -- zero the last four bits (space needed for counting) m6 = m6 + i -- add virtual interface id return string.format('%02x:%s:%s:%s:%s:%02x', m1, m2, m3, m4, m5, m6) diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/wireless.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/wireless.lua index a46fc674d9..245d58e889 100644 --- a/package/gluon-core/luasrc/usr/lib/lua/gluon/wireless.lua +++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/wireless.lua @@ -118,6 +118,8 @@ function M.foreach_radio(uci, f) f(radio, index, site.wifi24) elseif band == '5g' then f(radio, index, site.wifi5) + elseif band == '60g' then + f(radio, index, site.wifi60) end end end @@ -174,4 +176,17 @@ function M.device_uses_11a(uci) return ret end +function M.device_uses_ad(uci) + local ret = false + + uci:foreach('wireless', 'wifi-device', function(radio) + if radio.band == '60g' then + ret = true + return false + end + end) + + return ret +end + return M diff --git a/package/gluon-ebtables-filter-multicast/Makefile b/package/gluon-ebtables-filter-multicast/Makefile deleted file mode 100644 index 92b2be2ae3..0000000000 --- a/package/gluon-ebtables-filter-multicast/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=gluon-ebtables-filter-multicast - -include ../gluon.mk - -define Package/gluon-ebtables-filter-multicast - TITLE:=Ebtables filters for multicast packets - DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv -endef - -define Package/gluon-ebtables-filter-multicast/description - Gluon community wifi mesh firmware framework: Ebtables filters for multicast packets - - These filters drop non-essential multicast traffic before it enters the mesh. - - Allowed protocols are: DHCP, DHCPv6, ARP, ICMP, ICMPv6, BitTorrent local peer discovery, BABEL and OSPF -endef - -$(eval $(call BuildPackageGluon,gluon-ebtables-filter-multicast)) diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-arp b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-arp deleted file mode 100644 index 927776a8bc..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-arp +++ /dev/null @@ -1,7 +0,0 @@ --- Bridge loop avoidance -rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-gratuitous --arp-mac-dst ff:43:05:00:00:00/ff:ff:ff:fc:00:00 -j RETURN' -rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-gratuitous --arp-mac-dst ff:43:05:05:00:00/ff:ff:ff:ff:00:00 -j RETURN' - -rule 'MULTICAST_OUT -p ARP --arp-opcode Reply --arp-ip-src 0.0.0.0 -j DROP' -rule 'MULTICAST_OUT -p ARP --arp-opcode Request --arp-ip-dst 0.0.0.0 -j DROP' -rule 'MULTICAST_OUT -p ARP -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-babel b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-babel deleted file mode 100644 index d5b81771ac..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-babel +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 6696 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-btlpd b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-btlpd deleted file mode 100644 index 20b709f81b..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-btlpd +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv4 --ip-destination 239.192.152.143 --ip-protocol udp --ip-destination-port 6771 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-dhcpv4 b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-dhcpv4 deleted file mode 100644 index 2fca222309..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-dhcpv4 +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv4 --ip-protocol udp --ip-destination-port 67 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-dhcpv6 b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-dhcpv6 deleted file mode 100644 index 6d7f0f557e..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-dhcpv6 +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-icmpv6 b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-icmpv6 deleted file mode 100644 index 0058ed86b8..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-icmpv6 +++ /dev/null @@ -1,3 +0,0 @@ -rule 'MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type echo-request -j RETURN' -rule 'MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 139 -j RETURN' -- ICMP Node Information Query -rule 'MULTICAST_OUT_ICMPV6 -j ACCEPT' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-igmp b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-igmp deleted file mode 100644 index 2d3814ae8a..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-igmp +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv4 --ip-protocol igmp -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-ospf b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-ospf deleted file mode 100644 index da928d4b36..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-ospf +++ /dev/null @@ -1,2 +0,0 @@ -rule 'MULTICAST_OUT -p IPv4 --ip-protocol ospf -j RETURN' -rule 'MULTICAST_OUT -p IPv6 --ip6-protocol ospf -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-respondd b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-respondd deleted file mode 100644 index 7df37ec984..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-respondd +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination-port 1001 --ip6-dst ff05::2:1001 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-ripng b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-ripng deleted file mode 100644 index 37d3187742..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/110-mcast-allow-ripng +++ /dev/null @@ -1 +0,0 @@ -rule 'MULTICAST_OUT -p IPv6 --ip6-protocol udp --ip6-destination ff02::9 --ip6-destination-port 521 -j RETURN' diff --git a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/355-mcast-drop b/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/355-mcast-drop deleted file mode 100644 index a47dda7eb6..0000000000 --- a/package/gluon-ebtables-filter-multicast/luasrc/lib/gluon/ebtables/355-mcast-drop +++ /dev/null @@ -1,3 +0,0 @@ -rule ('MULTICAST_OUT -p IPv6 --ip6-dst ff02::1/128 -j DROP') -rule ('MULTICAST_OUT -p IPv6 --ip6-dst ff00::/8 -j mark --set-mark 0x4 --mark-target RETURN') -rule ('MULTICAST_OUT -j DROP') diff --git a/package/gluon-ebtables-filter-ra-dhcp/Makefile b/package/gluon-ebtables-filter-ra-dhcp/Makefile deleted file mode 100644 index bc52747a33..0000000000 --- a/package/gluon-ebtables-filter-ra-dhcp/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=gluon-ebtables-filter-ra-dhcp - -include ../gluon.mk - -define Package/gluon-ebtables-filter-ra-dhcp - TITLE:=Ebtables filters for Router Advertisement and DHCP packets - DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv -endef - -define Package/gluon-ebtables-filter-ra-dhcp/description - Gluon community wifi mesh firmware framework: Ebtables filters for Router Advertisement and DHCP packets - - These filters ensure that RA and DHCP packets are only forwarded from the mesh into the - client network, and not vice-versa. -endef - -$(eval $(call BuildPackageGluon,gluon-ebtables-filter-ra-dhcp)) diff --git a/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-dhcpv4 b/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-dhcpv4 deleted file mode 100644 index 87b4bd7f9f..0000000000 --- a/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-dhcpv4 +++ /dev/null @@ -1,11 +0,0 @@ -local uci = require('simple-uci').cursor() - -local gw_mode = uci:get('network', 'gluon_bat0', 'gw_mode') - -if gw_mode ~= 'server' then - rule 'FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 67 -j OUT_ONLY' - rule 'OUTPUT -p IPv4 --ip-protocol udp --ip-destination-port 67 -j OUT_ONLY' - - rule 'FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 68 -j IN_ONLY' - rule 'INPUT -p IPv4 --ip-protocol udp --ip-destination-port 68 -j IN_ONLY' -end diff --git a/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-dhcpv6 b/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-dhcpv6 deleted file mode 100644 index 470a76489c..0000000000 --- a/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-dhcpv6 +++ /dev/null @@ -1,5 +0,0 @@ -rule 'FORWARD -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j OUT_ONLY' -rule 'OUTPUT -p IPv6 --ip6-protocol udp --ip6-destination-port 547 -j OUT_ONLY' - -rule 'FORWARD -p IPv6 --ip6-protocol udp --ip6-destination-port 546 -j IN_ONLY' -rule 'INPUT -p IPv6 --ip6-protocol udp --ip6-destination-port 546 -j IN_ONLY' diff --git a/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-radv b/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-radv deleted file mode 100644 index b34d4c76de..0000000000 --- a/package/gluon-ebtables-filter-ra-dhcp/luasrc/lib/gluon/ebtables/200-dir-radv +++ /dev/null @@ -1,5 +0,0 @@ -rule 'FORWARD -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -j OUT_ONLY' -rule 'OUTPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -j OUT_ONLY' - -rule 'FORWARD -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j IN_ONLY' -rule 'INPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j IN_ONLY' diff --git a/package/gluon-ebtables-limit-arp/luasrc/lib/gluon/ebtables/100-arp-limit-chains b/package/gluon-ebtables-limit-arp/luasrc/lib/gluon/ebtables/100-arp-limit-chains deleted file mode 100644 index b39b35c89a..0000000000 --- a/package/gluon-ebtables-limit-arp/luasrc/lib/gluon/ebtables/100-arp-limit-chains +++ /dev/null @@ -1,3 +0,0 @@ -chain('ARP_LIMIT', 'DROP') -chain('ARP_LIMIT_DATCHECK', 'RETURN') -chain('ARP_LIMIT_TLCHECK', 'RETURN') diff --git a/package/gluon-ebtables-limit-arp/luasrc/lib/gluon/ebtables/320-arp-limit-rules b/package/gluon-ebtables-limit-arp/luasrc/lib/gluon/ebtables/320-arp-limit-rules deleted file mode 100644 index 416bdd9668..0000000000 --- a/package/gluon-ebtables-limit-arp/luasrc/lib/gluon/ebtables/320-arp-limit-rules +++ /dev/null @@ -1,6 +0,0 @@ -rule('ARP_LIMIT -j ARP_LIMIT_DATCHECK') -rule('ARP_LIMIT --mark 0x2/0x2 -j RETURN') -rule('ARP_LIMIT -j ARP_LIMIT_TLCHECK') -rule('ARP_LIMIT --limit 1/sec --limit-burst 50 -j RETURN') - -rule('FORWARD -p ARP --logical-out br-client -o bat0 --arp-op Request -j ARP_LIMIT') diff --git a/package/gluon-ebtables-source-filter/Makefile b/package/gluon-ebtables-source-filter/Makefile deleted file mode 100644 index 17377e1fac..0000000000 --- a/package/gluon-ebtables-source-filter/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=gluon-ebtables-source-filter - -include ../gluon.mk - -define Package/gluon-ebtables-source-filter - TITLE:=Ebtables rules to filter unreasonable L2 traffic. - DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv -endef - -define Package/gluon-ebtables-source-filter/description - This package adds an additional layer-2 filter-ruleset to prevent unreasonable - traffic entering the network via the nodes. -endef - -$(eval $(call BuildPackageGluon,gluon-ebtables-source-filter)) diff --git a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/100-local-forward-chain b/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/100-local-forward-chain deleted file mode 100644 index b9f4467d9b..0000000000 --- a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/100-local-forward-chain +++ /dev/null @@ -1 +0,0 @@ -chain('LOCAL_FORWARD', 'DROP') diff --git a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-arp b/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-arp deleted file mode 100644 index 06436cf227..0000000000 --- a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-arp +++ /dev/null @@ -1,6 +0,0 @@ -local prefix4 = require('gluon.site').prefix4() - -if prefix4 then - rule('LOCAL_FORWARD -p ARP --arp-ip-src ' .. prefix4 .. ' --arp-ip-dst ' .. prefix4 .. ' -j RETURN') - rule('LOCAL_FORWARD -p ARP --arp-ip-src 0.0.0.0 --arp-ip-dst ' .. prefix4 .. ' -j RETURN') -end diff --git a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-ipv4 b/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-ipv4 deleted file mode 100644 index e712c5fbe2..0000000000 --- a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-ipv4 +++ /dev/null @@ -1,6 +0,0 @@ -local prefix4 = require('gluon.site').prefix4() - -if prefix4 then - rule('LOCAL_FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 67 -j RETURN') - rule('LOCAL_FORWARD -p IPv4 --ip-src ' .. prefix4 .. ' -j RETURN') -end diff --git a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-ipv6 b/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-ipv6 deleted file mode 100644 index f6a197477a..0000000000 --- a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/110-local-forward-allow-ipv6 +++ /dev/null @@ -1,9 +0,0 @@ -local site = require 'gluon.site' - -rule('LOCAL_FORWARD -p IPv6 --ip6-src fe80::/64 -j RETURN') -rule('LOCAL_FORWARD -p IPv6 --ip6-src ::/128 --ip6-proto ipv6-icmp -j RETURN') -rule('LOCAL_FORWARD -p IPv6 --ip6-src ' .. site.prefix6() .. ' -j RETURN') - -for _, prefix in ipairs(site.extra_prefixes6({})) do - rule('LOCAL_FORWARD -p IPv6 --ip6-src ' .. prefix .. ' -j RETURN') -end diff --git a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/300-local-forward-rules b/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/300-local-forward-rules deleted file mode 100644 index 6c5a925728..0000000000 --- a/package/gluon-ebtables-source-filter/luasrc/lib/gluon/ebtables/300-local-forward-rules +++ /dev/null @@ -1 +0,0 @@ -rule('FORWARD --logical-in br-client -i ! bat0 -j LOCAL_FORWARD') diff --git a/package/gluon-ebtables/Makefile b/package/gluon-ebtables/Makefile deleted file mode 100644 index e69a83bf17..0000000000 --- a/package/gluon-ebtables/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -include $(TOPDIR)/rules.mk - -PKG_NAME:=gluon-ebtables - -include ../gluon.mk - -define Package/gluon-ebtables - TITLE:=Ebtables support - DEPENDS:=+gluon-core +ebtables-tiny \ - +kmod-ebtables +kmod-ebtables-ipv4 +kmod-ebtables-ipv6 -endef - -define Package/gluon-ebtables/description - Gluon community wifi mesh firmware framework: ebtables support -endef - -$(eval $(call BuildPackageGluon,gluon-ebtables)) diff --git a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables b/package/gluon-ebtables/files/etc/init.d/gluon-ebtables deleted file mode 100755 index 60add180f6..0000000000 --- a/package/gluon-ebtables/files/etc/init.d/gluon-ebtables +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/sh /etc/rc.common -# Copyright (C) 2013 Project Gluon -# -# Firewall script for inserting and removing ebtables rules. -# -# Example format, for filtering any IPv4 multicast packets to the SSDP UDP port: -# rule FORWARD --logical-out br-client -d Multicast -p IPv4 --ip-protocol udp --ip-destination-port 5355 -j DROP -# -# Removing all rules: -# $ /etc/init.d/gluon-ebtables stop -# Inserting all rules: -# $ /etc/init.d/gluon-ebtables start -# Inserting a specific rule file: -# $ /etc/init.d/gluon-ebtables start /lib/gluon/ebtables/100-mcast-chain -# Removing a specific rule file: -# $ /etc/init.d/gluon-ebtables stop /lib/gluon/ebtables/100-mcast-chain - - -START=19 -STOP=91 - - -exec_file() { - local file="$1" - - /usr/bin/lua -e " - function rule(command, table) - table = table or 'filter' - os.execute($EBTABLES_RULE) - end - function chain(name, policy, table) - table = table or 'filter' - os.execute($EBTABLES_CHAIN) - end - - " "$file" -} - -exec_all() { - local sort_arg="$1" - - local old_ifs="$IFS" - IFS=' -' - for file in `find /lib/gluon/ebtables -type f | sort $sort_arg`; do - exec_file "$file" - done - IFS="$old_ifs" -} - - -start() { - ( - export EBTABLES_RULE='"ebtables-tiny -t " .. table .. " -A " .. command' - export EBTABLES_CHAIN='"ebtables-tiny -t " .. table .. " -N " .. name .. " -P " .. policy' - - # Contains /var/lib/ebtables/lock for '--concurrent' - [ ! -d "/var/lib/ebtables" ] && \ - mkdir -p /var/lib/ebtables - - if [ -z "$1" ]; then - exec_all '' - else - exec_file "$1" - fi - ) -} - -stop() { - ( - export EBTABLES_RULE='"ebtables-tiny -t " .. table .. " -D " .. command' - export EBTABLES_CHAIN='"ebtables-tiny -t " .. table .. " -X " .. name' - - if [ -z "$1" ]; then - exec_all '-r' - else - exec_file "$1" - fi - ) -} diff --git a/package/gluon-ebtables/files/lib/gluon/reload.d/381-gluon-ebtables-stop b/package/gluon-ebtables/files/lib/gluon/reload.d/381-gluon-ebtables-stop deleted file mode 100755 index ab714cc235..0000000000 --- a/package/gluon-ebtables/files/lib/gluon/reload.d/381-gluon-ebtables-stop +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -/etc/init.d/gluon-ebtables stop diff --git a/package/gluon-ebtables/files/lib/gluon/reload.d/719-gluon-ebtables-start b/package/gluon-ebtables/files/lib/gluon/reload.d/719-gluon-ebtables-start deleted file mode 100755 index 579c2e6309..0000000000 --- a/package/gluon-ebtables/files/lib/gluon/reload.d/719-gluon-ebtables-start +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -/etc/init.d/gluon-ebtables start diff --git a/package/gluon-ebtables/luasrc/lib/gluon/ebtables/100-dir-chain b/package/gluon-ebtables/luasrc/lib/gluon/ebtables/100-dir-chain deleted file mode 100644 index 62b92947a6..0000000000 --- a/package/gluon-ebtables/luasrc/lib/gluon/ebtables/100-dir-chain +++ /dev/null @@ -1,9 +0,0 @@ -chain('IN_ONLY', 'RETURN') -chain('OUT_ONLY', 'RETURN') - --- nat chain runs early, so we can drop IGMP/MLD -chain('MULTICAST_IN', 'RETURN', 'nat') -chain('MULTICAST_IN_ICMPV6', 'RETURN', 'nat') - -chain('MULTICAST_OUT', 'RETURN') -chain('MULTICAST_OUT_ICMPV6', 'RETURN') diff --git a/package/gluon-ebtables/luasrc/lib/gluon/ebtables/101-dir-rules b/package/gluon-ebtables/luasrc/lib/gluon/ebtables/101-dir-rules deleted file mode 100644 index 74486ae537..0000000000 --- a/package/gluon-ebtables/luasrc/lib/gluon/ebtables/101-dir-rules +++ /dev/null @@ -1,7 +0,0 @@ -rule 'IN_ONLY --logical-in br-client -i bat0 -j RETURN' -rule 'IN_ONLY --logical-in br-client -i local-port -j RETURN' -rule 'IN_ONLY --logical-in br-client -j DROP' - -rule 'OUT_ONLY --logical-out br-client -o bat0 -j RETURN' -rule 'OUT_ONLY --logical-out br-client -o local-port -j RETURN' -rule 'OUT_ONLY --logical-out br-client -j DROP' diff --git a/package/gluon-ebtables/luasrc/lib/gluon/ebtables/105-mcast-drop-igmp-mld b/package/gluon-ebtables/luasrc/lib/gluon/ebtables/105-mcast-drop-igmp-mld deleted file mode 100644 index 3b1ecab3b4..0000000000 --- a/package/gluon-ebtables/luasrc/lib/gluon/ebtables/105-mcast-drop-igmp-mld +++ /dev/null @@ -1,20 +0,0 @@ -local site = require 'gluon.site' - -rule('MULTICAST_IN -p IPv4 --ip-protocol igmp --ip-igmp-type membership-query -j DROP', 'nat') -rule('MULTICAST_OUT -p IPv4 --ip-protocol igmp --ip-igmp-type membership-query -j DROP') - -rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 130 -j DROP') -- MLD Query -rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 130 -j DROP', 'nat') -- MLD Query - -if site.mesh.filter_membership_reports(true) then - rule('MULTICAST_IN -p IPv4 --ip-protocol igmp -j DROP', 'nat') - rule('MULTICAST_OUT -p IPv4 --ip-protocol igmp -j DROP') - - rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 131 -j DROP') -- MLDv1 Report - rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 132 -j DROP') -- MLDv1 Done - rule('MULTICAST_OUT_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 143 -j DROP') -- MLDv2 Report - - rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 131 -j DROP', 'nat') -- MLDv1 Report - rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 132 -j DROP', 'nat') -- MLDv1 Done - rule('MULTICAST_IN_ICMPV6 -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type 143 -j DROP', 'nat') -- MLDv2 Report -end diff --git a/package/gluon-iptables-clamp-mss-to-pmtu/files/lib/gluon/mesh-vpn/iptables-mss.rules b/package/gluon-iptables-clamp-mss-to-pmtu/files/lib/gluon/mesh-vpn/iptables-mss.rules deleted file mode 100644 index a61a900df5..0000000000 --- a/package/gluon-iptables-clamp-mss-to-pmtu/files/lib/gluon/mesh-vpn/iptables-mss.rules +++ /dev/null @@ -1,3 +0,0 @@ -*mangle --A FORWARD -o mesh-vpn+ -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu -COMMIT diff --git a/package/gluon-iptables-clamp-mss-to-pmtu/luasrc/lib/gluon/upgrade/800-iptables-mesh-vpn-clamp-mss-to-pmtu b/package/gluon-iptables-clamp-mss-to-pmtu/luasrc/lib/gluon/upgrade/800-iptables-mesh-vpn-clamp-mss-to-pmtu deleted file mode 100755 index 961a063efc..0000000000 --- a/package/gluon-iptables-clamp-mss-to-pmtu/luasrc/lib/gluon/upgrade/800-iptables-mesh-vpn-clamp-mss-to-pmtu +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/lua - -local uci = require('simple-uci').cursor() -uci:section('firewall', 'include', 'vpn_clamp_mss', { - family = 'ipv6', - type = 'restore', - path = '/lib/gluon/mesh-vpn/iptables-mss.rules' -}) - -uci:save('firewall') diff --git a/package/gluon-l3roamd/files/etc/ddhcpd.d/l3roamd b/package/gluon-l3roamd/files/etc/ddhcpd.d/l3roamd new file mode 100755 index 0000000000..ef9cb68614 --- /dev/null +++ b/package/gluon-l3roamd/files/etc/ddhcpd.d/l3roamd @@ -0,0 +1,23 @@ +#!/bin/sh + +# NOTE: we need to prefix with ::ffff: in del_address because of a bug in l3roamd +# add_address is modified for consistency + +case "$1" in + lease) + echo add_address "::ffff:$2" "$3" | uc /var/run/l3roamd.sock + ;; + + release) + echo del_address "::ffff:$2" "$3" | uc /var/run/l3roamd.sock + ;; + + + claim) + ip route add "$2" dev local-node proto 158 + ;; + + claimrelease) + ip route del "$2" dev local-node proto 158 + ;; +esac diff --git a/package/gluon-mesh-babel/Makefile b/package/gluon-mesh-babel/Makefile index 239d7132fe..c3d831967b 100644 --- a/package/gluon-mesh-babel/Makefile +++ b/package/gluon-mesh-babel/Makefile @@ -9,7 +9,7 @@ include ../gluon.mk define Package/gluon-mesh-babel TITLE:=Babel mesh - DEPENDS:=+gluon-core +babeld +gluon-mesh-layer3-common +libiwinfo +libgluonutil +firewall +libjson-c +libnl-tiny +libubus +libubox +libblobmsg-json +libbabelhelper +luabitop + DEPENDS:=+gluon-core +babeld +gluon-mesh-layer3-common +libiwinfo +libgluonutil +firewall4 +libjson-c +libnl-tiny +libubus +libubox +libblobmsg-json +libbabelhelper +luabitop PROVIDES:=gluon-mesh-provider endef diff --git a/package/gluon-mesh-batman-adv/Makefile b/package/gluon-mesh-batman-adv/Makefile index aac41f1ba8..6c6cb2269c 100644 --- a/package/gluon-mesh-batman-adv/Makefile +++ b/package/gluon-mesh-batman-adv/Makefile @@ -12,8 +12,9 @@ define Package/gluon-mesh-batman-adv-15 +gluon-core \ +libgluonutil \ +gluon-client-bridge \ - +gluon-ebtables \ - +firewall \ + +gluon-nftables \ + +gluon-nftables-multicast \ + +firewall4 \ +libiwinfo \ +kmod-dummy \ +libnl-tiny \ diff --git a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/ebtables/250-next-node b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/ebtables/250-next-node deleted file mode 100644 index c239f81e56..0000000000 --- a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/ebtables/250-next-node +++ /dev/null @@ -1,41 +0,0 @@ -local client_bridge = require 'gluon.client_bridge' -local site = require 'gluon.site' -local next_node = site.next_node({}) - -local macaddr = client_bridge.next_node_macaddr() - -rule('FORWARD --logical-out br-client -i bat0 -o local-port -j DROP') -rule('FORWARD --logical-out br-client -i local-port -o bat0 -j DROP') - -rule('PREROUTING --logical-in br-client -i bat0 -s ' .. macaddr .. ' -j DROP', 'nat') -rule('PREROUTING --logical-in br-client -i bat0 -d ' .. macaddr .. ' -j DROP', 'nat') - -rule('FORWARD --logical-out br-client -o bat0 -d ' .. macaddr .. ' -j DROP') -rule('OUTPUT --logical-out br-client -o bat0 -d ' .. macaddr .. ' -j DROP') -rule('FORWARD --logical-out br-client -o bat0 -s ' .. macaddr .. ' -j DROP') -rule('OUTPUT --logical-out br-client -o bat0 -s ' .. macaddr .. ' -j DROP') - -if next_node.ip4 then - rule('FORWARD --logical-out br-client -o bat0 -p ARP --arp-ip-src ' .. next_node.ip4 .. ' -j DROP') - rule('FORWARD --logical-out br-client -o bat0 -p ARP --arp-ip-dst ' .. next_node.ip4 .. ' -j DROP') - rule('FORWARD --logical-out br-client -i bat0 -p ARP --arp-ip-src ' .. next_node.ip4 .. ' -j DROP') - rule('FORWARD --logical-out br-client -i bat0 -p ARP --arp-ip-dst ' .. next_node.ip4 .. ' -j DROP') - - rule('OUTPUT --logical-out br-client -o bat0 -p ARP --arp-ip-src ' .. next_node.ip4 .. ' -j DROP') - rule('OUTPUT --logical-out br-client -o bat0 -p ARP --arp-ip-dst ' .. next_node.ip4 .. ' -j DROP') - - rule('INPUT -i bat0 -p ARP --arp-ip-src ' .. next_node.ip4 .. ' -j DROP') - rule('INPUT -i bat0 -p ARP --arp-ip-dst ' .. next_node.ip4 .. ' -j DROP') - - rule('FORWARD --logical-out br-client -o bat0 -p IPv4 --ip-destination ' .. next_node.ip4 .. ' -j DROP') - rule('OUTPUT --logical-out br-client -o bat0 -p IPv4 --ip-destination ' .. next_node.ip4 .. ' -j DROP') - rule('FORWARD --logical-out br-client -o bat0 -p IPv4 --ip-source ' .. next_node.ip4 .. ' -j DROP') - rule('OUTPUT --logical-out br-client -o bat0 -p IPv4 --ip-source ' .. next_node.ip4 .. ' -j DROP') -end - -if next_node.ip6 then - rule('FORWARD --logical-out br-client -o bat0 -p IPv6 --ip6-destination ' .. next_node.ip6 .. ' -j DROP') - rule('OUTPUT --logical-out br-client -o bat0 -p IPv6 --ip6-destination ' .. next_node.ip6 .. ' -j DROP') - rule('FORWARD --logical-out br-client -o bat0 -p IPv6 --ip6-source ' .. next_node.ip6 .. ' -j DROP') - rule('OUTPUT --logical-out br-client -o bat0 -p IPv6 --ip6-source ' .. next_node.ip6 .. ' -j DROP') -end diff --git a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/ebtables/300-radv-input-output b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/ebtables/300-radv-input-output deleted file mode 100644 index 377d11cdca..0000000000 --- a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/ebtables/300-radv-input-output +++ /dev/null @@ -1,2 +0,0 @@ -rule 'INPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-solicitation -i bat0 -j DROP' -rule 'OUTPUT -p IPv6 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -o bat0 -j DROP' diff --git a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/nftables/250-next-node.lua b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/nftables/250-next-node.lua new file mode 100644 index 0000000000..8593910999 --- /dev/null +++ b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/nftables/250-next-node.lua @@ -0,0 +1,41 @@ +local client_bridge = require 'gluon.client_bridge' +local site = require 'gluon.site' +local next_node = site.next_node({}) + +local macaddr = client_bridge.next_node_macaddr() + +bridge_rule('FORWARD', 'obrname "br-client" iifname "bat0" oifname "bat0" drop') +bridge_rule('FORWARD', 'obrname "br-client" iifname "local-port" oifname "bat0" drop') + +bridge_rule('PREROUTING', 'ibrname "br-client" iifname "bat0" ether saddr ' .. macaddr .. ' drop', 'nat') +bridge_rule('PREROUTING', 'ibrname "br-client" iifname "bat0" ether daddr ' .. macaddr .. ' drop', 'nat') + +bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" ether daddr ' .. macaddr .. ' drop') +bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" ether daddr ' .. macaddr .. ' drop') +bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" ether saddr ' .. macaddr .. ' drop') +bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" ether saddr ' .. macaddr .. ' drop') + +if next_node.ip4 then + bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" arp saddr ip ' .. next_node.ip4 .. ' drop') + bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" arp daddr ip ' .. next_node.ip4 .. ' drop') + bridge_rule('FORWARD', 'obrname "br-client" iifname "bat0" arp saddr ip ' .. next_node.ip4 .. ' drop') + bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" arp daddr ip ' .. next_node.ip4 .. ' drop') + + bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" arp saddr ip ' .. next_node.ip4 .. ' drop') + bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" arp daddr ip ' .. next_node.ip4 .. ' drop') + + bridge_rule('INPUT', 'iifname "bat0" arp saddr ip ' .. next_node.ip4 .. ' drop') + bridge_rule('INPUT', 'iifname "bat0" arp daddr ip ' .. next_node.ip4 .. ' drop') + + bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" ip daddr ' .. next_node.ip4 .. ' drop') + bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" ip daddr ' .. next_node.ip4 .. ' drop') + bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" ip saddr ' .. next_node.ip4 .. ' drop') + bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" ip saddr ' .. next_node.ip4 .. ' drop') +end + +if next_node.ip6 then + bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" ip6 daddr ' .. next_node.ip6 .. ' drop') + bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" ip6 daddr ' .. next_node.ip6 .. ' drop') + bridge_rule('FORWARD', 'obrname "br-client" oifname "bat0" ip6 saddr ' .. next_node.ip6 .. ' drop') + bridge_rule('OUTPUT', 'obrname "br-client" oifname "bat0" ip6 saddr ' .. next_node.ip6 .. ' drop') +end diff --git a/package/gluon-mesh-batman-adv/luasrc/lib/gluon/nftables/300-radv-input-output.lua b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/nftables/300-radv-input-output.lua new file mode 100644 index 0000000000..681659f0f8 --- /dev/null +++ b/package/gluon-mesh-batman-adv/luasrc/lib/gluon/nftables/300-radv-input-output.lua @@ -0,0 +1,2 @@ +bridge_rule('INPUT', 'iifname "bat0" icmpv6 type nd-router-solicit drop') +bridge_rule('OUTPUT', 'oifname "bat0" icmpv6 type nd-router-advert drop') diff --git a/package/gluon-mesh-layer3-common/Makefile b/package/gluon-mesh-layer3-common/Makefile index 45ad2f577a..a41bf84f55 100644 --- a/package/gluon-mesh-layer3-common/Makefile +++ b/package/gluon-mesh-layer3-common/Makefile @@ -6,7 +6,7 @@ include ../gluon.mk define Package/gluon-mesh-layer3-common TITLE:=Layer3 common files - DEPENDS:=+gluon-core +gluon-mmfd +firewall + DEPENDS:=+gluon-core +gluon-mmfd +firewall4 endef $(eval $(call BuildPackageGluon,gluon-mesh-layer3-common)) diff --git a/package/gluon-mesh-olsrd/Makefile b/package/gluon-mesh-olsrd/Makefile index adf1a07a4b..059a8dcd01 100644 --- a/package/gluon-mesh-olsrd/Makefile +++ b/package/gluon-mesh-olsrd/Makefile @@ -3,17 +3,62 @@ include $(TOPDIR)/rules.mk PKG_NAME:=gluon-mesh-olsrd PKG_VERSION=1 +PKG_BUILD_DEPENDS += libjson-c + include ../gluon.mk define Package/gluon-mesh-olsrd TITLE:=olsrd mesh DEPENDS:= \ +gluon-core \ - @IPV6 \ + +kmod-macvlan \ + +olsrd \ +oonf-olsrd2 \ - +firewall \ - +gluon-mesh-layer3-common + +firewall4 \ + +libgluonutil \ + +libjson-c \ + +libubox +libuclient \ + +olsrd-mod-jsoninfo \ + +olsrd-mod-httpinfo \ + +olsrd-mod-txtinfo \ + +liblua \ + +ip-full \ + +gluon-mesh-layer3-common \ + +gluon-l3roamd \ + +gluon-radvd \ + +lua-jsonc \ + +gluon-state-check PROVIDES:=gluon-mesh-provider endef +define Package/gluon-mesh-olsrd/install + $(Gluon/Build/Install) + + $(INSTALL_DIR) $(1)/usr/lib/lua/gluon + $(INSTALL_BIN) $(PKG_BUILD_DIR)/olsrd.so $(1)/usr/lib/lua/gluon/ + $(INSTALL_DIR) $(1)/usr/lib/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/libolsrdhelper.so $(1)/usr/lib/ + + $(INSTALL_DIR) $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/olsrd-debug $(1)/usr/bin/olsrd-debug + $(INSTALL_BIN) $(PKG_BUILD_DIR)/olsr-respondd $(1)/usr/bin/olsr-respondd + +ifdef CONFIG_GLUON_BASE + rm -rf $(1)/lib/gluon/upgrade/ +endif +endef + +define Build/InstallDev + $(INSTALL_DIR) $(1)/usr/lib/lua/gluon + $(INSTALL_BIN) $(PKG_BUILD_DIR)/olsrd.so $(1)/usr/lib/lua/gluon/ + $(INSTALL_DIR) $(1)/usr/lib/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/libolsrdhelper.so $(1)/usr/lib/ + + $(INSTALL_DIR) $(1)/usr/include/gluon-mesh-olsrd + $(INSTALL_BIN) $(PKG_BUILD_DIR)/libolsrdhelper.h $(1)/usr/include/gluon-mesh-olsrd/ + $(INSTALL_DIR) $(1)/usr/lib/pkgconfig/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/libolsrdhelper.pc $(1)/usr/lib/pkgconfig/ +endef + + $(eval $(call BuildPackageGluon,gluon-mesh-olsrd)) diff --git a/package/gluon-mesh-olsrd/check_site.lua b/package/gluon-mesh-olsrd/check_site.lua index 3e13679068..15d19dcde1 100644 --- a/package/gluon-mesh-olsrd/check_site.lua +++ b/package/gluon-mesh-olsrd/check_site.lua @@ -1 +1,17 @@ -need_table({'mesh', 'olsrd', 'v2', 'config'}, nil, false) +if need_boolean({'mesh', 'olsrd', 'v1_6', 'enable'}, false) then + need_table({'mesh', 'olsrd', 'v1_6', 'config'}, nil, false) +end + +if need_boolean({'mesh', 'olsrd', 'v1_4', 'enable'}, false) then + need_table({'mesh', 'olsrd', 'v1_4', 'config'}, nil, false) +end + +if need_boolean({'mesh', 'olsrd', 'v2', 'enable'}, false) then + need_table({'mesh', 'olsrd', 'v2', 'config'}, nil, false) + need_boolean({'mesh', 'olsrd', 'v2', 'ip6_exclusive_mode'}, false) + need_boolean({'mesh', 'olsrd', 'v2', 'ip4_exclusive_mode'}, false) + if need_boolean({'mesh', 'olsrd', 'v2', 'ip4_exclusive_mode'}, false) and need_boolean({'mesh', 'olsrd', 'v2', 'ip6_exclusive_mode'}, false) then + -- FIXME: we could check the value but idk how to do that. basically both options are xor. + error('you cant enable both olsrv2 ip4 and ip6 exclusive mode') + end +end diff --git a/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr index e96dcce2c6..73e7458ff5 100755 --- a/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr +++ b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr @@ -7,4 +7,6 @@ reload_running() { fi } +reload_running olsrd reload_running olsrd2 +reload_running olsrd6 diff --git a/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr index 0eb0e58caa..c18f7b6998 100755 --- a/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr +++ b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr @@ -7,4 +7,6 @@ reload_running() { fi } +reload_running olsrd reload_running olsrd2 +reload_running olsrd6 diff --git a/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd index 8f39df9944..25d151dce7 100755 --- a/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd +++ b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd @@ -3,5 +3,7 @@ . /lib/gluon/autoupdater/lib.sh +start_enabled olsrd start_enabled olsrd2 +start_enabled olsrd6 wifi up diff --git a/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd index c9cd9a8cca..e78ea79404 100755 --- a/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd +++ b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd @@ -3,5 +3,7 @@ . /lib/gluon/autoupdater/lib.sh +stop olsrd stop olsrd2 +stop olsrd6 wifi down diff --git a/package/gluon-mesh-olsrd/files/usr/lib/micron.d/olsr-respondd b/package/gluon-mesh-olsrd/files/usr/lib/micron.d/olsr-respondd new file mode 100644 index 0000000000..7aca0a92c5 --- /dev/null +++ b/package/gluon-mesh-olsrd/files/usr/lib/micron.d/olsr-respondd @@ -0,0 +1,3 @@ +* * * * * /usr/bin/olsr-respondd n > /tmp/olsrd-respondd-neighbours.json +*/5 * * * * /usr/bin/olsr-respondd s > /tmp/olsrd-respondd-statistics.json +*/5 * * * * /usr/bin/olsr-respondd i > /tmp/olsrd-respondd-nodeinfo.json diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_default_gw4 b/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_default_gw4 new file mode 100755 index 0000000000..ccb1a639f7 --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_default_gw4 @@ -0,0 +1,27 @@ +#!/usr/bin/env lua + +local olsrd = require 'gluon.olsrd' + +local info = olsrd.oi() + +local gateways = { } + +if info.olsr2.running then + for _, network in ipairs(olsrd.olsr2_get_nodeinfo('olsrv2info jsonraw attached_network').attached_network) do + if network.attached_net_src == '0.0.0.0/0' then + table.insert(gateways, network.node) + end + end +end + +if info.olsr1.running then + for _, hna in ipairs(olsrd.olsr1_get_nodeinfo('hna').hna) do + if hna.destination == '0.0.0.0' then + table.insert(gateways, hna.gateway) + end + end +end + +if #gateways == 0 then + os.exit(1) +end diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_default_gw6 b/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_default_gw6 new file mode 100755 index 0000000000..d6c6e82ad7 --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_default_gw6 @@ -0,0 +1,27 @@ +#!/usr/bin/env lua + +local olsrd = require 'gluon.olsrd' + +local info = olsrd.oi() + +local gateways = { } + +if info.olsr2.running then + for _, network in ipairs(olsrd.olsr2_get_nodeinfo('olsrv2info jsonraw attached_network').attached_network) do + if network.attached_net_src == '::/0' then + table.insert(gateways, network.node) + end + end +end + +if info.olsr1.running then + for _, hna in ipairs(olsrd.olsr1_get_nodeinfo('hna').hna) do + if hna.destination == '::' then + table.insert(gateways, hna.gateway) + end + end +end + +if #gateways == 0 then + os.exit(1) +end diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_neighbours b/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_neighbours new file mode 100755 index 0000000000..6d782dfa88 --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/state/check.d/has_neighbours @@ -0,0 +1,21 @@ +#!/usr/bin/lua + +local olsrd = require 'gluon.olsrd' + +local oi = olsrd.oi() + +local links = 0 + +if oi.olsr1.running then + local neigh1 = olsrd.olsr1_get_nodeinfo('links') + links = links + #neigh1.links +end + +if oi.olsr2.running then + local neigh2 = olsrd.olsr2_get_nodeinfo('nhdpinfo jsonraw link') + links = links + #neigh2.link +end + +if links == 0 then + os.exit(1) +end diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf index 880dc31fd4..c66152d4f1 100755 --- a/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf @@ -4,6 +4,10 @@ local uci = require('simple-uci').cursor() local site = require 'gluon.site' local util = require 'gluon.util' local wireless = require 'gluon.wireless' +local sysconfig = require 'gluon.sysconfig' +local olsrd = require 'gluon.olsrd' +local l3 = require 'gluon.l3' + local mesh_interfaces = util.get_role_interfaces(uci, 'mesh') local uplink_interfaces = util.get_role_interfaces(uci, 'uplink') local client_interfaces = util.get_role_interfaces(uci, 'client') @@ -38,9 +42,19 @@ end -- get all mesh radios and mesh lans and then add them to olsrd wireless.foreach_radio(uci, function(radio, _, _) local radio_name = radio['.name'] - table.insert(intf.radio_mesh, 'mesh_' .. radio_name) + if uci:get('network', 'mesh_' .. radio_name, 'proto') then + table.insert(intf.radio_mesh, 'mesh_' .. radio_name) + end + if uci:get('network', 'p2p_' .. radio_name, 'proto') then + table.insert(intf.radio_mesh, 'p2p_' .. radio_name) + end + if uci:get('network', 'ibss_' .. radio_name, 'proto') then + table.insert(intf.radio_mesh, 'ibss_' .. radio_name) + end end) +local vpn_mesh = false + if pcall(function() require 'gluon.mesh-vpn' end) then local vpn_core = require 'gluon.mesh-vpn' @@ -48,10 +62,25 @@ if pcall(function() require 'gluon.mesh-vpn' end) then -- mesh_vpn is a interface that has the right ifname -- we can't use mesh-vpn (dash instead of underscore) since it's not a uci interface table.insert(intf.vpn_mesh, 'mesh_vpn') + vpn_mesh = true end end -table.insert(intf.wired_mesh, 'loopback') +local function has_role_mesh(ifname) + local roles = uci:get('gluon', 'iface_' .. ifname, 'role') + + local has_role = false + + if roles then + for _, r in ipairs(roles) do + if r == 'mesh' then + has_role = true + end + end + end + + return has_role +end local has_uplink_mesh = false local has_other_mesh = false @@ -65,6 +94,17 @@ for _,i in pairs(mesh_interfaces) do end if has_uplink_mesh then + -- use uplink with macvlan so we do not have traffic leaks + uci:section('network', 'device', 'm_uplink', { + name = 'm_uplink', + type = 'macvlan', + ifname = 'br-wan', + }) + + uci:section('network', 'interface', 'mesh_uplink', { + ifname = 'm_uplink', + }) + table.insert(intf.wired_mesh, 'mesh_uplink') end @@ -72,15 +112,144 @@ if has_other_mesh then table.insert(intf.wired_mesh, 'mesh_other') end +-- for _,i in pairs(mesh_interfaces) do +-- local name = i +-- +-- if util.contains(uplink_interfaces, i) then +-- name = 'uplink' +-- i = 'br-wan' +-- elseif util.contains(client_interfaces, i) then +-- name = 'client' +-- i = 'br-client' +-- else +-- name = 'other' +-- i = 'br-mesh_other' +-- end +-- +-- local iname = 'olsr_mesh_' .. name +-- +-- if not util.contains(intf.wired_mesh, iname) then +-- uci:section('network', 'device', 'm_' .. name, { +-- name = 'm_' .. name, +-- type = 'macvlan', +-- ifname = i, +-- }) +-- +-- uci:section('network', 'interface', iname, { +-- ifname = 'm_' .. name, +-- }) +-- +-- table.insert(intf.wired_mesh, iname) +-- end +-- end + +for _, _olsr in ipairs({ + { 'v1_4', 4, 698, 'olsrd' }, + { 'v1_6', 6, 698, 'olsrd6' }, +}) do + local _olsr_key, _olsr_ip, _olsr_port, _olsr_name = unpack(_olsr) + local cfg = site.mesh.olsrd[_olsr_key] + local uci_name = _olsr_name + + uci:delete_all(uci_name, 'Interface') + uci:delete_all(uci_name, 'LoadPlugin') + + if cfg ~= nil and cfg.enable(false) then + os.execute('/etc/init.d/' .. _olsr_name .. ' enable') + + -- set config + local olsrConfig = { + IpVersion = _olsr_ip, + FIBMetric = 'flat', + LinkQualityLevel = '2', + OlsrPort = _olsr_port, + Willingness = '3' + } + + local extraConf = cfg.config() + if extraConf then + for k, v in pairs(extraConf) do + olsrConfig[k] = extraConf[k] + end + end + + uci:delete_all(uci_name, 'olsrd') + uci:section(uci_name, 'olsrd', nil, olsrConfig) + + -- add jsoninfo + uci:section(uci_name, 'LoadPlugin', 'jsoninfo', { + library = olsrd.find_module_version('olsrd_jsoninfo'), + accept = '127.0.0.1', + }) + -- add txtinfo + uci:section(uci_name, 'LoadPlugin', 'txtinfo', { + library = olsrd.find_module_version('olsrd_txtinfo'), + accept = '127.0.0.1', + }) + -- add httpinfo + uci:section(uci_name, 'LoadPlugin', 'httpinfo', { + library = olsrd.find_module_version('olsrd_httpinfo'), + Net = {'127.0.0.1'}, + }) + + if #intf.wired_mesh then + uci:section(uci_name, 'Interface', 'wired_mesh', { + interface = intf.wired_mesh, + Mode = 'mesh', -- should be mode 'ether' but that appears broken for some reason + }) + end + + if #intf.vpn_mesh then + uci:section(uci_name, 'Interface', 'vpn_mesh', { + interface = intf.vpn_mesh, + Mode = 'mesh', + }) + end + + if #intf.radio_mesh then + uci:section(uci_name, 'Interface', 'radio_mesh', { + interface = intf.radio_mesh, + Mode = 'mesh', + }) + end + + uci:foreach(uci_name, 'userconfig', function(config) + local section_name = config['.name'] + + for k, v in pairs(config) do + if k:find("^\\.") == nil then + if not config['.anonymous'] then + uci:set(uci_name, section_name:sub(2), k, v) + else + uci:set(uci_name, uci:get_first(uci_name, 'olsrd'), k, v) + end + end + end + end) + + uci:section('firewall', 'rule', 'allow_olsr' .. _olsr_ip .. '_mesh', { + src = 'mesh', + dest_port = _olsr_port, + proto = 'udp', + target = 'ACCEPT', + }) + else + -- site.mesh.olsrd.v1/v1_4.enable false + os.execute('/etc/init.d/' .. _olsr_name .. ' disable') + uci:delete('firewall', 'allow_' .. _olsr_name .. '_mesh') + end +end + uci:delete_all('olsrd2', 'interface') +uci:delete_all('olsrd2', 'lan_import') if site.mesh.olsrd.v2.enable(true) then os.execute('/etc/init.d/olsrd2 enable') local addrs = { } - local lan = { } + local lan = nil + local orig = { } local cfg = site.mesh.olsrd.v2 - local config = uci:get_first("olsrd2", "olsrv2") -- set global config local olsr2Config = { @@ -109,17 +278,56 @@ if site.mesh.olsrd.v2.enable(true) then }) + -- useless on its own, this is for userconfig + uci:delete_all('olsrd2', 'log') + uci:section('olsrd2', 'log', 'log', { + + }) + if cfg.lan() then lan = cfg.lan() end - table.insert(addrs, '-127.0.0.1/8') - table.insert(addrs, '-::1/128') + if cfg.ip6_exclusive_mode(false) then + table.insert(addrs, '-0.0.0.0/0') + else + table.insert(addrs, '-127.0.0.1/8') + table.insert(orig, site.node_prefix4()) + end + if cfg.ip4_exclusive_mode(false) then + table.insert(addrs, '-::/0') + else + table.insert(addrs, '-::1/128') + local addr = uci:get('network', 'loopback', 'ip6addr') + table.insert(orig, addr) + end table.insert(addrs, 'default_accept') + table.insert(orig, 'default_reject') + + local client_ranges_v4 = {} + local client_ranges_v6 = {} + local l3roamd_ranges = {} - uci:set("olsrd2", config, "originator", addrs) - uci:set("olsrd2", config, "lan", lan) + table.insert(client_ranges_v4, site.prefix4()) + + if site.ddhcpd.range() then + table.insert(client_ranges_v4, site.ddhcpd.range()) + end + + table.insert(client_ranges_v6, site.prefix6()) + + table.insert(client_ranges_v4, 'default_reject') + table.insert(client_ranges_v6, 'default_reject') + + table.insert(l3roamd_ranges, l3.node_client_prefix6()) + table.insert(l3roamd_ranges, 'default_reject') + + uci:delete_all('olsrd2', 'olsrv2') + uci:section('olsrd2', 'olsrv2', nil, { + originator = orig, + lan = lan, + }) if #intf.wired_mesh then uci:section('olsrd2', 'interface', 'wired_mesh', { @@ -142,11 +350,61 @@ if site.mesh.olsrd.v2.enable(true) then }) end - uci:section('olsrd2', 'interface', 'loopback', { - ifname = { 'loopback' }, - bindto = addrs, + if not cfg.ip4_exclusive_mode(false) then + local loopback_addrs = { + uci:get('network', 'loopback', 'ip6addr'), + 'default_reject', + } + + uci:section('olsrd2', 'interface', 'loopback', { + ifname = { 'loopback' }, + routeable = loopback_addrs, + bindto = loopback_addrs, + }) + end + + -- deugging + -- uci:section('olsrd2', 'userconfig', '_log', { debug = 'lan_import' }) + -- ref http://www.olsr.org/mediawiki/index.php/LAN_Import_Plugin + -- ref https://github.com/OLSR/OONF/issues/32 + + if not cfg.ip6_exclusive_mode(false) then + uci:section('olsrd2', 'lan_import', 'l3roamd_clients_v4', { + name = 'l3roamd_clients_v4', + matches = client_ranges_v4, + prefix_length = 32, + protocol = 158, -- l3roamd + }) + end + + if not cfg.ip4_exclusive_mode(false) then + uci:section('olsrd2', 'lan_import', 'l3roamd_clients_v6', { + name = 'l3roamd_clients_v6', + matches = client_ranges_v6, + prefix_length = 128, + protocol = 158, -- l3roamd + }) + end + + uci:section('olsrd2', 'lan_import', 'l3roamd_prefix', { + name = 'l3roamd_ranges', + matches = l3roamd_ranges, }) + uci:foreach('olsrd2', 'userconfig', function(config) + local section_name = config['.name'] + + for k, v in pairs(config) do + if k:find("^\\.") == nil then + if not config['.anonymous'] then + uci:set('olsrd2', section_name:sub(2), k, v) + else + uci:set('olsrd2', uci:get_first('olsrd2', 'olsrv2'), k, v) + end + end + end + end) + uci:section('firewall', 'rule', 'allow_olsr2_mesh', { src = 'mesh', dest_port = '269', @@ -158,6 +416,23 @@ else os.execute('/etc/init.d/olsrd2 disable') uci:delete('firewall', 'allow_olsr2_mesh') end + + uci:section('firewall', 'rule', 'hack_ssh', { + src = '*', + dest_port = '22', + proto = 'tcp', + target = 'ACCEPT', + }) + + uci:section('firewall', 'rule', 'hack_http', { + src = '*', + dest_port = '80', + proto = 'tcp', + target = 'ACCEPT', + }) + +uci:save('olsrd') +uci:save('olsrd6') uci:save('olsrd2') uci:save('firewall') uci:save('network') diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw index 65387e8755..8499421427 100755 --- a/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw @@ -1,10 +1,79 @@ #!/usr/bin/lua local uci = require('simple-uci').cursor() +local site = require 'gluon.site' local util = require 'gluon.util' +local wireless = require 'gluon.wireless' local networks = uci:get_list('firewall', 'drop', 'network') util.remove_from_set(networks, 'client') uci:set_list('firewall', 'drop', 'network', networks) +uci:section('firewall', 'rule', 'allow_mesh_ping', { + name = 'Allow mesh ping', + src = 'mesh', + proto = 'icmp', + icmp_type = { + 'echo-request' + }, + family = 'ipv4', + target = 'ACCEPT', +}) + +uci:section('firewall', 'rule', 'allow_mesh_mld', { + name = 'Allow mesh MLD', + src = 'mesh', + proto = 'icmp', + src_ip = 'fe80::/10', + icmp_type = { + '130/0', + '131/0', + '132/0', + '143/0', + }, + family = 'ipv6', + target = 'ACCEPT', +}) + +uci:section('firewall', 'rule', 'allow_mesh_icmpv6_input', { + name = 'Allow mesh icmpv6 input', + src = 'mesh', + proto = 'icmp', + icmp_type = { + 'echo-request', + 'echo-reply', + 'destination-unreachable', + 'packet-too-big', + 'time-exceeded', + 'bad-header', + 'unknown-header-type', + 'router-solicitation', + 'neighbour-solicitation', + 'router-advertisement', + 'neighbour-advertisement' + }, + limit = '1000/sec', + family = 'ipv6', + target = 'ACCEPT', +}) + +uci:section('firewall', 'rule', 'allow_mesh_icmpv6_forward', { + name = 'Allow mesh icmpv6 forward', + src = 'mesh', + dest = '*', + proto = 'icmp', + icmp_type = { + 'echo-request', + 'echo-reply', + 'destination-unreachable', + 'packet-too-big', + 'time-exceeded', + 'bad-header', + 'unknown-header-type' + }, + limit = '1000/sec', + family = 'ipv6', + target = 'ACCEPT', +}) + uci:save('firewall') diff --git a/package/gluon-mesh-olsrd/luasrc/usr/bin/olsrd-cli b/package/gluon-mesh-olsrd/luasrc/usr/bin/olsrd-cli new file mode 100755 index 0000000000..f6a39323d7 --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/usr/bin/olsrd-cli @@ -0,0 +1,149 @@ +#!/usr/bin/lua + +local uci = require('simple-uci').cursor() +local site = require 'gluon.site' +local util = require 'gluon.util' +local wireless = require 'gluon.wireless' +local sysconfig = require 'gluon.sysconfig' +local util = require 'gluon.util' +local olsrd = require 'gluon.olsrd' + +-- Utils + +function printf(...) + print(string.format(...)) +end + +-- Print contents of `tbl`, with indentation. +-- `indent` sets the initial level of indentation. +-- src https://gist.github.com/xytis/5361405 +function tprint (tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(' ', indent) .. k .. ': ' + if type(v) == 'table' then + print(formatting) + tprint(v, indent + 1) + else + print(formatting .. tostring(v)) + end + end +end + +-- src https://stackoverflow.com/a/24823383/3990041 +function table.slice(tbl, first, last, step) + local sliced = {} + + for i = first or 1, last or #tbl, step or 1 do + sliced[#sliced+1] = tbl[i] + end + + return sliced +end + +-- CLI lib + +function exec_cmd(args, sub) + if sub[args[1]] == nil then + return cmd_err('does not exist') + else + local cmd = sub[args[1]] + if cmd[3] ~= nil and #args > 1 then + exec_cmd(table.slice(args, 2), cmd[3]) + else + cmd[1](unpack(table.slice(args, 2))) + end + end +end + +function list_cmd(level, sub) + for key, cmd in pairs(sub) do + printf('%s%s: %s', string.rep(' ', level), key, cmd[2]) + if cmd[3] ~= nil then + list_cmd(level + 1, cmd[3]) + end + end +end + +function show_help() + printf('Usage: %s ', arg[0]) + list_cmd(1, sub) +end + +function cmd_err(msg, no_show_help) + -- since argv0 is at... well... 0... even though this is lua... + --- ...slice just returns arg without argv0 as the for starts at 1 + printf('Error: Command "%s" %s', table.concat(table.slice(arg), ' '), msg) + if not no_show_help then + printf('') + show_help() + end + os.exit(2) +end + +function dummy() + cmd_err('requires a subcommand') +end + +-- our stuff + +function show_info() + local info = olsrd.oi() + tprint(info) +end + +function olsr1_nodeinfo(...) + if #{ ... } == 0 then + return cmd_err('requires at least one argument (example: "all")', true) + end + + local query = table.concat({ ... }, '/') + local res = olsrd.olsr1_get_nodeinfo(query) + tprint(res) +end + +function olsr2_nodeinfo_raw(...) + if #{ ... } == 0 then + return cmd_err('requires at least one argument (example: "nhdpinfo link")', true) + end + + local query = table.concat({ ... }, ' ') + local res = olsrd.olsr2_get_nodeinfo_raw(query) + print(res) +end + +function olsr2_nodeinfo_json(...) + if #{ ... } == 0 then + return cmd_err('requires at least one argument (example: "nhdpinfo jsonraw link")', true) + end + + local query = table.concat({ ... }, ' ') + local res = olsrd.olsr2_get_nodeinfo(query) + tprint(res) +end + +function olsr1_neigh() + return olsr1_nodeinfo('links') +end + +function olsr2_neigh() + return olsr2_nodeinfo_json('nhdpinfo jsonraw link') +end + +sub = { + info = { show_info, 'Show information about status of olsr1 and olsr2' }, + help = { show_help, 'Show help' }, + olsr1 = { dummy, 'OLSRv1 Control Commands', { + nodeinfo = { olsr1_nodeinfo, 'OLSRv1 Nodeinfo' }, + neigh = { olsr1_neigh, 'OLSRv1 Neighbour List' }, + } }, + olsr2 = { dummy, 'OLSRv2 Control Commands', { + nodeinfo = { dummy, 'OLSRv2 Nodeinfo', { + raw = { olsr2_nodeinfo_raw, 'OLSRv2 Nodeinfo Raw' }, + json = { olsr2_nodeinfo_json, 'OLSRv2 Nodeinfo JSON' } + } }, + neigh = { olsr2_neigh, 'OLSRv2 Neighbour List' }, + } } +} + +exec_cmd(table.slice(arg), sub) diff --git a/package/gluon-mesh-olsrd/src/Makefile b/package/gluon-mesh-olsrd/src/Makefile new file mode 100644 index 0000000000..e0f7b70736 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/Makefile @@ -0,0 +1,50 @@ +all: respondd.so libolsrdhelper.so olsrd.so olsrd-debug olsr-respondd + +CFLAGS += -Wall -D_GNU_SOURCE -g + +ifeq ($(origin PKG_CONFIG), undefined) + PKG_CONFIG = pkg-config + ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),) + $(error $(PKG_CONFIG) not found) + endif +endif + +CFLAGS += $(shell pkg-config --cflags json-c) +LDFLAGS += $(shell pkg-config --libs json-c) + +SOURCES_HELPER = libolsrdhelper.c libolsrdhelper-neigh.c uclient.c arp.c +FILES_HELPER = $(SOURCES_HELPER) libolsrdhelper.h uclient.h arp.h + +SOURCES_RESPONDD = respondd.c +SOURCES_RESPONDD_REAL = $(SOURCES_RESPONDD) respondd-neighbours.c respondd-nodeinfo.c respondd-statistics.c +FILES_RESPONDD = $(SOURCES_RESPONDD) respondd-common.h +FILES_RESPONDD_REAL = $(SOURCES_RESPONDD_REAL) respondd-common.h respondd-real.h + +SOURCES_DEBUG = olsrd-debug.c $(SOURCES_RESPONDD_REAL) +FILES_DEBUG = $(SOURCES_DEBUG) + +SOURCES_OLSR_RESPONDD = olsr-respondd.c $(SOURCES_RESPONDD_REAL) +FILES_OLSR_RESPONDD = $(SOURCES_OLSR_RESPONDD) + +SOURCES_LUA = olsrd.c +FILES_LUA = $(SOURCES_LUA) + +all: olsrd-debug olsrd.so libolsrdhelper.so olsr-respondd respondd.so + +respondd.so: libolsrdhelper.so $(FILES_RESPONDD) + $(CC) $(CFLAGS) $(LDFLAGS) -I. -L. -shared -fPIC -fvisibility=hidden -o $@ $(SOURCES_RESPONDD) $(LDLIBS) + +olsr-respondd: respondd.so $(FILES_OLSR_RESPONDD) + $(CC) $(LUA_CFLAGS) $(CFLAGS) $(LDFLAGS) -fPIE -I. -L. -o $@ $(SOURCES_OLSR_RESPONDD) $(LDLIBS) -lgluonutil -lolsrdhelper -luci + +libolsrdhelper.so: libolsrdhelper.h libolsrdhelper.c + $(CC) $(CFLAGS) $(LDFLAGS) -shared -fPIC -o $@ $(SOURCES_HELPER) $(LDLIBS) -lgluonutil -luclient + +olsrd.so: libolsrdhelper.so $(FILES_LUA) + $(CC) $(LUA_CFLAGS) $(CFLAGS) $(LDFLAGS) -I. -L. -shared -fPIC -o $@ $(SOURCES_LUA) $(LDLIBS) -lgluonutil -lolsrdhelper -luci -llua-jsonc + +olsrd-debug: libolsrdhelper.so $(FILES_DEBUG) + $(CC) $(LUA_CFLAGS) $(CFLAGS) $(LDFLAGS) -fPIE -I. -L. -o $@ $(SOURCES_DEBUG) $(LDLIBS) -lgluonutil -lolsrdhelper -luci -llua-jsonc -llua + +clean: + rm -f *.so *.o olsrd-debug olsr-respondd diff --git a/package/gluon-mesh-olsrd/src/arp.c b/package/gluon-mesh-olsrd/src/arp.c new file mode 100644 index 0000000000..f2dba47844 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/arp.c @@ -0,0 +1,475 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +// adapted from https://codereview.stackexchange.com/a/58107/130114 + +#include "arp.h" +#include +#include +#include + +/** + * Macros to turn a numeric macro into a string literal. See + * https://gcc.gnu.org/onlinedocs/cpp/Stringification.html + */ +#define xstr(s) str(s) +#define str(s) #s + +#define ARP_CACHE "/proc/net/arp" + +/* Format for fscanf() to read the 1st, 4th, and 6th space-delimited fields */ +#define ARP_LINE_FORMAT "%" xstr(ARP_STRING_LEN) "s %*s %*s " \ + "%" xstr(ARP_STRING_LEN) "s %*s " \ + "%" xstr(ARP_STRING_LEN) "s" + +void cleanup_arp_cache (struct arp_cache * cache) { + struct arp_cache * del; + + while(cache) { + del = cache; + cache = del->next; + free(del); + } +} + +struct arp_cache * read_arp_cache () { + FILE *arpCache = fopen(ARP_CACHE, "r"); + if (!arpCache) { + perror("Arp Cache: Failed to open file \"" ARP_CACHE "\""); + return NULL; + } + + /* Ignore the first line, which contains the header */ + char header[ARP_BUFFER_LEN]; + if (!fgets(header, sizeof(header), arpCache)) { + return NULL; + } + + struct arp_cache * first = NULL; + struct arp_cache * prev = first; + struct arp_cache * current = first; + + while(true) { + current = malloc(sizeof(struct arp_cache)); + current->next = NULL; + + if (!current) { + goto cleanup; + } + + if (!first) { + first = current; + } + + if (fscanf(arpCache, ARP_LINE_FORMAT, current->ipAddr, current->hwAddr, current->device) != 3) { + fclose(arpCache); + free(current); + + return first; + } + + if (prev) { + prev->next = current; + } + + prev = current; + } + +cleanup: + cleanup_arp_cache(first); + + return NULL; +} + +// adapted from https://stackoverflow.com/a/39287433/3990041 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // htons etc + +#define PROTO_ARP 0x0806 +#define ETH2_HEADER_LEN 14 +#define HW_TYPE 1 +#define MAC_LENGTH 6 +#define IPV4_LENGTH 4 +#define ARP_REQUEST 0x01 +#define ARP_REPLY 0x02 +#define BUF_SIZE 60 + +void vfnc() { + return; +} + +#define debug(x...) vfnc(); // printf(x);printf("\n"); +#define err(x...) printf(x);printf("\n"); + +struct arp_header { + unsigned short hardware_type; + unsigned short protocol_type; + unsigned char hardware_len; + unsigned char protocol_len; + unsigned short opcode; + unsigned char sender_mac[MAC_LENGTH]; + unsigned char sender_ip[IPV4_LENGTH]; + unsigned char target_mac[MAC_LENGTH]; + unsigned char target_ip[IPV4_LENGTH]; +}; + +/* + * Converts struct sockaddr with an IPv4 address to network byte order uin32_t. + * Returns 0 on success. + */ +int int_ip4(struct sockaddr *addr, uint32_t *ip) +{ + if (addr->sa_family == AF_INET) { + struct sockaddr_in *i = (struct sockaddr_in *) addr; + *ip = i->sin_addr.s_addr; + return 0; + } else { + err("Not AF_INET"); + return 1; + } +} + +/* + * Formats sockaddr containing IPv4 address as human readable string. + * Returns 0 on success. + */ +int format_ip4(struct sockaddr *addr, char *out) +{ + if (addr->sa_family == AF_INET) { + struct sockaddr_in *i = (struct sockaddr_in *) addr; + const char *ip = inet_ntoa(i->sin_addr); + if (!ip) { + return -2; + } else { + strcpy(out, ip); + return 0; + } + } else { + return -1; + } +} + +/* + * Writes interface IPv4 address as network byte order to ip. + * Returns 0 on success. + */ +int get_if_ip4(int fd, const char *ifname, uint32_t *ip) { + int err = -1; + struct ifreq ifr; + memset(&ifr, 0, sizeof(struct ifreq)); + if (strlen(ifname) > (IFNAMSIZ - 1)) { + err("Too long interface name"); + goto out; + } + + strcpy(ifr.ifr_name, ifname); + if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) { + perror("SIOCGIFADDR"); + goto out; + } + + if (int_ip4(&ifr.ifr_addr, ip)) { + goto out; + } + err = 0; + +out: + return err; +} + +/* + * Sends an ARP who-has request to dst_ip + * on interface ifindex, using source mac src_mac and source ip src_ip. + */ +int send_arp(int fd, int ifindex, const unsigned char *src_mac, uint32_t src_ip, uint32_t dst_ip) +{ + int err = -1; + unsigned char buffer[BUF_SIZE]; + memset(buffer, 0, sizeof(buffer)); + + struct sockaddr_ll socket_address; + socket_address.sll_family = AF_PACKET; + socket_address.sll_protocol = htons(ETH_P_ARP); + socket_address.sll_ifindex = ifindex; + socket_address.sll_hatype = htons(ARPHRD_ETHER); + socket_address.sll_pkttype = (PACKET_BROADCAST); + socket_address.sll_halen = MAC_LENGTH; + socket_address.sll_addr[6] = 0x00; + socket_address.sll_addr[7] = 0x00; + + struct ethhdr *send_req = (struct ethhdr *) buffer; + struct arp_header *arp_req = (struct arp_header *) (buffer + ETH2_HEADER_LEN); + int index; + ssize_t ret, length = 0; + + /* Broadcast */ + memset(send_req->h_dest, 0xff, MAC_LENGTH); + + /* Target MAC zero */ + memset(arp_req->target_mac, 0x00, MAC_LENGTH); + + /* Set source mac to our MAC address */ + memcpy(send_req->h_source, src_mac, MAC_LENGTH); + memcpy(arp_req->sender_mac, src_mac, MAC_LENGTH); + memcpy(socket_address.sll_addr, src_mac, MAC_LENGTH); + + /* Setting protocol of the packet */ + send_req->h_proto = htons(ETH_P_ARP); + + /* Creating ARP request */ + arp_req->hardware_type = htons(HW_TYPE); + arp_req->protocol_type = htons(ETH_P_IP); + arp_req->hardware_len = MAC_LENGTH; + arp_req->protocol_len = IPV4_LENGTH; + arp_req->opcode = htons(ARP_REQUEST); + + debug("Copy IP address to arp_req"); + memcpy(arp_req->sender_ip, &src_ip, sizeof(uint32_t)); + memcpy(arp_req->target_ip, &dst_ip, sizeof(uint32_t)); + + ret = sendto(fd, buffer, 42, 0, (struct sockaddr *) &socket_address, sizeof(socket_address)); + if (ret == -1) { + perror("sendto():"); + goto out; + } + err = 0; +out: + return err; +} + +/* + * Gets interface information by name: + * IPv4 + * MAC + * ifindex + */ +int get_if_info(const char *ifname, uint32_t *ip, char *mac, int *ifindex) +{ + debug("get_if_info for %s", ifname); + int err = -1; + struct ifreq ifr; + + int sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); + if (sd <= 0) { + perror("socket()"); + goto out; + } + + if (strlen(ifname) > (IFNAMSIZ - 1)) { + err("Too long interface name, MAX=%i\n", IFNAMSIZ - 1); + goto out; + } + + strcpy(ifr.ifr_name, ifname); + + // Get interface index using name + if (ioctl(sd, SIOCGIFINDEX, &ifr) == -1) { + perror("SIOCGIFINDEX"); + goto out; + } + *ifindex = ifr.ifr_ifindex; + debug("interface index is %d\n", *ifindex); + + // Get MAC address of the interface + if (ioctl(sd, SIOCGIFHWADDR, &ifr) == -1) { + perror("SIOCGIFINDEX"); + goto out; + } + + // Copy mac address to output + memcpy(mac, ifr.ifr_hwaddr.sa_data, MAC_LENGTH); + + if (get_if_ip4(sd, ifname, ip)) { + goto out; + } + debug("get_if_info OK"); + + err = 0; +out: + if (sd > 0) { + debug("Clean up temporary socket"); + close(sd); + } + return err; +} + +/* + * Creates a raw socket that listens for ARP traffic on specific ifindex. + * Writes out the socket's FD. + * Return 0 on success. + */ +int bind_arp(int ifindex, int *fd) +{ + debug("bind_arp: ifindex=%i", ifindex); + int ret = -1; + + // Submit request for a raw socket descriptor. + *fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); + if (*fd < 1) { + perror("socket()"); + goto out; + } + + debug("Binding to ifindex %i", ifindex); + struct sockaddr_ll sll; + memset(&sll, 0, sizeof(struct sockaddr_ll)); + sll.sll_family = AF_PACKET; + sll.sll_ifindex = ifindex; + if (bind(*fd, (struct sockaddr*) &sll, sizeof(struct sockaddr_ll)) < 0) { + perror("bind"); + goto out; + } + + ret = 0; +out: + if (ret && *fd > 0) { + debug("Cleanup socket"); + close(*fd); + } + return ret; +} + +/* + * Reads a single ARP reply from fd. + * Returns 0 on success, writes arp cache entry + */ +char * read_arp(int fd, struct arp_cache * res) +{ + debug("read_arp"); + int ret = -1; + unsigned char buffer[BUF_SIZE]; + ssize_t length = recvfrom(fd, buffer, BUF_SIZE, 0, NULL, NULL); + int index; + if (length == -1) { + perror("recvfrom()"); + goto out; + } + struct ethhdr *rcv_resp = (struct ethhdr *) buffer; + struct arp_header *arp_resp = (struct arp_header *) (buffer + ETH2_HEADER_LEN); + if (ntohs(rcv_resp->h_proto) != PROTO_ARP) { + debug("Not an ARP packet"); + goto out; + } + if (ntohs(arp_resp->opcode) != ARP_REPLY) { + debug("Not an ARP reply"); + goto out; + } + debug("received ARP len=%ld", length); + struct in_addr sender_a; + memset(&sender_a, 0, sizeof(struct in_addr)); + memcpy(&sender_a.s_addr, arp_resp->sender_ip, sizeof(uint32_t)); + debug("Sender IP: %s", inet_ntoa(sender_a)); + + debug("Sender MAC: %02X:%02X:%02X:%02X:%02X:%02X", + arp_resp->sender_mac[0], + arp_resp->sender_mac[1], + arp_resp->sender_mac[2], + arp_resp->sender_mac[3], + arp_resp->sender_mac[4], + arp_resp->sender_mac[5]); + + sprintf(res->hwAddr, "%02x:%02x:%02x:%02x:%02x:%02x", + arp_resp->sender_mac[0], + arp_resp->sender_mac[1], + arp_resp->sender_mac[2], + arp_resp->sender_mac[3], + arp_resp->sender_mac[4], + arp_resp->sender_mac[5]); + + sprintf(res->ipAddr, "%s", inet_ntoa(sender_a)); + + ret = 0; + +out: + return ret; +} + +/* + * + * Sample code that sends an ARP who-has request on + * interface to IPv4 address . + * Returns 0 on success. + */ +struct arp_cache * test_arping(const char *ifname, const char *ip) { + struct arp_cache * res = malloc(sizeof(struct arp_cache)); + + strcpy(res->device, ifname); + res->next = NULL; + + int ret = -1; + uint32_t dst = inet_addr(ip); + if (dst == 0 || dst == 0xffffffff) { + err("Invalid source IP\n"); + return 1; + } + + int src; + int ifindex; + char mac[MAC_LENGTH]; + if (get_if_info(ifname, &src, mac, &ifindex)) { + err("get_if_info failed, interface %s not found or no IP set?", ifname); + goto out; + } + int arp_fd; + if (bind_arp(ifindex, &arp_fd)) { + err("Failed to bind_arp()"); + goto out; + } + + if (send_arp(arp_fd, ifindex, mac, src, dst)) { + err("Failed to send_arp"); + goto out; + } + + while(1) { + int r = read_arp(arp_fd, res); + if (r == 0) { + break; + } + } + + ret = 0; +out: + if (arp_fd) { + close(arp_fd); + arp_fd = 0; + } + + if (!ret) return res; + free(res); + return NULL; +} + +char * resolve_mac(struct arp_cache * cache, const char * intf, const char * ip, bool active_resolve) +{ + while(cache) { + if (!strcmp(&cache->ipAddr, ip) && !strcmp(&cache->device, intf)) { + char * out = malloc(ARP_BUFFER_LEN); + if (!out) { + return NULL; + } + + strcpy(out, &cache->hwAddr); + return out; + } + + if (!cache->next && active_resolve) { + cache->next = test_arping(intf, ip); + active_resolve = false; + } + + cache = cache->next; + } + + return NULL; +} diff --git a/package/gluon-mesh-olsrd/src/arp.h b/package/gluon-mesh-olsrd/src/arp.h new file mode 100644 index 0000000000..7e165627e5 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/arp.h @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include + +#define ARP_STRING_LEN 1023 +#define ARP_BUFFER_LEN (ARP_STRING_LEN + 1) + +struct arp_cache { + char ipAddr[ARP_BUFFER_LEN]; + char hwAddr[ARP_BUFFER_LEN]; + char device[ARP_BUFFER_LEN]; + struct arp_cache * next; +}; + +char * resolve_mac(struct arp_cache * cache, const char * intf, const char * ip, bool active_resolve); + +struct arp_cache * read_arp_cache (); +void cleanup_arp_cache (struct arp_cache * cache); diff --git a/package/gluon-mesh-olsrd/src/libolsrdhelper-neigh.c b/package/gluon-mesh-olsrd/src/libolsrdhelper-neigh.c new file mode 100644 index 0000000000..365294544c --- /dev/null +++ b/package/gluon-mesh-olsrd/src/libolsrdhelper-neigh.c @@ -0,0 +1,305 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "libolsrdhelper.h" +#include +#include "arp.h" + +void merge_neighs(json_object * out, json_object * neighs, char * version) { + json_object_object_foreach(neighs, mac, neighbour_original) { + json_object * neighbour = json_object_object_get(out, mac); + + if (!neighbour) { + neighbour = json_object_new_object(); + json_object_object_add(out, mac, neighbour); + } + + json_object_object_foreach(neighbour_original, key, new) { + json_object * cur = json_object_object_get(neighbour, key); + + if (!strcmp(key, "tq")) { + if (cur) { + json_object_object_add( + neighbour, + "tq", + json_object_new_double( + (json_object_get_double(cur) + json_object_get_double(new)) / 2 + ) + ); + } else { + json_object_object_add(neighbour, "tq", json_object_get(new)); + } + } else if (!strcmp(key, "etx")) { + if (cur) { + json_object_object_add( + neighbour, + "etx", + json_object_new_double( + (json_object_get_double(cur) + json_object_get_double(new)) / 2 + ) + ); + } else { + json_object_object_add(neighbour, "etx", json_object_get(new)); + } + } else if (!strcmp(key, "ip")) { + char str[10]; + sprintf(str, "%s_%s", version, key); + + json_object_object_add(neighbour, str, json_object_get(new)); + } else if (!strcmp(key, "best")) { + if (cur) { + json_object_object_add( + neighbour, + "best", + json_object_new_boolean( + json_object_get_boolean(cur) || json_object_get_boolean(new) + ) + ); + } else { + json_object_object_add(neighbour, "best", json_object_get(new)); + } + } else { + json_object_object_add(neighbour, key, json_object_get(new)); + } + } + } +} + +json_object * get_merged_neighs() { + struct olsr_info info; + + if (oi(&info)) + goto end; + + json_object *out = json_object_new_object(); + if (!out) { + goto end; + } + + if (info.olsr2.running) { + json_object *olsr2_neigh; + + olsr2_neigh = olsr2_get_neigh(); + if (!olsr2_neigh) { + goto fail; + } + + merge_neighs(out, olsr2_neigh, "olsr2"); + json_object_put(olsr2_neigh); + } + + if (info.olsr1.running) { + json_object *olsr1_neigh; + + olsr1_neigh = olsr1_get_neigh(); + if (!olsr1_neigh) { + goto fail; + } + + merge_neighs(out, olsr1_neigh, "olsr1"); + json_object_put(olsr1_neigh); + } + + goto end; + +fail: + json_object_put(out); + out = NULL; +end: + return out; +} + +struct json_object * olsr1_get_neigh(void) { + json_object *out = NULL; + json_object *resp; + + if (olsr1_get_nodeinfo("links", &resp)) { + goto cleanup; + } + + // olsr1 does not give us the mac that the other side uses + // this is bad, since macs are the magic uuids in gluon + // but since it's IP we can just do ARP + + // note that we run on the assumption that we've already commounicated with this ip, + // otherwise we just ping it + + struct arp_cache * cache = read_arp_cache(); + if (!cache) { + goto cleanup; + } + + /* + + links + + localIP "10.12.11.43" + remoteIP "10.12.11.1" + olsrInterface "mesh-vpn" + ifName "mesh-vpn" + validityTime 141239 + symmetryTime 123095 + asymmetryTime 25552910 + vtime 124000 + currentLinkStatus "SYMMETRIC" + previousLinkStatus "SYMMETRIC" + hysteresis 0 + pending false + lostLinkTime 0 + helloTime 0 + lastHelloTime 0 + seqnoValid false + seqno 0 + lossHelloInterval 3000 + lossTime 3595 + lossMultiplier 65536 + linkCost 1.084961 + linkQuality 1 + neighborLinkQuality 0.921 + + */ + + json_object *links = json_object_object_get(resp, "links"); + if (!links) { + goto cleanup_resp; + } + + out = json_object_new_object(); + + if (!out) { + goto cleanup_cache; + } + + size_t linkcount = json_object_array_length(links); + + for (size_t i = 0; i < linkcount; i++) { + struct json_object *link = json_object_array_get_idx(links, i); + if (!link) { + goto fail; + } + + struct json_object *neigh = json_object_new_object(); + if (!neigh) { + goto fail; + } + + J_OCPY2(neigh, link, "ifname", "ifName"); + // set this if we detect peer in hna is doing gw + json_object_object_add(neigh, "best", json_object_new_boolean(0)); + + const double linkQuality = json_object_get_double(json_object_object_get(link, "linkQuality")); + const double neighborLinkQuality = json_object_get_double(json_object_object_get(link, "neighborLinkQuality")); + + json_object_object_add(neigh, "etx", json_object_new_double(1 / (linkQuality * neighborLinkQuality))); + J_OCPY2(neigh, link, "ip", "remoteIP"); + + json_object_object_add(neigh, "tq", json_object_new_double(255 * (linkQuality * neighborLinkQuality))); + + char * mac = resolve_mac( + cache, + json_object_get_string(json_object_object_get(link, "ifName")), + json_object_get_string(json_object_object_get(link, "remoteIP")), + true + ); + + if (!mac) { + continue; + } + + json_object_object_add(out, mac, neigh); + + free(mac); + } + + goto cleanup_resp; + +fail: + json_object_put(out); + out = NULL; +cleanup_resp: + json_object_put(resp); +cleanup_cache: + cleanup_arp_cache(cache); +cleanup: + return out; +} + +struct json_object * olsr2_get_neigh(void) { + json_object *out = NULL; + json_object *resp; + + if (olsr2_get_nodeinfo("nhdpinfo jsonraw link", &resp)) { + goto cleanup; + } + + /* + + links + + "if":"olsr12", + "link_bindto":"fe80::ec67:efff:fed3:d856", + "link_vtime_value":20, + "link_itime_value":2, + "link_symtime":19.786, + "link_heardtime":19.886, + "link_vtime":39.886, + "link_status":"symmetric", + "link_dualstack":"-", + "link_mac":"b2:d6:9d:88:c1:58", + "link_flood_local":"true", + "link_flood_remote":"true", + "link_flood_willingness":7, + "neighbor_originator":"fdff:182f:da60:abc::66", + "neighbor_dualstack":"-", + "domain":0, + "domain_metric":"ff_dat_metric", + "domain_metric_in":"1.02kbit/s", + "domain_metric_in_raw":2105088, + "domain_metric_out":"1.02kbit/s", + "domain_metric_out_raw":2105088, + + */ + + json_object *links = json_object_object_get(resp, "link"); + if (!links) { + goto cleanup_resp; + } + + size_t linkcount = json_object_array_length(links); + + out = json_object_new_object(); + + if (!out) { + goto cleanup_resp; + } + + for (size_t i = 0; i < linkcount; i++) { + struct json_object *link = json_object_array_get_idx(links, i); + if (!link) { + goto fail; + } + + struct json_object *neigh = json_object_new_object(); + if (!neigh) { + goto fail; + } + + J_OCPY2(neigh, link, "ifname", "if"); + // set this if nhdpinfo returns this peer as being used for :: or 0.0.0.0 + json_object_object_add(neigh, "best", json_object_new_boolean(0)); + J_OCPY2(neigh, link, "etx", "link_vtime"); + J_OCPY2(neigh, link, "ip", "neighbor_originator"); + + json_object_object_add(out, json_object_get_string(json_object_object_get(link, "link_mac")), neigh); + } + + goto cleanup_resp; + +fail: + json_object_put(out); + out = NULL; +cleanup_resp: + json_object_put(resp); +cleanup: + return out; +} diff --git a/package/gluon-mesh-olsrd/src/libolsrdhelper.c b/package/gluon-mesh-olsrd/src/libolsrdhelper.c new file mode 100644 index 0000000000..cffb188ad6 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/libolsrdhelper.c @@ -0,0 +1,495 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "libolsrdhelper.h" +#include "uclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BASE_URL_1 "http://127.0.0.1:9090" +#define BASE_URL_1_LEN sizeof(BASE_URL_1) +#define BASE_URL_2 "http://127.0.0.1:1980/telnet" +#define BASE_URL_2_LEN sizeof(BASE_URL_2) + +// 10 mb +#define MAX_RESPONSE (1024 * 1024 * 1024 * 10) +#define PACKET (1024 * 1024) + +struct list_obj { + char* data; + size_t len; + struct list_obj *next; +}; + +struct get_data_ctx { + struct list_obj *current; + struct list_obj *start; + size_t total_length; + int error; +}; + +struct recv_json_ctx { + size_t size; + int error; + json_object *parsed; + + struct get_data_ctx *get_data; +}; + +struct recv_txt_ctx { + size_t size; + int error; + char *data; + + struct get_data_ctx *get_data; +}; + +// get_all_data is flaky and perhaps it's written a bit gruesomely + +/** Recieves all the data */ +struct get_data_ctx * get_all_data_init() { + struct get_data_ctx *ctx = malloc(sizeof(struct get_data_ctx)); + + if (!ctx) + goto fail; + + *ctx = (struct get_data_ctx){ + .total_length = 0, + .error = 0 + }; + + ctx->start = malloc(sizeof(struct list_obj)); + if (!ctx->start) + goto fail; + + *ctx->start = (struct list_obj){ + .len = 0, + .next = NULL + }; + + ctx->current = ctx->start; + + return ctx; + +fail: + if (ctx) { + if (ctx->start) + free(ctx->start); + + free(ctx); + } + + return NULL; +} + +// returns true when we are not done (if (process) return) +bool get_all_data_process(struct uclient *cl, struct get_data_ctx *ctx) { + char buf[PACKET]; + size_t len; + + while (true) { + len = uclient_read_account(cl, buf, sizeof(buf)); + + if (len == -1) { + ctx->error = 1; + return false; + } + + if (!len) { + return false; + } + + ctx->current->data = malloc(len); + if (!ctx->current->data) { + ctx->error = 1; + return false; + } + ctx->current->len = len; + + memcpy(ctx->current->data, buf, len); + ctx->total_length += len; + + ctx->current->next = malloc(sizeof(struct list_obj)); + *ctx->current->next = (struct list_obj){ + .len = 0, + .next = NULL + }; + ctx->current = ctx->current->next; + } + + return true; +} + +int get_all_data_finalize(struct get_data_ctx *ctx, char ** data, size_t * size) { + if (ctx->error) { + int error = ctx->error; + + // TODO: free + + return error; + }; + + ctx->total_length++; // trailing null + + *data = malloc(ctx->total_length); + if (!data) + return 1; + + size_t offset = 0; + + struct list_obj *prev; + + ctx->current = ctx->start; + while(ctx->current) { + if (ctx->current->len) { + memcpy(*data + offset, ctx->current->data, ctx->current->len); + offset += ctx->current->len; + free(ctx->current->data); + } + + prev = ctx->current; + ctx->current = ctx->current->next; + free(prev); + } + + (*data)[ctx->total_length - 1] = '\0'; + *size = ctx->total_length; + + return 0; +} + +/** Receives data from uclient and writes it to memory */ +static void recv_json_cb(struct uclient *cl) { + struct recv_json_ctx *ctx = uclient_get_custom(cl); + + if (get_all_data_process(cl, ctx->get_data)) { + return; + } +} + +static void recv_json_eof_cb(struct uclient *cl) { + struct recv_json_ctx *ctx = uclient_get_custom(cl); + + char * data; + size_t size; + + if (get_all_data_finalize(ctx->get_data, &data, &size)) { + ctx->error = UCLIENT_ERROR_SIZE_MISMATCH; + return; + } + + if (data[0] != "{"[0]) { + ctx->error = UCLIENT_ERROR_NOT_JSON; + return; + } + + // TODO: handle parser error, add error code for malformed json + ctx->parsed = json_tokener_parse(data); +} + +/** Receives data from uclient and writes it to memory */ +static void recv_txt_cb(struct uclient *cl) { + struct recv_txt_ctx *ctx = uclient_get_custom(cl); + + if (get_all_data_process(cl, ctx->get_data)) { + return; + } +} + +static void recv_txt_eof_cb(struct uclient *cl) { + struct recv_txt_ctx *ctx = uclient_get_custom(cl); + + if (get_all_data_finalize(ctx->get_data, &ctx->data, &ctx->size)) { + ctx->error = UCLIENT_ERROR_SIZE_MISMATCH; + return; + } +} + +bool success_exit(char *cmd, ...) { + pid_t pid = fork(); + + if (pid == 0) { + va_list val; + char **args = NULL; + int argc; + + // Determine number of variadic arguments + va_start(val, cmd); + argc = 2; // leading command + trailing NULL + while (va_arg(val, char *) != NULL) + argc++; + va_end(val); + + // Allocate args, put references to command / variadic arguments + NULL in args + args = (char **) malloc(argc * sizeof(char*)); + args[0] = cmd; + va_start(val, cmd); + int i = 0; + do { + i++; + args[i] = va_arg(val, char *); + // since this triggers AFTERWARDS, the trailing null still gets pushed + } while (args[i] != NULL); + va_end(val); + + execv(cmd, args); + exit(1); + return false; + } + + int status; + + if (waitpid(pid, &status, 0) == -1) { + return false; + } + + if (WIFEXITED(status)) { + return WEXITSTATUS(status) == 0; + } + + return false; +} + +// get enabled from site.conf mesh.olsrX.enabled, get running from service X status +int oi(struct olsr_info *out) { + int ex = 1; + + json_object *site = gluonutil_load_site_config(); + if (!site) + goto end; + + json_object *mesh = json_object_object_get(site, "mesh"); + if (!mesh) + goto cleanup_site; + + json_object *olsrd = json_object_object_get(mesh, "olsrd"); + if (!olsrd) + goto cleanup_site; + + json_object *v1 = json_object_object_get(olsrd, "v1_4"); + json_object *v2 = json_object_object_get(olsrd, "v2"); + + *out = (struct olsr_info){ + .olsr1 = { + .enabled = false, + .running = false, + }, + .olsr2 = { + .enabled = false, + .running = false, + } + }; + + if (v1 && json_object_get_boolean(json_object_object_get(v1, "enable"))) { + out->olsr1.enabled = true; + + if (success_exit("/etc/init.d/olsrd", "running", NULL)) { + out->olsr1.running = true; + } + } + + if (v2 && json_object_get_boolean(json_object_object_get(v2, "enable"))) { + out->olsr2.enabled = true; + + if (success_exit("/etc/init.d/olsrd2", "running", NULL)) { + out->olsr2.running = true; + } + } + + ex = 0; + +cleanup_site: + json_object_put(site); +end: + return ex; +} + +int olsr1_get_nodeinfo(const char *path, json_object **out) { + char url[BASE_URL_1_LEN + strlen(path) + 2]; + sprintf(url, "%s/%s", BASE_URL_1, path); + + uloop_init(); + struct recv_json_ctx json_ctx = { }; + json_ctx.get_data = get_all_data_init(); + if (!json_ctx.get_data) + return 1; + + int err_code = get_url(url, &recv_json_cb, &recv_json_eof_cb, &json_ctx, -1); + uloop_done(); + + if (err_code) { + return err_code; + } + + if (json_ctx.error) { + return json_ctx.error; + } + + *out = json_ctx.parsed; + + return 0; +} + +int olsr2_get_nodeinfo(const char *cmd, json_object **out) { + char url[BASE_URL_2_LEN + strlen(cmd) + 2]; + sprintf(url, "%s/%s", BASE_URL_2, cmd); + + uloop_init(); + struct recv_json_ctx json_ctx = { }; + json_ctx.get_data = get_all_data_init(); + if (!json_ctx.get_data) + return 1; + int err_code = get_url(url, &recv_json_cb, &recv_json_eof_cb, &json_ctx, -1); + uloop_done(); + + if (err_code) { + return err_code; + } + + if (json_ctx.error) { + return json_ctx.error; + } + + *out = json_ctx.parsed; + + return 0; +} + +int olsr2_get_nodeinfo_raw(const char *cmd, char **out) { + char url[BASE_URL_2_LEN + strlen(cmd) + 2]; + sprintf(url, "%s/%s", BASE_URL_2, cmd); + + uloop_init(); + struct recv_txt_ctx txt_ctx = { }; + txt_ctx.get_data = get_all_data_init(); + if (!txt_ctx.get_data) + return 1; + int err_code = get_url(url, &recv_txt_cb, &recv_txt_eof_cb, &txt_ctx, -1); + uloop_done(); + + if (err_code) { + return err_code; + } + + if (txt_ctx.error) { + return txt_ctx.error; + } + + *out = txt_ctx.data; + + return 0; +} + +/* + out is an optional parameter. If not set the raw fd will be returned. + Example: + int fd = socket_request("/var/run/mmfd.sock", "get_neighbours", NULL); + if (fd < 0) return NULL; + struct json_object * response = json_object_from_fd(fd); +*/ +int socket_request(const char *path, const char *cmd, char **out) { + int fd; + int ok = 0; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + goto end; + } + + struct sockaddr_un addr = {}; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + goto end; + } + + if (send(fd, cmd, strlen(cmd), 0) == -1) { + goto close_end; + } + + if (shutdown(fd, SHUT_WR)) { + goto close_end; + } + + if (out == NULL) { + return fd; + } + + size_t chunk_size = 4096; + char * chunk = malloc(chunk_size); + size_t current = 0; + size_t size_recv; + + if (!chunk) { + errno = ENOMEM; + goto close_end; + } + + // TODO: use uloop + if ((size_recv = recv(fd, chunk + current, chunk_size - current, MSG_PEEK | MSG_TRUNC)) < 0) { + if (size_recv + current > chunk_size) { + // chunk to small, adjust + char * newchunk = malloc(chunk_size + 4096); + if (!newchunk) { + errno = ENOMEM; + goto free_end; + } + + memcpy(newchunk, chunk, current); + free(chunk); + chunk = newchunk; + chunk_size += 4096; + } + + if ((size_recv = recv(fd, chunk + current, chunk_size - current, 0)) < 0) { + current += size_recv; + } else if (size_recv < 0) { + goto free_end; + } + } else if (size_recv < 0) { + goto free_end; + } + + chunk[current] = '\0'; + + *out = chunk; + + errno = 0; + ok = 1; + +free_end: + free(chunk); +close_end: + close(fd); +end: + if (!ok) { + return -errno; + } + + return 0; +} + +json_object * socket_request_json(const char *path, const char *cmd) { + json_object * ret = NULL; + + int fd = socket_request(path, cmd, NULL); + if (fd < 0) { + goto end; + } + + ret = json_object_from_fd(fd); + + close(fd); +end: + return ret; +} diff --git a/package/gluon-mesh-olsrd/src/libolsrdhelper.h b/package/gluon-mesh-olsrd/src/libolsrdhelper.h new file mode 100755 index 0000000000..314bbb42b9 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/libolsrdhelper.h @@ -0,0 +1,61 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#pragma once + +#include +#include +#include +#include + +struct olsr1_info { + bool enabled; + bool running; +}; + +struct olsr2_info { + bool enabled; + bool running; +}; + +struct olsr_info { + struct olsr1_info olsr1; + struct olsr2_info olsr2; +}; + +int oi(struct olsr_info *out); + +int olsr1_get_nodeinfo(const char *path, json_object **out); + +int olsr2_get_nodeinfo(const char *cmd, json_object **out); +int olsr2_get_nodeinfo_raw(const char *cmd, char **out); + +struct json_object * olsr1_get_neigh(void); +struct json_object * olsr2_get_neigh(void); +void merge_neighs(json_object * out, json_object * neighs, char * version); +json_object * get_merged_neighs(); + +// stuff that could be in a shared library named responddhelper + +/* + out is an optional parameter. If not set the raw fd will be returned. + Example: + int fd = socket_request("/var/run/mmfd.sock", "get_neighbours", NULL); + if (fd < 0) return NULL; + struct json_object * response = json_object_from_fd(fd); +*/ +int socket_request(const char *path, const char *cmd, char **out); + +json_object * socket_request_json(const char *path, const char *cmd); + +// macros for json c + +#define J_OUT(x) json_object *out = json_object_get((x)); \ + json_object_put(resp); \ + return out; + +#define J_OGET(obj, key) json_object_get(json_object_object_get(obj, key)) + +#define J_OCPY(dst, src, key) json_object_object_add(dst, key, json_object_get(json_object_object_get(src, key))) + +#define J_OCPY2(dst, src, dkey, skey) json_object_object_add(dst, dkey, json_object_get(json_object_object_get(src, skey))) diff --git a/package/gluon-mesh-olsrd/src/libolsrdhelper.pc b/package/gluon-mesh-olsrd/src/libolsrdhelper.pc new file mode 100644 index 0000000000..50a3d902ba --- /dev/null +++ b/package/gluon-mesh-olsrd/src/libolsrdhelper.pc @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: MIT +# +# olsr helper functions library +# +# Copyright (c) 2022, Maciej Krüger +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +prefix=/usr +exec_prefix=/usr +libdir=${exec_prefix}/lib +includedir=${prefix}/include/gluon-mesh-olsrd + +Name: libolsrdhelper +Version: 1 +Description: olsr helper functions library +Requires.private: json-c +Libs: -luclient -lolsrdhelper -L${libdir} -lgluonutil -luci +Libs.private: +Cflags: -I${includedir} diff --git a/package/gluon-mesh-olsrd/src/olsr-respondd.c b/package/gluon-mesh-olsrd/src/olsr-respondd.c new file mode 100644 index 0000000000..8dad6535f3 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/olsr-respondd.c @@ -0,0 +1,41 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "respondd-common.h" +#include +#include + +int run (json_object * resp) { + if (!resp) return 1; + + printf("%s", json_object_to_json_string_ext(resp, JSON_C_TO_STRING_PLAIN)); + + json_object_put(resp); + + return 0; +} + +int main (int argc, char *argv[]) { + if (argc < 2) { + goto help; + } + + switch(argv[1][0]) { + case 115: { // s + return run(real_respondd_provider_statistics()); + } + case 110: { // n + return run(real_respondd_provider_neighbours()); + } + case 105: { // i + return run(real_respondd_provider_nodeinfo()); + } + default: { + goto help; + } + } + +help: + printf("i = nodeinfo, n = neighbours, s = statistics\n"); + return 2; +} diff --git a/package/gluon-mesh-olsrd/src/olsrd-debug.c b/package/gluon-mesh-olsrd/src/olsrd-debug.c new file mode 100644 index 0000000000..2fada678ba --- /dev/null +++ b/package/gluon-mesh-olsrd/src/olsrd-debug.c @@ -0,0 +1,51 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include +#include "respondd-real.h" +#include +#include +#include + +void d(const char * fnc, json_object * res) { + if (!res) { + printf("%s: failed\n\n", fnc); + } else { + const char * str = json_object_to_json_string_ext(res, JSON_C_TO_STRING_PLAIN); + printf("%s: %s\n\n", fnc, str); + json_object_put(res); + } +} + +void d2(const char * fnc, json_object * res, int code) { + if (code) { + printf("%s: failed %i\n\n", fnc, code); + assert(!res); + } else { + const char * str = json_object_to_json_string_ext(res, JSON_C_TO_STRING_PLAIN); + printf("%s: %s\n\n", fnc, str); + json_object_put(res); + } +} + +int main (int argc, char *argv[]) { + json_object *resp; + + d("get_traffic", get_traffic()); + + d("provider_neighbours", real_respondd_provider_neighbours()); + + d("provider_nodeinfo", real_respondd_provider_nodeinfo()); + + d("provider_statistics", real_respondd_provider_statistics()); + + d2("olsr1_nodeinfo", resp, olsr1_get_nodeinfo("version", &resp)); + + d2("olsr2_nodeinfo", resp, olsr2_get_nodeinfo("nhdpinfo jsonraw link", &resp)); + + d("l3roamd_clients", socket_request_json("/var/run/l3roamd.sock", "get_clients")); + + d("make_safe", make_safe("n")); + + return 0; +} diff --git a/package/gluon-mesh-olsrd/src/olsrd.c b/package/gluon-mesh-olsrd/src/olsrd.c new file mode 100644 index 0000000000..2f7d531244 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/olsrd.c @@ -0,0 +1,151 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include +#include +#include + +#include +#include +#include + +#include + +#define OLSRD "gluon.olsrd" + +static int find_module_version (lua_State *L) { + const char *mod = luaL_checkstring(L, 1); + + DIR *d = opendir("/usr/lib"); + + if (d == NULL) + return luaL_error(L, "cannot open /usr/lib: %s", strerror(errno)); + + struct dirent *entry; + while ((entry = readdir(d)) != NULL) { + if (entry->d_type == DT_REG && !strncmp(mod, entry->d_name, strlen(mod))) { + lua_pushstring(L, entry->d_name); + closedir(d); + return 1; + } + } + + closedir(d); + return luaL_error(L, "mod %s not found", mod); +} + +static int lua_olsr1_get_nodeinfo (lua_State *L) { + const char *query = luaL_checkstring(L, 1); + + json_object *resp; + + if (olsr1_get_nodeinfo(query, &resp)) + return luaL_error(L, "olsr1_get_nodeinfo(%s) failed", query); + + lua_jsonc_push_json(L, resp); + + return 1; +} + +static int lua_olsr2_get_nodeinfo (lua_State *L) { + const char *query = luaL_checkstring(L, 1); + + json_object *resp; + + if (olsr2_get_nodeinfo(query, &resp)) + return luaL_error(L, "olsr2_get_nodeinfo(%s) failed", query); + + lua_jsonc_push_json(L, resp); + + return 1; +} + +static int lua_olsr2_get_nodeinfo_raw (lua_State *L) { + const char *query = luaL_checkstring(L, 1); + + char *resp; + + if (olsr2_get_nodeinfo_raw(query, &resp)) + return luaL_error(L, "olsr2_get_nodeinfo_raw(%s) failed", query); + + lua_pushstring(L, resp); + + return 1; +} + +static int lua_olsr1_get_neigh (lua_State *L) { + json_object *resp = olsr1_get_neigh(); + + if (!resp) + return luaL_error(L, "olsr2_get_neigh() failed"); + + lua_jsonc_push_json(L, resp); + + return 1; +} + +static int lua_olsr2_get_neigh (lua_State *L) { + json_object *resp = olsr2_get_neigh(); + + if (!resp) + return luaL_error(L, "olsr2_get_neigh() failed"); + + lua_jsonc_push_json(L, resp); + + return 1; +} + +static int lua_oi (lua_State *L) { + struct olsr_info info; + + if (oi(&info)) + return luaL_error(L, "olsr_info() call failed"); + + lua_newtable(L); + + + lua_newtable(L); // olsr1 + + lua_pushboolean(L, info.olsr1.enabled); + lua_setfield(L, -2, "enabled"); + + lua_pushboolean(L, info.olsr1.running); + lua_setfield(L, -2, "running"); + + lua_setfield(L, -2, "olsr1"); + + + lua_newtable(L); // olsr2 + + lua_pushboolean(L, info.olsr2.enabled); + lua_setfield(L, -2, "enabled"); + + lua_pushboolean(L, info.olsr2.running); + lua_setfield(L, -2, "running"); + + lua_setfield(L, -2, "olsr2"); + + return 1; +} + +static const luaL_reg olsrd_methods[] = { + { "find_module_version", find_module_version }, + + { "oi", lua_oi }, + + { "olsr1_get_nodeinfo", lua_olsr1_get_nodeinfo }, + + { "olsr2_get_nodeinfo", lua_olsr2_get_nodeinfo }, + { "olsr2_get_nodeinfo_raw", lua_olsr2_get_nodeinfo_raw }, + + { "olsr1_get_neigh", lua_olsr1_get_neigh }, + { "olsr2_get_neigh", lua_olsr2_get_neigh }, + { } +}; + +int luaopen_gluon_olsrd(lua_State *L) +{ + luaL_register(L, OLSRD, olsrd_methods); + + return 1; +} diff --git a/package/gluon-mesh-olsrd/src/respondd-common.h b/package/gluon-mesh-olsrd/src/respondd-common.h new file mode 100644 index 0000000000..b5bdc97647 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-common.h @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#pragma once + +#define make_safe_fnc(name) \ +struct json_object * respondd_provider_##name (void) { \ + return make_safe(&(#name)); \ +} + +struct json_object * make_safe(const char * name); + +struct json_object * respondd_provider_neighbours(); +struct json_object * respondd_provider_nodeinfo(); +struct json_object * respondd_provider_statistics(); diff --git a/package/gluon-mesh-olsrd/src/respondd-neighbours.c b/package/gluon-mesh-olsrd/src/respondd-neighbours.c new file mode 100644 index 0000000000..4ff5ed90d4 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-neighbours.c @@ -0,0 +1,67 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "respondd-common.h" +#include "respondd-real.h" + +#include +#include +#include +#include + +struct json_object * real_respondd_provider_neighbours() { + json_object * ret = json_object_new_object(); + if (!ret) { + return NULL; + } + + json_object * out = json_object_new_object(); + if (!out) { + goto fail; + } + + json_object_object_add(ret, "batadv", out); + + json_object * neighs = get_merged_neighs(); + + if (!neighs) { + goto fail; + } + + json_object_object_foreach(neighs, mac, neigh) { + const char * ifname = json_object_get_string(json_object_object_get(neigh, "ifname")); + + if (!ifname) { + continue; + } + + char *ifaddr = gluonutil_get_interface_address(ifname); + if (!ifaddr) { + continue; + } + + json_object * intf = json_object_object_get(out, ifaddr); + if (!intf) { + intf = json_object_new_object(); + json_object_object_add(out, ifaddr, intf); + } + + json_object * ifneigh = json_object_object_get(intf, "neighbours"); + if (!ifneigh) { + ifneigh = json_object_new_object(); + json_object_object_add(intf, "neighbours", ifneigh); + } + + json_object_object_add(ifneigh, mac, json_object_get(neigh)); + } + + json_object_put(neighs); + +end: + return ret; + +fail: + json_object_put(ret); + ret = NULL; + goto end; +} diff --git a/package/gluon-mesh-olsrd/src/respondd-nodeinfo.c b/package/gluon-mesh-olsrd/src/respondd-nodeinfo.c new file mode 100644 index 0000000000..7fc8402d68 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-nodeinfo.c @@ -0,0 +1,503 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-FileCopyrightText: 2016-2019, Matthias Schiffer */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "respondd-common.h" +#include "respondd-real.h" + +#include + +#include + +#include + +#include +#include +#include + +static json_object * olsr1_get_plugins(void) { + json_object *resp; + + if (olsr1_get_nodeinfo("plugins", &resp)) + return NULL; + + J_OUT(json_object_object_get(resp, "plugins")); +} + +static json_object * olsr1_get_version (void) { + json_object *resp; + + if (olsr1_get_nodeinfo("version", &resp)) + return NULL; + + J_OUT(json_object_object_get(json_object_object_get(resp, "version"), "version")); +} + +static json_object * olsr2_get_version (void) { + json_object *resp; + + if (olsr2_get_nodeinfo("systeminfo jsonraw version", &resp)) + return NULL; + + J_OUT(json_object_object_get(json_object_array_get_idx(json_object_object_get(resp, "version"), 0), "version_text")); +} + +static json_object * olsr1_get_addresses (void) { + json_object *resp; + + if (olsr1_get_nodeinfo("interfaces", &resp)) + return NULL; + + /* + + interfaces [] + name "m_uplink" + configured true + hostEmulation false + hostEmulationAddress "0.0.0.0" + olsrInterface + -- might be false (and then ipAddress key is missing) + up true + ipv4Address "10.12.23.234" + ipv4Netmask "255.255.0.0" + ipv4Broadcast "10.12.255.255" + mode "mesh" + ipv6Address "::" + ipv6Multicast "::" + -- we need this + ipAddress "10.12.23.234" + .. + InterfaceConfiguration {…} + InterfaceConfigurationDefaults {…} + */ + + json_object *out = json_object_new_array(); + + json_object *intfs = json_object_object_get(resp, "interfaces"); + + for (int i = 0; i < json_object_array_length(intfs); i++) { + struct json_object *el = json_object_array_get_idx(intfs, i); + struct json_object *olsr = json_object_object_get(el, "olsrInterface"); + struct json_object *ip = J_OGET(olsr, "ipAddress"); // might be null (up=false) + if (ip) { + json_object_array_add(out, ip); + } + } + + return out; +} + +static json_object * olsr2_get_addresses (void) { + /* + + > olsrv2info jsonraw originator + {"originator": [{ + "originator":"-"},{ + "originator":"fdff:182f:da60:abc:23:ee1a:dec6:d17c"}]} + + if you're wondering "what the fuck": me too, me too + + */ + + json_object *resp; + + if (olsr2_get_nodeinfo("olsrv2info jsonraw originator", &resp)) + return NULL; + + json_object *out = json_object_new_array(); + + json_object *origs = json_object_object_get(resp, "originator"); + + for (int i = 0; i < json_object_array_length(origs); i++) { + json_object *el = json_object_array_get_idx(origs, i); + json_object *orig = J_OGET(el, "originator"); + if (json_object_get_string(orig)[0] != "-"[0]) { + json_object_array_add(out, orig); + } + } + + json_object_put(resp); + + return out; +} + +struct json_object * olsr1_get_interfaces (void) { + json_object *resp; + + if (olsr1_get_nodeinfo("interfaces", &resp)) + return NULL; + + /* + + interfaces [] + name "m_uplink" + configured true + hostEmulation false + hostEmulationAddress "0.0.0.0" + olsrInterface + -- might be false (and then ipAddress key is missing) + up true + ipv4Address "10.12.23.234" + ipv4Netmask "255.255.0.0" + ipv4Broadcast "10.12.255.255" + mode "mesh" + ipv6Address "::" + ipv6Multicast "::" + -- we need this + ipAddress "10.12.23.234" + .. + InterfaceConfiguration {…} + InterfaceConfigurationDefaults {…} + */ + + json_object *out = json_object_new_object(); + + json_object *intfs = json_object_object_get(resp, "interfaces"); + + for (int i = 0; i < json_object_array_length(intfs); i++) { + json_object *el = json_object_array_get_idx(intfs, i); + json_object *olsr = json_object_object_get(el, "olsrInterface"); + + json_object *intf = json_object_new_object(); + json_object_object_add(out, + json_object_get_string(json_object_object_get(el, "name")), + intf + ); + + J_OCPY(intf, el, "configured"); + J_OCPY(intf, olsr, "up"); + J_OCPY(intf, olsr, "ipAddress"); + J_OCPY(intf, olsr, "mode"); + } + + return out; +} + +struct json_object * olsr2_get_interfaces (void) { + json_object *resp; + + if (olsr2_get_nodeinfo("nhdpinfo jsonraw interface", &resp)) + return NULL; + + /* + + we're currently just using nhdpinfo, but layer2info might be interesting at some point + + > nhdpinfo jsonraw interface + {"interface": [{ + "if":"ibss0", + "if_bindto_v4":"-", + "if_bindto_v6":"-", + "if_mac":"b8:69:f4:0d:1a:3c", + "if_flooding_v4":"false", + "if_flooding_v6":"false", + "if_dualstack_mode":"-"},{ + "if":"lo", + "if_bindto_v4":"-", + "if_bindto_v6":"fdff:182f:da60:abc:23:ee1a:dec6:d17c", + "if_mac":"00:00:00:00:00:00", + "if_flooding_v4":"false", + "if_flooding_v6":"false", + "if_dualstack_mode":"-"},{ + + > layer2info jsonraw interface + {"interface": [{ + "if":"ibss0", + "if_index":14, + "if_local_addr":"b8:69:f4:0d:1a:3c", + "if_type":"wireless", + "if_dlep":"false", + "if_ident":"", + "if_ident_addr":"", + "if_lastseen":0, + "if_frequency1":"0", + "if_frequency2":"0", + "if_bandwidth1":"0", + "if_bandwidth2":"0", + "if_noise":"-92", + "if_ch_active":"40448.827", + "if_ch_busy":"1015.889", + "if_ch_rx":"263.867", + "if_ch_tx":"127.433", + "if_mtu":"0", + "if_mcs_by_probing":"true", + "if_rx_only_unicast":"false", + "if_tx_only_unicast":"false", + "if_frequency1_origin":"", + "if_frequency2_origin":"", + "if_bandwidth1_origin":"", + "if_bandwidth2_origin":"", + "if_noise_origin":"nl80211", + "if_ch_active_origin":"nl80211", + "if_ch_busy_origin":"nl80211", + "if_ch_rx_origin":"nl80211", + "if_ch_tx_origin":"nl80211", + "if_mtu_origin":"", + "if_mcs_by_probing_origin":"nl80211", + "if_rx_only_unicast_origin":"", + "if_tx_only_unicast_origin":""},{ + "if":"lo", + "if_index":1, + "if_local_addr":"00:00:00:00:00:00", + "if_type":"wireless", + "if_dlep":"false", + "if_ident":"", + "if_ident_addr":"", + "if_lastseen":0, + "if_frequency1":"0", + "if_frequency2":"0", + "if_bandwidth1":"0", + "if_bandwidth2":"0", + "if_noise":"0", + "if_ch_active":"0", + "if_ch_busy":"0", + "if_ch_rx":"0", + "if_ch_tx":"0", + "if_mtu":"0", + "if_mcs_by_probing":"false", + "if_rx_only_unicast":"false", + "if_tx_only_unicast":"false", + "if_frequency1_origin":"", + "if_frequency2_origin":"", + "if_bandwidth1_origin":"", + "if_bandwidth2_origin":"", + "if_noise_origin":"", + "if_ch_active_origin":"", + "if_ch_busy_origin":"", + "if_ch_rx_origin":"", + "if_ch_tx_origin":"", + "if_mtu_origin":"", + "if_mcs_by_probing_origin":"", + "if_rx_only_unicast_origin":"", + "if_tx_only_unicast_origin":""},{ + + */ + + json_object *out = json_object_new_object(); + + json_object *intfs = json_object_object_get(resp, "interface"); + + for (int i = 0; i < json_object_array_length(intfs); i++) { + json_object *el = json_object_array_get_idx(intfs, i); + + json_object *intf = json_object_new_object(); + json_object_object_add(out, + json_object_get_string(json_object_object_get(el, "if")), + intf + ); + + J_OCPY2(intf, el, "mac", "if_mac"); + + if (json_object_get_string(json_object_object_get(el, "if_bindto_v4"))[0] != "-"[0]) { + J_OCPY2(intf, el, "if_bindto_v4", "v4"); + } else { + json_object_object_add(intf, "v4", json_object_new_null()); + } + + if (json_object_get_string(json_object_object_get(el, "if_bindto_v6"))[0] != "-"[0]) { + J_OCPY2(intf, el, "v6", "if_bindto_v6"); + } else { + json_object_object_add(intf, "v6", json_object_new_null()); + } + } + + return out; +} + +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 void mesh_add_subif(const char *ifname, struct json_object *wireless, struct json_object *wired, + struct json_object *tunnel, struct json_object *other) { + struct json_object *address = gluonutil_wrap_and_free_string(gluonutil_get_interface_address(ifname)); + + /* In case of VLAN and bridge interfaces, we want the lower interface + * to determine the interface type (but not for the interface address) */ + char lowername[IF_NAMESIZE]; + gluonutil_get_interface_lower(lowername, ifname); + + switch(gluonutil_get_interface_type(lowername)) { + case GLUONUTIL_INTERFACE_TYPE_WIRELESS: + json_object_array_add(wireless, address); + break; + + case GLUONUTIL_INTERFACE_TYPE_WIRED: + json_object_array_add(wired, address); + break; + + case GLUONUTIL_INTERFACE_TYPE_TUNNEL: + json_object_array_add(tunnel, address); + break; + + default: + 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 *wired = 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; + int globreturn; + if (!(globreturn = 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, wired, tunnel, other); + } + + globfree(&lower); + + // TODO: add the device's own mac aswell + // not sure if we're handling this correctly and if it may make more sense + // to just query this + mesh_add_subif(ifname, wireless, wired, tunnel, other); + } else if (globreturn == GLOB_NOMATCH) { + // this is already a lower interface, add directly + mesh_add_subif(ifname, wireless, wired, tunnel, other); + } + + struct json_object *ret = json_object_new_object(); + add_if_not_empty(ret, "wireless", wireless); + add_if_not_empty(ret, "wired", wired); + add_if_not_empty(ret, "tunnel", tunnel); + add_if_not_empty(ret, "other", other); + return ret; +} + +struct json_object * real_respondd_provider_nodeinfo() { + struct olsr_info info; + + json_object *ret = NULL; + + if (oi(&info)) + return ret; + + ret = json_object_new_object(); + if (!ret) { + return NULL; + } + + struct json_object *network = json_object_new_object(); + + struct json_object *n_addresses = json_object_new_array(); + + json_object_object_add(network, "addresses", n_addresses); + + struct json_object *n_interfaces = json_object_new_object(); + + json_object_object_add(network, "interfaces", n_interfaces); + + struct json_object *n_mesh = json_object_new_object(); + + json_object_object_add(network, "mesh", n_mesh); + + json_object_object_add(ret, "network", network); + + struct json_object *software = json_object_new_object(); + + json_object_object_add(ret, "software", software); + + if (info.olsr1.enabled) { + struct json_object *software_olsr1 = json_object_new_object(); + + json_object_object_add(software_olsr1, "running", json_object_new_boolean(info.olsr1.running)); + + if (info.olsr1.running) { + struct json_object *version = olsr1_get_version(); + if (version) + json_object_object_add(software_olsr1, "version", version); + + struct json_object *plugins = olsr1_get_plugins(); + if (plugins) + json_object_object_add(software_olsr1, "plugins", plugins); + + struct json_object *addresses = olsr1_get_addresses(); + if (addresses) { + json_object_object_add(software_olsr1, "addresses", addresses); + + for (int i = 0; i < json_object_array_length(addresses); i++) + json_object_array_add(n_addresses, json_object_get(json_object_array_get_idx(addresses, i))); + } + + struct json_object *interfaces = olsr1_get_interfaces(); + if (interfaces) { + json_object_object_add(software_olsr1, "interfaces", interfaces); + + json_object_object_foreach(interfaces, name, interface) { + json_object *merged_interface = json_object_object_get(n_interfaces, name); + + if (!merged_interface) { + merged_interface = json_object_new_object(); + json_object_object_add(n_interfaces, name, merged_interface); + } + + json_object_object_add(merged_interface, "olsr1", json_object_get(interface)); + } + } + } + + json_object_object_add(software, "olsr1", software_olsr1); + } + + if (info.olsr2.enabled) { + struct json_object *software_olsr2 = json_object_new_object(); + + json_object_object_add(software_olsr2, "running", json_object_new_boolean(info.olsr2.running)); + + if (info.olsr2.running) { + struct json_object *version = olsr2_get_version(); + if (version) + json_object_object_add(software_olsr2, "version", version); + + struct json_object *addresses = olsr2_get_addresses(); + if (addresses) { + json_object_object_add(software_olsr2, "addresses", addresses); + + for (int i = 0; i < json_object_array_length(addresses); i++) + json_object_array_add(n_addresses, json_object_get(json_object_array_get_idx(addresses, i))); + } + + struct json_object *interfaces = olsr2_get_interfaces(); + if (interfaces) { + json_object_object_add(software_olsr2, "interfaces", interfaces); + + json_object_object_foreach(interfaces, name, interface) { + json_object *merged_interface = json_object_object_get(n_interfaces, name); + + if (!merged_interface) { + merged_interface = json_object_new_object(); + json_object_object_add(n_interfaces, name, merged_interface); + } + + json_object_object_add(merged_interface, "olsr2", json_object_get(interface)); + } + } + } + + json_object_object_add(software, "olsr2", software_olsr2); + } + + json_object_object_foreach(n_interfaces, name, value) { + if (strcmp(name, "lo")) { // everything that ISN'T loopback + struct json_object * intf = json_object_new_object(); + json_object_object_add(intf, "interfaces", get_mesh_subifs(name)); + json_object_object_add(n_mesh, name, intf); + } + } + + return ret; +} diff --git a/package/gluon-mesh-olsrd/src/respondd-real.h b/package/gluon-mesh-olsrd/src/respondd-real.h new file mode 100644 index 0000000000..2043c5dbb9 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-real.h @@ -0,0 +1,9 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#pragma once + +struct json_object * real_respondd_provider_neighbours(); +struct json_object * real_respondd_provider_nodeinfo(); +struct json_object * real_respondd_provider_statistics(); +struct json_object * get_traffic(void); diff --git a/package/gluon-mesh-olsrd/src/respondd-statistics.c b/package/gluon-mesh-olsrd/src/respondd-statistics.c new file mode 100644 index 0000000000..91c29123ee --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-statistics.c @@ -0,0 +1,170 @@ +/* SPDX-FileCopyrightText: 2023, Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "respondd-common.h" +#include "respondd-real.h" + +#include + +#include + +#include +#include + +#include +#include + +#include + +static void add_gateway(struct json_object *obj) { + // TODO: get gateway (like in state-check scripts) + // note: we can only specify ONE gateway so we'll have trouble with + // ipv4+ipv6 having different gateways + // { gateway: mac, gateway_tq: tq, gateway_nexthop: best_node_mac } +} + +int rtnl_get_link(struct rtnl_link_stats64 ** out) { + *out = NULL; + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + return errno; + } + + struct sockaddr_nl recv_addr; + memset(&recv_addr, 0, sizeof(recv_addr)); + recv_addr.nl_family = AF_NETLINK; + recv_addr.nl_pid = getpid(); + recv_addr.nl_groups = 0; + + if (bind(sock, (struct sockaddr*)&recv_addr, sizeof(recv_addr)) < 0) { + goto socket_end; + } + + struct sockaddr_nl dest_addr; + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; + dest_addr.nl_groups = 0; + + struct { + struct nlmsghdr nh; + struct ifinfomsg ifmsg; + char attrbuf[512]; + } req; + struct rtattr *rta; + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST; + req.nh.nlmsg_type = RTM_GETLINK; + req.ifmsg.ifi_family = AF_UNSPEC; + req.ifmsg.ifi_index = if_nametoindex("local-port"); + req.ifmsg.ifi_change = 0xffffffff; /* ??? */ + + rta = (struct rtattr *)(((char *) &req) + + NLMSG_ALIGN(req.nh.nlmsg_len)); + rta->rta_type = IFLA_STATS; + rta->rta_len = RTA_LENGTH(sizeof(unsigned int)); + req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + + RTA_LENGTH(sizeof(struct rtnl_link_stats)); + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + return errno; + } + + char buf[4096]; + if (recv(sock, &buf, 4096, 0) < 0) + return errno; + + struct nlmsghdr *recv_hdr = (struct nlmsghdr*)buf; + struct ifinfomsg *infomsg = NLMSG_DATA(recv_hdr); + + rta = IFLA_RTA(infomsg); + int len = recv_hdr->nlmsg_len; + + while (RTA_OK(rta, len)){ + if (rta->rta_type == IFLA_STATS64) { + struct rtnl_link_stats64 *stats64 = RTA_DATA(rta); + struct rtnl_link_stats64 *copy = malloc(sizeof(struct rtnl_link_stats64)); + if (!copy) { + errno = ENOMEM; + goto socket_end; + } + memcpy(copy, stats64, sizeof(struct rtnl_link_stats64)); + *out = copy; + break; + } + + rta = RTA_NEXT(rta, len); + } + + if (!*out) { + return ENODATA; + } + +socket_end: + close(sock); +end: + return errno; +} + +#define SET_STAT(jsonc, field) \ + json_object_object_add((jsonc), #field, \ + json_object_new_int64(rtnl->jsonc ## _ ## field)); + +struct json_object * get_traffic(void) { + struct json_object *out = NULL; + + struct rtnl_link_stats64 * rtnl; + + if (rtnl_get_link(&rtnl)) + goto end; + + struct json_object *rx = json_object_new_object(); + SET_STAT(rx, bytes); + SET_STAT(rx, packets); + SET_STAT(rx, dropped); + SET_STAT(rx, errors); + + struct json_object *tx = json_object_new_object(); + SET_STAT(tx, bytes); + SET_STAT(tx, packets); + SET_STAT(tx, dropped); + SET_STAT(tx, errors); + + out = json_object_new_object(); + json_object_object_add(out, "rx", rx); + json_object_object_add(out, "tx", tx); + + free(rtnl); + +end: + return out; +} + + +static struct json_object * get_clients(void) { + struct json_object * response = socket_request_json("/var/run/l3roamd.sock", "get_clients"); + if (!response) return NULL; + + struct json_object *ret = json_object_new_object(); + + J_OCPY2(ret, response, "total", "clients"); + json_object_object_add(ret, "list", json_object_object_get(response, "clientlist") ? J_OGET(response, "clientlist") : json_object_new_object()); + + json_object_put(response); + + return ret; +} + +struct json_object * real_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; +} diff --git a/package/gluon-mesh-olsrd/src/respondd.c b/package/gluon-mesh-olsrd/src/respondd.c new file mode 100644 index 0000000000..0cfda8c0e6 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd.c @@ -0,0 +1,129 @@ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "respondd-common.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +make_safe_fnc(neighbours) +make_safe_fnc(nodeinfo) +make_safe_fnc(statistics) + +#define RUN_LOG_ERROR_DESC(f, s) \ + if ((f)) { \ + fprintf(stderr, "Failed to " # f ": %s\n", strerror(errno)); \ + } + +#define RUN_LOG_ERROR(f) RUN_LOG_ERROR_DESC(f, #f) + +struct json_object * run_safe(const char * filename, const char * name, bool foreground) { + FILE *fp; + FILE *fo; + int fd; + + char exec[40]; + sprintf(exec, "%s %s", "/usr/bin/olsr-respondd", name); + + fo = fopen(filename, "a"); + if (!fo) { + fprintf(stderr, "Failed to open %s: %s\n", filename, strerror(errno)); + } else { + fd = fileno(fo); + if (flock(fd, LOCK_EX)) { + if (errno != EAGAIN) { + fprintf(stderr, "Failed to lock %s: %s\n", filename, strerror(errno)); + } + goto fo_fail; + } + + goto fo_continue; + } + +fo_fail: + RUN_LOG_ERROR(fclose(fo)); + fo = NULL; + if (!foreground) { + return NULL; + } + +fo_continue: + fp = popen(exec, "r"); + if (fp == NULL) { + if (fo) { + RUN_LOG_ERROR(fclose(fo)); + } + return NULL; + } + + json_object * root = json_object_from_fd(fileno(fp)); + + if (fo && root) { + if (ftruncate(fd, 0)) { + fprintf(stderr, "Failed to truncate %s: %s\n", filename, strerror(errno)); + goto fo_close; + } + if (json_object_to_fd(fd, root, JSON_C_TO_STRING_PLAIN) < 0) { + fprintf(stderr, "Failed to write JSON to %s: %s\n", filename, json_util_get_last_err()); + goto fo_close; + } + } + +fo_close: + RUN_LOG_ERROR(fclose(fo)); + RUN_LOG_ERROR(pclose(fp)); + return root; +} + +struct json_object * make_safe(const char * name) { + char filename[40]; + sprintf(filename, "/tmp/olsrd-respondd-%s.json", name); + + // TODO: use access + struct stat filestat; + if (stat(filename, &filestat)) { + // no file, fail + return NULL; + // no file, do sync + // return run_safe(filename, name, true); + } + + /* double diff = difftime(time(NULL), filestat.st_ctime); + if (diff > 60 * 10 * 1000) { // if older than 10 minutes, ignore + return run_safe(filename, name, true); + } + + // use cached, update in background + int child = fork(); + if (!child) { + run_safe(filename, name, false); + exit(EXIT_SUCCESS); + } + + if (child < 0) { + fprintf(stderr, "Failed to fork: %s\n", strerror(errno)); + } */ + + json_object * ret = json_object_from_file(filename); + if (!ret) { // something is messed up + return NULL; + // return run_safe(filename, name, true); + } + + return ret; +} + +__attribute__ ((visibility ("default"))) +const struct respondd_provider_info respondd_providers[] = { + {"nodeinfo", respondd_provider_nodeinfo}, + {"statistics", respondd_provider_statistics}, + {"neighbours", respondd_provider_neighbours}, + {} +}; diff --git a/package/gluon-mesh-olsrd/src/uclient.c b/package/gluon-mesh-olsrd/src/uclient.c new file mode 100644 index 0000000000..c2f36953ea --- /dev/null +++ b/package/gluon-mesh-olsrd/src/uclient.c @@ -0,0 +1,257 @@ +/* SPDX-FileCopyrightText: 2017 Jan-Philipp Litza */ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#include "uclient.h" + +#include +#include + +#include +#include + +#define TIMEOUT_MSEC 300000 + +static const char *const user_agent = "olsrdhelper (libuclient)"; + + +const char *uclient_get_errmsg(int code) { + static char http_code_errmsg[16]; + if (code & UCLIENT_ERROR_STATUS_CODE) { + snprintf(http_code_errmsg, 16, "HTTP error %d", + code & (~UCLIENT_ERROR_STATUS_CODE)); + return http_code_errmsg; + } + switch(code) { + case UCLIENT_ERROR_CONNECT: + return "Connection failed"; + case UCLIENT_ERROR_TIMEDOUT: + return "Connection timed out"; + case UCLIENT_ERROR_REDIRECT_FAILED: + return "Failed to redirect"; + case UCLIENT_ERROR_TOO_MANY_REDIRECTS: + return "Too many redirects"; + case UCLIENT_ERROR_CONNECTION_RESET_PREMATURELY: + return "Connection reset prematurely"; + case UCLIENT_ERROR_SIZE_MISMATCH: + return "Incorrect file size"; + case UCLIENT_ERROR_NOT_JSON: + return "Response not json"; + default: + return "Unknown error"; + } +} + + +static void request_done(struct uclient *cl, int err_code) { + uclient_data(cl)->err_code = err_code; + uclient_disconnect(cl); + uloop_end(); +} + + +static void header_done_cb(struct uclient *cl) { + const struct blobmsg_policy policy = { + .name = "content-length", + .type = BLOBMSG_TYPE_STRING, + }; + struct blob_attr *tb_len; + + if (uclient_data(cl)->retries < 10) { + int ret = uclient_http_redirect(cl); + if (ret < 0) { + request_done(cl, UCLIENT_ERROR_REDIRECT_FAILED); + return; + } + if (ret > 0) { + uclient_data(cl)->retries++; + return; + } + } + + switch (cl->status_code) { + case 200: + break; + case 301: + case 302: + case 307: + request_done(cl, UCLIENT_ERROR_TOO_MANY_REDIRECTS); + return; + default: + request_done(cl, UCLIENT_ERROR_STATUS_CODE | cl->status_code); + return; + } + + blobmsg_parse(&policy, 1, &tb_len, blob_data(cl->meta), blob_len(cl->meta)); + if (tb_len) { + char *endptr; + + errno = 0; + unsigned long long val = strtoull(blobmsg_get_string(tb_len), &endptr, 10); + if (!errno && !*endptr && val <= SSIZE_MAX) { + if (uclient_data(cl)->length >= 0 && uclient_data(cl)->length != (ssize_t)val) { + request_done(cl, UCLIENT_ERROR_SIZE_MISMATCH); + return; + } + + uclient_data(cl)->length = val; + } + } +} + + +static void eof_cb(struct uclient *cl) { + request_done(cl, cl->data_eof ? 0 : UCLIENT_ERROR_CONNECTION_RESET_PREMATURELY); + if (cl->data_eof) { + uclient_data(cl)->eof(cl); + } +} + + +ssize_t uclient_read_account(struct uclient *cl, char *buf, int len) { + struct uclient_data *d = uclient_data(cl); + int r = uclient_read(cl, buf, len); + + if (r >= 0) { + d->downloaded += r; + + if (d->length >= 0 && d->downloaded > d->length) { + request_done(cl, UCLIENT_ERROR_SIZE_MISMATCH); + return -1; + } + } + + return r; +} + +// src https://github.com/curl/curl/blob/2610142139d14265ed9acf9ed83cdf73d6bb4d05/lib/escape.c + +/* Portable character check (remember EBCDIC). Do not use isalnum() because + its behavior is altered by the current locale. + See https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 +*/ +bool Curl_isunreserved(unsigned char in) +{ + switch(in) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': + case '-': case '.': case '_': case '~': + return true; + default: + break; + } + return false; +} + +char *curl_easy_escape(const char *string, int inlength) +{ + size_t length; + + if (inlength < 0) + return NULL; + + length = (inlength ? (size_t)inlength : strlen(string)); + if (!length) + return strdup(""); + + char * out = malloc((length * 3) + 1); + + if (!out) + return NULL; + + size_t offset = 0; + + // this isn't pretending like we're complying to any spec other than urlencode, thx + int slashes = 0; + + while (length--) { + unsigned char in = *string; /* we need to treat the characters unsigned */ + + if (slashes == 3) { + if (Curl_isunreserved(in)) { + /* append this */ + out[offset] = in; + offset++; + } else { + /* encode it */ + if (snprintf(out + offset, 4, "%%%02X", in) != 3) { + free(out); + return NULL; + } + + offset += 3; + } + } else { + out[offset] = in; + offset++; + + if (in == '/') { + slashes++; + } + } + + string++; + } + + out[offset] = '\0'; + + return out; +} + +int get_url(const char *user_url, void (*read_cb)(struct uclient *cl), void (*eof2_cb)(struct uclient *cl), void *cb_data, ssize_t len) { + char *url = curl_easy_escape(user_url, 0); + if (!url) + return UCLIENT_ERROR_CONNECT; + + struct uclient_data d = { .custom = cb_data, .length = len, .eof = eof2_cb }; + struct uclient_cb cb = { + .header_done = header_done_cb, + .data_read = read_cb, + .data_eof = eof_cb, + .error = request_done, + }; + + struct uclient *cl = uclient_new(url, NULL, &cb); + if (!cl) + goto err; + + cl->priv = &d; + if (uclient_set_timeout(cl, TIMEOUT_MSEC)) + goto err; + if (uclient_connect(cl)) + goto err; + if (uclient_http_set_request_type(cl, "GET")) + goto err; + if (uclient_http_reset_headers(cl)) + goto err; + if (uclient_http_set_header(cl, "User-Agent", user_agent)) + goto err; + if (uclient_request(cl)) + goto err; + uloop_run(); + uclient_free(cl); + free(url); + + if (!d.err_code && d.length >= 0 && d.downloaded != d.length) + return UCLIENT_ERROR_SIZE_MISMATCH; + + return d.err_code; + +err: + if (cl) + uclient_free(cl); + + free(url); + + return UCLIENT_ERROR_CONNECT; +} diff --git a/package/gluon-mesh-olsrd/src/uclient.h b/package/gluon-mesh-olsrd/src/uclient.h new file mode 100644 index 0000000000..718a77b1ce --- /dev/null +++ b/package/gluon-mesh-olsrd/src/uclient.h @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2017 Jan-Philipp Litza */ +/* SPDX-FileCopyrightText: 2021-2023 Maciej Krüger */ +/* SPDX-License-Identifier: BSD-2-Clause */ + +#pragma once + + +#include +#include + + +struct uclient_data { + /* data that can be passed in by caller and used in custom callbacks */ + void *custom; + /* data used by uclient callbacks */ + int retries; + int err_code; + ssize_t downloaded; + ssize_t length; + void (*eof)(struct uclient *cl); +}; + +enum uclient_own_error_code { + UCLIENT_ERROR_REDIRECT_FAILED = 32, + UCLIENT_ERROR_TOO_MANY_REDIRECTS, + UCLIENT_ERROR_CONNECTION_RESET_PREMATURELY, + UCLIENT_ERROR_SIZE_MISMATCH, + UCLIENT_ERROR_STATUS_CODE = 1024, + UCLIENT_ERROR_NOT_JSON = 2048 +}; + +inline struct uclient_data * uclient_data(struct uclient *cl) { + return (struct uclient_data *)cl->priv; +} + +inline void * uclient_get_custom(struct uclient *cl) { + return uclient_data(cl)->custom; +} + + +ssize_t uclient_read_account(struct uclient *cl, char *buf, int len); + +int get_url(const char *url, void (*read_cb)(struct uclient *cl), void (*eof2_cb)(struct uclient *cl), void *cb_data, ssize_t len); +const char *uclient_get_errmsg(int code); diff --git a/package/gluon-mesh-vpn-core/Makefile b/package/gluon-mesh-vpn-core/Makefile index 2111c7cef6..c183c54262 100644 --- a/package/gluon-mesh-vpn-core/Makefile +++ b/package/gluon-mesh-vpn-core/Makefile @@ -6,7 +6,7 @@ include ../gluon.mk define Package/gluon-mesh-vpn-core TITLE:=Basic support for connecting meshes via VPN tunnels - DEPENDS:=+gluon-core +gluon-wan-dnsmasq +iptables-zz-legacy +iptables-mod-extra +simple-tc + DEPENDS:=+gluon-core +gluon-nftables +gluon-wan-dnsmasq +simple-tc USERID:=:gluon-mesh-vpn=800 endef diff --git a/package/gluon-mesh-vpn-core/files/lib/gluon/mesh-vpn/iptables.rules b/package/gluon-mesh-vpn-core/files/lib/gluon/mesh-vpn/iptables.rules deleted file mode 100644 index 771fb40c5a..0000000000 --- a/package/gluon-mesh-vpn-core/files/lib/gluon/mesh-vpn/iptables.rules +++ /dev/null @@ -1,3 +0,0 @@ -*nat --I OUTPUT -m owner --gid-owner gluon-mesh-vpn -o lo -d 127.0.0.1 -p udp --dport 53 -j DNAT --to-destination :54 -COMMIT diff --git a/package/gluon-mesh-vpn-core/files/lib/gluon/nftables/mesh_vpn_dns.nft b/package/gluon-mesh-vpn-core/files/lib/gluon/nftables/mesh_vpn_dns.nft new file mode 100644 index 0000000000..cd26ec31b7 --- /dev/null +++ b/package/gluon-mesh-vpn-core/files/lib/gluon/nftables/mesh_vpn_dns.nft @@ -0,0 +1 @@ +meta skgid gluon-mesh-vpn oifname "lo" ip daddr 127.0.0.1 udp dport 53 redirect to 54 diff --git a/package/gluon-mesh-vpn-core/luasrc/lib/gluon/nftables/mesh_vpn.lua b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/nftables/mesh_vpn.lua new file mode 100644 index 0000000000..337ca5dd76 --- /dev/null +++ b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/nftables/mesh_vpn.lua @@ -0,0 +1,4 @@ +include('mesh_vpn_dns', { + position = 'chain-pre', + chain = 'dstnat', +}) diff --git a/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn index b14952552d..61f88217ac 100755 --- a/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn +++ b/package/gluon-mesh-vpn-core/luasrc/lib/gluon/upgrade/500-mesh-vpn @@ -25,14 +25,6 @@ uci:save('network') users.remove_user('gluon-fastd') users.remove_group('gluon-fastd') -uci:section('firewall', 'include', 'mesh_vpn_dns', { - type = 'restore', - path = '/lib/gluon/mesh-vpn/iptables.rules', - family = 'ipv4', -}) - -uci:save('firewall') - -- VPN migration if not uci:get('gluon', 'mesh_vpn') then diff --git a/package/gluon-mesh-vpn-wireguard/Makefile b/package/gluon-mesh-vpn-wireguard/Makefile index 61c5333200..9d4e9a791b 100644 --- a/package/gluon-mesh-vpn-wireguard/Makefile +++ b/package/gluon-mesh-vpn-wireguard/Makefile @@ -6,7 +6,13 @@ include ../gluon.mk define Package/gluon-mesh-vpn-wireguard TITLE:=Support for connecting meshes via wireguard - DEPENDS:=+gluon-core +libgluonutil +gluon-mesh-vpn-core +wireguard-tools +wgpeerselector +libubus + DEPENDS:=+gluon-core +libgluonutil +gluon-mesh-vpn-core +wireguard-tools +wgpeerselector +libubox +libubus +endef + +define Package/gluon-mesh-vpn-wireguard/install + $(Gluon/Build/Install) + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/gluon-hex-to-b64 $(1)/usr/sbin/ endef $(eval $(call BuildPackageGluon,gluon-mesh-vpn-wireguard)) diff --git a/package/gluon-mesh-vpn-wireguard/luasrc/lib/gluon/upgrade/400-mesh-vpn-wireguard b/package/gluon-mesh-vpn-wireguard/luasrc/lib/gluon/upgrade/400-mesh-vpn-wireguard index 18b5197b45..b58ceb9106 100755 --- a/package/gluon-mesh-vpn-wireguard/luasrc/lib/gluon/upgrade/400-mesh-vpn-wireguard +++ b/package/gluon-mesh-vpn-wireguard/luasrc/lib/gluon/upgrade/400-mesh-vpn-wireguard @@ -1,18 +1,63 @@ #!/usr/bin/lua local uci = require('simple-uci').cursor() +local unistd = require 'posix.unistd' +local util = require('gluon.util') local site = require 'gluon.site' +local sp = util.subprocess +local wait = require 'posix.sys.wait' -local private_key = uci:get("network_gluon-old", 'wg_mesh', "private_key") +local wg_private_key = uci:get("network_gluon-old", 'wg_mesh', "private_key") -if not private_key or not private_key:match("^" .. ("[%a%d+/]"):rep(42) .. "[AEIMQUYcgkosw480]=$") then - private_key = "generate" +local function valid_fastd_key(fastd_key) + return fastd_key and fastd_key:match(('%x'):rep(64)) end +local function valid_wireguard_key(wireguard_key) + return wireguard_key and wireguard_key:match("^" .. ("[%a%d+/]"):rep(42) .. "[AEIMQUYcgkosw480]=$") +end + +local function migrate_from_fastd_secret(fastd_secret) + local options = { + stdin = sp.PIPE, + stdout = sp.PIPE, + } + local pid, pipe = sp.popen('gluon-hex-to-b64', {}, options) + + if not pid then + return + end + + local inw = pipe.stdin + local out = pipe.stdout + + unistd.write(inw, string.format('%s\n', fastd_secret)) + unistd.close(inw) + + local wpid, status, code = wait.wait(pid) + if wpid and status == 'exited' and code == 0 then + local result = unistd.read(out, 44) + unistd.close(out) + return result + end +end + +if not valid_wireguard_key(wg_private_key) then + local fastd_secret = uci:get('fastd', 'mesh_vpn', 'secret') + if valid_fastd_key(fastd_secret) then + wg_private_key = migrate_from_fastd_secret(fastd_secret) + end +end + +if not valid_wireguard_key(wg_private_key) then + wg_private_key = "generate" +end + + uci:section('network', 'interface', 'wg_mesh', { proto = 'wireguard', fwmark = 1, - private_key = private_key, + private_key = wg_private_key, }) uci:section('network', 'interface', 'mesh_wg_mesh', { diff --git a/package/gluon-mesh-vpn-wireguard/src/Makefile b/package/gluon-mesh-vpn-wireguard/src/Makefile index 0b027848f6..ff847068c3 100644 --- a/package/gluon-mesh-vpn-wireguard/src/Makefile +++ b/package/gluon-mesh-vpn-wireguard/src/Makefile @@ -1,6 +1,9 @@ -all: respondd.so +all: respondd.so gluon-hex-to-b64 CFLAGS += -Wall -Werror-implicit-function-declaration +gluon-hex-to-b64: gluon-hex-to-b64.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -Wall -o $@ $^ $(LDLIBS) -lubox + respondd.so: respondd.c $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -lubus diff --git a/package/gluon-mesh-vpn-wireguard/src/gluon-hex-to-b64.c b/package/gluon-mesh-vpn-wireguard/src/gluon-hex-to-b64.c new file mode 100644 index 0000000000..0bca245e65 --- /dev/null +++ b/package/gluon-mesh-vpn-wireguard/src/gluon-hex-to-b64.c @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2022 Jan-Niklas Burfeind +// SPDX-License-Identifier: BSD-2-Clause +// SPDX-FileContributor: read_hex() by Matthias Schiffer + +#include +#include +#include +#include +#include +#include + +/** + * how many blocks should be encoded at once - can be configured + */ +#define BLOCK_AMOUNT 32 + +/** + * smallest possible block size to encode in b64 without further contex + * is three bytes - do not change + */ +#define CHUNK_SIZE (3*BLOCK_AMOUNT) + +/** print usage info and exit as failed */ +static void usage(void) { + fprintf(stderr, "Usage: gluon-hex-to-b64\n"); + exit(1); +} + +/** + * read a string of hexadecimal characters and return them as bytes + * return false in case any non-hexadecimal characters are provided + * return true on success + */ +static bool read_hex(uint8_t key[CHUNK_SIZE], const char *hexstr) { + if (strspn(hexstr, "0123456789abcdefABCDEF") != strlen(hexstr)) + return false; + + size_t i; + for (i = 0; i < CHUNK_SIZE; i++) + sscanf(&hexstr[2 * i], "%02hhx", &key[i]); + + return true; +} + +int main(int argc, char *argv[]) { + if (argc != 1) + usage(); + + unsigned char hex_input[CHUNK_SIZE * 2 + 1]; + uint8_t as_bytes[CHUNK_SIZE]; + int byte_count; + int b64_buflen = B64_ENCODE_LEN(CHUNK_SIZE); + int b64_return; + size_t ret; + + char str[b64_buflen]; + + do { + ret = fread(hex_input, 1, sizeof(hex_input) - 1, stdin); + hex_input[ret] = '\0'; + + /* in case fread did not fill six characters */ + if (ret != sizeof(hex_input)-1) { + /* drop newline by replacing it with a null character */ + hex_input[strcspn(hex_input, "\n")] = 0; + + /* + * count length of resulting string and make sure it's even, + * as bytes are represented using pairs of hex characters + */ + ret = strlen(hex_input); + if (ret % 2 == 1) { + fprintf(stderr, "Error: Incomplete hex representation of a byte.\n"); + exit(EXIT_FAILURE); + } + } + + byte_count = ret / 2; + b64_buflen = B64_ENCODE_LEN(byte_count); + + /* in case read_hex fails due to invalid characters */ + if (!read_hex(as_bytes, hex_input)) { + fprintf(stderr, "Error: Invalid hexadecimal input.\n"); + exit(EXIT_FAILURE); + } + + b64_return = b64_encode(as_bytes, byte_count, str, b64_buflen); + + /* trailing '\0' is not counted by b64_encode(), so we subtract one character */ + if (b64_buflen - 1 != b64_return) { + fprintf(stderr, "Error: Encoding bytes as b64 failed.\n"); + exit(EXIT_FAILURE); + } + + printf("%s", str); + /* repeat until a non full block is read */ + } while (ret == sizeof(hex_input)-1); + printf("\n"); + + exit(EXIT_SUCCESS); +} diff --git a/package/gluon-iptables-clamp-mss-to-pmtu/Makefile b/package/gluon-nftables-clamp-mss-to-pmtu/Makefile similarity index 86% rename from package/gluon-iptables-clamp-mss-to-pmtu/Makefile rename to package/gluon-nftables-clamp-mss-to-pmtu/Makefile index d5d91443b9..f502dff840 100644 --- a/package/gluon-iptables-clamp-mss-to-pmtu/Makefile +++ b/package/gluon-nftables-clamp-mss-to-pmtu/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk -PKG_NAME:=gluon-iptables-clamp-mss-to-pmtu +PKG_NAME:=gluon-nftables-clamp-mss-to-pmtu include ../gluon.mk define Package/$(PKG_NAME) TITLE:=This will establish a firewall rule to clamp the mss to pmtu on the mesh-vpn interface when the connection is towards 64:ff9b::/96 - DEPENDS:= +ip6tables-zz-legacy + DEPENDS:=+gluon-nftables endef define Package/$(PKG_NAME)/description diff --git a/package/gluon-nftables-clamp-mss-to-pmtu/files/lib/gluon/nftables/mesh_vpn_clamp_mss_to_pmtu.nft b/package/gluon-nftables-clamp-mss-to-pmtu/files/lib/gluon/nftables/mesh_vpn_clamp_mss_to_pmtu.nft new file mode 100644 index 0000000000..bbc6d21a73 --- /dev/null +++ b/package/gluon-nftables-clamp-mss-to-pmtu/files/lib/gluon/nftables/mesh_vpn_clamp_mss_to_pmtu.nft @@ -0,0 +1 @@ +oifname "mesh-vpn*" tcp flags & (syn|rst) == syn counter tcp option maxseg size set rt mtu diff --git a/package/gluon-nftables-clamp-mss-to-pmtu/luasrc/lib/gluon/nftables/mesh_vpn_clamp_mss_to_pmtu.lua b/package/gluon-nftables-clamp-mss-to-pmtu/luasrc/lib/gluon/nftables/mesh_vpn_clamp_mss_to_pmtu.lua new file mode 100755 index 0000000000..5d784158cb --- /dev/null +++ b/package/gluon-nftables-clamp-mss-to-pmtu/luasrc/lib/gluon/nftables/mesh_vpn_clamp_mss_to_pmtu.lua @@ -0,0 +1,4 @@ +include('mesh_vpn_clamp_mss_to_pmtu', { + position = 'chain-prepend', + chain = 'mangle_forward', +}) diff --git a/package/gluon-nftables-filter-multicast/Makefile b/package/gluon-nftables-filter-multicast/Makefile new file mode 100644 index 0000000000..c2c1e969b5 --- /dev/null +++ b/package/gluon-nftables-filter-multicast/Makefile @@ -0,0 +1,20 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-nftables-filter-multicast + +include ../gluon.mk + +define Package/gluon-nftables-filter-multicast + TITLE:=nftables filters for multicast packets + DEPENDS:=+gluon-core +gluon-nftables +gluon-nftables-multicast +gluon-mesh-batman-adv +endef + +define Package/gluon-nftables-filter-multicast/description + Gluon community wifi mesh firmware framework: nftables filters for multicast packets + + These filters drop non-essential multicast traffic before it enters the mesh. + + Allowed protocols are: DHCP, DHCPv6, ARP, ICMP, ICMPv6, BitTorrent local peer discovery, BABEL and OSPF +endef + +$(eval $(call BuildPackageGluon,gluon-nftables-filter-multicast)) diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-arp.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-arp.lua new file mode 100644 index 0000000000..8295200399 --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-arp.lua @@ -0,0 +1,7 @@ +-- Bridge loop avoidance +-- bridge_rule('MULTICAST_OUT', 'arp operation reply arp saddr ip = arp daddr ip arp daddr ether ff:43:05:00:00:00/ff:ff:ff:fc:00:00 return') +-- bridge_rule('MULTICAST_OUT', 'arp operation reply arp saddr ip = arp daddr ip arp daddr ether ff:43:05:05:00:00/ff:ff:ff:ff:00:00 return') + +bridge_rule('MULTICAST_OUT', 'arp operation reply arp saddr ip 0.0.0.0 drop') +bridge_rule('MULTICAST_OUT', 'arp operation request arp daddr ip 0.0.0.0 drop') +bridge_rule('MULTICAST_OUT', 'ether type arp return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-babel.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-babel.lua new file mode 100644 index 0000000000..8268586928 --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-babel.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip version 6 udp dport 6696 return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-btlpd.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-btlpd.lua new file mode 100644 index 0000000000..a6f8598e84 --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-btlpd.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip daddr 239.192.152.143 udp dport 6771 return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-dhcpv4.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-dhcpv4.lua new file mode 100644 index 0000000000..7ae0c57d03 --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-dhcpv4.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip version 4 udp dport 67 return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-dhcpv6.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-dhcpv6.lua new file mode 100644 index 0000000000..22ef48a41d --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-dhcpv6.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip version 6 udp dport 547 return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-icmpv6.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-icmpv6.lua new file mode 100644 index 0000000000..866360657c --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-icmpv6.lua @@ -0,0 +1,3 @@ +bridge_rule('MULTICAST_OUT_ICMPV6', 'icmpv6 type echo-request return') +bridge_rule('MULTICAST_OUT_ICMPV6', 'icmpv6 type 139 return') +bridge_rule('MULTICAST_OUT_ICMPV6', 'accept') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-igmp.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-igmp.lua new file mode 100644 index 0000000000..e6c73d36ca --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-igmp.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip protocol igmp return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-ospf.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-ospf.lua new file mode 100644 index 0000000000..a5b575f058 --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-ospf.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip protocol ospf return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-respondd.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-respondd.lua new file mode 100644 index 0000000000..309e191b6d --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-respondd.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip6 daddr ff05::2:1001 udp dport 1001 return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-ripng.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-ripng.lua new file mode 100644 index 0000000000..5162dacb7a --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/110-mcast-allow-ripng.lua @@ -0,0 +1 @@ +bridge_rule('MULTICAST_OUT', 'ip6 daddr ff02::9 udp dport 521 return') diff --git a/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/355-mcast-drop.lua b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/355-mcast-drop.lua new file mode 100644 index 0000000000..95554c7020 --- /dev/null +++ b/package/gluon-nftables-filter-multicast/luasrc/lib/gluon/nftables/355-mcast-drop.lua @@ -0,0 +1,3 @@ +bridge_rule('MULTICAST_OUT', 'ip6 daddr f02::1/128 drop') +bridge_rule('MULTICAST_OUT', 'ip6 daddr ff00::/8 mark 0x4 return') +bridge_rule('MULTICAST_OUT', 'drop') diff --git a/package/gluon-nftables-filter-ra-dhcp/Makefile b/package/gluon-nftables-filter-ra-dhcp/Makefile new file mode 100644 index 0000000000..ad714a1b24 --- /dev/null +++ b/package/gluon-nftables-filter-ra-dhcp/Makefile @@ -0,0 +1,19 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-nftables-filter-ra-dhcp + +include ../gluon.mk + +define Package/gluon-nftables-filter-ra-dhcp + TITLE:=nftables filters for Router Advertisement and DHCP packets + DEPENDS:=+gluon-core +gluon-nftables +gluon-mesh-batman-adv +endef + +define Package/gluon-nftables-filter-ra-dhcp/description + Gluon community wifi mesh firmware framework: nftables filters for Router Advertisement and DHCP packets + + These filters ensure that RA and DHCP packets are only forwarded from the mesh into the + client network, and not vice-versa. +endef + +$(eval $(call BuildPackageGluon,gluon-nftables-filter-ra-dhcp)) diff --git a/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-dhcpv4.lua b/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-dhcpv4.lua new file mode 100644 index 0000000000..d77f8711ce --- /dev/null +++ b/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-dhcpv4.lua @@ -0,0 +1,11 @@ +local uci = require('simple-uci').cursor() + +local gw_mode = uci:get('network', 'gluon_bat0', 'gw_mode') + +if gw_mode ~= 'server' then + bridge_rule('FORWARD', 'ip version 4 udp dport 67 jump out_only') + bridge_rule('OUTPUT', 'ip version 4 udp dport 67 jump out_only') + + bridge_rule('FORWARD', 'ip version 4 udp dport 68 jump in_only') + bridge_rule('INPUT', 'ip version 4 udp dport 68 jump in_only') +end diff --git a/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-dhcpv6.lua b/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-dhcpv6.lua new file mode 100644 index 0000000000..1dd953e203 --- /dev/null +++ b/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-dhcpv6.lua @@ -0,0 +1,5 @@ +bridge_rule('FORWARD', 'ip version 6 udp dport 547 jump out_only') +bridge_rule('OUTPUT', 'ip version 6 udp dport 547 jump out_only') + +bridge_rule('FORWARD', 'ip version 6 udp dport 546 jump in_only') +bridge_rule('INPUT', 'ip version 6 udp dport 546 jump in_only') diff --git a/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-radv.lua b/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-radv.lua new file mode 100644 index 0000000000..8a54b1e4ee --- /dev/null +++ b/package/gluon-nftables-filter-ra-dhcp/luasrc/lib/gluon/nftables/200-dir-radv.lua @@ -0,0 +1,5 @@ +bridge_rule('FORWARD', 'icmpv6 type nd-router-solicit jump out_only') +bridge_rule('OUTPUT', 'icmpv6 type nd-router-solicit jump out_only') + +bridge_rule('FORWARD', 'icmpv6 type nd-router-advert jump in_only') +bridge_rule('INPUT', 'icmpv6 type nd-router-advert jump in_only') diff --git a/package/gluon-ebtables-limit-arp/Makefile b/package/gluon-nftables-limit-arp/Makefile similarity index 66% rename from package/gluon-ebtables-limit-arp/Makefile rename to package/gluon-nftables-limit-arp/Makefile index 5f71b1c8f5..a842cfca46 100644 --- a/package/gluon-ebtables-limit-arp/Makefile +++ b/package/gluon-nftables-limit-arp/Makefile @@ -1,16 +1,16 @@ include $(TOPDIR)/rules.mk -PKG_NAME:=gluon-ebtables-limit-arp +PKG_NAME:=gluon-nftables-limit-arp include ../gluon.mk -define Package/gluon-ebtables-limit-arp - TITLE:=Ebtables limiter for ARP packets - DEPENDS:=+gluon-core +gluon-ebtables gluon-mesh-batman-adv +define Package/gluon-nftables-limit-arp + TITLE:=nftables limiter for ARP packets + DEPENDS:=+gluon-core +gluon-nftables +gluon-mesh-batman-adv endef -define Package/gluon-ebtables-limit-arp/description - Gluon community wifi mesh firmware framework: Ebtables rules to +define Package/gluon-nftables-limit-arp/description + Gluon community wifi mesh firmware framework: nftables rules to rate-limit ARP packets. This package adds filters to limit the amount of ARP Requests @@ -19,7 +19,7 @@ define Package/gluon-ebtables-limit-arp/description node in total. A burst of up to 50 ARP Requests is allowed until the rate-limiting - takes effect (see --limit-burst in the ebtables manpage). + takes effect (see burst in the nft manpage). Furthermore, ARP Requests with a target IP already present in the batman-adv DAT Cache are excluded from the rate-limiting, @@ -30,13 +30,15 @@ define Package/gluon-ebtables-limit-arp/description However it should mitigate the problem of curious people or smart devices scanning the whole IP range. Which could create a significant amount of overhead for all participants so far. + + Note that this package currently only supports batman. endef -define Package/gluon-ebtables-limit-arp/install +define Package/gluon-nftables-limit-arp/install $(Gluon/Build/Install) $(INSTALL_DIR) $(1)/usr/sbin/ $(CP) $(PKG_BUILD_DIR)/gluon-arp-limiter $(1)/usr/sbin/gluon-arp-limiter endef -$(eval $(call BuildPackageGluon,gluon-ebtables-limit-arp)) +$(eval $(call BuildPackageGluon,gluon-nftables-limit-arp)) diff --git a/package/gluon-ebtables-limit-arp/files/etc/init.d/gluon-arp-limiter b/package/gluon-nftables-limit-arp/files/etc/init.d/gluon-arp-limiter similarity index 100% rename from package/gluon-ebtables-limit-arp/files/etc/init.d/gluon-arp-limiter rename to package/gluon-nftables-limit-arp/files/etc/init.d/gluon-arp-limiter diff --git a/package/gluon-nftables-limit-arp/files/lib/gluon/nftables/limit_arp_chain.nft b/package/gluon-nftables-limit-arp/files/lib/gluon/nftables/limit_arp_chain.nft new file mode 100644 index 0000000000..b048d25178 --- /dev/null +++ b/package/gluon-nftables-limit-arp/files/lib/gluon/nftables/limit_arp_chain.nft @@ -0,0 +1,61 @@ +set limitmac { + type ether_addr +} + +set datips { + type ipv4_addr +} + +# Rewrite arp packet target hardware address if target protocol address matches a given address. +# input meta iifname enp2s0 arp ptype 0x0800 arp htype 1 arp hlen 6 arp plen 4 @nh,192,32 0xc0a88f10 @nh,144,48 set 0x112233445566 accept + +# chain('ARP_LIMIT', 'DROP') +chain arplimit { + # obrname "br-client" \ + # oifname "bat0" \ + # arp operation request \ + # counter + + # match everything which will land on bridge br-client + # protocol type: ipv4 + # hardware type: ethernet + # hardware address length: 6 byte mac + # protocol address length: 4 byte ipv4 + # arp request + # source address is mac to be limited + # target address is not in DAT + # we're over the limit + # count + # obrname "br-client" \ + # oifname "bat0" \ + arp ptype 0x0800 \ + arp htype 1 \ + arp hlen 6 \ + arp plen 4 \ + arp operation request \ + arp saddr ether @limitmac \ + arp daddr ip != @datips \ + limit rate over 6/minute burst 50 packets \ + counter \ + drop + + # obrname "br-client" \ + # oifname "bat0" \ + arp ptype 0x0800 \ + arp htype 1 \ + arp hlen 6 \ + arp plen 4 \ + arp operation request \ + arp saddr ether != @limitmac \ + arp daddr ip != @datips \ + limit rate over 1/second burst 50 packets \ + counter \ + drop +} + +# chain('ARP_LIMIT_DATCHECK', 'RETURN') +# %s ARP_LIMIT_DATCHECK -p ARP --arp-ip-dst %s -j mark --mark-or 0x2 --mark-target RETURN + +# chain('ARP_LIMIT_TLCHECK', 'RETURN') +# %s ARP_LIMIT_TLCHECK --source %s --limit 6/min --limit-burst 50 -j RETURN" +# %s ARP_LIMIT_TLCHECK (add ? "2" : "") --source %s -j DROP diff --git a/package/gluon-ebtables-limit-arp/files/lib/gluon/reload.d/380-gluon-arp-limiter-stop b/package/gluon-nftables-limit-arp/files/lib/gluon/reload.d/380-gluon-arp-limiter-stop similarity index 100% rename from package/gluon-ebtables-limit-arp/files/lib/gluon/reload.d/380-gluon-arp-limiter-stop rename to package/gluon-nftables-limit-arp/files/lib/gluon/reload.d/380-gluon-arp-limiter-stop diff --git a/package/gluon-ebtables-limit-arp/files/lib/gluon/reload.d/720-gluon-arp-limiter-start b/package/gluon-nftables-limit-arp/files/lib/gluon/reload.d/720-gluon-arp-limiter-start similarity index 100% rename from package/gluon-ebtables-limit-arp/files/lib/gluon/reload.d/720-gluon-arp-limiter-start rename to package/gluon-nftables-limit-arp/files/lib/gluon/reload.d/720-gluon-arp-limiter-start diff --git a/package/gluon-nftables-limit-arp/luasrc/lib/gluon/nftables/limit_arp.lua b/package/gluon-nftables-limit-arp/luasrc/lib/gluon/nftables/limit_arp.lua new file mode 100644 index 0000000000..caa142af33 --- /dev/null +++ b/package/gluon-nftables-limit-arp/luasrc/lib/gluon/nftables/limit_arp.lua @@ -0,0 +1,6 @@ +-- include('limit_arp', { +-- position = 'ruleset-pre' +-- }) + +bridge_include_table('pre', 'limit_arp_chain') +bridge_rule('FORWARD', 'oifname "bat0" obrname "br-client" arp operation request counter jump arplimit') diff --git a/package/gluon-ebtables-limit-arp/src/Makefile b/package/gluon-nftables-limit-arp/src/Makefile similarity index 100% rename from package/gluon-ebtables-limit-arp/src/Makefile rename to package/gluon-nftables-limit-arp/src/Makefile diff --git a/package/gluon-ebtables-limit-arp/src/addr_store.c b/package/gluon-nftables-limit-arp/src/addr_store.c similarity index 100% rename from package/gluon-ebtables-limit-arp/src/addr_store.c rename to package/gluon-nftables-limit-arp/src/addr_store.c diff --git a/package/gluon-ebtables-limit-arp/src/addr_store.h b/package/gluon-nftables-limit-arp/src/addr_store.h similarity index 100% rename from package/gluon-ebtables-limit-arp/src/addr_store.h rename to package/gluon-nftables-limit-arp/src/addr_store.h diff --git a/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.c b/package/gluon-nftables-limit-arp/src/gluon-arp-limiter.c similarity index 80% rename from package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.c rename to package/gluon-nftables-limit-arp/src/gluon-arp-limiter.c index 93940a3c6e..99d16e5c9a 100644 --- a/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.c +++ b/package/gluon-nftables-limit-arp/src/gluon-arp-limiter.c @@ -14,7 +14,7 @@ #define BATCTL_DC "/usr/sbin/batctl dc -H -n" #define BATCTL_TL "/usr/sbin/batctl tl -H -n" -#define EBTABLES "/usr/sbin/ebtables-tiny" +#define NFTABLES "/usr/sbin/nft" #define BUILD_BUG_ON(check) ((void)sizeof(int[1-2*!!(check)])) @@ -39,13 +39,13 @@ static void ebt_ip_call(char *mod, struct in_addr ip) int ret; snprintf(str, sizeof(str), - EBTABLES " %s ARP_LIMIT_DATCHECK -p ARP --arp-ip-dst %s -j mark --mark-or 0x2 --mark-target RETURN", + NFTABLES " %s element bridge gluon datips { %s }", mod, inet_ntoa(ip)); ret = system(str); if (ret) fprintf(stderr, - "%i: Calling ebtables for DAT failed with status %i\n", + "%i: Calling nft for DAT failed with status %i\n", clock, ret); } @@ -53,7 +53,7 @@ static void ip_node_destructor(struct addr_list *node) { struct in_addr *ip = (struct in_addr *)node->addr; - ebt_ip_call("-D", *ip); + ebt_ip_call("delete", *ip); } static void ebt_mac_limit_call(char *mod, struct mac_addr *mac) @@ -62,40 +62,22 @@ static void ebt_mac_limit_call(char *mod, struct mac_addr *mac) int ret; snprintf(str, sizeof(str), - EBTABLES " %s ARP_LIMIT_TLCHECK --source %s --limit 6/min --limit-burst 50 -j RETURN", + NFTABLES " %s element bridge gluon limitmac { %s }", mod, mac_ntoa(mac)); ret = system(str); if (ret) fprintf(stderr, - "%i: Calling ebtables for TL failed with status %i\n", - clock, ret); -} - -static void ebt_mac_ret_call(char *mod, struct mac_addr *mac, int add) -{ - char str[128]; - int ret; - - snprintf(str, sizeof(str), - EBTABLES " %s ARP_LIMIT_TLCHECK %s --source %s -j DROP", - mod, add ? "2" : "", mac_ntoa(mac)); - - ret = system(str); - if (ret) - fprintf(stderr, - "%i: Calling ebtables for TL failed with status %i\n", + "%i: Calling nft for TL failed with status %i\n", clock, ret); } static void ebt_mac_call(char *mod, struct mac_addr *mac) { - if (!strncmp(mod, "-D", strlen(mod))) { - ebt_mac_ret_call(mod, mac, 0); + if (!strncmp(mod, "delete", strlen(mod))) { ebt_mac_limit_call(mod, mac); } else { ebt_mac_limit_call(mod, mac); - ebt_mac_ret_call(mod, mac, 1); } } @@ -103,7 +85,7 @@ static void mac_node_destructor(struct addr_list *node) { struct mac_addr *mac = (struct mac_addr *)node->addr; - ebt_mac_call("-D", mac); + ebt_mac_call("delete", mac); } static int dat_parse_line(const char *line, struct in_addr *ip) @@ -141,7 +123,7 @@ static void ebt_add_ip(struct in_addr ip) if (ret) return; - ebt_ip_call("-I", ip); + ebt_ip_call("add", ip); } static void ebt_add_mac(struct mac_addr *mac) @@ -152,7 +134,7 @@ static void ebt_add_mac(struct mac_addr *mac) if (ret) return; - ebt_mac_call("-I", mac); + ebt_mac_call("add", mac); } static void ebt_dat_update(void) @@ -168,7 +150,7 @@ static void ebt_dat_update(void) fprintf(stderr, "%i: Error: Could not call batctl dc\n", clock); return; } - + while (1) { pline = fgets(line, sizeof(line), fp); if (!pline) { @@ -257,18 +239,18 @@ static void ebt_tl_update(void) static void ebt_dat_flush(void) { - int ret = system(EBTABLES " -F ARP_LIMIT_DATCHECK"); + int ret = system(NFTABLES " flush set bridge gluon datips"); if (ret) - fprintf(stderr, "Error flushing ARP_LIMIT_DATCHECK\n"); + fprintf(stderr, "Error flushing arplimit datips set\n"); } static void ebt_tl_flush(void) { - int ret = system(EBTABLES " -F ARP_LIMIT_TLCHECK"); + int ret = system(NFTABLES " flush set bridge gluon limitmac"); if (ret) - fprintf(stderr, "Error flushing ARP_LIMIT_TLCHECK\n"); + fprintf(stderr, "Error flushing arplimit limitmac\n"); } int main(int argc, char *argv[]) diff --git a/package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.h b/package/gluon-nftables-limit-arp/src/gluon-arp-limiter.h similarity index 100% rename from package/gluon-ebtables-limit-arp/src/gluon-arp-limiter.h rename to package/gluon-nftables-limit-arp/src/gluon-arp-limiter.h diff --git a/package/gluon-ebtables-limit-arp/src/lookup3.c b/package/gluon-nftables-limit-arp/src/lookup3.c similarity index 100% rename from package/gluon-ebtables-limit-arp/src/lookup3.c rename to package/gluon-nftables-limit-arp/src/lookup3.c diff --git a/package/gluon-ebtables-limit-arp/src/lookup3.h b/package/gluon-nftables-limit-arp/src/lookup3.h similarity index 100% rename from package/gluon-ebtables-limit-arp/src/lookup3.h rename to package/gluon-nftables-limit-arp/src/lookup3.h diff --git a/package/gluon-ebtables-limit-arp/src/mac.c b/package/gluon-nftables-limit-arp/src/mac.c similarity index 100% rename from package/gluon-ebtables-limit-arp/src/mac.c rename to package/gluon-nftables-limit-arp/src/mac.c diff --git a/package/gluon-ebtables-limit-arp/src/mac.h b/package/gluon-nftables-limit-arp/src/mac.h similarity index 100% rename from package/gluon-ebtables-limit-arp/src/mac.h rename to package/gluon-nftables-limit-arp/src/mac.h diff --git a/package/gluon-nftables-multicast/Makefile b/package/gluon-nftables-multicast/Makefile new file mode 100644 index 0000000000..5002aa9fb9 --- /dev/null +++ b/package/gluon-nftables-multicast/Makefile @@ -0,0 +1,16 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-nftables-multicast + +include ../gluon.mk + +define Package/gluon-nftables-multicast + TITLE:=nftables multicast filtering + DEPENDS:=+gluon-core +gluon-nftables +endef + +define Package/gluon-nftables-multicast/description + Gluon community wifi mesh firmware framework: nftables multicast filtering +endef + +$(eval $(call BuildPackageGluon,gluon-nftables-multicast)) diff --git a/package/gluon-ebtables/check_site.lua b/package/gluon-nftables-multicast/check_site.lua similarity index 100% rename from package/gluon-ebtables/check_site.lua rename to package/gluon-nftables-multicast/check_site.lua diff --git a/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/100-dir-chain.lua b/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/100-dir-chain.lua new file mode 100644 index 0000000000..76ea749128 --- /dev/null +++ b/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/100-dir-chain.lua @@ -0,0 +1,9 @@ +bridge_chain('IN_ONLY') +bridge_chain('OUT_ONLY') + +-- nat chain runs early, so we can drop IGMP/MLD +bridge_chain('MULTICAST_IN', nil, 'nat') +bridge_chain('MULTICAST_IN_ICMPV6', nil, 'nat') + +bridge_chain('MULTICAST_OUT') +bridge_chain('MULTICAST_OUT_ICMPV6') diff --git a/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/101-dir-rules.lua b/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/101-dir-rules.lua new file mode 100644 index 0000000000..cb74ee17b8 --- /dev/null +++ b/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/101-dir-rules.lua @@ -0,0 +1,5 @@ +bridge_rule('IN_ONLY', 'ibrname "br-client" iifname { "bat0", "local-port" } return') +bridge_rule('IN_ONLY', 'drop') + +bridge_rule('OUT_ONLY', 'obrname "br-client" oifname { "bat0", "local-port" } return') +bridge_rule('OUT_ONLY', 'drop') diff --git a/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/105-mcast-drop-igmp-mld.lua b/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/105-mcast-drop-igmp-mld.lua new file mode 100644 index 0000000000..0ea389d7e4 --- /dev/null +++ b/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/105-mcast-drop-igmp-mld.lua @@ -0,0 +1,15 @@ +local site = require 'gluon.site' + +bridge_rule('MULTICAST_IN', 'igmp type membership-query drop') +bridge_rule('MULTICAST_OUT', 'igmp type membership-query drop') + +bridge_rule('MULTICAST_OUT_ICMPV6', 'icmpv6 type 130 drop comment "MLD Query"') +bridge_rule('MULTICAST_IN_ICMPV6', 'icmpv6 type 130 drop comment "MLD Query"') + +if site.mesh.filter_membership_reports(true) then + bridge_rule('MULTICAST_OUT', 'ip protocol igmp drop') + bridge_rule('MULTICAST_IN', 'ip protocol igmp drop', 'nat') + + bridge_rule('MULTICAST_OUT_ICMPV6', 'icmpv6 type { 131, 132, 143 } drop comment "MLDv1 Report, MLDv1 Done, MLDv2 Report"') + bridge_rule('MULTICAST_IN_ICMPV6', 'icmpv6 type { 131, 132, 143 } drop comment "MLDv1 Report, MLDv1 Done, MLDv2 Report"', 'nat') +end diff --git a/package/gluon-ebtables/luasrc/lib/gluon/ebtables/350-mcast-dir-rules b/package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/350-mcast-dir-rules similarity index 100% rename from package/gluon-ebtables/luasrc/lib/gluon/ebtables/350-mcast-dir-rules rename to package/gluon-nftables-multicast/luasrc/lib/gluon/nftables/350-mcast-dir-rules diff --git a/package/gluon-nftables-source-filter/Makefile b/package/gluon-nftables-source-filter/Makefile new file mode 100644 index 0000000000..9f7ab8841f --- /dev/null +++ b/package/gluon-nftables-source-filter/Makefile @@ -0,0 +1,17 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-nftables-source-filter + +include ../gluon.mk + +define Package/gluon-nftables-source-filter + TITLE:=nftables rules to filter unreasonable L2 traffic. + DEPENDS:=+gluon-core +gluon-nftables +gluon-mesh-batman-adv +endef + +define Package/gluon-nftables-source-filter/description + This package adds an additional layer-2 filter-ruleset to prevent unreasonable + traffic entering the network via the nodes. +endef + +$(eval $(call BuildPackageGluon,gluon-nftables-source-filter)) diff --git a/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/100-local-forward-chain.lua b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/100-local-forward-chain.lua new file mode 100644 index 0000000000..51437cb8a4 --- /dev/null +++ b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/100-local-forward-chain.lua @@ -0,0 +1 @@ +bridge_chain('LOCAL_FORWARD') diff --git a/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-arp.lua b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-arp.lua new file mode 100644 index 0000000000..028f931942 --- /dev/null +++ b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-arp.lua @@ -0,0 +1,6 @@ +local prefix4 = require('gluon.site').prefix4() + +if prefix4 then + bridge_rule('LOCAL_FORWARD', 'arp saddr ip ' .. prefix4 .. ' arp daddr ip ' .. prefix4 .. ' return') + bridge_rule('LOCAL_FORWARD', 'arp saddr ip 0.0.0.0 arp daddr ip ' .. prefix4 .. ' return') +end diff --git a/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-ipv4.lua b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-ipv4.lua new file mode 100644 index 0000000000..38fe99d3de --- /dev/null +++ b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-ipv4.lua @@ -0,0 +1,6 @@ +local prefix4 = require('gluon.site').prefix4() + +if prefix4 then + bridge_rule('LOCAL_FORWARD', 'ip version 4 udp dport 67 return') + bridge_rule('LOCAL_FORWARD', 'ip saddr ' .. prefix4 .. ' return') +end diff --git a/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-ipv6.lua b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-ipv6.lua new file mode 100644 index 0000000000..9da7a8bec4 --- /dev/null +++ b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/110-local-forward-allow-ipv6.lua @@ -0,0 +1,9 @@ +local site = require 'gluon.site' + +bridge_rule('LOCAL_FORWARD', 'ip6 saddr fe80::/64 return') +bridge_rule('LOCAL_FORWARD', 'ip6 saddr ::/128 ip6 nexthdr icmpv6') +bridge_rule('LOCAL_FORWARD', 'ip6 saddr ' .. site.prefix6() .. ' return') + +for _, prefix in ipairs(site.extra_prefixes6({})) do + bridge_rule('LOCAL_FORWARD', 'ip6 saddr ' .. prefix .. ' return') +end diff --git a/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/300-local-forward-rules.lua b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/300-local-forward-rules.lua new file mode 100644 index 0000000000..6fb3228ccd --- /dev/null +++ b/package/gluon-nftables-source-filter/luasrc/lib/gluon/nftables/300-local-forward-rules.lua @@ -0,0 +1 @@ +bridge_rule('FORWARD', 'ibrname "br-client" iifname != "bat0" jump local_forward') diff --git a/package/gluon-nftables/Makefile b/package/gluon-nftables/Makefile new file mode 100644 index 0000000000..da9ef009fc --- /dev/null +++ b/package/gluon-nftables/Makefile @@ -0,0 +1,16 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-nftables + +include ../gluon.mk + +define Package/gluon-nftables + TITLE:=Nftables support + DEPENDS:=+nftables-json +kmod-nft-bridge +endef + +define Package/gluon-nftables/description + Gluon community wifi mesh firmware framework: Nftables support +endef + +$(eval $(call BuildPackageGluon,gluon-nftables)) diff --git a/package/gluon-nftables/files/lib/gluon/nftables/bridge.nft b/package/gluon-nftables/files/lib/gluon/nftables/bridge.nft new file mode 100644 index 0000000000..e69de29bb2 diff --git a/package/gluon-nftables/luasrc/lib/gluon/upgrade/300-nftables b/package/gluon-nftables/luasrc/lib/gluon/upgrade/300-nftables new file mode 100755 index 0000000000..63322506d8 --- /dev/null +++ b/package/gluon-nftables/luasrc/lib/gluon/upgrade/300-nftables @@ -0,0 +1,188 @@ +#!/usr/bin/lua + +local uci = require('simple-uci').cursor() +local glob = require 'posix.glob' + +-- Library + +function string.starts(string, start) + return string.sub(string, 1, string.len(start)) == start +end + +function basepath(str) + return str:match("([^./\\]+).lua") +end + +function write_all_lines(table, file, close) + for _, line in ipairs(table) do + file:write(line .. '\n') + end + + if close then + file:write('}\n') + end +end + +function read_include(file) + local f = assert(io.open(path(file), "rb")) + local content = f:read("*all") + f:close() + return content +end + +-- Functions + +function path(name) + return '/lib/gluon/nftables/' .. name .. '.nft' +end + +function include(name, parameters) + local boilerplate = { + type = 'nftables', + path = path(name), + } + + for k, v in pairs(parameters) do + boilerplate[k] = v + end + + uci:section('firewall', 'include', 'gluon_nftables_' .. name, boilerplate) +end + +function rule(name, chain, content) + local file = io.open(path(name), 'w') + file:write(content + '\n') + file:close() + + include(name, { + position = 'chain-post', + chain = chain, + }) +end + +local bridge = { + chain = { + INPUT = { + 'chain input {', + 'type filter hook input priority filter; policy accept;', + }, + FORWARD = { + 'chain forward {', + 'type filter hook forward priority filter; policy accept;', + }, + OUTPUT = { + 'chain output {', + 'type filter hook output priority filter; policy accept;', + }, + PREROUTING = { + 'chain prerouting {', + 'type filter hook prerouting priority dstnat; policy accept;', + }, + }, + table = { + pre = { + 'table bridge gluon', + 'flush table bridge gluon', + 'table bridge gluon {', + }, + post = { + + }, + } +} + +function bridge_rule(chain, content) + if bridge.chain[chain] == nil then + error('No bridge chain ' .. chain) + end + + table.insert(bridge.chain[chain], content) +end + +function bridge_table(position, content) + if bridge.table[position] == nil then + error('No bridge position ' .. position) + end + + table.insert(bridge.table[position], content) +end + +function bridge_chain(name) + if bridge.chain[name] ~= nil then + error('Chain already exists ' .. name) + end + + bridge.chain[name] = { + 'chain ' .. name:lower() .. ' {' + } +end + +function bridge_include_rule(chain, file) + if bridge.chain[chain] == nil then + error('No bridge chain ' .. chain) + end + + table.insert(bridge.chain[chain], read_include(file)) +end + +function bridge_include_table(position, file) + if bridge.table[position] == nil then + error('No bridge position ' .. position) + end + + table.insert(bridge.table[position], read_include(file)) +end + +-- Loader + +function load_file(path) + local nft = assert(loadfile(path)) + + local fncs = setmetatable({ + path = path, + include = include, + rule = rule, + + bridge_rule = bridge_rule, + bridge_chain = bridge_chain, + bridge_table = bridge_table, + bridge_include_rule = bridge_include_rule, + bridge_include_table = bridge_include_table, + }, { __index = _G }) + + local env = setmetatable({}, { __index = fncs }) + setfenv(nft, env) + + nft() +end + +-- Clean old rules + +uci:foreach('firewall', 'include', function(i) + if string.starts(i['.name'], 'gluon_nftables_') then + uci:delete('firewall', i['.name']) + end +end) + +-- Load new rules + +for _, path in ipairs(glob.glob("/lib/gluon/nftables/*.lua", 0) or {}) do + print(' - NFTables: ' .. basepath(path)) + load_file(path) +end + +local b = io.open(path('bridge'), 'w') + +include('bridge', { + position = 'ruleset-pre', +}) + +write_all_lines(bridge.table.pre, b) + +for _, lines in pairs(bridge.chain) do + write_all_lines(lines, b, true) +end + +write_all_lines(bridge.table.post, b, true) + +uci:save('firewall') diff --git a/package/gluon-radv-filterd/Makefile b/package/gluon-radv-filterd/Makefile index 4cab89606c..0015d5ee85 100644 --- a/package/gluon-radv-filterd/Makefile +++ b/package/gluon-radv-filterd/Makefile @@ -6,7 +6,7 @@ include ../gluon.mk define Package/gluon-radv-filterd TITLE:=Filter IPv6 router advertisements - DEPENDS:=+gluon-ebtables +libgluonutil +libbatadv +libnl-tiny + DEPENDS:=+gluon-nftables +libgluonutil +libbatadv +libnl-tiny endef MAKE_VARS += \ diff --git a/package/gluon-radv-filterd/luasrc/lib/gluon/ebtables/400-radv-filterd b/package/gluon-radv-filterd/luasrc/lib/gluon/ebtables/400-radv-filterd deleted file mode 100644 index 178084d41f..0000000000 --- a/package/gluon-radv-filterd/luasrc/lib/gluon/ebtables/400-radv-filterd +++ /dev/null @@ -1,3 +0,0 @@ -chain('RADV_FILTER', 'DROP') -rule 'FORWARD -p IPv6 -i bat0 --ip6-protocol ipv6-icmp --ip6-icmp-type router-advertisement -j RADV_FILTER' -rule 'RADV_FILTER -j ACCEPT' diff --git a/package/gluon-radv-filterd/luasrc/lib/gluon/nftables/400-radv-filterd.lua b/package/gluon-radv-filterd/luasrc/lib/gluon/nftables/400-radv-filterd.lua new file mode 100644 index 0000000000..0e516b3614 --- /dev/null +++ b/package/gluon-radv-filterd/luasrc/lib/gluon/nftables/400-radv-filterd.lua @@ -0,0 +1,15 @@ +bridge_table('pre', [[set radv_allow { + type ether_addr +} + +set radv_filter { + type ether_addr +} +]]) + +-- This rule starts filtering once the address is in radv_filter + +-- Daemon adds 00:00:../ff:ff:.. to radv_filter (todo) so everything gets picked up, +-- effectivly turning radv_filter into a bool + +bridge_rule('FORWARD', 'ether saddr @radv_filter iifname "bat0" icmpv6 type nd-router-advert ether saddr != @radv_allow drop') diff --git a/package/gluon-radv-filterd/src/gluon-radv-filterd.c b/package/gluon-radv-filterd/src/gluon-radv-filterd.c index f9f8fb877b..fd77fcea1f 100644 --- a/package/gluon-radv-filterd/src/gluon-radv-filterd.c +++ b/package/gluon-radv-filterd/src/gluon-radv-filterd.c @@ -149,12 +149,12 @@ static void cleanup(void) { if (G.chain) { /* Reset chain to accept everything again */ - if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) - { "ebtables-tiny", "-F", G.chain, NULL })) + if (fork_execvp_timeout(&timeout, "ebtables", (const char *[]) + { "ebtables", "-F", G.chain, NULL })) DEBUG_MSG("warning: flushing ebtables chain %s failed, not adding a new rule", G.chain); - if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) - { "ebtables-tiny", "-A", G.chain, "-j", "ACCEPT", NULL })) + if (fork_execvp_timeout(&timeout, "ebtables", (const char *[]) + { "ebtables", "-A", G.chain, "-j", "ACCEPT", NULL })) DEBUG_MSG("warning: adding new rule to ebtables chain %s failed", G.chain); } } @@ -700,11 +700,11 @@ static void update_ebtables(void) { G.max_tq); G.best_router = router; - if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) - { "ebtables-tiny", "-F", G.chain, NULL })) + if (fork_execvp_timeout(&timeout, "ebtables", (const char *[]) + { "ebtables", "-F", G.chain, NULL })) error_message(0, 0, "warning: flushing ebtables chain %s failed, not adding a new rule", G.chain); - else if (fork_execvp_timeout(&timeout, "ebtables-tiny", (const char *[]) - { "ebtables-tiny", "-A", G.chain, "-s", mac, "-j", "ACCEPT", NULL })) + else if (fork_execvp_timeout(&timeout, "ebtables", (const char *[]) + { "ebtables", "-A", G.chain, "-s", mac, "-j", "ACCEPT", NULL })) error_message(0, 0, "warning: adding new rule to ebtables chain %s failed", G.chain); } diff --git a/package/gluon-radv-filterd/src/respondd.c b/package/gluon-radv-filterd/src/respondd.c index 8c2c7eb428..d81c5ca58a 100644 --- a/package/gluon-radv-filterd/src/respondd.c +++ b/package/gluon-radv-filterd/src/respondd.c @@ -8,7 +8,7 @@ #include "mac.h" static struct json_object * get_radv_filter() { - FILE *f = popen("exec ebtables-tiny -L RADV_FILTER", "r"); + FILE *f = popen("exec ebtables -L RADV_FILTER", "r"); char *line = NULL; size_t len = 0; struct ether_addr mac = {}; diff --git a/package/gluon-respondd/files/etc/init.d/gluon-respondd b/package/gluon-respondd/files/etc/init.d/gluon-respondd index c7b071eb2e..902a9868a5 100755 --- a/package/gluon-respondd/files/etc/init.d/gluon-respondd +++ b/package/gluon-respondd/files/etc/init.d/gluon-respondd @@ -24,6 +24,6 @@ service_triggers() { local name=$(basename ${script:-$initscript}) procd_open_trigger - procd_add_raw_trigger "interface.*" 0 "/etc/init.d/$name" reload + procd_add_raw_trigger "interface.*" 0 "/etc/init.d/$name" restart procd_close_trigger } diff --git a/package/gluon-setup-mode/Makefile b/package/gluon-setup-mode/Makefile index cb5aac40d1..5ee9117c9f 100644 --- a/package/gluon-setup-mode/Makefile +++ b/package/gluon-setup-mode/Makefile @@ -9,7 +9,7 @@ include ../gluon.mk define Package/gluon-setup-mode TITLE:=Setup mode - DEPENDS:=+gluon-core +gluon-lock-password +ubus +dnsmasq-full + DEPENDS:=+gluon-core +gluon-lock-password +ubus +dnsmasq endef define Package/gluon-setup-mode/description diff --git a/package/gluon-state-check/files/lib/gluon/state/check.d/has_default_gw6 b/package/gluon-state-check/files/lib/gluon/state/check.d/has_default_gw6 deleted file mode 100755 index d099a60d5a..0000000000 --- a/package/gluon-state-check/files/lib/gluon/state/check.d/has_default_gw6 +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -out=$(ip -6 route show default dev br-client 2>/dev/null) && [ -n "$out" ] diff --git a/package/gluon-status-page-mesh-olsrd/Makefile b/package/gluon-status-page-mesh-olsrd/Makefile new file mode 100644 index 0000000000..78b165f1bd --- /dev/null +++ b/package/gluon-status-page-mesh-olsrd/Makefile @@ -0,0 +1,20 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-status-page-mesh-olsrd +PKG_VERSION:=1 + +include ../gluon.mk + +define Package/gluon-status-page-mesh-olsrd + TITLE:=OLSRD-data provider for gluon-status-page + DEPENDS:=+gluon-status-page +gluon-mesh-olsrd +libjson-c +libubox +libuclient +endef + +define Package/gluon-status-page-mesh-olsrd/install + $(Gluon/Build/Install) + + $(INSTALL_DIR) $(1)/lib/gluon/status-page/providers + $(INSTALL_BIN) $(PKG_BUILD_DIR)/neighbours-olsrd $(1)/lib/gluon/status-page/providers/ +endef + +$(eval $(call BuildPackageGluon,gluon-status-page-mesh-olsrd)) diff --git a/package/gluon-status-page-mesh-olsrd/files/lib/gluon/status-page/www/cgi-bin/dyn/neighbours-olsrd b/package/gluon-status-page-mesh-olsrd/files/lib/gluon/status-page/www/cgi-bin/dyn/neighbours-olsrd new file mode 100755 index 0000000000..0bb48a5c8e --- /dev/null +++ b/package/gluon-status-page-mesh-olsrd/files/lib/gluon/status-page/www/cgi-bin/dyn/neighbours-olsrd @@ -0,0 +1,5 @@ +#!/bin/sh + +CMD='exec /lib/gluon/status-page/providers/neighbours-olsrd' + +exec /usr/sbin/sse-multiplex "$CMD" diff --git a/package/gluon-status-page-mesh-olsrd/luasrc/lib/gluon/status-page/mesh.lua b/package/gluon-status-page-mesh-olsrd/luasrc/lib/gluon/status-page/mesh.lua new file mode 100644 index 0000000000..e7139c96d9 --- /dev/null +++ b/package/gluon-status-page-mesh-olsrd/luasrc/lib/gluon/status-page/mesh.lua @@ -0,0 +1,12 @@ +return { + provider = '/cgi-bin/dyn/neighbours-olsrd', + -- List of mesh-specific attributes, each a tuple of + -- 1) the internal identifier (JSON key) + -- 2) human-readable key (not translatable yet) + -- 3) value suffix (optional) + attrs = { + {'etx', 'Quality (ETX)', ' '}, + {'olsr1_ip', 'OLSRv1 IP', ' '}, + {'olsr2_ip', 'OLSRv2 IP', ' '}, + }, +} diff --git a/package/gluon-status-page-mesh-olsrd/src/Makefile b/package/gluon-status-page-mesh-olsrd/src/Makefile new file mode 100644 index 0000000000..f3fd65cda4 --- /dev/null +++ b/package/gluon-status-page-mesh-olsrd/src/Makefile @@ -0,0 +1,19 @@ +all: neighbours-olsrd + +CFLAGS += -Wall -D_GNU_SOURCE + +ifeq ($(origin PKG_CONFIG), undefined) + PKG_CONFIG = pkg-config + ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),) + $(error $(PKG_CONFIG) not found) + endif +endif + +CFLAGS_JSONC = $(shell $(PKG_CONFIG) --cflags json-c) +LDFLAGS_JSONC = $(shell $(PKG_CONFIG) --libs json-c) + +CFLAGS_OLSR += $(shell $(PKG_CONFIG) --cflags libolsrdhelper) +LDFLAGS_OLSR += $(shell $(PKG_CONFIG) --libs libolsrdhelper) + +neighbours-olsrd: neighbours-olsrd.c + $(CC) $(CFLAGS) $(CFLAGS_JSONC) $(CFLAGS_OLSR) $(LDFLAGS) $(LDFLAGS_JSONC) $(LDFLAGS_OLSR) -o $@ $^ $(LDLIBS) diff --git a/package/gluon-status-page-mesh-olsrd/src/neighbours-olsrd.c b/package/gluon-status-page-mesh-olsrd/src/neighbours-olsrd.c new file mode 100644 index 0000000000..3197ecd29a --- /dev/null +++ b/package/gluon-status-page-mesh-olsrd/src/neighbours-olsrd.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +int main(void) { + struct json_object *obj; + + printf("Content-type: text/event-stream\n\n"); + fflush(stdout); + + while (1) { + obj = get_merged_neighs(); + if (obj) { + printf("data: %s\n\n", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN)); + fflush(stdout); + json_object_put(obj); + } + sleep(10); + } + + return 0; +} diff --git a/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html b/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html index 0d21f94e9b..e58023f0a9 100644 --- a/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html +++ b/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html @@ -154,7 +154,7 @@

<%:Overview%>

<%- end %>
<%:Model%>
<%| nodeinfo.hardware.model %>
<%:Primary MAC address%>
<%| nodeinfo.network.mac %>
-
<%:IP address%>
<%= pcdata(table.concat(sorted(nodeinfo.network.addresses), '\n')):gsub('\n', '
') %>
+
<%:IP address%>
<%= pcdata(table.concat(sorted(nodeinfo.network.addresses or {}), '\n')):gsub('\n', '
') %>
<%:Firmware%>
<%| nodeinfo.software.firmware.release %>
<% if nodeinfo.network.mesh_vpn then -%>
<%:Mesh VPN%>
@@ -195,7 +195,7 @@

<%:Overview%>

string.format(' (%s)', nodeinfo.software.autoupdater.branch) %> <%- end %> - <% if nodeinfo.software.babeld or nodeinfo.software['batman-adv'] then -%> + <% if nodeinfo.software.babeld or nodeinfo.software['batman-adv'] or nodeinfo.software.olsr2 or nodeinfo.software.olsr1 then -%>
<%:Mesh protocol%>
<% if nodeinfo.software.babeld then -%>
babeld <%| nodeinfo.software.babeld.version %>
@@ -203,6 +203,12 @@

<%:Overview%>

<% if nodeinfo.software['batman-adv'] then -%>
batman-adv <%| nodeinfo.software['batman-adv'].version %> (compat<%| nodeinfo.software['batman-adv'].compat %>)
<%- end %> + <% if nodeinfo.software.olsr2 then -%> +
OLSRv2 <%| nodeinfo.software.olsr2.version %> (<%:running%>: <%| translate(nodeinfo.software.olsr2.running and 'yes' or 'no') %>)
+ <%- end %> + <% if nodeinfo.software.olsr1 then -%> +
OLSRv1 <%| nodeinfo.software.olsr1.version %> (<%:running%>: <%| translate(nodeinfo.software.olsr1.running and 'yes' or 'no') %>)
+ <%- end %> <%- end %> diff --git a/package/gluon-status-page/files/lib/gluon/status-page/www/static/status-page.css b/package/gluon-status-page/files/lib/gluon/status-page/www/static/status-page.css index cdf4d04f65..28b05a0159 100644 --- a/package/gluon-status-page/files/lib/gluon/status-page/www/static/status-page.css +++ b/package/gluon-status-page/files/lib/gluon/status-page/www/static/status-page.css @@ -1 +1 @@ -html,body,div,span,h1,h2,h3,dl,dt,dd,canvas,header,table,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{background:rgba(0,0,0,0.12);font-family:Roboto, Lucida Grande, sans, Arial;color:rgba(0,0,0,0.87);font-size:14px;line-height:1}a{color:rgba(220,0,103,0.87);text-decoration:none;margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent}a:hover{text-decoration:underline}h1{font-weight:bold}h2{font-size:16px;margin-bottom:16px;color:rgba(0,0,0,0.54)}h3{font-size:15px;margin-top:16px;margin-bottom:8px;color:rgba(0,0,0,0.54)}header{display:flex;padding:0 14px;background:#dc0067;color:rgba(255,255,255,0.98);position:absolute;top:0;width:100%;box-sizing:border-box;height:20vh;z-index:-1;box-shadow:0px 5px 6px rgba(0,0,0,0.16),0px 1.5px 3px rgba(0,0,0,0.23);white-space:nowrap}header h1{font-size:24px;margin:10px 0;padding:6px 0;text-overflow:ellipsis;overflow:hidden;flex:1}.container{display:flex;max-width:90vw;margin:64px auto 24px auto;background:#fdfdfd;box-shadow:0px 5px 20px rgba(0,0,0,0.19),0px 3px 6px rgba(0,0,0,0.23)}.container>.frame{flex:1;border-style:solid;border-color:rgba(0,0,0,0.12);box-sizing:border-box;padding:16px}.container>.frame+.frame{border-width:0 0 0 1px}.container>.frame-wide{flex:2}dt,th,td::before{font-weight:bold;color:rgba(0,0,0,0.87)}dt{margin-bottom:4px}th,td{text-align:left;padding:4px 16px 4px 0}th:last-child,td:last-child{padding-right:0}dd,td{font-weight:normal;font-size:0.9em;color:rgba(0,0,0,0.54)}dd{margin-bottom:16px}table{border-collapse:collapse;border-spacing:0}table.datatable{width:100%;table-layout:fixed}table.datatable th,table.datatable td{font-size:1em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.datatable th.row-tq{width:45px}table.datatable th.row-signal{width:36px}table.datatable th.row-distance{width:90px}table.datatable th.row-inactive{width:130px}table.datatable tr.inactive{opacity:0.33}table.datatable tr.highlight{background:rgba(255,180,0,0.25)}canvas.signalgraph{margin-top:8px;width:100%}@media only screen and (max-width: 1250px){.container{max-width:none;margin:56px 0 0}header{height:56px;z-index:1;position:fixed}.datatable tr{display:block;margin-bottom:15px}.datatable tr:first-child{margin-bottom:0}.datatable th{display:none}.datatable td{display:block;position:relative;padding-left:150px;max-width:calc(100% - 150px)}.datatable td::before{position:absolute;left:5px;content:attr(data-label)}}@media only screen and (max-width: 700px){.container{display:block}.container>.frame+.frame{border-width:1px 0 0 0}} +html,body,div,span,h1,h2,h3,dl,dt,dd,canvas,header,table,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{background:rgba(85,98,112,0.12);font-family:Roboto, Lucida Grande, sans, Arial;color:rgba(0,0,0,0.87);font-size:14px;line-height:1}a{color:rgba(161,200,32,0.87);text-decoration:none;margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent}a:hover{text-decoration:underline}h1{font-weight:bold}h2{font-size:16px;margin-bottom:16px;color:rgba(0,0,0,0.54)}h3{font-size:15px;margin-top:16px;margin-bottom:8px;color:rgba(0,0,0,0.54)}header{display:flex;padding:0 14px;background:#6991aa;color:rgba(255,255,255,0.98);position:absolute;top:0;width:100%;box-sizing:border-box;height:20vh;z-index:-1;box-shadow:0px 5px 6px rgba(0,0,0,0.16),0px 1.5px 3px rgba(0,0,0,0.23);white-space:nowrap}header h1{font-size:24px;margin:10px 0;padding:6px 0;text-overflow:ellipsis;overflow:hidden;flex:1}.container{display:flex;max-width:90vw;margin:64px auto 24px auto;background:#fdfdfd;box-shadow:0px 5px 20px rgba(0,0,0,0.19),0px 3px 6px rgba(0,0,0,0.23)}.container>.frame{flex:1;border-style:solid;border-color:rgba(0,0,0,0.12);box-sizing:border-box;padding:16px}.container>.frame+.frame{border-width:0 0 0 1px}.container>.frame-wide{flex:2}dt,th,td::before{font-weight:bold;color:rgba(0,0,0,0.87)}dt{margin-bottom:4px}th,td{text-align:left;padding:4px 16px 4px 0}th:last-child,td:last-child{padding-right:0}dd,td{font-weight:normal;font-size:0.9em;color:rgba(0,0,0,0.54)}dd{margin-bottom:16px}table{border-collapse:collapse;border-spacing:0}table.datatable{width:100%;table-layout:fixed}table.datatable th,table.datatable td{font-size:1em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.datatable th.row-tq{width:45px}table.datatable th.row-signal{width:36px}table.datatable th.row-distance{width:90px}table.datatable th.row-inactive{width:130px}table.datatable tr.inactive{opacity:0.33}table.datatable tr.highlight{background:rgba(255,180,0,0.25)}canvas.signalgraph{margin-top:8px;width:100%}@media only screen and (max-width: 1250px){.container{max-width:none;margin:56px 0 0}header{height:56px;z-index:1;position:fixed}.datatable tr{display:block;margin-bottom:15px}.datatable tr:first-child{margin-bottom:0}.datatable th{display:none}.datatable td{display:block;position:relative;padding-left:150px;max-width:calc(100% - 150px)}.datatable td::before{position:absolute;left:5px;content:attr(data-label)}}@media only screen and (max-width: 700px){.container{display:block}.container>.frame+.frame{border-width:1px 0 0 0}} diff --git a/package/gluon-status-page/i18n/de.po b/package/gluon-status-page/i18n/de.po index 27801e197c..b687d9077b 100644 --- a/package/gluon-status-page/i18n/de.po +++ b/package/gluon-status-page/i18n/de.po @@ -156,3 +156,12 @@ msgstr "nicht verbunden" #~ msgid "Gateway Nexthop" #~ msgstr "Gateway Nexthop" + +msgid "running" +msgstr "läuft" + +msgid "yes" +msgstr "ja" + +msgid "no" +msgstr "nein" diff --git a/package/gluon-status-page/i18n/gluon-status-page.pot b/package/gluon-status-page/i18n/gluon-status-page.pot index 50d7d6bee7..960149a69f 100644 --- a/package/gluon-status-page/i18n/gluon-status-page.pot +++ b/package/gluon-status-page/i18n/gluon-status-page.pot @@ -144,3 +144,12 @@ msgstr "" msgid "not connected" msgstr "" + +msgid "running" +msgstr "" + +msgid "yes" +msgstr "" + +msgid "no" +msgstr "" diff --git a/package/gluon-status-page/sass/status-page.scss b/package/gluon-status-page/sass/status-page.scss index c093f1719a..ccb6eb5385 100644 --- a/package/gluon-status-page/sass/status-page.scss +++ b/package/gluon-status-page/sass/status-page.scss @@ -25,7 +25,7 @@ table, tr, th, td { } body { - background: rgba(0, 0, 0, 0.12); + background: rgba(85, 98, 112, 0.12); // $feuer_greyblue font-family: Roboto, Lucida Grande, sans, Arial; color: rgba(0, 0, 0, 0.87); font-size: 14px; @@ -34,7 +34,8 @@ body { a { - color: rgba(220, 0, 103, 0.87); + // color: rgba(105, 145, 170, 0.87); // $feuer_lightblue + color: rgba(161, 200, 32, 0.87); // $feuer_green text-decoration: none; margin: 0; @@ -68,7 +69,7 @@ h3 { header { display: flex; padding: 0 14px; - background: #dc0067; + background: #6991aa; // $feuer_lightblue color: rgba(255, 255, 255, 0.98); position: absolute; top: 0; diff --git a/package/gluon-wan-dnsmasq/Makefile b/package/gluon-wan-dnsmasq/Makefile index 44a68f3b13..da42cf0163 100644 --- a/package/gluon-wan-dnsmasq/Makefile +++ b/package/gluon-wan-dnsmasq/Makefile @@ -6,7 +6,7 @@ include ../gluon.mk define Package/gluon-wan-dnsmasq TITLE:=Support for a secondary DNS server using the WAN interface - DEPENDS:=+gluon-core +libubus-lua +dnsmasq-full +libpacketmark + DEPENDS:=+gluon-core +libubus-lua +dnsmasq +libpacketmark endef define Package/gluon-wan-dnsmasq/description diff --git a/package/gluon-web-admin/luasrc/lib/gluon/config-mode/model/admin/remote.lua b/package/gluon-web-admin/luasrc/lib/gluon/config-mode/model/admin/remote.lua index 226969c07c..c408f06300 100644 --- a/package/gluon-web-admin/luasrc/lib/gluon/config-mode/model/admin/remote.lua +++ b/package/gluon-web-admin/luasrc/lib/gluon/config-mode/model/admin/remote.lua @@ -35,6 +35,44 @@ function keys:write(value) end end +local file = '/etc/dropbear/authorized_keys' +local uci = require('simple-uci').cursor() +local keys = {} + +local site_keys = s:option(Flag, "disable_site_keys", translate("Disable and remove site keys")) +site_keys.default = uci:get_bool('gluon', 'core', 'disable_site_keys') + +local function load_keys() + for line in io.lines(file) do + keys[line] = true + end +end + +function site_keys:write(value) + -- NOTE: re-adding keys happens automatically if this option is off, so we don't need to take care of that + if value and not site_keys.default then + pcall(load_keys) + + local f = io.open(file, 'w') + local all_site_keys = {} + for _, key in ipairs(site.authorized_keys()) do + all_site_keys[key] = true + end + + for key, _ in pairs(keys) do + if not all_site_keys[key] then + f:write(key .. '\n') + end + end + + f:close() + keys.default = "" + end + + uci:set('gluon', 'core', 'disable_site_keys', value) + uci:save('gluon') +end + local config = site.config_mode.remote_login if not config.show_password_form(false) then -- password login is disabled in site.conf diff --git a/package/gluon-web-model/files/lib/gluon/web/view/model/mlvalue.html b/package/gluon-web-model/files/lib/gluon/web/view/model/mlvalue.html new file mode 100644 index 0000000000..0a4fb5c9db --- /dev/null +++ b/package/gluon-web-model/files/lib/gluon/web/view/model/mlvalue.html @@ -0,0 +1,25 @@ +<% + local br = self.orientation == "horizontal" and '   ' or '
' + local entries = self:entries() + local util = require 'gluon.util' +%> +
+ <% for i, entry in pairs(entries) do %> + > + > + > + <%|entry.value%> + + <% if i ~= #entries then write(br) end %> + <% end %> +
diff --git a/package/gluon-web-model/javascript/gluon-web-model.js b/package/gluon-web-model/javascript/gluon-web-model.js index ec9cdc4af3..4596a14392 100644 --- a/package/gluon-web-model/javascript/gluon-web-model.js +++ b/package/gluon-web-model/javascript/gluon-web-model.js @@ -1,19 +1,15 @@ /* - Copyright 2008 Steven Barth - Copyright 2008-2012 Jo-Philipp Wich - Copyright 2017 Matthias Schiffer - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 + SPDX-License-Identifier: Apache-2.0 + SPDX-FileCopyrightText: 2008, Steven Barth + SPDX-FileCopyrightText: 2008-2012, Jo-Philipp Wich + SPDX-FileCopyrightText: 2017, Matthias Schiffer + SPDX-FileCopyrightText: 2023, Leonardo Mörlein */ /* Build using: - uglifyjs javascript/gluon-web-model.js -o javascript/gluon-web-model.min.js -c -m --support-ie8 + uglifyjs javascript/gluon-web-model.js -o javascript/gluon-web-model.min.js -c -m --ie */ @@ -219,6 +215,20 @@ parent.parentNode.style.display = (parent.options.length <= 1) ? 'none' : ''; } + var nodes = document.querySelectorAll('[data-exclusive-with]'); + for (var i = 0, node; (node = nodes[i]) !== undefined; i++) { + var exclusive_with = JSON.parse(node.getAttribute('data-exclusive-with')); + + node.disabled = false; + for (var list_item of exclusive_with) { + var el = document.getElementById(node.name + '.' + list_item); + node.disabled ||= el.checked; + } + + if (node.disabled) + node.checked = false; + } + if (state) { update(); } diff --git a/package/gluon-web-model/javascript/gluon-web-model.min.js b/package/gluon-web-model/javascript/gluon-web-model.min.js index 07478cbb02..d0ae42fc84 100644 --- a/package/gluon-web-model/javascript/gluon-web-model.min.js +++ b/package/gluon-web-model/javascript/gluon-web-model.min.js @@ -1 +1 @@ -!function(){var v={};function a(e){return/^-?\d+$/.test(e)?+e:NaN}function r(e){return/^-?\d*\.?\d+?$/.test(e)?+e:NaN}var u={integer:function(){return!isNaN(a(this))},uinteger:function(){return 0<=a(this)},float:function(){return!isNaN(r(this))},ufloat:function(){return 0<=r(this)},ipaddr:function(){return u.ip4addr.apply(this)||u.ip6addr.apply(this)},ip4addr:function(){var e;return!!(e=this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/))&&(0<=e[1]&&e[1]<=255&&0<=e[2]&&e[2]<=255&&0<=e[3]&&e[3]<=255&&0<=e[4]&&e[4]<=255)},ip6addr:function(){return this.indexOf("::")<0?null!=this.match(/^(?:[a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$/i):!(0<=this.indexOf(":::")||this.match(/::.+::/)||this.match(/^:[^:]/)||this.match(/[^:]:$/))&&(!!this.match(/^(?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}$/i)||(!!this.match(/^(?:[a-f0-9]{1,4}:){7}:$/i)||!!this.match(/^:(?::[a-f0-9]{1,4}){7}$/i)))},wpakey:function(){var e=this;return 64==e.length?null!=e.match(/^[a-f0-9]{64}$/i):8<=e.length&&e.length<=63},range:function(e,t){var n=r(this);return+e<=n&&n<=+t},min:function(e){return r(this)>=+e},max:function(e){return r(this)<=+e},irange:function(e,t){var n=a(this);return+e<=n&&n<=+t},imin:function(e){return a(this)>=+e},imax:function(e){return a(this)<=+e},minlength:function(e){return+e<=(""+this).length},maxlength:function(e){return(""+this).length<=+e}};function o(e){for(var t=0;tn.index);i=i.nextSibling);i?r.insertBefore(n.node,i):r.appendChild(n.node),n.node.dispatchEvent(new Event("gluon-show")),e=!0}r&&r.parentNode&&r.getAttribute("data-optionals")&&(r.parentNode.style.display=r.options.length<=1?"none":"")}e&&h()}function g(e,t,n,a){return e.addEventListener?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,function(){var e=window.event;return!e.target&&e.srcElement&&(e.target=e.srcElement),!!n(e)}),e}function m(l,s){var c=s.prefix;function o(e,t,n){for(var a=[];l.firstChild;){var r=l.firstChild;(i=+r.index)!=n&&("input"==r.nodeName.toLowerCase()?a.push(r.value||""):"select"==r.nodeName.toLowerCase()&&(a[a.length-1]=r.options[r.selectedIndex].value)),l.removeChild(r)}0<=t?(e=t+1,a.splice(t,0,"")):s.optional||0!=a.length||a.push("");for(var i=1;i<=a.length;i++){var o=document.createElement("input");if(o.id=c+"."+i,o.name=c,o.value=a[i-1],o.type="text",o.index=i,s.size&&(o.size=s.size),s.placeholder&&(o.placeholder=s.placeholder),l.appendChild(o),s.type&&y(o,!1,s.type),g(o,"keydown",f),g(o,"keypress",p),i==e)o.focus();else if(-i==e){o.focus();var d=o.value;o.value=" ",o.value=d}if(s.optional||1=+e},max:function(e){return i(this)<=+e},irange:function(e,t){var n=a(this);return+e<=n&&n<=+t},imin:function(e){return a(this)>=+e},imax:function(e){return a(this)<=+e},minlength:function(e){return+e<=(""+this).length},maxlength:function(e){return(""+this).length<=+e}};function p(e){for(var t,n,a,i=0;in.index);r=r.nextSibling);r?i.insertBefore(n.node,r):i.appendChild(n.node),n.node.dispatchEvent(new Event("gluon-show")),t=!0}i&&i.parentNode&&i.getAttribute("data-optionals")&&(i.parentNode.style.display=i.options.length<=1?"none":"")}for(var d=document.querySelectorAll("[data-exclusive-with]"),o=0;(a=d[o])!==undefined;o++){var u,l=JSON.parse(a.getAttribute("data-exclusive-with"));a.disabled=!1;for(u of l){var c=document.getElementById(a.name+"."+u);a.disabled||=c.checked}a.disabled&&(a.checked=!1)}t&&f()}function v(e,t,n,a){e.addEventListener?e.addEventListener(t,n,!!a):e.attachEvent("on"+t,function(){var e=window.event;return!e.target&&e.srcElement&&(e.target=e.srcElement),!!n(e)})}function m(t,n,e){var a,i,r=(i=(e=e).match(/^([^\(]+)\(([^,]+),([^\)]+)\)$/))&&(a=d[i[1]])!==undefined?function(){return a.apply(this,[i[2],i[3]])}:(i=e.match(/^([^\(]+)\(([^,\)]+)\)$/))&&(a=d[i[1]])!==undefined?function(){return a.apply(this,[i[2]])}:d[e];r&&(v(t,"blur",e=function(){var e;t.form&&(t.className=t.className.replace(/ gluon-input-invalid/g,""),0==(e=(t.options&&-1 --- Copyright 2017-2018 Matthias Schiffer --- Licensed to the public under the Apache License 2.0. +-- SPDX-License-Identifier: Apache-2.0 +-- SPDX-FileCopyrightText: 2008, Steven Barth +-- SPDX-FileCopyrightText: 2017-2018, Matthias Schiffer +-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein local util = require "gluon.web.util" +local gluon_util = require "gluon.util" local datatypes = require "gluon.web.model.datatypes" local class = util.class @@ -361,6 +363,83 @@ function ListValue:validate() end +local MultiListValue = class(AbstractValue) +M.MultiListValue = MultiListValue + +function MultiListValue:__init__(...) + AbstractValue.__init__(self, ...) + self.subtemplate = "model/mlvalue" + + self.size = 1 + + self.keys = {} + self.entry_list = {} +end + +function MultiListValue:value(key, val, ...) + key = tostring(key) + + if self.keys[key] then + return + end + self.keys[key] = true + self.exclusions = {} + + val = val or key + table.insert(self.entry_list, { + key = key, + value = tostring(val), + deps = {...}, + }) +end + +function MultiListValue:entries() + local ret = {unpack(self.entry_list)} + + return ret +end + +function MultiListValue:validate() + for _, val in ipairs(self.data) do + if not self.keys[val] then + return false + end + end + + for key, exclusive_with in pairs(self.exclusions) do + if gluon_util.contains(self.data, key) then + for _, exclusion in ipairs(exclusive_with) do + if gluon_util.contains(self.data, exclusion) then + return false + end + end + end + end + + return true +end + +function MultiListValue:exclusive(a, b) + if not self.exclusions[a] then + self.exclusions[a] = {} + end + if not self.exclusions[b] then + self.exclusions[b] = {} + end + + gluon_util.add_to_set(self.exclusions[a], b) + gluon_util.add_to_set(self.exclusions[b], a) +end + +function MultiListValue:defaultvalue() + return self.default or {} +end + +function MultiListValue:formvalue(http) + return http:formvaluetable(self:id()) +end + + local DynamicList = class(AbstractValue) M.DynamicList = DynamicList diff --git a/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua b/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua index fdee7d7dbe..31c1aa82fc 100644 --- a/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua +++ b/package/gluon-web-model/luasrc/usr/lib/lua/gluon/web/model/datatypes.lua @@ -1,6 +1,6 @@ --- Copyright 2010 Jo-Philipp Wich --- Copyright 2017 Matthias Schiffer --- Licensed to the public under the Apache License 2.0. +-- SPDX-License-Identifier: Apache-2.0 +-- SPDX-FileCopyrightText: 2010, Jo-Philipp Wich +-- SPDX-FileCopyrightText: 2017, Matthias Schiffer local M = {} diff --git a/package/gluon-web-network/i18n/de.po b/package/gluon-web-network/i18n/de.po index 77b3a6a350..ccb29f48ca 100644 --- a/package/gluon-web-network/i18n/de.po +++ b/package/gluon-web-network/i18n/de.po @@ -28,15 +28,6 @@ msgstr "PoE-Passthrough aktivieren" msgid "Enable PoE Power Port %s" msgstr "PoE-Ausgabe auf Port %s aktivieren" -msgid "Enable meshing on the Ethernet interface" -msgstr "Mesh auf dem Ethernet-Port aktivieren" - -msgid "Enable meshing on the LAN interface" -msgstr "Mesh auf dem LAN-Port aktivieren" - -msgid "Enable meshing on the WAN interface" -msgstr "Mesh auf dem WAN-Port aktivieren" - msgid "Gateway" msgstr "Gateway" @@ -49,6 +40,12 @@ msgstr "IPv4" msgid "IPv6" msgstr "IPv6" +msgid "Interface" +msgstr "Interface" + +msgid "LAN Interfaces" +msgstr "LAN-Interfaces" + msgid "Netmask" msgstr "Netzmaske" @@ -61,5 +58,8 @@ msgstr "Statisch" msgid "Static DNS servers" msgstr "Statische DNS-Server" +msgid "WAN Interfaces" +msgstr "WAN-Interfaces" + msgid "WAN connection" msgstr "WAN-Verbindung" diff --git a/package/gluon-web-network/i18n/fr.po b/package/gluon-web-network/i18n/fr.po index 97067343df..7bc8980195 100644 --- a/package/gluon-web-network/i18n/fr.po +++ b/package/gluon-web-network/i18n/fr.po @@ -28,15 +28,6 @@ msgstr "" msgid "Enable PoE Power Port %s" msgstr "" -msgid "Enable meshing on the Ethernet interface" -msgstr "" - -msgid "Enable meshing on the LAN interface" -msgstr "Activer le réseau MESH sur le port LAN" - -msgid "Enable meshing on the WAN interface" -msgstr "Activer le réseau MESH sur les ports WAN" - msgid "Gateway" msgstr "Passerelle" diff --git a/package/gluon-web-network/i18n/gluon-web-network.pot b/package/gluon-web-network/i18n/gluon-web-network.pot index a75929dfe6..ce8cb39bd2 100644 --- a/package/gluon-web-network/i18n/gluon-web-network.pot +++ b/package/gluon-web-network/i18n/gluon-web-network.pot @@ -19,15 +19,6 @@ msgstr "" msgid "Enable PoE Power Port %s" msgstr "" -msgid "Enable meshing on the Ethernet interface" -msgstr "" - -msgid "Enable meshing on the LAN interface" -msgstr "" - -msgid "Enable meshing on the WAN interface" -msgstr "" - msgid "Gateway" msgstr "" @@ -40,6 +31,12 @@ msgstr "" msgid "IPv6" msgstr "" +msgid "Interface" +msgstr "" + +msgid "LAN Interfaces" +msgstr "" + msgid "Netmask" msgstr "" @@ -52,5 +49,8 @@ msgstr "" msgid "Static DNS servers" msgstr "" +msgid "WAN Interfaces" +msgstr "" + msgid "WAN connection" msgstr "" diff --git a/package/gluon-web-network/luasrc/lib/gluon/config-mode/model/admin/network.lua b/package/gluon-web-network/luasrc/lib/gluon/config-mode/model/admin/network.lua index df92c96590..4c803ad255 100644 --- a/package/gluon-web-network/luasrc/lib/gluon/config-mode/model/admin/network.lua +++ b/package/gluon-web-network/luasrc/lib/gluon/config-mode/model/admin/network.lua @@ -1,21 +1,18 @@ ---[[ -Copyright 2014 Nils Schneider - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 -]]-- +-- SPDX-License-Identifier: Apache-2.0 +-- SPDX-FileCopyrightText: 2014, Nils Schneider +-- SPDX-FileCopyrightText: 2023, Leonardo Mörlein local uci = require("simple-uci").cursor() -local sysconfig = require 'gluon.sysconfig' -local util = require 'gluon.util' local wan = uci:get_all("network", "wan") local wan6 = uci:get_all("network", "wan6") local dns_static = uci:get_first("gluon-wan-dnsmasq", "static") +local files = require 'posix.dirent'.files +local unistd = require "posix.unistd" +local sysconfig = require 'gluon.sysconfig' +local util = require 'gluon.util' + local f = Form(translate("WAN connection")) local s = f:section(Section) @@ -76,36 +73,60 @@ end s = f:section(Section) -local wired_mesh_help = { - single = translate('Enable meshing on the Ethernet interface'), - wan = translate('Enable meshing on the WAN interface'), - lan = translate('Enable meshing on the LAN interface'), +local pretty_ifnames = { + ["/wan"] = translate("WAN Interfaces"), + ["/single"] = translate("Interface"), + ["/lan"] = translate("LAN Interfaces") } -local function wired_mesh(iface) - if not sysconfig[iface .. '_ifname'] then return end - local iface_roles = uci:get_list('gluon', 'iface_' .. iface, 'role') +local function has_devtype(iface_dir, devtype) + return util.file_contains_line(iface_dir..'/uevent', 'DEVTYPE='..devtype) +end + +local function is_physical(iface_dir) + return unistd.access(iface_dir .. '/device') == 0 +end - local option = s:option(Flag, 'mesh_' .. iface, wired_mesh_help[iface]) - option.default = util.contains(iface_roles, 'mesh') ~= false +local function ethernet_interfaces() + local eth_ifaces = {} + local ifaces_dir = '/sys/class/net/' - function option:write(data) - local roles = uci:get_list('gluon', 'iface_' .. iface, 'role') - if data then - util.add_to_set(roles, 'mesh') - else - util.remove_from_set(roles, 'mesh') + for iface in files(ifaces_dir) do + if iface ~= '.' and iface ~= '..' then + local iface_dir = ifaces_dir .. iface + if is_physical(iface_dir) and not has_devtype(iface_dir, 'wlan') then + table.insert(eth_ifaces, iface) + end end - uci:set_list('gluon', 'iface_' .. iface, 'role', roles) - - -- Reconfigure on next reboot - uci:set('gluon', 'core', 'reconfigure', true) end + + return eth_ifaces end -wired_mesh('single') -wired_mesh('wan') -wired_mesh('lan') +local vlan_interface_sections = {} + +uci:foreach('gluon', 'interface', function(config) + local section_name = config['.name'] + local ifaces = s:option(MultiListValue, section_name, pretty_ifnames[config.name] or config.name) + + if section_name:find("vlan") then + vlan_interface_sections[section_name] = config.name + end + + ifaces.orientation = 'horizontal' + ifaces:value('uplink', 'Uplink') + ifaces:value('mesh', 'Mesh') + ifaces:value('client', 'Client') + ifaces:exclusive('uplink', 'client') + ifaces:exclusive('mesh', 'client') + + ifaces.default = config.role + + function ifaces:write(data) + uci:set_list("gluon", section_name, "role", data) + end +end) + local section uci:foreach("system", "gpio_switch", function(si) @@ -166,4 +187,63 @@ function f:write() uci:commit('system') end -return f + +local f_actions = Form(translate("Actions")) + +local s = f_actions:section(Section) + +local action = s:option(ListValue, "action", translate("Action")) +action:value("create_vlan_interface", translate("Create VLAN interface config")) +action:value("delete_vlan_interface", translate("Delete VLAN interface config")) + +action:value("expand_wan_interfaces", translate("Expand WAN interfaces")) + +action:value("contract_wan_interfaces", translate("Contract WAN interfaces")) + +-- Options for create_vlan_interface + +local interface = s:option(ListValue, "interface", translate("Interface")) +for _, iface in ipairs(ethernet_interfaces()) do + -- TODO: this should not include vlan interfaces + interface:value(iface, iface) +end +interface:depends(action, "create_vlan_interface") + +local vlan_id = s:option(Value, "vlan_id", translate("VLAN ID")) +vlan_id.datatype = "irange(1,4094)" +vlan_id:depends(action, "create_vlan_interface") + +function create_vlan_interface() + local new_iface = interface.data .. '.' .. vlan_id.data + local section_name = 'iface_' .. interface.data .. '_vlan' .. vlan_id.data + + uci:section('gluon', 'interface', section_name, { + name = new_iface, + role = {} + }) +end + +-- Options for delete_vlan_interface + +local vlan_iface_to_delete = s:option(ListValue, "vlan_iface_to_delete", translate("VLAN Interface")) +vlan_iface_to_delete:depends(action, "delete_vlan_interface") +for section_name, iface in pairs(vlan_interface_sections) do + vlan_iface_to_delete:value(section_name, iface) +end + +function delete_vlan_interface() + uci:delete('gluon', vlan_iface_to_delete.data) +end + +function f_actions:write(data) + if action.data == 'create_vlan_interface' then + create_vlan_interface() + elseif action.data == 'delete_vlan_interface' then + delete_vlan_interface() + end + + uci:commit('gluon') +end + + +return f, f_actions diff --git a/package/gluon-web-wifi-config/i18n/de.po b/package/gluon-web-wifi-config/i18n/de.po index f0883ad6f4..0e8fcd5939 100644 --- a/package/gluon-web-wifi-config/i18n/de.po +++ b/package/gluon-web-wifi-config/i18n/de.po @@ -19,33 +19,17 @@ msgstr "2,4GHz-WLAN" msgid "5GHz WLAN" msgstr "5GHz-WLAN" -msgid "" -"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency " -"and transmission power that conforms with the local regulatory requirements. " -"It also enables dynamic frequency selection (DFS; radar detection). At the " -"same time, mesh functionality is disabled as it requires neighbouring nodes " -"to stay on the same channel permanently." -msgstr "" -"Ist der Knoten für den Einsatz im Freien konfiguriert, wird ein WLAN-Kanal " -"auf dem 5-GHz-Band sowie eine Sendeleistung entsprechend den gesetzlichen " -"Frequenzregulatorien gewählt. Gleichzeitig wird die dynamische Frequenzwahl " -"(DFS; Radarerkennung) aktiviert und die Mesh-Funktionalität deaktiviert, da " -"sich Nachbarknoten dauerhaft auf demselben Kanal befinden müssen." - msgid "Enable client network (access point)" msgstr "Client-Netz aktivieren (Access Point)" msgid "Enable mesh network (802.11s)" msgstr "Mesh-Netz aktivieren (802.11s)" -msgid "HT Mode" -msgstr "HT-Modus" +msgid "Enable mesh network (IBSS, outdated)" +msgstr "Mesh-Netz aktivieren (IBSS, veraltet)" -msgid "Node will be installed outdoors" -msgstr "Knoten wird im Außenbereich betrieben" - -msgid "Outdoor Installation" -msgstr "Outdoor-Installation" +msgid "Enable point-to-point (AP/STA) mesh" +msgstr "Punkt-zu-Punkt (AP/STA) mesh aktivieren" msgid "Transmission power" msgstr "Sendeleistung" @@ -68,3 +52,28 @@ msgstr "" "werden. Wenn möglich, ist in den Werten der Sendeleistung der Antennengewinn " "enthalten; diese Werte sind allerdings für viele Geräte nicht verfügbar oder " "fehlerhaft." + +msgid "Outdoor Installation" +msgstr "Installation im Außenbereich" + +msgid "Node will be installed outdoors" +msgstr "Knoten wird im Außenbereich betrieben" + +msgid "" +"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency " +"and transmission power that conforms with the local regulatory requirements. " +"It also enables dynamic frequency selection (DFS; radar detection). At the " +"same time, mesh functionality is disabled as it requires neighbouring nodes " +"to stay on the same channel permanently." +msgstr "" +"Ist der Knoten für den Einsatz im Freien konfiguriert, wird ein WLAN-Kanal auf " +"dem 5-GHz-Band sowie eine Sendeleistung entsprechend den gesetzlichen " +"Frequenzregulatorien gewählt. Gleichzeitig wird die dynamische Frequenzwahl " +"(DFS; Radarerkennung) aktiviert und die Mesh-Funktionalität deaktiviert, da " +"sich Nachbarknoten dauerhaft auf demselben Kanal befinden müssen." + +msgid "HT Mode" +msgstr "HT-Modus" + +msgid "Channel" +msgstr "Kanal" diff --git a/package/gluon-web-wifi-config/i18n/fr.po b/package/gluon-web-wifi-config/i18n/fr.po index eb09694f47..e065e4e5a9 100644 --- a/package/gluon-web-wifi-config/i18n/fr.po +++ b/package/gluon-web-wifi-config/i18n/fr.po @@ -1,15 +1,14 @@ msgid "" msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" "Project-Id-Version: PACKAGE VERSION\n" "PO-Revision-Date: 2015-08-19 23:30+0100\n" "Last-Translator:Tobias Bernot \n" "Language-Team: French\n" "Language: fr\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" - msgid "(default)" msgstr "(défaut)" @@ -19,28 +18,14 @@ msgstr "2,4GHz Wi-Fi" msgid "5GHz WLAN" msgstr "5GHz Wi-Fi" -msgid "" -"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency " -"and transmission power that conforms with the local regulatory requirements. " -"It also enables dynamic frequency selection (DFS; radar detection). At the " -"same time, mesh functionality is disabled as it requires neighbouring nodes " -"to stay on the same channel permanently." -msgstr "" - msgid "Enable client network (access point)" msgstr "Activer le réseau client (Access Point)" msgid "Enable mesh network (802.11s)" msgstr "Activer le réseau MESH (802.11s)" -msgid "HT Mode" -msgstr "Mode HT" - -msgid "Node will be installed outdoors" -msgstr "" - -msgid "Outdoor Installation" -msgstr "Installation extérieure" +msgid "Enable mesh network (IBSS, outdated)" +msgstr "Activer le réseau MESH (IBSS, obsolète)" msgid "Transmission power" msgstr "Puissance d'émission" diff --git a/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot b/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot index 2af9b5f3a5..f7a400129f 100644 --- a/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot +++ b/package/gluon-web-wifi-config/i18n/gluon-web-wifi-config.pot @@ -10,27 +10,16 @@ msgstr "" msgid "5GHz WLAN" msgstr "" -msgid "" -"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency " -"and transmission power that conforms with the local regulatory requirements. " -"It also enables dynamic frequency selection (DFS; radar detection). At the " -"same time, mesh functionality is disabled as it requires neighbouring nodes " -"to stay on the same channel permanently." -msgstr "" - msgid "Enable client network (access point)" msgstr "" msgid "Enable mesh network (802.11s)" msgstr "" -msgid "HT Mode" -msgstr "" - -msgid "Node will be installed outdoors" +msgid "Enable mesh network (IBSS, outdated)" msgstr "" -msgid "Outdoor Installation" +msgid msgid "Enable point-to-point (AP/STA) mesh" msgstr "" msgid "Transmission power" @@ -47,3 +36,23 @@ msgid "" "values include the antenna gain where available, but there are many devices " "for which the gain is unavailable or inaccurate." msgstr "" + +msgid "Outdoor installation" +msgstr "" + +msgid "Node will be installed outdoors" +msgstr "" + +msgid "" +"Configuring the node for outdoor use tunes the 5 GHz radio to a frequency " +"and transmission power that conforms with the local regulatory requirements. " +"It also enables dynamic frequency selection (DFS; radar detection). At the " +"same time, mesh functionality is disabled as it requires neighbouring nodes " +"to stay on the same channel permanently." +msgstr "" + +msgid "HT Mode" +msgstr "" + +msgid "Channel" +msgstr "" diff --git a/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua b/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua index 49009eaf46..446563828a 100644 --- a/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua +++ b/package/gluon-web-wifi-config/luasrc/lib/gluon/config-mode/model/admin/wifi-config.lua @@ -2,6 +2,7 @@ local iwinfo = require 'iwinfo' local uci = require("simple-uci").cursor() local site = require 'gluon.site' local wireless = require 'gluon.wireless' +local sysconfig = require 'gluon.sysconfig' local function txpower_list(phy) @@ -37,18 +38,22 @@ f:section(Section, nil, translate( local mesh_vifs_5ghz = {} - +local mesh_outdoor_dependent = {} uci:foreach('wireless', 'wifi-device', function(config) local radio = config['.name'] local is_5ghz = false + local is_60ghz = false local title if config.band == '2g' then title = translate("2.4GHz WLAN") elseif config.band == '5g' then is_5ghz = true title = translate("5GHz WLAN") + elseif config.band == '60g' then + is_60ghz = true + title = translate("60Ghz WLAN") else return end @@ -93,39 +98,114 @@ uci:foreach('wireless', 'wifi-device', function(config) return o end - vif_option('client', {'client', 'owe'}, translate('Enable client network (access point)')) + if not is_60ghz then + vif_option('client', {'client', 'owe'}, translate('Enable client network (access point)')) + vif_option('ibss', {'ibss'}, translate("Enable mesh network (IBSS, outdated)")) - local mesh_vif = vif_option('mesh', {'mesh'}, translate("Enable mesh network (802.11s)")) - if is_5ghz then - table.insert(mesh_vifs_5ghz, mesh_vif) + local mesh_vif = vif_option('mesh', {'mesh'}, translate("Enable mesh network (802.11s)")) + if is_5ghz then + table.insert(mesh_vifs_5ghz, mesh_vif) + end end - local phy = wireless.find_phy(config) - if not phy then - return + -- leftover todos for 60ghz + -- - since client AP on 60ghz makes no sense (and additional APs can't be created due to limit of 1 device) + -- a function would be needed to say "device.supports_access_points()" or "device.client_facing()" or similar + -- that would return a bool whether to setup & show private AP, client AP, etc options + -- - 802.11s on 60ghz may or may not become a thing + -- could be handeled dynamically. a toggle to switch between p2p and mesh if driver supports it. + local vif = vif_option('p2p', {'p2p'}, translate('Enable point-to-point (AP/STA) mesh')) + if vif then + local name6 = 'p2p_' .. radio + + vif.default = not uci:get_bool('wireless', name6, 'disabled') + + local id = p:option(Value, radio .. '_p2pid', translate('SSID')) + id.datatype = "maxlength(32)" + id.default = uci:get('wireless', name6, 'ssid') or 'g-' .. string.sub(string.gsub(sysconfig.primary_mac, ':', ''), 8) + id:depends(vif, true) + function id:write(data) + uci:set('wireless', name6, 'ssid', data) + end + + local mode = p:option(ListValue, radio .. '_p2pmode', translate("P2P Mode"), translate("Master=AP Slave=Station")) + mode.default = uci:get('wireless', name6, 'mode') or 'ap' + mode:value('ap', translate('Master')) + mode:value('sta', translate('Slave')) + mode:depends(vif, true) + function mode:write(data) + uci:set('wireless', name6, 'mode', data) + end end - local txpowers = txpower_list(phy) - if #txpowers <= 1 then - return + local function txpower_field() + local phy = wireless.find_phy(config) + if not phy then + return + end + + local txpowers = txpower_list(phy) + if #txpowers <= 1 then + return + end + + local tp = p:option(ListValue, radio .. '_txpower', translate("Transmission power")) + tp.default = uci:get('wireless', radio, 'txpower') or 'default' + + tp:value('default', translate("(default)")) + + table.sort(txpowers, function(a, b) return a.driver_dbm > b.driver_dbm end) + + for _, entry in ipairs(txpowers) do + tp:value(entry.driver_dbm, string.format("%i dBm (%i mW)", entry.display_dbm, entry.display_mw)) + end + + function tp:write(data) + if data == 'default' then + data = nil + end + uci:set('wireless', radio, 'txpower', data) + end + + return tp end - local tp = p:option(ListValue, radio .. '_txpower', translate("Transmission power")) - tp.default = uci:get('wireless', radio, 'txpower') or 'default' + local tp = txpower_field() - tp:value('default', translate("(default)")) + if tp and is_5ghz then + table.insert(mesh_outdoor_dependent, tp) + end - table.sort(txpowers, function(a, b) return a.driver_dbm > b.driver_dbm end) + local conf - for _, entry in ipairs(txpowers) do - tp:value(entry.driver_dbm, string.format("%i dBm (%i mW)", entry.display_dbm, entry.display_mw)) + if is_5ghz then + conf = site.wifi5 + elseif is_60ghz then + conf = site.wifi60 + else + conf = site.wifi24 end - function tp:write(data) - if data == 'default' then - data = nil + if conf.channel_adjustable(false) then + local ch = p:option(ListValue, radio .. '_channel', translate("Channel")) + ch.default = uci:get('wireless', radio, 'channel') + + local defaultChannel = conf.channel() + + local phy = wireless.find_phy(uci:get_all('wireless', radio)) + local channels = iwinfo.nl80211.freqlist(phy) + + for _, entry in ipairs(channels) do + ch:value(entry.channel, string.format(entry.channel == defaultChannel and "%i " .. translate("(default)") or "%i", entry.channel)) + end + + function ch:write(data) + uci:set('wireless', radio, 'channel', data) + end + + if is_5ghz then + table.insert(mesh_outdoor_dependent, ch) end - uci:set('wireless', radio, 'txpower', data) end end) @@ -149,6 +229,10 @@ if wireless.device_uses_11a(uci) and not wireless.preserve_channels(uci) then end end + for _, field in ipairs(mesh_outdoor_dependent) do + field:depends(outdoor, false) + end + function outdoor:write(data) uci:set('gluon', 'wireless', 'outdoor', data) end diff --git a/package/gluon-wireless-encryption-wpa3-openssl/Makefile b/package/gluon-wireless-encryption-wpa3-openssl/Makefile new file mode 100644 index 0000000000..558ac15791 --- /dev/null +++ b/package/gluon-wireless-encryption-wpa3-openssl/Makefile @@ -0,0 +1,19 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-wireless-encryption-wpa3-openssl + +include ../gluon.mk + +define Package/gluon-wireless-encryption-wpa3-openssl + DEPENDS:=+hostapd-openssl + TITLE:=Package for supporting WPA3 encrypted wireless networks +endef + +define Package/gluon-wireless-encryption-wpa3-openssl/install + $(INSTALL_DIR) $(1)/lib/gluon/features + + touch $(1)/lib/gluon/features/wpa2 + touch $(1)/lib/gluon/features/wpa3 +endef + +$(eval $(call BuildPackageGluon,gluon-wireless-encryption-wpa3-openssl)) diff --git a/package/libbatadv/src/batadv-genl.c b/package/libbatadv/src/batadv-genl.c index 12ea679145..7d421dbea5 100644 --- a/package/libbatadv/src/batadv-genl.c +++ b/package/libbatadv/src/batadv-genl.c @@ -41,58 +41,206 @@ __attribute__ ((visibility ("default"))) struct nla_policy batadv_genl_policy[NUM_BATADV_ATTR] = { - [BATADV_ATTR_VERSION] = { .type = NLA_STRING }, - [BATADV_ATTR_ALGO_NAME] = { .type = NLA_STRING }, - [BATADV_ATTR_MESH_IFINDEX] = { .type = NLA_U32 }, - [BATADV_ATTR_MESH_IFNAME] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ }, - [BATADV_ATTR_MESH_ADDRESS] = { .type = NLA_UNSPEC, + [BATADV_ATTR_VERSION] = { + .type = NLA_STRING, + }, + [BATADV_ATTR_ALGO_NAME] = { + .type = NLA_STRING, + }, + [BATADV_ATTR_MESH_IFINDEX] = { + .type = NLA_U32, + }, + [BATADV_ATTR_MESH_IFNAME] = { + .type = NLA_STRING, + .maxlen = IFNAMSIZ, + }, + [BATADV_ATTR_MESH_ADDRESS] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_HARD_IFINDEX] = { .type = NLA_U32 }, - [BATADV_ATTR_HARD_IFNAME] = { .type = NLA_STRING, - .maxlen = IFNAMSIZ }, - [BATADV_ATTR_HARD_ADDRESS] = { .type = NLA_UNSPEC, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_HARD_IFINDEX] = { + .type = NLA_U32, + }, + [BATADV_ATTR_HARD_IFNAME] = { + .type = NLA_STRING, + .maxlen = IFNAMSIZ, + }, + [BATADV_ATTR_HARD_ADDRESS] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_ORIG_ADDRESS] = { .type = NLA_UNSPEC, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_ORIG_ADDRESS] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_TPMETER_RESULT] = { .type = NLA_U8 }, - [BATADV_ATTR_TPMETER_TEST_TIME] = { .type = NLA_U32 }, - [BATADV_ATTR_TPMETER_BYTES] = { .type = NLA_U64 }, - [BATADV_ATTR_TPMETER_COOKIE] = { .type = NLA_U32 }, - [BATADV_ATTR_PAD] = { .type = NLA_UNSPEC }, - [BATADV_ATTR_ACTIVE] = { .type = NLA_FLAG }, - [BATADV_ATTR_TT_ADDRESS] = { .type = NLA_UNSPEC, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_TPMETER_RESULT] = { + .type = NLA_U8, + }, + [BATADV_ATTR_TPMETER_TEST_TIME] = { + .type = NLA_U32, + }, + [BATADV_ATTR_TPMETER_BYTES] = { + .type = NLA_U64, + }, + [BATADV_ATTR_TPMETER_COOKIE] = { + .type = NLA_U32, + }, + [BATADV_ATTR_PAD] = { + .type = NLA_UNSPEC, + }, + [BATADV_ATTR_ACTIVE] = { + .type = NLA_FLAG, + }, + [BATADV_ATTR_TT_ADDRESS] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_TT_TTVN] = { .type = NLA_U8 }, - [BATADV_ATTR_TT_LAST_TTVN] = { .type = NLA_U8 }, - [BATADV_ATTR_TT_CRC32] = { .type = NLA_U32 }, - [BATADV_ATTR_TT_VID] = { .type = NLA_U16 }, - [BATADV_ATTR_TT_FLAGS] = { .type = NLA_U32 }, - [BATADV_ATTR_FLAG_BEST] = { .type = NLA_FLAG }, - [BATADV_ATTR_LAST_SEEN_MSECS] = { .type = NLA_U32 }, - [BATADV_ATTR_NEIGH_ADDRESS] = { .type = NLA_UNSPEC, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_TT_TTVN] = { + .type = NLA_U8, + }, + [BATADV_ATTR_TT_LAST_TTVN] = { + .type = NLA_U8, + }, + [BATADV_ATTR_TT_CRC32] = { + .type = NLA_U32, + }, + [BATADV_ATTR_TT_VID] = { + .type = NLA_U16, + }, + [BATADV_ATTR_TT_FLAGS] = { + .type = NLA_U32, + }, + [BATADV_ATTR_FLAG_BEST] = { + .type = NLA_FLAG, + }, + [BATADV_ATTR_LAST_SEEN_MSECS] = { + .type = NLA_U32, + }, + [BATADV_ATTR_NEIGH_ADDRESS] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_TQ] = { .type = NLA_U8 }, - [BATADV_ATTR_THROUGHPUT] = { .type = NLA_U32 }, - [BATADV_ATTR_BANDWIDTH_UP] = { .type = NLA_U32 }, - [BATADV_ATTR_BANDWIDTH_DOWN] = { .type = NLA_U32 }, - [BATADV_ATTR_ROUTER] = { .type = NLA_UNSPEC, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_TQ] = { + .type = NLA_U8, + }, + [BATADV_ATTR_THROUGHPUT] = { + .type = NLA_U32, + }, + [BATADV_ATTR_BANDWIDTH_UP] = { + .type = NLA_U32, + }, + [BATADV_ATTR_BANDWIDTH_DOWN] = { + .type = NLA_U32, + }, + [BATADV_ATTR_ROUTER] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_BLA_OWN] = { .type = NLA_FLAG }, - [BATADV_ATTR_BLA_ADDRESS] = { .type = NLA_UNSPEC, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_BLA_OWN] = { + .type = NLA_FLAG, + }, + [BATADV_ATTR_BLA_ADDRESS] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_BLA_VID] = { .type = NLA_U16 }, - [BATADV_ATTR_BLA_BACKBONE] = { .type = NLA_UNSPEC, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_BLA_VID] = { + .type = NLA_U16, + }, + [BATADV_ATTR_BLA_BACKBONE] = { + .type = NLA_UNSPEC, .minlen = ETH_ALEN, - .maxlen = ETH_ALEN }, - [BATADV_ATTR_BLA_CRC] = { .type = NLA_U16 }, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_BLA_CRC] = { + .type = NLA_U16, + }, + [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { + .type = NLA_U32, + }, + [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { + .type = NLA_UNSPEC, + .minlen = ETH_ALEN, + .maxlen = ETH_ALEN, + }, + [BATADV_ATTR_DAT_CACHE_VID] = { + .type = NLA_U16, + }, + [BATADV_ATTR_MCAST_FLAGS] = { + .type = NLA_U32, + }, + [BATADV_ATTR_MCAST_FLAGS_PRIV] = { + .type = NLA_U32, + }, + [BATADV_ATTR_VLANID] = { + .type = NLA_U16, + }, + [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_AP_ISOLATION_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_ISOLATION_MARK] = { + .type = NLA_U32, + }, + [BATADV_ATTR_ISOLATION_MASK] = { + .type = NLA_U32, + }, + [BATADV_ATTR_BONDING_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_FRAGMENTATION_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_GW_BANDWIDTH_DOWN] = { + .type = NLA_U32, + }, + [BATADV_ATTR_GW_BANDWIDTH_UP] = { + .type = NLA_U32, + }, + [BATADV_ATTR_GW_MODE] = { + .type = NLA_U8, + }, + [BATADV_ATTR_GW_SEL_CLASS] = { + .type = NLA_U32, + }, + [BATADV_ATTR_HOP_PENALTY] = { + .type = NLA_U8, + }, + [BATADV_ATTR_LOG_LEVEL] = { + .type = NLA_U32, + }, + [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_NETWORK_CODING_ENABLED] = { + .type = NLA_U8, + }, + [BATADV_ATTR_ORIG_INTERVAL] = { + .type = NLA_U32, + }, + [BATADV_ATTR_ELP_INTERVAL] = { + .type = NLA_U32, + }, + [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { + .type = NLA_U32, + }, + [BATADV_ATTR_MULTICAST_FANOUT] = { + .type = NLA_U32, + }, }; /** diff --git a/package/libbatadv/src/batman_adv.h b/package/libbatadv/src/batman_adv.h index ae00c99cbe..35dc016c9b 100644 --- a/package/libbatadv/src/batman_adv.h +++ b/package/libbatadv/src/batman_adv.h @@ -1,25 +1,7 @@ /* SPDX-License-Identifier: MIT */ -/* Copyright (C) 2016-2017 B.A.T.M.A.N. contributors: +/* Copyright (C) B.A.T.M.A.N. contributors: * * Matthias Schiffer - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. */ #ifndef _UAPI_LINUX_BATMAN_ADV_H_ @@ -27,6 +9,7 @@ #define BATADV_NL_NAME "batadv" +#define BATADV_NL_MCAST_GROUP_CONFIG "config" #define BATADV_NL_MCAST_GROUP_TPMETER "tpmeter" /** @@ -86,11 +69,72 @@ enum batadv_tt_client_flags { /** * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be - * part of the network but no nnode has already announced it + * part of the network but no node has already announced it */ BATADV_TT_CLIENT_TEMP = (1 << 11), }; +/** + * enum batadv_mcast_flags_priv - Private, own multicast flags + * + * These are internal, multicast related flags. Currently they describe certain + * multicast related attributes of the segment this originator bridges into the + * mesh. + * + * Those attributes are used to determine the public multicast flags this + * originator is going to announce via TT. + * + * For netlink, if BATADV_MCAST_FLAGS_BRIDGED is unset then all querier + * related flags are undefined. + */ +enum batadv_mcast_flags_priv { + /** + * @BATADV_MCAST_FLAGS_BRIDGED: There is a bridge on top of the mesh + * interface. + */ + BATADV_MCAST_FLAGS_BRIDGED = (1 << 0), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS: Whether an IGMP querier + * exists in the mesh + */ + BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS = (1 << 1), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS: Whether an MLD querier + * exists in the mesh + */ + BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS = (1 << 2), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING: If an IGMP querier + * exists, whether it is potentially shadowing multicast listeners + * (i.e. querier is behind our own bridge segment) + */ + BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING = (1 << 3), + + /** + * @BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING: If an MLD querier + * exists, whether it is potentially shadowing multicast listeners + * (i.e. querier is behind our own bridge segment) + */ + BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING = (1 << 4), +}; + +/** + * enum batadv_gw_modes - gateway mode of node + */ +enum batadv_gw_modes { + /** @BATADV_GW_MODE_OFF: gw mode disabled */ + BATADV_GW_MODE_OFF, + + /** @BATADV_GW_MODE_CLIENT: send DHCP requests to gw servers */ + BATADV_GW_MODE_CLIENT, + + /** @BATADV_GW_MODE_SERVER: announce itself as gateway server */ + BATADV_GW_MODE_SERVER, +}; + /** * enum batadv_nl_attrs - batman-adv netlink attributes */ @@ -272,6 +316,171 @@ enum batadv_nl_attrs { */ BATADV_ATTR_BLA_CRC, + /** + * @BATADV_ATTR_DAT_CACHE_IP4ADDRESS: Client IPv4 address + */ + BATADV_ATTR_DAT_CACHE_IP4ADDRESS, + + /** + * @BATADV_ATTR_DAT_CACHE_HWADDRESS: Client MAC address + */ + BATADV_ATTR_DAT_CACHE_HWADDRESS, + + /** + * @BATADV_ATTR_DAT_CACHE_VID: VLAN ID + */ + BATADV_ATTR_DAT_CACHE_VID, + + /** + * @BATADV_ATTR_MCAST_FLAGS: Per originator multicast flags + */ + BATADV_ATTR_MCAST_FLAGS, + + /** + * @BATADV_ATTR_MCAST_FLAGS_PRIV: Private, own multicast flags + */ + BATADV_ATTR_MCAST_FLAGS_PRIV, + + /** + * @BATADV_ATTR_VLANID: VLAN id on top of soft interface + */ + BATADV_ATTR_VLANID, + + /** + * @BATADV_ATTR_AGGREGATED_OGMS_ENABLED: whether the batman protocol + * messages of the mesh interface shall be aggregated or not. + */ + BATADV_ATTR_AGGREGATED_OGMS_ENABLED, + + /** + * @BATADV_ATTR_AP_ISOLATION_ENABLED: whether the data traffic going + * from a wireless client to another wireless client will be silently + * dropped. + */ + BATADV_ATTR_AP_ISOLATION_ENABLED, + + /** + * @BATADV_ATTR_ISOLATION_MARK: the isolation mark which is used to + * classify clients as "isolated" by the Extended Isolation feature. + */ + BATADV_ATTR_ISOLATION_MARK, + + /** + * @BATADV_ATTR_ISOLATION_MASK: the isolation (bit)mask which is used to + * classify clients as "isolated" by the Extended Isolation feature. + */ + BATADV_ATTR_ISOLATION_MASK, + + /** + * @BATADV_ATTR_BONDING_ENABLED: whether the data traffic going through + * the mesh will be sent using multiple interfaces at the same time. + */ + BATADV_ATTR_BONDING_ENABLED, + + /** + * @BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED: whether the bridge loop + * avoidance feature is enabled. This feature detects and avoids loops + * between the mesh and devices bridged with the soft interface + */ + BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED, + + /** + * @BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED: whether the distributed + * arp table feature is enabled. This feature uses a distributed hash + * table to answer ARP requests without flooding the request through + * the whole mesh. + */ + BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED, + + /** + * @BATADV_ATTR_FRAGMENTATION_ENABLED: whether the data traffic going + * through the mesh will be fragmented or silently discarded if the + * packet size exceeds the outgoing interface MTU. + */ + BATADV_ATTR_FRAGMENTATION_ENABLED, + + /** + * @BATADV_ATTR_GW_BANDWIDTH_DOWN: defines the download bandwidth which + * is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set + * to 'server'. + */ + BATADV_ATTR_GW_BANDWIDTH_DOWN, + + /** + * @BATADV_ATTR_GW_BANDWIDTH_UP: defines the upload bandwidth which + * is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set + * to 'server'. + */ + BATADV_ATTR_GW_BANDWIDTH_UP, + + /** + * @BATADV_ATTR_GW_MODE: defines the state of the gateway features. + * Possible values are specified in enum batadv_gw_modes + */ + BATADV_ATTR_GW_MODE, + + /** + * @BATADV_ATTR_GW_SEL_CLASS: defines the selection criteria this node + * will use to choose a gateway if gw_mode was set to 'client'. + */ + BATADV_ATTR_GW_SEL_CLASS, + + /** + * @BATADV_ATTR_HOP_PENALTY: defines the penalty which will be applied + * to an originator message's tq-field on every hop and/or per + * hard interface + */ + BATADV_ATTR_HOP_PENALTY, + + /** + * @BATADV_ATTR_LOG_LEVEL: bitmask with to define which debug messages + * should be send to the debug log/trace ring buffer + */ + BATADV_ATTR_LOG_LEVEL, + + /** + * @BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED: whether multicast + * optimizations should be replaced by simple broadcast-like flooding + * of multicast packets. If set to non-zero then all nodes in the mesh + * are going to use classic flooding for any multicast packet with no + * optimizations. + */ + BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED, + + /** + * @BATADV_ATTR_NETWORK_CODING_ENABLED: whether Network Coding (using + * some magic to send fewer wifi packets but still the same content) is + * enabled or not. + */ + BATADV_ATTR_NETWORK_CODING_ENABLED, + + /** + * @BATADV_ATTR_ORIG_INTERVAL: defines the interval in milliseconds in + * which batman sends its protocol messages. + */ + BATADV_ATTR_ORIG_INTERVAL, + + /** + * @BATADV_ATTR_ELP_INTERVAL: defines the interval in milliseconds in + * which batman emits probing packets for neighbor sensing (ELP). + */ + BATADV_ATTR_ELP_INTERVAL, + + /** + * @BATADV_ATTR_THROUGHPUT_OVERRIDE: defines the throughput value to be + * used by B.A.T.M.A.N. V when estimating the link throughput using + * this interface. If the value is set to 0 then batman-adv will try to + * estimate the throughput by itself. + */ + BATADV_ATTR_THROUGHPUT_OVERRIDE, + + /** + * @BATADV_ATTR_MULTICAST_FANOUT: defines the maximum number of packet + * copies that may be generated for a multicast-to-unicast conversion. + * Once this limit is exceeded distribution will fall back to broadcast. + */ + BATADV_ATTR_MULTICAST_FANOUT, + /* add attributes above here, update the policy in netlink.c */ /** @@ -300,10 +509,14 @@ enum batadv_nl_commands { BATADV_CMD_UNSPEC, /** - * @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv - * device + * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh */ - BATADV_CMD_GET_MESH_INFO, + BATADV_CMD_GET_MESH, + + /** + * @BATADV_CMD_GET_MESH_INFO: Alias for @BATADV_CMD_GET_MESH + */ + BATADV_CMD_GET_MESH_INFO = BATADV_CMD_GET_MESH, /** * @BATADV_CMD_TP_METER: Start a tp meter session @@ -321,9 +534,15 @@ enum batadv_nl_commands { BATADV_CMD_GET_ROUTING_ALGOS, /** - * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces + * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the + * current softif */ - BATADV_CMD_GET_HARDIFS, + BATADV_CMD_GET_HARDIF, + + /** + * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF + */ + BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF, /** * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations @@ -361,6 +580,39 @@ enum batadv_nl_commands { */ BATADV_CMD_GET_BLA_BACKBONE, + /** + * @BATADV_CMD_GET_DAT_CACHE: Query list of DAT cache entries + */ + BATADV_CMD_GET_DAT_CACHE, + + /** + * @BATADV_CMD_GET_MCAST_FLAGS: Query list of multicast flags + */ + BATADV_CMD_GET_MCAST_FLAGS, + + /** + * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh + */ + BATADV_CMD_SET_MESH, + + /** + * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the + * current softif + */ + BATADV_CMD_SET_HARDIF, + + /** + * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the + * current softif + */ + BATADV_CMD_GET_VLAN, + + /** + * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the + * current softif + */ + BATADV_CMD_SET_VLAN, + /* add new commands above here */ /** @@ -423,4 +675,30 @@ enum batadv_tp_meter_reason { BATADV_TP_REASON_TOO_MANY = 133, }; +/** + * enum batadv_ifla_attrs - batman-adv ifla nested attributes + */ +enum batadv_ifla_attrs { + /** + * @IFLA_BATADV_UNSPEC: unspecified attribute which is not parsed by + * rtnetlink + */ + IFLA_BATADV_UNSPEC, + + /** + * @IFLA_BATADV_ALGO_NAME: routing algorithm (name) which should be + * used by the newly registered batadv net_device. + */ + IFLA_BATADV_ALGO_NAME, + + /* add attributes above here, update the policy in soft-interface.c */ + + /** + * @__IFLA_BATADV_MAX: internal use + */ + __IFLA_BATADV_MAX, +}; + +#define IFLA_BATADV_MAX (__IFLA_BATADV_MAX - 1) + #endif /* _UAPI_LINUX_BATMAN_ADV_H_ */ diff --git a/patches/openwrt/0004-prune-opkg.patch b/patches/openwrt/0004-prune-opkg.patch new file mode 100644 index 0000000000..0304bb9642 --- /dev/null +++ b/patches/openwrt/0004-prune-opkg.patch @@ -0,0 +1,25 @@ +From 7b8b5624e8eb4674246b55d693dac82e2fe927f7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= +Date: Fri, 30 Dec 2022 03:07:27 +0100 +Subject: [PATCH] prune opkg + +--- + include/rootfs.mk | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/include/rootfs.mk b/include/rootfs.mk +index f2ed648d2f..76f95d4123 100644 +--- a/include/rootfs.mk ++++ b/include/rootfs.mk +@@ -98,4 +98,8 @@ define prepare_rootfs + $(call clean_ipkg,$(1)) + $(call mklibs,$(1)) + $(if $(SOURCE_DATE_EPOCH),find $(1)/ -mindepth 1 -execdir touch -hcd "@$(SOURCE_DATE_EPOCH)" "{}" +) ++ test "$(GLUON_DEBUG)" == "1" || test "$(KEEP_OPKG)" == "1" || rm -rf $(1)/usr/lib/opkg ++ test "$(GLUON_DEBUG)" == "1" || test "$(KEEP_OPKG)" == "1" || rm -rf $(1)/bin/opkg ++ test "$(GLUON_DEBUG)" == "1" || test "$(KEEP_OPKG)" == "1" || rm -rf $(1)/usr/sbin/opkg-key ++ test "$(GLUON_DEBUG)" == "1" || test "$(KEEP_OPKG)" == "1" || rm -rf $(1)/etc/opkg + endef +-- +2.38.1 + diff --git a/patches/openwrt/0004-ramips-mt7621-make-DSA-images-swconfig-upgradable.patch b/patches/openwrt/0004-ramips-mt7621-make-DSA-images-swconfig-upgradable.patch deleted file mode 100644 index 06bc534c2a..0000000000 --- a/patches/openwrt/0004-ramips-mt7621-make-DSA-images-swconfig-upgradable.patch +++ /dev/null @@ -1,64 +0,0 @@ -From: David Bauer -Date: Sun, 5 Jun 2022 23:43:38 +0200 -Subject: ramips-mt7621: make DSA images swconfig upgradable - -diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk -index c3e2291a9d91bae234707b1d81277b645bdd6920..64e02a53d30b71144ef4158a06c3a791f8d91f90 100644 ---- a/target/linux/ramips/image/mt7621.mk -+++ b/target/linux/ramips/image/mt7621.mk -@@ -180,7 +180,6 @@ endef - TARGET_DEVICES += asiarf_ap7621-nv1 - - define Device/asus_rt-ac57u -- $(Device/dsa-migration) - DEVICE_VENDOR := ASUS - DEVICE_MODEL := RT-AC57U - DEVICE_ALT0_VENDOR := ASUS -@@ -1271,7 +1270,6 @@ endef - TARGET_DEVICES += mts_wg430223 - - define Device/netgear_ex6150 -- $(Device/dsa-migration) - $(Device/uimage-lzma-loader) - DEVICE_VENDOR := NETGEAR - DEVICE_MODEL := EX6150 -@@ -1284,7 +1282,6 @@ endef - TARGET_DEVICES += netgear_ex6150 - - define Device/netgear_sercomm_nand -- $(Device/dsa-migration) - $(Device/uimage-lzma-loader) - BLOCKSIZE := 128k - PAGESIZE := 2048 -@@ -1467,7 +1464,6 @@ endef - TARGET_DEVICES += netgear_wax202 - - define Device/netgear_wndr3700-v5 -- $(Device/dsa-migration) - $(Device/netgear_sercomm_nor) - $(Device/uimage-lzma-loader) - IMAGE_SIZE := 15232k -@@ -1804,7 +1800,6 @@ endef - TARGET_DEVICES += tplink_tl-wpa8631p-v3 - - define Device/ubnt_edgerouter_common -- $(Device/dsa-migration) - $(Device/uimage-lzma-loader) - DEVICE_VENDOR := Ubiquiti - IMAGE_SIZE := 256768k -@@ -2220,7 +2215,6 @@ endef - TARGET_DEVICES += zbtlink_zbt-wg2626 - - define Device/zbtlink_zbt-wg3526-16m -- $(Device/dsa-migration) - $(Device/uimage-lzma-loader) - IMAGE_SIZE := 16064k - DEVICE_VENDOR := Zbtlink -@@ -2233,7 +2227,6 @@ endef - TARGET_DEVICES += zbtlink_zbt-wg3526-16m - - define Device/zbtlink_zbt-wg3526-32m -- $(Device/dsa-migration) - $(Device/uimage-lzma-loader) - IMAGE_SIZE := 32448k - DEVICE_VENDOR := Zbtlink diff --git a/patches/openwrt/0009-xrx200-migrate-fritz7360-v2-using-incorrect-image.patch b/patches/openwrt/0004-xrx200-migrate-fritz7360-v2-using-incorrect-image.patch similarity index 100% rename from patches/openwrt/0009-xrx200-migrate-fritz7360-v2-using-incorrect-image.patch rename to patches/openwrt/0004-xrx200-migrate-fritz7360-v2-using-incorrect-image.patch diff --git a/patches/openwrt/0005-ramips-add-MT7621-WiFi-devpath-migration.patch b/patches/openwrt/0005-ramips-add-MT7621-WiFi-devpath-migration.patch deleted file mode 100644 index 5e02c78041..0000000000 --- a/patches/openwrt/0005-ramips-add-MT7621-WiFi-devpath-migration.patch +++ /dev/null @@ -1,53 +0,0 @@ -From: David Bauer -Date: Sat, 18 Jun 2022 02:37:56 +0200 -Subject: ramips: add MT7621 WiFi devpath migration - -Add a migration script to migrate the device path of PCIe WiFi hardware -from OpenWrt 19.07 to the one used with OpenWrt 21.02+. - -Signed-off-by: David Bauer - -diff --git a/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/00-wifi-migration b/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/00-wifi-migration -new file mode 100644 -index 0000000000000000000000000000000000000000..17fd4a58ff2d56694743e149292746c136b6f27a ---- /dev/null -+++ b/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/00-wifi-migration -@@ -0,0 +1,38 @@ -+#!/bin/sh -+ -+# Migrate WiFi path from 19.07 to 21.02+ -+ -+WIFI_PATH_CHANGED=0 -+ -+. /lib/functions.sh -+ -+migrate_wifi_path() { -+ local section="$1" -+ local path -+ -+ config_get path ${section} path -+ case ${path} in -+ "pci0000:00/0000:00:00.0/0000:01:00.0") -+ path="1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0" -+ WIFI_PATH_CHANGED=1 -+ ;; -+ "pci0000:00/0000:00:01.0/0000:02:00.0") -+ path="1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0" -+ WIFI_PATH_CHANGED=1 -+ ;; -+ *) -+ return 0 -+ ;; -+ esac -+ -+ uci set wireless.${section}.path=${path} -+} -+ -+[ "${ACTION}" = "add" ] && { -+ [ ! -e /etc/config/wireless ] && return 0 -+ -+ config_load wireless -+ config_foreach migrate_wifi_path wifi-device -+ -+ [ "${WIFI_PATH_CHANGED}" = "1" ] && uci commit wireless -+} diff --git a/patches/openwrt/0006-lantiq-xrx200-make-DSA-images-swconfig-upgradable.patch b/patches/openwrt/0006-lantiq-xrx200-make-DSA-images-swconfig-upgradable.patch deleted file mode 100644 index 061a898c9f..0000000000 --- a/patches/openwrt/0006-lantiq-xrx200-make-DSA-images-swconfig-upgradable.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: David Bauer -Date: Tue, 5 Jul 2022 23:49:31 +0200 -Subject: lantiq-xrx200: make DSA images swconfig upgradable - -diff --git a/target/linux/lantiq/image/tp-link.mk b/target/linux/lantiq/image/tp-link.mk -index 756105b94c7f844439132ab57837c9ac87d231ca..5563968de9ab3f491fa89d755fcc6e6fff79948f 100644 ---- a/target/linux/lantiq/image/tp-link.mk -+++ b/target/linux/lantiq/image/tp-link.mk -@@ -1,7 +1,7 @@ - DEVICE_VARS += TPLINK_FLASHLAYOUT TPLINK_HWID TPLINK_HWREV TPLINK_HWREVADD TPLINK_HVERSION - - define Device/dsa-migration -- DEVICE_COMPAT_VERSION := 1.1 -+ DEVICE_COMPAT_VERSION := 1.0 - DEVICE_COMPAT_MESSAGE := Config cannot be migrated from swconfig to DSA - endef - -diff --git a/target/linux/lantiq/image/vr9.mk b/target/linux/lantiq/image/vr9.mk -index deea3996670fcef6d2a546373425c4ca5d92734b..d23b80e94e5548ccb19738f22c29c75fa19ab1e7 100644 ---- a/target/linux/lantiq/image/vr9.mk -+++ b/target/linux/lantiq/image/vr9.mk -@@ -1,7 +1,7 @@ - DEVICE_VARS += NETGEAR_BOARD_ID NETGEAR_HW_ID - - define Device/dsa-migration -- DEVICE_COMPAT_VERSION := 1.1 -+ DEVICE_COMPAT_VERSION := 1.0 - DEVICE_COMPAT_MESSAGE := Config cannot be migrated from swconfig to DSA - endef - diff --git a/patches/openwrt/0007-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch b/patches/openwrt/0007-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch deleted file mode 100644 index ec9641c032..0000000000 --- a/patches/openwrt/0007-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch +++ /dev/null @@ -1,914 +0,0 @@ -From: Linus Lüssing -Date: Sat, 1 Jan 2022 10:09:13 +0100 -Subject: kernel: bridge: Implement MLD Querier wake-up calls / Android bug workaround - -Implement a configurable MLD Querier wake-up calls "feature" which -works around a widely spread Android bug in connection with IGMP/MLD -snooping. - -Currently there are mobile devices (e.g. Android) which are not able -to receive and respond to MLD Queries reliably because the Wifi driver -filters a lot of ICMPv6 when the device is asleep - including -MLD. This in turn breaks IPv6 communication when MLD Snooping is -enabled. However there is one ICMPv6 type which is allowed to pass and -which can be used to wake up the mobile device: ICMPv6 Echo Requests. - -If this bridge is the selected MLD Querier then setting -"multicast_wakeupcall" to a number n greater than 0 will send n -ICMPv6 Echo Requests to each host behind this port to wake -them up with each MLD Query. Upon receiving a matching ICMPv6 Echo -Reply an MLD Query with a unicast ethernet destination will be sent -to the specific host(s). - -Link: https://issuetracker.google.com/issues/149630944 -Link: https://github.com/freifunk-gluon/gluon/issues/1832 - -Signed-off-by: Linus Lüssing - -diff --git a/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch b/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch -new file mode 100644 -index 0000000000000000000000000000000000000000..e3da684dc950ea4c226705d27b23b0470499d54b ---- /dev/null -+++ b/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch -@@ -0,0 +1,176 @@ -+From 6db5050d9e046a486260ea23a77118db4f8a6672 Mon Sep 17 00:00:00 2001 -+From: =?UTF-8?q?Linus=20L=C3=BCssing?= -+Date: Sun, 5 Jul 2020 23:33:51 +0200 -+Subject: [PATCH] bridge: Add multicast_wakeupcall option -+MIME-Version: 1.0 -+Content-Type: text/plain; charset=UTF-8 -+Content-Transfer-Encoding: 8bit -+ -+This makes the new per bridge port multicast_wakeupcall feature -+for the Linux bridge configurable for wireless interfaces and enables it -+by default for an AP interface. -+ -+The MLD Querier wake-up calls "feature" works around a widely spread Android -+bug in connection with IGMP/MLD snooping. -+ -+Currently there are mobile devices (e.g. Android) which are not able -+to receive and respond to MLD Queries reliably because the Wifi driver -+filters a lot of ICMPv6 when the device is asleep - including -+MLD. This in turn breaks IPv6 communication when MLD Snooping is -+enabled. However there is one ICMPv6 type which is allowed to pass and -+which can be used to wake up the mobile device: ICMPv6 Echo Requests. -+ -+If this bridge is the selected MLD Querier then setting -+"multicast_wakeupcall" to a number n greater than 0 will send n -+ICMPv6 Echo Requests to each host behind this port to wake -+them up with each MLD Query. Upon receiving a matching ICMPv6 Echo -+Reply an MLD Query with a unicast ethernet destination will be sent -+to the specific host(s). -+ -+Link: https://issuetracker.google.com/issues/149630944 -+Link: https://github.com/freifunk-gluon/gluon/issues/1832 -+ -+Signed-off-by: Linus Lüssing -+--- -+ device.c | 9 +++++++++ -+ device.h | 37 ++++++++++++++++++++----------------- -+ system-linux.c | 13 +++++++++++++ -+ 3 files changed, 42 insertions(+), 17 deletions(-) -+ -+--- a/device.c -++++ b/device.c -+@@ -47,6 +47,7 @@ static const struct blobmsg_policy dev_a -+ [DEV_ATTR_NEIGHGCSTALETIME] = { .name = "neighgcstaletime", .type = BLOBMSG_TYPE_INT32 }, -+ [DEV_ATTR_DADTRANSMITS] = { .name = "dadtransmits", .type = BLOBMSG_TYPE_INT32 }, -+ [DEV_ATTR_MULTICAST_TO_UNICAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL }, -++ [DEV_ATTR_MULTICAST_WAKEUPCALL] = { .name = "multicast_wakeupcall", .type = BLOBMSG_TYPE_INT32 }, -+ [DEV_ATTR_MULTICAST_ROUTER] = { .name = "multicast_router", .type = BLOBMSG_TYPE_INT32 }, -+ [DEV_ATTR_MULTICAST_FAST_LEAVE] = { .name = "multicast_fast_leave", . type = BLOBMSG_TYPE_BOOL }, -+ [DEV_ATTR_MULTICAST] = { .name ="multicast", .type = BLOBMSG_TYPE_BOOL }, -+@@ -261,6 +262,7 @@ device_merge_settings(struct device *dev -+ n->multicast = s->flags & DEV_OPT_MULTICAST ? -+ s->multicast : os->multicast; -+ n->multicast_to_unicast = s->multicast_to_unicast; -++ n->multicast_wakeupcall = s->multicast_wakeupcall; -+ n->multicast_router = s->multicast_router; -+ n->multicast_fast_leave = s->multicast_fast_leave; -+ n->learning = s->learning; -+@@ -386,6 +388,11 @@ device_init_settings(struct device *dev, -+ s->flags |= DEV_OPT_MULTICAST_TO_UNICAST; -+ } -+ -++ if ((cur = tb[DEV_ATTR_MULTICAST_WAKEUPCALL])) { -++ s->multicast_wakeupcall = blobmsg_get_u32(cur); -++ s->flags |= DEV_OPT_MULTICAST_WAKEUPCALL; -++ } -++ -+ if ((cur = tb[DEV_ATTR_MULTICAST_ROUTER])) { -+ s->multicast_router = blobmsg_get_u32(cur); -+ if (s->multicast_router <= 2) -+@@ -1186,6 +1193,8 @@ device_dump_status(struct blob_buf *b, s -+ blobmsg_add_u32(b, "dadtransmits", st.dadtransmits); -+ if (st.flags & DEV_OPT_MULTICAST_TO_UNICAST) -+ blobmsg_add_u8(b, "multicast_to_unicast", st.multicast_to_unicast); -++ if (st.flags & DEV_OPT_MULTICAST_WAKEUPCALL) -++ blobmsg_add_u32(b, "multicast_wakeupcall", st.multicast_wakeupcall); -+ if (st.flags & DEV_OPT_MULTICAST_ROUTER) -+ blobmsg_add_u32(b, "multicast_router", st.multicast_router); -+ if (st.flags & DEV_OPT_MULTICAST_FAST_LEAVE) -+--- a/device.h -++++ b/device.h -+@@ -44,6 +44,7 @@ enum { -+ DEV_ATTR_NEIGHREACHABLETIME, -+ DEV_ATTR_DADTRANSMITS, -+ DEV_ATTR_MULTICAST_TO_UNICAST, -++ DEV_ATTR_MULTICAST_WAKEUPCALL, -+ DEV_ATTR_MULTICAST_ROUTER, -+ DEV_ATTR_MULTICAST_FAST_LEAVE, -+ DEV_ATTR_MULTICAST, -+@@ -109,23 +110,24 @@ enum { -+ DEV_OPT_MTU6 = (1ULL << 12), -+ DEV_OPT_DADTRANSMITS = (1ULL << 13), -+ DEV_OPT_MULTICAST_TO_UNICAST = (1ULL << 14), -+- DEV_OPT_MULTICAST_ROUTER = (1ULL << 15), -+- DEV_OPT_MULTICAST = (1ULL << 16), -+- DEV_OPT_LEARNING = (1ULL << 17), -+- DEV_OPT_UNICAST_FLOOD = (1ULL << 18), -+- DEV_OPT_NEIGHGCSTALETIME = (1ULL << 19), -+- DEV_OPT_MULTICAST_FAST_LEAVE = (1ULL << 20), -+- DEV_OPT_SENDREDIRECTS = (1ULL << 21), -+- DEV_OPT_NEIGHLOCKTIME = (1ULL << 22), -+- DEV_OPT_ISOLATE = (1ULL << 23), -+- DEV_OPT_IP6SEGMENTROUTING = (1ULL << 24), -+- DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST = (1ULL << 25), -+- DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST = (1ULL << 26), -+- DEV_OPT_DROP_GRATUITOUS_ARP = (1ULL << 27), -+- DEV_OPT_DROP_UNSOLICITED_NA = (1ULL << 28), -+- DEV_OPT_ARP_ACCEPT = (1ULL << 29), -+- DEV_OPT_SPEED = (1ULL << 30), -+- DEV_OPT_DUPLEX = (1ULL << 31), -++ DEV_OPT_MULTICAST_WAKEUPCALL = (1ULL << 15), -++ DEV_OPT_MULTICAST_ROUTER = (1ULL << 16), -++ DEV_OPT_MULTICAST = (1ULL << 17), -++ DEV_OPT_LEARNING = (1ULL << 18), -++ DEV_OPT_UNICAST_FLOOD = (1ULL << 19), -++ DEV_OPT_NEIGHGCSTALETIME = (1ULL << 20), -++ DEV_OPT_MULTICAST_FAST_LEAVE = (1ULL << 21), -++ DEV_OPT_SENDREDIRECTS = (1ULL << 22), -++ DEV_OPT_NEIGHLOCKTIME = (1ULL << 23), -++ DEV_OPT_ISOLATE = (1ULL << 24), -++ DEV_OPT_IP6SEGMENTROUTING = (1ULL << 25), -++ DEV_OPT_DROP_V4_UNICAST_IN_L2_MULTICAST = (1ULL << 26), -++ DEV_OPT_DROP_V6_UNICAST_IN_L2_MULTICAST = (1ULL << 27), -++ DEV_OPT_DROP_GRATUITOUS_ARP = (1ULL << 28), -++ DEV_OPT_DROP_UNSOLICITED_NA = (1ULL << 29), -++ DEV_OPT_ARP_ACCEPT = (1ULL << 30), -++ DEV_OPT_SPEED = (1ULL << 31), -++ DEV_OPT_DUPLEX = (1ULL << 32), -+ }; -+ -+ /* events broadcasted to all users of a device */ -+@@ -187,6 +189,7 @@ struct device_settings { -+ int neigh4locktime; -+ unsigned int dadtransmits; -+ bool multicast_to_unicast; -++ unsigned int multicast_wakeupcall; -+ unsigned int multicast_router; -+ bool multicast_fast_leave; -+ bool multicast; -+--- a/system-linux.c -++++ b/system-linux.c -+@@ -465,6 +465,11 @@ static void system_bridge_set_multicast_ -+ system_set_dev_sysfs("brport/multicast_to_unicast", dev->ifname, val); -+ } -+ -++static void system_bridge_set_multicast_wakeupcall(struct device *dev, const char *val) -++{ -++ system_set_dev_sysfs("brport/multicast_wakeupcall", dev->ifname, val); -++} -++ -+ static void system_bridge_set_multicast_fast_leave(struct device *dev, const char *val) -+ { -+ system_set_dev_sysfs("brport/multicast_fast_leave", dev->ifname, val); -+@@ -844,8 +849,10 @@ static char *system_get_bridge(const cha -+ static void -+ system_bridge_set_wireless(struct device *bridge, struct device *dev) -+ { -++ unsigned int mcast_wakeupcall = dev->wireless_ap ? 2 : 0; -+ bool mcast_to_ucast = dev->wireless_ap; -+ bool hairpin; -++ char buf[64]; -+ -+ if (bridge->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST && -+ !bridge->settings.multicast_to_unicast) -+@@ -858,6 +865,12 @@ system_bridge_set_wireless(struct device -+ system_bridge_set_multicast_to_unicast(dev, mcast_to_ucast ? "1" : "0"); -+ system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0"); -+ system_bridge_set_proxyarp_wifi(dev, dev->wireless_proxyarp ? "1" : "0"); -++ -++ if (bridge->settings.flags & DEV_OPT_MULTICAST_WAKEUPCALL) -++ mcast_wakeupcall = dev->settings.multicast_wakeupcall; -++ -++ snprintf(buf, sizeof(buf), "%u", mcast_wakeupcall); -++ system_bridge_set_multicast_wakeupcall(dev, buf); -+ } -+ -+ int system_bridge_addif(struct device *bridge, struct device *dev) -diff --git a/target/linux/generic/config-5.10 b/target/linux/generic/config-5.10 -index a348d4821ecef796c8c2b983a969b7becb7ae334..dad7baabe69f53ca9afeff8e9392397dd9dc35aa 100644 ---- a/target/linux/generic/config-5.10 -+++ b/target/linux/generic/config-5.10 -@@ -738,6 +738,7 @@ CONFIG_BRIDGE=y - # CONFIG_BRIDGE_EBT_T_NAT is not set - # CONFIG_BRIDGE_EBT_VLAN is not set - CONFIG_BRIDGE_IGMP_SNOOPING=y -+CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS=y - # CONFIG_BRIDGE_MRP is not set - # CONFIG_BRIDGE_NETFILTER is not set - # CONFIG_BRIDGE_NF_EBTABLES is not set -diff --git a/target/linux/generic/hack-5.10/601-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch b/target/linux/generic/hack-5.10/601-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch -new file mode 100644 -index 0000000000000000000000000000000000000000..17df35fe4a812b5b5118beb9239bf825be7b67ed ---- /dev/null -+++ b/target/linux/generic/hack-5.10/601-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch -@@ -0,0 +1,687 @@ -+From e0fd3ecf9b4f6825ad0380b759baf88ef508bed5 Mon Sep 17 00:00:00 2001 -+From: =?UTF-8?q?Linus=20L=C3=BCssing?= -+Date: Mon, 29 Jun 2020 19:04:05 +0200 -+Subject: [PATCH] bridge: Implement MLD Querier wake-up calls / Android bug -+ workaround -+MIME-Version: 1.0 -+Content-Type: text/plain; charset=UTF-8 -+Content-Transfer-Encoding: 8bit -+ -+Implement a configurable MLD Querier wake-up calls "feature" which -+works around a widely spread Android bug in connection with IGMP/MLD -+snooping. -+ -+Currently there are mobile devices (e.g. Android) which are not able -+to receive and respond to MLD Queries reliably because the Wifi driver -+filters a lot of ICMPv6 when the device is asleep - including -+MLD. This in turn breaks IPv6 communication when MLD Snooping is -+enabled. However there is one ICMPv6 type which is allowed to pass and -+which can be used to wake up the mobile device: ICMPv6 Echo Requests. -+ -+If this bridge is the selected MLD Querier then setting -+"multicast_wakeupcall" to a number n greater than 0 will send n -+ICMPv6 Echo Requests to each host behind this port to wake -+them up with each MLD Query. Upon receiving a matching ICMPv6 Echo -+Reply an MLD Query with a unicast ethernet destination will be sent -+to the specific host(s). -+ -+Link: https://issuetracker.google.com/issues/149630944 -+Link: https://github.com/freifunk-gluon/gluon/issues/1832 -+ -+Signed-off-by: Linus Lüssing -+--- -+ include/linux/if_bridge.h | 1 + -+ include/net/addrconf.h | 1 + -+ include/uapi/linux/if_link.h | 1 + -+ net/bridge/Kconfig | 26 ++++ -+ net/bridge/br_fdb.c | 10 ++ -+ net/bridge/br_input.c | 4 +- -+ net/bridge/br_multicast.c | 289 ++++++++++++++++++++++++++++++++++- -+ net/bridge/br_netlink.c | 19 +++ -+ net/bridge/br_private.h | 19 +++ -+ net/bridge/br_sysfs_if.c | 18 +++ -+ net/core/rtnetlink.c | 2 +- -+ net/ipv6/mcast_snoop.c | 3 +- -+ 12 files changed, 383 insertions(+), 10 deletions(-) -+ -+diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h -+index ea1c7d151f46..d7714f60b88a 100644 -+--- a/include/linux/if_bridge.h -++++ b/include/linux/if_bridge.h -+@@ -58,6 +58,7 @@ struct br_ip_list { -+ #define BR_MRP_LOST_IN_CONT BIT(19) -+ #define BR_BPDU_FILTER BIT(20) -+ #define BR_OFFLOAD BIT(21) -++#define BR_MULTICAST_WAKEUPCALL BIT(22) -+ -+ #define BR_DEFAULT_AGEING_TIME (300 * HZ) -+ -+diff --git a/include/net/addrconf.h b/include/net/addrconf.h -+index e7ce719838b5..af8f153e1b5f 100644 -+--- a/include/net/addrconf.h -++++ b/include/net/addrconf.h -+@@ -235,6 +235,7 @@ void ipv6_mc_unmap(struct inet6_dev *idev); -+ void ipv6_mc_remap(struct inet6_dev *idev); -+ void ipv6_mc_init_dev(struct inet6_dev *idev); -+ void ipv6_mc_destroy_dev(struct inet6_dev *idev); -++int ipv6_mc_check_icmpv6(struct sk_buff *skb); -+ int ipv6_mc_check_mld(struct sk_buff *skb); -+ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp); -+ -+diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h -+index ffeb05c061d2..1956ab99e97d 100644 -+--- a/include/uapi/linux/if_link.h -++++ b/include/uapi/linux/if_link.h -+@@ -525,6 +525,7 @@ enum { -+ IFLA_BRPORT_MRP_RING_OPEN, -+ IFLA_BRPORT_MRP_IN_OPEN, -+ IFLA_BRPORT_BPDU_FILTER, -++ IFLA_BRPORT_MCAST_WAKEUPCALL, -+ __IFLA_BRPORT_MAX -+ }; -+ #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) -+diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig -+index 80879196560c..056e80bf00c4 100644 -+--- a/net/bridge/Kconfig -++++ b/net/bridge/Kconfig -+@@ -48,6 +48,32 @@ config BRIDGE_IGMP_SNOOPING -+ -+ If unsure, say Y. -+ -++config BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ bool "MLD Querier wake-up calls" -++ depends on BRIDGE_IGMP_SNOOPING -++ depends on IPV6 -++ help -++ If you say Y here, then the MLD Snooping Querier will be built -++ with a per bridge port wake-up call "feature"/workaround. -++ -++ Currently there are mobile devices (e.g. Android) which are not able -++ to receive and respond to MLD Queries reliably because the Wifi driver -++ filters a lot of ICMPv6 when the device is asleep - including MLD. -++ This in turn breaks IPv6 communication when MLD Snooping is enabled. -++ However there is one ICMPv6 type which is allowed to pass and -++ which can be used to wake up the mobile device: ICMPv6 Echo Requests. -++ -++ If this bridge is the selected MLD Querier then setting -++ "multicast_wakeupcall" to a number n greater than 0 will send n -++ ICMPv6 Echo Requests to each host behind this port to wake them up -++ with each MLD Query. Upon receiving a matching ICMPv6 Echo Reply -++ an MLD Query with a unicast ethernet destination will be sent to the -++ specific host(s). -++ -++ Say N to exclude this support and reduce the binary size. -++ -++ If unsure, say N. -++ -+ config BRIDGE_VLAN_FILTERING -+ bool "VLAN filtering" -+ depends on BRIDGE -+diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c -+index bc6c8d5cd1e1..98ca86b7ca42 100644 -+--- a/net/bridge/br_fdb.c -++++ b/net/bridge/br_fdb.c -+@@ -85,6 +85,10 @@ static void fdb_rcu_free(struct rcu_head *head) -+ { -+ struct net_bridge_fdb_entry *ent -+ = container_of(head, struct net_bridge_fdb_entry, rcu); -++ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ del_timer_sync(&ent->wakeupcall_timer); -++#endif -+ kmem_cache_free(br_fdb_cache, ent); -+ } -+ -+@@ -516,6 +520,12 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br, -+ fdb->updated = fdb->used = jiffies; -+ INIT_HLIST_HEAD(&fdb->offload_in); -+ INIT_HLIST_HEAD(&fdb->offload_out); -++ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ timer_setup(&fdb->wakeupcall_timer, -++ br_multicast_send_wakeupcall, 0); -++#endif -++ -+ if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl, -+ &fdb->rhnode, -+ br_fdb_rht_params)) { -+diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c -+index 64f3eaa3879d..a449a1f60f1b 100644 -+--- a/net/bridge/br_input.c -++++ b/net/bridge/br_input.c -+@@ -163,8 +163,10 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb -+ if (dst) { -+ unsigned long now = jiffies; -+ -+- if (test_bit(BR_FDB_LOCAL, &dst->flags)) -++ if (test_bit(BR_FDB_LOCAL, &dst->flags)) { -++ br_multicast_wakeupcall_rcv(br, p, skb, vid); -+ return br_pass_frame_up(skb); -++ } -+ -+ if (now != dst->used) -+ dst->used = now; -+diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c -+index e5328a2777ec..a2f45bd523ab 100644 -+--- a/net/bridge/br_multicast.c -++++ b/net/bridge/br_multicast.c -+@@ -833,15 +833,16 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, -+ const struct in6_addr *group, -+ bool with_srcs, bool over_llqt, -+ u8 sflag, u8 *igmp_type, -+- bool *need_rexmit) -++ bool *need_rexmit, -++ bool delay) -+ { -+ struct net_bridge_port *p = pg ? pg->key.port : NULL; -+ struct net_bridge_group_src *ent; -+ size_t pkt_size, mld_hdr_size; -+ unsigned long now = jiffies; -++ unsigned long interval = 0; -+ struct mld2_query *mld2q; -+ void *csum_start = NULL; -+- unsigned long interval; -+ __sum16 *csum = NULL; -+ struct ipv6hdr *ip6h; -+ struct mld_msg *mldq; -+@@ -922,9 +923,13 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, -+ -+ /* ICMPv6 */ -+ skb_set_transport_header(skb, skb->len); -+- interval = ipv6_addr_any(group) ? -+- br->multicast_query_response_interval : -+- br->multicast_last_member_interval; -++ if (delay) { -++ interval = ipv6_addr_any(group) ? -++ br->multicast_query_response_interval : -++ br->multicast_last_member_interval; -++ interval = jiffies_to_msecs(interval); -++ } -++ -+ *igmp_type = ICMPV6_MGM_QUERY; -+ switch (br->multicast_mld_version) { -+ case 1: -+@@ -932,7 +937,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, -+ mldq->mld_type = ICMPV6_MGM_QUERY; -+ mldq->mld_code = 0; -+ mldq->mld_cksum = 0; -+- mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval)); -++ mldq->mld_maxdelay = htons((u16)interval); -+ mldq->mld_reserved = 0; -+ mldq->mld_mca = *group; -+ csum = &mldq->mld_cksum; -+@@ -1022,7 +1027,7 @@ static struct sk_buff *br_multicast_alloc_query(struct net_bridge *br, -+ &ip6_dst, &group->dst.ip6, -+ with_srcs, over_lmqt, -+ sflag, igmp_type, -+- need_rexmit); -++ need_rexmit, true); -+ } -+ #endif -+ } -+@@ -1427,6 +1432,168 @@ static void br_multicast_select_own_querier(struct net_bridge *br, -+ #endif -+ } -+ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ -++#define BR_MC_WAKEUP_ID htons(0xEC6B) /* random identifier */ -++#define BR_MC_ETH_ZERO { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } -++#define BR_MC_IN6_ZERO \ -++{ \ -++ .s6_addr32[0] = 0, .s6_addr32[1] = 0, \ -++ .s6_addr32[2] = 0, .s6_addr32[3] = 0, \ -++} -++ -++#define BR_MC_IN6_FE80 \ -++{ \ -++ .s6_addr32[0] = htonl(0xfe800000), \ -++ .s6_addr32[1] = 0, \ -++ .s6_addr32[2] = htonl(0x000000ff), \ -++ .s6_addr32[3] = htonl(0xfe000000), \ -++} -++ -++#define BR_MC_ECHO_LEN sizeof(pkt->echohdr) -++ -++static struct sk_buff *br_multicast_alloc_wakeupcall(struct net_bridge *br, -++ struct net_bridge_port *port, -++ u8 *eth_dst) -++{ -++ struct in6_addr ip6_src, ip6_dst = BR_MC_IN6_FE80; -++ struct sk_buff *skb; -++ __wsum csum_part; -++ __sum16 csum; -++ -++ struct wakeupcall_pkt { -++ struct ethhdr ethhdr; -++ struct ipv6hdr ip6hdr; -++ struct icmp6hdr echohdr; -++ } __packed; -++ -++ struct wakeupcall_pkt *pkt; -++ -++ static const struct wakeupcall_pkt __pkt_template = { -++ .ethhdr = { -++ .h_dest = BR_MC_ETH_ZERO, // update -++ .h_source = BR_MC_ETH_ZERO, // update -++ .h_proto = htons(ETH_P_IPV6), -++ }, -++ .ip6hdr = { -++ .priority = 0, -++ .version = 0x6, -++ .flow_lbl = { 0x00, 0x00, 0x00 }, -++ .payload_len = htons(BR_MC_ECHO_LEN), -++ .nexthdr = IPPROTO_ICMPV6, -++ .hop_limit = 1, -++ .saddr = BR_MC_IN6_ZERO, // update -++ .daddr = BR_MC_IN6_ZERO, // update -++ }, -++ .echohdr = { -++ .icmp6_type = ICMPV6_ECHO_REQUEST, -++ .icmp6_code = 0, -++ .icmp6_cksum = 0, // update -++ .icmp6_dataun.u_echo = { -++ .identifier = BR_MC_WAKEUP_ID, -++ .sequence = 0, -++ }, -++ }, -++ }; -++ -++ memcpy(&ip6_dst.s6_addr32[2], ð_dst[0], ETH_ALEN / 2); -++ memcpy(&ip6_dst.s6_addr[13], ð_dst[3], ETH_ALEN / 2); -++ ip6_dst.s6_addr[8] ^= 0x02; -++ if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6_dst, 0, -++ &ip6_src)) -++ return NULL; -++ -++ skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*pkt)); -++ if (!skb) -++ return NULL; -++ -++ skb->protocol = htons(ETH_P_IPV6); -++ skb->dev = port->dev; -++ -++ pkt = (struct wakeupcall_pkt *)skb->data; -++ *pkt = __pkt_template; -++ -++ ether_addr_copy(pkt->ethhdr.h_source, br->dev->dev_addr); -++ ether_addr_copy(pkt->ethhdr.h_dest, eth_dst); -++ -++ pkt->ip6hdr.saddr = ip6_src; -++ pkt->ip6hdr.daddr = ip6_dst; -++ -++ csum_part = csum_partial(&pkt->echohdr, sizeof(pkt->echohdr), 0); -++ csum = csum_ipv6_magic(&ip6_src, &ip6_dst, sizeof(pkt->echohdr), -++ IPPROTO_ICMPV6, csum_part); -++ pkt->echohdr.icmp6_cksum = csum; -++ -++ skb_reset_mac_header(skb); -++ skb_set_network_header(skb, offsetof(struct wakeupcall_pkt, ip6hdr)); -++ skb_set_transport_header(skb, offsetof(struct wakeupcall_pkt, echohdr)); -++ skb_put(skb, sizeof(*pkt)); -++ __skb_pull(skb, sizeof(pkt->ethhdr)); -++ -++ return skb; -++} -++ -++void br_multicast_send_wakeupcall(struct timer_list *t) -++{ -++ struct net_bridge_fdb_entry *fdb = from_timer(fdb, t, wakeupcall_timer); -++ struct net_bridge_port *port = fdb->dst; -++ struct net_bridge *br = port->br; -++ struct sk_buff *skb, *skb0; -++ int i; -++ -++ skb0 = br_multicast_alloc_wakeupcall(br, port, fdb->key.addr.addr); -++ if (!skb0) -++ return; -++ -++ for (i = port->wakeupcall_num_rings; i > 0; i--) { -++ if (i > 1) { -++ skb = skb_clone(skb0, GFP_ATOMIC); -++ if (!skb) { -++ kfree_skb(skb0); -++ break; -++ } -++ } else { -++ skb = skb0; -++ } -++ -++ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, -++ dev_net(port->dev), NULL, skb, NULL, skb->dev, -++ br_dev_queue_push_xmit); -++ } -++} -++ -++static void br_multicast_schedule_wakeupcalls(struct net_bridge *br, -++ struct net_bridge_port *port, -++ const struct in6_addr *group) -++{ -++ struct net_bridge_fdb_entry *fdb; -++ unsigned long delay; -++ -++ rcu_read_lock(); -++ hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) { -++ if (!fdb->dst || fdb->dst->dev != port->dev) -++ continue; -++ -++ /* Wake-up calls to VLANs unsupported for now */ -++ if (fdb->key.vlan_id) -++ continue; -++ -++ /* Spread the ICMPv6 Echo Requests to avoid congestion. -++ * We then won't use a max response delay for the queries later, -++ * as that would be redundant. Spread randomly by a little less -++ * than max response delay to anticipate the extra round trip. -++ */ -++ delay = ipv6_addr_any(group) ? -++ br->multicast_query_response_interval : -++ br->multicast_last_member_interval; -++ delay = prandom_u32() % (3 * delay / 4); -++ -++ timer_reduce(&fdb->wakeupcall_timer, jiffies + delay); -++ } -++ rcu_read_unlock(); -++} -++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */ -++ -+ static void __br_multicast_send_query(struct net_bridge *br, -+ struct net_bridge_port *port, -+ struct net_bridge_port_group *pg, -+@@ -1455,6 +1622,13 @@ static void __br_multicast_send_query(struct net_bridge *br, -+ dev_net(port->dev), NULL, skb, NULL, skb->dev, -+ br_dev_queue_push_xmit); -+ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ if (port->wakeupcall_num_rings && -++ group->proto == htons(ETH_P_IPV6)) -++ br_multicast_schedule_wakeupcalls(br, port, -++ &group->dst.ip6); -++#endif -++ -+ if (over_lmqt && with_srcs && sflag) { -+ over_lmqt = false; -+ goto again_under_lmqt; -+@@ -3164,6 +3338,98 @@ int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, -+ return ret; -+ } -+ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ -++static bool br_multicast_wakeupcall_check(struct net_bridge *br, -++ struct net_bridge_port *port, -++ struct sk_buff *skb, u16 vid) -++{ -++ struct ethhdr *eth = eth_hdr(skb); -++ const struct ipv6hdr *ip6h; -++ unsigned int offset, len; -++ struct icmp6hdr *icmp6h; -++ -++ /* Wake-up calls to VLANs unsupported for now */ -++ if (!port->wakeupcall_num_rings || vid || -++ eth->h_proto != htons(ETH_P_IPV6)) -++ return false; -++ -++ if (!ether_addr_equal(eth->h_dest, br->dev->dev_addr) || -++ is_multicast_ether_addr(eth->h_source) || -++ is_zero_ether_addr(eth->h_source)) -++ return false; -++ -++ offset = skb_network_offset(skb) + sizeof(*ip6h); -++ if (!pskb_may_pull(skb, offset)) -++ return false; -++ -++ ip6h = ipv6_hdr(skb); -++ -++ if (ip6h->version != 6) -++ return false; -++ -++ len = offset + ntohs(ip6h->payload_len); -++ if (skb->len < len || len <= offset) -++ return false; -++ -++ if (ip6h->nexthdr != IPPROTO_ICMPV6) -++ return false; -++ -++ skb_set_transport_header(skb, offset); -++ -++ if (ipv6_mc_check_icmpv6(skb) < 0) -++ return false; -++ -++ icmp6h = (struct icmp6hdr *)skb_transport_header(skb); -++ if (icmp6h->icmp6_type != ICMPV6_ECHO_REPLY || -++ icmp6h->icmp6_dataun.u_echo.identifier != BR_MC_WAKEUP_ID) -++ return false; -++ -++ return true; -++} -++ -++static void br_multicast_wakeupcall_send_mldq(struct net_bridge *br, -++ struct net_bridge_port *port, -++ const u8 *eth_dst) -++{ -++ const struct in6_addr group = BR_MC_IN6_ZERO; -++ struct in6_addr ip6_dst; -++ struct sk_buff *skb; -++ u8 igmp_type; -++ -++ /* we might have been triggered by multicast-address-specific query -++ * but reply with a general MLD query for now to keep things simple -++ */ -++ ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0, htonl(1)); -++ -++ skb = br_ip6_multicast_alloc_query(br, NULL, &ip6_dst, &group, false, -++ false, false, &igmp_type, NULL, -++ false); -++ if (!skb) -++ return; -++ -++ skb->dev = port->dev; -++ ether_addr_copy(eth_hdr(skb)->h_dest, eth_dst); -++ -++ br_multicast_count(br, port, skb, igmp_type, -++ BR_MCAST_DIR_TX); -++ NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, -++ dev_net(port->dev), NULL, skb, NULL, skb->dev, -++ br_dev_queue_push_xmit); -++} -++ -++void br_multicast_wakeupcall_rcv(struct net_bridge *br, -++ struct net_bridge_port *port, -++ struct sk_buff *skb, u16 vid) -++{ -++ if (!br_multicast_wakeupcall_check(br, port, skb, vid)) -++ return; -++ -++ br_multicast_wakeupcall_send_mldq(br, port, eth_hdr(skb)->h_source); -++} -++ -++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */ -++ -+ static void br_multicast_query_expired(struct net_bridge *br, -+ struct bridge_mcast_own_query *query, -+ struct bridge_mcast_querier *querier) -+@@ -3444,6 +3710,15 @@ int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val) -+ return err; -+ } -+ -++int br_multicast_set_wakeupcall(struct net_bridge_port *p, unsigned long val) -++{ -++ if (val > U8_MAX) -++ return -EINVAL; -++ -++ p->wakeupcall_num_rings = val; -++ return 0; -++} -++ -+ static void br_multicast_start_querier(struct net_bridge *br, -+ struct bridge_mcast_own_query *query) -+ { -+diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c -+index e8116a4438b9..b544889fb7fe 100644 -+--- a/net/bridge/br_netlink.c -++++ b/net/bridge/br_netlink.c -+@@ -151,6 +151,9 @@ static inline size_t br_port_info_size(void) -+ + nla_total_size_64bit(sizeof(u64)) /* IFLA_BRPORT_HOLD_TIMER */ -+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING -+ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MULTICAST_ROUTER */ -++#endif -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MCAST_WAKEUPCALL */ -+ #endif -+ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_GROUP_FWD_MASK */ -+ + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MRP_RING_OPEN */ -+@@ -243,6 +246,11 @@ static int br_port_fill_attrs(struct sk_buff *skb, -+ p->multicast_router)) -+ return -EMSGSIZE; -+ #endif -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ if (nla_put_u8(skb, IFLA_BRPORT_MCAST_WAKEUPCALL, -++ p->wakeupcall_num_rings)) -++ return -EMSGSIZE; -++#endif -+ -+ /* we might be called only with br->lock */ -+ rcu_read_lock(); -+@@ -723,6 +731,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { -+ [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 }, -+ [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 }, -+ [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 }, -++ [IFLA_BRPORT_MCAST_WAKEUPCALL] = { .type = NLA_U8 }, -+ [IFLA_BRPORT_MCAST_FLOOD] = { .type = NLA_U8 }, -+ [IFLA_BRPORT_BCAST_FLOOD] = { .type = NLA_U8 }, -+ [IFLA_BRPORT_VLAN_TUNNEL] = { .type = NLA_U8 }, -+@@ -872,6 +881,16 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) -+ } -+ #endif -+ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ if (tb[IFLA_BRPORT_MCAST_WAKEUPCALL]) { -++ u8 wakeupcall = nla_get_u8(tb[IFLA_BRPORT_MCAST_WAKEUPCALL]); -++ -++ err = br_multicast_set_wakeupcall(p, wakeupcall); -++ if (err) -++ return err; -++ } -++#endif -++ -+ if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) { -+ u16 fwd_mask = nla_get_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]); -+ -+diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h -+index 61b5b81b6828..6390d2fe88f6 100644 -+--- a/net/bridge/br_private.h -++++ b/net/bridge/br_private.h -+@@ -214,6 +214,10 @@ struct net_bridge_fdb_entry { -+ }; -+ struct rcu_head rcu; -+ }; -++ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ struct timer_list wakeupcall_timer; -++#endif -+ }; -+ -+ #define MDB_PG_FLAGS_PERMANENT BIT(0) -+@@ -333,6 +337,7 @@ struct net_bridge_port { -+ struct timer_list multicast_router_timer; -+ struct hlist_head mglist; -+ struct hlist_node rlist; -++ u8 wakeupcall_num_rings; -+ #endif -+ -+ #ifdef CONFIG_SYSFS -+@@ -1065,6 +1070,20 @@ static inline int br_multicast_igmp_type(const struct sk_buff *skb) -+ } -+ #endif -+ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++void br_multicast_wakeupcall_rcv(struct net_bridge *br, -++ struct net_bridge_port *port, -++ struct sk_buff *skb, u16 vid); -++void br_multicast_send_wakeupcall(struct timer_list *t); -++int br_multicast_set_wakeupcall(struct net_bridge_port *p, unsigned long val); -++#else -++static inline void br_multicast_wakeupcall_rcv(struct net_bridge *br, -++ struct net_bridge_port *port, -++ struct sk_buff *skb, u16 vid) -++{ -++} -++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */ -++ -+ /* br_vlan.c */ -+ #ifdef CONFIG_BRIDGE_VLAN_FILTERING -+ bool br_allowed_ingress(const struct net_bridge *br, -+diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c -+index 969eb6ca0776..88b74a5a7ccb 100644 -+--- a/net/bridge/br_sysfs_if.c -++++ b/net/bridge/br_sysfs_if.c -+@@ -254,6 +254,21 @@ BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE); -+ BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST); -+ #endif -+ -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++static ssize_t show_multicast_wakeupcall(struct net_bridge_port *p, char *buf) -++{ -++ return sprintf(buf, "%d\n", p->wakeupcall_num_rings); -++} -++ -++static int store_multicast_wakeupcall(struct net_bridge_port *p, -++ unsigned long v) -++{ -++ return br_multicast_set_wakeupcall(p, v); -++} -++static BRPORT_ATTR(multicast_wakeupcall, 0644, show_multicast_wakeupcall, -++ store_multicast_wakeupcall); -++#endif -++ -+ static const struct brport_attribute *brport_attrs[] = { -+ &brport_attr_path_cost, -+ &brport_attr_priority, -+@@ -279,6 +294,9 @@ static const struct brport_attribute *brport_attrs[] = { -+ &brport_attr_multicast_router, -+ &brport_attr_multicast_fast_leave, -+ &brport_attr_multicast_to_unicast, -++#endif -++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS -++ &brport_attr_multicast_wakeupcall, -+ #endif -+ &brport_attr_proxyarp, -+ &brport_attr_proxyarp_wifi, -+diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c -+index ccb62f7d1054..3b269aafd3b5 100644 -+--- a/net/core/rtnetlink.c -++++ b/net/core/rtnetlink.c -+@@ -55,7 +55,7 @@ -+ #include -+ -+ #define RTNL_MAX_TYPE 50 -+-#define RTNL_SLAVE_MAX_TYPE 37 -++#define RTNL_SLAVE_MAX_TYPE 38 -+ -+ struct rtnl_link { -+ rtnl_doit_func doit; -+diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c -+index 04d5fcdfa6e0..9a5061edbaf3 100644 -+--- a/net/ipv6/mcast_snoop.c -++++ b/net/ipv6/mcast_snoop.c -+@@ -131,7 +131,7 @@ static inline __sum16 ipv6_mc_validate_checksum(struct sk_buff *skb) -+ return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo); -+ } -+ -+-static int ipv6_mc_check_icmpv6(struct sk_buff *skb) -++int ipv6_mc_check_icmpv6(struct sk_buff *skb) -+ { -+ unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr); -+ unsigned int transport_len = ipv6_transport_len(skb); -+@@ -150,6 +150,7 @@ static int ipv6_mc_check_icmpv6(struct sk_buff *skb) -+ -+ return 0; -+ } -++EXPORT_SYMBOL(ipv6_mc_check_icmpv6); -+ -+ /** -+ * ipv6_mc_check_mld - checks whether this is a sane MLD packet -+-- -+2.36.1 -+ diff --git a/patches/openwrt/0008-hostapd-don-t-select-indoor-channel-on-outdoor-operation.patch b/patches/openwrt/0008-hostapd-don-t-select-indoor-channel-on-outdoor-operation.patch deleted file mode 100644 index 4d3909e21a..0000000000 --- a/patches/openwrt/0008-hostapd-don-t-select-indoor-channel-on-outdoor-operation.patch +++ /dev/null @@ -1,74 +0,0 @@ -From: David Bauer -Date: Sat, 10 Sep 2022 01:06:02 +0200 -Subject: hostapd: don't select indoor channel on outdoor operation - -Don't select channels designated for exclusive-indoor use when the -country3 element is set on outdoor operation. - -Signed-off-by: David Bauer -(cherry picked from commit 5110cf7ebdd5af57f98fe4581ce4c5ddb0a2bf86) - -diff --git a/package/network/services/hostapd/patches/800-acs-don-t-select-indoor-channel-on-outdoor-operation.patch b/package/network/services/hostapd/patches/800-acs-don-t-select-indoor-channel-on-outdoor-operation.patch -new file mode 100644 -index 0000000000000000000000000000000000000000..96ebdefae9e710a350fa44a359896071b142af67 ---- /dev/null -+++ b/package/network/services/hostapd/patches/800-acs-don-t-select-indoor-channel-on-outdoor-operation.patch -@@ -0,0 +1,58 @@ -+From 37528a5205cb0b9e2238b7d97fb2ff5457448f1c Mon Sep 17 00:00:00 2001 -+From: David Bauer -+Date: Thu, 8 Sep 2022 01:45:41 +0200 -+Subject: [PATCH] acs: don't select indoor channel on outdoor operation -+ -+Don't select channels designated for exclusive-indoor use when the -+country3 element is set on outdoor operation. -+ -+Signed-off-by: David Bauer -+--- -+ src/ap/acs.c | 9 +++++++++ -+ src/ap/dfs.c | 3 +++ -+ 2 files changed, 12 insertions(+) -+ -+--- a/src/ap/acs.c -++++ b/src/ap/acs.c -+@@ -552,6 +552,9 @@ static void acs_survey_mode_interference -+ if (chan->max_tx_power < iface->conf->min_tx_power) -+ continue; -+ -++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) -++ continue; -++ -+ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)", -+ chan->chan, chan->freq); -+ -+@@ -686,6 +689,9 @@ acs_find_ideal_chan_mode(struct hostapd_ -+ if (chan->max_tx_power < iface->conf->min_tx_power) -+ continue; -+ -++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) -++ continue; -++ -+ if (!chan_bw_allowed(chan, bw, 1, 1)) { -+ wpa_printf(MSG_DEBUG, -+ "ACS: Channel %d: BW %u is not supported", -+@@ -1065,6 +1071,9 @@ static int * acs_request_scan_add_freqs( -+ if (chan->max_tx_power < iface->conf->min_tx_power) -+ continue; -+ -++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) -++ continue; -++ -+ *freq++ = chan->freq; -+ } -+ -+--- a/src/ap/dfs.c -++++ b/src/ap/dfs.c -+@@ -282,6 +282,9 @@ static int dfs_find_channel(struct hosta -+ if (chan->max_tx_power < iface->conf->min_tx_power) -+ continue; -+ -++ if (chan->flag & HOSTAPD_CHAN_INDOOR_ONLY && iface->conf->country[2] == 0x4f) -++ continue; -++ -+ if (ret_chan && idx == channel_idx) { -+ wpa_printf(MSG_DEBUG, "Selected channel %d (%d)", -+ chan->freq, chan->chan); diff --git a/patches/openwrt/0009-extrem-led.patch b/patches/openwrt/0009-extrem-led.patch new file mode 100644 index 0000000000..6cdfa93944 --- /dev/null +++ b/patches/openwrt/0009-extrem-led.patch @@ -0,0 +1,19 @@ +From: Maciej Krüger +Date: Sat, 1 Oct 2022 22:20:18 +0200 +Subject: extrem led + +diff --git a/target/linux/ath79/generic/base-files/etc/board.d/01_leds b/target/linux/ath79/generic/base-files/etc/board.d/01_leds +index 12a54f3bd5733ea5591b90fc46f76f805c0cfcef..8387d54929ccc374426da923e3603b13fc3df5d3 100644 +--- a/target/linux/ath79/generic/base-files/etc/board.d/01_leds ++++ b/target/linux/ath79/generic/base-files/etc/board.d/01_leds +@@ -229,6 +229,10 @@ etactica,eg200) + ucidef_set_led_netdev "lan" "LAN" "red:eth0" "eth0" + ucidef_set_led_oneshot "modbus" "Modbus" "red:modbus" "100" "33" + ;; ++extreme-networks,ws-ap3805i) ++ ucidef_set_led_netdev "wlan2" "WLAN2" "green:wlan2" "mesh1" ++ ucidef_set_led_netdev "wlan5" "WLAN5" "green:wlan5" "mesh0" ++ ;; + glinet,gl-mifi|\ + qxwlan,e600g-v2-8m|\ + qxwlan,e600g-v2-16m|\ diff --git a/patches/openwrt/0010-kconfig-abort-configuration-on-unset-symbol.patch b/patches/openwrt/0010-kconfig-abort-configuration-on-unset-symbol.patch deleted file mode 100644 index 5116508c63..0000000000 --- a/patches/openwrt/0010-kconfig-abort-configuration-on-unset-symbol.patch +++ /dev/null @@ -1,75 +0,0 @@ -From: David Bauer -Date: Fri, 11 Nov 2022 13:37:07 +0100 -Subject: kconfig: abort configuration on unset symbol - -This fixes the initial patch to cover all cases where unset symbols are -handled in the code. - -Fixes commit eaa9c94c7574 ("generic: Kconfig: exit on unset symbol") - -Signed-off-by: David Bauer - -diff --git a/target/linux/generic/hack-5.10/205-kconfig-abort-configuration-on-unset-symbol.patch b/target/linux/generic/hack-5.10/205-kconfig-abort-configuration-on-unset-symbol.patch -new file mode 100644 -index 0000000000000000000000000000000000000000..bd5c54d4b8b73b36c9f5b809447a8e76fce19e5d ---- /dev/null -+++ b/target/linux/generic/hack-5.10/205-kconfig-abort-configuration-on-unset-symbol.patch -@@ -0,0 +1,41 @@ -+From 310e8e04a05d9eb43fa9dd7f00143300afcaa37a Mon Sep 17 00:00:00 2001 -+From: David Bauer -+Date: Fri, 11 Nov 2022 13:33:44 +0100 -+Subject: [PATCH] kconfig: abort configuration on unset symbol -+ -+When a target configuration has unset Kconfig symbols, the build will -+fail when OpenWrt is compiled with V=s and stdin is connected to a tty. -+ -+In case OpenWrt is compiled without either of these preconditions, the -+build will succeed with the symbols in question being unset. -+ -+Modify the kernel configuration in a way it fails on unset symbols -+regardless of the aforementioned preconditions. -+ -+Signed-off-by: David Bauer -+--- -+ scripts/kconfig/conf.c | 6 ++++++ -+ 1 file changed, 6 insertions(+) -+ -+--- a/scripts/kconfig/conf.c -++++ b/scripts/kconfig/conf.c -+@@ -109,6 +109,9 @@ static int conf_askvalue(struct symbol * -+ } -+ /* fall through */ -+ case oldaskconfig: -++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { -++ exit(1); -++ } -+ fflush(stdout); -+ xfgets(line, sizeof(line), stdin); -+ return 1; -+@@ -303,6 +306,9 @@ static int conf_choice(struct menu *menu -+ } -+ /* fall through */ -+ case oldaskconfig: -++ if (!tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) { -++ exit(1); -++ } -+ fflush(stdout); -+ xfgets(line, sizeof(line), stdin); -+ strip(line); -diff --git a/target/linux/generic/hack-5.10/205-kconfig-exit.patch b/target/linux/generic/hack-5.10/205-kconfig-exit.patch -deleted file mode 100644 -index c3fb7a1f999e56da34b0a15565ce36098cfba7fb..0000000000000000000000000000000000000000 ---- a/target/linux/generic/hack-5.10/205-kconfig-exit.patch -+++ /dev/null -@@ -1,11 +0,0 @@ ----- a/scripts/kconfig/conf.c --+++ b/scripts/kconfig/conf.c --@@ -215,6 +215,8 @@ static int conf_sym(struct menu *menu) -- break; -- continue; -- case 0: --+ if (!sym_has_value(sym) && !tty_stdio && getenv("FAIL_ON_UNCONFIGURED")) --+ exit(1); -- newval = oldval; -- break; -- case '?': diff --git a/patches/openwrt/0011-ubus-patch-for-lua-async.patch b/patches/openwrt/0011-ubus-patch-for-lua-async.patch new file mode 100644 index 0000000000..f409872d4f --- /dev/null +++ b/patches/openwrt/0011-ubus-patch-for-lua-async.patch @@ -0,0 +1,258 @@ +From be122e1c9ffa4a1bca7ac3ee124f79f68fd70879 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= +Date: Sat, 21 Jan 2023 20:37:55 +0100 +Subject: [PATCH] ubus patch for lua async + +--- + .../0001-Add-support-for-async-calls.patch | 239 ++++++++++++++++++ + 1 file changed, 239 insertions(+) + create mode 100644 package/system/ubus/patches/0001-Add-support-for-async-calls.patch + +diff --git a/package/system/ubus/patches/0001-Add-support-for-async-calls.patch b/package/system/ubus/patches/0001-Add-support-for-async-calls.patch +new file mode 100644 +index 0000000000..eb7d1469b2 +--- /dev/null ++++ b/package/system/ubus/patches/0001-Add-support-for-async-calls.patch +@@ -0,0 +1,239 @@ ++From ea4da76fef7d7d2c6249f53594b950c9b1dfacd2 Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= ++Date: Sat, 21 Jan 2023 18:04:27 +0100 ++Subject: [PATCH] Add support for async calls ++ ++--- ++ lua/ubus.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++- ++ 1 file changed, 166 insertions(+), 2 deletions(-) ++ ++diff --git a/lua/ubus.c b/lua/ubus.c ++index 07b816d..51643df 100644 ++--- a/lua/ubus.c +++++ b/lua/ubus.c ++@@ -19,6 +19,7 @@ ++ #include ++ #include ++ #include +++#include ++ ++ #define MODNAME "ubus" ++ #define METANAME MODNAME ".meta" ++@@ -42,6 +43,12 @@ struct ubus_lua_event { ++ int r; ++ }; ++ +++struct ubus_lua_request { +++ struct ubus_request r; +++ struct ustream_fd fd; +++ int fnc; +++}; +++ ++ struct ubus_lua_subscriber { ++ struct ubus_subscriber s; ++ int rnotify; ++@@ -660,6 +667,134 @@ ubus_lua_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) ++ ubus_lua_parse_blob_array(L, blob_data(msg), blob_len(msg), true); ++ } ++ +++static void +++ubus_lua_async_complete_cb(struct ubus_request *req, int ret) +++{ +++ struct ubus_lua_request *lureq = container_of(req, struct ubus_lua_request, r); +++ +++ lua_getglobal(state, "__ubus_cb_async"); +++ lua_rawgeti(state, -1, lureq->fnc); +++ lua_remove(state, -2); +++ +++ if (lua_isfunction(state, -1)) { +++ lua_pushnil(state); +++ +++ lua_newtable(state); +++ +++ lua_pushstring(state, "type"); +++ lua_pushstring(state, "connected"); +++ lua_settable(state, -3); +++ +++ lua_pushstring(state, "return"); +++ lua_pushnumber(state, ret); +++ lua_settable(state, -3); +++ +++ lua_call(state, 2, 0); +++ } else { +++ lua_pop(state, 1); +++ } +++} +++ +++static void +++ubus_lua_async_cb(struct ustream *s, struct blob_attr *msg) +++{ +++ struct ubus_lua_request *lureq = container_of(s, struct ubus_lua_request, fd.stream); +++ +++ lua_getglobal(state, "__ubus_cb_async"); +++ lua_rawgeti(state, -1, lureq->fnc); +++ lua_remove(state, -2); +++ +++ if (lua_isfunction(state, -1)) { +++ if( msg ){ +++ ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); +++ } else { +++ lua_pushnil(state); +++ } +++ lua_call(state, 1, 0); +++ } else { +++ lua_pop(state, 1); +++ } +++} +++ +++static void +++ubus_lua_async_data_cb(struct ustream *s, int bytes) +++{ +++ while (true) { +++ struct blob_attr *a; +++ int len, cur_len; +++ +++ a = (void*) ustream_get_read_buf(s, &len); +++ if (len < (int)sizeof(*a)) +++ break; +++ +++ cur_len = blob_len(a) + sizeof(*a); +++ if (len < cur_len) +++ break; +++ +++ ubus_lua_async_cb(s, a); +++ ustream_consume(s, cur_len); +++ } +++} +++ +++static void +++ubus_lua_async_state_cb(struct ustream *s) +++{ +++ struct ubus_lua_request *lureq = container_of(s, struct ubus_lua_request, fd.stream); +++ +++ lua_getglobal(state, "__ubus_cb_async"); +++ lua_rawgeti(state, -1, lureq->fnc); +++ lua_remove(state, -2); +++ +++ if (lua_isfunction(state, -1)) { +++ lua_pushnil(state); +++ +++ lua_newtable(state); +++ +++ lua_pushstring(state, "type"); +++ lua_pushstring(state, "closed"); +++ lua_settable(state, -3); +++ +++ lua_call(state, 2, 0); +++ } else { +++ lua_pop(state, 1); +++ } +++} +++ +++static void +++ubus_lua_async_fd_cb(struct ubus_request *req, int fd) +++{ +++ struct ubus_lua_request *lureq = container_of(req, struct ubus_lua_request, r); +++ +++ lureq->fd.stream.notify_read = ubus_lua_async_data_cb; +++ lureq->fd.stream.notify_state = ubus_lua_async_state_cb; +++ ustream_fd_init(&lureq->fd, fd); +++} +++ +++static int +++ubus_lua_register_async( struct ubus_lua_request ** retlureq, struct ubus_context *ctx, lua_State *L, +++ int fnc ) +++{ +++ struct ubus_lua_request *lureq; +++ +++ lureq = calloc( 1, sizeof( struct ubus_lua_request ) ); +++ if( !lureq ){ +++ lua_pushstring( L, "Out of memory" ); +++ return lua_error(L); +++ } +++ +++ lua_getglobal(L, "__ubus_cb_async"); +++ lua_pushvalue(L, fnc); +++ lureq->fnc = luaL_ref(L, -2); +++ lua_pop(L, 1); +++ +++ // remove the fnc +++ lua_pop(L, 1); +++ +++ *retlureq = lureq; +++ +++ return 0; +++} +++ ++ static int ++ ubus_lua_call(lua_State *L) ++ { ++@@ -669,6 +804,20 @@ ubus_lua_call(lua_State *L) ++ const char *path = luaL_checkstring(L, 2); ++ const char *func = luaL_checkstring(L, 3); ++ +++ bool isAsync = lua_isfunction(L, 5); +++ struct ubus_lua_request * req = NULL; +++ +++ if (isAsync) { +++ int ret = ubus_lua_register_async(&req, c->ctx, L, lua_gettop(L)); +++ if (ret) { +++ return ret; +++ } +++ if (!req) { +++ lua_pushstring(L, "Failed to register async callback"); +++ return lua_error( L ); +++ } +++ } +++ ++ luaL_checktype(L, 4, LUA_TTABLE); ++ blob_buf_init(&c->buf, 0); ++ ++@@ -689,7 +838,14 @@ ubus_lua_call(lua_State *L) ++ } ++ ++ top = lua_gettop(L); ++- rv = ubus_invoke(c->ctx, id, func, c->buf.head, ubus_lua_call_cb, L, c->timeout * 1000); +++ +++ if (isAsync) { +++ rv = ubus_invoke_async(c->ctx, id, func, c->buf.head, &req->r); +++ req->r.fd_cb = ubus_lua_async_fd_cb; +++ req->r.complete_cb = ubus_lua_async_complete_cb; +++ } else { +++ rv = ubus_invoke(c->ctx, id, func, c->buf.head, ubus_lua_call_cb, L, c->timeout * 1000); +++ } ++ ++ if (rv != UBUS_STATUS_OK) ++ { ++@@ -699,6 +855,10 @@ ubus_lua_call(lua_State *L) ++ return 2; ++ } ++ +++ if (isAsync) { +++ ubus_complete_request_async(c->ctx, &req->r); +++ } +++ ++ return lua_gettop(L) - top; ++ } ++ ++@@ -731,7 +891,7 @@ ubus_lua_load_event(lua_State *L) ++ ++ event->e.cb = ubus_event_handler; ++ ++- /* update the he callback lookup table */ +++ /* update the callback lookup table */ ++ lua_getglobal(L, "__ubus_cb_event"); ++ lua_pushvalue(L, -2); ++ event->r = luaL_ref(L, -2); ++@@ -1021,5 +1181,9 @@ luaopen_ubus(lua_State *L) ++ /* create the publisher table - notifications of new subs */ ++ lua_createtable(L, 1, 0); ++ lua_setglobal(L, "__ubus_cb_publisher"); +++ +++ /* create the async table - callbacks for invoke_async */ +++ lua_createtable(L, 1, 0); +++ lua_setglobal(L, "__ubus_cb_async"); ++ return 0; ++ } ++-- ++2.38.1 ++ +-- +2.38.1 + diff --git a/patches/packages/routing/0004-oonf-olsrd2-add-support-to-check-if-service-is-running.patch b/patches/packages/routing/0004-oonf-olsrd2-add-support-to-check-if-service-is-running.patch index 4b8c9aad76..7a418927c8 100644 --- a/patches/packages/routing/0004-oonf-olsrd2-add-support-to-check-if-service-is-running.patch +++ b/patches/packages/routing/0004-oonf-olsrd2-add-support-to-check-if-service-is-running.patch @@ -1,20 +1,51 @@ -From: Maciej Krüger +From 0c4b0ec2880a9b837ffea6c10dfdd505c21f154a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= Date: Sun, 10 Apr 2022 01:58:41 +0200 -Subject: oonf-olsrd2: add support to check if service is running +Subject: [PATCH] oonf-olsrd2: add support to check if service is running +--- + oonf-init-scripts/files/oonf_init.sh | 17 +++++++++++++++++ + oonf-olsrd2/files/olsrd2.init | 4 ++++ + 2 files changed, 21 insertions(+) + +diff --git a/oonf-init-scripts/files/oonf_init.sh b/oonf-init-scripts/files/oonf_init.sh +index 8ab5b3b..a945c4e 100755 +--- a/oonf-init-scripts/files/oonf_init.sh ++++ b/oonf-init-scripts/files/oonf_init.sh +@@ -118,3 +118,20 @@ reload() + oonf_add_devices_to_configuration + oonf_reread_config + } ++ ++running() ++{ ++ # check if we have a pidfile and then check if that pid still exists. ++ # since we don't use -e this has to be explicitly returned. exit would stop the process. ++ test -e "/tmp/run/olsrd2.pid" && test -e "/proc/$(cat "/tmp/run/olsrd2.pid")" && return 0 ++ return 1 ++} ++ ++status() ++{ ++ if running; then ++ echo "running" ++ else ++ echo "stopped" ++ fi ++} diff --git a/oonf-olsrd2/files/olsrd2.init b/oonf-olsrd2/files/olsrd2.init -index debae9883258b821a5ea0aecebe879ddc84e29eb..b6c1e9a5522788005db850ceaf6699aa1eee6877 100755 +index 04b30ff..9f5595b 100755 --- a/oonf-olsrd2/files/olsrd2.init +++ b/oonf-olsrd2/files/olsrd2.init -@@ -3,4 +3,11 @@ - START=82 - DAEMON='olsrd2' +@@ -5,4 +5,8 @@ DAEMON='olsrd2' -+running() { -+ test -e "/tmp/run/olsrd2.pid" && test -e "/proc/$(cat "/tmp/run/olsrd2.pid")" && return 0 -+ return 1 -+} + [ -n "$IPKG_INSTROOT" ] || { + . /lib/functions/oonf_init.sh + -+extra_command "running" "Check if service is running" ++ extra_command "running" "Check if service is running" ++ extra_command "status" "Service status" + } + - . /lib/functions/oonf_init.sh +-- +2.40.1 + diff --git a/patches/packages/routing/0006-olsrd-add-l3roamd-patch.patch b/patches/packages/routing/0006-olsrd-add-l3roamd-patch.patch new file mode 100644 index 0000000000..6c88718e79 --- /dev/null +++ b/patches/packages/routing/0006-olsrd-add-l3roamd-patch.patch @@ -0,0 +1,446 @@ +From: Maciej Krüger +Date: Tue, 3 Jan 2023 01:20:58 +0100 +Subject: olsrd: add l3roamd patch + +diff --git a/olsrd/patches/999-l3roamd.patch b/olsrd/patches/999-l3roamd.patch +new file mode 100644 +index 0000000000000000000000000000000000000000..2a2de7e93e68ba588c9a2264f2d7157110e2b7dc +--- /dev/null ++++ b/olsrd/patches/999-l3roamd.patch +@@ -0,0 +1,436 @@ ++From 40fcf67808b44eb80f11cce7933b769a213c872b Mon Sep 17 00:00:00 2001 ++From: =?UTF-8?q?Maciej=20Kr=C3=BCger?= ++Date: Sun, 15 Jan 2023 02:03:19 +0100 ++Subject: [PATCH] l3roamd ++ ++--- ++ src/l3roamd.c | 263 +++++++++++++++++++++++++++++++++++ ++ src/l3roamd.h | 13 ++ ++ src/linux/kernel_routes_nl.c | 36 ++++- ++ src/main.c | 8 +- ++ src/olsr_cfg.h | 2 + ++ 5 files changed, 315 insertions(+), 7 deletions(-) ++ create mode 100644 src/l3roamd.c ++ create mode 100644 src/l3roamd.h ++ ++diff --git a/src/l3roamd.c b/src/l3roamd.c ++new file mode 100644 ++index 00000000..a68db739 ++--- /dev/null +++++ b/src/l3roamd.c ++@@ -0,0 +1,263 @@ +++#include "olsr.h" +++#include "olsr_types.h" +++#include "l3roamd.h" +++ +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++// adapted from https://olegkutkov.me/2019/03/24/getting-linux-routing-table-using-netlink/ +++ +++// ref https://gist.github.com/cl4u2/5204374 +++ +++int rtnl_receive(int fd, struct msghdr *msg, int flags) +++{ +++ int len; +++ +++ do { +++ len = recvmsg(fd, msg, flags); +++ } while (len < 0 && (errno == EINTR || errno == EAGAIN)); +++ +++ if (len < 0) { +++ perror("Netlink receive failed"); +++ return -errno; +++ } +++ +++ if (len == 0) { +++ perror("EOF on netlink"); +++ return -ENODATA; +++ } +++ +++ return len; +++} +++ +++static int rtnl_recvmsg(int fd, struct msghdr *msg, char **answer) +++{ +++ struct iovec *iov = msg->msg_iov; +++ char *buf; +++ int len; +++ +++ iov->iov_base = NULL; +++ iov->iov_len = 0; +++ +++ len = rtnl_receive(fd, msg, MSG_PEEK | MSG_TRUNC); +++ +++ if (len < 0) { +++ return len; +++ } +++ +++ buf = malloc(len); +++ +++ if (!buf) { +++ perror("malloc failed"); +++ return -ENOMEM; +++ } +++ +++ iov->iov_base = buf; +++ iov->iov_len = len; +++ +++ len = rtnl_receive(fd, msg, 0); +++ +++ if (len < 0) { +++ free(buf); +++ return len; +++ } +++ +++ *answer = buf; +++ +++ return len; +++} +++ +++void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +++{ +++ memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); +++ +++ while (RTA_OK(rta, len)) { +++ if (rta->rta_type <= max) { +++ tb[rta->rta_type] = rta; +++ } +++ +++ rta = RTA_NEXT(rta,len); +++ } +++} +++ +++static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb) +++{ +++ __u32 table = r->rtm_table; +++ +++ if (tb[RTA_TABLE]) { +++ table = *(__u32 *)RTA_DATA(tb[RTA_TABLE]); +++ } +++ +++ return table; +++} +++ +++#define RTPROTO_L3ROAMD 158 +++ +++void +++l3_ip_prefix_list_add(struct ip_prefix_list **list, const union olsr_ip_addr *net, uint8_t prefix_len) { +++ OLSR_PRINTF(0, "l3roamd HNA: Add %s/%u\n", inet_ntoa(net->v4), prefix_len); +++ return ip_prefix_list_add(list, net, prefix_len); +++} +++ +++int +++l3_ip_prefix_list_remove(struct ip_prefix_list **list, const union olsr_ip_addr *net, uint8_t prefix_len) { +++ int ret = ip_prefix_list_remove(list, net, prefix_len); +++ OLSR_PRINTF(0, "l3roamd HNA: Remove %s/%u - OK %i\n", inet_ntoa(net->v4), prefix_len, ret); +++ return ret; +++} +++ +++int convert_route (struct nlmsghdr* nl_header_answer, union olsr_ip_addr * prefix, uint8_t * prefix_len) { +++ struct rtmsg* r = NLMSG_DATA(nl_header_answer); +++ int len = nl_header_answer->nlmsg_len; +++ struct rtattr* tb[RTA_MAX+1]; +++ int table; +++ +++ len -= NLMSG_LENGTH(sizeof(*r)); +++ +++ if (len < 0) { +++ perror("Wrong message length"); +++ return -1; +++ } +++ +++ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); +++ +++ table = rtm_get_table(r, tb); +++ +++ if (r->rtm_family != AF_INET || table != RT_TABLE_MAIN || r->rtm_protocol != RTPROTO_L3ROAMD || !tb[RTA_DST] || r->rtm_dst_len != 32) { +++ return 1; +++ } +++ +++ memcpy(&prefix->v4, RTA_DATA(tb[RTA_DST]), sizeof(struct in_addr)); +++ +++ *prefix_len = r->rtm_dst_len; +++ +++ return 0; +++} +++ +++int do_route_dump_requst(int sock) +++{ +++ struct { +++ struct nlmsghdr nlh; +++ struct rtmsg rtm; +++ } nl_request; +++ +++ memset(&nl_request, 0, sizeof(nl_request)); +++ +++ nl_request.nlh.nlmsg_type = RTM_GETROUTE; +++ nl_request.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; +++ nl_request.nlh.nlmsg_len = sizeof(nl_request); +++ nl_request.nlh.nlmsg_seq = time(NULL); +++ nl_request.rtm.rtm_family = AF_INET; +++ +++ // set l3 specifics +++ +++ nl_request.rtm.rtm_dst_len = 32; +++ nl_request.rtm.rtm_protocol = RTPROTO_L3ROAMD; +++ +++ return send(sock, &nl_request, sizeof(nl_request), 0); +++} +++ +++int get_route_dump_response(int sock, struct ip_prefix_list ** out) +++{ +++ struct sockaddr_nl nladdr; +++ struct iovec iov; +++ struct msghdr msg = { +++ .msg_name = &nladdr, +++ .msg_namelen = sizeof(nladdr), +++ .msg_iov = &iov, +++ .msg_iovlen = 1, +++ }; +++ +++ char *buf; +++ +++ int status = rtnl_recvmsg(sock, &msg, &buf); +++ +++ struct nlmsghdr *h = (struct nlmsghdr *)buf; +++ int msglen = status; +++ +++ union olsr_ip_addr prefix; +++ memset(&prefix, 0, sizeof(union olsr_ip_addr)); +++ uint8_t prefix_len; +++ +++ while (NLMSG_OK(h, msglen)) { +++ if (h->nlmsg_flags & NLM_F_DUMP_INTR) { +++ fprintf(stderr, "Dump was interrupted\n"); +++ free(buf); +++ return -1; +++ } +++ +++ if (nladdr.nl_pid != 0) { +++ continue; +++ } +++ +++ if (h->nlmsg_type == NLMSG_ERROR) { +++ perror("netlink reported error"); +++ goto cleanup; +++ } +++ +++ int route_status = convert_route(h, &prefix, &prefix_len); +++ +++ if (route_status < 0) { +++ status = -1; +++ goto cleanup; +++ } +++ +++ if (!route_status) { +++ l3_ip_prefix_list_add(out, &prefix, prefix_len); +++ } +++ +++ h = NLMSG_NEXT(h, msglen); +++ } +++ +++ free(buf); +++ +++ return 0; +++cleanup: +++ free(buf); +++ ip_prefix_list_clear(out); +++ +++ return status; +++} +++ +++void process_l3roamd_nlh(struct nlmsghdr * nlh, bool isDelete) { +++ union olsr_ip_addr prefix; +++ memset(&prefix, 0, sizeof(union olsr_ip_addr)); +++ uint8_t prefix_len; +++ +++ int route_status = convert_route(nlh, &prefix, &prefix_len); +++ +++ if (route_status < 0) { +++ OLSR_PRINTF(1, "l3roamd HNA: Failed to convert route: %i", route_status); +++ return; +++ } +++ +++ if (route_status) { // we should ignore it +++ return; +++ } +++ +++ if (isDelete) { +++ if (!l3_ip_prefix_list_remove(&olsr_cnf->hna_entries, &prefix, prefix_len)) { +++ olsr_exit("l3roamd HNA: remove failed to delete route, this should not happen", 1); +++ } +++ } else { +++ l3_ip_prefix_list_add(&olsr_cnf->hna_entries, &prefix, prefix_len); +++ } +++} +++ +++void l3roamd_hna_init() { +++ if (do_route_dump_requst(olsr_cnf->rtnl_s) < 0) { +++ perror("Failed to perfom request"); +++ olsr_exit("l3roamd HNA: failed to send dump request", 1); +++ } +++ +++ if (get_route_dump_response(olsr_cnf->rtnl_s, &olsr_cnf->hna_entries)) { +++ olsr_exit("l3roamd HNA: failed to dump netlink response", 1); +++ } +++} ++diff --git a/src/l3roamd.h b/src/l3roamd.h ++new file mode 100644 ++index 00000000..3e536adf ++--- /dev/null +++++ b/src/l3roamd.h ++@@ -0,0 +1,13 @@ +++// +++// Created by maciej on 02.01.23. +++// +++ +++#include +++ +++#ifndef OLSRD_L3ROAMD_H +++#define OLSRD_L3ROAMD_H +++ +++void process_l3roamd_nlh(struct nlmsghdr * nlh, bool isDelete); +++void l3roamd_hna_init(); +++ +++#endif //OLSRD_L3ROAMD_H ++diff --git a/src/linux/kernel_routes_nl.c b/src/linux/kernel_routes_nl.c ++index 1a2810da..49513e19 100644 ++--- a/src/linux/kernel_routes_nl.c +++++ b/src/linux/kernel_routes_nl.c ++@@ -50,6 +50,8 @@ ++ #include "log.h" ++ #include "net_os.h" ++ #include "ifnet.h" +++#include "l3roamd.h" +++#include "olsr.h" ++ ++ #include ++ #include ++@@ -166,14 +168,31 @@ static void rtnetlink_read(int sock, void *data __attribute__ ((unused)), unsign ++ 0 ++ }; ++ ++- char buffer[4096]; +++ int bufsize = 4096; +++ +++ char * buffer = olsr_malloc(bufsize, "netlink receive buffer"); +++ iov.iov_base = (void *) buffer; +++ iov.iov_len = bufsize; ++ struct nlmsghdr *nlh = (struct nlmsghdr *)ARM_NOWARN_ALIGN(buffer); +++ ++ int ret; ++ ++- iov.iov_base = (void *) buffer; ++- iov.iov_len = sizeof(buffer); +++ while ((ret = recvmsg(sock, &msg, MSG_DONTWAIT | MSG_TRUNC | MSG_PEEK)) >= 0) { +++ if (ret > bufsize) { +++ free(buffer); +++ buffer = olsr_malloc(ret, "netlink receive buffer expansion"); +++ bufsize = ret; +++ +++ iov.iov_base = (void *) buffer; +++ iov.iov_len = bufsize; +++ nlh = (struct nlmsghdr *)ARM_NOWARN_ALIGN(buffer); +++ } +++ +++ ret = recvmsg(sock, &msg, MSG_DONTWAIT); +++ if (ret < 0) { +++ goto end; +++ } ++ ++- while ((ret = recvmsg(sock, &msg, MSG_DONTWAIT)) >= 0) { ++ /*check message*/ ++ len = nlh->nlmsg_len; ++ plen = len - sizeof(nlh); ++@@ -181,7 +200,7 @@ static void rtnetlink_read(int sock, void *data __attribute__ ((unused)), unsign ++ OLSR_PRINTF(1,"Malformed netlink message: " ++ "len=%d left=%d plen=%d\n", ++ len, ret, plen); ++- return; +++ goto end; ++ } ++ ++ OLSR_PRINTF(3, "Netlink message received: type 0x%x\n", nlh->nlmsg_type); ++@@ -189,11 +208,18 @@ static void rtnetlink_read(int sock, void *data __attribute__ ((unused)), unsign ++ /* handle ifup/ifdown */ ++ netlink_process_link(nlh); ++ } +++ +++ if ((nlh->nlmsg_type == RTM_DELROUTE) || (nlh->nlmsg_type == RTM_NEWROUTE)) { +++ /* handle l3roamd route changes */ +++ process_l3roamd_nlh(nlh, nlh->nlmsg_type == RTM_DELROUTE); +++ } ++ } ++ ++ if (errno != EAGAIN) { ++ OLSR_PRINTF(1,"netlink listen error %u - %s\n",errno,strerror(errno)); ++ } +++end: +++ free(buffer); ++ } ++ ++ static void ++diff --git a/src/main.c b/src/main.c ++index 6e663595..f490fdaa 100644 ++--- a/src/main.c +++++ b/src/main.c ++@@ -86,6 +86,7 @@ ++ #include ++ #include ++ #include "kernel_routes.h" +++#include "l3roamd.h" ++ ++ #endif /* __linux__ */ ++ ++@@ -416,6 +417,7 @@ void get_argc_argv(int *argc, char ***argv) { ++ } ++ } ++ +++ ++ int main(int argc, char *argv[]) { ++ int argcLocal = argc; ++ ++@@ -571,7 +573,7 @@ int main(int argc, char *argv[]) { ++ olsr_syslog(OLSR_LOG_INFO, "rtnetlink could not be set to nonblocking"); ++ } ++ ++- if ((olsr_cnf->rt_monitor_socket = rtnetlink_register_socket(RTMGRP_LINK)) < 0) { +++ if ((olsr_cnf->rt_monitor_socket = rtnetlink_register_socket(RTMGRP_LINK | RTMGRP_IPV4_ROUTE)) < 0) { ++ char buf2[1024]; ++ snprintf(buf2, sizeof(buf2), "rtmonitor socket: %s", strerror(errno)); ++ olsr_exit(buf2, EXIT_FAILURE); ++@@ -771,7 +773,9 @@ int main(int argc, char *argv[]) { ++ signal(SIGUSR2, SIG_IGN); ++ #endif /* _WIN32 */ ++ ++- /* Starting scheduler */ +++ l3roamd_hna_init(); +++ +++ /* Starting scheduler */ ++ olsr_scheduler(); ++ ++ /* We'll only get here when olsr_shutdown has stopped the scheduler */ ++diff --git a/src/olsr_cfg.h b/src/olsr_cfg.h ++index de490627..4e4195b9 100644 ++--- a/src/olsr_cfg.h +++++ b/src/olsr_cfg.h ++@@ -417,6 +417,8 @@ extern "C" { ++ ++ int ip_prefix_list_remove(struct ip_prefix_list **, const union olsr_ip_addr *, uint8_t); ++ +++ void ip_prefix_list_clear(struct ip_prefix_list **list); +++ ++ struct ip_prefix_list *ip_prefix_list_find(struct ip_prefix_list *, const union olsr_ip_addr *net, uint8_t prefix_len); ++ ++ /* ++-- ++2.38.1 ++ diff --git a/patches/packages/routing/0007-oonf-init-scripts-start-olsrd2-10-seceond-after-boot.-Workaround.patch b/patches/packages/routing/0007-oonf-init-scripts-start-olsrd2-10-seceond-after-boot.-Workaround.patch new file mode 100644 index 0000000000..6f12357624 --- /dev/null +++ b/patches/packages/routing/0007-oonf-init-scripts-start-olsrd2-10-seceond-after-boot.-Workaround.patch @@ -0,0 +1,42 @@ +From fea85afce1b1a4c6be5bcbc17b1e8fd1c707e895 Mon Sep 17 00:00:00 2001 +From: Patrick Grimm +Date: Fri, 13 Jan 2023 20:52:36 +0100 +Subject: [PATCH] oonf-init-scripts: start olsrd2 10 seceond after boot. + Workaround. + +Signed-off-by: Patrick Grimm +--- + oonf-init-scripts/Makefile | 2 +- + oonf-init-scripts/files/oonf_init.sh | 6 ++++++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/oonf-init-scripts/Makefile b/oonf-init-scripts/Makefile +index 8bfbf6e..7abd3b9 100644 +--- a/oonf-init-scripts/Makefile ++++ b/oonf-init-scripts/Makefile +@@ -3,7 +3,7 @@ include $(INCLUDE_DIR)/kernel.mk + + PKG_NAME:=oonf-init-scripts + PKG_VERSION:=0.9.1-r3 +-PKG_RELEASE:=1 ++PKG_RELEASE:=2 + PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) + + include $(INCLUDE_DIR)/package.mk +diff --git a/oonf-init-scripts/files/oonf_init.sh b/oonf-init-scripts/files/oonf_init.sh +index a945c4e..32a5a2f 100755 +--- a/oonf-init-scripts/files/oonf_init.sh ++++ b/oonf-init-scripts/files/oonf_init.sh +@@ -135,3 +135,9 @@ status() + echo "stopped" + fi + } ++ ++boot() { ++ sleep 10 ++ logger -t olsrd2init "boot delay" ++ start ++} +-- +2.40.1 + diff --git a/scripts/copy_output.lua b/scripts/copy_output.lua index 1af2f8f7bb..581dcd16a4 100755 --- a/scripts/copy_output.lua +++ b/scripts/copy_output.lua @@ -35,7 +35,7 @@ lib.include(target) local function image_source(image) return string.format( - 'openwrt/bin/targets/%s/openwrt-%s-%s%s%s', + 'openwrt/bin/targets/%s/' .. (env.GLUON_PREFIX or 'openwrt') .. '-%s-%s%s%s', bindir, openwrt_target, image.name, image.in_suffix, image.extension) end diff --git a/scripts/target_config_lib.lua b/scripts/target_config_lib.lua index ef487f0614..48655f4758 100644 --- a/scripts/target_config_lib.lua +++ b/scripts/target_config_lib.lua @@ -174,13 +174,16 @@ local function get_default_pkgs() end lib.include('generic') +lib.include('generic_' .. env.GLUON_BUILDTYPE) lib.include(target) lib.check_devices() handle_target_pkgs(concat_list(get_default_pkgs(), lib.target_packages)) -for _, dev in ipairs(lib.devices) do +-- the if condition in ipairs checks if a user-configured target is +-- trying to build all devices, in which case specific gluon target-definitions are skipped +for _, dev in ipairs(lib.configs.TARGET_ALL_PROFILES and {} or lib.devices) do local device_pkgs = {} local function handle_pkgs(pkgs) for _, pkg in ipairs(pkgs) do @@ -192,9 +195,14 @@ for _, dev in ipairs(lib.devices) do end handle_pkgs(lib.target_packages) - handle_pkgs(class_packages(dev.options.class)) handle_pkgs(dev.options.packages or {}) - handle_pkgs(site_packages(dev.image)) + + if env.GLUON_BUILDTYPE == 'gluon' then + handle_pkgs(class_packages(dev.options.class)) + handle_pkgs(site_packages(dev.image)) + else + handle_pkgs(lib.target_class_packages[dev.options.class] or {}) + end local profile_config = string.format('%s_DEVICE_%s', openwrt_config_target, dev.name) lib.config( diff --git a/scripts/target_lib.lua b/scripts/target_lib.lua index b8dd933e48..eba7200822 100644 --- a/scripts/target_lib.lua +++ b/scripts/target_lib.lua @@ -25,6 +25,7 @@ M.site_code = assert( dofile('scripts/site_config.lua')('site.conf').site_code, 'site_code missing in site.conf' ) M.target_packages = {} +M.target_class_packages = {} M.configs = {} M.devices = {} M.images = {} @@ -202,6 +203,17 @@ function F.packages(pkgs) end M.packages = F.packages +function F.class_packages(target, pkgs) + if not M.target_class_packages[target] then + M.target_class_packages[target] = {} + end + + for _, pkg in ipairs(pkgs) do + table.insert(M.target_class_packages[target], pkg) + end +end +M.class_packages = F.class_packages + local function as_table(v) if type(v) == 'table' then return v @@ -270,12 +282,38 @@ function F.defaults(options) default_options = merge(default_options, options) end +local function load_and_assert(...) + for _, path in ipairs(arg) do + local fd = io.open(path, 'r') + if fd ~= nil then + fd:close() + -- only assert if file exists. this allows trying multiple files. + return assert(loadfile(path)) + end + end + + assert(nil) +end + +-- this function allows including target configurations from the first source +-- that a file is found +-- targets are loaded in the following order: +-- - current working directory +-- - site +-- - gluon function F.include(name) - local f = assert(loadfile(env.GLUON_TARGETSDIR .. '/' .. name)) + local f = load_and_assert('./' .. name, env.GLUON_SITEDIR .. '/' .. name, env.GLUON_TARGETSDIR .. '/' .. name) setfenv(f, funcs) return f() end +-- this function allows including target configurations from gluon +-- can be used to include original targets via site, for example +function F.include_gluon(name) + local f = load_and_assert(env.GLUON_TARGETSDIR .. '/' .. name) + setfenv(f, funcs) + return f() +end function M.check_devices() local device_list = {} diff --git a/targets/ath79-generic b/targets/ath79-generic index dc88494690..f95c9aec3b 100644 --- a/targets/ath79-generic +++ b/targets/ath79-generic @@ -222,6 +222,7 @@ device('netgear-wnr2200-16m', 'netgear_wnr2200-16m', { device('ocedo-koala', 'ocedo_koala', { factory = false, packages = ATH10K_PACKAGES_QCA9880, + broken = true, }) device('ocedo-raccoon', 'ocedo_raccoon', { @@ -410,6 +411,7 @@ device('tp-link-archer-c6-v2-eu-ru-jp', 'tplink_archer-c6-v2', { manifest_aliases = { 'tp-link-archer-c6-v2', -- Upgrade from OpenWrt 19.07 }, + broken = true, }) device('tp-link-archer-c60-v1', 'tplink_archer-c60-v1', { @@ -474,22 +476,27 @@ device('tp-link-eap225-outdoor-v1', 'tplink_eap225-outdoor-v1', { packages = ATH10K_PACKAGES_QCA9888, }) -device('tp-link-re355-v1', 'tplink_re355-v1', { - manifest_aliases = { - 'tp-link-re355', -- upgrade from OpenWrt 19.07 - }, - packages = ATH10K_PACKAGES_SMALLBUFFERS_QCA9880, - broken = true, -- OOM with 5GHz enabled in most environments if device is 64M RAM variant - class = 'tiny', -- Only 6M of usable Firmware space +device('tp-link-eap225-outdoor-v3', 'tplink_eap225-outdoor-v3', { + factory = false, + packages = ATH10K_PACKAGES_QCA9888, }) -device('tp-link-re450-v1', 'tplink_re450-v1', { - packages = ATH10K_PACKAGES_QCA9880, - manifest_aliases = { - 'tp-link-re450', -- upgrade from OpenWrt 19.07 - }, - class = 'tiny', -- Only 6M of usable Firmware space -}) +-- device('tp-link-re355-v1', 'tplink_re355-v1', { +-- manifest_aliases = { +-- 'tp-link-re355', -- upgrade from OpenWrt 19.07 +-- }, +-- packages = ATH10K_PACKAGES_SMALLBUFFERS_QCA9880, +-- broken = true, -- OOM with 5GHz enabled in most environments if device is 64M RAM variant +-- class = 'tiny', -- Only 6M of usable Firmware space +-- }) + +-- device('tp-link-re450-v1', 'tplink_re450-v1', { +-- packages = ATH10K_PACKAGES_QCA9880, +-- manifest_aliases = { +-- 'tp-link-re450', -- upgrade from OpenWrt 19.07 +-- }, +-- class = 'tiny', -- Only 6M of usable Firmware space +-- }) device('tp-link-tl-wdr3500-v1', 'tplink_tl-wdr3500-v1') device('tp-link-tl-wdr3600-v1', 'tplink_tl-wdr3600-v1') @@ -580,9 +587,10 @@ device('ubiquiti-unifi-ac-mesh-pro', 'ubnt_unifiac-mesh-pro', { device('ubiquiti-unifi-ac-pro', 'ubnt_unifiac-pro', { factory = false, packages = ATH10K_PACKAGES_QCA9880, + broken = true, }) -device('ubiquiti-unifi-ap', 'ubnt_unifi', { +device('ubiquiti-unifi-ap', 'ubnt_unifi-ap', { aliases = { 'ubiquiti-unifi-ap-lr', }, diff --git a/targets/generic b/targets/generic index 20111220c6..8f72674cad 100644 --- a/targets/generic +++ b/targets/generic @@ -1,26 +1,8 @@ -assert(env.GLUON_LANGS) - - -config('GLUON_SITEDIR', env.GLUON_SITEDIR) -config('GLUON_VERSION', env.GLUON_VERSION) -config('GLUON_SITE_VERSION', env.GLUON_SITE_VERSION) -config('GLUON_RELEASE', env.GLUON_RELEASE) - -try_config('GLUON_AUTOUPDATER_BRANCH', env.GLUON_AUTOUPDATER_BRANCH) -try_config('GLUON_AUTOUPDATER_ENABLED', istrue(env.GLUON_AUTOUPDATER_ENABLED)) - -for lang in string.gmatch(env.GLUON_LANGS, '%S+') do - try_config('GLUON_WEB_LANG_' .. lang, true) -end - config('TARGET_' .. env.BOARD, true) if env.SUBTARGET ~= '' then config(string.format('TARGET_%s_%s', env.BOARD, env.SUBTARGET), true) end --- Disable non-default feeds in distfeeds.conf -config('FEED_gluon_base', false) - local default_feeds = {} for feed in string.gmatch(exec_capture_raw('. scripts/default_feeds.sh && echo "$DEFAULT_FEEDS"'), '%S+') do default_feeds[feed] = true @@ -32,53 +14,6 @@ for feed in string.gmatch(exec_capture_raw('. scripts/modules.sh && echo -n "$FE end end - -config('TARGET_ROOTFS_INITRAMFS', false) - -config('DEVEL', true) -config('ALL_NONSHARED', true) - -try_config('PACKAGE_usbip', false) -- fails to build - -try_config('PACKAGE_ATH_DEBUG', true) - -try_config('PACKAGE_dnsmasq_full_dhcpv6', false) -try_config('PACKAGE_dnsmasq_full_auth', false) -try_config('PACKAGE_dnsmasq_full_ipset', false) -try_config('PACKAGE_dnsmasq_full_nftset', false) -try_config('PACKAGE_dnsmasq_full_conntrack', false) -try_config('PACKAGE_dnsmasq_full_noid', false) -try_config('PACKAGE_dnsmasq_full_broken_rtc', false) -try_config('PACKAGE_dnsmasq_full_rtc', false) - -try_config('TARGET_SQUASHFS_BLOCK_SIZE', 256) - -config('KERNEL_PROC_STRIPPED', true) -config('KERNEL_AIO', false) -config('KERNEL_IO_URING', false) -config('KERNEL_FHANDLE', false) -config('KERNEL_FANOTIFY', false) -config('KERNEL_CGROUPS', false) -config('KERNEL_IP_MROUTE', false) -config('KERNEL_IPV6_MROUTE', false) -config('KERNEL_IPV6_SEG6_LWTUNNEL', false) -config('SECCOMP', false) -config('KERNEL_SECCOMP', false) --- kmod-mt7915e pulls in CONFIG_KERNEL_RELAY --- use try_config, so enabling the package is still possible -try_config('PACKAGE_kmod-mt7915e', false) - -try_config('OONF_GENERIC_HTTP', true) - -config('COLLECT_KERNEL_DEBUG', true) - -config('TARGET_MULTI_PROFILE', true) -config('TARGET_PER_DEVICE_ROOTFS', true) - -config('GLUON_MULTIDOMAIN', istrue(env.GLUON_MULTIDOMAIN)) - -config('AUTOREMOVE', istrue(env.GLUON_AUTOREMOVE)) - if istrue(env.GLUON_DEBUG) then config('DEBUG', true) config('NO_STRIP', true) @@ -87,24 +22,3 @@ if istrue(env.GLUON_DEBUG) then try_config('TARGET_ROOTFS_PARTSIZE', 500) end - -config('GLUON_MINIFY', istrue(env.GLUON_MINIFY)) - -packages { - '-ca-bundle', - '-dnsmasq', - '-kmod-ipt-offload', - '-kmod-nft-offload', - '-libustream-wolfssl', - '-libwolfssl', - '-nftables', - '-odhcpd-ipv6only', - '-ppp', - '-ppp-mod-pppoe', - '-wpad-mini', - '-wpad-basic', - '-wpad-basic-wolfssl', - '-firewall4', - 'gluon-core', - 'ip6tables-zz-legacy', -} diff --git a/targets/generic_gluon b/targets/generic_gluon new file mode 100644 index 0000000000..eb2a5402a0 --- /dev/null +++ b/targets/generic_gluon @@ -0,0 +1,92 @@ +assert(env.GLUON_LANGS) + + +config('GLUON_SITEDIR', env.GLUON_SITEDIR) +config('GLUON_VERSION', env.GLUON_VERSION) +config('GLUON_SITE_VERSION', env.GLUON_SITE_VERSION) +config('GLUON_RELEASE', env.GLUON_RELEASE) + +try_config('GLUON_AUTOUPDATER_BRANCH', env.GLUON_AUTOUPDATER_BRANCH) +try_config('GLUON_AUTOUPDATER_ENABLED', istrue(env.GLUON_AUTOUPDATER_ENABLED)) + +for lang in string.gmatch(env.GLUON_LANGS, '%S+') do + try_config('GLUON_WEB_LANG_' .. lang, true) +end + +-- Disable non-default feeds in distfeeds.conf +config('FEED_gluon_base', false) + + +config('TARGET_ROOTFS_INITRAMFS', false) + +config('DEVEL', true) +config('ALL_NONSHARED', true) + +try_config('PACKAGE_usbip', false) -- fails to build +try_config('PACKAGE_coova-chilli', false) -- fails to build +try_config('PACKAGE_kmod-ipt-coova', false) -- fails to build + +try_config('PACKAGE_ATH_DEBUG', true) + +try_config('TARGET_SQUASHFS_BLOCK_SIZE', 256) + +config('KERNEL_PROC_STRIPPED', true) +config('KERNEL_AIO', false) +config('KERNEL_IO_URING', false) +config('KERNEL_FHANDLE', false) +config('KERNEL_FANOTIFY', false) +config('KERNEL_CGROUPS', false) +config('KERNEL_IP_MROUTE', false) +config('KERNEL_IPV6_MROUTE', false) +config('KERNEL_IPV6_SEG6_LWTUNNEL', false) +config('SECCOMP', false) +config('KERNEL_SECCOMP', false) +-- kmod-mt7915e pulls in CONFIG_KERNEL_RELAY +-- use try_config, so enabling the package is still possible +try_config('PACKAGE_kmod-mt7915e', false) + +try_config('OONF_GENERIC_HTTP', true) +try_config('OONF_OLSRV2_LAN_IMPORT', true) + +config('COLLECT_KERNEL_DEBUG', true) + +config('TARGET_MULTI_PROFILE', true) +config('TARGET_PER_DEVICE_ROOTFS', true) + +config('GLUON_MULTIDOMAIN', istrue(env.GLUON_MULTIDOMAIN)) + +config('OPENVPN_openssl_ENABLE_SMALL', true) + +config('AUTOREMOVE', istrue(env.GLUON_AUTOREMOVE)) + + +config('GLUON_MINIFY', istrue(env.GLUON_MINIFY)) + +packages { + '-ca-bundle', + '-kmod-ipt-offload', + '-kmod-nft-offload', + '-libustream-mbedtls20201210', + '-libmbedtls12', + '-opkg', + '-odhcpd-ipv6only', + '-ppp', + '-ppp-mod-pppoe', + '-wpad-mini', + '-wpad-basic', + '-wpad-basic-mbedtls', + 'gluon-core', + '-iptables', + '-ip6tables', + '-nftables', + 'nftables-json', + '-xtables-legacy', + '-ip6tables-nft', + '-iptables-nft', + '-kmod-ipt-core', + -- old shit that doesnt build + '-kmod-fs-antfs', + '-kmod-ipt-coova', + '-kmod-usb-serial-dmx_usb_module', + '-kmod-jool-netfilter', +} diff --git a/targets/ipq40xx-generic b/targets/ipq40xx-generic index 83b4a128bd..2b7d31935e 100644 --- a/targets/ipq40xx-generic +++ b/targets/ipq40xx-generic @@ -41,10 +41,10 @@ device('aruba-ap-303h', 'aruba_ap-303h', { aliases = {'aruba-instant-on-ap11d'}, }) -device('aruba-ap-365', 'aruba_ap-365', { - factory = false, - aliases = {'aruba-instant-on-ap17'}, -}) +-- device('aruba-ap-365', 'aruba_ap-365', { +-- factory = false, +-- aliases = {'aruba-instant-on-ap17'}, +-- }) -- AVM @@ -68,23 +68,23 @@ device('avm-fritz-repeater-1200', 'avm_fritzrepeater-1200', { -- EnGenius -device('engenius-ens620ext', 'engenius_ens620ext', { - factory = false, - extra_images = { - {'-squashfs-factory_30', '-factory_fw30', '.bin'}, - {'-squashfs-factory_35', '-factory_fw35', '.bin'}, - }, -}) +-- device('engenius-ens620ext', 'engenius_ens620ext', { +-- factory = false, +-- extra_images = { +-- {'-squashfs-factory_30', '-factory_fw30', '.bin'}, +-- {'-squashfs-factory_35', '-factory_fw35', '.bin'}, +-- }, +-- }) -- GL.iNet -device('gl.inet-gl-ap1300', 'glinet_gl-ap1300', { - factory = '-squashfs-nand-factory', - factory_ext = '.ubi', - sysupgrade = '-squashfs-nand-sysupgrade', - sysupgrade_ext = '.bin', -}) +-- device('gl.inet-gl-ap1300', 'glinet_gl-ap1300', { +-- factory = '-squashfs-nand-factory', +-- factory_ext = '.ubi', +-- sysupgrade = '-squashfs-nand-sysupgrade', +-- sysupgrade_ext = '.bin', +-- }) device('gl.inet-gl-b1300', 'glinet_gl-b1300', { factory = false, @@ -129,11 +129,10 @@ device('plasma-cloud-pa2200', 'plasmacloud_pa2200', { device('zyxel-nbg6617', 'zyxel_nbg6617') -device('zyxel-wre6606', 'zyxel_wre6606', { - packages = ATH10K_PACKAGES_IPQ40XX_SMALLBUFFERS, - factory = false, - class = 'tiny', -- 128M ath10k + ath10k -}) +-- device('zyxel-wre6606', 'zyxel_wre6606', { +-- factory = false, +-- class = 'tiny', -- 128M ath10k + ath10k +-- }) -- 8devices diff --git a/targets/ipq40xx-mikrotik b/targets/ipq40xx-mikrotik index aed02b2a4e..fdf0544cde 100644 --- a/targets/ipq40xx-mikrotik +++ b/targets/ipq40xx-mikrotik @@ -10,3 +10,9 @@ device('mikrotik-sxtsq-5-ac-rbsxtsqg-5acd', 'mikrotik_sxtsq-5-ac', { factory = false, aliases = {'mikrotik-discg-5acd'}, }) + +device('mikrotik-wireless-wire-dish-lhgg-60ad', 'mikrotik_lhgg-60ad', { + packages = { + 'wpa-supplicant' + } +}) diff --git a/targets/ipq806x-generic b/targets/ipq806x-generic index 1b3263fc3e..9c7311e42a 100644 --- a/targets/ipq806x-generic +++ b/targets/ipq806x-generic @@ -26,3 +26,7 @@ device('netgear-nighthawk-x4s-r7800', 'netgear_r7800', { factory_ext = '.img', packages = QCA9984_PACKAGES, }) + +device('zyxel_nbg6817', 'zyxel_nbg6817', { + +}) diff --git a/targets/ramips-mt7621 b/targets/ramips-mt7621 index dac65c04ed..60198b9829 100644 --- a/targets/ramips-mt7621 +++ b/targets/ramips-mt7621 @@ -143,3 +143,7 @@ device('ubiquiti-edgerouter-x-sfp', 'ubnt_edgerouter-x-sfp', { 'ubnt-erx-sfp', }, }) + +device('mikrotik-routerboard-750gr3', 'mikrotik_routerboard-750gr3', { + factory = false, +})