diff --git a/contrib/ci/olsr-site/i18n b/contrib/ci/olsr-site/i18n new file mode 120000 index 0000000000..57a3c3977a --- /dev/null +++ b/contrib/ci/olsr-site/i18n @@ -0,0 +1 @@ +../minimal-site/i18n \ No newline at end of file diff --git a/contrib/ci/olsr-site/modules b/contrib/ci/olsr-site/modules new file mode 120000 index 0000000000..72d21398d0 --- /dev/null +++ b/contrib/ci/olsr-site/modules @@ -0,0 +1 @@ +../minimal-site/modules \ No newline at end of file diff --git a/contrib/ci/olsr-site/site.conf b/contrib/ci/olsr-site/site.conf new file mode 100644 index 0000000000..74907b13df --- /dev/null +++ b/contrib/ci/olsr-site/site.conf @@ -0,0 +1,169 @@ +-- This is an example site configuration for Gluon v2018.2+ +-- +-- Take a look at the documentation located at +-- https://gluon.readthedocs.io/ for details. +-- +-- This configuration will not work as is. You're required to make +-- community specific changes to it! +{ + -- Used for generated hostnames, e.g. freifunk-abcdef123456. (optional) + -- hostname_prefix = 'freifunk-', + + -- Name of the community. + site_name = 'Continious Integration', + + -- Shorthand of the community. + site_code = 'ci', + + -- 32 bytes of random data, encoded in hexadecimal + -- This data must be unique among all sites and domains! + -- Can be generated using: echo $(hexdump -v -n 32 -e '1/1 "%02x"' >' "$2" +# to decide if a version is newer or not. + +DEFAULT_GLUON_RELEASE := 0.6+exp$(shell date '+%Y%m%d') + +# Variables set with ?= can be overwritten from the command line + +## GLUON_RELEASE +# call make with custom GLUON_RELEASE flag, to use your own release version scheme. +# e.g.: +# $ make images GLUON_RELEASE=23.42+5 +# would generate images named like this: +# gluon-ff%site_code%-23.42+5-%router_model%.bin + +GLUON_RELEASE ?= $(DEFAULT_GLUON_RELEASE) + +# Default priority for updates. +GLUON_PRIORITY ?= 0 + +# Region code required for some images; supported values: us eu +GLUON_REGION ?= eu + +# Languages to include +GLUON_LANGS ?= en de diff --git a/docs/features/olsrd.rst b/docs/features/olsrd.rst new file mode 100644 index 0000000000..01a6f44be2 --- /dev/null +++ b/docs/features/olsrd.rst @@ -0,0 +1,76 @@ +OLSRD +=========== + +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 = { + -- + -- } + } + } + } + } + +Static IP managment +------------------- +This feature is enabled as part of olsrd support + +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::/64', + node_prefix6_range = 64, -- (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 (TODO: should they or would dynamic4 and dynamic4IsTmp be better?) diff --git a/docs/index.rst b/docs/index.rst index daa91d1d34..bfd1f03d58 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -27,6 +27,7 @@ Several Freifunk communities in Germany use Gluon as the foundation of their Fre features/dns-forwarder features/monitoring features/multidomain + features/olsrd features/authorized-keys features/roles features/vpn diff --git a/package/features b/package/features index da68b369e0..ef3d3980f3 100644 --- a/package/features +++ b/package/features @@ -46,6 +46,9 @@ 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', diff --git a/package/gluon-mesh-olsrd/Makefile b/package/gluon-mesh-olsrd/Makefile new file mode 100644 index 0000000000..5b18664a75 --- /dev/null +++ b/package/gluon-mesh-olsrd/Makefile @@ -0,0 +1,54 @@ +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 \ + +kmod-macvlan \ + @IPV6 +olsrd +oonf-olsrd2 \ + +firewall \ + +libgluonutil \ + +libjson-c \ + +libubox +libuclient \ + +olsrd-mod-jsoninfo \ + +olsrd-mod-httpinfo \ + +olsrd-mod-txtinfo \ + +liblua \ + +ip-full \ + +gluon-mmfd \ + +gluon-l3roamd \ + +gluon-radvd \ + +lua-jsonc + 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/ +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 new file mode 100644 index 0000000000..f97081b0fe --- /dev/null +++ b/package/gluon-mesh-olsrd/check_site.lua @@ -0,0 +1,17 @@ +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 new file mode 100755 index 0000000000..9e1716ff44 --- /dev/null +++ b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/post-setup.d/30-reload-olsr @@ -0,0 +1,12 @@ +#!/bin/sh + +reload_running() { + if [ -x /etc/init.d/"$1" ] && /etc/init.d/"$1" enabled && /etc/init.d/"$1" running; then + echo "(post-setup.d:$IFNAME) Reloading $1..." + /etc/init.d/"$1" reload + 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 new file mode 100755 index 0000000000..308f6cfee0 --- /dev/null +++ b/package/gluon-mesh-olsrd/files/lib/gluon/core/mesh/teardown.d/70-reload-olsr @@ -0,0 +1,12 @@ +#!/bin/sh + +reload_running() { + if [ -x /etc/init.d/"$1" ] && /etc/init.d/"$1" enabled && /etc/init.d/"$1" running; then + echo "(teardown.d:$IFNAME) Reloading $1..." + /etc/init.d/"$1" reload + fi +} + +reload_running olsrd +reload_running olsrd2 +reload_running olsrd6 diff --git a/package/gluon-mesh-olsrd/files/lib/gluon/respondd/client.dev b/package/gluon-mesh-olsrd/files/lib/gluon/respondd/client.dev new file mode 100644 index 0000000000..202a2374ae --- /dev/null +++ b/package/gluon-mesh-olsrd/files/lib/gluon/respondd/client.dev @@ -0,0 +1 @@ +mmfd 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 new file mode 100755 index 0000000000..25d151dce7 --- /dev/null +++ b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/abort.d/10olsrd @@ -0,0 +1,9 @@ +#!/bin/sh + +. /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 new file mode 100755 index 0000000000..e78ea79404 --- /dev/null +++ b/package/gluon-mesh-olsrd/files/usr/lib/autoupdater/upgrade.d/10olsrd @@ -0,0 +1,9 @@ +#!/bin/sh + +. /lib/gluon/autoupdater/lib.sh + + +stop olsrd +stop olsrd2 +stop olsrd6 +wifi down diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/radvd/arguments b/package/gluon-mesh-olsrd/luasrc/lib/gluon/radvd/arguments new file mode 100755 index 0000000000..661ac000d6 --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/radvd/arguments @@ -0,0 +1,7 @@ +#!/usr/bin/lua +local site = require "gluon.site" + +io.write("-i local-node --default-lifetime 900 -a " .. site.node_client_prefix6() or site.node_prefix6() or site.prefix6()) +if site.dns() and site.dns.servers() then + io.write(" --rdnss " .. site.next_node.ip6()) +end diff --git a/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/310-gluon-mesh-olsrd-firewall b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/310-gluon-mesh-olsrd-firewall new file mode 100755 index 0000000000..0df698766a --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/310-gluon-mesh-olsrd-firewall @@ -0,0 +1,95 @@ +#!/usr/bin/lua + +local uci = require('simple-uci').cursor() +local site = require "gluon.site" + +uci:section('firewall', 'zone', 'l3roamd', { + name = 'l3roamd', + input = 'ACCEPT', + output = 'ACCEPT', + forward = 'REJECT', + device = 'l3roam+', + log = '1', +}) + +uci:section('firewall', 'zone', 'mmfd', { + name = 'mmfd', + input = 'REJECT', + output = 'accept', + forward = 'REJECT', + device = 'mmfd+', + log = '1', +}) + +-- forwardings and respective rules +uci:section('firewall', 'forwarding', 'fcc', { + src = 'loc_client', + dest = 'loc_client', +}) + +uci:section('firewall', 'forwarding', 'fcm', { + src = 'loc_client', + dest = 'mesh', +}) + +uci:section('firewall', 'forwarding', 'fmc', { + src = 'mesh', + dest = 'loc_client', +}) + +uci:section('firewall', 'forwarding', 'fmm', { + src = 'mesh', + dest = 'mesh', +}) + +uci:section('firewall', 'forwarding', 'flc', { + src = 'l3roamd', + dest = 'loc_client', +}) + +uci:section('firewall', 'forwarding', 'fcl', { + src = 'loc_client', + dest = 'l3roamd', +}) + +uci:section('firewall', 'rule', 'mesh_respondd_mcast_ll', { + src = 'mesh', + src_ip = 'fe80::/64' , + dest_port = '1001', + proto = 'udp', + target = 'ACCEPT', +}) + +uci:section('firewall', 'rule', 'mesh_respondd_mcast2', { + src = 'mesh', + src_ip = site.node_prefix6() or site.prefix6(), + dest_port = '1001', + proto = 'udp', + target = 'ACCEPT', +}) + +uci:section('firewall', 'rule', 'mmfd_respondd_ll', { + src = 'mmfd', + src_ip = 'fe80::/64', + dest_port = '1001', + proto = 'udp', + target = 'ACCEPT', +}) + +uci:section('firewall', 'rule', 'mmfd_respondd_mesh', { + src = 'mmfd', + src_ip = site.node_prefix6() or site.prefix6(), + dest_port = '1001', + proto = 'udp', + target = 'ACCEPT', +}) + +uci:section('firewall', 'rule', 'mesh_mmfd', { + src = 'mesh', + src_ip = 'fe80::/64', + dest_port = '27275', + proto = 'udp', + target = 'ACCEPT', +}) + +uci:save('firewall') 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 new file mode 100755 index 0000000000..f06a9be82e --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/360-gluon-mesh-olsrd-setup-intf @@ -0,0 +1,370 @@ +#!/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' + +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') + +local mesh_interfaces_uplink = {} +local mesh_interfaces_client = {} +local mesh_interfaces_other = {} +for _, iface in ipairs(mesh_interfaces) do + if util.contains(uplink_interfaces, iface) then + table.insert(mesh_interfaces_uplink, iface) + elseif util.contains(client_interfaces, iface) then + table.insert(mesh_interfaces_client, iface) + else + table.insert(mesh_interfaces_other, iface) + end +end + +-- NOTE: this is a gigantic hack that overrides the gluon rules because they rely on gluon_proto +-- this will need a rework once olsr and gluon_proto go together +-- (i suspect it will pretty much just take an "ip addr add " and the same for v6 for the statics to work) +-- (we could prob also do multiple v6 then on loopback?) + +local intf = { + wired_mesh = {}, + vpn_mesh = {}, + radio_mesh = {}, +} + +intf.all_intfs = {} + +for _, l in ipairs({ intf.wired_mesh, intf.vpn_mesh, intf.radio_mesh }) do + for _, n in ipairs(l) do + table.insert(intf.all_intfs, n) + end +end + +-- get all mesh radios and mesh lans and then add them to olsrd +wireless.foreach_radio(uci, function(radio, index, config) + local radio_name = radio['.name'] + table.insert(intf.radio_mesh, 'mesh_' .. radio_name) + 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' + + if vpn_core.enabled() 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 + +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 + +table.insert(intf.wired_mesh, 'loopback') + +local has_uplink_mesh = false +local has_other_mesh = false + +for _,i in pairs(mesh_interfaces) do + if util.contains(uplink_interfaces, i) then + has_uplink_mesh = true + else + has_other_mesh = true + end +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 + +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:section('firewall', 'rule', 'allow_olsr' .. _olsr_ip .. '_mesh', { + -- src = 'mesh', + -- HACK: this is a temporary hack because firewall doesn't want to work + src = '*', + dest_port = _olsr_port, + proto = 'udp', + target = 'ACCEPT', + }) + + uci:section('firewall', 'rule', 'allow_olsr' .. _olsr_ip .. '_wired_mesh', { + src = 'wired_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') + uci:delete('firewall', 'allow_' .. _olsr_name .. '_wired_mesh') + end +end + +uci:delete_all('olsrd2', 'interface') + +if site.mesh.olsrd.v2() ~= nil and site.mesh.olsrd.v2.enable(false) then + os.execute('/etc/init.d/olsrd2 enable') + + local addrs = { } + local lan = { } + local cfg = site.mesh.olsrd.v2 + local config = uci:get_first("olsrd2", "olsrv2") + + -- set global config + local olsr2Config = { + failfast = 'no', + pidfile = '/var/run/olsrd2.pid', + lockfile = '/var/lock/olsrd2' + } + + local extraConf = cfg.config() + if extraConf then + for k, v in pairs(extraConf) do + olsr2Config[k] = extraConf[k] + end + end + + uci:delete_all('olsrd2', 'global') + uci:section('olsrd2', 'global', nil, olsr2Config) + + uci:delete_all('olsrd2', 'telnet') + uci:section('olsrd2', 'telnet', 'telnet', { + + }) + + uci:delete_all('olsrd2', 'http') + uci:section('olsrd2', 'http', 'http', { + + }) + + if cfg.lan() then + lan = cfg.lan() + end + + if cfg.ip6_exclusive_mode(false) then + table.insert(addrs, '-0.0.0.0/0') + else + table.insert(addrs, '-127.0.0.1/8') + end + if cfg.ip4_exclusive_mode(false) then + table.insert(addrs, '-::/0') + else + table.insert(addrs, '-::1/128') + end + + if site.prefix4() and not cfg.ip6_exclusive_mode(false) then + table.insert(lan, site.prefix4()) + end + if site.prefix6() and not cfg.ip4_exclusive_mode(false) then + table.insert(lan, site.prefix6()) + end + + table.insert(addrs, 'default_accept') + + uci:set("olsrd2", config, "originator", addrs) + uci:set("olsrd2", config, "lan", lan) + + if #intf.wired_mesh then + uci:section('olsrd2', 'interface', 'wired_mesh', { + ifname = intf.wired_mesh, + bindto = addrs, + }) + end + + if #intf.vpn_mesh then + uci:section('olsrd2', 'interface', 'vpn_mesh', { + ifname = intf.vpn_mesh, + bindto = addrs, + }) + end + + if #intf.radio_mesh then + uci:section('olsrd2', 'interface', 'radio_mesh', { + ifname = intf.radio_mesh, + bindto = addrs, + }) + end + + uci:section('olsrd2', 'interface', 'loopback', { + ifname = { 'loopback' }, + bindto = addrs, + }) + + uci:section('firewall', 'rule', 'allow_olsr2_mesh', { + -- src = 'mesh', + -- HACK: this is a temporary hack because firewall doesn't want to work + src = '*', + dest_port = '269', + proto = 'udp', + target = 'ACCEPT', + }) + + uci:section('firewall', 'rule', 'allow_olsr2_wired_mesh', { + src = 'wired_mesh', + dest_port = '269', + proto = 'udp', + target = 'ACCEPT', + }) +else + -- site.mesh.olsrd.v2.enable false + os.execute('/etc/init.d/olsrd2 disable') + uci:delete('firewall', 'allow_olsr2_mesh') + uci:delete('firewall', 'allow_olsr2_wired_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('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 new file mode 100755 index 0000000000..a7ffe93526 --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/370-gluon-mesh-olsrd-setup-fw @@ -0,0 +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' + +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/lib/gluon/upgrade/430-gluon-mesh-olsrd-add-mmfd-interface b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/430-gluon-mesh-olsrd-add-mmfd-interface new file mode 100755 index 0000000000..073d288e8f --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/lib/gluon/upgrade/430-gluon-mesh-olsrd-add-mmfd-interface @@ -0,0 +1,10 @@ +#!/usr/bin/lua + +local uci = require('simple-uci').cursor() + +uci:section('network', 'interface', 'mmfd', { + proto = 'static', + ifname = 'mmfd0', + ip6addr = 'fe80::1/64' +}) +uci:save('network') 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..d5860e4f44 --- /dev/null +++ b/package/gluon-mesh-olsrd/luasrc/usr/bin/olsrd-cli @@ -0,0 +1,139 @@ +#!/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 + +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' } + } }, + olsr2 = { dummy, 'OLSRv2 Control Commands', { + nodeinfo = { dummy, 'OLSRv2 Nodeinfo', { + raw = { olsr2_nodeinfo_raw, 'OLSRv2 Nodeinfo Raw' }, + json = { olsr2_nodeinfo_json, 'OLSRv2 Nodeinfo JSON' } + } } + } } +} + +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..5843a2c249 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/Makefile @@ -0,0 +1,34 @@ +all: respondd.so libolsrdhelper.so olsrd.so + +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 += $(shell pkg-config --cflags json-c) +LDFLAGS += $(shell pkg-config --libs json-c) + +SOURCES_HELPER = libolsrdhelper.c libolsrdhelper-neigh.c uclient.c +FILES_HELPER = $(SOURCES_HELPER) libolsrdhelper.h uclient.h + +SOURCES_RESPONDD = respondd.c respondd-nodeinfo.c respondd-neighbours.c +FILES_RESPONDD = $(SOURCES_RESPONDD) respondd-common.h + +SOURCES_CLI = olsrd-cli.c +FILES_CLI = $(SOURCES_CLI) + +SOURCES_LUA = olsrd.c +FILES_LUA = $(SOURCES_LUA) + +respondd.so: libolsrdhelper.so $(FILES_RESPONDD) + $(CC) $(CFLAGS) $(LDFLAGS) -I. -L. -shared -fPIC -fvisibility=hidden -o $@ $(SOURCES_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 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..d96a181316 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/libolsrdhelper-neigh.c @@ -0,0 +1,164 @@ +/* + Copyright (c) 2021, Maciej Krüger + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "libolsrdhelper.h" + +struct json_object * olsr1_get_neigh(void) { + json_object *resp; + int error = olsr1_get_nodeinfo("links", &resp); + + if (error) { + return NULL; + } + + json_object *out = json_object_new_object(); + + if (!out) + return NULL; + + /* + + 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) + return NULL; + + int linkcount = json_object_array_length(links); + + for (int i = 0; i < linkcount; i++) { + struct json_object *link = json_object_array_get_idx(links, i); + if (!link) + return NULL; + + struct json_object *neigh = json_object_new_object(); + if (!neigh) + return NULL; + + json_object_object_add(neigh, "ifname", json_object_object_get(link, "ifName")); + // TODO: do we need this? should we set this? (we could pick the one peer that we currently route 0.0.0.0 over...) + json_object_object_add(neigh, "best", json_object_new_boolean(0)); + json_object_object_add(neigh, "etx", json_object_object_get(link, "etx")); + json_object_object_add(neigh, "ip", json_object_object_get(link, "remoteIP")); + + json_object_object_add(out, json_object_get_string(json_object_object_get(link, "remoteIP")), neigh); + } + + return out; +} + +struct json_object * olsr2_get_neigh(void) { + json_object *resp; + int error = olsr2_get_nodeinfo("nhdpinfo jsonraw link", &resp); + + if (error) { + return NULL; + } + + json_object *out = json_object_new_object(); + + if (!out) + return NULL; + + /* + + 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) + return NULL; + + int linkcount = json_object_array_length(links); + + for (int i = 0; i < linkcount; i++) { + struct json_object *link = json_object_array_get_idx(links, i); + if (!link) + return NULL; + + struct json_object *neigh = json_object_new_object(); + if (!neigh) + return NULL; + + json_object_object_add(neigh, "ifname", json_object_object_get(link, "if")); + // TODO: do we need this? should we set this? (we could pick the one peer that we currently route 0.0.0.0 over...) + json_object_object_add(neigh, "best", json_object_new_boolean(0)); + json_object_object_add(neigh, "etx", json_object_object_get(link, "link_vtime")); + json_object_object_add(neigh, "ip", json_object_object_get(link, "neighbor_originator")); + + json_object_object_add(out, json_object_get_string(json_object_object_get(link, "link_mac")), neigh); + } + + 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..a7b797b0bc --- /dev/null +++ b/package/gluon-mesh-olsrd/src/libolsrdhelper.c @@ -0,0 +1,415 @@ +/* + Copyright (c) 2021, Maciej Krüger + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "libolsrdhelper.h" +#include "uclient.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define USER_AGENT "olsrdhelper (libuclient)" + +#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; +}; + +/** 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 = 0; + + json_object *site = gluonutil_load_site_config(); + if (!site) + goto fail; + + json_object *mesh = json_object_object_get(site, "mesh"); + if (!mesh) + goto fail; + + json_object *olsrd = json_object_object_get(mesh, "olsrd"); + if (!olsrd) + goto fail; + + json_object *v1 = json_object_object_get(olsrd, "v1_4"); + json_object *v2 = json_object_object_get(olsrd, "v2"); + + struct olsr_info *info = (struct olsr_info *) malloc(sizeof(struct olsr_info)); + + *info = (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"))) { + info->olsr1.enabled = true; + + if (success_exit("/etc/init.d/olsrd", "running", NULL)) { + info->olsr1.running = true; + } + } + + if (v2 && json_object_get_boolean(json_object_object_get(v2, "enable"))) { + info->olsr2.enabled = true; + + if (success_exit("/etc/init.d/olsrd2", "running", NULL)) { + info->olsr2.running = true; + } + } + + *out = info; + +end: + return ex; + +fail: + ex = 1; + goto end; +} + +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; +} diff --git a/package/gluon-mesh-olsrd/src/libolsrdhelper.h b/package/gluon-mesh-olsrd/src/libolsrdhelper.h new file mode 100755 index 0000000000..0cd45dc51f --- /dev/null +++ b/package/gluon-mesh-olsrd/src/libolsrdhelper.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2021, Maciej Krüger + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#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); 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/olsrd.c b/package/gluon-mesh-olsrd/src/olsrd.c new file mode 100644 index 0000000000..60c3f0d74d --- /dev/null +++ b/package/gluon-mesh-olsrd/src/olsrd.c @@ -0,0 +1,168 @@ +/* + +Copyright 2022 Maciej Krüger + +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 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include +#include +#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..699e83a0a4 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-common.h @@ -0,0 +1,36 @@ +/* + Copyright (c) 2021, Maciej Krüger + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#pragma once + +#define make_safe_fnc(name, real_name) \ +struct json_object * name (void) { \ + return make_safe(&(real_name)); \ +} + +struct json_object * make_safe(struct json_object * (*fnc)(void)); + +struct json_object * respondd_provider_nodeinfo(void); +struct json_object * respondd_provider_neighbours(void); 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..133a0da79c --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-neighbours.c @@ -0,0 +1,67 @@ +/* + Copyright (c) 2022, Maciej Krüger + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "respondd-common.h" + +#include + +#include + +#include + +#include +#include +#include + +struct json_object * real_respondd_provider_neighbours(void) { + struct olsr_info *info; + + struct json_object *ret = json_object_new_object(); + + if (oi(&info)) + return ret; + + struct json_object *neigh = json_object_new_array(); + + if (info->olsr1.enabled && info->olsr1.running) { + struct json_object *neigh1 = olsr1_get_neigh(); + if (neigh1) + for (int i = 0; i < json_object_array_length(neigh1); i++) + json_object_array_add(neigh, json_object_array_get_idx(neigh1, i)); + } + + if (info->olsr2.enabled && info->olsr2.running) { + struct json_object *neigh2 = olsr2_get_neigh(); + if (neigh2) + for (int i = 0; i < json_object_array_length(neigh2); i++) + json_object_array_add(neigh, json_object_array_get_idx(neigh2, i)); + } + + json_object_object_add(ret, "neigh", neigh); + + return ret; +} + +make_safe_fnc(respondd_provider_neighbours, real_respondd_provider_neighbours) 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..02391ffe6c --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd-nodeinfo.c @@ -0,0 +1,466 @@ +/* + Copyright (c) 2022, Maciej Krüger + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "respondd-common.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; + + return json_object_object_get(resp, "plugins"); +} + +static json_object * olsr1_get_version (void) { + json_object *resp; + + if (olsr1_get_nodeinfo("version", &resp)) + return NULL; + + return 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; + + return json_object_object_get(json_object_object_get(resp, "version"), "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 = json_object_object_get(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++) { + struct json_object *el = json_object_array_get_idx(origs, i); + if (json_object_get_string(el)[0] != "-"[0]) { + json_object_array_add(out, el); + } + } + + 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 + ); + + json_object_object_add(intf, "configured", json_object_object_get(el, "configured")); + json_object_object_add(intf, "up", json_object_object_get(olsr, "up")); + json_object_object_add(intf, "ipAddress", json_object_object_get(olsr, "ipAddress")); + json_object_object_add(intf, "mode", json_object_object_get(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 + ); + + json_object_object_add(intf, "mac", json_object_object_get(el, "if_mac")); + json_object_object_add(intf, "v4", json_object_object_get(el, "if_bindto_v4")); + json_object_object_add(intf, "v6", json_object_object_get(el, "if_bindto_v6")); + } + + return out; +} + +/* static struct json_object * get_mesh(void) { + struct json_object *ret = json_object_new_object(); + struct json_object *bat0_interfaces = json_object_new_object(); + json_object_object_add(bat0_interfaces, "interfaces", get_mesh_subifs("bat0")); + json_object_object_add(ret, "bat0", bat0_interfaces); + return ret; +} */ + +struct json_object * real_respondd_provider_nodeinfo(void) { + struct olsr_info *info; + + struct json_object *ret = json_object_new_object(); + + if (oi(&info)) + return ret; + + /* struct json_object *network = json_object_new_object(); + json_object_object_add(network, "addresses", get_addresses()); + json_object_object_add(network, "mesh", get_mesh()); + json_object_object_add(ret, "network", network); */ + + /* + + TODO: get interfaces and return in following schema + + { + interfaces: { + $intf_name: { + olsr1: { + configured, + up: intf.olsrInterface.up, + } + olsr2: { + + } + } + } + } + + */ + + 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); + + json_object_object_add(ret, "network", network); + + struct json_object *software = json_object_new_object(); + + 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_array_get_idx(addresses, i)); + } + + struct json_object *interfaces = olsr1_get_interfaces(); + if (interfaces) { + json_object_object_add(software_olsr1, "interfaces", interfaces); + + struct json_object_iterator it = json_object_iter_begin(interfaces); + struct json_object_iterator itEnd = json_object_iter_end(interfaces); + + while (!json_object_iter_equal(&it, &itEnd)) { + const char * name = json_object_iter_peek_name(&it); + json_object *append_key = json_object_object_get(n_interfaces, name); + + if (!append_key) { + append_key = json_object_new_object(); + json_object_object_add(n_interfaces, name, append_key); + } + + json_object_object_add(append_key, "olsr1", + json_object_object_get(interfaces, name)); + json_object_iter_next(&it); + } + } + } + + 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_array_get_idx(addresses, i)); + } + + struct json_object *interfaces = olsr2_get_interfaces(); + if (interfaces) { + json_object_object_add(software_olsr2, "interfaces", interfaces); + + struct json_object_iterator it = json_object_iter_begin(interfaces); + struct json_object_iterator itEnd = json_object_iter_end(interfaces); + + while (!json_object_iter_equal(&it, &itEnd)) { + const char * name = json_object_iter_peek_name(&it); + json_object *append_key = json_object_object_get(n_interfaces, name); + + if (!append_key) { + append_key = json_object_new_object(); + json_object_object_add(n_interfaces, name, append_key); + } + + json_object_object_add(append_key, "olsr2", + json_object_object_get(interfaces, name)); + json_object_iter_next(&it); + } + } + } + + json_object_object_add(software, "olsr2", software_olsr2); + } + + json_object_object_add(ret, "software", software); + + return ret; +} + +make_safe_fnc(respondd_provider_nodeinfo, real_respondd_provider_nodeinfo) diff --git a/package/gluon-mesh-olsrd/src/respondd.c b/package/gluon-mesh-olsrd/src/respondd.c new file mode 100644 index 0000000000..94ff67432d --- /dev/null +++ b/package/gluon-mesh-olsrd/src/respondd.c @@ -0,0 +1,89 @@ +/* + Copyright (c) 2021, Maciej Krüger + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "respondd-common.h" + +#include + +#include + +#define RESP_SIZE 1024 * 1024 * 1024 + +#include +#include +#include +#include +#include +#include +#include + +struct json_object * make_safe(struct json_object * (*fnc)(void)) { + char * shared = mmap(NULL, RESP_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + + pid_t pid = fork(); + + struct json_object *r = NULL; + + if (pid == 0) { + struct json_object *resp = fnc(); + if (!resp) { + _exit(EXIT_FAILURE); + } + + const char *resp_str = json_object_to_json_string(resp); + size_t len = strlen(resp_str); + if (len > RESP_SIZE) { + _exit(EXIT_FAILURE); + } + + memcpy(shared, resp_str, len); + _exit(EXIT_SUCCESS); + } else { + int status; + + if (waitpid(pid, &status, 0) == -1) { + goto ret; + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) { + r = json_tokener_parse(shared); + } + } + } + +ret: + munmap(shared, RESP_SIZE); + + return r; +} + +__attribute__ ((visibility ("default"))) +const struct respondd_provider_info respondd_providers[] = { + {"nodeinfo", respondd_provider_nodeinfo}, + {"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..1863820101 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/uclient.c @@ -0,0 +1,281 @@ +/* + Copyright (c) 2017, Jan-Philipp Litza + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "uclient.h" + +#include +#include + +#include +#include + + +#define TIMEOUT_MSEC 300000 + +static const char *const user_agent = "OLSRDHelper.so (using 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) { + printf("calling eof normally\n"); + 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..a8729225d1 --- /dev/null +++ b/package/gluon-mesh-olsrd/src/uclient.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2017, Jan-Philipp Litza + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#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-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..6bce784cb6 --- /dev/null +++ b/package/gluon-status-page-mesh-olsrd/luasrc/lib/gluon/status-page/mesh.lua @@ -0,0 +1,10 @@ +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', ' '}, + }, +} 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..7b977dacf9 --- /dev/null +++ b/package/gluon-status-page-mesh-olsrd/src/neighbours-olsrd.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +static json_object *neighbours(void) { + json_object *resp; + if (olsr1_get_nodeinfo("links", &resp)) + return NULL; + + json_object *out = json_object_new_object(); + if (!out) + return NULL; + + /* + + 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 + + */ + + // TODO: use olsr1_get_neigh and olsr2_get_neigh, iterate over both, then copy stuffs into the right format (and use mac as primary) + + json_object *links = json_object_object_get(resp, "links"); + if (!links) + return NULL; + + int linkcount = json_object_array_length(links); + + for (int i = 0; i < linkcount; i++) { + struct json_object *link = json_object_array_get_idx(links, i); + if (!link) + return NULL; + + struct json_object *neigh = json_object_new_object(); + if (!neigh) + return NULL; + + json_object_object_add(neigh, "ifname", json_object_object_get(link, "ifName")); + // TODO: do we need this? should we set this? (we could pick the one peer that we currently route 0.0.0.0 over...) + json_object_object_add(neigh, "best", json_object_new_boolean(0)); + json_object_object_add(neigh, "etx", json_object_object_get(link, "etx")); + + json_object_object_add(out, json_object_get_string(json_object_object_get(link, "remoteIP")), neigh); + } + + return out; +} + +int main(void) { + struct json_object *obj; + + printf("Content-type: text/event-stream\n\n"); + fflush(stdout); + + while (1) { + obj = neighbours(); + 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/patches/packages/routing/0004--oonf-olsrd2-add-support-to-check-if-service-is-runni.patch b/patches/packages/routing/0004--oonf-olsrd2-add-support-to-check-if-service-is-runni.patch new file mode 100644 index 0000000000..82bc3ecdbf --- /dev/null +++ b/patches/packages/routing/0004--oonf-olsrd2-add-support-to-check-if-service-is-runni.patch @@ -0,0 +1,28 @@ +From e5c0192003d6a0afee97abb9a947bc0944883091 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: [PATCH] oonf-olsrd2: add support to check if service is running + +--- + oonf-olsrd2/files/olsrd2.init | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/oonf-olsrd2/files/olsrd2.init b/oonf-olsrd2/files/olsrd2.init +index debae98..b6c1e9a 100755 +--- a/oonf-olsrd2/files/olsrd2.init ++++ b/oonf-olsrd2/files/olsrd2.init +@@ -3,4 +3,11 @@ + START=82 + DAEMON='olsrd2' + ++running() { ++ test -e "/tmp/run/olsrd2.pid" && test -e "/proc/$(cat "/tmp/run/olsrd2.pid")" && return 0 ++ return 1 ++} ++ ++extra_command "running" "Check if service is running" ++ + . /lib/functions/oonf_init.sh +-- +2.35.1 + diff --git a/targets/generic b/targets/generic index 7e8c17f2bb..35590f6731 100644 --- a/targets/generic +++ b/targets/generic @@ -59,6 +59,8 @@ config('KERNEL_SECCOMP', false) -- use try_config, so enabling the package is still possible try_config('PACKAGE_kmod-mt7915e', false) +config('OONF_GENERIC_HTTP', true) + config('COLLECT_KERNEL_DEBUG', true) config('TARGET_MULTI_PROFILE', true)