mirror of
https://github.com/Telecominfraproject/olg-installer.git
synced 2026-03-20 03:39:11 +00:00
Compare commits
4 Commits
v0.0.3
...
minimal-vy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29e3068c0e | ||
|
|
e74f615c36 | ||
|
|
a9173bf602 | ||
|
|
e43625c24e |
5
README.md
Normal file
5
README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# OLG Ucentral Client SDK
|
||||
|
||||
This is a sdk to build ucentral client image to support olg schema and configure VYOS. It can be used to validate OLG's end-to-end working, from accepting configuration from the Cloud controller to configuring VyOS.
|
||||
- vyos/config_prepare.uc: Conversion from templates to VyOS text style configuration.
|
||||
- vyos/https_server_api.uc: This will call VyOS retrieve and load APIs.
|
||||
8
olg-ucentral-client-sdk/Makefile
Normal file
8
olg-ucentral-client-sdk/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
.PHONY: all purge
|
||||
|
||||
all:
|
||||
./dock-run.sh ./build.sh $(TARGET)
|
||||
|
||||
purge:
|
||||
cd openwrt && rm -rf * && rm -rf .*
|
||||
@echo Done
|
||||
83
olg-ucentral-client-sdk/README.md
Normal file
83
olg-ucentral-client-sdk/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# OpenWiFi AP NOS
|
||||
|
||||
OpenWrt-based access point network operating system (AP NOS) for TIP OpenWiFi.
|
||||
Read more at [openwifi.tip.build](https://openwifi.tip.build/).
|
||||
|
||||
## Building
|
||||
|
||||
### Setting up your build machine
|
||||
|
||||
Building requires a recent Linux installation. Older systems without Python 3.7
|
||||
will have trouble. See this guide for details:
|
||||
https://openwrt.org/docs/guide-developer/toolchain/beginners-build-guide
|
||||
|
||||
Install build packages on Debian/Ubuntu (or see above guide for other systems):
|
||||
```
|
||||
sudo apt install build-essential libncurses5-dev gawk git libssl-dev gettext zlib1g-dev swig unzip time rsync python3 python3-setuptools python3-yaml
|
||||
```
|
||||
|
||||
### Doing a native build on Linux
|
||||
|
||||
Use `./build.sh <target>`, or follow the manual steps below:
|
||||
|
||||
1. Clone and set up the tree. This will create an `openwrt/` directory.
|
||||
```shell
|
||||
./setup.py --setup # for subsequent builds, use --rebase instead
|
||||
```
|
||||
|
||||
2. Select the profile and base package selection. This setup will install the
|
||||
feeds and packages and generate the `.config` file.
|
||||
```shell
|
||||
cd openwrt
|
||||
./scripts/gen_config.py linksys_ea8300
|
||||
```
|
||||
|
||||
3. Build the tree (replace `-j 8` with the number of cores to use).
|
||||
```shell
|
||||
make -j 8 V=s
|
||||
```
|
||||
|
||||
### Build output
|
||||
|
||||
The build results are located in the `openwrt/bin/` directory:
|
||||
|
||||
| Type | Path |
|
||||
| ---------------- | ---------------------------------------------------- |
|
||||
| Firmware images | `openwrt/bin/targets/<target>/<subtarget>/` |
|
||||
| Kernel modules | `openwrt/bin/targets/<target>/<subtarget>/packages/` |
|
||||
| Package binaries | `openwrt/bin/packages/<platform>/<feed>/` |
|
||||
|
||||
## Developer Notes
|
||||
|
||||
### Branching model
|
||||
|
||||
- `main` - Stable dev branch
|
||||
- `next` - Integration branch
|
||||
- `staging-*` - Feature/bug branches
|
||||
- `release/v#.#.#` - Release branches (*major.minor.patch*)
|
||||
|
||||
### Repository structure
|
||||
|
||||
Build files:
|
||||
- `Makefile` - Calls Docker environment per target
|
||||
- `dock-run.sh` - Dockerized build environment
|
||||
- `docker/Dockerfile` - Dockerfile for build image
|
||||
- `build.sh` - Build script
|
||||
- `setup.py` - Clone and set up the tree
|
||||
- `config.yml` - Specifies OpenWrt version and patches to apply
|
||||
|
||||
Directories:
|
||||
- `feeds/` - OpenWiFi feeds
|
||||
- `patches/` - OpenWiFi patches applied during builds
|
||||
- `profiles/` - Per-target kernel configs, packages, and feeds
|
||||
- [wifi-ax](profiles/wifi-ax.yml): Wi-Fi AX packages
|
||||
- [ucentral-ap](profiles/ucentral-ap.yml): uCentral packages
|
||||
- [x64_vm](profiles/x64_vm.yml): x86-64 VM image
|
||||
|
||||
### uCentral packages
|
||||
|
||||
AP-NOS packages implementing the uCentral protocol include the following
|
||||
repositories (refer to the [ucentral](feeds/ucentral/) feed for a full list):
|
||||
- ucentral-client: https://github.com/Telecominfraproject/wlan-ucentral-client
|
||||
- ucentral-schema: https://github.com/Telecominfraproject/wlan-ucentral-schema
|
||||
- ucentral-wifi: https://github.com/blogic/ucentral-wifi
|
||||
27
olg-ucentral-client-sdk/build.sh
Executable file
27
olg-ucentral-client-sdk/build.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
ROOT_PATH=${PWD}
|
||||
BUILD_DIR=${ROOT_PATH}/openwrt
|
||||
TARGET=${1}
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Error: please specify TARGET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! "$(ls -A $BUILD_DIR)" ]; then
|
||||
python3 setup.py --setup || exit 1
|
||||
|
||||
else
|
||||
python3 setup.py --rebase
|
||||
echo "### OpenWrt repo already setup"
|
||||
fi
|
||||
|
||||
cd ${BUILD_DIR}
|
||||
./scripts/gen_config.py ${TARGET} || exit 1
|
||||
cd -
|
||||
|
||||
echo "### Building image ..."
|
||||
cd $BUILD_DIR
|
||||
make -j$(nproc) V=s
|
||||
7
olg-ucentral-client-sdk/config.yml
Normal file
7
olg-ucentral-client-sdk/config.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
repo: https://github.com/openwrt/openwrt.git
|
||||
branch: openwrt-23.05
|
||||
revision: e92cf0c46ffe3ac7fca936c18577bfb19eb4ce9e
|
||||
output_dir: ./output
|
||||
|
||||
patch_folders:
|
||||
- patches
|
||||
9
olg-ucentral-client-sdk/dock-run.sh
Executable file
9
olg-ucentral-client-sdk/dock-run.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
tag=$(echo ${PWD} | tr / - | cut -b2- | tr A-Z a-z)
|
||||
groups=$(id -G | xargs -n1 echo -n " --group-add ")
|
||||
params="-v ${PWD}:${PWD} --rm -w ${PWD} -u"$(id -u):$(id -g)" $groups -v/etc/passwd:/etc/passwd:ro -v/etc/group:/etc/group:ro -v$HOME/.gitconfig:$HOME/.gitconfig:ro ${tag}"
|
||||
|
||||
docker build --tag=${tag} docker
|
||||
|
||||
docker run $params $@
|
||||
12
olg-ucentral-client-sdk/docker/Dockerfile
Normal file
12
olg-ucentral-client-sdk/docker/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata \
|
||||
&& apt-get install -y \
|
||||
time git-core build-essential gcc-multilib clang \
|
||||
libncurses5-dev zlib1g-dev gawk flex gettext wget unzip python \
|
||||
python3 python3-pip python3-yaml libssl-dev rsync \
|
||||
&& apt-get clean
|
||||
RUN git config --global user.email "you@example.com"
|
||||
RUN git config --global user.name "Your Name"
|
||||
RUN pip3 install kconfiglib
|
||||
30
olg-ucentral-client-sdk/feeds/tip/certificates/Makefile
Normal file
30
olg-ucentral-client-sdk/feeds/tip/certificates/Makefile
Normal file
@@ -0,0 +1,30 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=certificates
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/certificates
|
||||
SECTION:=ucentral
|
||||
CATEGORY:=uCentral
|
||||
TITLE:=TIP DigiCer certificate store
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Compile/Default
|
||||
|
||||
endef
|
||||
Build/Compile = $(Build/Compile/Default)
|
||||
|
||||
define Package/certificates/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,certificates))
|
||||
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/etc/init.d/early_boot
Executable file
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/etc/init.d/early_boot
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=09
|
||||
|
||||
copy_certificates() {
|
||||
[ -f /certificates/key.pem ] || return
|
||||
|
||||
cp /certificates/*.pem /certificates/*.ca /etc/ucentral/ 2>/dev/null || true
|
||||
chown root.network /etc/ucentral/*.pem /etc/ucentral/*.ca
|
||||
chmod 0440 root.network /etc/ucentral/*.pem /etc/ucentral/*.ca
|
||||
[ -f /certificates/gateway.json ] && cp /certificates/gateway.json /etc/ucentral/gateway.flash
|
||||
[ -f /certificates/restrictions.json ] && cp /certificates/restrictions.json /etc/ucentral/
|
||||
[ -f /certificates/sign_pubkey.pem ] && cp /certificates/sign_pubkey.pem /etc/ucentral/
|
||||
country=`cat /certificates/ucentral.defaults | jsonfilter -e '@.country'`
|
||||
[ -z "$country" -a -f /etc/uci-defaults/30_uboot-envtools ] && {
|
||||
(. /etc/uci-defaults/30_uboot-envtools)
|
||||
fw_printenv
|
||||
country=$(fw_printenv -n country)
|
||||
}
|
||||
[ -z "$country" ] && country=US
|
||||
echo "options cfg80211 ieee80211_regdom="$country > /etc/modules.conf
|
||||
echo -n $country > /etc/ucentral/country
|
||||
sync
|
||||
exit 0
|
||||
}
|
||||
|
||||
boot() {
|
||||
case "$(board_name)" in
|
||||
sonicfi,rap6*)
|
||||
touch /tmp/squashfs
|
||||
;;
|
||||
esac
|
||||
[ -f /etc/ucentral/key.pem ] && return
|
||||
/usr/bin/mount_certs
|
||||
copy_certificates
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci add system certificates
|
||||
uci set system.@certificates[-1].key=/etc/ucentral/key.pem
|
||||
uci set system.@certificates[-1].cert=/etc/ucentral/operational.pem
|
||||
uci set system.@certificates[-1].ca=/etc/ucentral/operational.ca
|
||||
uci commit
|
||||
@@ -0,0 +1,18 @@
|
||||
generate_certificate_volume() {
|
||||
grep certificates /proc/mtd > /dev/null
|
||||
[ $? -eq 0 ] && return
|
||||
|
||||
ls /dev/ubi0 > /dev/null
|
||||
[ $? -eq 0 ] || return
|
||||
|
||||
ubinfo /dev/ubi0 -N certificates > /dev/null
|
||||
[ $? -eq 0 ] || {
|
||||
ubinfo /dev/ubi0 -N wifi_fw > /dev/null
|
||||
[ $? -eq 0 ] && ubirmvol /dev/ubi0 -N wifi_fw
|
||||
|
||||
ubirsvol /dev/ubi0 -N rootfs_data -s 10MiB
|
||||
ubimkvol /dev/ubi0 -N certificates -S 20
|
||||
}
|
||||
}
|
||||
|
||||
boot_hook_add preinit_main generate_certificate_volume
|
||||
158
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/mount_certs
Executable file
158
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/mount_certs
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
|
||||
check_certificates() {
|
||||
[ -f /certificates/cert.pem -a -f /certificates/key.pem ] && exit 0
|
||||
}
|
||||
|
||||
check_certificates
|
||||
|
||||
tar_part_lookup() {
|
||||
part="$(fw_printenv -n cert_part)"
|
||||
if [ "$part" -eq 0 ]; then
|
||||
echo "$2"
|
||||
part=1
|
||||
else
|
||||
echo "$1"
|
||||
part=0
|
||||
fi
|
||||
fw_setenv cert_part $part
|
||||
}
|
||||
|
||||
. /lib/functions.sh
|
||||
mkdir -p /certificates /etc/ucentral/
|
||||
case "$(board_name)" in
|
||||
cig,wf660a)
|
||||
mmc_dev=$(echo $(find_mmc_part "0:ETHPHYFW") | sed 's/^.\{5\}//')
|
||||
[ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates
|
||||
;;
|
||||
cig,wf672)
|
||||
mmc_dev=$(echo $(find_mmc_part "cert") | sed 's/^.\{5\}//')
|
||||
[ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates
|
||||
;;
|
||||
sonicfi,rap7*)
|
||||
if [ "$(board_name)" = "sonicfi,rap7110c-341x" ]; then
|
||||
mmc_dev=$(echo $(find_mmc_part "certificates") | sed 's/^.\{5\}//')
|
||||
[ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates
|
||||
else
|
||||
mtd=$(find_mtd_index certificates)
|
||||
[ -n "$mtd" ] && mount -t ext4 /dev/mtdblock$mtd /certificates
|
||||
fi
|
||||
;;
|
||||
sonicfi,rap6*)
|
||||
mtd=$(find_mtd_index certificates)
|
||||
if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then
|
||||
mount -t squashfs /dev/mtdblock$mtd /mnt
|
||||
cp /mnt/* /certificates
|
||||
umount /mnt
|
||||
fi
|
||||
mtd=$(find_mtd_index devinfo)
|
||||
[ -n "$mtd" ] && tar xf /dev/mtdblock$mtd -C /certificates
|
||||
;;
|
||||
udaya,a5-id2|\
|
||||
yuncore,ax820)
|
||||
mtd=$(find_mtd_index certificates)
|
||||
if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then
|
||||
mount -t squashfs /dev/mtdblock$mtd /mnt
|
||||
cp /mnt/* /certificates
|
||||
umount /mnt
|
||||
fi
|
||||
part=$(tar_part_lookup "insta1" "insta2")
|
||||
if [ -n "insta" ]; then
|
||||
mtd=$(find_mtd_index $part)
|
||||
[ -n "$mtd" ] && tar xf /dev/mtdblock$mtd -C /certificates
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
mtd=$(find_mtd_index certificates)
|
||||
|
||||
if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then
|
||||
mount -t squashfs /dev/mtdblock$mtd /certificates
|
||||
else
|
||||
[ -n "$mtd" -a -f /sys/class/mtd/mtd$mtd/oobsize ] && ubiattach -p /dev/mtd$mtd
|
||||
if [ -n "$(ubinfo -a | grep certificates)" ]; then
|
||||
[ -e /dev/ubi0 ] && mount -t ubifs ubi0:certificates /certificates
|
||||
[ -e /dev/ubi1 ] && mount -t ubifs ubi1:certificates /certificates
|
||||
fi
|
||||
fi
|
||||
esac
|
||||
|
||||
check_certificates
|
||||
|
||||
# if we get here no valid certificates were found
|
||||
|
||||
PART_NAME=
|
||||
|
||||
case "$(board_name)" in
|
||||
actiontec,web7200)
|
||||
if grep -q bootselect=0 /proc/cmdline; then
|
||||
PART_NAME=firmware2
|
||||
else
|
||||
PART_NAME=firmware1
|
||||
fi
|
||||
;;
|
||||
edgecore,ecw5211|\
|
||||
edgecore,eap101|\
|
||||
edgecore,eap102|\
|
||||
edgecore,eap104|\
|
||||
edgecore,eap105|\
|
||||
edgecore,eap111|\
|
||||
edgecore,eap112|\
|
||||
edgecore,oap101|\
|
||||
edgecore,oap101e|\
|
||||
edgecore,oap101-6e|\
|
||||
edgecore,oap101e-6e|\
|
||||
edgecore,oap103)
|
||||
if grep -q rootfs1 /proc/cmdline; then
|
||||
PART_NAME=rootfs2
|
||||
else
|
||||
PART_NAME=rootfs1
|
||||
fi
|
||||
;;
|
||||
hfcl,ion4xi|\
|
||||
hfcl,ion4xi_w|\
|
||||
hfcl,ion4x_w|\
|
||||
hfcl,ion4xi_HMR|\
|
||||
hfcl,ion4x|\
|
||||
hfcl,ion4x_2|\
|
||||
hfcl,ion4xi_wp|\
|
||||
hfcl,ion4xe)
|
||||
if grep -q rootfs_1 /proc/cmdline; then
|
||||
PART_NAME=rootfs
|
||||
else
|
||||
PART_NAME=rootfs_1
|
||||
fi
|
||||
;;
|
||||
cig,wf186w|\
|
||||
cig,wf189|\
|
||||
cig,wf189w|\
|
||||
cig,wf189h|\
|
||||
cig,wf186h|\
|
||||
cig,wf196|\
|
||||
cig,wf188n|\
|
||||
emplus,wap380c|\
|
||||
emplus,wap385c|\
|
||||
emplus,wap386v2|\
|
||||
emplus,wap581|\
|
||||
yuncore,ax840|\
|
||||
yuncore,fap655)
|
||||
PART_NAME=rootfs_1
|
||||
;;
|
||||
senao,iap2300m|\
|
||||
senao,iap4300m|\
|
||||
emplus,wap588m|\
|
||||
senao,jeap6500)
|
||||
PART_NAME=ubi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
MTD=$(find_mtd_index $PART_NAME)
|
||||
|
||||
[ -z "$MTD" ] && return 1
|
||||
|
||||
ubiattach -m $MTD -d 3
|
||||
[ -e /dev/ubi3 ] && mount -t ubifs ubi3:certificates /certificates
|
||||
|
||||
check_certificates
|
||||
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/store_certs
Executable file
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/store_certs
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
|
||||
tar_part_lookup() {
|
||||
part="$(fw_printenv -n cert_part)"
|
||||
if [ "$part" -eq 0 ]; then
|
||||
echo "$2"
|
||||
part=1
|
||||
else
|
||||
echo "$1"
|
||||
part=0
|
||||
fi
|
||||
fw_setenv cert_part $part
|
||||
}
|
||||
|
||||
. /lib/functions.sh
|
||||
case "$(board_name)" in
|
||||
udaya,a5-id2|\
|
||||
yuncore,ax820)
|
||||
cd /certificates
|
||||
tar cf /tmp/certs.tar .
|
||||
part=$(tar_part_lookup "insta1" "insta2")
|
||||
mtd=$(find_mtd_index $part)
|
||||
dd if=/tmp/certs.tar of=/dev/mtdblock$mtd
|
||||
;;
|
||||
sonicfi,rap6*)
|
||||
if [ "$(fw_printenv -n store_certs_disabled)" != "1" ]; then
|
||||
cd /certificates
|
||||
tar cf /tmp/certs.tar .
|
||||
mtd=$(find_mtd_index devinfo)
|
||||
block_size=$(cat /sys/class/mtd/mtd$mtd/size)
|
||||
dd if=/tmp/certs.tar of=/tmp/certs_pad.tar bs=$block_size conv=sync
|
||||
mtd write /tmp/certs_pad.tar /dev/mtd$mtd
|
||||
rm -f /tmp/certs.tar /tmp/certs_pad.tar
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
24
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/Makefile
Normal file
24
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=cloud_discovery
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/cloud_discovery
|
||||
SECTION:=ucentral
|
||||
CATEGORY:=uCentral
|
||||
TITLE:=TIP cloud_discovery
|
||||
DEPENDS:=+certificates +bind-dig
|
||||
endef
|
||||
|
||||
Build/Compile=
|
||||
|
||||
define Package/cloud_discovery/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,cloud_discovery))
|
||||
@@ -0,0 +1 @@
|
||||
touch /tmp/ntp.set
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=98
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/cloud_discovery
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger ucentral
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
ubus call cloud reload
|
||||
}
|
||||
|
||||
start_service() {
|
||||
[ -f /etc/ucentral/capabilities.json ] || {
|
||||
mkdir -p /etc/ucentral/
|
||||
/usr/share/ucentral/capabilities.uc
|
||||
}
|
||||
|
||||
local valid=$(cat /etc/ucentral/gateway.json | jsonfilter -e '@["valid"]')
|
||||
[ "$valid" == "true" ] ||
|
||||
/usr/share/ucentral/ucentral.uc /etc/ucentral/ucentral.cfg.0000000001 > /dev/null
|
||||
|
||||
est_client check
|
||||
[ $? -eq 1 ] && {
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
logger The certificate used has a CN that does not match the serial of the device
|
||||
echo The certificate used has a CN that does not match the serial of the device
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
return
|
||||
}
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/share/ucentral/cloud_discovery.uc $1
|
||||
519
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/cloud_discovery
Executable file
519
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/cloud_discovery
Executable file
@@ -0,0 +1,519 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log';
|
||||
import { query } from 'resolv';
|
||||
import * as libubus from 'ubus';
|
||||
import * as uloop from 'uloop';
|
||||
import * as libuci from 'uci';
|
||||
import * as math from 'math';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const DISCOVER = 0;
|
||||
const VALIDATING = 1;
|
||||
const ONLINE = 2;
|
||||
const OFFLINE = 3;
|
||||
const ORPHAN = 4;
|
||||
|
||||
const DISCOVER_DHCP = "DHCP";
|
||||
const DISCOVER_FLASH = "FLASH";
|
||||
const DISCOVER_FQDN = "STANDARD_FQDN";
|
||||
const DISCOVER_LOOKUP = "OpenLAN";
|
||||
|
||||
const STANDARD_FQDN = "openwifi.wlan.local";
|
||||
const STANDARD_FQDN_PORT = 15002;
|
||||
|
||||
let ubus = libubus.connect();
|
||||
let uci = libuci.cursor();
|
||||
let state = DISCOVER;
|
||||
let discovery_method = "";
|
||||
let discovery_block_list = [];
|
||||
let validate_time;
|
||||
let offline_time;
|
||||
let orphan_time;
|
||||
let interval;
|
||||
let timeouts = {
|
||||
'offline': 4 * 60 * 60,
|
||||
'validate': 120,
|
||||
'orphan': 2 * 60 * 60,
|
||||
interval: 10000,
|
||||
expiry_interval: 60 * 60 * 1000,
|
||||
expiry_threshold: 1 * 365 * 24 * 60 * 60,
|
||||
};
|
||||
|
||||
ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "cloud_discover");
|
||||
|
||||
ulog(LOG_INFO, 'Start\n');
|
||||
|
||||
uloop.init();
|
||||
|
||||
let cds_server = 'discovery.open-lan.org';
|
||||
|
||||
function detect_certificate_type() {
|
||||
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
|
||||
let issuer = pipe.read("all");
|
||||
pipe.close();
|
||||
|
||||
if (match(issuer, /OpenLAN Demo Birth CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Demo" \n');
|
||||
cds_server = 'discovery-qa.open-lan.org';
|
||||
timeouts.expiry_threshold = 3 * 24 * 60 * 60;
|
||||
} else if (match(issuer, /OpenLAN Birth Issuing CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Production"\n');
|
||||
} else {
|
||||
ulog(LOG_INFO, 'Certificate type is "TIP"\n');
|
||||
}
|
||||
}
|
||||
|
||||
function readjsonfile(path) {
|
||||
let file = fs.readfile(path);
|
||||
if (file)
|
||||
file = json(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
function timeouts_load(){
|
||||
let data = uci.get_all('ucentral', 'timeouts');
|
||||
|
||||
for (let key in [ 'offline', 'validate', 'orphan' ])
|
||||
if (data && data[key])
|
||||
timeouts[key] = +data[key];
|
||||
let time_skew = timeouts.offline / 50 * (math.rand() % 50);
|
||||
timeouts.offline_skew = timeouts.offline + time_skew;
|
||||
ulog(LOG_INFO, 'Randomizing offline time from %d->%d \n', timeouts.offline, timeouts.offline_skew);
|
||||
|
||||
time_skew = timeouts.orphan / 50 * (math.rand() % 50);
|
||||
timeouts.orphan_skew = timeouts.orphan + time_skew;
|
||||
ulog(LOG_INFO, 'Randomizing orphan time from %d->%d \n', timeouts.orphan, timeouts.orphan_skew);
|
||||
}
|
||||
|
||||
function client_start() {
|
||||
ulog(LOG_INFO, '(re)starting client\n');
|
||||
system('/etc/init.d/ucentral restart');
|
||||
}
|
||||
|
||||
function dhcp_restart() {
|
||||
ulog(LOG_INFO, 'restarting dhcp\n');
|
||||
system('killall -USR1 udhcpc');
|
||||
}
|
||||
|
||||
function ntp_restart() {
|
||||
ulog(LOG_INFO, 'restarting ntp\n');
|
||||
system('/etc/init.d/sysntpd restart');
|
||||
}
|
||||
|
||||
function gateway_load() {
|
||||
return readjsonfile('/etc/ucentral/gateway.json');
|
||||
}
|
||||
|
||||
function discovery_state_write() {
|
||||
if (length(discovery_method) == 0)
|
||||
return;
|
||||
|
||||
let discovery_state = {
|
||||
"type": discovery_method,
|
||||
"updated": time()
|
||||
};
|
||||
fs.writefile('/etc/ucentral/discovery.state.json', discovery_state);
|
||||
}
|
||||
|
||||
function gateway_write(data) {
|
||||
let gateway = gateway_load();
|
||||
gateway ??= {};
|
||||
let new = {};
|
||||
let changed = false;
|
||||
for (let key in [ 'server', 'port', 'valid', 'hostname_validate', 'cert', 'ca' ]) {
|
||||
if (exists(data, key))
|
||||
new[key] = data[key];
|
||||
else if (exists(gateway, key))
|
||||
new[key] = gateway[key];
|
||||
if (new[key] != gateway[key])
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
fs.writefile('/etc/ucentral/gateway.json', new);
|
||||
system('sync');
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
function gateway_available() {
|
||||
let gateway = gateway_load();
|
||||
if (!gateway || !gateway.server || !gateway.port)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function dnsmasq_rebind_allow(fqdn) {
|
||||
let config_dir = '/tmp/dnsmasq.d';
|
||||
let config_file = `${config_dir}/cloud-discovery.conf`;
|
||||
|
||||
if (!fs.stat(config_dir))
|
||||
fs.mkdir(config_dir);
|
||||
|
||||
fs.writefile(config_file, `rebind-domain-ok=/${fqdn}/\n`);
|
||||
system('/etc/init.d/dnsmasq reload');
|
||||
}
|
||||
|
||||
function set_state(set) {
|
||||
if (state == set)
|
||||
return;
|
||||
let prev = state;
|
||||
state = set;
|
||||
|
||||
switch(state) {
|
||||
case DISCOVER:
|
||||
ulog(LOG_INFO, 'Setting cloud to undiscovered\n');
|
||||
fs.unlink('/tmp/cloud.json');
|
||||
fs.unlink('/etc/ucentral/gateway.json');
|
||||
gateway_write({ valid: false });
|
||||
dhcp_restart();
|
||||
break;
|
||||
|
||||
case VALIDATING:
|
||||
ulog(LOG_INFO, 'Wait for validation\n');
|
||||
validate_time = time();
|
||||
state = VALIDATING;
|
||||
push(discovery_block_list, discovery_method);
|
||||
break;
|
||||
|
||||
case ONLINE:
|
||||
ulog(LOG_INFO, 'Connected to cloud\n');
|
||||
if (prev == VALIDATING) {
|
||||
ulog(LOG_INFO, 'Setting cloud controller to validated\n');
|
||||
gateway_write({ valid: true });
|
||||
discovery_state_write();
|
||||
discovery_block_list = [];
|
||||
}
|
||||
break;
|
||||
|
||||
case OFFLINE:
|
||||
ulog(LOG_INFO, 'Lost connection to cloud\n');
|
||||
offline_time = time();
|
||||
break;
|
||||
|
||||
case ORPHAN:
|
||||
ulog(LOG_INFO, 'Device is an orphan\n');
|
||||
orphan_time = time();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function discover_dhcp() {
|
||||
let dhcp = readjsonfile('/tmp/cloud.json');
|
||||
if (dhcp?.dhcp_server && dhcp?.dhcp_port) {
|
||||
let fqdn = split(dhcp.dhcp_server, ':')[0];
|
||||
dnsmasq_rebind_allow(fqdn);
|
||||
if (gateway_write({
|
||||
server: dhcp.dhcp_server,
|
||||
port: dhcp.dhcp_port,
|
||||
valid: false,
|
||||
hostname_validate: dhcp.no_validation ? 0 : 1,
|
||||
cert: `/etc/ucentral/${fqdn}.pem`,
|
||||
ca: `/etc/ucentral/${fqdn}.ca`
|
||||
})) {
|
||||
ulog(LOG_INFO, `Discovered cloud via DHCP ${dhcp.dhcp_server}:${dhcp.dhcp_port} - trying EST\n`);
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_DHCP);
|
||||
if (system('/usr/bin/est_client enroll'))
|
||||
return false;
|
||||
ulog(LOG_INFO, `Discovered cloud via DHCP ${dhcp.dhcp_server}:${dhcp.dhcp_port} - starting client\n`);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return !dhcp?.lease;
|
||||
}
|
||||
|
||||
function redirector_lookup() {
|
||||
const path = '/tmp/ucentral.redirector';
|
||||
ulog(LOG_INFO, 'Contact redirector service\n');
|
||||
let serial = uci.get('system', '@system[-1]', 'mac');
|
||||
|
||||
fs.unlink(path);
|
||||
system(`curl -k --cert /etc/ucentral/operational.pem --key /etc/ucentral/key.pem --cacert /etc/ucentral/operational.ca https://${cds_server}/v1/devices/${serial} --output /tmp/ucentral.redirector`);
|
||||
if (!fs.stat(path))
|
||||
return;
|
||||
let redir = readjsonfile(path);
|
||||
if (redir?.controller_endpoint) {
|
||||
let controller_endpoint = split(redir.controller_endpoint, ':');
|
||||
if (gateway_write({
|
||||
server: controller_endpoint[0],
|
||||
port: controller_endpoint[1] || 15002,
|
||||
valid: false,
|
||||
hostname_validate: 1,
|
||||
cert: '/etc/ucentral/operational.pem',
|
||||
ca: '/etc/ucentral/operational.ca'
|
||||
})) {
|
||||
ulog(LOG_INFO, `Discovered cloud via lookup service ${controller_endpoint[0]}:${controller_endpoint[1] || 15002}\n`);
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_LOOKUP);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
}
|
||||
} else {
|
||||
ulog(LOG_INFO, 'Failed to discover cloud endpoint\n');
|
||||
}
|
||||
}
|
||||
|
||||
function discover_flash() {
|
||||
if (!fs.stat('/etc/ucentral/gateway.flash'))
|
||||
return 1;
|
||||
ulog(LOG_INFO, 'Using pre-populated cloud information\n');
|
||||
fs.writefile('/etc/ucentral/gateway.json', fs.readfile('/etc/ucentral/gateway.flash'));
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_FLASH);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function discover_standard_fqdn() {
|
||||
ulog(LOG_INFO, `Trying standard FQDN: ${STANDARD_FQDN}\n`);
|
||||
|
||||
let result = query([STANDARD_FQDN], { type: ['A'] });
|
||||
if (!result || !result[STANDARD_FQDN] || !result[STANDARD_FQDN].A) {
|
||||
ulog(LOG_INFO, `Failed to resolve ${STANDARD_FQDN}\n`);
|
||||
return false;
|
||||
}
|
||||
|
||||
let address = result[STANDARD_FQDN].A[0];
|
||||
ulog(LOG_INFO, `Resolved ${STANDARD_FQDN} to ${address}\n`);
|
||||
|
||||
dnsmasq_rebind_allow(STANDARD_FQDN);
|
||||
|
||||
if (gateway_write({
|
||||
server: STANDARD_FQDN,
|
||||
port: STANDARD_FQDN_PORT,
|
||||
valid: false,
|
||||
hostname_validate: 1,
|
||||
cert: `/etc/ucentral/${STANDARD_FQDN}.pem`,
|
||||
ca: `/etc/ucentral/${STANDARD_FQDN}.ca`
|
||||
})) {
|
||||
ulog(LOG_INFO, `Discovered cloud via standard FQDN ${STANDARD_FQDN}\n`);
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_FQDN);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function time_is_valid() {
|
||||
let valid = !!fs.stat('/tmp/ntp.set');
|
||||
if (!valid)
|
||||
ntp_restart();
|
||||
ulog(LOG_INFO, `Time is ${valid ? '': 'not '}valid\n`);
|
||||
return valid;
|
||||
}
|
||||
|
||||
function is_discover_method_blacked() {
|
||||
if (discovery_method in discovery_block_list)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function interval_handler() {
|
||||
printf(`State ${state}\n`);
|
||||
switch(state) {
|
||||
case DISCOVER:
|
||||
if (timeouts.interval < 60000)
|
||||
timeouts.interval += 10000;
|
||||
break;
|
||||
default:
|
||||
timeouts.interval = 10000;
|
||||
break;
|
||||
}
|
||||
|
||||
printf('setting interval to %d\n', timeouts.interval);
|
||||
interval.set(timeouts.interval);
|
||||
|
||||
switch(state) {
|
||||
case ORPHAN:
|
||||
if (time() - orphan_time <= timeouts.orphan_skew)
|
||||
break;
|
||||
orphan_time = time();
|
||||
|
||||
/* fall through */
|
||||
|
||||
case DISCOVER:
|
||||
ulog(LOG_INFO, 'Starting discover\n');
|
||||
|
||||
if (!time_is_valid())
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_DHCP;
|
||||
if (!is_discover_method_blacked() && discover_dhcp())
|
||||
return;
|
||||
|
||||
if (system('/usr/bin/est_client enroll'))
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_FLASH;
|
||||
if (!is_discover_method_blacked() && !discover_flash())
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_FQDN;
|
||||
if (!is_discover_method_blacked() && discover_standard_fqdn())
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_LOOKUP;
|
||||
redirector_lookup();
|
||||
|
||||
discovery_block_list = [];
|
||||
break;
|
||||
|
||||
case VALIDATING:
|
||||
if (time() - validate_time <= timeouts.validate)
|
||||
break;
|
||||
ulog(LOG_INFO, 'validation failed, restarting discovery process\n');
|
||||
set_state(DISCOVER);
|
||||
break;
|
||||
|
||||
case OFFLINE:
|
||||
if (time() - offline_time <= timeouts.offline_skew)
|
||||
break;
|
||||
ulog(LOG_INFO, 'offline for too long, setting device as orphaned\n');
|
||||
set_state(ORPHAN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function trigger_reenroll() {
|
||||
ulog(LOG_INFO, 'triggering reenroll\n');
|
||||
|
||||
if (system('/usr/bin/est_client reenroll')) {
|
||||
ulog(LOG_INFO, 'reenroll failed\n');
|
||||
return;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'reenroll succeeded\n');
|
||||
ulog(LOG_INFO, 'stopping client\n');
|
||||
|
||||
system('/etc/init.d/ucentral stop');
|
||||
set_state(DISCOVER);
|
||||
}
|
||||
|
||||
function expiry_handler() {
|
||||
let stat = fs.stat('/etc/ucentral/operational.ca');
|
||||
if (!stat)
|
||||
return;
|
||||
|
||||
let ret = system(`openssl x509 -checkend ${timeouts.expiry_threshold} -noout -in /certificates/operational.pem`);
|
||||
if (!ret) {
|
||||
ulog(LOG_INFO, 'checked certificate expiry - all ok\n');
|
||||
return;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'certificate will expire soon\n');
|
||||
trigger_reenroll();
|
||||
}
|
||||
|
||||
let ubus_methods = {
|
||||
discover: {
|
||||
call: function(req) {
|
||||
set_state(DISCOVER);
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
renew: {
|
||||
call: function(req) {
|
||||
if (state != ONLINE)
|
||||
return;
|
||||
|
||||
ulog(LOG_INFO, 'Validate cloud due to DHCP renew event\n');
|
||||
|
||||
let gateway = gateway_load();
|
||||
let cloud = readjsonfile('/tmp/cloud.json');
|
||||
if (!cloud?.dhcp_server || !cloud?.dhcp_port)
|
||||
return 0;
|
||||
|
||||
if (cloud.dhcp_server != gateway?.server || cloud.dhcp_port != gateway?.port)
|
||||
set_state(DISCOVER);
|
||||
else
|
||||
ulog(LOG_INFO, 'Cloud has not changed\n');
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
online: {
|
||||
call: function(req) {
|
||||
set_state(ONLINE);
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
offline: {
|
||||
call: function(req) {
|
||||
if (state == ONLINE)
|
||||
set_state(OFFLINE);
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
reload: {
|
||||
call: function(req) {
|
||||
timeouts_load();
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
|
||||
}
|
||||
},
|
||||
status: {
|
||||
call: function(req) {
|
||||
const names = [ 'discover', 'validate', 'online', 'offline', 'orphan' ];
|
||||
let ret = { state: names[state] };
|
||||
switch(state){
|
||||
case OFFLINE:
|
||||
ret.since = time() - offline_time;
|
||||
break;
|
||||
case ORPHAN:
|
||||
ret.since = time() - orphan_time;
|
||||
break;
|
||||
case VALIDATING:
|
||||
ret.since = time() - validate_time;;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
args: {},
|
||||
},
|
||||
reenroll: {
|
||||
call: function(req) {
|
||||
trigger_reenroll();
|
||||
return 0;
|
||||
},
|
||||
args: {},
|
||||
},
|
||||
};
|
||||
|
||||
detect_certificate_type();
|
||||
|
||||
if (gateway_available()) {
|
||||
let status = ubus.call('ucentral', 'status');
|
||||
ulog(LOG_INFO, 'cloud is known\n');
|
||||
if (status?.connected) {
|
||||
state = ONLINE;
|
||||
} else {
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
}
|
||||
} else {
|
||||
dhcp_restart();
|
||||
}
|
||||
|
||||
timeouts_load();
|
||||
|
||||
interval = uloop.interval(timeouts.interval, interval_handler);
|
||||
uloop.interval(timeouts.expiry_interval, expiry_handler);
|
||||
|
||||
ubus.publish('cloud', ubus_methods);
|
||||
|
||||
uloop.run();
|
||||
uloop.done();
|
||||
303
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/est_client
Executable file
303
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/est_client
Executable file
@@ -0,0 +1,303 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log';
|
||||
import * as fs from 'fs';
|
||||
import * as libuci from 'uci';
|
||||
|
||||
let store_operational_pem = false;
|
||||
let store_operational_ca = false;
|
||||
let est_server = 'est.certificates.open-lan.org';
|
||||
let cert_prefix = 'operational';
|
||||
|
||||
function cert_prefix_determine() {
|
||||
let cloud_config = fs.readfile('/tmp/cloud.json');
|
||||
if (cloud_config) {
|
||||
let cloud = json(cloud_config);
|
||||
if (cloud?.dhcp_server) {
|
||||
let fqdn = split(cloud.dhcp_server, ':')[0];
|
||||
ulog(LOG_INFO, `Using controller-specific cert prefix from cloud.json: ${fqdn}\n`);
|
||||
return fqdn;
|
||||
}
|
||||
}
|
||||
|
||||
let discovery_method = trim(fs.readfile('/tmp/discovery.method') || 'OpenLAN');
|
||||
ulog(LOG_INFO, `Discovery method from file: ${discovery_method}\n`);
|
||||
|
||||
if (discovery_method == 'OpenLAN') {
|
||||
ulog(LOG_INFO, 'Using operational cert prefix\n');
|
||||
return 'operational';
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'Using operational cert prefix as fallback\n');
|
||||
return 'operational';
|
||||
}
|
||||
|
||||
function discover_est_server_via_caa() {
|
||||
let cloud_config = fs.readfile('/tmp/cloud.json');
|
||||
if (!cloud_config)
|
||||
return null;
|
||||
|
||||
let cloud = json(cloud_config);
|
||||
if (!cloud || !cloud.dhcp_server)
|
||||
return null;
|
||||
|
||||
let controller_fqdn = cloud.dhcp_server;
|
||||
let fqdn_parts = split(controller_fqdn, ':');
|
||||
if (length(fqdn_parts) > 0)
|
||||
controller_fqdn = fqdn_parts[0];
|
||||
|
||||
ulog(LOG_INFO, `Attempting CAA lookup for controller FQDN: ${controller_fqdn}\n`);
|
||||
|
||||
let pipe = fs.popen(`dig @localhost ${controller_fqdn} CAA +short | cut -d'"' -f2`);
|
||||
let est_server = pipe.read('all');
|
||||
pipe.close();
|
||||
|
||||
if (!est_server)
|
||||
return null;
|
||||
|
||||
est_server = trim(est_server);
|
||||
if (est_server) {
|
||||
ulog(LOG_INFO, `Found EST server via CAA: ${est_server}\n`);
|
||||
return est_server;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function set_est_server() {
|
||||
let discovered_server = discover_est_server_via_caa();
|
||||
if (discovered_server) {
|
||||
est_server = discovered_server;
|
||||
return;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'No EST server found via CAA, using certificate issuer-based selection\n');
|
||||
|
||||
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
|
||||
let issuer = pipe.read("all");
|
||||
pipe.close();
|
||||
|
||||
if (match(issuer, /OpenLAN Demo Birth CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Demo" \n');
|
||||
est_server = 'qaest.certificates.open-lan.org:8001';
|
||||
} else if (match(issuer, /OpenLAN Birth Issuing CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Production"\n');
|
||||
} else {
|
||||
ulog(LOG_INFO, 'Certificate type is "TIP"\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv('EST_SERVER'))
|
||||
est_server = getenv('EST_SERVER');
|
||||
|
||||
if (getenv('CERT_PREFIX'))
|
||||
cert_prefix = getenv('CERT_PREFIX');
|
||||
|
||||
ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "est_client");
|
||||
|
||||
function generate_csr(cert) {
|
||||
if (!fs.stat('/tmp/csr.nohdr.p10')) {
|
||||
let pipe = fs.popen(`openssl x509 -in ${cert} -noout -subject`);
|
||||
let subject = pipe.read("all");
|
||||
pipe.close();
|
||||
subject = rtrim(subject);
|
||||
subject = replace(subject, 'subject=', '/');
|
||||
subject = replace(subject, ' = ', '=');
|
||||
subject = replace(subject, ', ', '/');
|
||||
|
||||
let ret = system(`openssl req -subj "${subject}" -new -key /etc/ucentral/key.pem -out /tmp/csr.p10`);
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, 'Failed to generate CSR\n');
|
||||
return 1;
|
||||
}
|
||||
|
||||
let input = fs.open('/tmp/csr.p10', 'r');
|
||||
let output = fs.open('/tmp/csr.nohdr.p10', 'w');
|
||||
let line;
|
||||
while (line = input.read('line')) {
|
||||
if (substr(line, 0, 4) == '----')
|
||||
continue;
|
||||
output.write(line);
|
||||
}
|
||||
input.close();
|
||||
output.close();
|
||||
ulog(LOG_INFO, 'Generated CSR\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function store_operational_cert(path, target) {
|
||||
system('mount_certs');
|
||||
system(`cp ${path} /certificates/${target}`);
|
||||
|
||||
ulog(LOG_INFO, `Persistently stored ${target}\n`);
|
||||
}
|
||||
|
||||
function p7_too_pem(src, dst) {
|
||||
let input = fs.readfile(src);
|
||||
let output = fs.open('/tmp/convert.p7', 'w');
|
||||
output.write('-----BEGIN PKCS #7 SIGNED DATA-----\n');
|
||||
output.write(`${input}\n-----END PKCS #7 SIGNED DATA-----`);
|
||||
output.close();
|
||||
|
||||
let ret = system(`openssl pkcs7 -outform PEM -print_certs -in /tmp/convert.p7 -out ${dst}`);
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, 'Failed to convert P7 to PEM\n');
|
||||
return 1;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'Converted P7 to PEM\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function call_est_server(path, cert, target) {
|
||||
if (generate_csr(cert))
|
||||
return 1;
|
||||
|
||||
set_est_server();
|
||||
|
||||
let curl_cmd = 'curl -m 10 -X POST https://' + est_server + '/.well-known/est/' + path + ' -d @/tmp/csr.nohdr.p10 -H "Content-Type: application/pkcs10" --cert ' + cert + ' --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/operational.nohdr.p7';
|
||||
ulog(LOG_INFO, `Executing: ${curl_cmd}\n`);
|
||||
let ret = system(curl_cmd);
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, `Failed to request operational certificate (exit code: ${ret})\n`);
|
||||
return 1;
|
||||
}
|
||||
ulog(LOG_INFO, 'EST succeeded\n');
|
||||
|
||||
return p7_too_pem('/tmp/operational.nohdr.p7', target);
|
||||
}
|
||||
|
||||
|
||||
function simpleenroll() {
|
||||
cert_prefix = cert_prefix_determine();
|
||||
ulog(LOG_INFO, `Checking for certificate: /etc/ucentral/${cert_prefix}.pem\n`);
|
||||
|
||||
if (fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) {
|
||||
ulog(LOG_INFO, 'Operational certificate is present\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'Operational certificate not found, enrolling...\n');
|
||||
if (call_est_server('simpleenroll', '/etc/ucentral/cert.pem', '/etc/ucentral/' + cert_prefix + '.pem'))
|
||||
return 1;
|
||||
|
||||
ulog(LOG_INFO, 'Operational cert acquired\n');
|
||||
store_operational_pem = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function simplereenroll() {
|
||||
cert_prefix = cert_prefix_determine();
|
||||
|
||||
if (!fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) {
|
||||
ulog(LOG_INFO, 'Operational certificate was not found\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (call_est_server('simplereenroll', '/etc/ucentral/' + cert_prefix + '.pem', '/tmp/' + cert_prefix + '.pem'))
|
||||
return 1;
|
||||
|
||||
ulog(LOG_INFO, 'Operational cert updated\n');
|
||||
store_operational_cert('/tmp/' + cert_prefix + '.pem', cert_prefix + '.pem');
|
||||
system('cp /tmp/' + cert_prefix + '.pem /etc/ucentral/');
|
||||
system('store_certs');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function load_operational_ca() {
|
||||
cert_prefix = cert_prefix_determine();
|
||||
|
||||
if (fs.stat('/etc/ucentral/' + cert_prefix + '.ca')) {
|
||||
ulog(LOG_INFO, 'Operational CA is present\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
set_est_server();
|
||||
|
||||
let curl_cmd = 'curl -m 10 -X GET https://' + est_server + '/.well-known/est/cacerts --cert /etc/ucentral/' + cert_prefix + '.pem --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/' + cert_prefix + '.ca.nohdr.p7';
|
||||
ulog(LOG_INFO, `Executing: ${curl_cmd}\n`);
|
||||
let ret = system(curl_cmd);
|
||||
if (!ret)
|
||||
ret = p7_too_pem('/tmp/' + cert_prefix + '.ca.nohdr.p7', '/etc/ucentral/' + cert_prefix + '.ca');
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, `Failed to load CA (exit code: ${ret})\n`);
|
||||
return 1;
|
||||
}
|
||||
system('cat /etc/ucentral/openlan.pem >> /etc/ucentral/' + cert_prefix + '.ca');
|
||||
ulog(LOG_INFO, 'Acquired CA\n');
|
||||
store_operational_ca = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function fwtool() {
|
||||
if (!fs.stat('/etc/ucentral/cert.pem'))
|
||||
return 0;
|
||||
|
||||
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
|
||||
let issuer = pipe.read("all");
|
||||
pipe.close();
|
||||
|
||||
if (!(match(issuer, /OpenLAN/) && match(issuer, /Birth/)))
|
||||
return 0;
|
||||
|
||||
ulog(LOG_INFO, 'The issuer is insta\n');
|
||||
|
||||
let metadata = fs.readfile('/tmp/sysupgrade.meta');
|
||||
if (metadata)
|
||||
metadata = json(metadata);
|
||||
if (!metadata)
|
||||
return 0;
|
||||
|
||||
if (!metadata.est_supported) {
|
||||
ulog(LOG_INFO, 'The image does not support EST\n');
|
||||
return 1;
|
||||
}
|
||||
ulog(LOG_INFO, 'The image supports EST\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function check_cert() {
|
||||
if (!fs.stat('/etc/ucentral/cert.pem'))
|
||||
return 0;
|
||||
let pipe = fs.popen("openssl x509 -in /etc/ucentral/cert.pem -noout -subject -nameopt multiline | grep commonName | awk '{ print $3 }'");
|
||||
let cn = pipe.read("all");
|
||||
pipe.close();
|
||||
if (!cn)
|
||||
return 0;
|
||||
cn = lc(trim(cn));
|
||||
let uci = libuci.cursor();
|
||||
let serial = uci.get('ucentral', 'config', 'serial');
|
||||
return cn != serial;
|
||||
}
|
||||
|
||||
switch(ARGV[0]) {
|
||||
case 'enroll':
|
||||
let ret = simpleenroll();
|
||||
if (!ret)
|
||||
ret = load_operational_ca();
|
||||
if (store_operational_pem)
|
||||
store_operational_cert('/etc/ucentral/' + cert_prefix + '.pem', cert_prefix + '.pem');
|
||||
if (store_operational_ca)
|
||||
store_operational_cert('/etc/ucentral/' + cert_prefix + '.ca', cert_prefix + '.ca');
|
||||
if (store_operational_pem || store_operational_ca)
|
||||
system('store_certs');
|
||||
|
||||
exit(ret);
|
||||
|
||||
case 'reenroll':
|
||||
if (simplereenroll())
|
||||
exit(1);
|
||||
exit(0);
|
||||
|
||||
case 'fwtool':
|
||||
exit(fwtool());
|
||||
|
||||
case 'check':
|
||||
exit(check_cert());
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
import * as libubus from 'ubus';
|
||||
import * as fs from 'fs';
|
||||
|
||||
let cmd = ARGV[0];
|
||||
let ifname = getenv("interface");
|
||||
let opt138 = fs.readfile('/tmp/dhcp-option-138');
|
||||
let opt224 = fs.readfile('/tmp/dhcp-option-224');
|
||||
|
||||
if (cmd != 'bound' && cmd != 'renew')
|
||||
exit(0);
|
||||
|
||||
/*let file = fs.readfile('/etc/ucentral/gateway.json');
|
||||
if (file)
|
||||
file = json(file);
|
||||
file ??= {};
|
||||
if (file.server && file.port && file.valid)
|
||||
exit(0);
|
||||
*/
|
||||
|
||||
let cloud = {
|
||||
lease: true,
|
||||
};
|
||||
if (opt138) {
|
||||
let dhcp = opt138;
|
||||
dhcp = split(dhcp, ':');
|
||||
cloud.dhcp_server = dhcp[0];
|
||||
cloud.dhcp_port = dhcp[1] ?? 15002;
|
||||
cloud.no_validation = true;
|
||||
}
|
||||
if (opt224) {
|
||||
let dhcp = opt224;
|
||||
dhcp = split(dhcp, ':');
|
||||
cloud.dhcp_server = dhcp[0];
|
||||
cloud.dhcp_port = dhcp[1] ?? 15002;
|
||||
}
|
||||
fs.writefile('/tmp/cloud.json', cloud);
|
||||
|
||||
if ((opt138 || opt224) && cmd == 'renew') {
|
||||
let ubus = libubus.connect();
|
||||
ubus.call('cloud', 'renew');
|
||||
}
|
||||
|
||||
exit(0);
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI uCentral Configuration
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
include ../luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require dom';
|
||||
'require rpc';
|
||||
'require session';
|
||||
'require baseclass';
|
||||
|
||||
var callReboot = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'reboot',
|
||||
expect: { result: 0 }
|
||||
});
|
||||
|
||||
function handleReboot(ev) {
|
||||
return callReboot().then(function(res) {
|
||||
if (res != 0) {
|
||||
showError(_('The reboot command failed with code %d').format(res));
|
||||
L.raise('Error', 'Reboot failed');
|
||||
}
|
||||
|
||||
showProgress(_('The system is rebooting in order to attempt applying the remote configuration profile now. If not successful, the device will revert back into the initial provisioning state.'));
|
||||
|
||||
ui.awaitReconnect();
|
||||
}).catch(function(e) { showError(e.message) });
|
||||
}
|
||||
|
||||
function setDirty(isDirty) {
|
||||
if (isDirty) {
|
||||
session.setLocalData('ucentral-dirty', true);
|
||||
ui.showIndicator('ucentral-dirty', _('Reboot required'), showApplySettings);
|
||||
}
|
||||
else {
|
||||
session.setLocalData('ucentral-dirty', null);
|
||||
ui.hideIndicator('ucentral-dirty');
|
||||
}
|
||||
}
|
||||
|
||||
function handleContinue(ev) {
|
||||
setDirty(true);
|
||||
ui.hideModal();
|
||||
}
|
||||
|
||||
function showApplySettings() {
|
||||
ui.showModal(_('Apply Settings'), [
|
||||
E('p', _('The device must be rebooted in order to apply the changed settings. Once the uCentral agent successfully connects to the controller, the remote configuration profile will be applied and the initial provisioning web interface is disabled.')),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', { 'click': handleReboot, 'class': 'btn primary' }, [ _('Apply settings and reboot device now') ]),
|
||||
'\xa0',
|
||||
E('button', { 'click': handleContinue, 'class': 'btn' }, [ _('Continue configuration') ])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
function showProgress(text, timeout) {
|
||||
var dlg = ui.showModal(null, [
|
||||
E('p', { 'class': (timeout > 0) ? null : 'spinning' }, text)
|
||||
]);
|
||||
|
||||
dlg.removeChild(dlg.firstElementChild);
|
||||
|
||||
if (timeout > 0)
|
||||
window.setTimeout(ui.hideModal, timeout);
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
function showError(text) {
|
||||
ui.showModal(_('Error'), [
|
||||
E('p', [ text ]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', { 'class': 'btn', 'click': ui.hideModal }, _('OK'))
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (session.getLocalData('ucentral-dirty'))
|
||||
setDirty(true);
|
||||
|
||||
return baseclass.extend({
|
||||
save: function(serializeFn, ev) {
|
||||
var m = dom.findClassInstance(document.querySelector('.cbi-map'));
|
||||
|
||||
return m.save().then(function() {
|
||||
return fs.write('/etc/ucentral/profile.json', serializeFn(m.data.data));
|
||||
}).then(function() {
|
||||
return fs.exec('/sbin/profileupdate');
|
||||
}).then(function() {
|
||||
showApplySettings();
|
||||
}).catch(function(err) {
|
||||
showError(_('Unable to save settings: %s').format(err));
|
||||
});
|
||||
},
|
||||
|
||||
setDirty: setDirty,
|
||||
showError: showError,
|
||||
showProgress: showProgress,
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var profile = null;
|
||||
|
||||
function serialize(data) {
|
||||
if (!L.isObject(profile.unit))
|
||||
profile.unit = {};
|
||||
|
||||
profile.redirector = data.local.redirector;
|
||||
profile.unit.location = data.local.location;
|
||||
|
||||
return JSON.stringify(profile, null, '\t');
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return L.resolveDefault(fs.read('/etc/ucentral/profile.json'), '').then(function(data) {
|
||||
try { profile = JSON.parse(data); }
|
||||
catch(e) { profile = {}; };
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var m, s, o, data = { local: {
|
||||
redirector: profile.redirector,
|
||||
location: L.isObject(profile.unit) ? profile.unit.location : ''
|
||||
} };
|
||||
|
||||
m = new form.JSONMap(data);
|
||||
m.readonly = !L.hasViewPermission();
|
||||
|
||||
s = m.section(form.NamedSection, 'local', 'local', _('Local settings'),
|
||||
_('The settings on this page specify how the local uCentral client connects to the controller server.'));
|
||||
|
||||
s.option(form.Value, 'redirector', _('Redirector URL'));
|
||||
s.option(form.Value, 'location', _('Unit location'));
|
||||
|
||||
o = s.option(form.Button, '_certs', _('Certificates'));
|
||||
o.inputtitle = _('Upload certificate bundle…');
|
||||
o.onclick = function(ev) {
|
||||
return ui.uploadFile('/tmp/certs.tar').then(function(res) {
|
||||
uctool.showProgress(_('Verifying certificates…'));
|
||||
|
||||
return fs.exec('/sbin/certupdate').then(function(res) {
|
||||
if (res.code) {
|
||||
uctool.showError(_('Certificate validation failed: %s').format(res.stderr || res.stdout));
|
||||
}
|
||||
else {
|
||||
uctool.showProgress(_('Certificates updated.'), 1500);
|
||||
uctool.setDirty(true);
|
||||
}
|
||||
}, function(err) {
|
||||
uctool.showError(_('Unable to verify certificates: %s').format(err));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
|
||||
handleSave: uctool.save.bind(uctool, serialize),
|
||||
|
||||
handleSaveApply: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
'use strict';
|
||||
'require rpc';
|
||||
'require view';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var callSystemBoard = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'board'
|
||||
});
|
||||
|
||||
var callSystemInfo = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'info'
|
||||
});
|
||||
|
||||
function progressbar(value, max, byte) {
|
||||
var vn = parseInt(value) || 0,
|
||||
mn = parseInt(max) || 100,
|
||||
fv = byte ? String.format('%1024.2mB', value) : value,
|
||||
fm = byte ? String.format('%1024.2mB', max) : max,
|
||||
pc = Math.floor((100 / mn) * vn);
|
||||
|
||||
return E('div', {
|
||||
'class': 'cbi-progressbar',
|
||||
'title': '%s / %s (%d%%)'.format(fv, fm, pc)
|
||||
}, E('div', { 'style': 'width:%.2f%%'.format(pc) }));
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(callSystemBoard(), {}),
|
||||
L.resolveDefault(callSystemInfo(), {})
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var boardinfo = data[0],
|
||||
systeminfo = data[1],
|
||||
mem = L.isObject(systeminfo.memory) ? systeminfo.memory : {},
|
||||
swap = L.isObject(systeminfo.swap) ? systeminfo.swap : {},
|
||||
datestr = null;
|
||||
|
||||
if (systeminfo.localtime) {
|
||||
var date = new Date(systeminfo.localtime * 1000);
|
||||
|
||||
datestr = '%04d-%02d-%02d %02d:%02d:%02d'.format(
|
||||
date.getUTCFullYear(),
|
||||
date.getUTCMonth() + 1,
|
||||
date.getUTCDate(),
|
||||
date.getUTCHours(),
|
||||
date.getUTCMinutes(),
|
||||
date.getUTCSeconds()
|
||||
);
|
||||
}
|
||||
|
||||
var sysfields = [
|
||||
_('Hostname'), boardinfo.hostname,
|
||||
_('Model'), boardinfo.model,
|
||||
_('Architecture'), boardinfo.system,
|
||||
_('Firmware Version'), (L.isObject(boardinfo.release) ? boardinfo.release.description : '?'),
|
||||
_('Kernel Version'), boardinfo.kernel,
|
||||
_('Local Time'), datestr,
|
||||
_('Uptime'), systeminfo.uptime ? '%t'.format(systeminfo.uptime) : null,
|
||||
_('Load Average'), Array.isArray(systeminfo.load) ? '%.2f, %.2f, %.2f'.format(
|
||||
systeminfo.load[0] / 65535.0,
|
||||
systeminfo.load[1] / 65535.0,
|
||||
systeminfo.load[2] / 65535.0
|
||||
) : null
|
||||
];
|
||||
|
||||
var systable = E('table', { 'class': 'table' });
|
||||
|
||||
for (var i = 0; i < sysfields.length; i += 2) {
|
||||
systable.appendChild(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '33%' }, [ sysfields[i] ]),
|
||||
E('td', { 'class': 'td left' }, [ (sysfields[i + 1] != null) ? sysfields[i + 1] : '?' ])
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
var memfields = [
|
||||
_('Total Available'), (mem.available) ? mem.available : (mem.total && mem.free && mem.buffered) ? mem.free + mem.buffered : null, mem.total,
|
||||
_('Used'), (mem.total && mem.free) ? (mem.total - mem.free) : null, mem.total,
|
||||
_('Buffered'), (mem.total && mem.buffered) ? mem.buffered : null, mem.total
|
||||
];
|
||||
|
||||
if (mem.cached)
|
||||
memfields.push(_('Cached'), mem.cached, mem.total);
|
||||
|
||||
if (swap.total > 0)
|
||||
memfields.push(_('Swap free'), swap.free, swap.total);
|
||||
|
||||
var memtable = E('table', { 'class': 'table' });
|
||||
|
||||
for (var i = 0; i < memfields.length; i += 3) {
|
||||
memtable.appendChild(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '33%' }, [ memfields[i] ]),
|
||||
E('td', { 'class': 'td left' }, [
|
||||
(memfields[i + 1] != null) ? progressbar(memfields[i + 1], memfields[i + 2], true) : '?'
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
return E([], [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', _('System')),
|
||||
systable
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', _('Memory')),
|
||||
memtable
|
||||
]),
|
||||
]);
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,210 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require dom';
|
||||
'require form';
|
||||
'require rpc';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var callSystemValidateFirmwareImage = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'validate_firmware_image',
|
||||
params: [ 'path' ],
|
||||
expect: { '': { valid: false, forcable: true } }
|
||||
});
|
||||
|
||||
var callReboot = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'reboot',
|
||||
expect: { result: 0 }
|
||||
});
|
||||
|
||||
var mapdata = { actions: {}, config: {} };
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
var tasks = [
|
||||
fs.trimmed('/proc/mtd'),
|
||||
fs.trimmed('/proc/mounts')
|
||||
];
|
||||
|
||||
return Promise.all(tasks);
|
||||
},
|
||||
|
||||
handleFirstboot: function(ev) {
|
||||
if (!confirm(_('Do you really want to erase all settings?')))
|
||||
return;
|
||||
|
||||
uctool.showProgress(_('The system is erasing the configuration partition now and will reboot itself when finished.'));
|
||||
|
||||
/* Currently the sysupgrade rpc call will not return, hence no promise handling */
|
||||
fs.exec('/sbin/firstboot', [ '-r', '-y' ]);
|
||||
|
||||
ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
|
||||
},
|
||||
|
||||
handleDiagnostics: function(ev) {
|
||||
return fs.exec('/sbin/diagnostic-bundle').then(function(result) {
|
||||
var form = E('form', {
|
||||
method: 'post',
|
||||
action: L.env.cgi_base + '/cgi-download',
|
||||
enctype: 'application/x-www-form-urlencoded'
|
||||
}, [
|
||||
E('input', { 'type': 'hidden', 'name': 'sessionid', 'value': L.env.sessionid }),
|
||||
E('input', { 'type': 'hidden', 'name': 'path', 'value': '/tmp/bundle.maverick.tar.gz' }),
|
||||
E('input', { 'type': 'hidden', 'name': 'filename', 'value': 'bundle.maverick.tar.gz' }),
|
||||
E('input', { 'type': 'hidden', 'name': 'mimetype', 'value': 'application/gzip' })
|
||||
]);
|
||||
|
||||
document.body.appendChild(form);
|
||||
|
||||
form.submit();
|
||||
form.parentNode.removeChild(form);
|
||||
});
|
||||
},
|
||||
|
||||
handleSysupgrade: function(ev) {
|
||||
return ui.uploadFile('/tmp/firmware.bin', ev.target.firstChild)
|
||||
.then(L.bind(function(btn, reply) {
|
||||
btn.firstChild.data = _('Checking image…');
|
||||
uctool.showProgress(_('Verifying the uploaded image file.'));
|
||||
|
||||
return callSystemValidateFirmwareImage('/tmp/firmware.bin')
|
||||
.then(function(res) { return [ reply, res ]; });
|
||||
}, this, ev.target))
|
||||
.then(L.bind(function(btn, reply) {
|
||||
return fs.exec('/sbin/sysupgrade', [ '--test', '/tmp/firmware.bin' ])
|
||||
.then(function(res) { reply.push(res); return reply; });
|
||||
}, this, ev.target))
|
||||
.then(L.bind(function(btn, res) {
|
||||
var is_valid = res[1].valid,
|
||||
body = [];
|
||||
|
||||
if (is_valid) {
|
||||
body.push(E('p', _("The firmware image was uploaded. Compare the checksum and file size listed below with the original file to ensure data integrity. <br /> Click 'Continue' below to start the flash procedure.")));
|
||||
body.push(E('ul', {}, [
|
||||
res[0].size ? E('li', {}, '%s: %1024.2mB'.format(_('Size'), res[0].size)) : '',
|
||||
res[0].checksum ? E('li', {}, '%s: %s'.format(_('MD5'), res[0].checksum)) : '',
|
||||
res[0].sha256sum ? E('li', {}, '%s: %s'.format(_('SHA256'), res[0].sha256sum)) : ''
|
||||
]));
|
||||
}
|
||||
else {
|
||||
body.push(E('p', _("The firmware image is invalid and cannot be flashed. Check the diagnostics below for further details.")));
|
||||
|
||||
if (res[2].stderr)
|
||||
body.push(E('pre', { 'class': 'alert-message' }, [ res[2].stderr ]));
|
||||
}
|
||||
|
||||
var cntbtn = E('button', {
|
||||
'class': 'btn cbi-button-action important',
|
||||
'click': ui.createHandlerFn(this, 'handleSysupgradeConfirm', btn)
|
||||
}, [ _('Continue') ]);
|
||||
if (!is_valid)
|
||||
cntbtn.disabled = true;
|
||||
|
||||
body.push(E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.createHandlerFn(this, function(ev) {
|
||||
return fs.remove('/tmp/firmware.bin').finally(ui.hideModal);
|
||||
})
|
||||
}, [ _('Cancel') ]), ' ', cntbtn
|
||||
]));
|
||||
|
||||
ui.showModal(is_valid ? _('Flash image?') : _('Invalid image'), body);
|
||||
}, this, ev.target))
|
||||
.catch(function(e) { uctool.showError(e.message) })
|
||||
.finally(L.bind(function(btn) {
|
||||
btn.firstChild.data = _('Flash image…');
|
||||
}, this, ev.target));
|
||||
},
|
||||
|
||||
handleSysupgradeConfirm: function(btn, ev) {
|
||||
btn.firstChild.data = _('Flashing…');
|
||||
|
||||
uctool.showProgress(_('The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings.'));
|
||||
|
||||
/* Currently the sysupgrade rpc call will not return, hence no promise handling */
|
||||
fs.exec('/sbin/sysupgrade', [ '-n', '/tmp/firmware.bin' ]);
|
||||
|
||||
ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
|
||||
},
|
||||
|
||||
handleReboot: function(ev) {
|
||||
return callReboot().then(function(res) {
|
||||
if (res != 0) {
|
||||
uctool.showError(_('The reboot command failed with code %d').format(res));
|
||||
L.raise('Error', 'Reboot failed');
|
||||
}
|
||||
|
||||
uctool.showProgress(_('The device is rebooting now. This page will try to reconnect automatically once the device is fully booted.'));
|
||||
|
||||
ui.awaitReconnect();
|
||||
})
|
||||
.catch(function(e) { uctool.showError(e.message) });
|
||||
},
|
||||
|
||||
render: function(rpc_replies) {
|
||||
var procmtd = rpc_replies[0],
|
||||
procmounts = rpc_replies[1],
|
||||
has_rootfs_data = (procmtd.match(/"rootfs_data"/) != null) || (procmounts.match("overlayfs:\/overlay \/ ") != null),
|
||||
m, s, o, ss;
|
||||
|
||||
m = new form.JSONMap(mapdata);
|
||||
m.readonly = !L.hasViewPermission();
|
||||
|
||||
s = m.section(form.NamedSection, 'actions');
|
||||
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Reboot device'),
|
||||
_('Issue a reboot and restart the operating system on this device.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
o = ss.option(form.Button, 'reboot');
|
||||
o.inputstyle = 'action important';
|
||||
o.inputtitle = _('Reboot');
|
||||
o.onclick = L.bind(this.handleReboot, this);
|
||||
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Reset to defaults'),
|
||||
_('Reset the system to its initial state and discard any configuration changes.'));
|
||||
ss = o.subsection;
|
||||
|
||||
if (has_rootfs_data) {
|
||||
o = ss.option(form.Button, 'reset');
|
||||
o.inputstyle = 'negative important';
|
||||
o.inputtitle = _('Perform reset');
|
||||
o.onclick = this.handleFirstboot;
|
||||
}
|
||||
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Firmware upgrade'),
|
||||
_('Upload a compatible firmware image here to upgrade the running system.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
o = ss.option(form.Button, 'sysupgrade');
|
||||
o.inputstyle = 'action important';
|
||||
o.inputtitle = _('Flash image…');
|
||||
o.onclick = L.bind(this.handleSysupgrade, this);
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Diagnostic bundle'),
|
||||
_('Download the default diagnostic bundle from the AP.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
o = ss.option(form.Button, 'Diagnostic');
|
||||
o.inputstyle = 'action important';
|
||||
o.inputtitle = _('Download Diagnostics');
|
||||
o.onclick = L.bind(this.handleDiagnostics, this);
|
||||
|
||||
|
||||
return m.render();
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,138 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require fs';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var profile = null;
|
||||
|
||||
function serialize(data) {
|
||||
if (data.broadband.protocol != 'default')
|
||||
profile.broadband = Object.assign({}, data.broadband);
|
||||
else
|
||||
delete profile.broadband;
|
||||
|
||||
return JSON.stringify(profile, function(key, val) {
|
||||
return (key.charAt(0) != '.') ? val : undefined;
|
||||
}, '\t');
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return L.resolveDefault(fs.read('/etc/ucentral/profile.json'), '').then(function(data) {
|
||||
try { profile = JSON.parse(data); }
|
||||
catch(e) { profile = {}; };
|
||||
|
||||
if (!L.isObject(profile.broadband))
|
||||
profile.broadband = { protocol: 'default' };
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var m, s, o, data = { broadband: {} };
|
||||
|
||||
m = new form.JSONMap(data);
|
||||
m.readonly = !L.hasViewPermission();
|
||||
|
||||
s = m.section(form.NamedSection, 'broadband', 'broadband', _('Uplink configuration'),
|
||||
_('The uplink settings allow overriding the WAN connection properties of the local device.'));
|
||||
|
||||
o = s.option(form.ListValue, 'protocol', _('Connection'));
|
||||
o.value('default', _('Use default cloud settings'));
|
||||
o.value('static', _('Static address configuration'));
|
||||
o.value('dhcp', _('Address configuration via DHCP'));
|
||||
o.value('pppoe', _('Address configuration via PPPoE'));
|
||||
o.value('wwan', _('Cellular network connection'));
|
||||
o.value('wds', _('WiFi WDS uplink'));
|
||||
|
||||
o = s.option(form.ListValue, 'modem-type', _('Modem type'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.value('wwan', _('Automatic', 'Automatic modem type selection'));
|
||||
o.value('mbim', 'MBIM');
|
||||
o.value('qmi', 'QMI');
|
||||
|
||||
o = s.option(form.Value, 'access-point-name', _('APN', 'Cellular access point name'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.validate = function(section_id, value) {
|
||||
if (!/^[a-zA-Z0-9\-.]*[a-zA-Z0-9]$/.test(value))
|
||||
return _('Invalid APN provided');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
o = s.option(form.Value, 'pin-code', _('PIN'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.datatype = 'and(uinteger,minlength(4),maxlength(8))';
|
||||
|
||||
o = s.option(form.ListValue, 'authentication-type', _('Authentication'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.value('', _('No authentication'));
|
||||
o.value('pap-chap', 'PAP/CHAP');
|
||||
o.value('chap', 'CHAP');
|
||||
o.value('pap', 'PAP');
|
||||
|
||||
o = s.option(form.Value, 'user-name', _('Username'));
|
||||
o.depends('authentication-type', 'pap-chap');
|
||||
o.depends('authentication-type', 'chap');
|
||||
o.depends('authentication-type', 'pap');
|
||||
o.depends('protocol', 'pppoe');
|
||||
|
||||
o = s.option(form.Value, 'password', _('Password'));
|
||||
o.depends('authentication-type', 'pap-chap');
|
||||
o.depends('authentication-type', 'chap');
|
||||
o.depends('authentication-type', 'pap');
|
||||
o.depends('protocol', 'pppoe');
|
||||
o.password = true;
|
||||
|
||||
o = s.option(form.Value, 'ipv4-address', _('IPv4 Address'), _('Address and mask in CIDR notation.'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'or(cidr4,ipnet4)';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'ipv4-gateway', _('IPv4 Gateway'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'ip4addr("nomask")';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'ipv6-address', _('IPv6 Address'), _('Address and mask in CIDR notation.'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'or(cidr6,ipnet6)';
|
||||
|
||||
o = s.option(form.Value, 'ipv6-gateway', _('IPv6 Gateway'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'ip6addr("nomask")';
|
||||
|
||||
o = s.option(form.DynamicList, 'use-dns', _('DNS Servers'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'ipaddr("nomask")';
|
||||
|
||||
o = s.option(form.Value, 'ssid', _('SSID'));
|
||||
o.depends('protocol', 'wds');
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'passphrase', _('Passphrase'));
|
||||
o.depends('protocol', 'wds');
|
||||
o.password = true;
|
||||
o.rmempty = false;
|
||||
o.datatype = "rangelength(8, 31)";
|
||||
|
||||
o = s.option(form.ListValue, 'encryption', _('Encryption'));
|
||||
o.depends('protocol', 'wds');
|
||||
o.value('psk', 'PSK');
|
||||
o.value('psk-mixed', 'PSK-Mixed');
|
||||
o.value('psk2', 'PSK2');
|
||||
o.value('sae', 'SAE');
|
||||
o.value('sae-mixed', 'SAE-Mixed');
|
||||
o.password = true;
|
||||
|
||||
for (var i = 0; i < s.children.length; i++)
|
||||
data.broadband[s.children[i].option] = profile.broadband[s.children[i].option];
|
||||
|
||||
return m.render();
|
||||
},
|
||||
|
||||
handleSave: uctool.save.bind(uctool, serialize),
|
||||
|
||||
handleSaveApply: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,426 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: \n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"X-Generator: Poedit 2.4.2\n"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:53
|
||||
msgctxt "Cellular access point name"
|
||||
msgid "APN"
|
||||
msgstr "APN"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "Address and mask in CIDR notation."
|
||||
msgstr "Adresse und Netzmaske in CIDR-Notation."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:43
|
||||
msgid "Address configuration via DHCP"
|
||||
msgstr "Adresskonfiguration mittels DHCP"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:44
|
||||
msgid "Address configuration via PPPoE"
|
||||
msgstr "Adresskonfiguration mittels PPPoE"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:49
|
||||
msgid "Apply Settings"
|
||||
msgstr "Einstellungen anwenden"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:52
|
||||
msgid "Apply settings and reboot device now"
|
||||
msgstr "Anwenden und Gerät jetzt neu starten"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:60
|
||||
msgid "Architecture"
|
||||
msgstr "Architektur"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:66
|
||||
msgid "Authentication"
|
||||
msgstr "Authentifizierung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:49
|
||||
msgctxt "Automatic modem type selection"
|
||||
msgid "Automatic"
|
||||
msgstr "automatisch"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:85
|
||||
msgid "Buffered"
|
||||
msgstr "Gepuffert"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:89
|
||||
msgid "Cached"
|
||||
msgstr "Gecached"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:92
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:45
|
||||
msgid "Cellular network connection"
|
||||
msgstr "Mobilfunkverbindung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:51
|
||||
msgid "Certificate validation failed: %s"
|
||||
msgstr "Validierung der Zertifikate fehlgeschlagen: %s"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:43
|
||||
msgid "Certificates"
|
||||
msgstr "Zertifikate"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:54
|
||||
msgid "Certificates updated."
|
||||
msgstr "Zertifikate aktualisiert."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:51
|
||||
msgid "Checking image…"
|
||||
msgstr "Prüfe Imagedatei…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:40
|
||||
msgid "Connection"
|
||||
msgstr "Verbindung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:84
|
||||
msgid "Continue"
|
||||
msgstr "Fortfahren"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:54
|
||||
msgid "Continue configuration"
|
||||
msgstr "Konfiguration fortsetzen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:104
|
||||
msgid "DNS Servers"
|
||||
msgstr "DNS-Server"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:37
|
||||
msgid "Do you really want to erase all settings?"
|
||||
msgstr "Sollen wirklich alle Systemeinstellungen zurückgesetzt werden?"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:73
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:61
|
||||
msgid "Firmware Version"
|
||||
msgstr "Firmware-Version"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:163
|
||||
msgid "Firmware upgrade"
|
||||
msgstr "Firmware Update"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Flash image?"
|
||||
msgstr "Image flashen?"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:99
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:170
|
||||
msgid "Flash image…"
|
||||
msgstr "Image flashen…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:104
|
||||
msgid "Flashing…"
|
||||
msgstr "Schreibt…"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/rpcd/acl.d/luci-mod-ucentral.json:3
|
||||
msgid "Grant access to ucentral configuration"
|
||||
msgstr "Zugriff auf uCentral-Konfigurationseinstellungen ermöglichen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:58
|
||||
msgid "Hostname"
|
||||
msgstr "Hostname"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
msgid "IPv4 Address"
|
||||
msgstr "IPv4-Adresse"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:91
|
||||
msgid "IPv4 Gateway"
|
||||
msgstr "IPv4-Gateway"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "IPv6 Address"
|
||||
msgstr "IPv6-Adresse"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:100
|
||||
msgid "IPv6 Gateway"
|
||||
msgstr "IPv6-Gateway"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:57
|
||||
msgid "Invalid APN provided"
|
||||
msgstr "Ungültige APN angegeben"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Invalid image"
|
||||
msgstr "Ungültige Image-Datei"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:141
|
||||
msgid "Issue a reboot and restart the operating system on this device."
|
||||
msgstr "Einen Reboot auslösen und das Betriebssystem des Gerätes neu starten."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:62
|
||||
msgid "Kernel Version"
|
||||
msgstr "Kernel-Version"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:65
|
||||
msgid "Load Average"
|
||||
msgstr "Systemlast"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:63
|
||||
msgid "Local Time"
|
||||
msgstr "Lokalzeit"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:37
|
||||
msgid "Local settings"
|
||||
msgstr "Lokale Einstellungen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:69
|
||||
msgid "MD5"
|
||||
msgstr "MD5"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:112
|
||||
msgid "Memory"
|
||||
msgstr "Arbeitsspeicher"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:59
|
||||
msgid "Model"
|
||||
msgstr "Modell"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:47
|
||||
msgid "Modem type"
|
||||
msgstr "Modemtyp"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:68
|
||||
msgid "No authentication"
|
||||
msgstr "keine Authentifizierung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:76
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:62
|
||||
msgid "PIN"
|
||||
msgstr "OIN"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:79
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:158
|
||||
msgid "Perform reset"
|
||||
msgstr "System zurücksetzen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:147
|
||||
msgid "Reboot"
|
||||
msgstr "Reboot"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:140
|
||||
msgid "Reboot device"
|
||||
msgstr "Gerät neu starten"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:35
|
||||
msgid "Reboot required"
|
||||
msgstr "Neustart erforderlich"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:40
|
||||
msgid "Redirector URL"
|
||||
msgstr "Redirector-URL"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:152
|
||||
msgid ""
|
||||
"Reset the system to its initial state and discard any configuration changes."
|
||||
msgstr ""
|
||||
"Das System auf Grundeinstellungen zurücksetzen und sämtliche "
|
||||
"Konfigurationsänderungen verwerfen."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:151
|
||||
msgid "Reset to defaults"
|
||||
msgstr "Grundeinstellungen wiederherstellen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:70
|
||||
msgid "SHA256"
|
||||
msgstr "SHA256"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:40
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:68
|
||||
msgid "Size"
|
||||
msgstr "Größe"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:42
|
||||
msgid "Static address configuration"
|
||||
msgstr "Statische Adresskonfiguration"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:16
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:92
|
||||
msgid "Swap free"
|
||||
msgstr "Freier Auslagerungsspeicher"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:108
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:52
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:121
|
||||
msgid ""
|
||||
"The device is rebooting now. This page will try to reconnect automatically "
|
||||
"once the device is fully booted."
|
||||
msgstr ""
|
||||
"Das Gerät startet jetzt neu. Diese Seite wird versuchen sich automatisch neu "
|
||||
"zu verbinden sobald das Gerät wieder voll hochgefahren ist."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:50
|
||||
msgid ""
|
||||
"The device must be rebooted in order to apply the changed settings. Once the "
|
||||
"uCentral agent successfully connects to the controller, the remote "
|
||||
"configuration profile will be applied and the initial provisioning web "
|
||||
"interface is disabled."
|
||||
msgstr ""
|
||||
"Das Gerät muss neu gestartet werden um die geänderten Einstellungen "
|
||||
"anzuwenden. Sobald sich der uCentral-Client nach dem Neustart erfolgreich "
|
||||
"mit dem Controller verbindet, wird das entfernte Konfigurationsprofil für "
|
||||
"dieses Gerät angewendet und das initiale Provisionierungs-Webinterface "
|
||||
"deaktiviert."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:74
|
||||
msgid ""
|
||||
"The firmware image is invalid and cannot be flashed. Check the diagnostics "
|
||||
"below for further details."
|
||||
msgstr ""
|
||||
"Die Firmware-Datei ist ungültig und kann nicht geflasht werden. Die "
|
||||
"nachfolgenden Diagnosemeldungen enthalten weitere Details."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:66
|
||||
msgid ""
|
||||
"The firmware image was uploaded. Compare the checksum and file size listed "
|
||||
"below with the original file to ensure data integrity. <br /> Click "
|
||||
"'Continue' below to start the flash procedure."
|
||||
msgstr ""
|
||||
"Die Firmware-Datei wurde hochgeladen. Die Prüfsummen und Dateigröße mit der "
|
||||
"Originaldatei vergleichen um die Integrität des Images sicherzustellen.<br /"
|
||||
"> \"Fortfahren\" anklicken um die Upgrade-Prozedur zu starten."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:22
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:117
|
||||
msgid "The reboot command failed with code %d"
|
||||
msgstr "Das Reboot-Kommando wurde mit Fehlercode %d abgebrochen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:38
|
||||
msgid ""
|
||||
"The settings on this page specify how the local uCentral client connects to "
|
||||
"the controller server."
|
||||
msgstr ""
|
||||
"Die Einstellungen auf dieser Seite beeinflussen die Verbindung des uCentral "
|
||||
"Clients zum Controller-Server."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:40
|
||||
msgid ""
|
||||
"The system is erasing the configuration partition now and will reboot itself "
|
||||
"when finished."
|
||||
msgstr ""
|
||||
"Das System löscht nun die Konfigurationspartition und startet das Gerät neu "
|
||||
"sobald die Prozedur abgeschlossen ist."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:106
|
||||
msgid ""
|
||||
"The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a "
|
||||
"few minutes before you try to reconnect. It might be necessary to renew the "
|
||||
"address of your computer to reach the device again, depending on your "
|
||||
"settings."
|
||||
msgstr ""
|
||||
"Das System-Upgrade läuft jetzt.<br />DAS GERÄT NICHT AUSSCHALTEN!<br /"
|
||||
">Einige Minuten warten, bevor ein Verbindungsversuch unternommen wird. Ja "
|
||||
"nach Netzwerktopologie kann es nötig sein, die lokalen Adresseinstellungen "
|
||||
"des Computers zu verändern bevor wieder eine Verbindung zum Gerät möglich "
|
||||
"ist."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:26
|
||||
msgid ""
|
||||
"The system is rebooting in order to attempt applying the remote "
|
||||
"configuration profile now. If not successful, the device will revert back "
|
||||
"into the initial provisioning state."
|
||||
msgstr ""
|
||||
"Das System startet jetzt neu um zu versuchen das entfernte "
|
||||
"Konfigurationsprofil anzuwenden. Im Fehlerfall wird das Gerät in den "
|
||||
"initialen Provisionierungs-Zustand zurückversetzt."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:38
|
||||
msgid ""
|
||||
"The uplink settings allow overriding the WAN connection properties of the "
|
||||
"local device."
|
||||
msgstr ""
|
||||
"Die Uplink-Einstellungen ermöglichen es die WAN-Verbindungseigenschaften des "
|
||||
"lokalen Gerätes zu überschreiben."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:83
|
||||
msgid "Total Available"
|
||||
msgstr "Gesamt verfügbar"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:95
|
||||
msgid "Unable to save settings: %s"
|
||||
msgstr "Einstellungen konnten nicht gespeichert werden: %s"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:58
|
||||
msgid "Unable to verify certificates: %s"
|
||||
msgstr "Zertifikate konnten nicht verifiziert werden: %s"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:41
|
||||
msgid "Unit location"
|
||||
msgstr "Gerätestandort"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:28
|
||||
msgid "Uplink"
|
||||
msgstr "Uplink"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:37
|
||||
msgid "Uplink configuration"
|
||||
msgstr "Uplink-Einstellungen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:164
|
||||
msgid "Upload a compatible firmware image here to upgrade the running system."
|
||||
msgstr ""
|
||||
"Kompatible Firmware-Datei hier hochladen um das laufende System zu "
|
||||
"aktualisieren."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:44
|
||||
msgid "Upload certificate bundle…"
|
||||
msgstr "Zertifikatsarchiv hochladen…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:64
|
||||
msgid "Uptime"
|
||||
msgstr "Laufzeit"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:41
|
||||
msgid "Use default cloud settings"
|
||||
msgstr "Cloud-Einstellungen nutzen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:84
|
||||
msgid "Used"
|
||||
msgstr "In Benutzung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:73
|
||||
msgid "Username"
|
||||
msgstr "Benutzername"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:47
|
||||
msgid "Verifying certificates…"
|
||||
msgstr "Überprüfe Zertifikate…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:52
|
||||
msgid "Verifying the uploaded image file."
|
||||
msgstr "Überprüfe die hochgeladene Image-Datei."
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:3
|
||||
msgid "uCentral"
|
||||
msgstr "uCentral"
|
||||
@@ -0,0 +1,385 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:53
|
||||
msgctxt "Cellular access point name"
|
||||
msgid "APN"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "Address and mask in CIDR notation."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:43
|
||||
msgid "Address configuration via DHCP"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:44
|
||||
msgid "Address configuration via PPPoE"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:49
|
||||
msgid "Apply Settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:52
|
||||
msgid "Apply settings and reboot device now"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:60
|
||||
msgid "Architecture"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:66
|
||||
msgid "Authentication"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:49
|
||||
msgctxt "Automatic modem type selection"
|
||||
msgid "Automatic"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:85
|
||||
msgid "Buffered"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:89
|
||||
msgid "Cached"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:92
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:45
|
||||
msgid "Cellular network connection"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:51
|
||||
msgid "Certificate validation failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:43
|
||||
msgid "Certificates"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:54
|
||||
msgid "Certificates updated."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:51
|
||||
msgid "Checking image…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:40
|
||||
msgid "Connection"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:84
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:54
|
||||
msgid "Continue configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:104
|
||||
msgid "DNS Servers"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:37
|
||||
msgid "Do you really want to erase all settings?"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:73
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:61
|
||||
msgid "Firmware Version"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:163
|
||||
msgid "Firmware upgrade"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Flash image?"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:99
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:170
|
||||
msgid "Flash image…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:104
|
||||
msgid "Flashing…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/rpcd/acl.d/luci-mod-ucentral.json:3
|
||||
msgid "Grant access to ucentral configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:58
|
||||
msgid "Hostname"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
msgid "IPv4 Address"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:91
|
||||
msgid "IPv4 Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "IPv6 Address"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:100
|
||||
msgid "IPv6 Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:57
|
||||
msgid "Invalid APN provided"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Invalid image"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:141
|
||||
msgid "Issue a reboot and restart the operating system on this device."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:62
|
||||
msgid "Kernel Version"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:65
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:63
|
||||
msgid "Local Time"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:37
|
||||
msgid "Local settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:69
|
||||
msgid "MD5"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:112
|
||||
msgid "Memory"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:59
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:47
|
||||
msgid "Modem type"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:68
|
||||
msgid "No authentication"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:76
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:62
|
||||
msgid "PIN"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:79
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:158
|
||||
msgid "Perform reset"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:147
|
||||
msgid "Reboot"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:140
|
||||
msgid "Reboot device"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:35
|
||||
msgid "Reboot required"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:40
|
||||
msgid "Redirector URL"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:152
|
||||
msgid ""
|
||||
"Reset the system to its initial state and discard any configuration changes."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:151
|
||||
msgid "Reset to defaults"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:70
|
||||
msgid "SHA256"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:40
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:68
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:42
|
||||
msgid "Static address configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:16
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:92
|
||||
msgid "Swap free"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:108
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:52
|
||||
msgid "System"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:121
|
||||
msgid ""
|
||||
"The device is rebooting now. This page will try to reconnect automatically "
|
||||
"once the device is fully booted."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:50
|
||||
msgid ""
|
||||
"The device must be rebooted in order to apply the changed settings. Once the "
|
||||
"uCentral agent successfully connects to the controller, the remote "
|
||||
"configuration profile will be applied and the initial provisioning web "
|
||||
"interface is disabled."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:74
|
||||
msgid ""
|
||||
"The firmware image is invalid and cannot be flashed. Check the diagnostics "
|
||||
"below for further details."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:66
|
||||
msgid ""
|
||||
"The firmware image was uploaded. Compare the checksum and file size listed "
|
||||
"below with the original file to ensure data integrity. <br /> Click "
|
||||
"'Continue' below to start the flash procedure."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:22
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:117
|
||||
msgid "The reboot command failed with code %d"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:38
|
||||
msgid ""
|
||||
"The settings on this page specify how the local uCentral client connects to "
|
||||
"the controller server."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:40
|
||||
msgid ""
|
||||
"The system is erasing the configuration partition now and will reboot itself "
|
||||
"when finished."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:106
|
||||
msgid ""
|
||||
"The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a "
|
||||
"few minutes before you try to reconnect. It might be necessary to renew the "
|
||||
"address of your computer to reach the device again, depending on your "
|
||||
"settings."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:26
|
||||
msgid ""
|
||||
"The system is rebooting in order to attempt applying the remote "
|
||||
"configuration profile now. If not successful, the device will revert back "
|
||||
"into the initial provisioning state."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:38
|
||||
msgid ""
|
||||
"The uplink settings allow overriding the WAN connection properties of the "
|
||||
"local device."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:83
|
||||
msgid "Total Available"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:95
|
||||
msgid "Unable to save settings: %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:58
|
||||
msgid "Unable to verify certificates: %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:41
|
||||
msgid "Unit location"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:28
|
||||
msgid "Uplink"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:37
|
||||
msgid "Uplink configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:164
|
||||
msgid "Upload a compatible firmware image here to upgrade the running system."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:44
|
||||
msgid "Upload certificate bundle…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:64
|
||||
msgid "Uptime"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:41
|
||||
msgid "Use default cloud settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:84
|
||||
msgid "Used"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:73
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:47
|
||||
msgid "Verifying certificates…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:52
|
||||
msgid "Verifying the uploaded image file."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:3
|
||||
msgid "uCentral"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
# make sure we have a tar file
|
||||
[ -f /tmp/certs.tar ] || exit 1
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
# amke sure the cert partition is mounted
|
||||
mount_certs
|
||||
|
||||
# make sure that this is a UBI volume
|
||||
ubi=$(grep certificates /proc/mounts | tail -n 1 | grep ubi)
|
||||
[ -z "$ubi" ] && exit 1
|
||||
|
||||
# extract the certificates
|
||||
mkdir -p /tmp/certs
|
||||
tar x -C /tmp/certs -f /tmp/certs.tar
|
||||
|
||||
# make sure the required files exist
|
||||
[ -f /tmp/certs/key.pem -a -f /tmp/certs/cert.pem ] || exit 1
|
||||
|
||||
# copy the certificates to /etc
|
||||
cp /tmp/certs/*.pem /certificates
|
||||
|
||||
# remove old operational certs
|
||||
rm /certificates/operational.*
|
||||
|
||||
# copy dev-id or gateway.json
|
||||
for a in gateway.json; do
|
||||
if [ -f /tmp/certs/$a ]; then
|
||||
cp /tmp/certs/$a /certificates
|
||||
else
|
||||
rm -f /certificates/$a
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/ucode
|
||||
push(REQUIRE_SEARCH_PATH, '/usr/share/ucentral/*.uc');
|
||||
let bundle = require('bundle');
|
||||
|
||||
bundle.init('maverick');
|
||||
include('/usr/share/ucentral/diagnostic.uc', { bundle });
|
||||
bundle.complete();
|
||||
system('chmod +r /tmp/bundle.maverick.tar.gz');
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
REDIRECTOR=$(cat /etc/ucentral/profile.json | jsonfilter -e '@.redirector')
|
||||
if [ -n "$REDIRECTOR" ]; then
|
||||
uci -c /etc/config-shadow/ set ucentral.config.server="$REDIRECTOR"
|
||||
uci -c /etc/config-shadow/ commit ucentral
|
||||
/etc/init.d/firstcontact disable
|
||||
/etc/init.d/ucentral enable
|
||||
else
|
||||
rm /etc/ucentral/redirector.json
|
||||
/etc/init.d/firstcontact enable
|
||||
/etc/init.d/ucentral disable
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"ucentral": {
|
||||
"title": "uCentral",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "firstchild",
|
||||
"recurse": true
|
||||
},
|
||||
"auth": {
|
||||
"methods": [ "cookie:sysauth", "cookie:sysauth_http", "cookie:sysauth_https" ],
|
||||
"login": true
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/status": {
|
||||
"title": "Status",
|
||||
"order": 1,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/status"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/uplink": {
|
||||
"title": "Uplink",
|
||||
"order": 2,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/uplink"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/settings": {
|
||||
"title": "Settings",
|
||||
"order": 3,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/settings"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/system": {
|
||||
"title": "System",
|
||||
"order": 4,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/system"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"luci-mod-ucentral": {
|
||||
"description": "Grant access to ucentral configuration",
|
||||
"read": {
|
||||
"cgi-io": [ "download" ],
|
||||
"file": {
|
||||
"/etc/ucentral/profile.json": [ "read" ],
|
||||
"/proc/mounts": [ "read" ],
|
||||
"/proc/mtd": [ "read" ],
|
||||
"/tmp/bundle.maverick.tar.gz": [ "read" ]
|
||||
},
|
||||
"ubus": {
|
||||
"file": [ "read" ],
|
||||
"system": [ "board", "info" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"cgi-io": [ "upload" ],
|
||||
"file": {
|
||||
"/etc/ucentral/profile.json": [ "write" ],
|
||||
"/sbin/certupdate": [ "exec" ],
|
||||
"/sbin/diagnostic-bundle": [ "exec" ],
|
||||
"/sbin/firstboot -r -y": [ "exec" ],
|
||||
"/sbin/profileupdate": [ "exec" ],
|
||||
"/sbin/sysupgrade -n /tmp/firmware.bin": [ "exec" ],
|
||||
"/sbin/sysupgrade --test /tmp/firmware.bin": [ "exec" ],
|
||||
"/tmp/certs.tar": [ "write" ],
|
||||
"/tmp/firmware.bin": [ "write" ]
|
||||
},
|
||||
"ubus": {
|
||||
"file": [ "exec", "remove", "write" ],
|
||||
"system": [ "reboot", "validate_firmware_image" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI theme for uCentral
|
||||
LUCI_DEPENDS:=+luci-lua-runtime
|
||||
|
||||
include ../luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -0,0 +1,152 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require ui';
|
||||
|
||||
return baseclass.extend({
|
||||
__init__: function() {
|
||||
ui.menu.load().then(L.bind(this.render, this));
|
||||
},
|
||||
|
||||
render: function(tree) {
|
||||
var menu = document.querySelector('#mainmenu'),
|
||||
nav = document.querySelector('#menubar > .navigation'),
|
||||
node = tree,
|
||||
url = '';
|
||||
|
||||
this.renderModeMenu(node);
|
||||
|
||||
if (L.env.dispatchpath.length >= 3) {
|
||||
for (var i = 0; i < 3 && node; i++) {
|
||||
node = node.children[L.env.dispatchpath[i]];
|
||||
url = url + (url ? '/' : '') + L.env.dispatchpath[i];
|
||||
}
|
||||
|
||||
if (node)
|
||||
this.renderTabMenu(node, url);
|
||||
}
|
||||
|
||||
if (menu.firstElementChild) {
|
||||
nav.addEventListener('click', ui.createHandlerFn(this, 'handleSidebarToggle'));
|
||||
nav.style.visibility = 'visible';
|
||||
}
|
||||
},
|
||||
|
||||
handleMenuExpand: function(ev) {
|
||||
var a = ev.target, ul1 = a.parentNode.parentNode, ul2 = a.nextElementSibling;
|
||||
|
||||
document.querySelectorAll('ul.mainmenu.l1 > li.active').forEach(function(li) {
|
||||
if (li !== a.parentNode)
|
||||
li.classList.remove('active');
|
||||
});
|
||||
|
||||
if (!ul2)
|
||||
return;
|
||||
|
||||
if (ul2.parentNode.offsetLeft + ul2.offsetWidth <= ul1.offsetLeft + ul1.offsetWidth)
|
||||
ul2.classList.add('align-left');
|
||||
|
||||
ul1.classList.add('active');
|
||||
a.parentNode.classList.add('active');
|
||||
a.blur();
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
},
|
||||
|
||||
renderMainMenu: function(tree, url, level) {
|
||||
var l = (level || 0) + 1,
|
||||
ul = E('ul', { 'class': 'mainmenu l%d'.format(l) }),
|
||||
children = ui.menu.getChildren(tree);
|
||||
|
||||
if (children.length == 0 || l > 2)
|
||||
return E([]);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.dispatchpath[l] == children[i].name),
|
||||
isReadonly = children[i].readonly,
|
||||
activeClass = 'mainmenu-item-%s%s'.format(children[i].name, isActive ? ' selected' : '');
|
||||
|
||||
ul.appendChild(E('li', { 'class': activeClass }, [
|
||||
E('a', {
|
||||
'href': L.url(url, children[i].name),
|
||||
'click': (l == 1) ? ui.createHandlerFn(this, 'handleMenuExpand') : null
|
||||
}, [ _(children[i].title) ]),
|
||||
this.renderMainMenu(children[i], url + '/' + children[i].name, l)
|
||||
]));
|
||||
}
|
||||
|
||||
if (l == 1)
|
||||
document.querySelector('#mainmenu').appendChild(E('div', [ ul ]));
|
||||
|
||||
return ul;
|
||||
},
|
||||
|
||||
renderModeMenu: function(tree, root) {
|
||||
var menu = document.querySelector('#modemenu'),
|
||||
children = ui.menu.getChildren(tree);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.requestpath.length ? children[i].name == L.env.requestpath[+!!root] : i == 0),
|
||||
isUcentral = (!root && children[i].name == 'ucentral');
|
||||
|
||||
if (root || children.length > 1)
|
||||
menu.appendChild(E('div', { 'class': isActive ? 'active' : null }, [
|
||||
E('a', { 'href': root ? L.url(root, children[i].name) : L.url(children[i].name) }, [ _(children[i].title) ])
|
||||
]));
|
||||
|
||||
if (isUcentral && isActive)
|
||||
this.renderModeMenu(children[i], children[i].name);
|
||||
else if (isActive)
|
||||
this.renderMainMenu(children[i], children[i].name);
|
||||
}
|
||||
|
||||
if (menu.children.length > 1)
|
||||
menu.style.display = '';
|
||||
},
|
||||
|
||||
renderTabMenu: function(tree, url, level) {
|
||||
var container = document.querySelector('#tabmenu'),
|
||||
l = (level || 0) + 1,
|
||||
ul = E('ul', { 'class': 'cbi-tabmenu' }),
|
||||
children = ui.menu.getChildren(tree),
|
||||
activeNode = null;
|
||||
|
||||
if (children.length == 0)
|
||||
return E([]);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.dispatchpath[l + 2] == children[i].name),
|
||||
activeClass = isActive ? ' cbi-tab' : '',
|
||||
className = 'tabmenu-item-%s %s'.format(children[i].name, activeClass);
|
||||
|
||||
ul.appendChild(E('li', { 'class': className }, [
|
||||
E('a', { 'href': L.url(url, children[i].name) }, [ _(children[i].title) ] )
|
||||
]));
|
||||
|
||||
if (isActive)
|
||||
activeNode = children[i];
|
||||
}
|
||||
|
||||
container.appendChild(ul);
|
||||
container.style.display = '';
|
||||
|
||||
if (activeNode)
|
||||
container.appendChild(this.renderTabMenu(activeNode, url + '/' + activeNode.name, l));
|
||||
|
||||
return ul;
|
||||
},
|
||||
|
||||
handleSidebarToggle: function(ev) {
|
||||
var btn = ev.currentTarget,
|
||||
bar = document.querySelector('#mainmenu');
|
||||
|
||||
if (btn.classList.contains('active')) {
|
||||
btn.classList.remove('active');
|
||||
bar.classList.remove('active');
|
||||
}
|
||||
else {
|
||||
btn.classList.add('active');
|
||||
bar.classList.add('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 251.2 114.2" style="enable-background:new 0 0 251.2 114.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FED206;}
|
||||
.st1{fill:#EB6F53;}
|
||||
.st2{fill:#3BA9B6;}
|
||||
.st3{fill:#414141;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M219.6,43.3C219.5,43.3,219.5,43.3,219.6,43.3c-1.3,0-2.2-1-2.2-2.2c0-0.2,0-0.4,0-0.6
|
||||
c0-11.9-9.7-21.6-21.6-21.6c-0.2,0-0.4,0-0.6,0c-1.2,0-2.2-0.9-2.2-2.1c0-1.2,0.9-2.2,2.1-2.2c0.2,0,0.5,0,0.7,0
|
||||
c14.3,0,25.9,11.6,25.9,25.9c0,0.2,0,0.5,0,0.7C221.7,42.4,220.7,43.3,219.6,43.3z"/>
|
||||
<path class="st1" d="M212.1,43.3C212,43.3,212,43.3,212.1,43.3c-1.3-0.1-2.2-1.1-2.2-2.3c0-0.2,0-0.4,0-0.6
|
||||
c0-7.7-6.3-14.1-14.1-14.1c-0.2,0-0.4,0-0.6,0c-1.2,0.1-2.2-0.9-2.3-2.1c0-1.2,0.9-2.2,2.1-2.3c0.3,0,0.5,0,0.8,0
|
||||
c10.2,0,18.4,8.3,18.4,18.4c0,0.2,0,0.5,0,0.8C214.2,42.4,213.2,43.3,212.1,43.3z"/>
|
||||
<path class="st2" d="M204.3,43.3c-0.1,0-0.1,0-0.2,0c-1.2-0.1-2.1-1.1-2-2.3c0-0.2,0-0.4,0-0.5c0-3.5-2.8-6.3-6.3-6.3
|
||||
c-0.1,0-0.3,0-0.5,0c-1.2,0.1-2.3-0.8-2.3-2c-0.1-1.2,0.8-2.3,2-2.3c0.3,0,0.6,0,0.9,0c5.9,0,10.7,4.8,10.7,10.7c0,0.3,0,0.5,0,0.9
|
||||
C206.4,42.4,205.4,43.3,204.3,43.3z"/>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st3" d="M61.9,89.9v-4.7h-1.7v-0.9h4.4v0.9h-1.7v4.7H61.9z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M65.6,89.9v-5.6h3.8v0.9h-2.9v1.4h2.8v0.9h-2.8V89h2.9v0.9H65.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M70.7,89.9v-5.6h1V89h2.5v0.9H70.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M74.9,89.9v-5.6h3.8v0.9h-2.9v1.4h2.8v0.9h-2.8V89h2.9v0.9H74.9z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M79.8,87.1c0-1.7,1.3-2.9,2.9-2.9c1.1,0,1.8,0.6,2.2,1.3l-0.8,0.4c-0.3-0.5-0.8-0.8-1.4-0.8
|
||||
c-1.1,0-1.9,0.8-1.9,2c0,1.2,0.8,2,1.9,2c0.6,0,1.1-0.4,1.4-0.8l0.8,0.4c-0.4,0.7-1.1,1.3-2.2,1.3C81.1,90,79.8,88.8,79.8,87.1z
|
||||
"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M85.5,87.1c0-1.7,1.2-2.9,2.9-2.9c1.7,0,2.9,1.2,2.9,2.9S90,90,88.3,90C86.7,90,85.5,88.8,85.5,87.1z
|
||||
M90.2,87.1c0-1.2-0.7-2-1.9-2c-1.1,0-1.9,0.9-1.9,2c0,1.1,0.7,2,1.9,2C89.5,89.1,90.2,88.3,90.2,87.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M96.9,89.9v-4.3l-1.7,4.3h-0.4l-1.7-4.3v4.3h-1v-5.6h1.4l1.5,3.8l1.5-3.8h1.4v5.6H96.9z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M103,89.9v-5.6h1v5.6H103z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M109.7,89.9l-2.9-4v4h-1v-5.6h1l2.9,3.9v-3.9h1v5.6H109.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M112.4,89.9v-5.6h3.8v0.9h-2.9v1.4h2.8v0.9h-2.8v2.4H112.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M120.3,89.9l-1.2-2.1h-1v2.1h-1v-5.6h2.5c1.1,0,1.8,0.7,1.8,1.8c0,1-0.7,1.5-1.3,1.6l1.4,2.2H120.3z
|
||||
M120.4,86.1c0-0.5-0.4-0.9-1-0.9h-1.4V87h1.4C120,87,120.4,86.6,120.4,86.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M126.6,89.9l-0.4-1.1h-2.6l-0.4,1.1h-1.1l2.2-5.6h1.2l2.2,5.6H126.6z M124.9,85.3l-1,2.7h2L124.9,85.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M131.4,89.9v-5.6h2.1c1.1,0,1.7,0.8,1.7,1.6c0,0.9-0.6,1.6-1.7,1.6h-1.6v2.3H131.4z M134.7,86
|
||||
c0-0.7-0.5-1.2-1.2-1.2h-1.6v2.4h1.6C134.2,87.2,134.7,86.6,134.7,86z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M139.4,89.9l-1.6-2.3h-1.2v2.3h-0.5v-5.6h2.1c1,0,1.7,0.6,1.7,1.6c0,1-0.7,1.6-1.6,1.6l1.6,2.3H139.4z
|
||||
M139.4,86c0-0.7-0.5-1.2-1.2-1.2h-1.6v2.4h1.6C138.9,87.2,139.4,86.7,139.4,86z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M141.2,87.1c0-1.6,1.1-2.9,2.7-2.9c1.6,0,2.7,1.3,2.7,2.9c0,1.6-1.1,2.9-2.7,2.9
|
||||
C142.3,90,141.2,88.8,141.2,87.1z M146.1,87.1c0-1.4-0.9-2.5-2.2-2.5c-1.4,0-2.2,1-2.2,2.5c0,1.4,0.9,2.5,2.2,2.5
|
||||
C145.2,89.6,146.1,88.5,146.1,87.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M147,89.3l0.3-0.4c0.3,0.3,0.6,0.6,1.1,0.6c0.8,0,1.2-0.5,1.2-1.3v-4h0.5v4c0,1.2-0.8,1.7-1.7,1.7
|
||||
C147.9,90,147.4,89.8,147,89.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M151.8,89.9v-5.6h3.5v0.4h-3.1v2.1h3v0.4h-3v2.2h3.1v0.4H151.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M156.3,87.1c0-1.7,1.3-2.9,2.8-2.9c0.9,0,1.6,0.4,2,1l-0.4,0.3c-0.4-0.5-1-0.8-1.6-0.8
|
||||
c-1.3,0-2.3,1-2.3,2.5c0,1.4,1,2.5,2.3,2.5c0.7,0,1.3-0.3,1.6-0.8l0.4,0.3c-0.5,0.6-1.2,1-2,1C157.5,90,156.3,88.8,156.3,87.1z"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M163.5,89.9v-5.2h-1.8v-0.4h4.1v0.4H164v5.2H163.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<polygon class="st3" points="33.7,86.5 41.2,79 48.6,86.5 49.8,86.5 41.2,77.9 32.6,86.5 "/>
|
||||
<polygon class="st3" points="48.6,87.8 41.2,95.2 33.7,87.8 32.6,87.8 41.2,96.4 49.8,87.8 "/>
|
||||
<polygon class="st3" points="40.3,86.5 47.8,79 55.3,86.5 56.4,86.5 47.8,77.9 39.2,86.5 "/>
|
||||
<polygon class="st3" points="55.3,87.8 47.8,95.2 40.3,87.8 39.2,87.8 47.8,96.4 56.4,87.8 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M51.2,41.3c2,1.1,3.6,2.6,4.7,4.5c1.1,1.9,1.7,4,1.7,6.4c0,2.3-0.6,4.5-1.7,6.4c-1.1,1.9-2.7,3.4-4.7,4.6
|
||||
c-2,1.1-4.2,1.7-6.6,1.7c-2.4,0-4.6-0.6-6.6-1.7c-2-1.1-3.6-2.6-4.7-4.6c-1.1-1.9-1.7-4.1-1.7-6.4c0-2.3,0.6-4.5,1.7-6.4
|
||||
c1.1-1.9,2.7-3.4,4.7-4.5c2-1.1,4.2-1.6,6.6-1.6C47,39.6,49.2,40.2,51.2,41.3z M40.5,44.9c-1.3,0.7-2.3,1.7-3,3
|
||||
c-0.7,1.3-1.1,2.7-1.1,4.2s0.4,3,1.1,4.2c0.8,1.3,1.8,2.3,3,3c1.3,0.7,2.7,1.1,4.1,1.1c1.5,0,2.8-0.4,4.1-1.1c1.3-0.7,2.3-1.8,3-3
|
||||
c0.7-1.3,1.1-2.7,1.1-4.2s-0.4-2.9-1.1-4.2c-0.7-1.3-1.7-2.3-3-3c-1.3-0.7-2.6-1.1-4.1-1.1C43.2,43.8,41.8,44.2,40.5,44.9z"/>
|
||||
<path class="st3" d="M76.9,46.8c1.3,0.8,2.4,1.9,3.1,3.4c0.7,1.4,1.1,3.1,1.1,5c0,1.9-0.4,3.5-1.1,4.9c-0.7,1.4-1.8,2.5-3.1,3.3
|
||||
c-1.3,0.8-2.9,1.2-4.6,1.2c-1.4,0-2.6-0.3-3.7-0.8c-1.1-0.5-2-1.3-2.7-2.4v9.8h-4.6V45.7H66v3.1c0.7-1.1,1.5-1.8,2.6-2.4
|
||||
c1.1-0.5,2.3-0.8,3.7-0.8C74,45.6,75.6,46,76.9,46.8z M75.1,59.1c1-1.1,1.5-2.4,1.5-4.1c0-1.7-0.5-3-1.5-4.1
|
||||
c-1-1.1-2.2-1.6-3.8-1.6c-1.6,0-2.8,0.5-3.8,1.6c-1,1-1.5,2.4-1.5,4.1c0,1.7,0.5,3,1.5,4.1c1,1.1,2.3,1.6,3.8,1.6
|
||||
C72.8,60.7,74.1,60.2,75.1,59.1z"/>
|
||||
<path class="st3" d="M99.3,48.1c1.5,1.7,2.3,4.1,2.3,7.2c0,0.6,0,1.1,0,1.4H87.7c0.3,1.3,0.9,2.4,1.9,3.1c0.9,0.8,2.1,1.1,3.5,1.1
|
||||
c1,0,1.9-0.2,2.7-0.5c0.9-0.4,1.6-0.9,2.3-1.6l2.5,2.6c-0.9,1-2.1,1.8-3.4,2.4c-1.3,0.6-2.8,0.8-4.5,0.8c-1.9,0-3.6-0.4-5.1-1.2
|
||||
c-1.5-0.8-2.6-1.9-3.4-3.3c-0.8-1.4-1.2-3.1-1.2-5c0-1.9,0.4-3.5,1.2-5c0.8-1.4,1.9-2.6,3.4-3.4c1.4-0.8,3.1-1.2,4.9-1.2
|
||||
C95.5,45.6,97.8,46.4,99.3,48.1z M97.4,53.6c0-1.4-0.5-2.5-1.4-3.3c-0.9-0.8-2-1.2-3.4-1.2c-1.3,0-2.4,0.4-3.3,1.2
|
||||
c-0.9,0.8-1.5,1.9-1.7,3.3H97.4z"/>
|
||||
<path class="st3" d="M121.5,47.5c1.2,1.3,1.9,3.1,1.9,5.3v11.7h-4.6V54.1c0-1.3-0.4-2.3-1.1-3.1c-0.7-0.8-1.8-1.1-3-1.1
|
||||
c-1.5,0-2.7,0.5-3.6,1.5s-1.3,2.3-1.3,3.8v9.2h-4.5V45.7h4.5v3.5c1.3-2.4,3.5-3.6,6.7-3.7C118.5,45.5,120.2,46.2,121.5,47.5z"/>
|
||||
<path class="st3" d="M156.5,39.9h4.9l-8.3,24.5h-4.9l-5.6-18.6l-5.7,18.6h-4.8l-8.3-24.5h5l5.8,19.4l5.7-19.4h4.6l5.8,19.5
|
||||
L156.5,39.9z"/>
|
||||
<path class="st3" d="M168,38.4c0.5,0.5,0.7,1.2,0.7,2c0,0.8-0.2,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.9,0.8c-0.7,0-1.4-0.3-1.9-0.8
|
||||
c-0.5-0.5-0.7-1.2-0.7-1.9c0-0.8,0.2-1.4,0.7-2c0.5-0.5,1.1-0.8,1.9-0.8C166.9,37.7,167.6,37.9,168,38.4z M164,45.7h4.5v18.7H164
|
||||
V45.7z"/>
|
||||
<path class="st3" d="M174,39.9h16.9l0,4.1h-12.2v6.6h11.1v4.1h-11.1v9.7H174V39.9z"/>
|
||||
<path class="st3" d="M197.9,38.4c0.5,0.5,0.7,1.2,0.7,2c0,0.8-0.2,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.9,0.8c-0.7,0-1.4-0.3-1.9-0.8
|
||||
c-0.5-0.5-0.7-1.2-0.7-1.9c0-0.8,0.2-1.4,0.7-2c0.5-0.5,1.1-0.8,1.9-0.8C196.8,37.7,197.4,37.9,197.9,38.4z M193.8,45.7h4.5v18.7
|
||||
h-4.5V45.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="132 132 264 264">
|
||||
<defs>
|
||||
<radialGradient id="g" cx="0%" cy="0%" r="60%">
|
||||
<stop offset=".8" style="stop-opacity:1" />
|
||||
<stop offset="1" style="stop-opacity:.5" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path style="fill:url(#g)" d="M 264 132 A 132 132 0 0 0 132 264 A 132 132 0 0 0 264 396 A 132 132 0 0 0 396 264 A 132 132 0 0 0 264 132 z M 264 170 A 94 94 0 0 1 359 264 A 94 94 0 0 1 264 359 A 94 94 0 0 1 170 264 A 94 94 0 0 1 264 170 z " />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 582 B |
@@ -0,0 +1,13 @@
|
||||
<%#
|
||||
Copyright 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">L.require('menu-ucentral')</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,67 @@
|
||||
<%#
|
||||
Copyright 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local http = require "luci.http"
|
||||
local disp = require "luci.dispatcher"
|
||||
local ver = require "luci.version"
|
||||
|
||||
local boardinfo = util.ubus("system", "board") or { }
|
||||
|
||||
local node = disp.context.dispatched
|
||||
local path = table.concat(disp.context.path, "-")
|
||||
|
||||
http.prepare_content("text/html; charset=UTF-8")
|
||||
-%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%=luci.i18n.context.lang%>">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Script-Type" content="text/javascript" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
|
||||
<link rel="icon" href="<%=media%>/logo.svg" type="image/svg+xml" />
|
||||
<script type="text/javascript" src="<%=url('admin/translations', luci.i18n.context.lang)%><%# ?v=PKG_VERSION %>"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
|
||||
<title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
|
||||
<% if css then %><style title="text/css">
|
||||
<%= css %>
|
||||
</style>
|
||||
<% end -%>
|
||||
</head>
|
||||
<body class="lang_<%=luci.i18n.context.lang%>" data-page="<%= pcdata(path) %>">
|
||||
|
||||
<p class="skiplink">
|
||||
<span id="skiplink1"><a href="#navigation"><%:Skip to navigation%></a></span>
|
||||
<span id="skiplink2"><a href="#content"><%:Skip to content%></a></span>
|
||||
</p>
|
||||
|
||||
|
||||
<div id="page">
|
||||
<div id="menubar">
|
||||
<h2 class="navigation" style="visibility:hidden"><a id="navigation" name="navigation"><%:Navigation%></a></h2>
|
||||
<img src="<%=media%>/logo.svg" />
|
||||
<span id="indicators"></span>
|
||||
</div>
|
||||
|
||||
<div id="modemenu" style="display:none"></div>
|
||||
|
||||
<div id="maincontainer">
|
||||
<div id="mainmenu"></div>
|
||||
|
||||
<div id="maincontent">
|
||||
<%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") and path ~= "admin-system-admin-password" then -%>
|
||||
<div class="alert-message warning">
|
||||
<h4><%:No password set!%></h4>
|
||||
<p><%:There is no password set on this router. Please configure a root password to protect the web interface.%></p>
|
||||
<% if disp.lookup("admin/system/admin") then %>
|
||||
<div class="right"><a class="btn" href="<%=url("admin/system/admin")%>"><%:Go to password configuration...%></a></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%- end -%>
|
||||
|
||||
<div id="tabmenu" style="display:none"></div>
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$PKG_UPGRADE" != 1 ]; then
|
||||
uci get luci.themes.uCentral >/dev/null 2>&1 || \
|
||||
uci batch <<-EOF
|
||||
set luci.themes.uCentral=/luci-static/ucentral
|
||||
set luci.main.mediaurlbase=/luci-static/ucentral
|
||||
commit luci
|
||||
EOF
|
||||
fi
|
||||
|
||||
exit 0
|
||||
294
olg-ucentral-client-sdk/feeds/tip/luci/luci.mk
Normal file
294
olg-ucentral-client-sdk/feeds/tip/luci/luci.mk
Normal file
@@ -0,0 +1,294 @@
|
||||
#
|
||||
# Copyright (C) 2008-2015 The LuCI Team <luci@lists.subsignal.org>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
LUCI_NAME?=$(notdir ${CURDIR})
|
||||
LUCI_TYPE?=$(word 2,$(subst -, ,$(LUCI_NAME)))
|
||||
LUCI_BASENAME?=$(patsubst luci-$(LUCI_TYPE)-%,%,$(LUCI_NAME))
|
||||
LUCI_LANGUAGES:=$(sort $(filter-out templates,$(notdir $(wildcard ${CURDIR}/po/*))))
|
||||
LUCI_DEFAULTS:=$(notdir $(wildcard ${CURDIR}/root/etc/uci-defaults/*))
|
||||
LUCI_PKGARCH?=$(if $(realpath src/Makefile),,all)
|
||||
|
||||
# Language code titles
|
||||
LUCI_LANG.bg=български (Bulgarian)
|
||||
LUCI_LANG.bn_BD=বাংলা (Bengali)
|
||||
LUCI_LANG.ca=Català (Catalan)
|
||||
LUCI_LANG.cs=Čeština (Czech)
|
||||
LUCI_LANG.de=Deutsch (German)
|
||||
LUCI_LANG.el=Ελληνικά (Greek)
|
||||
LUCI_LANG.en=English
|
||||
LUCI_LANG.es=Español (Spanish)
|
||||
LUCI_LANG.fr=Français (French)
|
||||
LUCI_LANG.he=עִבְרִית (Hebrew)
|
||||
LUCI_LANG.hi=हिंदी (Hindi)
|
||||
LUCI_LANG.hu=Magyar (Hungarian)
|
||||
LUCI_LANG.it=Italiano (Italian)
|
||||
LUCI_LANG.ja=日本語 (Japanese)
|
||||
LUCI_LANG.ko=한국어 (Korean)
|
||||
LUCI_LANG.mr=Marāṭhī (Marathi)
|
||||
LUCI_LANG.ms=Bahasa Melayu (Malay)
|
||||
LUCI_LANG.nb_NO=Norsk (Norwegian)
|
||||
LUCI_LANG.pl=Polski (Polish)
|
||||
LUCI_LANG.pt_BR=Português do Brasil (Brazilian Portuguese)
|
||||
LUCI_LANG.pt=Português (Portuguese)
|
||||
LUCI_LANG.ro=Română (Romanian)
|
||||
LUCI_LANG.ru=Русский (Russian)
|
||||
LUCI_LANG.sk=Slovenčina (Slovak)
|
||||
LUCI_LANG.sv=Svenska (Swedish)
|
||||
LUCI_LANG.tr=Türkçe (Turkish)
|
||||
LUCI_LANG.uk=Українська (Ukrainian)
|
||||
LUCI_LANG.vi=Tiếng Việt (Vietnamese)
|
||||
LUCI_LANG.zh_Hans=简体中文 (Chinese Simplified)
|
||||
LUCI_LANG.zh_Hant=繁體中文 (Chinese Traditional)
|
||||
|
||||
# Submenu titles
|
||||
LUCI_MENU.col=1. Collections
|
||||
LUCI_MENU.mod=2. Modules
|
||||
LUCI_MENU.app=3. Applications
|
||||
LUCI_MENU.theme=4. Themes
|
||||
LUCI_MENU.proto=5. Protocols
|
||||
LUCI_MENU.lib=6. Libraries
|
||||
|
||||
# Language aliases
|
||||
LUCI_LC_ALIAS.bn_BD=bn
|
||||
LUCI_LC_ALIAS.nb_NO=no
|
||||
LUCI_LC_ALIAS.pt_BR=pt-br
|
||||
LUCI_LC_ALIAS.zh_Hans=zh-cn
|
||||
LUCI_LC_ALIAS.zh_Hant=zh-tw
|
||||
|
||||
|
||||
PKG_NAME?=$(LUCI_NAME)
|
||||
|
||||
|
||||
# 1: everything expect po subdir or only po subdir
|
||||
define findrev
|
||||
$(shell \
|
||||
if git log -1 >/dev/null 2>/dev/null; then \
|
||||
set -- $$(git log -1 --format="%ct %h" --abbrev=7 -- $(if $(1),. ':(exclude)po',po)); \
|
||||
if [ -n "$$1" ]; then
|
||||
secs="$$(($$1 % 86400))"; \
|
||||
yday="$$(date --utc --date="@$$1" "+%y.%j")"; \
|
||||
printf 'git-%s.%05d-%s' "$$yday" "$$secs" "$$2"; \
|
||||
else \
|
||||
echo "unknown"; \
|
||||
fi; \
|
||||
else \
|
||||
ts=$$(find . -type f $(if $(1),-not) -path './po/*' -printf '%T@\n' 2>/dev/null | sort -rn | head -n1 | cut -d. -f1); \
|
||||
if [ -n "$$ts" ]; then \
|
||||
secs="$$(($$ts % 86400))"; \
|
||||
date="$$(date --utc --date="@$$ts" "+%y%m%d")"; \
|
||||
printf '%s.%05d' "$$date" "$$secs"; \
|
||||
else \
|
||||
echo "unknown"; \
|
||||
fi; \
|
||||
fi \
|
||||
)
|
||||
endef
|
||||
|
||||
PKG_PO_VERSION?=$(if $(DUMP),x,$(strip $(call findrev)))
|
||||
PKG_SRC_VERSION?=$(if $(DUMP),x,$(strip $(call findrev,1)))
|
||||
|
||||
PKG_GITBRANCH?=$(if $(DUMP),x,$(strip $(shell \
|
||||
variant="LuCI"; \
|
||||
if git log -1 >/dev/null 2>/dev/null; then \
|
||||
branch="$$(git branch --remote --verbose --no-abbrev --contains 2>/dev/null | \
|
||||
sed -rne 's|^[^/]+/([^ ]+) [a-f0-9]{40} .+$$|\1|p' | head -n1)"; \
|
||||
if [ "$$branch" != "master" ]; then \
|
||||
variant="LuCI $$branch branch"; \
|
||||
else \
|
||||
variant="LuCI Master"; \
|
||||
fi; \
|
||||
fi; \
|
||||
echo "$$variant" \
|
||||
)))
|
||||
|
||||
PKG_RELEASE?=1
|
||||
PKG_INSTALL:=$(if $(realpath src/Makefile),1)
|
||||
PKG_BUILD_DEPENDS += lua/host luci-base/host LUCI_CSSTIDY:csstidy/host LUCI_SRCDIET:luasrcdiet/host $(LUCI_BUILD_DEPENDS)
|
||||
PKG_CONFIG_DEPENDS += CONFIG_LUCI_SRCDIET CONFIG_LUCI_JSMIN CONFIG_LUCI_CSSTIDY
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=luci
|
||||
CATEGORY:=LuCI
|
||||
SUBMENU:=$(if $(LUCI_MENU.$(LUCI_TYPE)),$(LUCI_MENU.$(LUCI_TYPE)),$(LUCI_MENU.app))
|
||||
TITLE:=$(if $(LUCI_TITLE),$(LUCI_TITLE),LuCI $(LUCI_NAME) $(LUCI_TYPE))
|
||||
DEPENDS:=$(LUCI_DEPENDS)
|
||||
VERSION:=$(if $(PKG_VERSION),$(PKG_VERSION),$(PKG_SRC_VERSION))
|
||||
$(if $(LUCI_EXTRA_DEPENDS),EXTRA_DEPENDS:=$(LUCI_EXTRA_DEPENDS))
|
||||
$(if $(LUCI_PKGARCH),PKGARCH:=$(LUCI_PKGARCH))
|
||||
endef
|
||||
|
||||
ifneq ($(LUCI_DESCRIPTION),)
|
||||
define Package/$(PKG_NAME)/description
|
||||
$(strip $(LUCI_DESCRIPTION))
|
||||
endef
|
||||
endif
|
||||
|
||||
# Language selection for luci-base
|
||||
ifeq ($(PKG_NAME),luci-base)
|
||||
define Package/luci-base/config
|
||||
config LUCI_SRCDIET
|
||||
bool "Minify Lua sources"
|
||||
default n
|
||||
|
||||
config LUCI_JSMIN
|
||||
bool "Minify JavaScript sources"
|
||||
default y
|
||||
|
||||
config LUCI_CSSTIDY
|
||||
bool "Minify CSS files"
|
||||
default y
|
||||
|
||||
menu "Translations"$(foreach lang,$(LUCI_LANGUAGES),
|
||||
|
||||
config LUCI_LANG_$(lang)
|
||||
tristate "$(shell echo '$(LUCI_LANG.$(lang))' | sed -e 's/^.* (\(.*\))$$/\1/') ($(lang))")
|
||||
|
||||
endmenu
|
||||
endef
|
||||
endif
|
||||
|
||||
define Build/Prepare
|
||||
for d in luasrc htdocs root src; do \
|
||||
if [ -d ./$$$$d ]; then \
|
||||
mkdir -p $(PKG_BUILD_DIR)/$$$$d; \
|
||||
$(CP) ./$$$$d/* $(PKG_BUILD_DIR)/$$$$d/; \
|
||||
fi; \
|
||||
done
|
||||
$(call Build/Prepare/Default)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
ifneq ($(wildcard ${CURDIR}/src/Makefile),)
|
||||
MAKE_PATH := src/
|
||||
MAKE_VARS += FPIC="$(FPIC)" LUCI_VERSION="$(PKG_SRC_VERSION)" LUCI_GITBRANCH="$(PKG_GITBRANCH)"
|
||||
|
||||
define Build/Compile
|
||||
$(call Build/Compile/Default,clean compile)
|
||||
endef
|
||||
else
|
||||
define Build/Compile
|
||||
endef
|
||||
endif
|
||||
|
||||
HTDOCS = /www
|
||||
LUA_LIBRARYDIR = /usr/lib/lua
|
||||
LUCI_LIBRARYDIR = $(LUA_LIBRARYDIR)/luci
|
||||
|
||||
define SrcDiet
|
||||
$(FIND) $(1) -type f -name '*.lua' | while read src; do \
|
||||
if LUA_PATH="$(STAGING_DIR_HOSTPKG)/lib/lua/5.1/?.lua" luasrcdiet --noopt-binequiv -o "$$$$src.o" "$$$$src"; \
|
||||
then mv "$$$$src.o" "$$$$src"; fi; \
|
||||
done
|
||||
endef
|
||||
|
||||
define JsMin
|
||||
$(FIND) $(1) -type f -name '*.js' | while read src; do \
|
||||
if jsmin < "$$$$src" > "$$$$src.o"; \
|
||||
then mv "$$$$src.o" "$$$$src"; fi; \
|
||||
done
|
||||
endef
|
||||
|
||||
define CssTidy
|
||||
$(FIND) $(1) -type f -name '*.css' | while read src; do \
|
||||
if csstidy "$$$$src" --template=highest --remove_last_semicolon=true "$$$$src.o"; \
|
||||
then mv "$$$$src.o" "$$$$src"; fi; \
|
||||
done
|
||||
endef
|
||||
|
||||
define SubstituteVersion
|
||||
$(FIND) $(1) -type f -name '*.htm' | while read src; do \
|
||||
$(SED) 's/<%# *\([^ ]*\)PKG_VERSION *%>/\1$(if $(PKG_VERSION),$(PKG_VERSION),$(PKG_SRC_VERSION))/g' \
|
||||
-e 's/"\(<%= *\(media\|resource\) *%>[^"]*\.\(js\|css\)\)"/"\1?v=$(if $(PKG_VERSION),$(PKG_VERSION),$(PKG_SRC_VERSION))"/g' \
|
||||
"$$$$src"; \
|
||||
done
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
if [ -d $(PKG_BUILD_DIR)/luasrc ]; then \
|
||||
$(INSTALL_DIR) $(1)$(LUCI_LIBRARYDIR); \
|
||||
cp -pR $(PKG_BUILD_DIR)/luasrc/* $(1)$(LUCI_LIBRARYDIR)/; \
|
||||
$(FIND) $(1)$(LUCI_LIBRARYDIR)/ -type f -name '*.luadoc' | $(XARGS) rm; \
|
||||
$(if $(CONFIG_LUCI_SRCDIET),$(call SrcDiet,$(1)$(LUCI_LIBRARYDIR)/),true); \
|
||||
$(call SubstituteVersion,$(1)$(LUCI_LIBRARYDIR)/); \
|
||||
else true; fi
|
||||
if [ -d $(PKG_BUILD_DIR)/htdocs ]; then \
|
||||
$(INSTALL_DIR) $(1)$(HTDOCS); \
|
||||
cp -pR $(PKG_BUILD_DIR)/htdocs/* $(1)$(HTDOCS)/; \
|
||||
$(if $(CONFIG_LUCI_JSMIN),$(call JsMin,$(1)$(HTDOCS)/),true); \
|
||||
$(if $(CONFIG_LUCI_CSSTIDY),$(call CssTidy,$(1)$(HTDOCS)/),true); \
|
||||
else true; fi
|
||||
if [ -d $(PKG_BUILD_DIR)/root ]; then \
|
||||
$(INSTALL_DIR) $(1)/; \
|
||||
cp -pR $(PKG_BUILD_DIR)/root/* $(1)/; \
|
||||
else true; fi
|
||||
if [ -d $(PKG_BUILD_DIR)/src ]; then \
|
||||
$(call Build/Install/Default) \
|
||||
$(CP) $(PKG_INSTALL_DIR)/* $(1)/; \
|
||||
else true; fi
|
||||
endef
|
||||
|
||||
ifndef Package/$(PKG_NAME)/postinst
|
||||
define Package/$(PKG_NAME)/postinst
|
||||
[ -n "$${IPKG_INSTROOT}" ] || {$(foreach script,$(LUCI_DEFAULTS),
|
||||
(. /etc/uci-defaults/$(script)) && rm -f /etc/uci-defaults/$(script))
|
||||
rm -f /tmp/luci-indexcache
|
||||
rm -rf /tmp/luci-modulecache/
|
||||
killall -HUP rpcd 2>/dev/null
|
||||
exit 0
|
||||
}
|
||||
endef
|
||||
endif
|
||||
|
||||
|
||||
LUCI_BUILD_PACKAGES := $(PKG_NAME)
|
||||
|
||||
# 1: LuCI language code
|
||||
# 2: BCP 47 language tag
|
||||
define LuciTranslation
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
SECTION:=luci
|
||||
CATEGORY:=LuCI
|
||||
TITLE:=$(PKG_NAME) - $(1) translation
|
||||
HIDDEN:=1
|
||||
DEFAULT:=LUCI_LANG_$(2)||(ALL&&m)
|
||||
DEPENDS:=$(PKG_NAME)
|
||||
VERSION:=$(PKG_PO_VERSION)
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)/description
|
||||
Translation for $(PKG_NAME) - $(LUCI_LANG.$(2))
|
||||
endef
|
||||
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)/install
|
||||
$$(INSTALL_DIR) $$(1)/etc/uci-defaults
|
||||
echo "uci set luci.languages.$(subst -,_,$(1))='$(LUCI_LANG.$(2))'; uci commit luci" \
|
||||
> $$(1)/etc/uci-defaults/luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
$$(INSTALL_DIR) $$(1)$(LUCI_LIBRARYDIR)/i18n
|
||||
$(foreach po,$(wildcard ${CURDIR}/po/$(2)/*.po), \
|
||||
po2lmo $(po) \
|
||||
$$(1)$(LUCI_LIBRARYDIR)/i18n/$(basename $(notdir $(po))).$(1).lmo;)
|
||||
endef
|
||||
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)/postinst
|
||||
[ -n "$$$${IPKG_INSTROOT}" ] || {
|
||||
(. /etc/uci-defaults/luci-i18n-$(LUCI_BASENAME)-$(1)) && rm -f /etc/uci-defaults/luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
exit 0
|
||||
}
|
||||
endef
|
||||
|
||||
LUCI_BUILD_PACKAGES += luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
|
||||
endef
|
||||
|
||||
$(foreach lang,$(LUCI_LANGUAGES),$(eval $(call LuciTranslation,$(firstword $(LUCI_LC_ALIAS.$(lang)) $(lang)),$(lang))))
|
||||
$(foreach pkg,$(LUCI_BUILD_PACKAGES),$(eval $(call BuildPackage,$(pkg))))
|
||||
34
olg-ucentral-client-sdk/feeds/tip/tip-defaults/Makefile
Normal file
34
olg-ucentral-client-sdk/feeds/tip/tip-defaults/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=tip-defaults
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/tip-defaults
|
||||
SECTION:=ucentral
|
||||
CATEGORY:=uCentral
|
||||
TITLE:=tip-defaults
|
||||
endef
|
||||
|
||||
define Package/tip-defaults/description
|
||||
The default configuration of the AP.
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Compile/Default
|
||||
|
||||
endef
|
||||
Build/Compile = $(Build/Compile/Default)
|
||||
|
||||
define Package/tip-defaults/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,tip-defaults))
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=80
|
||||
|
||||
boot() {
|
||||
echo $(cat /etc/openwrt_release | grep DISTRIB_TIP= | cut -d\' -f2) > /tmp/ucentral.version
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFajCCA1KgAwIBAgICDnowDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUT3BlbkxBTiBEZW1vIFJvb3QgQ0EwHhcNMjUwMjIxMTUwMDAwWhcNMjYwMjIxMTUwMDAwWjAgMR4wHAYDVQQDExVPcGVuTEFOIERlbW8gQmlydGggQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVWIyySul6Fv4wl1O+DQpaLRa0p+Az5L/jcqTpdVf6w+8tlmeIY9C28uDQoDjewrIkvf3lcfK86nshs02s9ehqZUnEP8+GvKM19x3JbWxeTvWwFirjHir4x897iQ606bAMbrHHtntI9ZyBZyXDGeElGJxJQNX+0d50SFq609cB3yxpBPJ67ag+4Oq0uHgROHjEQMrfwLwlAune0c1fjQDrN14PDNjMZHvvhc/pkAHxR1PP6LOFNV5NuQ58tC5N7R2EqqFbIJ8VZgcagrGRYuAuFFTaV+D7RIt9xGTuWlCyxHI7VkRBJ1mRoEr4GOrP9QFjBD8NzNK+/wnR/fZwhpEnRsgHiI33wKHBDg+l3r8tvRzuB5X6Gl/SfuAeaoCuDHMncTjQg1zGhyEwjQhUe4RY3w+yHAjeeOE6c5spOMDDdaBibkzLmSjXztuLeAdzsUcD3fvGeOvh9vG14TKEmF8puNkqEcc0W8NyUWKFdr9umdJEMbaRSSsMGtp8bDj3Ddh4PhEJrIFeo89+HwXhU6sk+wzE9BULTohahsfwOV/08t1cZ3Q04Oj1KI+4YWu8BJns5gX35rQ8GIbkXQwfvFMwqmbg+ij2o9HWdkSL4bcqW/83Ho+31ce210rVGPK9cav0CjA2Eexgxi45cbgnfoade74Qa5zXboJEBmp7rbo4swIDAQABo4GuMIGrMB8GA1UdIwQYMBaAFDzIg8eyTI3xc4A2R60f8HanhBZDMB0GA1UdDgQWBBS5xC3inqLQl+vxzn9PsjNzlZ5hYDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vZGVtby5jZXJ0aWZpY2F0ZS5maS9jcmwvT3BlbkxBTkRlbW9Sb290Q0EuY3JsMA0GCSqGSIb3DQEBCwUAA4ICAQA9DJEjsDLqtSFkF0XTWfzbebXA+X8++Qmiukrw0s2LRx798ce0mVITRAFDLf78BeUYF0B+PQ8hgq4fWyFsXRgZVrITd1BszT3LJ2r/6xWQJVpVHzLqKgIlW/PY/uTUz+xqR0Ev6hYrmrjfya0K0XEZZqxkmrTrcECaA3RCFkWQl9ZUlb9BClmdhayO8x1XpJplIYAMKVuoPL9IUQH6HUPFnzlPNQHIK9gcFACtgPVWCJg3IAvSLa41KpRxTDwGFvlrNKtkBlGRYhFGCHWXXZn8fdQHW9vykkkfPOaPR/AVyuRzfAT6wbtVWSy38BurSdqSCuNQPQBfF2vMeUGwNbD/7B4tYrWVtnIbgxRPKvX0o3mZkKry6BJf2m/AKWA16W+i4ojnPRORLTTq9cEZ0WL6NRHCgMrbWaCs/+ADTErqK6cv7GhoOVEiqugvnz93dit2IXg4zdDJ4hF9ZSlicwgZKVvMqnNQ1iiXezIQBehgYcWwXRIfdrRPe88HgkySuDZ2lkKYdc+oTc6e7upRh4Kh2ZSipcRb6ehPan533jnQJyU8A9vFAJiQfZZ4lD3tcsqlsDnlu5YEDYSjcfnkyOH/Mlx5VVTWYGvqNNVKRDw2slSxKwVCobkcF/2dAxP9DqOaGaCnMeOaR7kMaBm5d1fwb+bCl9usQAELjZBv2vAH8g==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFIDCCAwigAwIBAgICDnkwDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUT3BlbkxBTiBEZW1vIFJvb3QgQ0EwHhcNMjUwMjIxMTUwMDAwWhcNMjYwMjIxMTUwMDAwWjAfMR0wGwYDVQQDDBRPcGVuTEFOIERlbW8gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMjExylKdJWoJu9mOHPJ6yZFXKe1lE467G65acpS2FKIWnPVFjNCmATMpkMOIFzEFwyFdbQjzOidtiL+73zlE52lOJpXCfOcxDFqDYDJJ8//J1/gQWsBaKpSvgLiHU/0awkQg+yJYZpj8YZa4NkFe+zTjQScSfOsqPPb3rZ7DOQ2BKAhjVShKmVbtNil0iO0zm8vE8DNkktTNMREp2pzb8MbCAgfOkwlrby6T+rV3TvmjThGdFUb5lWDFxWtlF8W0SUII9qj7p5TdGpryeLsO0nZTBtS4HxZNdvmKOHfgcRHmSZIJigB2NzKLNrXF9JBW0WnUSwZJZAG2C1RTx6lADILPueuusyfR/hZ3koKi4PHnSiTwQghzia9K9QjNHq5z9R9ZoCnhBg1VyU4LKmp862L0sIp2vgnOYunEIi9aCYBaDwo+0FuVjZuXyDIatwVuA7TN5IWPHA6XLdOt1mmkeYy1Ldr4XHjdondhtOyeei1UFXmyyLm2+kmRYfTm91TqYmNzRgbRV2NHO50AmsnBknX4Rv3gishGe0+dV5yFcUwZud0z2rSCkuoai5tKrPT+6Y6NqkT9u9HFifIBXnLwEzVUqHRtW6SuWj2DClVQIXIUZtFnhY4GuTuf6DlzgnXO58oDVCZmCW4ULIpbqGeRsvBHR8Sw5JXP/1+TMUYhE8TAgMBAAGjZjBkMB8GA1UdIwQYMBaAFDzIg8eyTI3xc4A2R60f8HanhBZDMB0GA1UdDgQWBBQ8yIPHskyN8XOANketH/B2p4QWQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAgEAkHZ5KR8IOrdfMFy+iOvauvZxfQ84LL6TpB2FQKDjneJUdd7c29UJJFNW/0mp4Gc6jKZab6J8Dx/pNnbH0RqFjGjeRGtJ4Sk0G7gf9zw1S7qut5WJDcisM9l/wXC+zy/KSKKPQmbt0grWOtU7+NNPh1YU76hIrInq/u2sVZyKH8SXQ957fbJk6BX6JTKyNEn05AB6rNSrbOWo8sy2MlcJ7bBsrWYI1t6GcWFh4b36bLu7/dKJWpyFNXXIkKJsgMEDpEQae56+fSSDo0KRNtYB82fNZDIQlGK81rGJWNzAahM+3GD1tgk/3ZVugfaJhcBpoHHKNOGqZAvtirLAIDocno7AzqoeIz974Rh2Olsl2/arApYPyyfi8PMYuFe/d4h+Wie8n+jh5n48lZ2Ve4PK+j+QHD6tTZS4f0bGnPL1puMxzQloltuQWgLDeVfEgrc3snLvjOg8aDzWm/es85lP8XcyW54U4t3JmrNUC2C7v+Uafx7cL7eDeunhs+BRhtGV+IUmjub2IrpqZp3zZqn+LVRdYJIy/qHhjS5+ImckXkFojOmeWhfmEmYSuNP8Oa6cGuXp829qnbxLh9Qzi3TfXV883KLse4kL5Zl7gBA/4hz2hVMyGJ8fY+VvzbaTuOXyvKJ+rGZCTcRSeotBLnIevVMiL7SqOEwN0j4Mfbznfq8=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFFTCCAv2gAwIBAgICAxIwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPT3BlbkxBTiBSb290IENBMCAXDTI1MDUxNDA4NDcxMFoYDzIwNTUwNTE0MDg0NzEwWjAaMRgwFgYDVQQDDA9PcGVuTEFOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGibJ04A55kSURTBSKgcBmLnND2I5wws1taKqqU9aaRhB7NtvMHwh2voH9b1brUiulZaZwTN/9kzd4AnXeKQ+0u5tV7Ofk0fzF2MK47n17TS30Yenqc4NuQEKdpKK/pM3VvOEppR/bqtgyLtDmbDnmFOx+zTj/+smTgouwA+Iier0P4s5OohYxn/bjOqwQbHbU79VpGBIWv6/kt55AhH7zvsqqKHkrzTxnsRBv3SBIufrjJr9PIhZBLDrqr56P6KgAi0eoutNt2ToiJbE0WfjU7GI1RSiSN5bGj1zXhjNVzQWs1H9QzRf3c9pl3+haHQZ7FZ1UqiTRewmbNrQ6I9k81au3SttUlb87MyAuDSzatkiq7CjQ8VE1J6te6ZBt2zWpUhHsR/Lg7g3eOw5dL4oZJdK5GgGu/MUajLUXifIqM13Mvg0VTzDhN69VLXLSL0gPcicsQCwJuAza1IC/VqmBGx19fAkyJhOurCXWOgisi0g1+xzPKRphUNwMPUf8vBVOM/Vc6xDIvwVGE3+eWXyhixneFlSpAI03nWWjpwWXihTBoxbfRXO3Y/ilJqrgFN+U4PJcCPA+Wo7ThH0mgX6bOTPcgXMUzT3v3FF6Bx5/PNV3kYrw2yLzribUiS6AGvVGnW4hX2Z6OQvA/aHME8KF+6y6m4pC7FkUjVaRlzWu/wIDAQABo2MwYTAfBgNVHSMEGDAWgBSUaFuoOPk4QLByZP47kj4p1IbCJjAdBgNVHQ4EFgQUlGhbqDj5OECwcmT+O5I+KdSGwiYwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAB+/RUC2X6eVoPsFNMkaXO5Iib/ub0JoWhODQm8j2Mr5dpGXESSpXjfDcqDOLuJbWWoflXBLdr8BsVCBqOA9YgCX0H8Br7dUWmCScixxLW0he592/424EvdwifxcKHZLjv9CKV5Txhqnm2djc5RY/nTH5MYVrIh/If2TNO5ydDP6+vgy9GQ4en04VK7rz+PW17O8l7k9/lOmYptZmHgSDAPj/cT3PlG+McqaI5rMSHeEHlzH+PvgWjtSeEhF4FwFBXroDl4/yb4l2JB8bqAZ3vsOXSkigFcZh5MXPe+zuSSW+G8iLr4xoi0CFsP2DaHEyxgqP4B1FtE9nFPo6cvWbwqTVT7QSzqfH+jPJuQvpFXeRF5UFegNZTFT5/uFFPamihakFslEYxeJey1y+OJdLcP6ef87ruSt8amsq56OAETYpnW4JFowlEh0C+QwLGHGGY6WrOgHY/90hJmPgXBdBVg/IoOhzbvk5A+LqZDvxV2/rLNfClw8Kr3g5e8obcB6dWgMCy2z+us0H79ucnmhzQKsjpxM9T1ncHovAQfiD3jVqfHULY53avh0wIAjosoTGbe8dyx80quHe+16qWan7C9idXeAYYJXbZt5hs6hLw4I8M1LsjTg6vwsqiaHZpsmDyyQLdFjNJldG7aosfS9F+BIpuwijF+1dashL0CPsbIJ
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGBzCCA++gAwIBAgICCQYwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPT3BlbkxBTiBSb290IENBMB4XDTI1MDUxNDA4NTY0MVoXDTQ1MDUxNDA5MjY0MVowJDEiMCAGA1UEAwwZT3BlbkxBTiBTZXJ2ZXIgSXNzdWluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALSdJpzwPfQM9oHBGt6w8UDLDJNznxI7cpfl0u0xVCHN1YY7onpwxFVkFRzUx/JrQ/tbEGZH19XtngaCZ91KbGbqVao9S32H0tyn2t3eTJ5h+klJ7+7YAbZr8UfOi3nG4bZzNSa5dDBPaNPvI51byKDN7siXXnALV3f0l6lZgDpLQco/E7ANU3lslUVjVNALfFUEonDyP7XV+lFAyidpjIn6dRn7oYs3SUwkzZUntYJAhAykmxXMWox+85gDkdb+2O3G8ci0uHVbb0A9LP+MeIhzxHgnnAMfWLfEZexdmEd2PwVHaz/D2Xp/gYrpPDTsbqWjQ9NmgdASwqN5j8BuJ8vHDVBVCztVDltm6JPw3Y6GQPN1LmiSLUzst7VYpydUJRDHYIAKJhT9DYxQ126VfiyMo6Xl4IQO8YZ/J6r8yR7gyvyUiBW+wvvC1bCY5+VuI4P/cY+6iA1qwC1SOWjYlccy+tbfGj9zr32Qf27e9RXSAkcATHen1rc/9AGEeAuSpKrzhmZIIvM4+EtYgbBvf91NkP51zbGpvsAbfWN/ecNmqH9SeyrrVgv68Z34hMijCcvJNyIvloo3nkb/gHYV4tAiwTTrX13Rio/8qNF4nwHLsjw0t7jEyRiXdOciePyhGbtdicuiUxrShzbGY7ID0yNwyTKcJYhorL/8r+YFpsXrAgMBAAGjggFLMIIBRzAfBgNVHSMEGDAWgBSUaFuoOPk4QLByZP47kj4p1IbCJjAdBgNVHQ4EFgQUBwUkiaCh5hdY+ZH6O8NmEE/nH5EwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwRwYDVR0fBEAwPjA8oDqgOIY2aHR0cDovL2NybC5jZXJ0aWZpY2F0ZXMub3Blbi1sYW4ub3JnL29wZW5sYW5yb290Y2EuY3JsMIGXBggrBgEFBQcBAQSBijCBhzBEBggrBgEFBQcwAoY4aHR0cDovL2NlcnRzLmNlcnRpZmljYXRlcy5vcGVuLWxhbi5vcmcvb3BlbmxhbnJvb3RjYS5jZXIwPwYIKwYBBQUHMAGGM2h0dHA6Ly9vY3NwLmNlcnRpZmljYXRlcy5vcGVuLWxhbi5vcmcvb3BlbmxhbnJvb3RjYTANBgkqhkiG9w0BAQsFAAOCAgEAqEk5ZJdpMVr2U0YhmqEU6gqxEeih9MWKcQfmsT/lhf5m5V7VuLMc3r+EBCsPssw60umdQcAU2IPlJXLAeWwdRyY7ZNNwQVgl9GBI/CM2b7x18+12/llCdXW9FOagdChTuuhwRnGTt71jcrJkleQyEYhqwwIEN82hxq4HSZO6XJDev4IsMRF00+qt8biJcf7OVGOSLoyiU6Dm/EzxoB+DZf3HdUc0vzfVjD4Im+yYzqXuwWV6c9oIBQH6obzaqlpg926CtEBFR8E1LQe93ahMvF7pExpIOkE5PTuqONvy7Xn3Ui8NRxHhmm8j/unql6bUTGENz9s68n8Im7weq6awC9Hfu8aGWjcnXI7tsDY5uJEguP5fSwCUrdTE85XgPgPHeKaIwBZsyRZTqVSvbky+c15Yv6ITXLWoA0AUxz9ste3WpqiWCNJVI90MCruSYKdpXGV0KU3QQXJDMKhHJBF5DLpuKiboFfh9O8pB7B4/tJ76JpAc6Z0rfaQUo2vxSpb3Sbd/IHNcL08zB8Ay+YUBULspxe+1StKthmCzCHI9DOhIgeASyNBpcL7uZPjCXiYGhUuzsFGv4sQ+d267Jyvql/Piw/vYg1k2aVBfdIoIU4TpIEVyQqPz4aAW+0SgL7OM+/zD9jxn3gVdusCpmHcoTzOfZRriH0FGIeDSQydpOJU=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,75 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEcTCCA1mgAwIBAgIUJFhIMlIJHJ7hW4gEzZuLBUaWjNcwDQYJKoZIhvcNAQEL
|
||||
BQAwbDELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
|
||||
dCwgSW5jLjEMMAoGA1UECxMDVElQMSkwJwYDVQQDEyBUZWxlY29tIEluZnJhIFBy
|
||||
b2plY3QgSXNzdWluZyBDQTAeFw0yMTA0MjUyMDMzNTRaFw0yNjA0MTMyMjM4NDZa
|
||||
MCMxITAfBgNVBAMTGGNhY2VydHMub25lLmRpZ2ljZXJ0LmNvbTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAJwKRHdkdEQkp32bNi9TdgN4FNRG0nRppguQ
|
||||
mdCysJHA6/SuyAXNwKSbENysjFrcBkfYTlALjvIMqSu4d26ix6Mv4HnVxLjDzapV
|
||||
TZhOhfxIbRQa3HNieNup2vMi8jJvgwLcK/4CwhBJsbEMkB5lbyL8UnCBxzW9GGbM
|
||||
IvurvDFkUDUpUmiFg47nTpjub79KME6NqK38DxKzlUHvJge1TKFM73kZ3YkfWExQ
|
||||
yRQPRiU5KxMi/Wkr30FOf/rMTx4XNacOgyTJvzcStGwrlr0iGr8eLC1/XVXoOQz3
|
||||
0lyOeUzTB+HPU1Z2JrbPW5PnGxcQ0f7v/3qkWV1B2wuvFcQk+D0CAwEAAaOCAVIw
|
||||
ggFOMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFIj2Mhdk10e46DeI+aEZKSSK8Hj+
|
||||
MB8GA1UdIwQYMBaAFLMbVLjgR6s98ziA5Dzl/QBhbdHoMA4GA1UdDwEB/wQEAwIE
|
||||
8DAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjCBhgYIKwYBBQUHAQEEejB4MCgGCCsG
|
||||
AQUFBzABhhxodHRwOi8vb2NzcC5vbmUuZGlnaWNlcnQuY29tMEwGCCsGAQUFBzAC
|
||||
hkBodHRwOi8vY2FjZXJ0cy5vbmUuZGlnaWNlcnQuY29tL1RlbGVjb21JbmZyYVBy
|
||||
b2plY3RJc3N1aW5nQ0EuY3J0ME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9jcmwu
|
||||
b25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQcm9qZWN0SXNzdWluZ0NBLmNy
|
||||
bDANBgkqhkiG9w0BAQsFAAOCAQEADlFwshNPkeI2Gl6ooIauZL9d+6k+RWa5RTle
|
||||
JWziYL23XVEBT11+dvp4IB9HwVw5dByl3XAfTd1r4qyncwgXQpc6j2X8e45E8izI
|
||||
z2S1zhLMe1bA2lOiZz/sdpbonvxIHdiISyQI7q3mWQsvNkpkbjivjxLAJTcGPmOS
|
||||
gc/95YL+2xqPV45XAnPcl5qkLThtmb57Xst1sLWiSS2fUId6HMVuCgZa5su+aAl9
|
||||
iMXv9YfHcvyfwXBaOtoBlItyMGl60uy0E/Fr5uEhEWi53EIqhty6KQckQBB7wdjQ
|
||||
eiXNI5Ox5cf+TFdesuKPaoEn3WNpFL9PCA3S5nGegJlZQ4N9Eg==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEnDCCA4SgAwIBAgIUVpyCUx1MUeUwxg+7I1BvGFTz7HkwDQYJKoZIhvcNAQEL
|
||||
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
|
||||
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
|
||||
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjUxMjZaFw0yNjA0MTMyMjM4NDZaMGwx
|
||||
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
|
||||
Yy4xDDAKBgNVBAsTA1RJUDEpMCcGA1UEAxMgVGVsZWNvbSBJbmZyYSBQcm9qZWN0
|
||||
IElzc3VpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtKBrq
|
||||
qd2aKVSk25KfL5xHu8X7/8rJrz3IvyPuVKWhk/N1zabot3suBcGaYNKjnRHxg78R
|
||||
yKwKzajKYWtiQFqztu24g16LQeAnoUxZnF6a0z3JkkRPsz14A2y8TUhdEe1tx+UU
|
||||
4VGsk3n+FMmOQHL+79FO57zQC1LwylgfLSltrI6mF3jowVUQvnwzKhUzT87AJ6EO
|
||||
ndK/q0T/Bgi+aI39zfVOjJjsTJwghvrmYW3iarP1THSKxeib2s02bZKrvvHa5HL4
|
||||
UI8+LvREpVZl4mzt1z6Nl344Y6f+UeJlYa/Ci0jJqaXJmyVnUbAz+c0i5JfwAVn3
|
||||
YQzfC4eLnZCmdF8zAgMBAAGjggE3MIIBMzAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBSzG1S44EerPfM4gOQ85f0AYW3R6DAfBgNVHSMEGDAWgBQCRpZgebFT9qny
|
||||
98WfIUDk6ZEB+jAOBgNVHQ8BAf8EBAMCAYYwgYMGCCsGAQUFBwEBBHcwdTAoBggr
|
||||
BgEFBQcwAYYcaHR0cDovL29jc3Aub25lLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcw
|
||||
AoY9aHR0cDovL2NhY2VydHMub25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQ
|
||||
cm9qZWN0Um9vdENBLmNydDBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLm9u
|
||||
ZS5kaWdpY2VydC5jb20vVGVsZWNvbUluZnJhUHJvamVjdFJvb3RDQS5jcmwwDQYJ
|
||||
KoZIhvcNAQELBQADggEBAFbz+K94bHIkBMJqps0dApniUmOn0pO6Q6cGh47UP/kX
|
||||
IiPIsnYgG+hqYD/qtsiqJhaWi0hixRWn38UmvZxMRk27aSTGE/TWx0JTC3qDGsSe
|
||||
XkUagumbSfmS0ZyiTwMPeGAjXwyzGorqZWeA95eKfImntMiOf3E7//GK0K7HpCx8
|
||||
IPCnLZsZD2q/mLyBsduImFIRQJbLAhwIxpcd1qYJk+BlGFL+HtBpEbq6JxW2Xy+v
|
||||
DpNWc2WIsUTle0rTc9JNJrLX4ChUJmKqf8obKHap3Xh3//qw/jDB9pOAinA33FLJ
|
||||
EmCnwBvQr9mfNmPBGMYZVU8cPruDQJ57GjmmvdisbJY=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDojCCAoqgAwIBAgIUPVYBpqNbcLYygF6Mx+qxSWwQyFowDQYJKoZIhvcNAQEL
|
||||
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
|
||||
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
|
||||
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjQyNDRaFw0zMTA0MTMyMjM4NDZaMGkx
|
||||
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
|
||||
Yy4xDDAKBgNVBAsTA1RJUDEmMCQGA1UEAxMdVGVsZWNvbSBJbmZyYSBQcm9qZWN0
|
||||
IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGCibwf5u
|
||||
AAwZ+1H8U0e3u2V+0d2gSctucoK86XwUmfe1V2a/qlCYZd29r80IuN1IIeB0naIm
|
||||
KnK/MzXW87clF6tFd1+HzEvmlY/W4KyIXalVCTEzirFSvBEG2oZpM0yC3AefytAO
|
||||
aOpA00LaM3xTfTqMKIRhJBuLy0I4ANUVG6ixVebbGuc78IodleqiLoWy2Q9QHyEO
|
||||
t/7hZndJhiVogh0PveRhho45EbsACu7ymDY+JhlIleevqwlE3iQoq0YcmYADHno6
|
||||
Eq8vcwLpZFxihupUafkd1T3WJYQAJf9coCjBu2qIhNgrcrGD8R9fGswwNRzMRMpX
|
||||
720+GjcDW3bJAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAJG
|
||||
lmB5sVP2qfL3xZ8hQOTpkQH6MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEAVjl9dm4epG9NUYnagT9sg7scVQEPfz3Lt6w1NXJXgD8mAUlK0jXmEyvM
|
||||
dCPD4514n+8+lM7US8fh+nxc7jO//LwK17Wm9FblgjNFR7+anv0Q99T9fP19DLlF
|
||||
PSNHL2emogy1bl1lLTAoj8nxg2wVKPDSHBGviQ5LR9fsWUIJDv9Bs5k0qWugWYSj
|
||||
19S6qnHeskRDB8MqRLhKMG82oDVLerSnhD0P6HjySBHgTTU7/tYS/OZr1jI6MPbG
|
||||
L+/DtiR5fDVMNdBSGU89UNTi0wHY9+RFuNlIuvZC+x/swF0V9R5mN+ywquTPtDLA
|
||||
5IOM7ItsRmen6u3qu+JXros54e4juQ==
|
||||
-----END CERTIFICATE-----
|
||||
36
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/Makefile
Normal file
36
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/Makefile
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (C) 2021 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=atfpolicy
|
||||
PKG_VERSION:=1
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
define Package/atfpolicy
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=A simple daemon for handling airtime fairness prioritization
|
||||
DEPENDS:=+libubox +libubus +libnl-tiny
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny
|
||||
|
||||
define Package/atfpolicy/install
|
||||
$(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/init.d $(1)/etc/config
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/atfpolicy $(1)/usr/sbin/
|
||||
$(INSTALL_BIN) ./files/atfpolicy.init $(1)/etc/init.d/atfpolicy
|
||||
$(INSTALL_DATA) ./files/atfpolicy.conf $(1)/etc/config/atfpolicy
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,atfpolicy))
|
||||
@@ -0,0 +1,8 @@
|
||||
config defaults
|
||||
option vo_queue_weight 4
|
||||
option update_pkt_threshold 100
|
||||
option bulk_percent_thresh 50
|
||||
option prio_percent_thresh 30
|
||||
option weight_normal 256
|
||||
option weight_prio 512
|
||||
option weight_bulk 128
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (c) 2021 OpenWrt.org
|
||||
|
||||
START=50
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/atfpolicy
|
||||
|
||||
add_option() {
|
||||
local type="$1"
|
||||
local name="$2"
|
||||
|
||||
config_get val "$cfg" "$name"
|
||||
|
||||
[ -n "$val" ] && json_add_$type "$name" "$val"
|
||||
}
|
||||
|
||||
add_defaults() {
|
||||
cfg="$1"
|
||||
|
||||
json_add_boolean reset 1
|
||||
|
||||
add_option int vo_queue_weight
|
||||
add_option int update_pkt_threshold
|
||||
add_option int bulk_percent_thresh
|
||||
add_option int prio_percent_thresh
|
||||
add_option int weight_normal
|
||||
add_option int weight_prio
|
||||
add_option int weight_bulk
|
||||
}
|
||||
|
||||
|
||||
reload_service() {
|
||||
json_init
|
||||
|
||||
config_load atfpolicy
|
||||
|
||||
config_foreach add_defaults defaults
|
||||
|
||||
ubus call atfpolicy config "$(json_dump)"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger atfpolicy
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 2 wait_for atfpolicy
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
PROJECT(atfpolicy C)
|
||||
|
||||
ADD_DEFINITIONS(-Os -Wall -Wno-unknown-warning-option -Wno-array-bounds -Wno-format-truncation -Werror --std=gnu99)
|
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
|
||||
find_library(nl NAMES nl-tiny)
|
||||
ADD_EXECUTABLE(atfpolicy main.c ubus.c interface.c nl80211.c)
|
||||
TARGET_LINK_LIBRARIES(atfpolicy ${nl} ubox ubus)
|
||||
|
||||
INSTALL(TARGETS atfpolicy
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
90
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/atf.h
Normal file
90
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/atf.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#ifndef __ATF_H
|
||||
#define __ATF_H
|
||||
|
||||
#include <net/if.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libubox/avl.h>
|
||||
|
||||
#define ATF_AVG_SCALE 12
|
||||
|
||||
#define ATF_AVG_WEIGHT_FACTOR 3
|
||||
#define ATF_AVG_WEIGHT_DIV 4
|
||||
|
||||
#define MAC_ADDR_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
#define MAC_ADDR_DATA(_a) \
|
||||
((const uint8_t *)(_a))[0], \
|
||||
((const uint8_t *)(_a))[1], \
|
||||
((const uint8_t *)(_a))[2], \
|
||||
((const uint8_t *)(_a))[3], \
|
||||
((const uint8_t *)(_a))[4], \
|
||||
((const uint8_t *)(_a))[5]
|
||||
|
||||
#define D(format, ...) do { \
|
||||
if (debug_flag) \
|
||||
fprintf(stderr, "DEBUG: %s(%d) " format "\n", __func__, __LINE__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
struct atf_config {
|
||||
int voice_queue_weight;
|
||||
int min_pkt_thresh;
|
||||
|
||||
int bulk_percent_thresh;
|
||||
int prio_percent_thresh;
|
||||
|
||||
int weight_normal;
|
||||
int weight_prio;
|
||||
int weight_bulk;
|
||||
};
|
||||
|
||||
struct atf_interface {
|
||||
struct avl_node avl;
|
||||
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
uint32_t ubus_obj;
|
||||
|
||||
struct avl_tree stations;
|
||||
};
|
||||
|
||||
struct atf_stats {
|
||||
uint64_t bulk, normal, prio;
|
||||
};
|
||||
|
||||
struct atf_station {
|
||||
struct avl_node avl;
|
||||
uint8_t macaddr[6];
|
||||
bool present;
|
||||
|
||||
uint8_t stats_idx;
|
||||
struct atf_stats stats[2];
|
||||
|
||||
uint16_t avg_bulk;
|
||||
uint16_t avg_prio;
|
||||
|
||||
int weight;
|
||||
};
|
||||
|
||||
extern struct atf_config config;
|
||||
extern int debug_flag;
|
||||
|
||||
void reset_config(void);
|
||||
|
||||
struct atf_interface *atf_interface_get(const char *ifname);
|
||||
void atf_interface_sta_update(struct atf_interface *iface);
|
||||
struct atf_station *atf_interface_sta_get(struct atf_interface *iface, uint8_t *macaddr);
|
||||
void atf_interface_sta_changed(struct atf_interface *iface, struct atf_station *sta);
|
||||
void atf_interface_sta_flush(struct atf_interface *iface);
|
||||
void atf_interface_update_all(void);
|
||||
|
||||
int atf_ubus_init(void);
|
||||
void atf_ubus_stop(void);
|
||||
void atf_ubus_set_sta_weight(struct atf_interface *iface, struct atf_station *sta);
|
||||
|
||||
int atf_nl80211_init(void);
|
||||
int atf_nl80211_interface_update(struct atf_interface *iface);
|
||||
|
||||
#endif
|
||||
108
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/interface.c
Normal file
108
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/interface.c
Normal file
@@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libubox/avl-cmp.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
static AVL_TREE(interfaces, avl_strcmp, false, NULL);
|
||||
|
||||
#ifndef container_of_safe
|
||||
#define container_of_safe(ptr, type, member) \
|
||||
(ptr ? container_of(ptr, type, member) : NULL)
|
||||
#endif
|
||||
|
||||
static int avl_macaddr_cmp(const void *k1, const void *k2, void *ptr)
|
||||
{
|
||||
return memcmp(k1, k2, 6);
|
||||
}
|
||||
|
||||
void atf_interface_sta_update(struct atf_interface *iface)
|
||||
{
|
||||
struct atf_station *sta;
|
||||
|
||||
avl_for_each_element(&iface->stations, sta, avl)
|
||||
sta->present = false;
|
||||
}
|
||||
|
||||
struct atf_station *atf_interface_sta_get(struct atf_interface *iface, uint8_t *macaddr)
|
||||
{
|
||||
struct atf_station *sta;
|
||||
|
||||
sta = avl_find_element(&iface->stations, macaddr, sta, avl);
|
||||
if (sta)
|
||||
goto out;
|
||||
|
||||
sta = calloc(1, sizeof(*sta));
|
||||
memcpy(sta->macaddr, macaddr, sizeof(sta->macaddr));
|
||||
sta->avl.key = sta->macaddr;
|
||||
sta->weight = -1;
|
||||
avl_insert(&iface->stations, &sta->avl);
|
||||
|
||||
out:
|
||||
sta->present = true;
|
||||
return sta;
|
||||
}
|
||||
|
||||
void atf_interface_sta_flush(struct atf_interface *iface)
|
||||
{
|
||||
struct atf_station *sta, *tmp;
|
||||
|
||||
avl_for_each_element_safe(&iface->stations, sta, avl, tmp) {
|
||||
if (sta->present)
|
||||
continue;
|
||||
|
||||
avl_delete(&iface->stations, &sta->avl);
|
||||
free(sta);
|
||||
}
|
||||
}
|
||||
|
||||
void atf_interface_sta_changed(struct atf_interface *iface, struct atf_station *sta)
|
||||
{
|
||||
int weight;
|
||||
|
||||
if (sta->avg_prio > config.prio_percent_thresh)
|
||||
weight = config.weight_prio;
|
||||
else if (sta->avg_prio > config.bulk_percent_thresh)
|
||||
weight = config.weight_bulk;
|
||||
else
|
||||
weight = config.weight_normal;
|
||||
|
||||
if (sta->weight == weight)
|
||||
return;
|
||||
|
||||
sta->weight = weight;
|
||||
atf_ubus_set_sta_weight(iface, sta);
|
||||
}
|
||||
|
||||
struct atf_interface *atf_interface_get(const char *ifname)
|
||||
{
|
||||
struct atf_interface *iface;
|
||||
|
||||
iface = avl_find_element(&interfaces, ifname, iface, avl);
|
||||
if (iface)
|
||||
return iface;
|
||||
|
||||
if (strlen(ifname) + 1 > sizeof(iface->ifname))
|
||||
return NULL;
|
||||
|
||||
iface = calloc(1, sizeof(*iface));
|
||||
strcpy(iface->ifname, ifname);
|
||||
iface->avl.key = iface->ifname;
|
||||
avl_init(&iface->stations, avl_macaddr_cmp, false, NULL);
|
||||
avl_insert(&interfaces, &iface->avl);
|
||||
|
||||
return iface;
|
||||
}
|
||||
|
||||
void atf_interface_update_all(void)
|
||||
{
|
||||
struct atf_interface *iface, *tmp;
|
||||
|
||||
avl_for_each_element_safe(&interfaces, iface, avl, tmp)
|
||||
atf_nl80211_interface_update(iface);
|
||||
}
|
||||
62
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/main.c
Normal file
62
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/main.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
struct atf_config config;
|
||||
int debug_flag;
|
||||
|
||||
void reset_config(void)
|
||||
{
|
||||
memset(&config, 0, sizeof(config));
|
||||
|
||||
config.voice_queue_weight = 4;
|
||||
config.min_pkt_thresh = 100;
|
||||
|
||||
config.bulk_percent_thresh = (50 << ATF_AVG_SCALE) / 100;
|
||||
config.prio_percent_thresh = (30 << ATF_AVG_SCALE) / 100;
|
||||
|
||||
config.weight_normal = 256;
|
||||
config.weight_bulk = 128;
|
||||
config.weight_prio = 512;
|
||||
}
|
||||
|
||||
static void atf_update_cb(struct uloop_timeout *t)
|
||||
{
|
||||
atf_interface_update_all();
|
||||
uloop_timeout_set(t, 1000);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static struct uloop_timeout update_timer = {
|
||||
.cb = atf_update_cb,
|
||||
};
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, argv, "d")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
debug_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reset_config();
|
||||
uloop_init();
|
||||
atf_ubus_init();
|
||||
atf_nl80211_init();
|
||||
atf_update_cb(&update_timer);
|
||||
uloop_run();
|
||||
atf_ubus_stop();
|
||||
uloop_done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
174
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/nl80211.c
Normal file
174
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/nl80211.c
Normal file
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <linux/nl80211.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <unl.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
static struct unl unl;
|
||||
|
||||
static void
|
||||
atf_parse_tid_stats(struct atf_interface *iface, struct atf_stats *stats,
|
||||
int tid, struct nlattr *attr)
|
||||
{
|
||||
struct nlattr *tb[NL80211_TID_STATS_MAX + 1];
|
||||
uint64_t msdu;
|
||||
|
||||
if (nla_parse_nested(tb, NL80211_TID_STATS_MAX, attr, NULL))
|
||||
return;
|
||||
|
||||
if (!tb[NL80211_TID_STATS_TX_MSDU])
|
||||
return;
|
||||
|
||||
msdu = nla_get_u64(tb[NL80211_TID_STATS_TX_MSDU]);
|
||||
switch (tid) {
|
||||
case 0:
|
||||
case 3:
|
||||
/* BE */
|
||||
stats->normal += msdu;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
/* BK */
|
||||
stats->bulk += msdu;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
/* VI */
|
||||
stats->prio += msdu;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
stats->prio += msdu * config.voice_queue_weight;
|
||||
/* VO */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atf_stats_total(struct atf_stats *stats)
|
||||
{
|
||||
return stats->normal + stats->prio + stats->bulk;
|
||||
}
|
||||
|
||||
static void atf_stats_diff(struct atf_stats *dest, struct atf_stats *cur, struct atf_stats *prev)
|
||||
{
|
||||
dest->normal = cur->normal - prev->normal;
|
||||
dest->prio = cur->prio - prev->prio;
|
||||
dest->bulk = cur->bulk - prev->bulk;
|
||||
}
|
||||
|
||||
static uint16_t atf_stats_avg(uint16_t avg, uint64_t cur, uint32_t total)
|
||||
{
|
||||
cur <<= ATF_AVG_SCALE;
|
||||
cur /= total;
|
||||
|
||||
if (!avg)
|
||||
return (uint16_t)cur;
|
||||
|
||||
avg *= ATF_AVG_WEIGHT_FACTOR;
|
||||
avg += cur * (ATF_AVG_WEIGHT_DIV - ATF_AVG_WEIGHT_FACTOR);
|
||||
avg /= ATF_AVG_WEIGHT_DIV;
|
||||
|
||||
if (!avg)
|
||||
avg = 1;
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
|
||||
static void atf_sta_update_avg(struct atf_station *sta, struct atf_stats *cur)
|
||||
{
|
||||
uint64_t total = atf_stats_total(cur);
|
||||
|
||||
D("sta "MAC_ADDR_FMT" total pkts: total=%d bulk=%d normal=%d prio=%d",
|
||||
MAC_ADDR_DATA(sta->macaddr), (uint32_t)total,
|
||||
(uint32_t)cur->bulk, (uint32_t)cur->normal, (uint32_t)cur->prio);
|
||||
if (total < config.min_pkt_thresh)
|
||||
return;
|
||||
|
||||
sta->avg_bulk = atf_stats_avg(sta->avg_bulk, cur->bulk, total);
|
||||
sta->avg_prio = atf_stats_avg(sta->avg_prio, cur->prio, total);
|
||||
D("avg bulk=%d prio=%d",
|
||||
(sta->avg_bulk * 100) >> ATF_AVG_SCALE,
|
||||
(sta->avg_prio * 100) >> ATF_AVG_SCALE);
|
||||
sta->stats_idx = !sta->stats_idx;
|
||||
}
|
||||
|
||||
static int
|
||||
atf_sta_cb(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct atf_interface *iface = arg;
|
||||
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||
struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
|
||||
struct atf_station *sta;
|
||||
struct atf_stats *stats, diff = {};
|
||||
struct nlattr *cur;
|
||||
int idx = 0;
|
||||
int rem;
|
||||
|
||||
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
genlmsg_attrlen(gnlh, 0), NULL);
|
||||
|
||||
if (!tb[NL80211_ATTR_STA_INFO] || !tb[NL80211_ATTR_MAC])
|
||||
return NL_SKIP;
|
||||
|
||||
if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
|
||||
tb[NL80211_ATTR_STA_INFO], NULL))
|
||||
return NL_SKIP;
|
||||
|
||||
if (!sinfo[NL80211_STA_INFO_TID_STATS])
|
||||
return NL_SKIP;
|
||||
|
||||
sta = atf_interface_sta_get(iface, nla_data(tb[NL80211_ATTR_MAC]));
|
||||
if (!sta)
|
||||
return NL_SKIP;
|
||||
|
||||
stats = &sta->stats[sta->stats_idx];
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
nla_for_each_nested(cur, sinfo[NL80211_STA_INFO_TID_STATS], rem)
|
||||
atf_parse_tid_stats(iface, stats, idx++, cur);
|
||||
|
||||
atf_stats_diff(&diff, stats, &sta->stats[!sta->stats_idx]);
|
||||
atf_sta_update_avg(sta, &diff);
|
||||
atf_interface_sta_changed(iface, sta);
|
||||
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
int atf_nl80211_interface_update(struct atf_interface *iface)
|
||||
{
|
||||
struct nl_msg *msg;
|
||||
int ifindex;
|
||||
|
||||
ifindex = if_nametoindex(iface->ifname);
|
||||
if (!ifindex)
|
||||
return -1;
|
||||
|
||||
atf_interface_sta_update(iface);
|
||||
|
||||
msg = unl_genl_msg(&unl, NL80211_CMD_GET_STATION, true);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
|
||||
unl_genl_request(&unl, msg, atf_sta_cb, iface);
|
||||
|
||||
atf_interface_sta_flush(iface);
|
||||
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int atf_nl80211_init(void)
|
||||
{
|
||||
return unl_genl_init(&unl, "nl80211");
|
||||
}
|
||||
164
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/ubus.c
Normal file
164
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/ubus.c
Normal file
@@ -0,0 +1,164 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <libubus.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
#define HOSTAPD_PREFIX "hostapd."
|
||||
|
||||
static struct ubus_auto_conn conn;
|
||||
static struct blob_buf b;
|
||||
|
||||
enum {
|
||||
ATF_CONFIG_RESET,
|
||||
ATF_CONFIG_VO_Q_WEIGHT,
|
||||
ATF_CONFIG_MIN_PKT_THRESH,
|
||||
ATF_CONFIG_BULK_PERCENT_THR,
|
||||
ATF_CONFIG_PRIO_PERCENT_THR,
|
||||
|
||||
ATF_CONFIG_WEIGHT_NORMAL,
|
||||
ATF_CONFIG_WEIGHT_PRIO,
|
||||
ATF_CONFIG_WEIGHT_BULK,
|
||||
__ATF_CONFIG_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy atf_config_policy[__ATF_CONFIG_MAX] = {
|
||||
[ATF_CONFIG_VO_Q_WEIGHT] = { "vo_queue_weight", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_MIN_PKT_THRESH] = { "update_pkt_threshold", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_BULK_PERCENT_THR] = { "bulk_percent_thresh", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_PRIO_PERCENT_THR] = { "prio_percent_thresh", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_WEIGHT_NORMAL] = { "weight_normal", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_WEIGHT_PRIO] = { "weight_prio", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_WEIGHT_BULK] = { "weight_bulk", BLOBMSG_TYPE_INT32 },
|
||||
};
|
||||
|
||||
static int
|
||||
atf_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__ATF_CONFIG_MAX];
|
||||
struct blob_attr *cur;
|
||||
static const struct {
|
||||
int id;
|
||||
int *field;
|
||||
} field_map[] = {
|
||||
{ ATF_CONFIG_VO_Q_WEIGHT, &config.voice_queue_weight },
|
||||
{ ATF_CONFIG_MIN_PKT_THRESH, &config.min_pkt_thresh },
|
||||
{ ATF_CONFIG_BULK_PERCENT_THR, &config.bulk_percent_thresh },
|
||||
{ ATF_CONFIG_PRIO_PERCENT_THR, &config.prio_percent_thresh },
|
||||
{ ATF_CONFIG_WEIGHT_NORMAL, &config.weight_normal },
|
||||
{ ATF_CONFIG_WEIGHT_PRIO, &config.weight_prio },
|
||||
{ ATF_CONFIG_WEIGHT_BULK, &config.weight_bulk },
|
||||
};
|
||||
bool reset = false;
|
||||
int i;
|
||||
|
||||
blobmsg_parse(atf_config_policy, __ATF_CONFIG_MAX, tb,
|
||||
blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if ((cur = tb[ATF_CONFIG_RESET]) != NULL)
|
||||
reset = blobmsg_get_bool(cur);
|
||||
|
||||
if (reset)
|
||||
reset_config();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(field_map); i++) {
|
||||
if ((cur = tb[field_map[i].id]) != NULL)
|
||||
*(field_map[i].field) = blobmsg_get_u32(cur);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct ubus_method atf_methods[] = {
|
||||
UBUS_METHOD("config", atf_ubus_config, atf_config_policy),
|
||||
};
|
||||
|
||||
static struct ubus_object_type atf_object_type =
|
||||
UBUS_OBJECT_TYPE("atfpolicy", atf_methods);
|
||||
|
||||
static struct ubus_object atf_object = {
|
||||
.name = "atfpolicy",
|
||||
.type = &atf_object_type,
|
||||
.methods = atf_methods,
|
||||
.n_methods = ARRAY_SIZE(atf_methods),
|
||||
};
|
||||
|
||||
static void
|
||||
atf_ubus_add_interface(struct ubus_context *ctx, const char *name)
|
||||
{
|
||||
struct atf_interface *iface;
|
||||
|
||||
iface = atf_interface_get(name + strlen(HOSTAPD_PREFIX));
|
||||
if (!iface)
|
||||
return;
|
||||
|
||||
iface->ubus_obj = 0;
|
||||
ubus_lookup_id(ctx, name, &iface->ubus_obj);
|
||||
D("add interface %s", name + strlen(HOSTAPD_PREFIX));
|
||||
}
|
||||
|
||||
static void
|
||||
atf_ubus_lookup_cb(struct ubus_context *ctx, struct ubus_object_data *obj,
|
||||
void *priv)
|
||||
{
|
||||
if (!strncmp(obj->path, HOSTAPD_PREFIX, strlen(HOSTAPD_PREFIX)))
|
||||
atf_ubus_add_interface(ctx, obj->path);
|
||||
}
|
||||
|
||||
void atf_ubus_set_sta_weight(struct atf_interface *iface, struct atf_station *sta)
|
||||
{
|
||||
D("set sta "MAC_ADDR_FMT" weight=%d", MAC_ADDR_DATA(sta->macaddr), sta->weight);
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_printf(&b, "sta", MAC_ADDR_FMT, MAC_ADDR_DATA(sta->macaddr));
|
||||
blobmsg_add_u32(&b, "weight", sta->weight);
|
||||
if (ubus_invoke(&conn.ctx, iface->ubus_obj, "update_airtime", b.head, NULL, NULL, 100))
|
||||
D("set airtime weight failed");
|
||||
}
|
||||
|
||||
static void
|
||||
atf_ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
static const struct blobmsg_policy policy =
|
||||
{ "path", BLOBMSG_TYPE_STRING };
|
||||
struct ubus_object_data obj;
|
||||
struct blob_attr *attr;
|
||||
|
||||
blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
obj.path = blobmsg_get_string(attr);
|
||||
atf_ubus_lookup_cb(ctx, &obj, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
ubus_connect_handler(struct ubus_context *ctx)
|
||||
{
|
||||
static struct ubus_event_handler ev = {
|
||||
.cb = atf_ubus_event_cb
|
||||
};
|
||||
|
||||
ubus_add_object(ctx, &atf_object);
|
||||
ubus_register_event_handler(ctx, &ev, "ubus.object.add");
|
||||
ubus_lookup(ctx, "hostapd.*", atf_ubus_lookup_cb, NULL);
|
||||
}
|
||||
|
||||
int atf_ubus_init(void)
|
||||
{
|
||||
conn.cb = ubus_connect_handler;
|
||||
ubus_auto_connect(&conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void atf_ubus_stop(void)
|
||||
{
|
||||
ubus_auto_shutdown(&conn);
|
||||
}
|
||||
62
olg-ucentral-client-sdk/feeds/ucentral/bridger/Makefile
Normal file
62
olg-ucentral-client-sdk/feeds/ucentral/bridger/Makefile
Normal file
@@ -0,0 +1,62 @@
|
||||
#
|
||||
# Copyright (C) 2022 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=bridger
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=https://github.com/nbd168/bridger
|
||||
PKG_SOURCE_DATE:=2024-04-22
|
||||
PKG_SOURCE_VERSION:=40b1c5b6be4e73a6749cf2997c664520eb20055d
|
||||
PKG_MIRROR_HASH:=83e514f93fa3b8f1d7d3b13df6196a3d176521185dbbcdeccc8ef4d999f74cda
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
PKG_BUILD_DEPENDS:=bpf-headers
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
include $(INCLUDE_DIR)/bpf.mk
|
||||
include $(INCLUDE_DIR)/nls.mk
|
||||
|
||||
define Package/bridger
|
||||
SECTION:=utils
|
||||
CATEGORY:=Base system
|
||||
TITLE:=Bridge forwarding accelerator
|
||||
DEPENDS:=+libbpf +libubox +libubus +libnl-tiny +kmod-sched-core +kmod-sched-flower +kmod-sched-bpf +kmod-sched-act-vlan $(BPF_DEPENDS)
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
-I$(STAGING_DIR)/usr/include/libnl-tiny \
|
||||
-I$(STAGING_DIR)/usr/include
|
||||
|
||||
CMAKE_OPTIONS += \
|
||||
-DLIBNL_LIBS=-lnl-tiny
|
||||
|
||||
define Build/Compile
|
||||
$(call CompileBPF,$(PKG_BUILD_DIR)/bridger-bpf.c)
|
||||
$(Build/Compile/Default)
|
||||
endef
|
||||
|
||||
define Package/bridger/install
|
||||
$(INSTALL_DIR) \
|
||||
$(1)/etc/config \
|
||||
$(1)/etc/init.d \
|
||||
$(1)/lib/bpf \
|
||||
$(1)/usr/sbin
|
||||
$(INSTALL_DATA) $(PKG_BUILD_DIR)/bridger-bpf.o $(1)/lib/bpf
|
||||
$(INSTALL_BIN) \
|
||||
$(PKG_INSTALL_DIR)/usr/bin/bridger \
|
||||
$(1)/usr/sbin/
|
||||
$(INSTALL_DATA) ./files/bridger.conf $(1)/etc/config/bridger
|
||||
$(INSTALL_BIN) ./files/bridger.init $(1)/etc/init.d/bridger
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,bridger))
|
||||
@@ -0,0 +1,3 @@
|
||||
config defaults
|
||||
# example for blacklisting individual devices or bridges
|
||||
# list blacklist eth0
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (c) 2021 OpenWrt.org
|
||||
|
||||
START=19
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/bridger
|
||||
|
||||
add_blacklist() {
|
||||
cfg="$1"
|
||||
|
||||
config_get blacklist "$cfg" blacklist
|
||||
for i in $blacklist; do
|
||||
json_add_string "" "$i"
|
||||
done
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
config_load bridger
|
||||
|
||||
json_init
|
||||
json_add_string name "config"
|
||||
json_add_array devices
|
||||
config_foreach add_blacklist defaults
|
||||
json_close_array
|
||||
|
||||
ubus call bridger set_blacklist "$(json_dump)"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger bridger
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 10 wait_for bridger
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
Index: bridger-2023-05-12-3159bbe0/bpf.c
|
||||
===================================================================
|
||||
--- bridger-2023-05-12-3159bbe0.orig/bpf.c
|
||||
+++ bridger-2023-05-12-3159bbe0/bpf.c
|
||||
@@ -42,6 +42,10 @@ void bridger_bpf_flow_upload(struct brid
|
||||
if (bpf_map_lookup_elem(map_offload, &flow->key, &val) == 0)
|
||||
flow->offload.packets = val.packets;
|
||||
|
||||
+ if ((flow->key.ifindex == flow->offload.target_port) &&
|
||||
+ (flow->key.vlan == flow->offload.vlan)) {
|
||||
+ return;
|
||||
+ }
|
||||
bpf_map_update_elem(map_offload, &flow->key, &flow->offload, BPF_ANY);
|
||||
}
|
||||
|
||||
27
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/Makefile
Normal file
27
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ieee8021x
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
PKG_SOURCE_DATE:=2023-10-05
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/ieee8021x
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Wired 802.1x
|
||||
DEPENDS:=+libubox +libubus +libuci
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
|
||||
endef
|
||||
|
||||
define Package/ieee8021x/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,ieee8021x))
|
||||
@@ -0,0 +1,5 @@
|
||||
#config network
|
||||
# list ports 'lan1'
|
||||
# list ports 'lan2'
|
||||
# list ports 'lan3'
|
||||
# list ports 'lan4'
|
||||
21
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/etc/init.d/ieee8021x
Executable file
21
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/etc/init.d/ieee8021x
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=80
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/ieee8021x.uc
|
||||
|
||||
reload_service() {
|
||||
ubus call ieee8021x reload
|
||||
restart
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger ieee8021x
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
307
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/usr/bin/ieee8021x.uc
Executable file
307
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/usr/bin/ieee8021x.uc
Executable file
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
let ubus = require('ubus').connect();
|
||||
let uci = require('uci').cursor();
|
||||
let uloop = require('uloop');
|
||||
let rtnl = require('rtnl');
|
||||
let fs = require('fs');
|
||||
let log = require("log");
|
||||
let hapd_subscriber;
|
||||
let device_subscriber;
|
||||
let ifaces = {};
|
||||
|
||||
/* load UCI */
|
||||
let config;
|
||||
function config_load() {
|
||||
config = uci.get_all('ieee8021x', '@config[0]');
|
||||
config.ports = {};
|
||||
for (let k, port in uci.get_all('ieee8021x')) {
|
||||
if (port['.type'] != 'port')
|
||||
continue;
|
||||
config.ports[k] = port;
|
||||
}
|
||||
}
|
||||
|
||||
/* set a wired ports auth state */
|
||||
function netifd_handle_iface(name, auth_status, vlan) {
|
||||
let msg = { name, auth_status, auth_vlans: [ ifaces[name].default_vlan + ':u' ]};
|
||||
if (ifaces[name].upstream && vlan)
|
||||
msg.auth_vlans = [ vlan + ':u' ];
|
||||
ubus.call('network.device', 'set_state', msg);
|
||||
if (auth_status && ifaces[name].upstream && vlan) {
|
||||
for (let wan in ifaces[name].wan_ports) {
|
||||
let msg = {
|
||||
name: wan,
|
||||
vlan: [ `${vlan}:t` ]
|
||||
};
|
||||
ubus.call('network.interface.up_none', 'add_device', msg);
|
||||
ubus.call('udevstats', 'add_device', { device: wan, vlan });
|
||||
}
|
||||
}
|
||||
ifaces[name].authenticated = auth_status;
|
||||
}
|
||||
|
||||
/* handle events from hostapd */
|
||||
function hapd_subscriber_notify_cb(notify) {
|
||||
switch(notify.type) {
|
||||
case 'sta-authorized':
|
||||
log.syslog(LOG_USER, "authenticated station");
|
||||
push(ifaces[notify.data.ifname].lladdr, notify.data.address);
|
||||
netifd_handle_iface(notify.data.ifname, true, notify.data.vlan);
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove arp and rate limit for a client */
|
||||
function flush_iface(name) {
|
||||
/* flush all arp entries */
|
||||
let neighs = rtnl.request(rtnl.const.RTM_GETNEIGH, rtnl.const.NLM_F_DUMP, { dev: name });
|
||||
for (let neigh in neighs)
|
||||
if (neigh.lladdr in ifaces[name].lladdr) {
|
||||
rtnl.request(rtnl.const.RTM_DELNEIGH, 0, { dst: neigh.dst, dev: neigh.dev, family: neigh.family });
|
||||
ubus.call('ratelimit', 'client_delete', { device: neigh.dev, address: neigh.lladdr });
|
||||
}
|
||||
ifaces[name].lladdr = [];
|
||||
}
|
||||
|
||||
/* generate a hostapd configuration */
|
||||
function hostapd_start(iface) {
|
||||
if (!fs.stat("/sys/class/net/" + iface)) {
|
||||
log.syslog(LOG_ERR, "Interface ${iface} does not exist yet");
|
||||
return;
|
||||
}
|
||||
if (!config.auth_server_addr) {
|
||||
log.syslog(LOG_ERR, "Auth server address is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
let path = '/var/run/hostapd-' + iface + '.conf';
|
||||
let file = fs.open(path, 'w+');
|
||||
|
||||
file.write('driver=wired\n');
|
||||
file.write('ieee8021x=1\n');
|
||||
file.write('eap_reauth_period=0\n');
|
||||
file.write('ctrl_interface=/var/run/hostapd\n');
|
||||
file.write('interface=' + iface + '\n');
|
||||
file.write('ca_cert=' + config.ca + '\n');
|
||||
file.write('server_cert=' + config.cert + '\n');
|
||||
file.write('private_key=' + config.key + '\n');
|
||||
file.write('dynamic_vlan=1\n');
|
||||
file.write('vlan_no_bridge=1\n');
|
||||
file.write('vlan_naming=1\n');
|
||||
|
||||
if (config.auth_server_addr) {
|
||||
file.write('dynamic_own_ip_addr=1\n');
|
||||
file.write('auth_server_addr=' + config.auth_server_addr + '\n');
|
||||
file.write('auth_server_port=' + config.auth_server_port + '\n');
|
||||
file.write('auth_server_shared_secret=' + config.auth_server_secret + '\n');
|
||||
if (config.acct_server_addr && config.acct_server_port && config.acct_server_secret + '\n') {
|
||||
file.write('acct_server_addr=' + config.acct_server_addr + '\n');
|
||||
file.write('acct_server_port=' + config.acct_server_port + '\n');
|
||||
file.write('acct_server_shared_secret=' + config.acct_server_secret + '\n');
|
||||
}
|
||||
if (config.nas_identifier)
|
||||
file.write('nas_identifier=${config.nas_identifier}\n');
|
||||
if (config.coa_server_addr && config.coa_server_port && config.coa_server_secret) {
|
||||
file.write('radius_das_client=' + config.coa_server_addr + ' ' + config.coa_server_secret + '\n');
|
||||
file.write('radius_das_port=' + config.coa_server_port + '\n');
|
||||
}
|
||||
if (+config.mac_address_bypass)
|
||||
file.write('macaddr_acl=2\n');
|
||||
} else {
|
||||
file.write('eap_server=1\n');
|
||||
file.write('eap_user_file=/var/run/hostapd-ieee8021x.eap_user\n');
|
||||
}
|
||||
file.close();
|
||||
|
||||
/* is hostapd already running ? */
|
||||
/*
|
||||
* For certains scenarios, we need to remove and add
|
||||
* instead of reload (reload did not work).
|
||||
* Consider the following scenario -
|
||||
* Say on CIG 186w as an example
|
||||
* eth0.4086 interface exists with some non-ieee8021x config.
|
||||
* Push ieee8021x config. In general the flow is that
|
||||
* reload_config is called followed by invocation of services (from ucentral-schema)
|
||||
* Services inovation does n't wait until the config reloaded ie in this context
|
||||
* ieee8021x service is invoked much before the network interfaces are recreated.
|
||||
* That is not correct. To handle this, we capture link-up events
|
||||
* and remove the existing interface (in hostapd as shown below) and add again
|
||||
*/
|
||||
|
||||
if (ifaces[iface].hostapd) {
|
||||
log.syslog(LOG_USER, "Remove the config ${iface}");
|
||||
ubus.call('hostapd', 'config_remove', { iface: iface });
|
||||
}
|
||||
log.syslog(LOG_USER, "Add config (clear the old one) " + iface);
|
||||
ubus.call('hostapd', 'config_add', { iface: iface, config: path });
|
||||
system('ifconfig ' + iface + ' up');
|
||||
// Subscribe to corresponding hostapd if it is (re)added
|
||||
hapd_subscriber.subscribe("hostapd." + iface);
|
||||
}
|
||||
|
||||
/* build a list of all running and new interfaces */
|
||||
/* handle events from netifd */
|
||||
function device_subscriber_notify_cb(notify) {
|
||||
switch(notify.type) {
|
||||
case 'link_down':
|
||||
if (!ifaces[notify.data.name])
|
||||
break;
|
||||
|
||||
/* de-auth all clients */
|
||||
ubus.call(ifaces[notify.data.name].path, 'del_clients');
|
||||
ifaces[notify.data.name].authenticated = false;
|
||||
flush_iface(notify.data.name);
|
||||
ubus.call('ratelimit', 'device_delete', { device: notify.data.name });
|
||||
break;
|
||||
case 'link_up':
|
||||
if (!ifaces[notify.data.name])
|
||||
break;
|
||||
log.syslog(LOG_USER, "starting iface ${notify.data.name}");
|
||||
hostapd_start(notify.data.name);
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function subscriber_remove_cb(remove) {
|
||||
}
|
||||
|
||||
/* track and subscribe to a hostapd/netifd object */
|
||||
function ubus_unsub_object(add, id, path) {
|
||||
let object = split(path, '.');
|
||||
|
||||
if (path == 'network.device' && add) {
|
||||
device_subscriber.subscribe(path);
|
||||
}
|
||||
|
||||
let object_hostapd = object[0];
|
||||
let object_ifaces = object[1];
|
||||
|
||||
if (length(object) > 2) {
|
||||
// For swconfig platforms
|
||||
// The interface name is of the form eth0.4086 etc
|
||||
object_ifaces = object[1] + "." + object[2];
|
||||
}
|
||||
|
||||
if (object_hostapd != 'hostapd' || !ifaces[object_ifaces])
|
||||
return;
|
||||
if (add) {
|
||||
log.syslog(LOG_USER, "adding " + path);
|
||||
hapd_subscriber.subscribe(path);
|
||||
ifaces[object_ifaces].hostapd = true;
|
||||
ifaces[object_ifaces].path = path;
|
||||
} else {
|
||||
// Mark the port as unauthorized. but dont delete it
|
||||
// ifaces contains the configured (from uci config) ports
|
||||
netifd_handle_iface(object_ifaces, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* try to add all enumerated objects */
|
||||
function ubus_listener_cb(event, payload) {
|
||||
ubus_unsub_object(event == 'ubus.object.add', payload.id, payload.path);
|
||||
}
|
||||
|
||||
/* setup the ubus object listener, allowing us to track hostapd objects that get added and removed */
|
||||
function ubus_listener_init() {
|
||||
/* setup the notification listener */
|
||||
hapd_subscriber = ubus.subscriber(hapd_subscriber_notify_cb, subscriber_remove_cb);
|
||||
device_subscriber = ubus.subscriber(device_subscriber_notify_cb, subscriber_remove_cb);
|
||||
|
||||
/* enumerate all existing ojects */
|
||||
let list = ubus.list();
|
||||
for (let k, path in list)
|
||||
ubus_unsub_object(true, 0, path);
|
||||
|
||||
/* register add/remove handlers */
|
||||
ubus.listener('ubus.object.add', ubus_listener_cb);
|
||||
ubus.listener('ubus.object.remove', ubus_listener_cb);
|
||||
}
|
||||
|
||||
function prepare_ifaces() {
|
||||
for (let k, v in ifaces)
|
||||
v.active = false;
|
||||
|
||||
for (let k, port in config.ports) {
|
||||
ifaces[port.iface] ??= {};
|
||||
ifaces[port.iface].active = true;
|
||||
ifaces[port.iface].authenticated = false;
|
||||
ifaces[port.iface].default_vlan = +port.vlan;
|
||||
ifaces[port.iface].wan_ports = port.wan_ports;
|
||||
ifaces[port.iface].lladdr = [];
|
||||
}
|
||||
|
||||
for (let iface, v in ifaces) {
|
||||
if (v.active)
|
||||
continue;
|
||||
ubus.call('hostapd', 'config_remove', { iface });
|
||||
delete ifaces[iface];
|
||||
system('ifconfig ' + iface + ' down');
|
||||
}
|
||||
}
|
||||
|
||||
/* start all active interfaces */
|
||||
function start_ifaces() {
|
||||
for (let iface, v in ifaces)
|
||||
hostapd_start(iface);
|
||||
}
|
||||
|
||||
/* shutdown all interfaces */
|
||||
function shutdown() {
|
||||
for (let iface, v in ifaces) {
|
||||
log.syslog(LOG_USER, "shutdown");
|
||||
ubus.call('hostapd', 'config_remove', { iface });
|
||||
netifd_handle_iface(iface, false);
|
||||
flush_iface(iface);
|
||||
ubus.call('ratelimit', 'device_delete', { device: iface });
|
||||
}
|
||||
}
|
||||
|
||||
/* the object published on ubus */
|
||||
let ubus_methods = {
|
||||
dump: {
|
||||
call: function(req) {
|
||||
return ifaces;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
|
||||
reload: {
|
||||
call: function(req) {
|
||||
config_load();
|
||||
prepare_ifaces();
|
||||
start_ifaces();
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/* handle rtnl events for carrier-down events */
|
||||
function rtnl_cb(msg) {
|
||||
if (msg.cmd != rtnl.const.RTM_NEWNEIGH)
|
||||
return;
|
||||
if (!ifaces[msg.msg.dev] || ifaces[msg.msg.dev].authenticated)
|
||||
return;
|
||||
ubus.call(ifaces[msg.msg.dev].path, 'mac_auth', { addr: msg.msg.lladdr });
|
||||
}
|
||||
|
||||
log.openlog("ieee8021x", log.LOG_PID, log.LOG_USER);
|
||||
uloop.init();
|
||||
ubus.publish('ieee8021x', ubus_methods);
|
||||
config_load();
|
||||
rtnl.listener(rtnl_cb, null, [ rtnl.const.RTNLGRP_NEIGH ]);
|
||||
prepare_ifaces();
|
||||
ubus_listener_init();
|
||||
start_ifaces();
|
||||
uloop.run();
|
||||
uloop.done();
|
||||
shutdown();
|
||||
log.closelog();
|
||||
103
olg-ucentral-client-sdk/feeds/ucentral/libwebsockets/Makefile
Normal file
103
olg-ucentral-client-sdk/feeds/ucentral/libwebsockets/Makefile
Normal file
@@ -0,0 +1,103 @@
|
||||
#
|
||||
# Copyright (C) 2014-2015 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=libwebsockets
|
||||
PKG_VERSION:=4.1.4
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/warmcat/libwebsockets/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=00da77b4b89db3e0b1a2778f756f08d55d7f6b1632e18d981fc399c412866147
|
||||
|
||||
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=LGPL-2.1+exception
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
CMAKE_INSTALL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
CMAKE_OPTIONS += -DLWS_IPV6=$(if $(CONFIG_IPV6),ON,OFF)
|
||||
CMAKE_OPTIONS += -DLWS_WITHOUT_TESTAPPS=ON
|
||||
|
||||
# other options worth noting
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_EXTENSIONS=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_DAEMONIZE=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_SERVER=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_DEBUG=ON
|
||||
|
||||
|
||||
define Package/libwebsockets/Default
|
||||
SECTION:=libs
|
||||
CATEGORY:=Libraries
|
||||
TITLE:=libwebsockets
|
||||
DEPENDS:=+zlib +libcap
|
||||
URL:=https://libwebsockets.org
|
||||
MAINTAINER:=Karl Palsson <karlp@etactica.com>
|
||||
PROVIDES:= libwebsockets
|
||||
endef
|
||||
|
||||
define Package/libwebsockets-openssl
|
||||
$(call Package/libwebsockets/Default)
|
||||
TITLE += (OpenSSL)
|
||||
DEPENDS += +libopenssl
|
||||
VARIANT:=openssl
|
||||
endef
|
||||
|
||||
define Package/libwebsockets-mbedtls
|
||||
$(call Package/$(PKG_NAME)/Default)
|
||||
TITLE += (mbedTLS)
|
||||
DEPENDS += +libmbedtls
|
||||
VARIANT:=mbedtls
|
||||
endef
|
||||
|
||||
define Package/libwebsockets-full
|
||||
$(call Package/libwebsockets/Default)
|
||||
TITLE += (Full - OpenSSL, libuv, plugins, CGI)
|
||||
DEPENDS += +libopenssl +libuv
|
||||
VARIANT:=full
|
||||
endef
|
||||
|
||||
ifeq ($(BUILD_VARIANT),openssl)
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_CLIENT_CERTS=/etc/ssl/certs
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_SUPPORT=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_SSL=ON
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_VARIANT),mbedtls)
|
||||
CMAKE_OPTIONS += -DLWS_WITH_MBEDTLS=1
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_VARIANT),full)
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_CLIENT_CERTS=/etc/ssl/certs
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_SUPPORT=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_SSL=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_LIBUV=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_PLUGINS=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_SERVER_STATUS=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_ACCESS_LOG=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_CGI=ON
|
||||
CMAKE_OPTIONS += -DLWS_UNIX_SOCK=ON
|
||||
endif
|
||||
|
||||
define Package/libwebsockets/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libwebsockets.so* $(1)/usr/lib/
|
||||
endef
|
||||
|
||||
Package/libwebsockets-mbedtls/install = $(Package/libwebsockets/install)
|
||||
Package/libwebsockets-openssl/install = $(Package/libwebsockets/install)
|
||||
Package/libwebsockets-full/install = $(Package/libwebsockets/install)
|
||||
|
||||
$(eval $(call BuildPackage,libwebsockets-openssl))
|
||||
$(eval $(call BuildPackage,libwebsockets-mbedtls))
|
||||
$(eval $(call BuildPackage,libwebsockets-full))
|
||||
@@ -0,0 +1,16 @@
|
||||
Index: libwebsockets-4.1.4/CMakeLists.txt
|
||||
===================================================================
|
||||
--- libwebsockets-4.1.4.orig/CMakeLists.txt
|
||||
+++ libwebsockets-4.1.4/CMakeLists.txt
|
||||
@@ -709,10 +709,10 @@ if ((CMAKE_COMPILER_IS_GNUCC OR CMAKE_CO
|
||||
endif()
|
||||
endif()
|
||||
|
||||
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations" )
|
||||
if (COMPILER_IS_CLANG)
|
||||
|
||||
# otherwise osx blows a bunch of openssl deprecated api errors
|
||||
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations" )
|
||||
if (UNIX AND LWS_HAVE_PTHREAD_H)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -Wno-error=unused-command-line-argument" )
|
||||
endif()
|
||||
@@ -0,0 +1,30 @@
|
||||
menu "Configuration"
|
||||
depends on PACKAGE_modemmanager
|
||||
|
||||
config MODEMMANAGER_WITH_MBIM
|
||||
bool "Include MBIM support"
|
||||
default y
|
||||
help
|
||||
Compile ModemManager with MBIM support
|
||||
|
||||
config MODEMMANAGER_WITH_QMI
|
||||
bool "Include QMI support"
|
||||
default y
|
||||
help
|
||||
Compile ModemManager with QMI support
|
||||
|
||||
config MODEMMANAGER_WITH_QRTR
|
||||
bool "Include QRTR support"
|
||||
default y
|
||||
depends on MODEMMANAGER_WITH_QMI
|
||||
select LIBQMI_WITH_QRTR_GLIB
|
||||
help
|
||||
Compile ModemManager with QRTR support
|
||||
|
||||
config MODEMMANAGER_WITH_AT_COMMAND_VIA_DBUS
|
||||
bool "Allow AT commands via DBus"
|
||||
default n
|
||||
help
|
||||
Compile ModemManager allowing AT commands without debug flag
|
||||
|
||||
endmenu
|
||||
140
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/Makefile
Normal file
140
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/Makefile
Normal file
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# Copyright (C) 2016 Velocloud Inc.
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=modemmanager
|
||||
PKG_SOURCE_VERSION:=1.20.6
|
||||
PKG_RELEASE:=12
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://gitlab.freedesktop.org/mobile-broadband/ModemManager.git
|
||||
PKG_MIRROR_HASH:=e90103e2e42bb826bbbac83937a9a69f50348cd6ce0d8da655a12b65494ce7c9
|
||||
|
||||
PKG_MAINTAINER:=Nicholas Smith <nicholas@nbembedded.com>
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_BUILD_DEPENDS:=glib2/host libxslt/host
|
||||
PKG_BUILD_FLAGS:=gc-sections
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/nls.mk
|
||||
include $(INCLUDE_DIR)/meson.mk
|
||||
|
||||
TARGET_CFLAGS += -fno-merge-all-constants -fmerge-constants
|
||||
|
||||
define Package/modemmanager/config
|
||||
source "$(SOURCE)/Config.in"
|
||||
endef
|
||||
|
||||
define Package/modemmanager
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Control utility for any kind of mobile broadband modem
|
||||
URL:=https://www.freedesktop.org/wiki/Software/ModemManager
|
||||
DEPENDS:= \
|
||||
$(INTL_DEPENDS) \
|
||||
+glib2 \
|
||||
+dbus \
|
||||
+ppp \
|
||||
+MODEMMANAGER_WITH_MBIM:libmbim \
|
||||
+MODEMMANAGER_WITH_QMI:libqmi \
|
||||
+MODEMMANAGER_WITH_QRTR:libqrtr-glib
|
||||
endef
|
||||
|
||||
define Package/modemmanager/description
|
||||
ModemManager is a D-Bus-activated service which allows controlling mobile
|
||||
broadband modems. Add kernel modules for your modems as needed.
|
||||
Select Utilities/usb-modeswitch if needed.
|
||||
endef
|
||||
|
||||
MESON_ARGS += \
|
||||
-Dudev=false \
|
||||
-Dudevdir=/lib/udev \
|
||||
-Dtests=false \
|
||||
-Dsystemdsystemunitdir=no \
|
||||
-Dsystemd_suspend_resume=false \
|
||||
-Dsystemd_journal=false \
|
||||
-Dpolkit=no \
|
||||
-Dintrospection=false \
|
||||
-Dman=false \
|
||||
-Dbash_completion=false \
|
||||
-Db_lto=true \
|
||||
-Dmbim=$(if $(CONFIG_MODEMMANAGER_WITH_MBIM),true,false) \
|
||||
-Dqmi=$(if $(CONFIG_MODEMMANAGER_WITH_QMI),true,false) \
|
||||
-Dqrtr=$(if $(CONFIG_MODEMMANAGER_WITH_QRTR),true,false) \
|
||||
-Dat_command_via_dbus=$(if $(CONFIG_MODEMMANAGER_WITH_AT_COMMAND_VIA_DBUS),true,false)
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(1)/usr/include/ModemManager
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/include/ModemManager/*.h $(1)/usr/include/ModemManager
|
||||
$(INSTALL_DIR) $(1)/usr/include/libmm-glib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/include/libmm-glib/*.h $(1)/usr/include/libmm-glib
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libmm-glib.so* $(1)/usr/lib
|
||||
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/ModemManager.pc $(1)/usr/lib/pkgconfig
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/mm-glib.pc $(1)/usr/lib/pkgconfig
|
||||
$(INSTALL_DIR) $(1)/usr/share/dbus-1/interfaces
|
||||
$(CP) $(PKG_BUILD_DIR)/introspection/org.freedesktop.ModemManager1.* $(1)/usr/share/dbus-1/interfaces
|
||||
endef
|
||||
|
||||
define Package/modemmanager/install
|
||||
$(INSTALL_DIR) $(1)/lib/udev/rules.d
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/lib/udev/rules.d/*.rules $(1)/lib/udev/rules.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ModemManager $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/ModemManager-wrapper $(1)/usr/sbin
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/mmcli $(1)/usr/bin
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libmm-glib.so.* $(1)/usr/lib
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/ModemManager
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/ModemManager/libmm-shared-*.so* $(1)/usr/lib/ModemManager
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/ModemManager/libmm-plugin-*.so* $(1)/usr/lib/ModemManager
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/ModemManager/connection.d
|
||||
$(INSTALL_BIN) ./files/10-report-down $(1)/usr/lib/ModemManager/connection.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/dbus-1/system.d
|
||||
$(INSTALL_CONF) $(PKG_INSTALL_DIR)/etc/dbus-1/system.d/org.freedesktop.ModemManager1.conf $(1)/etc/dbus-1/system.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/dbus-1/system-services
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/dbus-1/system-services/org.freedesktop.ModemManager1.service $(1)/usr/share/dbus-1/system-services
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/ModemManager
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/ModemManager/*.conf $(1)/usr/share/ModemManager
|
||||
$(INSTALL_DATA) ./files/modemmanager.common $(1)/usr/share/ModemManager
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/ModemManager/fcc-unlock.available.d
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/ModemManager/fcc-unlock.available.d/* $(1)/usr/share/ModemManager/fcc-unlock.available.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/modemmanager.init $(1)/etc/init.d/modemmanager
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/usb
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-usb $(1)/etc/hotplug.d/usb
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/net
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-net $(1)/etc/hotplug.d/net
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/tty
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-tty $(1)/etc/hotplug.d/tty
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/wwan
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-wwan $(1)/etc/hotplug.d/wwan
|
||||
|
||||
$(INSTALL_DIR) $(1)/lib/netifd/proto
|
||||
$(INSTALL_BIN) ./files/modemmanager.proto $(1)/lib/netifd/proto/modemmanager.sh
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,modemmanager))
|
||||
@@ -0,0 +1,44 @@
|
||||
# OpenWrt ModemManager
|
||||
|
||||
## Description
|
||||
|
||||
Cellular modem control and connectivity
|
||||
|
||||
Optional libraries libmbim and libqmi are available.
|
||||
Your modem may require additional kernel modules and/or the usb-modeswitch
|
||||
package.
|
||||
|
||||
## Usage
|
||||
|
||||
Once installed, you can configure the 2G/3G/4G modem connections directly in
|
||||
/etc/config/network as in the following example:
|
||||
|
||||
config interface 'broadband'
|
||||
option device '/sys/devices/platform/soc/20980000.usb/usb1/1-1/1-1.2/1-1.2.1'
|
||||
option proto 'modemmanager'
|
||||
option apn 'ac.vodafone.es'
|
||||
option allowedauth 'pap chap'
|
||||
option username 'vodafone'
|
||||
option password 'vodafone'
|
||||
option pincode '7423'
|
||||
option iptype 'ipv4'
|
||||
option plmn '214001'
|
||||
option lowpower '1'
|
||||
option signalrate '30'
|
||||
option allow_roaming '1'
|
||||
|
||||
Only 'device' and 'proto' are mandatory options, the remaining ones are all
|
||||
optional.
|
||||
|
||||
The 'allowedauth' option allows limiting the list of authentication protocols.
|
||||
It is given as a space-separated list of values, including any of the
|
||||
following: 'pap', 'chap', 'mschap', 'mschapv2' or 'eap'. It will default to
|
||||
allowing all protocols.
|
||||
|
||||
The 'iptype' option supports any of these values: 'ipv4', 'ipv6' or 'ipv4v6'.
|
||||
It will default to 'ipv4' if not given.
|
||||
|
||||
The 'plmn' option allows to set the network operator MCCMNC.
|
||||
|
||||
The 'signalrate' option set's the signal refresh rate (in seconds) for the device.
|
||||
You can call signal info with command: mmcli -m 0 --signal-get
|
||||
35
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/10-report-down
Executable file
35
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/10-report-down
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
# 2022 Aleksander Morgado <aleksander@aleksander.es>
|
||||
#
|
||||
# Automatically report to netifd that the underlying modem
|
||||
# is really disconnected
|
||||
#
|
||||
# require program name and at least 4 arguments
|
||||
[ $# -lt 4 ] && exit 1
|
||||
|
||||
MODEM_PATH="$1"
|
||||
BEARER_PATH="$2"
|
||||
INTERFACE="$3"
|
||||
STATE="$4"
|
||||
|
||||
[ "${STATE}" = "disconnected" ] || exit 0
|
||||
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
. /lib/netifd/netifd-proto.sh
|
||||
INCLUDE_ONLY=1 . /lib/netifd/proto/modemmanager.sh
|
||||
|
||||
MODEM_STATUS=$(mmcli --modem="${MODEM_PATH}" --output-keyvalue)
|
||||
[ -n "${MODEM_STATUS}" ] || exit 1
|
||||
|
||||
MODEM_DEVICE=$(modemmanager_get_field "${MODEM_STATUS}" "modem.generic.device")
|
||||
[ -n "${MODEM_DEVICE}" ] || exit 2
|
||||
|
||||
CFG=$(mm_get_modem_config "${MODEM_DEVICE}")
|
||||
[ -n "${CFG}" ] || exit 3
|
||||
|
||||
logger -t "modemmanager" "interface ${CFG} (network device ${INTERFACE}) ${STATE}"
|
||||
proto_init_update $INTERFACE 0
|
||||
proto_send_update $CFG
|
||||
exit 0
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016 Velocloud Inc
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# Load common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
# We require a interface name
|
||||
[ -n "${INTERFACE}" ] || exit
|
||||
|
||||
# Always make sure the rundir exists
|
||||
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
|
||||
|
||||
# Report network interface
|
||||
mm_log "info" "${ACTION} network interface ${INTERFACE}: event processed"
|
||||
mm_report_event "${ACTION}" "${INTERFACE}" "net" "/sys${DEVPATH}"
|
||||
|
||||
# Look for an associated cdc-wdm interface
|
||||
|
||||
cdcwdm=""
|
||||
|
||||
case "${ACTION}" in
|
||||
"add") cdcwdm=$(mm_track_cdcwdm "${INTERFACE}") ;;
|
||||
"remove") cdcwdm=$(mm_untrack_cdcwdm "${INTERFACE}") ;;
|
||||
esac
|
||||
|
||||
# Report cdc-wdm device, if any
|
||||
[ -n "${cdcwdm}" ] && {
|
||||
mm_log "info" "${ACTION} cdc interface ${cdcwdm}: custom event processed"
|
||||
mm_report_event "${ACTION}" "${cdcwdm}" "usbmisc" "/sys${DEVPATH}"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016 Velocloud Inc
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# Load hotplug common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
# We require a device name
|
||||
[ -n "$DEVNAME" ] || exit
|
||||
|
||||
# Always make sure the rundir exists
|
||||
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
|
||||
|
||||
# Report TTY
|
||||
mm_log "info" "${ACTION} serial interface ${DEVNAME}: event processed"
|
||||
mm_report_event "${ACTION}" "${DEVNAME}" "tty" "/sys${DEVPATH}"
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# We need to process only full USB device removal events, we don't
|
||||
# want to process specific interface removal events.
|
||||
[ "$ACTION" = remove ] || exit
|
||||
[ -z "${INTERFACE}" ] || exit
|
||||
|
||||
# Load common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
mm_clear_modem_wait_status "/sys${DEVPATH}"
|
||||
mm_cleanup_interface_by_sysfspath "/sys${DEVPATH}"
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# Load hotplug common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
# We require a device name
|
||||
[ -n "$DEVNAME" ] || exit
|
||||
|
||||
# Always make sure the rundir exists
|
||||
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
|
||||
|
||||
# Report wwan
|
||||
mm_log "info" "${ACTION} wwan control port ${DEVNAME}: event processed"
|
||||
mm_report_event "${ACTION}" "${DEVNAME}" "wwan" "/sys${DEVPATH}"
|
||||
@@ -0,0 +1,376 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016 Velocloud Inc
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
################################################################################
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/netifd/netifd-proto.sh
|
||||
|
||||
################################################################################
|
||||
# Runtime state
|
||||
|
||||
MODEMMANAGER_RUNDIR="/var/run/modemmanager"
|
||||
MODEMMANAGER_PID_FILE="${MODEMMANAGER_RUNDIR}/modemmanager.pid"
|
||||
MODEMMANAGER_CDCWDM_CACHE="${MODEMMANAGER_RUNDIR}/cdcwdm.cache"
|
||||
MODEMMANAGER_SYSFS_CACHE="${MODEMMANAGER_RUNDIR}/sysfs.cache"
|
||||
MODEMMANAGER_EVENTS_CACHE="${MODEMMANAGER_RUNDIR}/events.cache"
|
||||
|
||||
################################################################################
|
||||
# Common logging
|
||||
|
||||
mm_log() {
|
||||
local level="$1"; shift
|
||||
logger -p "daemon.${level}" -t "ModemManager[$$]" "hotplug: $*"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Receives as input argument the full sysfs path of the device
|
||||
# Returns the physical device sysfs path
|
||||
#
|
||||
# NOTE: this method only works when the device exists, i.e. it cannot be used
|
||||
# on removal hotplug events
|
||||
|
||||
mm_find_physdev_sysfs_path() {
|
||||
local tmp_path="$1"
|
||||
|
||||
while true; do
|
||||
tmp_path=$(dirname "${tmp_path}")
|
||||
|
||||
# avoid infinite loops iterating
|
||||
[ -z "${tmp_path}" ] || [ "${tmp_path}" = "/" ] && return
|
||||
|
||||
# For USB devices, the physical device will be that with a idVendor
|
||||
# and idProduct pair of files
|
||||
[ -f "${tmp_path}"/idVendor ] && [ -f "${tmp_path}"/idProduct ] && {
|
||||
tmp_path=$(readlink -f "$tmp_path")
|
||||
echo "${tmp_path}"
|
||||
return
|
||||
}
|
||||
|
||||
# For PCI devices, the physical device will be that with a vendor
|
||||
# and device pair of files
|
||||
[ -f "${tmp_path}"/vendor ] && [ -f "${tmp_path}"/device ] && {
|
||||
tmp_path=$(readlink -f "$tmp_path")
|
||||
echo "${tmp_path}"
|
||||
return
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
################################################################################
|
||||
|
||||
# Returns the cdc-wdm name retrieved from sysfs
|
||||
mm_track_cdcwdm() {
|
||||
local wwan="$1"
|
||||
local cdcwdm
|
||||
|
||||
cdcwdm=$(ls "/sys/class/net/${wwan}/device/usbmisc/")
|
||||
[ -n "${cdcwdm}" ] || return
|
||||
|
||||
# We have to cache it for later, as we won't be able to get the
|
||||
# associated cdc-wdm device on a remove event
|
||||
echo "${wwan} ${cdcwdm}" >> "${MODEMMANAGER_CDCWDM_CACHE}"
|
||||
|
||||
echo "${cdcwdm}"
|
||||
}
|
||||
|
||||
# Returns the cdc-wdm name retrieved from the cache
|
||||
mm_untrack_cdcwdm() {
|
||||
local wwan="$1"
|
||||
local cdcwdm
|
||||
|
||||
# Look for the cached associated cdc-wdm device
|
||||
[ -f "${MODEMMANAGER_CDCWDM_CACHE}" ] || return
|
||||
|
||||
cdcwdm=$(awk -v wwan="${wwan}" '!/^#/ && $0 ~ wwan { print $2 }' "${MODEMMANAGER_CDCWDM_CACHE}")
|
||||
[ -n "${cdcwdm}" ] || return
|
||||
|
||||
# Remove from cache
|
||||
sed -i "/${wwan} ${cdcwdm}/d" "${MODEMMANAGER_CDCWDM_CACHE}"
|
||||
|
||||
echo "${cdcwdm}"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# ModemManager needs some time from the ports being added until a modem object
|
||||
# is exposed in DBus. With the logic here we do an explicit wait of N seconds
|
||||
# for ModemManager to expose the new modem object, making sure that the wait is
|
||||
# unique per device (i.e. per physical device sysfs path).
|
||||
|
||||
# Gets the modem wait status as retrieved from the cache
|
||||
mm_get_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
|
||||
# If no sysfs cache file, we're done
|
||||
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] || return
|
||||
|
||||
# Get status of the sysfs path
|
||||
awk -v sysfspath="${sysfspath}" '!/^#/ && $0 ~ sysfspath { print $2 }' "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
|
||||
# Clear the modem wait status from the cache, if any
|
||||
mm_clear_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
|
||||
local escaped_sysfspath
|
||||
|
||||
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] && {
|
||||
# escape '/', '\' and '&' for sed...
|
||||
escaped_sysfspath=$(echo "$sysfspath" | sed -e 's/[\/&]/\\&/g')
|
||||
sed -i "/${escaped_sysfspath}/d" "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
}
|
||||
|
||||
# Sets the modem wait status in the cache
|
||||
mm_set_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
local status="$2"
|
||||
|
||||
# Remove sysfs line before adding the new one with the new state
|
||||
mm_clear_modem_wait_status "${sysfspath}"
|
||||
|
||||
# Add the new status
|
||||
echo "${sysfspath} ${status}" >> "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
|
||||
# Callback for config_foreach()
|
||||
mm_get_modem_config_foreach_cb() {
|
||||
local cfg="$1"
|
||||
local sysfspath="$2"
|
||||
|
||||
local proto
|
||||
config_get proto "${cfg}" proto
|
||||
[ "${proto}" = modemmanager ] || return 0
|
||||
|
||||
local dev
|
||||
dev=$(uci_get network "${cfg}" device)
|
||||
[ "${dev}" = "${sysfspath}" ] || return 0
|
||||
|
||||
echo "${cfg}"
|
||||
}
|
||||
|
||||
# Returns the name of the interface configured for this device
|
||||
mm_get_modem_config() {
|
||||
local sysfspath="$1"
|
||||
|
||||
# Look for configuration for the given sysfs path
|
||||
config_load network
|
||||
config_foreach mm_get_modem_config_foreach_cb interface "${sysfspath}"
|
||||
}
|
||||
|
||||
# Callback for config_foreach()
|
||||
mm_get_wwan_config_foreach_cb() {
|
||||
local cfg="$1"
|
||||
local sysfspath="$2"
|
||||
|
||||
local proto
|
||||
config_get proto "${cfg}" proto
|
||||
[ "${proto}" = "wwan" ] || return 0
|
||||
|
||||
local dev devname devpath
|
||||
dev=$(uci_get network "${cfg}" device)
|
||||
devname=$(basename "${dev}")
|
||||
devpath=$(find /sys/devices -name "${devname}")
|
||||
[[ "${devpath}" = "${sysfspath}*" ]] || return 0
|
||||
|
||||
echo "${cfg}"
|
||||
}
|
||||
|
||||
# Returns the name of the interface configured for this device
|
||||
mm_get_wwan_config() {
|
||||
local sysfspath="$1"
|
||||
|
||||
# Look for configuration for the given sysfs path
|
||||
config_load network
|
||||
config_foreach mm_get_wwan_config_foreach_cb interface "${sysfspath}"
|
||||
}
|
||||
|
||||
# Wait for a modem in the specified sysfspath
|
||||
mm_wait_for_modem() {
|
||||
local cfg="$1"
|
||||
local sysfspath="$2"
|
||||
|
||||
# TODO: config max wait
|
||||
local n=45
|
||||
local step=5
|
||||
|
||||
while [ $n -ge 0 ]; do
|
||||
[ -d "${sysfspath}" ] || {
|
||||
mm_log "error" "ignoring modem detection request: no device at ${sysfspath}"
|
||||
proto_set_available "${cfg}" 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if the modem exists at the given sysfs path
|
||||
if ! mmcli -m "${sysfspath}" > /dev/null 2>&1
|
||||
then
|
||||
mm_log "error" "modem not detected at sysfs path"
|
||||
else
|
||||
mm_log "info" "modem exported successfully at ${sysfspath}"
|
||||
mm_log "info" "setting interface '${cfg}' as available"
|
||||
proto_set_available "${cfg}" 1
|
||||
ifup "${cfg}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep $step
|
||||
n=$((n-step))
|
||||
done
|
||||
|
||||
mm_log "error" "timed out waiting for the modem to get exported at ${sysfspath}"
|
||||
proto_set_available "${cfg}" 0
|
||||
return 2
|
||||
}
|
||||
|
||||
mm_report_modem_wait() {
|
||||
local sysfspath=$1
|
||||
|
||||
local parent_sysfspath status
|
||||
|
||||
parent_sysfspath=$(mm_find_physdev_sysfs_path "$sysfspath")
|
||||
[ -n "${parent_sysfspath}" ] || {
|
||||
mm_log "error" "parent device sysfspath not found"
|
||||
return
|
||||
}
|
||||
|
||||
status=$(mm_get_modem_wait_status "${parent_sysfspath}")
|
||||
case "${status}" in
|
||||
"")
|
||||
local cfg
|
||||
|
||||
cfg=$(mm_get_modem_config "${parent_sysfspath}")
|
||||
[ -z "${cfg}" ] && cfg=$(mm_get_wwan_config "${parent_sysfspath}")
|
||||
if [ -n "${cfg}" ]; then
|
||||
mm_log "info" "interface '${cfg}' is set to configure device '${parent_sysfspath}'"
|
||||
mm_log "info" "now waiting for modem at sysfs path ${parent_sysfspath}"
|
||||
mm_set_modem_wait_status "${parent_sysfspath}" "processed"
|
||||
# Launch subshell for the explicit wait
|
||||
( mm_wait_for_modem "${cfg}" "${parent_sysfspath}" ) > /dev/null 2>&1 &
|
||||
fi
|
||||
;;
|
||||
"processed")
|
||||
mm_log "info" "already waiting for modem at sysfs path ${parent_sysfspath}"
|
||||
;;
|
||||
"ignored")
|
||||
;;
|
||||
*)
|
||||
mm_log "error" "unknown status read for device at sysfs path ${parent_sysfspath}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Cleanup interfaces
|
||||
|
||||
mm_cleanup_interface_cb() {
|
||||
local cfg="$1"
|
||||
|
||||
local proto
|
||||
config_get proto "${cfg}" proto
|
||||
[ "${proto}" = modemmanager ] || return 0
|
||||
|
||||
proto_set_available "${cfg}" 0
|
||||
}
|
||||
|
||||
mm_cleanup_interfaces() {
|
||||
config_load network
|
||||
config_foreach mm_cleanup_interface_cb interface
|
||||
}
|
||||
|
||||
mm_cleanup_interface_by_sysfspath() {
|
||||
local dev="$1"
|
||||
|
||||
local cfg
|
||||
cfg=$(mm_get_modem_config "$dev")
|
||||
[ -n "${cfg}" ] || return
|
||||
|
||||
mm_log "info" "setting interface '$cfg' as unavailable"
|
||||
proto_set_available "${cfg}" 0
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Event reporting
|
||||
|
||||
# Receives as input the action, the device name and the subsystem
|
||||
mm_report_event() {
|
||||
local action="$1"
|
||||
local name="$2"
|
||||
local subsystem="$3"
|
||||
local sysfspath="$4"
|
||||
|
||||
# Do not save virtual devices
|
||||
local virtual
|
||||
virtual="$(echo "$sysfspath" | cut -d'/' -f4)"
|
||||
[ "$virtual" = "virtual" ] && {
|
||||
mm_log "debug" "sysfspath is a virtual device ($sysfspath)"
|
||||
return
|
||||
}
|
||||
|
||||
# Track/untrack events in cache
|
||||
case "${action}" in
|
||||
"add")
|
||||
# On add events, store event details in cache (if not exists yet)
|
||||
grep -qs "${name},${subsystem}" "${MODEMMANAGER_EVENTS_CACHE}" || \
|
||||
echo "${action},${name},${subsystem},${sysfspath}" >> "${MODEMMANAGER_EVENTS_CACHE}"
|
||||
;;
|
||||
"remove")
|
||||
# On remove events, remove old events from cache (match by subsystem+name)
|
||||
sed -i "/${name},${subsystem}/d" "${MODEMMANAGER_EVENTS_CACHE}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Report the event
|
||||
mm_log "debug" "event reported: action=${action}, name=${name}, subsystem=${subsystem}"
|
||||
mmcli --report-kernel-event="action=${action},name=${name},subsystem=${subsystem}" 1>/dev/null 2>&1 &
|
||||
|
||||
# Wait for added modem if a sysfspath is given
|
||||
[ -n "${sysfspath}" ] && [ "$action" = "add" ] && mm_report_modem_wait "${sysfspath}"
|
||||
}
|
||||
|
||||
mm_report_event_from_cache_line() {
|
||||
local event_line="$1"
|
||||
|
||||
local action name subsystem sysfspath
|
||||
action=$(echo "${event_line}" | awk -F ',' '{ print $1 }')
|
||||
name=$(echo "${event_line}" | awk -F ',' '{ print $2 }')
|
||||
subsystem=$(echo "${event_line}" | awk -F ',' '{ print $3 }')
|
||||
sysfspath=$(echo "${event_line}" | awk -F ',' '{ print $4 }')
|
||||
|
||||
mm_log "debug" "cached event found: action=${action}, name=${name}, subsystem=${subsystem}, sysfspath=${sysfspath}"
|
||||
mm_report_event "${action}" "${name}" "${subsystem}" "${sysfspath}"
|
||||
}
|
||||
|
||||
mm_report_events_from_cache() {
|
||||
# Remove the sysfs cache
|
||||
rm -f "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
|
||||
local n=60
|
||||
local step=1
|
||||
local mmrunning=0
|
||||
|
||||
# Wait for ModemManager to be available in the bus
|
||||
while [ $n -ge 0 ]; do
|
||||
sleep $step
|
||||
mm_log "info" "checking if ModemManager is available..."
|
||||
|
||||
if ! mmcli -L >/dev/null 2>&1
|
||||
then
|
||||
mm_log "info" "ModemManager not yet available"
|
||||
else
|
||||
mmrunning=1
|
||||
break
|
||||
fi
|
||||
n=$((n-step))
|
||||
done
|
||||
|
||||
[ ${mmrunning} -eq 1 ] || {
|
||||
mm_log "error" "couldn't report initial kernel events: ModemManager not running"
|
||||
return
|
||||
}
|
||||
|
||||
# Report cached kernel events
|
||||
while IFS= read -r event_line; do
|
||||
mm_report_event_from_cache_line "${event_line}"
|
||||
done < ${MODEMMANAGER_EVENTS_CACHE}
|
||||
}
|
||||
38
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.init
Executable file
38
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.init
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
USE_PROCD=1
|
||||
START=70
|
||||
|
||||
LOG_LEVEL="INFO"
|
||||
|
||||
stop_service() {
|
||||
# Load common utils
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
# Set all configured interfaces as unavailable
|
||||
mm_cleanup_interfaces
|
||||
}
|
||||
|
||||
start_service() {
|
||||
# Setup ModemManager service
|
||||
#
|
||||
# We will make sure that the rundir always exists, and we initially cleanup
|
||||
# all interfaces flagging them as unavailable.
|
||||
#
|
||||
# The cached events processing will wait for MM to be available in DBus
|
||||
# and will make sure all ports are re-notified to ModemManager every time
|
||||
# it starts.
|
||||
#
|
||||
# All these commands need to be executed on every MM start, even after
|
||||
# procd-triggered respawns, which is why this is wrapped in a startup
|
||||
# wrapper script called '/usr/sbin/ModemManager-wrapper'.
|
||||
#
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/sbin/ModemManager-wrapper
|
||||
procd_append_param command --log-level="$LOG_LEVEL"
|
||||
[ "$LOG_LEVEL" = "DEBUG" ] && procd_append_param command --debug
|
||||
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
|
||||
procd_set_param pidfile "${MODEMMANAGER_PID_FILE}"
|
||||
procd_close_instance
|
||||
}
|
||||
745
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.proto
Executable file
745
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.proto
Executable file
@@ -0,0 +1,745 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016-2019 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
[ -x /usr/bin/mmcli ] || exit 0
|
||||
[ -x /usr/sbin/pppd ] || exit 0
|
||||
|
||||
[ -n "$INCLUDE_ONLY" ] || {
|
||||
. /lib/functions.sh
|
||||
. ../netifd-proto.sh
|
||||
. ./ppp.sh
|
||||
init_proto "$@"
|
||||
}
|
||||
|
||||
cdr2mask ()
|
||||
{
|
||||
# Number of args to shift, 255..255, first non-255 byte, zeroes
|
||||
set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
|
||||
if [ "$1" -gt 1 ]
|
||||
then
|
||||
shift "$1"
|
||||
else
|
||||
shift
|
||||
fi
|
||||
echo "${1-0}"."${2-0}"."${3-0}"."${4-0}"
|
||||
}
|
||||
|
||||
# This method expects as first argument a list of key-value pairs, as returned by mmcli --output-keyvalue
|
||||
# The second argument must be exactly the name of the field to read
|
||||
#
|
||||
# Sample output:
|
||||
# $ mmcli -m 0 -K
|
||||
# modem.dbus-path : /org/freedesktop/ModemManager1/Modem/0
|
||||
# modem.generic.device-identifier : ed6eff2e3e0f90463da1c2a755b2acacd1335752
|
||||
# modem.generic.manufacturer : Dell Inc.
|
||||
# modem.generic.model : DW5821e Snapdragon X20 LTE
|
||||
# modem.generic.revision : T77W968.F1.0.0.4.0.GC.009\n026
|
||||
# modem.generic.carrier-configuration : GCF
|
||||
# modem.generic.carrier-configuration-revision : 08E00009
|
||||
# modem.generic.hardware-revision : DW5821e Snapdragon X20 LTE
|
||||
# ....
|
||||
modemmanager_get_field() {
|
||||
local list=$1
|
||||
local field=$2
|
||||
local value=""
|
||||
|
||||
[ -z "${list}" ] || [ -z "${field}" ] && return
|
||||
|
||||
# there is always at least a whitespace after each key, and we use that as part of the
|
||||
# key matching we do (e.g. to avoid getting 'modem.generic.state-failed-reason' as a result
|
||||
# when grepping for 'modem.generic.state'.
|
||||
line=$(echo "${list}" | grep "${field} ")
|
||||
value=$(echo ${line#*:})
|
||||
|
||||
# not found?
|
||||
[ -n "${value}" ] || return 2
|
||||
|
||||
# only print value if set
|
||||
[ "${value}" != "--" ] && echo "${value}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# build a comma-separated list of values from the list
|
||||
modemmanager_get_multivalue_field() {
|
||||
local list=$1
|
||||
local field=$2
|
||||
local value=""
|
||||
local length idx item
|
||||
|
||||
[ -z "${list}" ] || [ -z "${field}" ] && return
|
||||
|
||||
length=$(modemmanager_get_field "${list}" "${field}.length")
|
||||
[ -n "${length}" ] || return 0
|
||||
[ "$length" -ge 1 ] || return 0
|
||||
|
||||
idx=1
|
||||
while [ $idx -le "$length" ]; do
|
||||
item=$(modemmanager_get_field "${list}" "${field}.value\[$idx\]")
|
||||
[ -n "${item}" ] && [ "${item}" != "--" ] && {
|
||||
[ -n "${value}" ] && value="${value}, "
|
||||
value="${value}${item}"
|
||||
}
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
|
||||
# nothing built?
|
||||
[ -n "${value}" ] || return 2
|
||||
|
||||
# only print value if set
|
||||
echo "${value}"
|
||||
return 0
|
||||
}
|
||||
|
||||
modemmanager_cleanup_connection() {
|
||||
local modemstatus="$1"
|
||||
|
||||
local bearercount idx bearerpath
|
||||
|
||||
bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
|
||||
|
||||
# do nothing if no bearers reported
|
||||
[ -n "${bearercount}" ] && [ "$bearercount" -ge 1 ] && {
|
||||
# explicitly disconnect just in case
|
||||
mmcli --modem="${device}" --simple-disconnect >/dev/null 2>&1
|
||||
# and remove all bearer objects, if any found
|
||||
idx=1
|
||||
while [ $idx -le "$bearercount" ]; do
|
||||
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[$idx\]")
|
||||
mmcli --modem "${device}" --delete-bearer="${bearerpath}" >/dev/null 2>&1
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
modemmanager_connected_method_ppp_ipv4() {
|
||||
local interface="$1"
|
||||
local ttyname="$2"
|
||||
local username="$3"
|
||||
local password="$4"
|
||||
local allowedauth="$5"
|
||||
|
||||
# all auth types are allowed unless a user given list is given
|
||||
local authopts
|
||||
local pap=1
|
||||
local chap=1
|
||||
local mschap=1
|
||||
local mschapv2=1
|
||||
local eap=1
|
||||
|
||||
[ -n "$allowedauth" ] && {
|
||||
pap=0 chap=0 mschap=0 mschapv2=0 eap=0
|
||||
for auth in $allowedauth; do
|
||||
case $auth in
|
||||
"pap") pap=1 ;;
|
||||
"chap") chap=1 ;;
|
||||
"mschap") mschap=1 ;;
|
||||
"mschapv2") mschapv2=1 ;;
|
||||
"eap") eap=1 ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
[ $pap -eq 1 ] || append authopts "refuse-pap"
|
||||
[ $chap -eq 1 ] || append authopts "refuse-chap"
|
||||
[ $mschap -eq 1 ] || append authopts "refuse-mschap"
|
||||
[ $mschapv2 -eq 1 ] || append authopts "refuse-mschap-v2"
|
||||
[ $eap -eq 1 ] || append authopts "refuse-eap"
|
||||
|
||||
proto_run_command "${interface}" /usr/sbin/pppd \
|
||||
"${ttyname}" \
|
||||
115200 \
|
||||
nodetach \
|
||||
noaccomp \
|
||||
nobsdcomp \
|
||||
nopcomp \
|
||||
novj \
|
||||
noauth \
|
||||
$authopts \
|
||||
${username:+ user "$username"} \
|
||||
${password:+ password "$password"} \
|
||||
lcp-echo-failure 5 \
|
||||
lcp-echo-interval 15 \
|
||||
lock \
|
||||
crtscts \
|
||||
nodefaultroute \
|
||||
usepeerdns \
|
||||
ipparam "${interface}" \
|
||||
ip-up-script /lib/netifd/ppp-up \
|
||||
ip-down-script /lib/netifd/ppp-down
|
||||
}
|
||||
|
||||
modemmanager_disconnected_method_ppp_ipv4() {
|
||||
local interface="$1"
|
||||
|
||||
echo "running disconnection (ppp method)"
|
||||
|
||||
[ -n "${ERROR}" ] && {
|
||||
local errorstring
|
||||
errorstring=$(ppp_exitcode_tostring "${ERROR}")
|
||||
case "$ERROR" in
|
||||
0)
|
||||
;;
|
||||
2)
|
||||
proto_notify_error "$interface" "$errorstring"
|
||||
proto_block_restart "$interface"
|
||||
;;
|
||||
*)
|
||||
proto_notify_error "$interface" "$errorstring"
|
||||
;;
|
||||
esac
|
||||
} || echo "pppd result code not given"
|
||||
|
||||
proto_kill_command "$interface"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_dhcp_ipv4() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local metric="$3"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
proto_send_update "${interface}"
|
||||
|
||||
json_init
|
||||
json_add_string name "${interface}_4"
|
||||
json_add_string ifname "@${interface}"
|
||||
json_add_string proto "dhcp"
|
||||
proto_add_dynamic_defaults
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
json_close_object
|
||||
ubus call network add_dynamic "$(json_dump)"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_static_ipv4() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local address="$3"
|
||||
local prefix="$4"
|
||||
local gateway="$5"
|
||||
local mtu="$6"
|
||||
local dns1="$7"
|
||||
local dns2="$8"
|
||||
local metric="$9"
|
||||
|
||||
local mask=""
|
||||
|
||||
[ -n "${address}" ] || {
|
||||
proto_notify_error "${interface}" ADDRESS_MISSING
|
||||
return
|
||||
}
|
||||
|
||||
[ -n "${prefix}" ] || {
|
||||
proto_notify_error "${interface}" PREFIX_MISSING
|
||||
return
|
||||
}
|
||||
mask=$(cdr2mask "${prefix}")
|
||||
|
||||
[ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
echo "adding IPv4 address ${address}, netmask ${mask}"
|
||||
proto_add_ipv4_address "${address}" "${mask}"
|
||||
[ -n "${gateway}" ] && {
|
||||
echo "adding default IPv4 route via ${gateway}"
|
||||
proto_add_ipv4_route "0.0.0.0" "0" "${gateway}" "${address}"
|
||||
}
|
||||
[ -n "${dns1}" ] && {
|
||||
echo "adding primary DNS at ${dns1}"
|
||||
proto_add_dns_server "${dns1}"
|
||||
}
|
||||
[ -n "${dns2}" ] && {
|
||||
echo "adding secondary DNS at ${dns2}"
|
||||
proto_add_dns_server "${dns2}"
|
||||
}
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
proto_send_update "${interface}"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_dhcp_ipv6() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local metric="$3"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
proto_send_update "${interface}"
|
||||
|
||||
json_init
|
||||
json_add_string name "${interface}_6"
|
||||
json_add_string ifname "@${interface}"
|
||||
json_add_string proto "dhcpv6"
|
||||
proto_add_dynamic_defaults
|
||||
json_add_string extendprefix 1 # RFC 7278: Extend an IPv6 /64 Prefix to LAN
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
json_close_object
|
||||
ubus call network add_dynamic "$(json_dump)"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_static_ipv6() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local address="$3"
|
||||
local prefix="$4"
|
||||
local gateway="$5"
|
||||
local mtu="$6"
|
||||
local dns1="$7"
|
||||
local dns2="$8"
|
||||
local metric="$9"
|
||||
|
||||
[ -n "${address}" ] || {
|
||||
proto_notify_error "${interface}" ADDRESS_MISSING
|
||||
return
|
||||
}
|
||||
|
||||
[ -n "${prefix}" ] || {
|
||||
proto_notify_error "${interface}" PREFIX_MISSING
|
||||
return
|
||||
}
|
||||
|
||||
[ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
echo "adding IPv6 address ${address}, prefix ${prefix}"
|
||||
proto_add_ipv6_address "${address}" "128"
|
||||
proto_add_ipv6_prefix "${address}/${prefix}"
|
||||
[ -n "${gateway}" ] && {
|
||||
echo "adding default IPv6 route via ${gateway}"
|
||||
proto_add_ipv6_route "${gateway}" "128"
|
||||
proto_add_ipv6_route "::0" "0" "${gateway}" "" "" "${address}/${prefix}"
|
||||
}
|
||||
[ -n "${dns1}" ] && {
|
||||
echo "adding primary DNS at ${dns1}"
|
||||
proto_add_dns_server "${dns1}"
|
||||
}
|
||||
[ -n "${dns2}" ] && {
|
||||
echo "adding secondary DNS at ${dns2}"
|
||||
proto_add_dns_server "${dns2}"
|
||||
}
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
proto_send_update "${interface}"
|
||||
}
|
||||
|
||||
modemmanager_disconnected_method_common() {
|
||||
local interface="$1"
|
||||
|
||||
echo "running disconnection (common)"
|
||||
|
||||
proto_init_update "*" 0
|
||||
proto_send_update "${interface}"
|
||||
}
|
||||
|
||||
proto_modemmanager_init_config() {
|
||||
available=1
|
||||
no_device=1
|
||||
proto_config_add_string device
|
||||
proto_config_add_string apn
|
||||
proto_config_add_string 'allowedauth:list(string)'
|
||||
proto_config_add_string username
|
||||
proto_config_add_string password
|
||||
proto_config_add_string allowedmode
|
||||
proto_config_add_string preferredmode
|
||||
proto_config_add_string pincode
|
||||
proto_config_add_string iptype
|
||||
proto_config_add_string plmn
|
||||
proto_config_add_int signalrate
|
||||
proto_config_add_boolean lowpower
|
||||
proto_config_add_boolean allow_roaming
|
||||
proto_config_add_defaults
|
||||
}
|
||||
|
||||
# Append param to the global 'connectargs' variable.
|
||||
append_param() {
|
||||
local param="$1"
|
||||
|
||||
[ -z "$param" ] && return
|
||||
[ -z "$connectargs" ] || connectargs="${connectargs},"
|
||||
connectargs="${connectargs}${param}"
|
||||
}
|
||||
|
||||
modemmanager_set_allowed_mode() {
|
||||
local device="$1"
|
||||
local interface="$2"
|
||||
local allowedmode="$3"
|
||||
|
||||
echo "setting allowed mode to '${allowedmode}'"
|
||||
mmcli --modem="${device}" --set-allowed-modes="${allowedmode}" || {
|
||||
proto_notify_error "${interface}" MM_INVALID_ALLOWED_MODES_LIST
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
modemmanager_set_preferred_mode() {
|
||||
local device="$1"
|
||||
local interface="$2"
|
||||
local allowedmode="$3"
|
||||
local preferredmode="$4"
|
||||
|
||||
[ -z "${preferredmode}" ] && {
|
||||
echo "no preferred mode configured"
|
||||
proto_notify_error "${interface}" MM_NO_PREFERRED_MODE_CONFIGURED
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
|
||||
[ -z "${allowedmode}" ] && {
|
||||
echo "no allowed mode configured"
|
||||
proto_notify_error "${interface}" MM_NO_ALLOWED_MODE_CONFIGURED
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "setting preferred mode to '${preferredmode}' (${allowedmode})"
|
||||
mmcli --modem="${device}" \
|
||||
--set-preferred-mode="${preferredmode}" \
|
||||
--set-allowed-modes="${allowedmode}" || {
|
||||
proto_notify_error "${interface}" MM_FAILED_SETTING_PREFERRED_MODE
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
exec_at_command() {
|
||||
local at_command=$1
|
||||
local serial_dev=$2
|
||||
|
||||
local resp_file="/tmp/at_response.txt"
|
||||
local cmd_timeout=1
|
||||
local retry_limit=3
|
||||
local i=0
|
||||
local socat_pid reader_pid
|
||||
|
||||
while [ $i -lt ${retry_limit} ]; do
|
||||
# Flush old data
|
||||
> ${resp_file}
|
||||
|
||||
echo "${at_command}" | socat - /dev/${serial_dev},crnl &
|
||||
socat_pid=$!
|
||||
|
||||
{
|
||||
cat /dev/${serial_dev} > ${resp_file} &
|
||||
reader_pid=$!
|
||||
sleep ${cmd_timeout}
|
||||
kill -9 ${reader_pid} 2>/dev/null
|
||||
}
|
||||
|
||||
kill -9 ${socat_pid} 2>/dev/null
|
||||
|
||||
case $(cat ${resp_file}) in
|
||||
*OK*)
|
||||
echo "AT command executed successfully"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo -n "AT command failed or no response"
|
||||
[ -n "$i" ] && echo ", retry $i" || echo
|
||||
;;
|
||||
esac
|
||||
|
||||
let "i++"
|
||||
done
|
||||
|
||||
rm -f ${resp_file}
|
||||
}
|
||||
|
||||
_mm_get_processed_device() {
|
||||
local mm_rundir="/var/run/modemmanager"
|
||||
local mm_sysfs_cache="${mm_rundir}/sysfs.cache"
|
||||
|
||||
awk -v status="processed" '!/^#/ && $0 ~ status {print $1}' "${mm_sysfs_cache}"
|
||||
}
|
||||
|
||||
proto_modemmanager_quectel_setup() {
|
||||
local apn username password pdptype
|
||||
local manufacturer ser_port context_id context_type at_command
|
||||
json_get_vars apn username password pdptype
|
||||
|
||||
local device=$(_mm_get_processed_device)
|
||||
[ -n "${device}" ] || {
|
||||
echo "No processed device"
|
||||
return 1
|
||||
}
|
||||
|
||||
# validate that ModemManager is handling the modem at the sysfs path
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
modempath=$(modemmanager_get_field "${modemstatus}" "modem.dbus-path")
|
||||
[ -n "${modempath}" ] || {
|
||||
echo "Device not managed by ModemManager"
|
||||
return 1
|
||||
}
|
||||
echo "modem available at ${modempath}"
|
||||
|
||||
# workaround for Quectel embedded TCP/IP Stack
|
||||
manufacturer=$(modemmanager_get_field "${modemstatus}" "modem.generic.manufacturer")
|
||||
ser_port=$(mmcli --modem="${device}" -K | grep modem.generic.ports | grep tty | awk '{print $3}' | head -n 1)
|
||||
if [ "${manufacturer}" = "Quectel" ]; then
|
||||
echo "setup TCP/IP Context"
|
||||
case "${pdptype}" in
|
||||
ip)
|
||||
context_type=1
|
||||
;;
|
||||
ipv6)
|
||||
context_type=2
|
||||
;;
|
||||
ipv4v6|*)
|
||||
context_type=3
|
||||
;;
|
||||
esac
|
||||
if [ -n "${username}" ] && [ -n "${password}" ]; then
|
||||
at_command="at+qicsgp=1,${context_type},\"${apn}\",\"${username}\",\"${password}\",3"
|
||||
else
|
||||
at_command="at+qicsgp=1,${context_type},\"${apn}\",\"\",\"\",0"
|
||||
fi
|
||||
exec_at_command "${at_command}" "${ser_port}"
|
||||
fi
|
||||
}
|
||||
|
||||
proto_modemmanager_setup() {
|
||||
local interface="$1"
|
||||
|
||||
local modempath modemstatus bearercount bearerpath connectargs bearerstatus beareriface
|
||||
local bearermethod_ipv4 bearermethod_ipv6 auth cliauth
|
||||
local operatorname operatorid registration accesstech signalquality
|
||||
local allowedmode preferredmode
|
||||
|
||||
local device apn allowedauth username password pincode
|
||||
local iptype plmn metric signalrate allow_roaming
|
||||
|
||||
local address prefix gateway mtu dns1 dns2
|
||||
|
||||
json_get_vars device apn allowedauth username password
|
||||
json_get_vars pincode iptype plmn metric signalrate allow_roaming
|
||||
json_get_vars allowedmode preferredmode
|
||||
|
||||
# validate sysfs path given in config
|
||||
[ -n "${device}" ] || {
|
||||
echo "No device specified"
|
||||
proto_notify_error "${interface}" NO_DEVICE
|
||||
proto_set_available "${interface}" 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# validate that ModemManager is handling the modem at the sysfs path
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
modempath=$(modemmanager_get_field "${modemstatus}" "modem.dbus-path")
|
||||
[ -n "${modempath}" ] || {
|
||||
echo "Device not managed by ModemManager"
|
||||
proto_notify_error "${interface}" DEVICE_NOT_MANAGED
|
||||
proto_set_available "${interface}" 0
|
||||
return 1
|
||||
}
|
||||
echo "modem available at ${modempath}"
|
||||
|
||||
[ -z "${allowedmode}" ] || {
|
||||
case "$allowedmode" in
|
||||
"2g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "2g"
|
||||
;;
|
||||
"3g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "3g"
|
||||
;;
|
||||
"4g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "4g"
|
||||
;;
|
||||
"5g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "5g"
|
||||
;;
|
||||
*)
|
||||
modemmanager_set_preferred_mode "$device" \
|
||||
"$interface" "${allowedmode}" "${preferredmode}"
|
||||
;;
|
||||
esac
|
||||
# check error for allowed_mode and preferred_mode function call
|
||||
[ "$?" -ne "0" ] && return 1
|
||||
}
|
||||
|
||||
# always cleanup before attempting a new connection, just in case
|
||||
modemmanager_cleanup_connection "${modemstatus}"
|
||||
|
||||
# if allowedauth list given, build option string
|
||||
for auth in $allowedauth; do
|
||||
cliauth="${cliauth}${cliauth:+|}$auth"
|
||||
done
|
||||
|
||||
# setup connect args; APN mandatory (even if it may be empty)
|
||||
echo "starting connection with apn '${apn}'..."
|
||||
proto_notify_error "${interface}" MM_CONNECT_IN_PROGRESS
|
||||
|
||||
# setup allow-roaming parameter
|
||||
if [ -n "${allow_roaming}" ] && [ "${allow_roaming}" -eq 0 ];then
|
||||
allow_roaming="no"
|
||||
else
|
||||
# allowed unless a user set the opposite
|
||||
allow_roaming="yes"
|
||||
fi
|
||||
|
||||
# Append options to 'connectargs' variable
|
||||
append_param "apn=${apn}"
|
||||
append_param "allow-roaming=${allow_roaming}"
|
||||
append_param "${iptype:+ip-type=${iptype}}"
|
||||
append_param "${plmn:+operator-id=${plmn}}"
|
||||
append_param "${cliauth:+allowed-auth=${cliauth}}"
|
||||
append_param "${username:+user=${username}}"
|
||||
append_param "${password:+password=${password}}"
|
||||
append_param "${pincode:+pin=${pincode}}"
|
||||
|
||||
mmcli --modem="${device}" --timeout 120 --simple-connect="${connectargs}" || {
|
||||
proto_notify_error "${interface}" MM_CONNECT_FAILED
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
|
||||
# check if Signal refresh rate is set
|
||||
if [ -n "${signalrate}" ] && [ "${signalrate}" -eq "${signalrate}" ] 2>/dev/null; then
|
||||
echo "setting signal refresh rate to ${signalrate} seconds"
|
||||
mmcli --modem="${device}" --signal-setup="${signalrate}"
|
||||
else
|
||||
echo "signal refresh rate is not set"
|
||||
fi
|
||||
|
||||
# log additional useful information
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
operatorname=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-name")
|
||||
[ -n "${operatorname}" ] && echo "network operator name: ${operatorname}"
|
||||
operatorid=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-code")
|
||||
[ -n "${operatorid}" ] && echo "network operator MCCMNC: ${operatorid}"
|
||||
registration=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.registration-state")
|
||||
[ -n "${registration}" ] && echo "registration type: ${registration}"
|
||||
accesstech=$(modemmanager_get_multivalue_field "${modemstatus}" "modem.generic.access-technologies")
|
||||
[ -n "${accesstech}" ] && echo "access technology: ${accesstech}"
|
||||
signalquality=$(modemmanager_get_field "${modemstatus}" "modem.generic.signal-quality.value")
|
||||
[ -n "${signalquality}" ] && echo "signal quality: ${signalquality}%"
|
||||
|
||||
# we won't like it if there are more than one bearers, as that would mean the
|
||||
# user manually created them, and that's unsupported by this proto
|
||||
bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
|
||||
[ -n "${bearercount}" ] && [ "$bearercount" -eq 1 ] || {
|
||||
proto_notify_error "${interface}" INVALID_BEARER_LIST
|
||||
return 1
|
||||
}
|
||||
|
||||
# load connected bearer information
|
||||
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
|
||||
bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
|
||||
|
||||
# load network interface and method information
|
||||
beareriface=$(modemmanager_get_field "${bearerstatus}" "bearer.status.interface")
|
||||
bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
|
||||
bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
|
||||
|
||||
# setup IPv4
|
||||
[ -n "${bearermethod_ipv4}" ] && {
|
||||
echo "IPv4 connection setup required in interface ${interface}: ${bearermethod_ipv4}"
|
||||
case "${bearermethod_ipv4}" in
|
||||
"dhcp")
|
||||
modemmanager_connected_method_dhcp_ipv4 "${interface}" "${beareriface}" "${metric}"
|
||||
;;
|
||||
"static")
|
||||
address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.address")
|
||||
prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.prefix")
|
||||
gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.gateway")
|
||||
mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.mtu")
|
||||
dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[1\]")
|
||||
dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[2\]")
|
||||
modemmanager_connected_method_static_ipv4 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
|
||||
;;
|
||||
"ppp")
|
||||
modemmanager_connected_method_ppp_ipv4 "${interface}" "${beareriface}" "${username}" "${password}" "${allowedauth}"
|
||||
;;
|
||||
*)
|
||||
proto_notify_error "${interface}" UNKNOWN_METHOD
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# setup IPv6
|
||||
# note: if using ipv4v6, both IPv4 and IPv6 settings will have the same MTU and metric values reported
|
||||
[ -n "${bearermethod_ipv6}" ] && {
|
||||
echo "IPv6 connection setup required in interface ${interface}: ${bearermethod_ipv6}"
|
||||
case "${bearermethod_ipv6}" in
|
||||
"dhcp")
|
||||
modemmanager_connected_method_dhcp_ipv6 "${interface}" "${beareriface}" "${metric}"
|
||||
;;
|
||||
"static")
|
||||
address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.address")
|
||||
prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.prefix")
|
||||
gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.gateway")
|
||||
mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.mtu")
|
||||
dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[1\]")
|
||||
dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[2\]")
|
||||
modemmanager_connected_method_static_ipv6 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
|
||||
;;
|
||||
"ppp")
|
||||
proto_notify_error "${interface}" "unsupported method"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
proto_notify_error "${interface}" UNKNOWN_METHOD
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
proto_modemmanager_teardown() {
|
||||
local interface="$1"
|
||||
|
||||
local modemstatus bearerpath errorstring
|
||||
local bearermethod_ipv4 bearermethod_ipv6
|
||||
|
||||
local device lowpower iptype
|
||||
json_get_vars device lowpower iptype
|
||||
|
||||
echo "stopping network"
|
||||
|
||||
# load connected bearer information, just the first one should be ok
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
|
||||
[ -n "${bearerpath}" ] || {
|
||||
echo "couldn't load bearer path: disconnecting anyway"
|
||||
mmcli --modem="${device}" --simple-disconnect >/dev/null 2>&1
|
||||
return
|
||||
}
|
||||
|
||||
# load bearer connection methods
|
||||
bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
|
||||
bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
|
||||
[ -n "${bearermethod_ipv4}" ] &&
|
||||
echo "IPv4 connection teardown required in interface ${interface}: ${bearermethod_ipv4}"
|
||||
bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
|
||||
[ -n "${bearermethod_ipv6}" ] &&
|
||||
echo "IPv6 connection teardown required in interface ${interface}: ${bearermethod_ipv6}"
|
||||
|
||||
# disconnection handling only requires special treatment in IPv4/PPP
|
||||
[ "${bearermethod_ipv4}" = "ppp" ] && modemmanager_disconnected_method_ppp_ipv4 "${interface}"
|
||||
modemmanager_disconnected_method_common "${interface}"
|
||||
|
||||
# disconnect
|
||||
mmcli --modem="${device}" --simple-disconnect ||
|
||||
proto_notify_error "${interface}" DISCONNECT_FAILED
|
||||
|
||||
# disable
|
||||
mmcli --modem="${device}" --disable
|
||||
|
||||
# low power, only if requested
|
||||
[ "${lowpower:-0}" -lt 1 ] ||
|
||||
mmcli --modem="${device}" --set-power-state-low
|
||||
|
||||
proto_init_update "*" 0
|
||||
proto_send_update "$interface"
|
||||
}
|
||||
|
||||
[ -n "$INCLUDE_ONLY" ] || {
|
||||
add_protocol modemmanager
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
|
||||
trap_with_arg() {
|
||||
func="$1" ; shift
|
||||
for sig ; do
|
||||
# shellcheck disable=SC2064
|
||||
trap "$func $sig" "$sig"
|
||||
done
|
||||
}
|
||||
|
||||
func_trap() {
|
||||
logger "ModemManager-wrapper[$$]" "Sending signal ${1}..."
|
||||
kill "-${1}" "$CHILD" 2>/dev/null
|
||||
}
|
||||
|
||||
main() {
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
trap_with_arg func_trap INT TERM KILL
|
||||
|
||||
mkdir -p "${MODEMMANAGER_RUNDIR}"
|
||||
chmod 0755 "${MODEMMANAGER_RUNDIR}"
|
||||
mm_cleanup_interfaces
|
||||
|
||||
/usr/sbin/ModemManager "$@" 1>/dev/null 2>/dev/null &
|
||||
CHILD="$!"
|
||||
|
||||
mm_report_events_from_cache
|
||||
|
||||
wait "$CHILD"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
42
olg-ucentral-client-sdk/feeds/ucentral/poe/Makefile
Normal file
42
olg-ucentral-client-sdk/feeds/ucentral/poe/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=poe
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
ifeq ($(CONFIG_TARGET_ipq50xx_generic_DEVICE_sonicfi_rap630w_311g),y)
|
||||
TARGET_CFLAGS += -DPLATFORM_EWW631_B1=1
|
||||
endif
|
||||
|
||||
define Package/poe
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Turn on/off PoE ports with TSP23861 chipset
|
||||
DEPENDS:= +libubox +libubus +libuci +libi2c
|
||||
endef
|
||||
|
||||
define Package/poe/description
|
||||
Turn on/off PoE ports
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
|
||||
define Package/poe/install
|
||||
$(INSTALL_DIR) $(1)
|
||||
$(INSTALL_DIR) $(1)/etc/config $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/poe.init $(1)/etc/init.d/poe
|
||||
$(INSTALL_BIN) ./files/poe.config $(1)/etc/config/poe
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/tps23861-poe-ctrl $(1)/usr/bin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,poe))
|
||||
33
olg-ucentral-client-sdk/feeds/ucentral/poe/files/poe.init
Normal file
33
olg-ucentral-client-sdk/feeds/ucentral/poe/files/poe.init
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=10
|
||||
|
||||
tps23861_poe_ctrl () {
|
||||
local section="$1"
|
||||
local num mode
|
||||
config_get num "$section" port_num
|
||||
config_get mode "$section" admin_mode
|
||||
|
||||
if [ "$mode" == "1" ]; then
|
||||
output=$(tps23861-poe-ctrl -p "${num}" -P on)
|
||||
echo "<6>${output}" > "/dev/kmsg"
|
||||
else
|
||||
output=$(tps23861-poe-ctrl -p "${num}" -P off)
|
||||
echo "<6>${output}" > "/dev/kmsg"
|
||||
fi
|
||||
}
|
||||
|
||||
start(){
|
||||
. /lib/functions.sh
|
||||
|
||||
board=$(board_name)
|
||||
case $board in
|
||||
sonicfi,rap630w-311g|\
|
||||
cybertan,eww631-b1)
|
||||
config_load poe
|
||||
config_foreach tps23861_poe_ctrl port
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
26
olg-ucentral-client-sdk/feeds/ucentral/poe/src/Makefile
Normal file
26
olg-ucentral-client-sdk/feeds/ucentral/poe/src/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
CFLAGS += -Wall -g
|
||||
|
||||
INCLUDES =
|
||||
|
||||
LDFLAGS = -lubus -lubox -li2c
|
||||
|
||||
LIBS =
|
||||
|
||||
SRCS = tps23861-poe-ctrl.c \
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
MAIN = tps23861-poe-ctrl
|
||||
|
||||
|
||||
all: $(MAIN)
|
||||
|
||||
$(MAIN): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LDFLAGS) $(LIBS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) *.o *~ $(MAIN) $(TEST)
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* User-space daemon formonitoring and managing PoE ports with
|
||||
* TI TPS23861 chips. based on the Linux Kernel TPS23861
|
||||
* HWMON driver.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* Standard input/output definitions */
|
||||
#include <string.h> /* String function definitions */
|
||||
#include <unistd.h> /* UNIX standard function definitions */
|
||||
#include <fcntl.h> /* File control definitions */
|
||||
#include <errno.h> /* Error number definitions */
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h> /* uapi/linux/i2c-dev.h */
|
||||
|
||||
#include <libubox/ulog.h>
|
||||
|
||||
#define TPS23861_I2C_ADDR 0x20
|
||||
#define DETECT_CLASS_RESTART 0x18
|
||||
#define POWER_ENABLE 0x19
|
||||
#define POWER_ON_SHIFT 0
|
||||
#define POWER_OFF_SHIFT 4
|
||||
|
||||
typedef unsigned char u8;
|
||||
|
||||
#if defined(PLATFORM_EWW631_B1)
|
||||
#define TPS23861_NUM_PORTS 1
|
||||
#endif
|
||||
|
||||
#define CONVERT_PORT_NUM(x) (1 << ((u8)x-1))
|
||||
|
||||
unsigned int PORT_POWER_STATUS[TPS23861_NUM_PORTS];
|
||||
|
||||
int i2c_handler = -1;
|
||||
#define ULOG_DBG(fmt, ...) ulog(LOG_DEBUG, fmt, ## __VA_ARGS__)
|
||||
|
||||
int open_device(void)
|
||||
{
|
||||
int fd, fset;
|
||||
|
||||
fd = open("/dev/i2c-0", O_RDWR);
|
||||
fset = fcntl(fd, F_SETFL, 0);
|
||||
if (fset < 0)
|
||||
printf("fcntl failed!\n");
|
||||
|
||||
//if (isatty(STDIN_FILENO) == 0)
|
||||
// printf("standard input is not a terminal device\n");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int access_salve(int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if((ret = ioctl(fd, I2C_SLAVE, TPS23861_I2C_ADDR)) < 0)
|
||||
{
|
||||
printf("%s: Failed to access slave bus[%s]\n",__func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
// Write to an I2C slave device's register:
|
||||
int i2c_write(u8 slave_addr, u8 reg, u8 data)
|
||||
{
|
||||
u8 outbuf[2];
|
||||
|
||||
struct i2c_msg msgs[1];
|
||||
struct i2c_rdwr_ioctl_data msgset[1];
|
||||
|
||||
outbuf[0] = reg;
|
||||
outbuf[1] = data;
|
||||
|
||||
msgs[0].addr = slave_addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 2;
|
||||
msgs[0].buf = outbuf;
|
||||
|
||||
msgset[0].msgs = msgs;
|
||||
msgset[0].nmsgs = 1;
|
||||
|
||||
if (ioctl(i2c_handler, I2C_RDWR, &msgset) < 0) {
|
||||
perror("ioctl(I2C_RDWR) in i2c_write");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void poe_set_PowerOnOff(u8 port, u8 on_off) {
|
||||
u8 value;
|
||||
u8 portBit;
|
||||
portBit = CONVERT_PORT_NUM(port+1);
|
||||
|
||||
if(on_off == 0) {
|
||||
value = (portBit << POWER_OFF_SHIFT);
|
||||
PORT_POWER_STATUS[port] = 0;
|
||||
} else {
|
||||
value = (portBit << POWER_ON_SHIFT);
|
||||
PORT_POWER_STATUS[port] = 1;
|
||||
}
|
||||
|
||||
ULOG_DBG("set Port%d Power Status [%d] portBit 0x[%x] value 0x[%x]\n", port+1, PORT_POWER_STATUS[port], portBit, value);
|
||||
|
||||
if(i2c_write(TPS23861_I2C_ADDR, POWER_ENABLE, value) < 0)
|
||||
{
|
||||
ULOG_ERR("Set port%d power on-off error (0x19)\n", port);
|
||||
}
|
||||
}
|
||||
|
||||
void RestartPortDetectClass(u8 port)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
value = (1 << port) | (1 << (port + 4));
|
||||
ULOG_DBG("RestartPortDetectClass value 0x%x\n", value);
|
||||
if(i2c_write(TPS23861_I2C_ADDR, DETECT_CLASS_RESTART, value) < 0) {
|
||||
ULOG_ERR("Set port%d detection and class on error\n",port);
|
||||
}
|
||||
}
|
||||
|
||||
int usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s -p <1-3> -P <on|off> [options]\n"
|
||||
"Required options:\n"
|
||||
" -p <1-3>: Select port number (Only port 1 is supported)\n"
|
||||
" -P <on|off>: Set PSE function state <on|off>\n"
|
||||
"Optional options:\n"
|
||||
" -d Enable debug mode\n"
|
||||
"\n", progname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setPSE(int port ,char *optarg)
|
||||
{
|
||||
int ret = 0;
|
||||
i2c_handler = open_device();
|
||||
if (i2c_handler < 0) {
|
||||
ULOG_ERR("open i2c-0 device error!\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ret = access_salve(i2c_handler);
|
||||
if (ret < 0)
|
||||
{
|
||||
ULOG_ERR("The i2c-0 access error\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if(!strncmp("on", optarg, 2)) {
|
||||
printf("Enable port%d PSE function\n", port);
|
||||
RestartPortDetectClass(port-1);
|
||||
}
|
||||
else if (!strncmp("off", optarg, 3)) {
|
||||
printf("Disable port%d PSE function\n", port);
|
||||
poe_set_PowerOnOff(port-1, 0);
|
||||
}
|
||||
else {
|
||||
ULOG_ERR("[Set] Do not accept this optarg!!!\n");
|
||||
ret = 1;
|
||||
}
|
||||
EXIT:
|
||||
close(i2c_handler);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ch, ret = 0, port = 0;
|
||||
char *PSE = NULL;
|
||||
if (argc == 1) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
|
||||
ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "tps23861");
|
||||
ulog_threshold(LOG_INFO);
|
||||
|
||||
while ((ch = getopt(argc, argv, "dp:P:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
printf("tps23861-i2c-control ulog_threshold set to debug level\n");
|
||||
ulog_threshold(LOG_DEBUG);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
PSE = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = usage(argv[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port < 1 || port > 3) {
|
||||
ret = usage(argv[0]);
|
||||
}
|
||||
else {
|
||||
if (PSE) {
|
||||
setPSE(port, PSE);
|
||||
}
|
||||
else {
|
||||
ret = usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
76
olg-ucentral-client-sdk/feeds/ucentral/qosify/Makefile
Normal file
76
olg-ucentral-client-sdk/feeds/ucentral/qosify/Makefile
Normal file
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (C) 2021 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=qosify
|
||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/qosify.git
|
||||
PKG_MIRROR_HASH:=648e648655097d7063b3a2119b054cf9ee4a120a194cdf5fb5df8e03c9207c83
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_DATE:=2023-07-20
|
||||
PKG_SOURCE_VERSION:=850cc271083d0ad4c3b2eefddb61f376390ddf62
|
||||
PKG_RELEASE:=$(AUTORELEASE)
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
PKG_BUILD_DEPENDS:=bpf-headers
|
||||
PKG_FLAGS:=nonshared
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
include $(INCLUDE_DIR)/bpf.mk
|
||||
include $(INCLUDE_DIR)/nls.mk
|
||||
|
||||
define Package/qosify
|
||||
SECTION:=utils
|
||||
CATEGORY:=Base system
|
||||
TITLE:=A simple QoS solution based eBPF + CAKE
|
||||
DEPENDS:=+libbpf +libubox +libubus +libnl-tiny +kmod-sched-cake +kmod-sched-bpf +kmod-ifb +tc $(BPF_DEPENDS)
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
-Wno-error=deprecated-declarations \
|
||||
-I$(STAGING_DIR)/usr/include/libnl-tiny \
|
||||
-I$(STAGING_DIR)/usr/include
|
||||
|
||||
CMAKE_OPTIONS += \
|
||||
-DLIBNL_LIBS=-lnl-tiny
|
||||
|
||||
define Build/Compile
|
||||
$(call CompileBPF,$(PKG_BUILD_DIR)/qosify-bpf.c)
|
||||
$(Build/Compile/Default)
|
||||
endef
|
||||
|
||||
define Package/qosify/conffiles
|
||||
/etc/config/qosify
|
||||
/etc/qosify/00-defaults.conf
|
||||
endef
|
||||
|
||||
define Package/qosify/install
|
||||
$(INSTALL_DIR) \
|
||||
$(1)/lib/bpf \
|
||||
$(1)/usr/sbin \
|
||||
$(1)/etc/init.d \
|
||||
$(1)/etc/config \
|
||||
$(1)/etc/qosify \
|
||||
$(1)/etc/hotplug.d/net \
|
||||
$(1)/etc/hotplug.d/iface
|
||||
$(INSTALL_DATA) $(PKG_BUILD_DIR)/qosify-bpf.o $(1)/lib/bpf
|
||||
$(INSTALL_BIN) \
|
||||
$(PKG_INSTALL_DIR)/usr/bin/qosify \
|
||||
./files/qosify-status \
|
||||
$(1)/usr/sbin/
|
||||
$(INSTALL_BIN) ./files/qosify.init $(1)/etc/init.d/qosify
|
||||
$(INSTALL_DATA) ./files/qosify-defaults.conf $(1)/etc/qosify/00-defaults.conf
|
||||
$(INSTALL_DATA) ./files/qosify.conf $(1)/etc/config/qosify
|
||||
$(INSTALL_DATA) ./files/qosify.hotplug $(1)/etc/hotplug.d/net/10-qosify
|
||||
$(INSTALL_DATA) ./files/qosify.hotplug $(1)/etc/hotplug.d/iface/10-qosify
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,qosify))
|
||||
@@ -0,0 +1,17 @@
|
||||
# DNS
|
||||
tcp:53 voice
|
||||
tcp:5353 voice
|
||||
udp:53 voice
|
||||
udp:5353 voice
|
||||
|
||||
# NTP
|
||||
udp:123 voice
|
||||
|
||||
# SSH
|
||||
tcp:22 +video
|
||||
|
||||
# HTTP/QUIC
|
||||
tcp:80 +besteffort
|
||||
tcp:443 +besteffort
|
||||
udp:80 +besteffort
|
||||
udp:443 +besteffort
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/bin/sh
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
dev_status() {
|
||||
tc -s qdisc sh dev "$1" root
|
||||
echo
|
||||
}
|
||||
|
||||
common_status() {
|
||||
json_get_vars ifname ingress egress
|
||||
|
||||
[ -n "$ifname" ] || return
|
||||
|
||||
[ "$egress" -gt 0 ] && {
|
||||
echo "egress status:"
|
||||
dev_status "$ifname"
|
||||
}
|
||||
[ "$ingress" -gt 0 ] && {
|
||||
echo "ingress status:"
|
||||
dev_status "$(printf %.16s "ifb-$ifname")"
|
||||
}
|
||||
}
|
||||
|
||||
is_active() {
|
||||
json_get_vars active
|
||||
|
||||
[ "${active:-0}" -gt 0 ]
|
||||
}
|
||||
|
||||
device_status() {
|
||||
local name="$2"
|
||||
|
||||
json_select "$name"
|
||||
|
||||
if is_active; then
|
||||
status="active"
|
||||
else
|
||||
status="not found"
|
||||
fi
|
||||
|
||||
echo "===== device $name: $status ====="
|
||||
|
||||
is_active && common_status
|
||||
|
||||
json_select ..
|
||||
}
|
||||
|
||||
interface_status() {
|
||||
local name="$2"
|
||||
|
||||
json_select "$name"
|
||||
|
||||
if is_active; then
|
||||
status="active"
|
||||
elif ubus -S -t 0 wait_for "network.interface.$name"; then
|
||||
status="down"
|
||||
else
|
||||
status="not found"
|
||||
fi
|
||||
|
||||
echo "===== interface $name: $status ====="
|
||||
|
||||
is_active && common_status
|
||||
|
||||
json_select ..
|
||||
}
|
||||
|
||||
json_load "$(ubus call qosify status)"
|
||||
json_for_each_item device_status devices
|
||||
json_for_each_item interface_status interfaces
|
||||
@@ -0,0 +1,6 @@
|
||||
config defaults
|
||||
list defaults /etc/qosify/*.conf
|
||||
option dscp_prio AF41
|
||||
option dscp_icmp +CS0
|
||||
option dscp_default_udp CS0
|
||||
option prio_max_avg_pkt_len 500
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ubus call qosify check_devices
|
||||
171
olg-ucentral-client-sdk/feeds/ucentral/qosify/files/qosify.init
Normal file
171
olg-ucentral-client-sdk/feeds/ucentral/qosify/files/qosify.init
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (c) 2021 OpenWrt.org
|
||||
|
||||
START=19
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/qosify
|
||||
|
||||
add_option() {
|
||||
local type="$1"
|
||||
local name="$2"
|
||||
|
||||
config_get val "$cfg" "$name"
|
||||
|
||||
[ -n "$val" ] && json_add_$type "$name" "$val"
|
||||
}
|
||||
|
||||
add_flow_config() {
|
||||
local cfg="$1"
|
||||
|
||||
add_option string dscp_prio
|
||||
add_option string dscp_bulk
|
||||
add_option int bulk_trigger_timeout
|
||||
add_option int bulk_trigger_pps
|
||||
add_option int prio_max_avg_pkt_len
|
||||
}
|
||||
|
||||
add_defaults() {
|
||||
cfg="$1"
|
||||
|
||||
json_add_boolean reset 1
|
||||
|
||||
config_get files "$cfg" defaults
|
||||
json_add_array files
|
||||
for i in $files; do
|
||||
json_add_string "" "$i"
|
||||
done
|
||||
json_close_array
|
||||
|
||||
add_flow_config "$cfg"
|
||||
add_option int timeout
|
||||
add_option string dscp_icmp
|
||||
add_option string dscp_default_udp
|
||||
add_option string dscp_default_tcp
|
||||
}
|
||||
|
||||
add_interface() {
|
||||
local cfg="$1"
|
||||
|
||||
config_get_bool disabled "$cfg" disabled 0
|
||||
[ "$disabled" -gt 0 ] && return
|
||||
|
||||
config_get name "$cfg" name
|
||||
json_add_object "$name"
|
||||
|
||||
config_get bw "$cfg" bandwidth
|
||||
|
||||
config_get bw_up "$cfg" bandwidth_up
|
||||
bw_up="${bw_up:-$bw}"
|
||||
[ -n "$bw_up" ] && json_add_string bandwidth_up "$bw_up"
|
||||
|
||||
config_get bw_down "$cfg" bandwidth_down
|
||||
bw_down="${bw_down:-$bw}"
|
||||
[ -n "$bw_down" ] && json_add_string bandwidth_down "$bw_down"
|
||||
|
||||
add_option string bandwidth
|
||||
add_option boolean ingress
|
||||
add_option boolean egress
|
||||
add_option string mode
|
||||
add_option boolean nat
|
||||
add_option boolean host_isolate
|
||||
add_option boolean autorate_ingress
|
||||
add_option string ingress_options
|
||||
add_option string egress_options
|
||||
|
||||
config_get user_options "$cfg" options
|
||||
|
||||
config_get otype "$cfg" overhead_type
|
||||
options=
|
||||
case "$otype" in
|
||||
none);;
|
||||
manual)
|
||||
config_get overhead "$cfg" overhead
|
||||
[ -n "$overhead" ] && append options "overhead $overhead"
|
||||
|
||||
config_get encap "$cfg" overhead_encap
|
||||
[ -n "$encap" ] && append options "$encap"
|
||||
;;
|
||||
conservative|\
|
||||
pppoa-vcmux|\
|
||||
pppoa-llc|\
|
||||
pppoe-vcmux|\
|
||||
pppoe-llcsnap|\
|
||||
bridged-vcmux|\
|
||||
bridged-llcsnap|\
|
||||
ipoa-vcmux|\
|
||||
ipoa-llcsnap|\
|
||||
pppoe-ptm|\
|
||||
bridged-ptm|\
|
||||
docsis|\
|
||||
ethernet)
|
||||
append options "$otype"
|
||||
;;
|
||||
esac
|
||||
|
||||
config_get mpu "$cfg" overhead_mpu
|
||||
[ -n "$mpu" ] && append options "mpu $mpu"
|
||||
|
||||
config_get ovlan "$cfg" overhead_vlan
|
||||
[ "${ovlan:-0}" -ge 2 ] && append options "ether-vlan"
|
||||
[ "${ovlan:-0}" -ge 1 ] && append options "ether-vlan"
|
||||
|
||||
[ -n "$user_options" ] && append options "$user_options"
|
||||
[ -n "$options" ] && json_add_string options "$options"
|
||||
|
||||
json_close_object
|
||||
}
|
||||
|
||||
add_class() {
|
||||
local cfg="$1"
|
||||
|
||||
config_get value "$cfg" value
|
||||
config_get ingress "$cfg" ingress
|
||||
config_get egress "$cfg" egress
|
||||
|
||||
json_add_object "$cfg"
|
||||
json_add_string ingress "${ingress:-$value}"
|
||||
json_add_string egress "${egress:-$value}"
|
||||
add_flow_config "$cfg"
|
||||
json_close_object
|
||||
}
|
||||
|
||||
|
||||
reload_service() {
|
||||
json_init
|
||||
|
||||
config_load qosify
|
||||
|
||||
config_foreach add_defaults defaults
|
||||
|
||||
json_add_object interfaces
|
||||
config_foreach add_interface interface
|
||||
json_close_object
|
||||
|
||||
json_add_object classes
|
||||
config_foreach add_class class
|
||||
config_foreach add_class alias
|
||||
json_close_object
|
||||
|
||||
json_add_object devices
|
||||
config_foreach add_interface device
|
||||
json_close_object
|
||||
|
||||
ubus call qosify config "$(json_dump)"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger qosify
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 10 wait_for qosify
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=radius-gw-proxy
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
define Package/radius-gw-proxy
|
||||
SECTION:=ucentral
|
||||
CATEGORY:=uCentral
|
||||
TITLE:=uCentral Gateway radius-gw-proxy
|
||||
DEPENDS:=+libubox +libubus
|
||||
endef
|
||||
|
||||
define Package/radius-gw-proxy/install
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/radius-gw-proxy $(1)/usr/sbin/
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,radius-gw-proxy))
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=99
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "/usr/sbin/radius-gw-proxy"
|
||||
procd_close_instance
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
PROJECT(radius-gw-proxy C)
|
||||
|
||||
ADD_DEFINITIONS(-Wall -Werror)
|
||||
IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6)
|
||||
ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration)
|
||||
ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral)
|
||||
ENDIF()
|
||||
ADD_DEFINITIONS(-Os -std=gnu99 -g3 -Wmissing-declarations -Wno-unused-parameter -Wno-strict-aliasing)
|
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
|
||||
SET(SOURCES main.c ubus.c)
|
||||
|
||||
FIND_LIBRARY(ubus NAMES ubus)
|
||||
FIND_LIBRARY(ubox NAMES ubox)
|
||||
|
||||
FIND_PATH(ubox_include_dir libubox/uloop.h)
|
||||
FIND_PATH(ubus_include_dir NAMES libubus.h)
|
||||
|
||||
INCLUDE_DIRECTORIES(${ubox_include_dir} ${ubus_include_dir})
|
||||
|
||||
ADD_EXECUTABLE(radius-gw-proxy ${SOURCES})
|
||||
|
||||
TARGET_LINK_LIBRARIES(radius-gw-proxy ${ubox} ${ubus})
|
||||
|
||||
INSTALL(TARGETS radius-gw-proxy
|
||||
RUNTIME DESTINATION sbin
|
||||
)
|
||||
@@ -0,0 +1,369 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubox/usock.h>
|
||||
#include <libubox/ulog.h>
|
||||
|
||||
#include <libubus.h>
|
||||
|
||||
#include "ubus.h"
|
||||
|
||||
#define RAD_PROX_BUFLEN (4 * 1024)
|
||||
|
||||
#define TLV_NAS_IP 4
|
||||
#define TLV_PROXY_STATE 33
|
||||
|
||||
struct radius_socket {
|
||||
struct uloop_fd fd;
|
||||
enum socket_type type;
|
||||
};
|
||||
|
||||
struct radius_header {
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
char auth[16];
|
||||
char avp[];
|
||||
};
|
||||
|
||||
struct radius_tlv {
|
||||
uint8_t id;
|
||||
uint8_t len;
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct radius_proxy_state_key {
|
||||
char id[256];
|
||||
enum socket_type type;
|
||||
};
|
||||
|
||||
struct radius_proxy_state {
|
||||
struct avl_node avl;
|
||||
struct radius_proxy_state_key key;
|
||||
int port;
|
||||
};
|
||||
|
||||
static struct radius_socket *sock_auth;
|
||||
static struct radius_socket *sock_acct;
|
||||
static struct radius_socket *sock_dae;
|
||||
|
||||
static int
|
||||
avl_memcmp(const void *k1, const void *k2, void *ptr)
|
||||
{
|
||||
return memcmp(k1, k2, sizeof(struct radius_proxy_state_key));
|
||||
}
|
||||
|
||||
static AVL_TREE(radius_proxy_states, avl_memcmp, false, NULL);
|
||||
static struct blob_buf b;
|
||||
|
||||
static void
|
||||
radius_proxy_state_add(char *id, int port, enum socket_type type)
|
||||
{
|
||||
struct radius_proxy_state *station;
|
||||
struct radius_proxy_state_key key = { .type = type };
|
||||
|
||||
strcpy(key.id, id);
|
||||
station = avl_find_element(&radius_proxy_states, &key, station, avl);
|
||||
|
||||
if (!station) {
|
||||
ULOG_INFO("new station/port, adding to avl tree\n");
|
||||
station = malloc(sizeof(*station));
|
||||
memset(station, 0, sizeof(*station));
|
||||
strcpy(station->key.id, id);
|
||||
station->key.type = type;
|
||||
station->avl.key = &station->key;
|
||||
avl_insert(&radius_proxy_states, &station->avl);
|
||||
}
|
||||
station->port = port;
|
||||
}
|
||||
|
||||
static char *
|
||||
b64enc(char *src, int len)
|
||||
{
|
||||
char *dst;
|
||||
int ret;
|
||||
|
||||
if (!src)
|
||||
return NULL;
|
||||
dst = malloc(len * 4);
|
||||
ret = b64_encode(src, len, dst, len * 4);
|
||||
if (ret < 1) {
|
||||
free(dst);
|
||||
return NULL;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static char *
|
||||
b64dec(char *src, int *ret)
|
||||
{
|
||||
int len = strlen(src);
|
||||
char *dst = malloc(len);
|
||||
*ret = b64_decode(src, dst, len);
|
||||
if (*ret < 0)
|
||||
return NULL;
|
||||
return dst;
|
||||
}
|
||||
|
||||
static void
|
||||
radius_forward_gw(char *buf, enum socket_type type)
|
||||
{
|
||||
struct radius_header *hdr = (struct radius_header *) buf;
|
||||
struct ubus_request async = { };
|
||||
char *data = b64enc(buf, ntohs(hdr->len));
|
||||
|
||||
if (!data || !ucentral)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
switch (type) {
|
||||
case RADIUS_AUTH:
|
||||
blobmsg_add_string(&b, "radius", "auth");
|
||||
break;
|
||||
case RADIUS_ACCT:
|
||||
blobmsg_add_string(&b, "radius", "acct");
|
||||
break;
|
||||
case RADIUS_DAS:
|
||||
blobmsg_add_string(&b, "radius", "coa");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
blobmsg_add_string(&b, "data", data);
|
||||
|
||||
ubus_invoke_async(&conn.ctx, ucentral, "radius", b.head, &async);
|
||||
ubus_abort_request(&conn.ctx, &async);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
static int
|
||||
radius_parse(char *buf, unsigned int len, int port, enum socket_type type, int tx)
|
||||
{
|
||||
struct radius_header *hdr = (struct radius_header *) buf;
|
||||
struct radius_tlv *proxy_state = NULL;
|
||||
char proxy_state_str[256] = {};
|
||||
void *avp = hdr->avp;
|
||||
unsigned int len_orig;
|
||||
uint8_t localhost[] = { 0x7f, 0, 0, 1 };
|
||||
|
||||
if (len < sizeof(*hdr)) {
|
||||
ULOG_ERR("invalid packet length, %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
len_orig = ntohs(hdr->len);
|
||||
|
||||
if (len_orig != len) {
|
||||
ULOG_ERR("invalid header length, %d %d\n", len_orig, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("\tcode:%d, id:%d, len:%d\n", hdr->code, hdr->id, len_orig);
|
||||
|
||||
len -= sizeof(*hdr);
|
||||
|
||||
while (len >= sizeof(struct radius_tlv)) {
|
||||
struct radius_tlv *tlv = (struct radius_tlv *)avp;
|
||||
|
||||
if (len < tlv->len || tlv->len < sizeof(*tlv)) {
|
||||
ULOG_ERR("invalid TLV length\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (tlv->id == TLV_PROXY_STATE)
|
||||
proxy_state = tlv;
|
||||
|
||||
if (type == RADIUS_DAS && tlv->id == TLV_NAS_IP && tlv->len == 6)
|
||||
memcpy(tlv->data, &localhost, 4);
|
||||
|
||||
printf("\tID:%d, len:%d\n", tlv->id, tlv->len);
|
||||
avp += tlv->len;
|
||||
len -= tlv->len;
|
||||
}
|
||||
|
||||
if (type == RADIUS_DAS) {
|
||||
if (tx) {
|
||||
radius_forward_gw(buf, type);
|
||||
} else {
|
||||
struct sockaddr_in dest;
|
||||
|
||||
memset(&dest, 0, sizeof(dest));
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = htons(3799);
|
||||
inet_pton(AF_INET, "127.0.0.1", &(dest.sin_addr.s_addr));
|
||||
|
||||
if (sendto(sock_dae->fd.fd, buf, len_orig,
|
||||
MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest)) < 0)
|
||||
ULOG_ERR("failed to deliver DAS frame to localhost\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!proxy_state) {
|
||||
ULOG_ERR("no proxy_state found\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(proxy_state_str, proxy_state->data, proxy_state->len - 2);
|
||||
printf("\tfowarding to %s, prox_state:%s\n", tx ? "gateway" : "hostapd", proxy_state_str);
|
||||
if (tx) {
|
||||
radius_proxy_state_add(proxy_state_str, port, type);
|
||||
radius_forward_gw(buf, type);
|
||||
} else {
|
||||
struct radius_proxy_state *proxy = avl_find_element(&radius_proxy_states, proxy_state, proxy, avl);
|
||||
struct radius_proxy_state_key key = {};
|
||||
struct sockaddr_in dest;
|
||||
struct radius_socket *sock;
|
||||
|
||||
switch(type) {
|
||||
case RADIUS_AUTH:
|
||||
sock = sock_auth;
|
||||
break;
|
||||
|
||||
case RADIUS_ACCT:
|
||||
sock = sock_acct;
|
||||
break;
|
||||
default:
|
||||
ULOG_ERR("bad socket type\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(key.id, proxy_state_str);
|
||||
key.type = type;
|
||||
proxy = avl_find_element(&radius_proxy_states, &key, proxy, avl);
|
||||
|
||||
if (!proxy) {
|
||||
ULOG_ERR("unknown proxy_state, dropping frame\n");
|
||||
return -1;
|
||||
}
|
||||
memset(&dest, 0, sizeof(dest));
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = proxy->port;
|
||||
inet_pton(AF_INET, "127.0.0.1", &(dest.sin_addr.s_addr));
|
||||
|
||||
if (sendto(sock->fd.fd, buf, len_orig,
|
||||
MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest)) < 0)
|
||||
ULOG_ERR("failed to deliver frame to localhost\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gateway_recv(char *data, enum socket_type type)
|
||||
{
|
||||
int len = 0;
|
||||
char *frame;
|
||||
|
||||
frame = b64dec(data, &len);
|
||||
|
||||
if (!frame) {
|
||||
ULOG_ERR("failed to b64_decode frame\n");
|
||||
return;
|
||||
}
|
||||
|
||||
radius_parse(frame, len, 0, type, 0);
|
||||
free(frame);
|
||||
}
|
||||
|
||||
static void
|
||||
sock_recv(struct uloop_fd *u, unsigned int events)
|
||||
{
|
||||
static char buf[RAD_PROX_BUFLEN];
|
||||
static char cmsg_buf[( CMSG_SPACE(sizeof(struct in_pktinfo)) + sizeof(int)) + 1];
|
||||
static struct sockaddr_in sin;
|
||||
char addr_str[INET_ADDRSTRLEN];
|
||||
static struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = sizeof(buf)
|
||||
};
|
||||
static struct msghdr msg = {
|
||||
.msg_name = &sin,
|
||||
.msg_namelen = sizeof(sin),
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_control = cmsg_buf,
|
||||
.msg_controllen = sizeof(cmsg_buf),
|
||||
};
|
||||
struct radius_socket *sock = container_of(u, struct radius_socket, fd);
|
||||
int len;
|
||||
|
||||
do {
|
||||
len = recvmsg(u->fd, &msg, 0);
|
||||
if (len < 0) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
return;
|
||||
case EINTR:
|
||||
continue;
|
||||
default:
|
||||
perror("recvmsg");
|
||||
uloop_fd_delete(u);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inet_ntop(AF_INET, &sin.sin_addr, addr_str, sizeof(addr_str));
|
||||
printf("RX: src:%s:%d, len=%d\n", addr_str, sin.sin_port, len);
|
||||
radius_parse(buf, (unsigned int)len, sin.sin_port, sock->type, 1);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static struct radius_socket *
|
||||
sock_open(char *port, enum socket_type type)
|
||||
{
|
||||
struct radius_socket *sock = malloc(sizeof(*sock));
|
||||
|
||||
if (!sock)
|
||||
return NULL;
|
||||
|
||||
memset(sock, 0, sizeof(*sock));
|
||||
|
||||
sock->fd.fd = usock(USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK |
|
||||
USOCK_NUMERIC | USOCK_IPV4ONLY,
|
||||
"127.0.0.1", port);
|
||||
if (sock->fd.fd < 0) {
|
||||
perror("usock");
|
||||
free(sock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sock->type = type;
|
||||
sock->fd.cb = sock_recv;
|
||||
|
||||
uloop_fd_add(&sock->fd, ULOOP_READ);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "radius-gw-proxy");
|
||||
|
||||
uloop_init();
|
||||
|
||||
ubus_init();
|
||||
|
||||
sock_auth = sock_open("1812", RADIUS_AUTH);
|
||||
sock_acct = sock_open("1813", RADIUS_ACCT);
|
||||
sock_dae = sock_open("3379", RADIUS_DAS);
|
||||
|
||||
uloop_run();
|
||||
uloop_end();
|
||||
ubus_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libubox/ulog.h>
|
||||
#include <libubus.h>
|
||||
#include "ubus.h"
|
||||
|
||||
struct ubus_auto_conn conn;
|
||||
uint32_t ucentral;
|
||||
|
||||
enum {
|
||||
RADIUS_TYPE,
|
||||
RADIUS_DATA,
|
||||
__RADIUS_MAX,
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy frame_policy[__RADIUS_MAX] = {
|
||||
[RADIUS_TYPE] = { .name = "radius", .type = BLOBMSG_TYPE_STRING },
|
||||
[RADIUS_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
static int ubus_frame_cb(struct ubus_context *ctx,
|
||||
struct ubus_object *obj,
|
||||
struct ubus_request_data *req,
|
||||
const char *method, struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__RADIUS_MAX] = {};
|
||||
enum socket_type type;
|
||||
char *radius, *data;
|
||||
|
||||
blobmsg_parse(frame_policy, __RADIUS_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
|
||||
if (!tb[RADIUS_TYPE] || !tb[RADIUS_DATA])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
radius = blobmsg_get_string(tb[RADIUS_TYPE]);
|
||||
data = blobmsg_get_string(tb[RADIUS_DATA]);
|
||||
|
||||
if (!strcmp(radius, "auth"))
|
||||
type = RADIUS_AUTH;
|
||||
else if (!strcmp(radius, "acct"))
|
||||
type = RADIUS_ACCT;
|
||||
else if (!strcmp(radius, "coa"))
|
||||
type = RADIUS_DAS;
|
||||
else
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
gateway_recv(data, type);
|
||||
|
||||
return UBUS_STATUS_OK;
|
||||
}
|
||||
static const struct ubus_method ucentral_methods[] = {
|
||||
UBUS_METHOD("frame", ubus_frame_cb, frame_policy),
|
||||
};
|
||||
|
||||
static struct ubus_object_type ubus_object_type =
|
||||
UBUS_OBJECT_TYPE("radius.proxy", ucentral_methods);
|
||||
|
||||
struct ubus_object ubus_object = {
|
||||
.name = "radius.proxy",
|
||||
.type = &ubus_object_type,
|
||||
.methods = ucentral_methods,
|
||||
.n_methods = ARRAY_SIZE(ucentral_methods),
|
||||
};
|
||||
|
||||
static void
|
||||
ubus_event_handler_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
enum {
|
||||
EVENT_ID,
|
||||
EVENT_PATH,
|
||||
__EVENT_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy status_policy[__EVENT_MAX] = {
|
||||
[EVENT_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
|
||||
[EVENT_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
struct blob_attr *tb[__EVENT_MAX];
|
||||
char *path;
|
||||
uint32_t id;
|
||||
|
||||
blobmsg_parse(status_policy, __EVENT_MAX, tb, blob_data(msg), blob_len(msg));
|
||||
|
||||
if (!tb[EVENT_ID] || !tb[EVENT_PATH])
|
||||
return;
|
||||
|
||||
path = blobmsg_get_string(tb[EVENT_PATH]);
|
||||
id = blobmsg_get_u32(tb[EVENT_ID]);
|
||||
|
||||
if (strcmp(path, "ucentral"))
|
||||
return;
|
||||
if (!strcmp("ubus.object.remove", type))
|
||||
ucentral = 0;
|
||||
else
|
||||
ucentral = id;
|
||||
}
|
||||
|
||||
static struct ubus_event_handler ubus_event_handler = { .cb = ubus_event_handler_cb };
|
||||
|
||||
static void
|
||||
ubus_connect_handler(struct ubus_context *ctx)
|
||||
{
|
||||
ubus_add_object(ctx, &ubus_object);
|
||||
ubus_register_event_handler(ctx, &ubus_event_handler, "ubus.object.add");
|
||||
ubus_register_event_handler(ctx, &ubus_event_handler, "ubus.object.remove");
|
||||
|
||||
ubus_lookup_id(ctx, "ucentral", &ucentral);
|
||||
}
|
||||
|
||||
void ubus_init(void)
|
||||
{
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
ucentral = 0;
|
||||
conn.cb = ubus_connect_handler;
|
||||
ubus_auto_connect(&conn);
|
||||
}
|
||||
|
||||
void ubus_deinit(void)
|
||||
{
|
||||
ubus_auto_shutdown(&conn);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
enum socket_type {
|
||||
RADIUS_AUTH = 0,
|
||||
RADIUS_ACCT,
|
||||
RADIUS_DAS
|
||||
};
|
||||
|
||||
extern struct ubus_auto_conn conn;
|
||||
extern uint32_t ucentral;
|
||||
|
||||
void ubus_init(void);
|
||||
void ubus_deinit(void);
|
||||
void gateway_recv(char *data, enum socket_type type);
|
||||
|
||||
34
olg-ucentral-client-sdk/feeds/ucentral/ratelimit/Makefile
Normal file
34
olg-ucentral-client-sdk/feeds/ucentral/ratelimit/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ratelimit
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/ratelimit
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Wireless ratelimiting
|
||||
DEPENDS:=+tc +kmod-ifb
|
||||
endef
|
||||
|
||||
define Package/ratelimit/description
|
||||
Allow Wireless client rate limiting
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Compile/Default
|
||||
|
||||
endef
|
||||
Build/Compile = $(Build/Compile/Default)
|
||||
|
||||
define Package/ratelimit/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,ratelimit))
|
||||
@@ -0,0 +1,3 @@
|
||||
#config rate uCentral
|
||||
# option egress 10
|
||||
# option ingress 20
|
||||
39
olg-ucentral-client-sdk/feeds/ucentral/ratelimit/files/etc/init.d/ratelimit
Executable file
39
olg-ucentral-client-sdk/feeds/ucentral/ratelimit/files/etc/init.d/ratelimit
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=80
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/ratelimit
|
||||
|
||||
add_rate() {
|
||||
local cfg="$1"
|
||||
config_get ssid "$cfg" ssid
|
||||
config_get ingress "$cfg" ingress
|
||||
config_get egress "$cfg" egress
|
||||
ubus call ratelimit defaults_set "{\"name\": \"$ssid\", \"rate_ingress\": \""$ingress"mbit\", \"rate_egress\": \""$egress"mbit\" }"
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
logger ratelimit reload
|
||||
ubus call ratelimit flush
|
||||
config_load ratelimit
|
||||
config_foreach add_rate rate
|
||||
ubus call ratelimit reload
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger ratelimit
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 10 wait_for ratelimit
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
|
||||
409
olg-ucentral-client-sdk/feeds/ucentral/ratelimit/files/usr/bin/ratelimit
Executable file
409
olg-ucentral-client-sdk/feeds/ucentral/ratelimit/files/usr/bin/ratelimit
Executable file
@@ -0,0 +1,409 @@
|
||||
#!/usr/bin/env ucode
|
||||
'use strict';
|
||||
|
||||
import { basename, popen } from 'fs';
|
||||
import * as ubus from 'ubus';
|
||||
import * as uloop from 'uloop';
|
||||
|
||||
let defaults = {};
|
||||
let devices = {};
|
||||
|
||||
function cmd(command, ignore_error) {
|
||||
// if (ignore_error)
|
||||
// command += "> /dev/null 2>&1";
|
||||
warn(`> ${command}\n`);
|
||||
let rc = system(command);
|
||||
return ignore_error || rc == 0;
|
||||
}
|
||||
|
||||
function qdisc_add_leaf(iface, id, opts) {
|
||||
opts ??= "";
|
||||
return cmd(`tc class replace dev ${iface} parent 1:1 classid 1:${id} htb rate 1mbit ${opts} burst 2k prio 1`) &&
|
||||
cmd(`tc qdisc replace dev ${iface} parent 1:${id} handle ${id}: fq_codel flows 128 limit 800 quantum 300 noecn`);
|
||||
}
|
||||
|
||||
function qdisc_del_leaf(iface, id) {
|
||||
cmd(`tc class del dev ${iface} parent 1:1 classid 1:${id}`, true);
|
||||
}
|
||||
|
||||
function qdisc_add(iface) {
|
||||
return cmd(`tc qdisc add dev ${iface} root handle 1: htb default 2`) &&
|
||||
cmd(`tc class add dev ${iface} parent 1: classid 1:1 htb rate 1000mbit burst 6k`) &&
|
||||
qdisc_add_leaf(iface, 2, "ceil 1000mbit");
|
||||
}
|
||||
|
||||
function qdisc_del(iface) {
|
||||
cmd(`tc qdisc del dev ${iface} root`, true);
|
||||
}
|
||||
|
||||
function ifb_dev(iface) {
|
||||
let ifbname;
|
||||
|
||||
if ((index(iface, 'phy') != -1) && (index(iface, 'ap') != -1)) {
|
||||
// For interfaces like phy6g-ap0, phy5g-ap0, phy2g-ap0
|
||||
// we replace 'phy' with "p" to confine the ifb name length
|
||||
// and replace 'ap' with 'a' to further shorten it.
|
||||
ifbname = replace(iface, 'phy', 'p');
|
||||
ifbname = replace(ifbname, 'ap', 'a');
|
||||
} else {
|
||||
ifbname = iface;
|
||||
}
|
||||
ifbname = "i-" + ifbname;
|
||||
return ifbname;
|
||||
}
|
||||
|
||||
function ifb_add(iface, ifbdev) {
|
||||
return cmd(`ip link add ${ifbdev} type ifb`) &&
|
||||
cmd(`ip link set ${ifbdev} up`) &&
|
||||
cmd(`tc qdisc add dev ${iface} clsact`, true) &&
|
||||
cmd(`tc filter add dev ${iface} ingress protocol all prio 512 matchall action mirred egress redirect dev ${ifbdev}`);
|
||||
}
|
||||
|
||||
function ifb_del(iface, ifbdev) {
|
||||
cmd(`tc filter del dev ${iface} ingress protocol all prio 512`);
|
||||
cmd(`ip link set ${ifbdev} down`, true);
|
||||
cmd(`ip link del ${ifbdev}`, true);
|
||||
}
|
||||
|
||||
function macfilter_add(iface, id, type, mac) {
|
||||
return cmd(`tc filter add dev ${iface} protocol all parent 1: prio 1 handle 800::${id} u32 match ether ${type} ${mac} flowid 1:${id}`);
|
||||
}
|
||||
|
||||
function macfilter_del(iface, id) {
|
||||
cmd(`tc filter del dev ${iface} protocol all parent 1: prio 1 handle 800::${id} u32`, true);
|
||||
}
|
||||
|
||||
function linux_client_del(device, client) {
|
||||
printf('-> linux_client_del\n');
|
||||
let ifbdev = ifb_dev(device.name);
|
||||
let id = client.id + 3;
|
||||
|
||||
macfilter_del(device.name, id);
|
||||
qdisc_del_leaf(device.name, id);
|
||||
macfilter_del(ifbdev, id);
|
||||
qdisc_del_leaf(ifbdev, id);
|
||||
}
|
||||
|
||||
function linux_client_set(device, client) {
|
||||
printf('-> linux_client_set\n');
|
||||
let ifbdev = ifb_dev(device.name);
|
||||
let id = client.id + 3;
|
||||
|
||||
linux_client_del(device, client);
|
||||
|
||||
let ret = qdisc_add_leaf(device.name, id, `ceil ${client.data.rate_egress}`) &&
|
||||
macfilter_add(device.name, id, "dst", client.address) &&
|
||||
qdisc_add_leaf(ifbdev, id, `ceil ${client.data.rate_ingress}`) &&
|
||||
macfilter_add(ifbdev, id, "src", client.address);
|
||||
|
||||
if (!ret)
|
||||
linux_client_del(device, client);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
let ops = {
|
||||
device: {
|
||||
add: function(name) {
|
||||
printf('-> device.add\n');
|
||||
let ifbdev = ifb_dev(name);
|
||||
|
||||
qdisc_del(name);
|
||||
ifb_del(name, ifbdev);
|
||||
|
||||
let ret = qdisc_add(name) &&
|
||||
ifb_add(name, ifbdev) &&
|
||||
qdisc_add(ifbdev);
|
||||
|
||||
if (!ret) {
|
||||
qdisc_del(name);
|
||||
ifb_del(name, ifbdev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
remove: function(name) {
|
||||
printf('-> device.remove\n');
|
||||
let ifbdev = ifb_dev(name);
|
||||
qdisc_del(name);
|
||||
ifb_del(name, ifbdev);
|
||||
}
|
||||
},
|
||||
client: {
|
||||
set: function(device, client) {
|
||||
printf('-> client.set\n');
|
||||
return linux_client_set(device, client);
|
||||
},
|
||||
remove: function(device, client) {
|
||||
printf('-> client.remove\n');
|
||||
linux_client_del(device, client);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function get_device(devices, name) {
|
||||
printf('-> get_device\n');
|
||||
let device = devices[name];
|
||||
|
||||
if (device)
|
||||
return device;
|
||||
|
||||
if (!ops.device.add(name))
|
||||
return null;
|
||||
|
||||
device = {
|
||||
name: name,
|
||||
clients: {},
|
||||
client_order: [],
|
||||
};
|
||||
|
||||
devices[name] = device;
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
function del_device(name) {
|
||||
printf('-> del_device\n');
|
||||
if (!devices[name])
|
||||
return;
|
||||
ops.device.remove(name);
|
||||
delete devices[name];
|
||||
}
|
||||
|
||||
function get_free_idx(list) {
|
||||
printf('-> get_free_idx\n');
|
||||
for (let i = 0; i < length(list); i++)
|
||||
if (list[i] == null)
|
||||
return i;
|
||||
|
||||
return length(list);
|
||||
}
|
||||
|
||||
function del_client(device, address) {
|
||||
printf('-> del_client\n');
|
||||
|
||||
let client = device.clients[address];
|
||||
|
||||
if (!client)
|
||||
return false;
|
||||
|
||||
delete device.clients[address];
|
||||
device.client_order[client.id] = null;
|
||||
|
||||
ops.client.remove(device, client);
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_client(device, address) {
|
||||
printf('-> get_client\n');
|
||||
let client = device.clients[address];
|
||||
|
||||
if (client)
|
||||
return client;
|
||||
|
||||
let i = get_free_idx(device.client_order);
|
||||
client = {};
|
||||
client.address = address;
|
||||
client.id = i;
|
||||
client.data = {};
|
||||
|
||||
device.clients[address] = client;
|
||||
device.client_order[i] = client;
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
function set_client(device, client, data) {
|
||||
printf('-> set_client\n');
|
||||
let update = false;
|
||||
|
||||
for (let key in data) {
|
||||
if (client.data[key] != data[key])
|
||||
update = true;
|
||||
|
||||
client.data[key] = data[key];
|
||||
}
|
||||
|
||||
if (update && !ops.client.set(device, client)) {
|
||||
del_client(device, client.address);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function run_service() {
|
||||
let uctx = ubus.connect();
|
||||
|
||||
uctx.publish("ratelimit", {
|
||||
flush: {
|
||||
call: function(req) {
|
||||
printf('-> flush\n');
|
||||
defaults = {};
|
||||
},
|
||||
args: {
|
||||
|
||||
}
|
||||
},
|
||||
defaults_set: {
|
||||
call: function(req) {
|
||||
printf('-> defaults_set\n');
|
||||
let r_i = req.args.rate_ingress ?? req.args.rate;
|
||||
let r_e = req.args.rate_egress ?? req.args.rate;
|
||||
let name = req.args.name;
|
||||
|
||||
if (!name || !r_i || !r_e)
|
||||
return ubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
defaults[name] = [ r_e, r_i ];
|
||||
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
name:"",
|
||||
rate:"",
|
||||
rate_ingress:"",
|
||||
rate_egress:"",
|
||||
}
|
||||
},
|
||||
client_set: {
|
||||
call: function(req) {
|
||||
printf('-> client_set\n');
|
||||
let r_i = req.args.rate_ingress ?? req.args.rate;
|
||||
let r_e = req.args.rate_egress ?? req.args.rate;
|
||||
|
||||
if (req.args.defaults && defaults[req.args.defaults]) {
|
||||
let def = defaults[req.args.defaults];
|
||||
|
||||
r_e ??= def[0];
|
||||
r_i ??= def[1];
|
||||
}
|
||||
|
||||
if (!req.args.device || !req.args.address || !r_i || !r_e)
|
||||
return ubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
let device = get_device(devices, req.args.device);
|
||||
if (!device)
|
||||
return ubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
let client = get_client(device, req.args.address);
|
||||
if (!client)
|
||||
return ubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
let data = {
|
||||
rate_ingress: r_i,
|
||||
rate_egress: r_e
|
||||
};
|
||||
|
||||
if (!set_client(device, client, data))
|
||||
return ubus.STATUS_UNKNOWN_ERROR;
|
||||
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
device:"",
|
||||
defaults:"",
|
||||
address:"",
|
||||
rate:"",
|
||||
rate_ingress:"",
|
||||
rate_egress:"",
|
||||
}
|
||||
},
|
||||
client_delete: {
|
||||
call: function(req) {
|
||||
printf('-> client_delete\n');
|
||||
if (!req.args.address)
|
||||
return ubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (req.args.device) {
|
||||
let device = devices[req.args.device];
|
||||
if (!device)
|
||||
return ubus.STATUS_NOT_FOUND;
|
||||
|
||||
if (!del_client(device, req.args.address))
|
||||
return ubus.STATUS_NOT_FOUND;
|
||||
} else {
|
||||
for (let dev in devices) {
|
||||
let device = devices[dev];
|
||||
|
||||
del_client(device, req.args.address);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
device:"",
|
||||
address:"",
|
||||
}
|
||||
},
|
||||
device_delete: {
|
||||
call: function(req) {
|
||||
printf('-> device_delete\n');
|
||||
let name = req.args.device;
|
||||
|
||||
if (!name)
|
||||
return ubus.STATUS_INVALID_ARGUMENT;
|
||||
|
||||
if (!devices[name])
|
||||
return ubus.STATUS_NOT_FOUND;
|
||||
|
||||
del_device(name);
|
||||
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
device:"",
|
||||
}
|
||||
},
|
||||
reload: {
|
||||
call: function(req) {
|
||||
printf('-> reload\n');
|
||||
let list = uctx.list();
|
||||
for (let obj in list) {
|
||||
if (!wildcard(obj, 'hostapd.wlan*') && !wildcard(obj, 'hostapd.phy*'))
|
||||
continue;
|
||||
let iface = split(obj, '.')[1];
|
||||
let device = get_device(devices, req.args.device);
|
||||
if (!device)
|
||||
continue;
|
||||
let status = uctx.call(obj, 'get_status');
|
||||
if (!status?.ssid)
|
||||
continue;
|
||||
if (!defaults[status?.ssid])
|
||||
continue;
|
||||
let data = {
|
||||
rate_ingress: defaults[status?.ssid][0],
|
||||
rate_egress: defaults[status?.ssid][1]
|
||||
};
|
||||
for (let k, client in device.clients)
|
||||
set_client(device, client, data);
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
dump: {
|
||||
call: function(req) {
|
||||
return { devices, defaults };
|
||||
},
|
||||
args: {}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
uloop.run();
|
||||
} catch (e) {
|
||||
warn(`Error: ${e}\n${e.stacktrace[0].context}`);
|
||||
}
|
||||
|
||||
for (let dev in devices) {
|
||||
del_device(dev);
|
||||
}
|
||||
}
|
||||
|
||||
uloop.init();
|
||||
run_service();
|
||||
uloop.done();
|
||||
27
olg-ucentral-client-sdk/feeds/ucentral/rrmd/Makefile
Normal file
27
olg-ucentral-client-sdk/feeds/ucentral/rrmd/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=rrmd
|
||||
PKG_RELEASE:=1
|
||||
PKG_LICENSE:=ISC
|
||||
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/rrmd
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
DEPENDS:=+ucrun
|
||||
TITLE:=radio resource management
|
||||
endef
|
||||
|
||||
define Build/Compile/Default
|
||||
|
||||
endef
|
||||
Build/Compile = $(Build/Compile/Default)
|
||||
|
||||
define Package/rrmd/install
|
||||
$(CP) ./files/* $(1)/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,rrmd))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user