diff --git a/.circleci/config.yml b/.circleci/config.yml index c2138d56..b9f93aef 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -79,14 +79,22 @@ jobs: name: Build ath79/nand command: make MAINTARGET=ath79 SUBTARGET=nand no_output_timeout: 1h + - run: + name: Build ipq40xx/generic + command: make MAINTARGET=ipq40xx SUBTARGET=generic + no_output_timeout: 2h - run: name: Build ipq40xx/mikrotik command: make MAINTARGET=ipq40xx SUBTARGET=mikrotik - no_output_timeout: 2h + no_output_timeout: 1h - run: name: Build x64/64 command: make MAINTARGET=x86 SUBTARGET=64 no_output_timeout: 2h + - run: + name: Build ramips/mt7621 + command: make MAINTARGET=ipq40xx SUBTARGET=mt7621 + no_output_timeout: 2h - run: name: Compress build files command: tar -cjf ~/${CIRCLE_BRANCH}_${ARTIFACTS_FILE} -C ${MY_WORKING_DIRECTORY}/${ARTIFACTS_DIR} . @@ -126,14 +134,22 @@ jobs: name: Build ath79/nand command: make MAINTARGET=ath79 SUBTARGET=nand no_output_timeout: 1h + - run: + name: Build ipq40xx/generic + command: make MAINTARGET=ipq40xx SUBTARGET=generic + no_output_timeout: 2h - run: name: Build ipq40xx/mikrotik command: make MAINTARGET=ipq40xx SUBTARGET=mikrotik - no_output_timeout: 2h + no_output_timeout: 1h - run: name: Build x64/64 command: make MAINTARGET=x86 SUBTARGET=64 no_output_timeout: 2h + - run: + name: Build ramips/mt7621 + command: make MAINTARGET=ipq40xx SUBTARGET=mt7621 + no_output_timeout: 2h - run: name: Compress build files command: tar -cjf ~/${CIRCLE_BRANCH}_${ARTIFACTS_FILE} -C ${MY_WORKING_DIRECTORY}/${ARTIFACTS_DIR} . diff --git a/Makefile b/Makefile index 8c06940e..3f04e84f 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,7 @@ feeds-update: stamp-clean-feeds-updated .stamp-feeds-updated cd $(OPENWRT_DIR); ./scripts/feeds install curl cd $(OPENWRT_DIR); ./scripts/feeds install ntpclient cd $(OPENWRT_DIR); ./scripts/feeds install socat - cd $(OPENWRT_DIR); ./scripts/feeds install luci-base + cd $(OPENWRT_DIR); ./scripts/feeds install luci-lib-base cd $(OPENWRT_DIR); ./scripts/feeds install luci-lib-nixio cd $(OPENWRT_DIR); ./scripts/feeds install luci-lib-ip cd $(OPENWRT_DIR); ./scripts/feeds install luci-lib-jsonc diff --git a/SUPPORTED_DEVICES.md b/SUPPORTED_DEVICES.md index fa3ed8cc..de9f13a4 100644 --- a/SUPPORTED_DEVICES.md +++ b/SUPPORTED_DEVICES.md @@ -15,9 +15,9 @@ The 'target' and 'subtarget' identify the directory in which to find the image o ## Mikrotik Model | SKUs | Band | Target | Subtarget | Image | RAM | Stability | Status :------ | :----: | :----: | :------: | :---------: | :-----: | :---: | :---------: | :------ -hAP ac lite
hAP ac lite TC | RB952Ui-5ac2nD
RB952Ui-5ac2nD-TC | 2 | ath79 | mikrotik | mikrotik-952ui-5ac2nd | 64MB | stable | released -hAP ac² | RBD52G-5HacD2HnD-TC | 2 | ipq40xx | mikrotik | mikrotik_hap-ac2 | 128MB | stable | released -hAP ac³ | RBD53iG-5HacD2HnD | 2 | ipq40xx | mikrotik | mikrotik_hap-ac3 | 256MB | stable | released +hAP ac lite
hAP ac lite TC | RB952Ui-5ac2nD
RB952Ui-5ac2nD-TC | 2 & 5 | ath79 | mikrotik | mikrotik-952ui-5ac2nd | 64MB | stable | released +hAP ac² | RBD52G-5HacD2HnD-TC | 2 & 5 | ipq40xx | mikrotik | mikrotik_hap-ac2 | 128MB | stable | released +hAP ac³ | RBD53iG-5HacD2HnD | 2 & 5 | ipq40xx | mikrotik | mikrotik_hap-ac3 | 256MB | stable | released SXTsq Lite2 | RBSXTsq2nD | 2 | ath79 | mikrotik | mikrotik-sxt-2nd | 64MB | stable | released SXTsq Lite5 | RBSXTsq5nD | 5 | ath79 | mikrotik | mikrotik-sxt-5nd | 64MB | stable | released SXTsq 5 High Power | RBSXTsq5HPnD | 5 | ath79 | mikrotik | mikrotik-sxt-5hpnd | 64MB | stable | released @@ -47,6 +47,7 @@ Bullet M2 XW || 2 | ath79 | generic | ubnt_bullet-m-xw | 64MB | untested | relea LiteAP 5AC | LAP-120
LAP-120-US
LBE-5AC-16-120
LBE-5AC-16-120-US | 5 | ath79 | generic | ubnt_lap-120 | 64MB | stable | released LiteBeam AC5 Gen2 | LBE-5AC
LBE-5AC-US | 5 | ath79 | generic | ubnt_litebeam-ac-gen2 | 64MB | stable | released LiteBeam M5 || 5 | ath79 | - | - | 64MB | untested | released +NanoBeam 2AC 13 (2WA) || 2 | ath79 | generic | ubnt_nanobeam-2ac-13 | 64MB | stable | nightly NanoBeam AC 5 (WA) || 5 | ath79 | generic | ubnt_nanobeam-ac | 64MB | untested | released NanoBeam AC 5 (XC) || 5 | ath79 | generic | ubnt_nanobeam-ac-xc | 64MB | stable | released NanoBeam AC 5 Gen 2 (WA) || 5 | ath79 | generic | ubnt_nanobeam-ac-gen2 | 128MB | stable | released @@ -131,6 +132,8 @@ Model | SKUs | Band | Target | Subtarget | Image | RAM | Stability | Status Shadow (16MB NOR) | GL-AR300M16
GL-AR300M16-Ext | 2 | ath79 | generic | glinet_gl-ar300m16 | 64MB | stable | released Shadow (128MB NAND) | GL-AR300M
GL-AR300M-Ext | 2 | ath79 | nand | gl-ar300m | 64MB | untested | released Mudi | GL-E750 | 2 | ath79 | nand | gl-e750 | 128MB | untested | released +Convexa-B | GL-B1300 | 2 & 5 | ipq40xx | generic | gl-b1300 | 256MB | untested | nightly +Beryl | GL-MT1300 | 2 & 5 | ramips | mt7621 | gl-mt1300 | 256MB | untested | nightly (4) **Sunset Devices** | | | | | | | | White | GL-AR150 | 2 | ath79 | generic | glinet_gl-ar150 | 64MB | stable | sunset (3) Microuter | GL-USB150 | 2 | ath79 | generic | glinet_gl-usb150 | 64MB | stable | sunset (3) @@ -146,6 +149,7 @@ Meraki MR-16 | MR16-HW | 5 | ath79 | - | - | 64MB | unsupported | **brick** 1. This device is supported for new installs. It can also be upgraded from 3.22.12.0 after first installing the [DangerousUpgrade package](https://github.com/kn6plv/DangerousUpgrade/raw/main/dangerousupgrade_0.1_all.ipk) to disable the firmware compatibility checks. Proceed carefully. 2. Tiny builds exclude support for *tunnels* and *WiFi AP* mode due to lack of resources. The relevant packages can be installed separately but this is not recommended. 3. These devices are no longer being manufactured by GL-iNET. They may not reboot reliably and you may need to power cycle them (several times) during an update. + 4. 20MHz channels only. Latest installation instructions are found at: https://docs.arednmesh.org/en/latest/ diff --git a/configs/ath79-generic.config b/configs/ath79-generic.config index 9fdb8e1f..c6a55310 100644 --- a/configs/ath79-generic.config +++ b/configs/ath79-generic.config @@ -20,11 +20,11 @@ CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_tplink_wbs210-v1=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_tplink_wbs210-v2=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_tplink_wbs510-v1=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_tplink_wbs510-v2=y -CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_bullet-m-xw=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_bullet-ac=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_bullet-m-xw=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_lap-120=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_litebeam-ac-gen2=y +CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_nanobeam-2ac-13=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_nanobeam-ac-gen2=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_nanobeam-ac=y CONFIG_TARGET_DEVICE_ath79_generic_DEVICE_ubnt_nanobeam-ac-xc=y diff --git a/configs/ath79-tiny.config b/configs/ath79-tiny.config index 338bce5e..765edea2 100644 --- a/configs/ath79-tiny.config +++ b/configs/ath79-tiny.config @@ -11,9 +11,11 @@ CONFIG_TARGET_DEVICE_ath79_tiny_DEVICE_ubnt_picostation-m=y CONFIG_KERNEL_CC_STACKPROTECTOR_NONE=y CONFIG_KERNEL_PRINTK=n CONFIG_PACKAGE_ATH_SPECTRAL=n -CONFIG_PACKAGE_wpad-mini=m -CONFIG_PACKAGE_vtun=m CONFIG_PACKAGE_ethtool=m CONFIG_PACKAGE_iperf3=m +CONFIG_PACKAGE_libustream-mbedtls=m +CONFIG_PACKAGE_vtun=m +CONFIG_PACKAGE_wireguard=m +CONFIG_PACKAGE_wireguard-tools=m CONFIG_PKG_CC_STACKPROTECTOR_NONE=y CONFIG_PKG_FORTIFY_SOURCE_NONE=y diff --git a/configs/common.config b/configs/common.config index 9ae75f6d..bbbadf2b 100644 --- a/configs/common.config +++ b/configs/common.config @@ -115,7 +115,6 @@ CONFIG_PACKAGE_lua-bit32=y CONFIG_PACKAGE_luaposix=n CONFIG_PACKAGE_luasocket=y CONFIG_PACKAGE_lua=y -CONFIG_PACKAGE_luci-base=n CONFIG_PACKAGE_luci-lib-base=y CONFIG_PACKAGE_luci-lib-ip=y CONFIG_PACKAGE_luci-lib-jsonc=y @@ -157,6 +156,8 @@ CONFIG_PACKAGE_wpad-mini=y CONFIG_PACKAGE_xinetd=n CONFIG_PACKAGE_zlib=m CONFIG_PACKAGE_zram-swap=n +CONFIG_PKG_ASLR_PIE_NONE=y +CONFIG_PKG_RELRO_NONE=y CONFIG_PREINITOPT=y CONFIG_SECCOMP=n CONFIG_STRIP_KERNEL_EXPORTS=y diff --git a/configs/ipq40xx-generic.config b/configs/ipq40xx-generic.config new file mode 100755 index 00000000..99dbbd26 --- /dev/null +++ b/configs/ipq40xx-generic.config @@ -0,0 +1,3 @@ +CONFIG_TARGET_ipq40xx=y +CONFIG_TARGET_ipq40xx_generic=y +CONFIG_TARGET_DEVICE_ipq40xx_generic_DEVICE_glinet_gl-b1300=y diff --git a/configs/ramips-mt7621.config b/configs/ramips-mt7621.config new file mode 100755 index 00000000..77b79631 --- /dev/null +++ b/configs/ramips-mt7621.config @@ -0,0 +1,3 @@ +CONFIG_TARGET_ramips=y +CONFIG_TARGET_ramips_mt7621=y +CONFIG_TARGET_DEVICE_ramips_mt7621_DEVICE_glinet_gl-mt1300=y diff --git a/feeds.conf b/feeds.conf index c4e5ef47..f5711628 100644 --- a/feeds.conf +++ b/feeds.conf @@ -1 +1 @@ -src-git arednpackages https://github.com/aredn/aredn_packages;develop +src-git arednpackages https://github.com/kn6plv/aredn_packages;working diff --git a/files/etc/config.mesh/network b/files/etc/config.mesh/network index 7d231807..bb822a3b 100644 --- a/files/etc/config.mesh/network +++ b/files/etc/config.mesh/network @@ -1,3 +1,7 @@ +#### Globals +config globals 'globals' + option packet_steering '1' + #### Loopback configuration config interface loopback option device "lo" diff --git a/files/etc/config/dhcp b/files/etc/config/dhcp index 9944f991..73d54c2d 100644 --- a/files/etc/config/dhcp +++ b/files/etc/config/dhcp @@ -3,6 +3,7 @@ config dhcp option start 5 option limit 20 option leasetime 12h + option force 1 config dhcp option interface wan diff --git a/files/etc/mesh-release b/files/etc/mesh-release index fd640a29..f87245c8 100644 --- a/files/etc/mesh-release +++ b/files/etc/mesh-release @@ -1 +1 @@ -KN6PLV-lz77decompression-d708f8c +KN6PLV-main-cdeb1b1d diff --git a/files/etc/permpkg b/files/etc/permpkg index 3526939d..30e7d748 100644 --- a/files/etc/permpkg +++ b/files/etc/permpkg @@ -4,6 +4,7 @@ ath10k-firmware-qca4019-ct ath10k-firmware-qca9887-ct ath10k-firmware-qca988x-ct base-files +bnx2-firmware busybox ca-bundle curl @@ -14,6 +15,9 @@ firewall4 fstools fwtool getrandom +grub2 +grub2-bios-setup +grub2-efi hostapd-common iperf3 iw @@ -22,12 +26,17 @@ jansson4 jshn jsonfilter kernel +kmod-amazon-ena +kmod-amd-xgbe kmod-ath kmod-ath10k-ct kmod-ath10k-ct-smallbuffers kmod-ath9k kmod-ath9k-common +kmod-bnx2 +kmod-button-hotplug kmod-cfg80211 +kmod-crypto-acompress kmod-crypto-aead kmod-crypto-ccm kmod-crypto-cmac @@ -43,42 +52,72 @@ kmod-crypto-null kmod-crypto-rng kmod-crypto-seqiv kmod-crypto-sha256 +kmod-crypto-sha512 +kmod-e1000 +kmod-e1000e +kmod-forcedeth +kmod-fs-vfat kmod-gpio-button-hotplug kmod-hwmon-core +kmod-i2c-algo-bit +kmod-i2c-core +kmod-igb +kmod-igc +kmod-input-core kmod-ipip kmod-iptunnel kmod-iptunnel4 +kmod-ixgbe kmod-leds-gpio kmod-ledtrig-gpio kmod-lib-crc32c +kmod-lib-lzo +kmod-libphy kmod-mac80211 +kmod-mdio +kmod-mdio-devres +kmod-mii kmod-nf-conntrack kmod-nf-flow kmod-nf-log kmod-nf-nat -kmod-nf-reject kmod-nfnetlink +kmod-nf-reject kmod-nft-core kmod-nft-fib kmod-nft-nat kmod-nft-offload kmod-nls-base +kmod-nls-cp437 +kmod-nls-iso8859-1 +kmod-nls-utf8 +kmod-phy-realtek +kmod-pps +kmod-ptp +kmod-r8169 +kmod-tg3 kmod-tun kmod-usb-core kmod-usb-dwc3 kmod-usb-dwc3-qcom +libblkid1 libblobmsg-json20220515 +libblobmsg-json20230523 libc libcurl4 +libf2fs6 libgcc1 +libiperf3 +libiwinfo20210430 +libiwinfo20230701 libiwinfo-data libiwinfo-lua -libiwinfo20210430 libjson-c5 libjson-script20220515 +libjson-script20230523 liblua5.1.5 -liblucihttp-lua liblucihttp0 +liblucihttp-lua liblzo2 libmbedtls12 libmnl0 @@ -86,13 +125,18 @@ libnftnl11 libnl-tiny1 libpthread librt +libsmartcols1 libubox20220515 -libubus-lua +libubox20230523 libubus20220601 -libuci-lua +libubus20230605 +libubus-lua libuci20130104 +libuci-lua libuclient20201210 libucode20220812 +libustream-mbedtls20201210 +libuuid1 libxtables12 logd lua @@ -101,6 +145,7 @@ luci-lib-base luci-lib-ip luci-lib-jsonc luci-lib-nixio +mkf2fs mtd netifd nftables-json @@ -114,8 +159,10 @@ olsrd-mod-txtinfo olsrd-mod-watchdog openwrt-keyring opkg +partx-utils procd prometheus-exporter +r8169-firmware rpcd rpcd-mod-file rpcd-mod-luci @@ -130,8 +177,11 @@ uci uclient-fetch ucode ucode-mod-fs +ucode-mod-nl80211 +ucode-mod-rtnl ucode-mod-ubus ucode-mod-uci +ucode-mod-uloop uhttpd urandom-seed urngd diff --git a/files/etc/radios.json b/files/etc/radios.json index 215c2c23..176e4996 100644 --- a/files/etc/radios.json +++ b/files/etc/radios.json @@ -20,6 +20,16 @@ "gl.inet gl-ar750s (nor/nand)": { "maxpower": "23" }, + "gl.inet gl-b1300": { + }, + "gl.inet gl-mt1300": { + "wlan0": { + "bandwidths": [ 20 ] + }, + "wlan1": { + "bandwidths": [ 20 ] + } + }, "tp-link cpe210 v1": { "maxpower": "23", "chanpower": { @@ -555,6 +565,14 @@ "maxpower": "22", "pwroffset": "4" }, + "0xe4f2": { + "name": "Ubiquiti NanoBeam 2AC 13 (2WA)", + "wlan0": { + "bandwidths": [ 10, 20 ], + "maxpower": 21, + "pwroffset": 6 + } + }, "0xe4f5": { "name": "Ubiquiti NanoBeam AC (XC)", "maxpower": "23", diff --git a/files/etc/uci-defaults/11_compat_version b/files/etc/uci-defaults/11_compat_version index ad2ed533..a8c86eba 100644 --- a/files/etc/uci-defaults/11_compat_version +++ b/files/etc/uci-defaults/11_compat_version @@ -1,15 +1,8 @@ #! /bin/sh -case "$(/usr/local/bin/get_boardid)" in - MikroTik\ hAP\ ac2|\ - MikroTik\ hAP\ ac3|\ - MikroTik\ SXTsq\ 5\ ac*|\ - MikroTik\ LDF\ 5\ ac*|\ - MikroTik\ LHG\ 5\ ac*) - sed -i "s/^compat_version = 1.0/compat_version = 1.1/" /etc/config.mesh/_setup - sed -i "s/^compat_version = 1.0/compat_version = 1.1/" /etc/config.mesh/_setup.default - /sbin/uci -q set system.@system[0].compat_version=1.1 - /sbin/uci -q commit system - ;; - *) - ;; -esac +VER=$(jsonfilter -e '@.system.compat_version' < /etc/board.json) +if [ "${VER}" != "" ]; then + sed -i "s/^compat_version = 1.0/compat_version = ${VER}/" /etc/config.mesh/_setup + sed -i "s/^compat_version = 1.0/compat_version = ${VER}/" /etc/config.mesh/_setup.default + uci -q set system.@system[0].compat_version=${VER} + uci -q commit system +fi diff --git a/files/etc/uci-defaults/94_update_permpkg b/files/etc/uci-defaults/94_update_permpkg new file mode 100755 index 00000000..76622a2e --- /dev/null +++ b/files/etc/uci-defaults/94_update_permpkg @@ -0,0 +1,4 @@ +#! /bin/sh +# Update the permanent package list so we can't uninstall any of the standard system + +opkg list-installed | sed s/\ .*// > /etc/permpkg diff --git a/files/usr/lib/lua/aredn/hardware.lua b/files/usr/lib/lua/aredn/hardware.lua index 44774527..0ede5101 100644 --- a/files/usr/lib/lua/aredn/hardware.lua +++ b/files/usr/lib/lua/aredn/hardware.lua @@ -71,6 +71,28 @@ function hardware.get_radio() return radio_json end +function hardware.get_radio_count() + local radio = hardware.get_radio() + if not radio then + return 0 + elseif radio.wlan0 then + if radio.wlan1 then + return 2 + else + return 1 + end + else + local count = 0 + if nixio.fs.stat("/sys/class/ieee80211") then + for file in nixio.fs.dir("/sys/class/ieee80211") + do + count = count + 1 + end + end + return count + end +end + function hardware.get_radio_intf(wifiintf) local radio = hardware.get_radio() if radio and radio[wifiintf] then @@ -277,19 +299,25 @@ function hardware.get_default_channel(wifiintf) for _, channel in ipairs(hardware.get_rfchannels(wifiintf)) do if channel.frequency == 912 then - return { channel = 5, bandwidth = 5, rfband = "900MHz" } + return { channel = 5, bandwidth = 5, band = "900MHz" } end + local bws = {} + for _, v in ipairs(hardware.get_rfbandwidths(wifiintf)) + do + bws[v] = v + end + local bw = bws[10] or bws[20] or bws[5] or 0 if channel.frequency == 2397 then - return { channel = -2, bandwidth = 10, rfband = "2.4GHz" } + return { channel = -2, bandwidth = bw, band = "2.4GHz" } end if channel.frequency == 2412 then - return { channel = 1, bandwidth = 10, rfband = "2.4GHz" } + return { channel = 1, bandwidth = bw, band = "2.4GHz" } end if channel.frequency == 3420 then - return { channel = 84, bandwidth = 10, rfband = "3GHz" } + return { channel = 84, bandwidth = bw, band = "3GHz" } end if channel.frequency == 5745 then - return { channel = 149, bandwidth = 10, rfband = "5GHz" } + return { channel = 149, bandwidth = bw, band = "5GHz" } end end return nil @@ -320,7 +348,7 @@ function hardware.get_rfchannels(wifiintf) end for line in f:lines() do - local freq, num = line:match("(%d+%.%d+) GHz %(Channel (%-?%d+)%)") + local freq, num = line:match("(%d+%.%d+) GHz %(Band: .*, Channel (%-?%d+)%)") if freq and not line:match("restricted") and not line:match("disabled") then freq = tonumber("" .. freq:gsub("%.", "")) + freq_adjust if freq >= freq_min and freq <= freq_max then diff --git a/files/usr/lib/lua/aredn/utils.lua b/files/usr/lib/lua/aredn/utils.lua index 10889003..d2650caa 100755 --- a/files/usr/lib/lua/aredn/utils.lua +++ b/files/usr/lib/lua/aredn/utils.lua @@ -37,7 +37,7 @@ local nxo = require("nixio") local ipc = require("luci.ip") -require("luci.http") +require('luci.ohttp') require("uci") function round2(num, idp) diff --git a/files/usr/lib/lua/luci/ohttp.lua b/files/usr/lib/lua/luci/ohttp.lua new file mode 100755 index 00000000..20b55f28 --- /dev/null +++ b/files/usr/lib/lua/luci/ohttp.lua @@ -0,0 +1,554 @@ +-- Copyright 2008 Steven Barth +-- Copyright 2010-2018 Jo-Philipp Wich +-- Licensed to the public under the Apache License 2.0. + +local util = require "luci.util" +local coroutine = require "coroutine" +local table = require "table" +local lhttp = require "lucihttp" +local nixio = require "nixio" +local ltn12 = require "luci.ltn12" + +local table, ipairs, pairs, type, tostring, tonumber, error = + table, ipairs, pairs, type, tostring, tonumber, error + +module "luci.http" + +HTTP_MAX_CONTENT = 1024*100 -- 100 kB maximum content size + +context = util.threadlocal() + +Request = util.class() +function Request.__init__(self, env, sourcein, sinkerr) + self.input = sourcein + self.error = sinkerr + + + -- File handler nil by default to let .content() work + self.filehandler = nil + + -- HTTP-Message table + self.message = { + env = env, + headers = {}, + params = urldecode_params(env.QUERY_STRING or ""), + } + + self.parsed_input = false +end + +function Request.formvalue(self, name, noparse) + if not noparse and not self.parsed_input then + self:_parse_input() + end + + if name then + return self.message.params[name] + else + return self.message.params + end +end + +function Request.formvaluetable(self, prefix) + local vals = {} + prefix = prefix and prefix .. "." or "." + + if not self.parsed_input then + self:_parse_input() + end + + local void = self.message.params[nil] + for k, v in pairs(self.message.params) do + if k:find(prefix, 1, true) == 1 then + vals[k:sub(#prefix + 1)] = tostring(v) + end + end + + return vals +end + +function Request.content(self) + if not self.parsed_input then + self:_parse_input() + end + + return self.message.content, self.message.content_length +end + +function Request.getcookie(self, name) + return lhttp.header_attribute("cookie; " .. (self:getenv("HTTP_COOKIE") or ""), name) +end + +function Request.getenv(self, name) + if name then + return self.message.env[name] + else + return self.message.env + end +end + +function Request.setfilehandler(self, callback) + self.filehandler = callback + + if not self.parsed_input then + return + end + + -- If input has already been parsed then uploads are stored as unlinked + -- temporary files pointed to by open file handles in the parameter + -- value table. Loop all params, and invoke the file callback for any + -- param with an open file handle. + local name, value + for name, value in pairs(self.message.params) do + if type(value) == "table" then + while value.fd do + local data = value.fd:read(1024) + local eof = (not data or data == "") + + callback(value, data, eof) + + if eof then + value.fd:close() + value.fd = nil + end + end + end + end +end + +function Request._parse_input(self) + parse_message_body( + self.input, + self.message, + self.filehandler + ) + self.parsed_input = true +end + +function close() + if not context.eoh then + context.eoh = true + coroutine.yield(3) + end + + if not context.closed then + context.closed = true + coroutine.yield(5) + end +end + +function content() + return context.request:content() +end + +function formvalue(name, noparse) + return context.request:formvalue(name, noparse) +end + +function formvaluetable(prefix) + return context.request:formvaluetable(prefix) +end + +function getcookie(name) + return context.request:getcookie(name) +end + +-- or the environment table itself. +function getenv(name) + return context.request:getenv(name) +end + +function setfilehandler(callback) + return context.request:setfilehandler(callback) +end + +function header(key, value) + if not context.headers then + context.headers = {} + end + context.headers[key:lower()] = value + coroutine.yield(2, key, value) +end + +function prepare_content(mime) + if not context.headers or not context.headers["content-type"] then + if mime == "application/xhtml+xml" then + if not getenv("HTTP_ACCEPT") or + not getenv("HTTP_ACCEPT"):find("application/xhtml+xml", nil, true) then + mime = "text/html; charset=UTF-8" + end + header("Vary", "Accept") + end + header("Content-Type", mime) + end +end + +function source() + return context.request.input +end + +function status(code, message) + code = code or 200 + message = message or "OK" + context.status = code + coroutine.yield(1, code, message) +end + +-- This function is as a valid LTN12 sink. +-- If the content chunk is nil this function will automatically invoke close. +function write(content, src_err) + if not content then + if src_err then + error(src_err) + else + close() + end + return true + elseif #content == 0 then + return true + else + if not context.eoh then + if not context.status then + status() + end + if not context.headers or not context.headers["content-type"] then + header("Content-Type", "text/html; charset=utf-8") + end + if not context.headers["cache-control"] then + header("Cache-Control", "no-cache") + header("Expires", "0") + end + if not context.headers["x-frame-options"] then + header("X-Frame-Options", "SAMEORIGIN") + end + if not context.headers["x-xss-protection"] then + header("X-XSS-Protection", "1; mode=block") + end + if not context.headers["x-content-type-options"] then + header("X-Content-Type-Options", "nosniff") + end + + context.eoh = true + coroutine.yield(3) + end + coroutine.yield(4, content) + return true + end +end + +function splice(fd, size) + coroutine.yield(6, fd, size) +end + +function redirect(url) + if url == "" then url = "/" end + status(302, "Found") + header("Location", url) + close() +end + +function build_querystring(q) + local s, n, k, v = {}, 1, nil, nil + + for k, v in pairs(q) do + s[n+0] = (n == 1) and "?" or "&" + s[n+1] = util.urlencode(k) + s[n+2] = "=" + s[n+3] = util.urlencode(v) + n = n + 4 + end + + return table.concat(s, "") +end + +urldecode = util.urldecode + +urlencode = util.urlencode + +function write_json(x) + util.serialize_json(x, write) +end + +-- from given url or string. Returns a table with urldecoded values. +-- Simple parameters are stored as string values associated with the parameter +-- name within the table. Parameters with multiple values are stored as array +-- containing the corresponding values. +function urldecode_params(url, tbl) + local parser, name + local params = tbl or { } + + parser = lhttp.urlencoded_parser(function (what, buffer, length) + if what == parser.TUPLE then + name, value = nil, nil + elseif what == parser.NAME then + name = lhttp.urldecode(buffer) + elseif what == parser.VALUE and name then + params[name] = lhttp.urldecode(buffer) or "" + end + + return true + end) + + if parser then + parser:parse((url or ""):match("[^?]*$")) + parser:parse(nil) + end + + return params +end + +-- separated by "&". Tables are encoded as parameters with multiple values by +-- repeating the parameter name with each value. +function urlencode_params(tbl) + local k, v + local n, enc = 1, {} + for k, v in pairs(tbl) do + if type(v) == "table" then + local i, v2 + for i, v2 in ipairs(v) do + if enc[1] then + enc[n] = "&" + n = n + 1 + end + + enc[n+0] = lhttp.urlencode(k) + enc[n+1] = "=" + enc[n+2] = lhttp.urlencode(v2) + n = n + 3 + end + else + if enc[1] then + enc[n] = "&" + n = n + 1 + end + + enc[n+0] = lhttp.urlencode(k) + enc[n+1] = "=" + enc[n+2] = lhttp.urlencode(v) + n = n + 3 + end + end + + return table.concat(enc, "") +end + +-- Content-Type. Stores all extracted data associated with its parameter name +-- in the params table within the given message object. Multiple parameter +-- values are stored as tables, ordinary ones as strings. +-- If an optional file callback function is given then it is fed with the +-- file contents chunk by chunk and only the extracted file name is stored +-- within the params table. The callback function will be called subsequently +-- with three arguments: +-- o Table containing decoded (name, file) and raw (headers) mime header data +-- o String value containing a chunk of the file data +-- o Boolean which indicates whether the current chunk is the last one (eof) +function mimedecode_message_body(src, msg, file_cb) + local parser, header, field + local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil) + + parser, err = lhttp.multipart_parser(msg.env.CONTENT_TYPE, function (what, buffer, length) + if what == parser.PART_INIT then + field = { } + + elseif what == parser.HEADER_NAME then + header = buffer:lower() + + elseif what == parser.HEADER_VALUE and header then + if header:lower() == "content-disposition" and + lhttp.header_attribute(buffer, nil) == "form-data" + then + field.name = lhttp.header_attribute(buffer, "name") + field.file = lhttp.header_attribute(buffer, "filename") + field[1] = field.file + end + + if field.headers then + field.headers[header] = buffer + else + field.headers = { [header] = buffer } + end + + elseif what == parser.PART_BEGIN then + return not field.file + + elseif what == parser.PART_DATA and field.name and length > 0 then + if field.file then + if file_cb then + file_cb(field, buffer, false) + msg.params[field.name] = msg.params[field.name] or field + else + if not field.fd then + field.fd = nixio.mkstemp(field.name) + end + + if field.fd then + field.fd:write(buffer) + msg.params[field.name] = msg.params[field.name] or field + end + end + else + field.value = buffer + end + + elseif what == parser.PART_END and field.name then + if field.file and msg.params[field.name] then + if file_cb then + file_cb(field, "", true) + elseif field.fd then + field.fd:seek(0, "set") + end + else + local val = msg.params[field.name] + + if type(val) == "table" then + val[#val+1] = field.value or "" + elseif val ~= nil then + msg.params[field.name] = { val, field.value or "" } + else + msg.params[field.name] = field.value or "" + end + end + + field = nil + + elseif what == parser.ERROR then + err = buffer + end + + return true + end, HTTP_MAX_CONTENT) + + return ltn12.pump.all(src, function (chunk) + len = len + (chunk and #chunk or 0) + + if maxlen and len > maxlen + 2 then + return nil, "Message body size exceeds Content-Length" + end + + if not parser or not parser:parse(chunk) then + return nil, err + end + + return true + end) +end + +-- Content-Type. Stores all extracted data associated with its parameter name +-- in the params table within the given message object. Multiple parameter +-- values are stored as tables, ordinary ones as strings. +function urldecode_message_body(src, msg) + local err, name, value, parser + local len, maxlen = 0, tonumber(msg.env.CONTENT_LENGTH or nil) + + parser = lhttp.urlencoded_parser(function (what, buffer, length) + if what == parser.TUPLE then + name, value = nil, nil + elseif what == parser.NAME then + name = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) + elseif what == parser.VALUE and name then + local val = msg.params[name] + + if type(val) == "table" then + val[#val+1] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" + elseif val ~= nil then + msg.params[name] = { val, lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" } + else + msg.params[name] = lhttp.urldecode(buffer, lhttp.DECODE_PLUS) or "" + end + elseif what == parser.ERROR then + err = buffer + end + + return true + end, HTTP_MAX_CONTENT) + + return ltn12.pump.all(src, function (chunk) + len = len + (chunk and #chunk or 0) + + if maxlen and len > maxlen + 2 then + return nil, "Message body size exceeds Content-Length" + elseif len > HTTP_MAX_CONTENT then + return nil, "Message body size exceeds maximum allowed length" + end + + if not parser or not parser:parse(chunk) then + return nil, err + end + + return true + end) +end + +-- This function will examine the Content-Type within the given message object +-- to select the appropriate content decoder. +-- Currently the application/x-www-urlencoded and application/form-data +-- mime types are supported. If the encountered content encoding can't be +-- handled then the whole message body will be stored unaltered as "content" +-- property within the given message object. +function parse_message_body(src, msg, filecb) + if msg.env.CONTENT_LENGTH or msg.env.REQUEST_METHOD == "POST" then + local ctype = lhttp.header_attribute(msg.env.CONTENT_TYPE, nil) + + -- Is it multipart/mime ? + if ctype == "multipart/form-data" then + return mimedecode_message_body(src, msg, filecb) + + -- Is it application/x-www-form-urlencoded ? + elseif ctype == "application/x-www-form-urlencoded" then + return urldecode_message_body(src, msg) + + end + + -- Unhandled encoding + -- If a file callback is given then feed it chunk by chunk, else + -- store whole buffer in message.content + local sink + + -- If we have a file callback then feed it + if type(filecb) == "function" then + local meta = { + name = "raw", + encoding = msg.env.CONTENT_TYPE + } + sink = function( chunk ) + if chunk then + return filecb(meta, chunk, false) + else + return filecb(meta, nil, true) + end + end + -- ... else append to .content + else + msg.content = "" + msg.content_length = 0 + + sink = function( chunk ) + if chunk then + if ( msg.content_length + #chunk ) <= HTTP_MAX_CONTENT then + msg.content = msg.content .. chunk + msg.content_length = msg.content_length + #chunk + return true + else + return nil, "POST data exceeds maximum allowed length" + end + end + return true + end + end + + -- Pump data... + while true do + local ok, err = ltn12.pump.step( src, sink ) + + if not ok and err then + return nil, err + elseif not ok then -- eof + return true + end + end + + return true + end + + return false +end diff --git a/files/usr/local/bin/mgr/watchdog.lua b/files/usr/local/bin/mgr/watchdog.lua index 4fbd7b42..42c0f9a9 100644 --- a/files/usr/local/bin/mgr/watchdog.lua +++ b/files/usr/local/bin/mgr/watchdog.lua @@ -65,7 +65,7 @@ end function watchdog() while true do - wait_for_ticks(21) + wait_for_ticks(223) local pid = read_all(pidfile) if pid and nixio.fs.stat("/proc/" .. pid) then diff --git a/files/usr/local/bin/mgr/wireless_monitor.lua b/files/usr/local/bin/mgr/wireless_monitor.lua index 89f5e452..14c53c1d 100755 --- a/files/usr/local/bin/mgr/wireless_monitor.lua +++ b/files/usr/local/bin/mgr/wireless_monitor.lua @@ -104,12 +104,6 @@ function M.reset_network(mode) elseif mode == "scan-all" then os.execute(IW .. " " .. wifi .. " scan > /dev/null 2>&1") os.execute(IW .. " " .. wifi .. " scan passive > /dev/null 2>&1") - elseif mode == "reset" then - if chipset == "ath9k" then - write_all("/sys/kernel/debug/ieee80211/" .. phy .. "/ath9k/reset", "1") - else - write_all("/sys/kernel/debug/ieee80211/" .. phy .. "/ath10k/simulate_fw_crash", "hw-restart") - end else log:write("-- unknown") end diff --git a/files/usr/local/bin/node-setup b/files/usr/local/bin/node-setup index 55f01a6d..00b47dbd 100755 --- a/files/usr/local/bin/node-setup +++ b/files/usr/local/bin/node-setup @@ -764,6 +764,7 @@ c:commit("dhcp") local config = "" local ifacenum = 0 local ifacecount = 0 +local devpaths = {} if nixio.fs.stat(ieee80211) then for devname in nixio.fs.dir(ieee80211) do @@ -778,6 +779,11 @@ if nixio.fs.stat(ieee80211) then if devpath:match("^platform.*/pci.*") then devpath = devpath:match("^platform/(.*)") end + local devpathc = devpaths[devpath] or 0 + devpaths[devpath] = devpathc + 1 + if devpathc > 0 then + devpath = devpath .. "+" .. devpathc + end local is_mesh_rf = false local htmode = "HT20" local disabled = "0" diff --git a/files/www/cgi-bin/admin b/files/www/cgi-bin/admin index 75ab656b..8b621a71 100755 --- a/files/www/cgi-bin/admin +++ b/files/www/cgi-bin/admin @@ -169,7 +169,7 @@ end local parms = {} local firmfile = "" if os.getenv("REQUEST_METHOD") == "POST" then - require('luci.http') + require('luci.ohttp') local request = luci.http.Request(nixio.getenv(), function() local v = io.read(1024) diff --git a/files/www/cgi-bin/advancedconfig b/files/www/cgi-bin/advancedconfig index 04554824..f576ed0f 100755 --- a/files/www/cgi-bin/advancedconfig +++ b/files/www/cgi-bin/advancedconfig @@ -765,7 +765,7 @@ end -- read_postdata local parms = {} if os.getenv("REQUEST_METHOD") == "POST" then - require('luci.http') + require('luci.ohttp') local request = luci.http.Request(nixio.getenv(), function() local v = io.read(1024) diff --git a/files/www/cgi-bin/advancednetwork b/files/www/cgi-bin/advancednetwork index ddac3722..19296ccf 100755 --- a/files/www/cgi-bin/advancednetwork +++ b/files/www/cgi-bin/advancednetwork @@ -300,7 +300,7 @@ local layout = layouts[get_board_type] local configs = {} if os.getenv("REQUEST_METHOD") == "POST" then - require('luci.http') + require('luci.ohttp') local request = luci.http.Request(nixio.getenv(), function() local v = io.read(1024) diff --git a/files/www/cgi-bin/mesh b/files/www/cgi-bin/mesh index 753b0a7d..a761877d 100755 --- a/files/www/cgi-bin/mesh +++ b/files/www/cgi-bin/mesh @@ -90,7 +90,7 @@ end -- post data if os.getenv("REQUEST_METHOD") == "POST" then - require('luci.http') + require('luci.ohttp') local request = luci.http.Request(nixio.getenv(), function() local v = io.read(1024) diff --git a/files/www/cgi-bin/ports b/files/www/cgi-bin/ports index bddfd204..94515080 100755 --- a/files/www/cgi-bin/ports +++ b/files/www/cgi-bin/ports @@ -60,7 +60,7 @@ end -- post_data local parms = {} if os.getenv("REQUEST_METHOD") == "POST" then - require('luci.http') + require('luci.ohttp') local request = luci.http.Request(nixio.getenv(), function() local v = io.read(1024) diff --git a/files/www/cgi-bin/scan b/files/www/cgi-bin/scan index f1e62e74..a97263fd 100755 --- a/files/www/cgi-bin/scan +++ b/files/www/cgi-bin/scan @@ -149,7 +149,7 @@ end -- scan end if os.getenv("REQUEST_METHOD") == "POST" then - require('luci.http') + require('luci.ohttp') local request = luci.http.Request(nixio.getenv(), function() local v = io.read(1024) diff --git a/files/www/cgi-bin/setup b/files/www/cgi-bin/setup index f386b51a..207d0cf1 100755 --- a/files/www/cgi-bin/setup +++ b/files/www/cgi-bin/setup @@ -41,7 +41,7 @@ require("aredn.http") require("aredn.utils") require("aredn.hardware") require("uci") -require('luci.http') +require('luci.ohttp') local html = require("aredn.html") local aredn_info = require("aredn.info") @@ -176,7 +176,7 @@ passwd1 = "" passwd2 = "" wifi_intf = "" -local phycount = tonumber(capture("ls -1d /sys/class/ieee80211/* | wc -l"):chomp()) +local phycount = aredn.hardware.get_radio_count() local radio_name = (aredn.hardware.get_radio() or {}).name or "" local M9model = radio_name:match("M9") local M3model = radio_name:match("M3") @@ -428,48 +428,53 @@ if (parms.button_apply or parms.button_save) and wifi_enable == "1" then os.execute("iw dev " .. wifi_intf .. " set txpower fixed " .. wifi_txpower .. "00 >/dev/null 2>&1") end -if parms.button_updatelocation then +if (parms.button_updatelocation or parms.button_save) then -- process gridsquare local cursora = uci.cursor(); local cursorb = uci.cursor("/etc/config.mesh") - if parms.gridsquare ~= "" then - if parms.gridsquare:match("^[A-Z][A-Z]%d%d[a-z][a-z]$") then - cursora:set("aredn", "@location[0]", "gridsquare", parms.gridsquare) - cursorb:set("aredn", "@location[0]", "gridsquare", parms.gridsquare) - out("Gridsquare updated.") + if (cursora:get("aredn", "@location[0]", "gridsquare") or "") ~= parms.gridsquare then + if parms.gridsquare ~= "" then + if parms.gridsquare:match("^[A-Z][A-Z]%d%d[a-z][a-z]$") then + cursora:set("aredn", "@location[0]", "gridsquare", parms.gridsquare) + cursorb:set("aredn", "@location[0]", "gridsquare", parms.gridsquare) + out("Gridsquare updated.") + else + err("ERROR: Gridsquare format is: 2-uppercase letters, 2-digits, 2-lowercase letters. (AB12cd)") + end else - err("ERROR: Gridsquare format is: 2-uppercase letters, 2-digits, 2-lowercase letters. (AB12cd)") + cursora:set("aredn", "@location[0]", "gridsquare", "") + cursorb:set("aredn", "@location[0]", "gridsquare", "") + out("Gridsquare purged.") end - else - cursora:set("aredn", "@location[0]", "gridsquare", "") - cursorb:set("aredn", "@location[0]", "gridsquare", "") - out("Gridsquare purged.") end -- process lat/lng - if parms.latitude ~= "" and parms.longitude ~= "" then - if parms.latitude:match("^[-+]?%d%d?%.%d+$") and parms.longitude:match("^[-+]?%d%d?%d?%.%d+$") then - if tonumber(parms.latitude) >= -90 and tonumber(parms.latitude) <= 90 and tonumber(parms.longitude) >= -180 and tonumber(parms.longitude) <= 180 then - cursora:set("aredn", "@location[0]", "lat", parms.latitude) - cursorb:set("aredn", "@location[0]", "lat", parms.latitude) - cursora:set("aredn", "@location[0]", "lon", parms.longitude) - cursorb:set("aredn", "@location[0]", "lon", parms.longitude) - out("Lat/lon updated.") + if (cursora:get("aredn", "@location[0]", "lat") or "") ~= parms.latitude or (cursora:get("aredn", "@location[0]", "lon") or "") ~= parms.longitude then + if parms.latitude ~= "" and parms.longitude ~= "" then + if parms.latitude:match("^[-+]?%d%d?%.%d+$") and parms.longitude:match("^[-+]?%d%d?%d?%.%d+$") then + if tonumber(parms.latitude) >= -90 and tonumber(parms.latitude) <= 90 and tonumber(parms.longitude) >= -180 and tonumber(parms.longitude) <= 180 then + cursora:set("aredn", "@location[0]", "lat", parms.latitude) + cursorb:set("aredn", "@location[0]", "lat", parms.latitude) + cursora:set("aredn", "@location[0]", "lon", parms.longitude) + cursorb:set("aredn", "@location[0]", "lon", parms.longitude) + out("Lat/lon updated.") + else + err("ERROR: Lat/lon values must be between -90/90 and -180/180, respectively.") + end else - err("ERROR: Lat/lon values must be between -90/90 and -180/180, respectively.") + err("ERROR: Lat/lon format is decimal: (ex. 30.121456 or -95.911154).") end else - err("ERROR: Lat/lon format is decimal: (ex. 30.121456 or -95.911154).") + cursora:set("aredn", "@location[0]", "lat", "") + cursorb:set("aredn", "@location[0]", "lat", "") + cursora:set("aredn", "@location[0]", "lon", "") + cursorb:set("aredn", "@location[0]", "lon", "") + out("Lat/lon purged.") end - else - cursora:set("aredn", "@location[0]", "lat", "") - cursorb:set("aredn", "@location[0]", "lat", "") - cursora:set("aredn", "@location[0]", "lon", "") - cursorb:set("aredn", "@location[0]", "lon", "") - out("Lat/lon purged.") end cursora:commit("aredn") cursorb:commit("aredn") + cursor = cursora end -- retrieve location data @@ -909,7 +914,19 @@ function foundLocation(position) { } function noLocation() { - alert('Could not find location. Try pinning it on the map.'); + const req = new XMLHttpRequest(); + req.addEventListener("load", function() { + try { + const json = JSON.parse(this.responseText); + foundLocation({ coords: { latitude: json.lat, longitude: json.lon }}) + return; + } + catch (_) { + } + alert('Could not find location. Try pinning it on the map.'); + }); + req.open("GET", "http://ip-api.com/json"); + req.send(); } function updDist(x) { @@ -1441,14 +1458,11 @@ html.print("") html.print("") html.print("") html.print("
Optional Settings
Latitude") -html.print(" ") +local locdisabled = pingOK and "" or "disabled" +html.print(" ") html.print("") -html.print("  ") -if pingOK then - html.print(" ") -else - html.print(" ") -end +html.print("  ") +html.print(" ") html.print("
LongitudeGrid Square

Timezone