8 Commits

Author SHA1 Message Date
Mike Hansen
45c70e2c9d Merge pull request #10 from Telecominfraproject/update-ucentral-client-olgV8
Prepare v0.0.3 release with updated ucentral-client image
2026-03-13 11:23:54 -04:00
Mike Hansen
61750ff76d Prepare v0.0.3 release with updated ucentral-client image
- Updated iso-files/ucentral-setup.sh to use mjhnetexp/ucentral-client:olgV8
- Updated ISO version to v0.0.3 in grub.cfg
- Added v0.0.3 changelog entry

The olgV8 image contains:
- Updated ucentral-schema from ra_proposal branch (SDK commit 6491570)
- Latest VyOS integration improvements
- Schema enhancements for NAT and operational commands
2026-03-11 10:49:57 -04:00
Andre Courchesne
9b49f798c0 - Update changelog, bump to v0.0.2 2026-02-12 10:19:48 -05:00
Andre Courchesne
2e5afdc7ec Merge pull request #4 from Telecominfraproject/ucentral-support
Add Ucentral support
2026-02-12 10:15:54 -05:00
NavneetBarwal-RA
a8a7535a6f - Add UCentral support and documentation 2026-02-12 10:15:19 -05:00
Andre Courchesne
6bb1e025cb - Update changelog, bump to v0.0.1 2025-12-17 11:28:28 -05:00
Andre Courchesne
317ec12332 Merge pull request #2 from Telecominfraproject/1-structure-setup-and-first-code
Fixes #1 - Structure setup and first code commit
2025-12-17 11:26:20 -05:00
Andre Courchesne
46950d9ff1 #1 - Structure setup and first code commit 2025-12-16 10:19:53 -05:00
537 changed files with 7450 additions and 60166 deletions

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
.vagrant
*.iso
box/
output-*/
manifest.json
files/iso_release
images/efiboot.img
*.swp
kickstart/devops_packages.cfg
*packages.yml
*.zip
.DS_Store
loop
firstboot/firstboot

19
CHANGELOG.md Normal file
View File

@@ -0,0 +1,19 @@
# Changelog
All notable changes to this project will be documented in this file.
NOTE: the project follows [Semantic Versioning](http://semver.org/).
## v0.0.3 - March 11th, 2026
- Updated ucentral-client Docker image to olgV8
- Includes updated ucentral-schema from ra_proposal branch (SDK commit 6491570)
- VyOS integration improvements and schema enhancements
## v0.0.2 - February 12th, 2026
- Add Ucentral support and documentation
## v0.0.1 - December 17th 2025
- First release

133
README.md
View File

@@ -1,5 +1,130 @@
# OLG Ucentral Client SDK
# Installer for OpenLAN Gateway
Project aimed at building ISOs for OpenLAN Gateway
## Requirements
- Docker
- Linux or macOS operating system (sorry not sorry Windows)
## Building an ISO
1. Run `script/build`
The result of this will be an ISO in the project working folder.
## Installing OpenLAN Gateway
### Install from ISO and VyOS VM configuration
- Boot on the ISO, once the install is completed the server will power-off
- Power back the server
- Login to the Linux host with username `olgadm` and password `olgadm`
- Edit `/opt/staging_scripts/setup-config` and adjust the network interface names and if required the VyOS VM sizing parameters
- If you will be using the "Standalone mode", you might need to adjust the VyOS rolling release path. Reference: https://github.com/vyos/vyos-nightly-build/releases
- Run the setup script:
- `sudo /opt/staging_scripts/setup-vyos-bridge.sh` to use the network bridge method
- `sudo /opt/staging_scripts/setup-vyos-hw-passthru.sh` to use the hardware passthru for the network interfaces (WIP, not tested)
- Reboot the host
- Connect to the VyOS console with `virsh console vyos`
- Login with username `vyos` and password `vyos`
- Type `install image` and press Enter.
- Follow the prompts (you can use all defaults)
- Once completed, type `reboot` to reboot the VM
- For some reason the VyOS VM does not reboot after this first `reboot` command. You must restart it manually with `virsh start vyos`
## Configuration options
At this time you can either load a default configuration and use VyOS in a "standalone" mode or have it connected to an OpenWifi Cloud SDK instance.
### OpenWifi Cloud SDK mode
### Setup of Ucentral-Client Container
- SSH to the OLG Ubuntu host
- Run `sudo /opt/staging_scripts/ucentral-setup.sh setup` to setup the ucentral-client container
- You can also use the following parameters for the `ucentral-setup.sh` script:
- `shell`: To get access to ucentral container.
- `cleanup`: To clean the setup.
- Copy your certificates to olg and then to container at `/etc/ucentral` in order to work with your cloud-controller.
- `sudo docker cp cert.pem ucentral-olg:/etc/ucentral/operational.pem`
- `sudo docker cp cas.pem ucentral-olg:/etc/ucentral/operational.ca`
- `sudo docker cp key.pem ucentral-olg:/etc/ucentral/`
- Run `sudo /opt/staging_scripts/ucentral-setup.sh shell` to get shell access to the ucentral container and perform the following tasks:
- Modify `/etc/ucentral/vyos-info.json` and update `host` value to the IP address assigned to the VyOS VM br-wan interface.
- Start the ucentral-client:
- Debug mode: `SERIALNUM=my_olg_serial ; URL=my_cloudsdk_uri ; /usr/sbin/ucentral -S $SERIALNUM -s $URL -P 15002 -d`
- Deamonized mode: `SERIALNUM=my_olg_serial ; URL=my_cloudsdk_uri ; /usr/sbin/ucentral -S $SERIALNUM -s $URL -P 15002 -d &`
- For example `SERIALNUM=74d4ddb965dc` for which certs are generated, `URL=openwifi1.routerarchitects.com`, for your OpenWifi Cloud SDK instance.
> [!WARNING]
> Ucentral Client must be started only after VyOS gets started.
>
> There is a bit of a chicken-and-the-egg scenario if the OLG device was never seen by the OpenWifi Cloud SDK instance. A blank configuration will be pushed to VyOS and the connection with the ucentral client might be broken.
>
> At this time the order of execution should be the following if the OLG device was never seen by the OpenWifi Cloud SDK instance:
> - Stop the VyOS VM with `virsh shutdown vyos`
> - Start the ucentral-client
> - Populate a configuration in the OpenWifi Cloud SDK for the OLG device
> - Restart the VyOS VM with `virsh start vyos`
> - If IP Address of VyOS VM gets changed then reconfigure value of `host` in `/etc/ucentral/vyos-info.json` , in ucentral and restart ucentral client.
### Standalone mode
The factory configuration consists of:
- `eth0` as the WAN interface in DHCP
- `eth1` as the LAN interface
- 3 VLANs:
- VLAN 100 for the switches
- VLAN 101 for the APs
- VLAN 1000 for the guest devices
- Each VLAN has it's own DHCP scope
Here is how to load this configuration:
- Open a console to the VyOS console with `virsh console vyos`
- If required login with your credentials
- Mound the ISO containing the configs
```
sudo mkdir /opt/vyos-configs ; sudo mount /dev/sr1 /opt/vyos-configs
```
- Go in config mode with `config`
- Load the factory config with:
```
source /opt/olg-configs/vyos-factory-config
commit
save
exit
```
## Sample UCentral configurations
Here are some sample configuration(s)
| File path | Description |
|-----------|-------------|
| [mdu.json](sample-configurations/mdu.json) | MDU configuration with two VLAN networks and also adhere to olg ucentral schema enhancement for NAT object as proposed in [olg-ucentral-schema ra_proposal branch](https://github.com/Telecominfraproject/olg-ucentral-schema/tree/ra_proposal) |
## Tested platforms
- MinisForum MS-01
## Contributing
- Create an issue
- Create a branch and an assoiated PR
- Code
- Ask for review and get your changes merged
## Protip
Use the Shipit CLI (https://gitlab.com/intello/shipit-cli-go)
This allows you to create the branch and associated PR in one simple command. The branch and PR will use a standardized naming scheme.
![image](docs/shipit-screenshot.png)
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.

BIN
docs/shipit-screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

12
grub.cfg Normal file
View File

@@ -0,0 +1,12 @@
set timeout=30
insmod all_video
insmod png
loadfont unicode
gfxpayload text
ISO_VERSION="v0.0.3"
menuentry "Install Open LAN Gateway (ISO $ISO_VERSION)" {
linux /casper/vmlinuz autoinstall fsck.mode=skip ds=nocloud\;s=/cdrom/nocloud/ ipv6.disable=1 console=ttyS0,115200n8 console=tty0 network-config=disabled ---
initrd /casper/initrd
}

View File

@@ -0,0 +1,6 @@
network:
version: 2
ethernets:
{{ HOST_ADMIN_PORT }}: # Admin port
dhcp4: true
dhcp6: false

View File

@@ -0,0 +1,20 @@
# Add /opt to the path
PATH=$PATH:/opt
export PATH
# Some useful aliases
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias reboot='shutdown -rf now'
alias ip4='ip -c -o -4'
alias ip6='ip -c -o -6'
# Source global definitions
if [ -f /etc/bashrc ]; then
source /etc/bashrc
fi
# Terminal tweaks
export LS_COLORS="no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=01;32:*.cmd=01;32:*.exe=01;32:*.com=01;32:*.btm=01;32:*.bat=01;32:*.sh=01;32:*.csh=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.bz=01;31:*.tz=01;31:*.rpm=01;31:*.cpio=01;31:*.jpg=01;35:*.gif=01;35:*.bmp=01;35:*.xbm=01;35:*.xpm=01;35:*.png=01;35:*.tif=01;35:"
export TERM=xterm-color

115
iso-files/destroy-vyos-vm.sh Executable file
View File

@@ -0,0 +1,115 @@
#!/usr/bin/env bash
set -euo pipefail
VM_NAME="vyos"
IMAGES_DIR="/var/lib/libvirt/images"
ISO_PATH="$IMAGES_DIR/vyos.iso"
DISK_PATH="$IMAGES_DIR/${VM_NAME}.qcow2"
NETPLAN_FILE="/etc/netplan/99-vyos-bridges.yaml"
# ====== Preflight ======
if [[ $EUID -ne 0 ]]; then
echo "Please run as root (sudo $0)"; exit 1
fi
echo "============================================"
echo "VyOS VM Destruction Script"
echo "============================================"
echo ""
echo "This script will:"
echo " 1. Stop and destroy the VyOS VM"
echo " 2. Undefine the VM from libvirt"
echo " 3. Remove VM disk image"
echo " 4. Optionally remove VyOS ISO"
echo " 5. Optionally remove bridge network configuration"
echo ""
# Check if VM exists
if ! virsh dominfo "$VM_NAME" >/dev/null 2>&1; then
echo ">>> VM '$VM_NAME' does not exist or is not defined."
else
# Check if VM is running
if virsh list --state-running | grep -q "$VM_NAME"; then
echo ">>> Stopping VM '$VM_NAME'..."
virsh destroy "$VM_NAME"
echo " VM stopped."
else
echo ">>> VM '$VM_NAME' is not running."
fi
# Undefine the VM
echo ">>> Undefining VM '$VM_NAME'..."
virsh undefine "$VM_NAME" --nvram 2>/dev/null || virsh undefine "$VM_NAME"
echo " VM undefined."
fi
# Remove disk image
if [[ -f "$DISK_PATH" ]]; then
echo ">>> Removing VM disk: $DISK_PATH"
rm -f "$DISK_PATH"
echo " Disk removed."
else
echo ">>> VM disk not found at $DISK_PATH (already removed or never created)."
fi
# Ask about ISO removal
if [[ -f "$ISO_PATH" ]]; then
echo ""
read -p "Remove VyOS ISO at $ISO_PATH? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo ">>> Removing VyOS ISO: $ISO_PATH"
rm -f "$ISO_PATH"
echo " ISO removed."
else
echo ">>> Keeping VyOS ISO at $ISO_PATH"
fi
else
echo ">>> VyOS ISO not found at $ISO_PATH (already removed or never downloaded)."
fi
# Ask about bridge network configuration removal
if [[ -f "$NETPLAN_FILE" ]]; then
echo ""
echo "WARNING: Removing the netplan bridge configuration will restore"
echo " the network interfaces to their previous state, but may"
echo " cause network disruption."
echo ""
read -p "Remove bridge network configuration at $NETPLAN_FILE? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo ">>> Removing netplan bridge configuration: $NETPLAN_FILE"
rm -f "$NETPLAN_FILE"
echo " Configuration file removed."
echo ""
read -p "Apply netplan changes now? (y/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo ">>> Applying netplan..."
netplan apply
echo " Netplan applied."
else
echo ">>> Skipping netplan apply. Run 'netplan apply' manually to restore interfaces."
fi
else
echo ">>> Keeping bridge network configuration at $NETPLAN_FILE"
fi
else
echo ">>> Bridge network configuration not found at $NETPLAN_FILE"
fi
echo ""
echo "============================================"
echo "VyOS VM Cleanup Complete!"
echo "============================================"
echo ""
echo "Summary:"
echo " - VM '$VM_NAME' has been destroyed and undefined"
echo " - VM disk has been removed"
echo ""
echo "Note: If you used PCI passthrough (setup-vyos-hw-passthru.sh),"
echo " the physical network interfaces should automatically return"
echo " to the host when the VM is destroyed. You may need to reload"
echo " the appropriate driver or reboot to fully restore them."
echo ""

764
iso-files/get-docker.sh Normal file
View File

@@ -0,0 +1,764 @@
#!/bin/sh
set -e
# Docker Engine for Linux installation script.
#
# This script is intended as a convenient way to configure docker's package
# repositories and to install Docker Engine, This script is not recommended
# for production environments. Before running this script, make yourself familiar
# with potential risks and limitations, and refer to the installation manual
# at https://docs.docker.com/engine/install/ for alternative installation methods.
#
# The script:
#
# - Requires `root` or `sudo` privileges to run.
# - Attempts to detect your Linux distribution and version and configure your
# package management system for you.
# - Doesn't allow you to customize most installation parameters.
# - Installs dependencies and recommendations without asking for confirmation.
# - Installs the latest stable release (by default) of Docker CLI, Docker Engine,
# Docker Buildx, Docker Compose, containerd, and runc. When using this script
# to provision a machine, this may result in unexpected major version upgrades
# of these packages. Always test upgrades in a test environment before
# deploying to your production systems.
# - Isn't designed to upgrade an existing Docker installation. When using the
# script to update an existing installation, dependencies may not be updated
# to the expected version, resulting in outdated versions.
#
# Source code is available at https://github.com/docker/docker-install/
#
# Usage
# ==============================================================================
#
# To install the latest stable versions of Docker CLI, Docker Engine, and their
# dependencies:
#
# 1. download the script
#
# $ curl -fsSL https://get.docker.com -o install-docker.sh
#
# 2. verify the script's content
#
# $ cat install-docker.sh
#
# 3. run the script with --dry-run to verify the steps it executes
#
# $ sh install-docker.sh --dry-run
#
# 4. run the script either as root, or using sudo to perform the installation.
#
# $ sudo sh install-docker.sh
#
# Command-line options
# ==============================================================================
#
# --version <VERSION>
# Use the --version option to install a specific version, for example:
#
# $ sudo sh install-docker.sh --version 23.0
#
# --channel <stable|test>
#
# Use the --channel option to install from an alternative installation channel.
# The following example installs the latest versions from the "test" channel,
# which includes pre-releases (alpha, beta, rc):
#
# $ sudo sh install-docker.sh --channel test
#
# Alternatively, use the script at https://test.docker.com, which uses the test
# channel as default.
#
# --mirror <Aliyun|AzureChinaCloud>
#
# Use the --mirror option to install from a mirror supported by this script.
# Available mirrors are "Aliyun" (https://mirrors.aliyun.com/docker-ce), and
# "AzureChinaCloud" (https://mirror.azure.cn/docker-ce), for example:
#
# $ sudo sh install-docker.sh --mirror AzureChinaCloud
#
# --setup-repo
#
# Use the --setup-repo option to configure Docker's package repositories without
# installing Docker packages. This is useful when you want to add the repository
# but install packages separately:
#
# $ sudo sh install-docker.sh --setup-repo
#
# Automatic Service Start
#
# By default, this script automatically starts the Docker daemon and enables the docker
# service after installation if systemd is used as init.
#
# If you prefer to start the service manually, use the --no-autostart option:
#
# $ sudo sh install-docker.sh --no-autostart
#
# Note: Starting the service requires appropriate privileges to manage system services.
#
# ==============================================================================
# Git commit from https://github.com/docker/docker-install when
# the script was uploaded (Should only be modified by upload job):
SCRIPT_COMMIT_SHA="f381ee68b32e515bb4dc034b339266aff1fbc460"
# strip "v" prefix if present
VERSION="${VERSION#v}"
# The channel to install from:
# * stable
# * test
DEFAULT_CHANNEL_VALUE="stable"
if [ -z "$CHANNEL" ]; then
CHANNEL=$DEFAULT_CHANNEL_VALUE
fi
DEFAULT_DOWNLOAD_URL="https://download.docker.com"
if [ -z "$DOWNLOAD_URL" ]; then
DOWNLOAD_URL=$DEFAULT_DOWNLOAD_URL
fi
DEFAULT_REPO_FILE="docker-ce.repo"
if [ -z "$REPO_FILE" ]; then
REPO_FILE="$DEFAULT_REPO_FILE"
# Automatically default to a staging repo fora
# a staging download url (download-stage.docker.com)
case "$DOWNLOAD_URL" in
*-stage*) REPO_FILE="docker-ce-staging.repo";;
esac
fi
mirror=''
DRY_RUN=${DRY_RUN:-}
REPO_ONLY=${REPO_ONLY:-0}
NO_AUTOSTART=${NO_AUTOSTART:-0}
while [ $# -gt 0 ]; do
case "$1" in
--channel)
CHANNEL="$2"
shift
;;
--dry-run)
DRY_RUN=1
;;
--mirror)
mirror="$2"
shift
;;
--version)
VERSION="${2#v}"
shift
;;
--setup-repo)
REPO_ONLY=1
shift
;;
--no-autostart)
NO_AUTOSTART=1
;;
--*)
echo "Illegal option $1"
;;
esac
shift $(( $# > 0 ? 1 : 0 ))
done
case "$mirror" in
Aliyun)
DOWNLOAD_URL="https://mirrors.aliyun.com/docker-ce"
;;
AzureChinaCloud)
DOWNLOAD_URL="https://mirror.azure.cn/docker-ce"
;;
"")
;;
*)
>&2 echo "unknown mirror '$mirror': use either 'Aliyun', or 'AzureChinaCloud'."
exit 1
;;
esac
case "$CHANNEL" in
stable|test)
;;
*)
>&2 echo "unknown CHANNEL '$CHANNEL': use either stable or test."
exit 1
;;
esac
command_exists() {
command -v "$@" > /dev/null 2>&1
}
# version_gte checks if the version specified in $VERSION is at least the given
# SemVer (Maj.Minor[.Patch]), or CalVer (YY.MM) version.It returns 0 (success)
# if $VERSION is either unset (=latest) or newer or equal than the specified
# version, or returns 1 (fail) otherwise.
#
# examples:
#
# VERSION=23.0
# version_gte 23.0 // 0 (success)
# version_gte 20.10 // 0 (success)
# version_gte 19.03 // 0 (success)
# version_gte 26.1 // 1 (fail)
version_gte() {
if [ -z "$VERSION" ]; then
return 0
fi
version_compare "$VERSION" "$1"
}
# version_compare compares two version strings (either SemVer (Major.Minor.Path),
# or CalVer (YY.MM) version strings. It returns 0 (success) if version A is newer
# or equal than version B, or 1 (fail) otherwise. Patch releases and pre-release
# (-alpha/-beta) are not taken into account
#
# examples:
#
# version_compare 23.0.0 20.10 // 0 (success)
# version_compare 23.0 20.10 // 0 (success)
# version_compare 20.10 19.03 // 0 (success)
# version_compare 20.10 20.10 // 0 (success)
# version_compare 19.03 20.10 // 1 (fail)
version_compare() (
set +x
yy_a="$(echo "$1" | cut -d'.' -f1)"
yy_b="$(echo "$2" | cut -d'.' -f1)"
if [ "$yy_a" -lt "$yy_b" ]; then
return 1
fi
if [ "$yy_a" -gt "$yy_b" ]; then
return 0
fi
mm_a="$(echo "$1" | cut -d'.' -f2)"
mm_b="$(echo "$2" | cut -d'.' -f2)"
# trim leading zeros to accommodate CalVer
mm_a="${mm_a#0}"
mm_b="${mm_b#0}"
if [ "${mm_a:-0}" -lt "${mm_b:-0}" ]; then
return 1
fi
return 0
)
is_dry_run() {
if [ -z "$DRY_RUN" ]; then
return 1
else
return 0
fi
}
is_wsl() {
case "$(uname -r)" in
*microsoft* ) true ;; # WSL 2
*Microsoft* ) true ;; # WSL 1
* ) false;;
esac
}
is_darwin() {
case "$(uname -s)" in
*darwin* ) true ;;
*Darwin* ) true ;;
* ) false;;
esac
}
deprecation_notice() {
distro=$1
distro_version=$2
echo
printf "\033[91;1mDEPRECATION WARNING\033[0m\n"
printf " This Linux distribution (\033[1m%s %s\033[0m) reached end-of-life and is no longer supported by this script.\n" "$distro" "$distro_version"
echo " No updates or security fixes will be released for this distribution, and users are recommended"
echo " to upgrade to a currently maintained version of $distro."
echo
printf "Press \033[1mCtrl+C\033[0m now to abort this script, or wait for the installation to continue."
echo
sleep 10
}
get_distribution() {
lsb_dist=""
# Every system that we officially support has /etc/os-release
if [ -r /etc/os-release ]; then
lsb_dist="$(. /etc/os-release && echo "$ID")"
fi
# Returning an empty string here should be alright since the
# case statements don't act unless you provide an actual value
echo "$lsb_dist"
}
start_docker_daemon() {
# Use systemctl if available (for systemd-based systems)
if command_exists systemctl; then
is_dry_run || >&2 echo "Using systemd to manage Docker service"
if (
is_dry_run || set -x
$sh_c systemctl enable --now docker.service 2>/dev/null
); then
is_dry_run || echo "INFO: Docker daemon enabled and started" >&2
else
is_dry_run || echo "WARNING: unable to enable the docker service" >&2
fi
else
# No service management available (container environment)
if ! is_dry_run; then
>&2 echo "Note: Running in a container environment without service management"
>&2 echo "Docker daemon cannot be started automatically in this environment"
>&2 echo "The Docker packages have been installed successfully"
fi
fi
>&2 echo
}
echo_docker_as_nonroot() {
if is_dry_run; then
return
fi
if command_exists docker && [ -e /var/run/docker.sock ]; then
(
set -x
$sh_c 'docker version'
) || true
fi
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-EOF", spaces are kept in the output
echo
echo "================================================================================"
echo
if version_gte "20.10"; then
echo "To run Docker as a non-privileged user, consider setting up the"
echo "Docker daemon in rootless mode for your user:"
echo
echo " dockerd-rootless-setuptool.sh install"
echo
echo "Visit https://docs.docker.com/go/rootless/ to learn about rootless mode."
echo
fi
echo
echo "To run the Docker daemon as a fully privileged service, but granting non-root"
echo "users access, refer to https://docs.docker.com/go/daemon-access/"
echo
echo "WARNING: Access to the remote API on a privileged Docker daemon is equivalent"
echo " to root access on the host. Refer to the 'Docker daemon attack surface'"
echo " documentation for details: https://docs.docker.com/go/attack-surface/"
echo
echo "================================================================================"
echo
}
# Check if this is a forked Linux distro
check_forked() {
# Check for lsb_release command existence, it usually exists in forked distros
if command_exists lsb_release; then
# Check if the `-u` option is supported
set +e
lsb_release -a -u > /dev/null 2>&1
lsb_release_exit_code=$?
set -e
# Check if the command has exited successfully, it means we're in a forked distro
if [ "$lsb_release_exit_code" = "0" ]; then
# Print info about current distro
cat <<-EOF
You're using '$lsb_dist' version '$dist_version'.
EOF
# Get the upstream release info
lsb_dist=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'id' | cut -d ':' -f 2 | tr -d '[:space:]')
dist_version=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'codename' | cut -d ':' -f 2 | tr -d '[:space:]')
# Print info about upstream distro
cat <<-EOF
Upstream release is '$lsb_dist' version '$dist_version'.
EOF
else
if [ -r /etc/debian_version ] && [ "$lsb_dist" != "ubuntu" ] && [ "$lsb_dist" != "raspbian" ]; then
if [ "$lsb_dist" = "osmc" ]; then
# OSMC runs Raspbian
lsb_dist=raspbian
else
# We're Debian and don't even know it!
lsb_dist=debian
fi
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
case "$dist_version" in
13|14|forky)
dist_version="trixie"
;;
12)
dist_version="bookworm"
;;
11)
dist_version="bullseye"
;;
10)
dist_version="buster"
;;
9)
dist_version="stretch"
;;
8)
dist_version="jessie"
;;
esac
fi
fi
fi
}
do_install() {
echo "# Executing docker install script, commit: $SCRIPT_COMMIT_SHA"
if command_exists docker; then
cat >&2 <<-'EOF'
Warning: the "docker" command appears to already exist on this system.
If you already have Docker installed, this script can cause trouble, which is
why we're displaying this warning and provide the opportunity to cancel the
installation.
If you installed the current Docker package using this script and are using it
again to update Docker, you can ignore this message, but be aware that the
script resets any custom changes in the deb and rpm repo configuration
files to match the parameters passed to the script.
You may press Ctrl+C now to abort this script.
EOF
( set -x; sleep 20 )
fi
user="$(id -un 2>/dev/null || true)"
sh_c='sh -c'
if [ "$user" != 'root' ]; then
if command_exists sudo; then
sh_c='sudo -E sh -c'
elif command_exists su; then
sh_c='su -c'
else
cat >&2 <<-'EOF'
Error: this installer needs the ability to run commands as root.
We are unable to find either "sudo" or "su" available to make this happen.
EOF
exit 1
fi
fi
if is_dry_run; then
sh_c="echo"
fi
# perform some very rudimentary platform detection
lsb_dist=$( get_distribution )
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
if is_wsl; then
echo
echo "WSL DETECTED: We recommend using Docker Desktop for Windows."
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop/"
echo
cat >&2 <<-'EOF'
You may press Ctrl+C now to abort this script.
EOF
( set -x; sleep 20 )
fi
case "$lsb_dist" in
ubuntu)
if command_exists lsb_release; then
dist_version="$(lsb_release --codename | cut -f2)"
fi
if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then
dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")"
fi
;;
debian|raspbian)
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
case "$dist_version" in
13)
dist_version="trixie"
;;
12)
dist_version="bookworm"
;;
11)
dist_version="bullseye"
;;
10)
dist_version="buster"
;;
9)
dist_version="stretch"
;;
8)
dist_version="jessie"
;;
esac
;;
centos|rhel)
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
fi
;;
*)
if command_exists lsb_release; then
dist_version="$(lsb_release --release | cut -f2)"
fi
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
fi
;;
esac
# Check if this is a forked Linux distro
check_forked
# Print deprecation warnings for distro versions that recently reached EOL,
# but may still be commonly used (especially LTS versions).
case "$lsb_dist.$dist_version" in
centos.8|centos.7|rhel.7)
deprecation_notice "$lsb_dist" "$dist_version"
;;
debian.buster|debian.stretch|debian.jessie)
deprecation_notice "$lsb_dist" "$dist_version"
;;
raspbian.buster|raspbian.stretch|raspbian.jessie)
deprecation_notice "$lsb_dist" "$dist_version"
;;
ubuntu.focal|ubuntu.bionic|ubuntu.xenial|ubuntu.trusty)
deprecation_notice "$lsb_dist" "$dist_version"
;;
ubuntu.oracular|ubuntu.mantic|ubuntu.lunar|ubuntu.kinetic|ubuntu.impish|ubuntu.hirsute|ubuntu.groovy|ubuntu.eoan|ubuntu.disco|ubuntu.cosmic)
deprecation_notice "$lsb_dist" "$dist_version"
;;
fedora.*)
if [ "$dist_version" -lt 41 ]; then
deprecation_notice "$lsb_dist" "$dist_version"
fi
;;
esac
# Run setup for each distro accordingly
case "$lsb_dist" in
ubuntu|debian|raspbian)
pre_reqs="ca-certificates curl"
apt_repo="deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] $DOWNLOAD_URL/linux/$lsb_dist $dist_version $CHANNEL"
(
if ! is_dry_run; then
set -x
fi
$sh_c 'apt-get -qq update >/dev/null'
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pre_reqs >/dev/null"
$sh_c 'install -m 0755 -d /etc/apt/keyrings'
$sh_c "curl -fsSL \"$DOWNLOAD_URL/linux/$lsb_dist/gpg\" -o /etc/apt/keyrings/docker.asc"
$sh_c "chmod a+r /etc/apt/keyrings/docker.asc"
$sh_c "echo \"$apt_repo\" > /etc/apt/sources.list.d/docker.list"
$sh_c 'apt-get -qq update >/dev/null'
)
if [ "$REPO_ONLY" = "1" ]; then
exit 0
fi
pkg_version=""
if [ -n "$VERSION" ]; then
if is_dry_run; then
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
else
# Will work for incomplete versions IE (17.12), but may not actually grab the "latest" if in the test channel
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/~ce~.*/g' | sed 's/-/.*/g')"
search_command="apt-cache madison docker-ce | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
pkg_version="$($sh_c "$search_command")"
echo "INFO: Searching repository for VERSION '$VERSION'"
echo "INFO: $search_command"
if [ -z "$pkg_version" ]; then
echo
echo "ERROR: '$VERSION' not found amongst apt-cache madison results"
echo
exit 1
fi
if version_gte "18.09"; then
search_command="apt-cache madison docker-ce-cli | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
echo "INFO: $search_command"
cli_pkg_version="=$($sh_c "$search_command")"
fi
pkg_version="=$pkg_version"
fi
fi
(
pkgs="docker-ce${pkg_version%=}"
if version_gte "18.09"; then
# older versions didn't ship the cli and containerd as separate packages
pkgs="$pkgs docker-ce-cli${cli_pkg_version%=} containerd.io"
fi
if version_gte "20.10"; then
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
fi
if version_gte "23.0"; then
pkgs="$pkgs docker-buildx-plugin"
fi
if version_gte "28.2"; then
pkgs="$pkgs docker-model-plugin"
fi
if ! is_dry_run; then
set -x
fi
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pkgs >/dev/null"
)
if [ "$NO_AUTOSTART" != "1" ]; then
start_docker_daemon
fi
echo_docker_as_nonroot
exit 0
;;
centos|fedora|rhel)
if [ "$(uname -m)" = "s390x" ]; then
echo "Effective v27.5, please consult RHEL distro statement for s390x support."
exit 1
fi
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
(
if ! is_dry_run; then
set -x
fi
if command_exists dnf5; then
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
$sh_c "dnf5 config-manager addrepo --overwrite --save-filename=docker-ce.repo --from-repofile='$repo_file_url'"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "dnf5 config-manager setopt \"docker-ce-*.enabled=0\""
$sh_c "dnf5 config-manager setopt \"docker-ce-$CHANNEL.enabled=1\""
fi
$sh_c "dnf makecache"
elif command_exists dnf; then
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
$sh_c "dnf config-manager --add-repo $repo_file_url"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "dnf config-manager --set-disabled \"docker-ce-*\""
$sh_c "dnf config-manager --set-enabled \"docker-ce-$CHANNEL\""
fi
$sh_c "dnf makecache"
else
$sh_c "yum -y -q install yum-utils"
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
$sh_c "yum-config-manager --add-repo $repo_file_url"
if [ "$CHANNEL" != "stable" ]; then
$sh_c "yum-config-manager --disable \"docker-ce-*\""
$sh_c "yum-config-manager --enable \"docker-ce-$CHANNEL\""
fi
$sh_c "yum makecache"
fi
)
if [ "$REPO_ONLY" = "1" ]; then
exit 0
fi
pkg_version=""
if command_exists dnf; then
pkg_manager="dnf"
pkg_manager_flags="-y -q --best"
else
pkg_manager="yum"
pkg_manager_flags="-y -q"
fi
if [ -n "$VERSION" ]; then
if is_dry_run; then
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
else
if [ "$lsb_dist" = "fedora" ]; then
pkg_suffix="fc$dist_version"
else
pkg_suffix="el"
fi
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g').*$pkg_suffix"
search_command="$pkg_manager list --showduplicates docker-ce | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
pkg_version="$($sh_c "$search_command")"
echo "INFO: Searching repository for VERSION '$VERSION'"
echo "INFO: $search_command"
if [ -z "$pkg_version" ]; then
echo
echo "ERROR: '$VERSION' not found amongst $pkg_manager list results"
echo
exit 1
fi
if version_gte "18.09"; then
# older versions don't support a cli package
search_command="$pkg_manager list --showduplicates docker-ce-cli | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
cli_pkg_version="$($sh_c "$search_command" | cut -d':' -f 2)"
fi
# Cut out the epoch and prefix with a '-'
pkg_version="-$(echo "$pkg_version" | cut -d':' -f 2)"
fi
fi
(
pkgs="docker-ce$pkg_version"
if version_gte "18.09"; then
# older versions didn't ship the cli and containerd as separate packages
if [ -n "$cli_pkg_version" ]; then
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
else
pkgs="$pkgs docker-ce-cli containerd.io"
fi
fi
if version_gte "20.10"; then
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
fi
if version_gte "23.0"; then
pkgs="$pkgs docker-buildx-plugin docker-model-plugin"
fi
if ! is_dry_run; then
set -x
fi
$sh_c "$pkg_manager $pkg_manager_flags install $pkgs"
)
if [ "$NO_AUTOSTART" != "1" ]; then
start_docker_daemon
fi
echo_docker_as_nonroot
exit 0
;;
sles)
echo "Effective v27.5, please consult SLES distro statement for s390x support."
exit 1
;;
*)
if [ -z "$lsb_dist" ]; then
if is_darwin; then
echo
echo "ERROR: Unsupported operating system 'macOS'"
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop"
echo
exit 1
fi
fi
echo
echo "ERROR: Unsupported distribution '$lsb_dist'"
echo
exit 1
;;
esac
exit 1
}
# wrapped up in a function so that we have some protection against only getting
# half the file during "curl | sh"
do_install

60
iso-files/set-hostname Normal file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
echo " * Starting : set_hostname"
if [ ! -e /tmp/previous_install_hostname ]; then
serial=`cat /sys/class/dmi/id/product_serial | tr '[:upper:]' '[:lower:]'`
product_name=`cat /sys/class/dmi/id/product_name`
if [ "$product_name" == "Default string" ] || [ "$product_name" == "" ]; then
product_name=`cat /sys/class/dmi/id/board_name`
fi
if [ "$serial" == "0" ]; then
hostname="olg-vbox-$RANDOM"
elif [ "$product_name" == "FW4A" ] || [ "$product_name" == "FW4B" ] || [ "$product_name" == "Aptio CRB" ]; then
unique_id=`cat /sys/class/net/enp1s0/address | sed 's/://g'`
hostname="olg-$unique_id"
elif [ "$product_name" == "VP2410" ]; then
unique_id=`cat /sys/class/net/enp2s0/address | sed 's/://g'`
hostname="olg-$unique_id"
elif [ "$serial" == "" ] || [ "$serial" == "default string" ]; then
macaddress=`cat /sys/class/net/*/address | grep -v "00:00:00:00:00:00" | sort |head -n 1`
if [ "${macaddress}" != "" ]; then
unique_id=`echo ${macaddress} | sed 's/://g'`
hostname="olg-$unique_id"
else
hostname="olg-unknown-$RANDOM"
fi
elif [ "$product_name" == "VMware Virtual Platform" ]; then
if [ -e /sys/class/net/eth1/address ]; then
unique_id=`cat /sys/class/net/eth1/address | sed 's/://g'`
hostname="olg-vmware-$unique_id"
elif [ -e /sys/class/net/ens33/address ]; then
unique_id=`cat /sys/class/net/ens33/address | sed 's/://g'`
hostname="olg-vmware-$unique_id"
elif [ -e /sys/class/net/ens192/address ]; then
unique_id=`cat /sys/class/net/ens192/address | sed 's/://g'`
hostname="olg-vmware-$unique_id"
else
hostname="olg-vmware-$RANDOM"
fi
else
ipmi_mac=`ipmitool lan print | grep "MAC Address" | awk '{print $4}' | sed 's/://g'`
if [ ! -z ${ipmi_mac} ]; then
hostname="olg-$ipmi_mac"
else
hostname="olg-$serial"
fi
fi
else
hostname=`cat /tmp/previous_install_hostname`
fi
# Remove any spaces
hostname=${hostname//[[:blank:]]/}
hostnamectl set-hostname ${hostname}
echo "Product name : '$product_name'"
echo "DMI Serial : '$serial'"
echo "Got hostname : '$hostname'"
echo "network --hostname='$hostname'"
echo " * Done : set-hostname"

17
iso-files/setup-config Normal file
View File

@@ -0,0 +1,17 @@
VCPUS="4"
RAM_MB="16384"
DISK_GB="50"
WAN_IF="enp2s0f0np0"
LAN_IF="enp2s0f1np1"
ADMIN_IF="enx000ec6f01419"
INTEL_AMT_PORT="enp88s0"
OLG_ISO_PLATFORM="VM" # BAREMETAL or VM
# RouterArchitects ISO (for OpenWifi Cloud SDK mode)
ISO_URL="'https://drive.usercontent.google.com/download?id=14W0hnFhM64b8_jn1CwDWiPybntIrBzlh&confirm=t&uuid=e9d7cc8d-0a8d-483f-a650-af5ca22ace15&at=APcXIO18cq3_AFU_XdJgtU1JXTyd%3A1770305061284'"
ISO_SHA256="3d6ad7bc5b5f51566cf8353cf231c395390a509588c4c855a7bd37df275d104e"
# VyOS official ISO (for stand-alone mode)
#ISO_URL="'https://github.com/vyos/vyos-nightly-build/releases/download/2026.02.03-0027-rolling/vyos-2026.02.03-0027-rolling-generic-amd64.iso'"
#ISO_SHA256="26ed11122794d4e3a457abfbdc54bd77996966e0d7e714bf4c8f66a16f0a6673"

View File

@@ -0,0 +1,186 @@
#!/usr/bin/env bash
set -euo pipefail
# Source our configs
source /opt/staging_scripts/setup-config
# Some statis configs
VM_NAME="vyos"
IMAGES_DIR="/var/lib/libvirt/images"
ISO_PATH="$IMAGES_DIR/vyos.iso"
DISK_PATH="$IMAGES_DIR/${VM_NAME}.qcow2"
BR_WAN="br-wan"
BR_LAN="br-lan"
NETPLAN_FILE="/etc/netplan/99-vyos-bridges.yaml"
# Default: no DHCP on WAN bridge
BR_WAN_DHCP4="false"
# OLG_ISO_PLATFORM is where this ISO is running
# Valid values: BAREMETAL | VM . Extensible (later: CLOUD, etc)
if [[ "${OLG_ISO_PLATFORM}" == "VM" ]]; then
BR_WAN_DHCP4="true"
fi
# Make sure we are running as root
if [[ $EUID -ne 0 ]]; then
echo "Please run as root (sudo $0)"; exit 1
fi
# Make sure we have netplan available
command -v netplan >/dev/null 2>&1 || { echo "netplan not found. This script targets Ubuntu with netplan."; exit 1; }
# Make sure we have all the minimum required interfaces
for IFACE in "$WAN_IF" "$LAN_IF"; do
if ! ip link show "$IFACE" >/dev/null 2>&1; then
echo "Interface $IFACE not found. Adjust WAN_IF/LAN_IF in the script."; exit 1
fi
done
echo ">>> Set the host hostname"
/opt/staging_scripts/set-hostname
echo ">>> Installing virtualization packages..."
apt-get update -y
apt-get install -y qemu-kvm libvirt-daemon-system libvirt-clients virtinst bridge-utils cloud-image-utils libguestfs-tools xorriso genisoimage syslinux-utils
echo ">>> Installing Docker..."
sh /opt/staging_scripts/get-docker.sh
# Get VyOS ISO
# All Downloads/Updates should be done on stable network, before network configurations are changed
if [[ ! -f "$ISO_PATH" ]]; then
echo ">>> Downloading VyOS ISO to $ISO_PATH"
echo -e "#!/bin/bash\ncurl -fL $ISO_URL -o $ISO_PATH" >download_iso.sh
chmod +x download_iso.sh
./download_iso.sh
rm -f download_iso.sh
# Verify SHA256 checksum
echo ">>> Verifying SHA256 checksum..."
ACTUAL_SHA256=$(sha256sum "$ISO_PATH" | awk '{print $1}')
if [[ "$ACTUAL_SHA256" != "$ISO_SHA256" ]]; then
echo "ERROR: SHA256 checksum mismatch!"
echo "Expected: $ISO_SHA256"
echo "Actual: $ACTUAL_SHA256"
echo "Removing corrupted ISO file..."
rm -f "$ISO_PATH"
exit 1
fi
echo ">>> SHA256 checksum verified successfully"
else
echo ">>> VyOS ISO already present at $ISO_PATH"
# Verify SHA256 checksum of existing file
echo ">>> Verifying SHA256 checksum of existing ISO..."
ACTUAL_SHA256=$(sha256sum "$ISO_PATH" | awk '{print $1}')
if [[ "$ACTUAL_SHA256" != "$ISO_SHA256" ]]; then
echo "WARNING: SHA256 checksum mismatch for existing ISO!"
echo "Expected: $ISO_SHA256"
echo "Actual: $ACTUAL_SHA256"
echo "Removing existing ISO and re-downloading..."
rm -f "$ISO_PATH"
echo -e "#!/bin/bash\ncurl -fL $ISO_URL -o $ISO_PATH" >download_iso.sh
chmod +x download_iso.sh
./download_iso.sh
rm -f download_iso.sh
# Verify SHA256 checksum of new download
echo ">>> Verifying SHA256 checksum..."
ACTUAL_SHA256=$(sha256sum "$ISO_PATH" | awk '{print $1}')
if [[ "$ACTUAL_SHA256" != "$ISO_SHA256" ]]; then
echo "ERROR: SHA256 checksum mismatch!"
echo "Expected: $ISO_SHA256"
echo "Actual: $ACTUAL_SHA256"
echo "Removing corrupted ISO file..."
rm -f "$ISO_PATH"
exit 1
fi
echo ">>> SHA256 checksum verified successfully"
else
echo ">>> SHA256 checksum verified successfully"
fi
fi
echo ">>> Ensuring libvirtd is running..."
systemctl enable --now libvirtd
mkdir -p "$IMAGES_DIR"
# Configure host bridges via netplan
echo ">>> Writing netplan to create $BR_WAN (via $WAN_IF) and $BR_LAN (via $LAN_IF): $NETPLAN_FILE"
cat > "$NETPLAN_FILE" <<EOF
network:
version: 2
renderer: networkd
ethernets:
${WAN_IF}:
dhcp4: false
dhcp6: false
${LAN_IF}:
dhcp4: false
dhcp6: false
bridges:
${BR_WAN}:
interfaces: [${WAN_IF}]
dhcp4: ${BR_WAN_DHCP4}
dhcp6: false
parameters:
stp: false
forward-delay: 0
${BR_LAN}:
interfaces: [${LAN_IF}]
dhcp4: false
dhcp6: false
parameters:
stp: false
forward-delay: 0
EOF
chmod 600 /etc/netplan/*.yaml
echo ">>> Applying netplan (this may momentarily disrupt links on $WAN_IF/$LAN_IF)..."
netplan apply
# System settings
echo br_netfilter | tee /etc/modules-load.d/br_netfilter.conf
modprobe br_netfilter
tee /etc/sysctl.d/99-bridge-nf-off.conf >/dev/null <<'EOF'
net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-arptables=0
EOF
sysctl --system
# Create an ISO with out example config files
mkisofs -joliet -rock -volid "cidata" -output /var/lib/libvirt/boot/vyos-configs.iso /opt/staging_scripts/vyos-configs/vyos-factory-config
# Create VM disk
if [[ ! -f "$DISK_PATH" ]]; then
echo ">>> Creating disk $DISK_PATH (${DISK_GB}G)"
qemu-img create -f qcow2 "$DISK_PATH" "${DISK_GB}G"
else
echo ">>> Disk already exists at $DISK_PATH"
fi
# If a previous domain with the same name exists, define a new one will fail.
if virsh dominfo "$VM_NAME" >/dev/null 2>&1; then
echo ">>> A VM named '$VM_NAME' already exists. Skipping creation."
echo "You can start it with: virsh start $VM_NAME && virsh console $VM_NAME"
exit 0
fi
# Create & start VM
echo ">>> Creating VyOS VM '$VM_NAME'..."
virt-install -n "$VM_NAME" \
--cpu host --ram "$RAM_MB" \
--vcpus "$VCPUS" \
--cdrom "$ISO_PATH" \
--os-variant debian12 \
--network bridge="$BR_WAN",model=virtio \
--network bridge="$BR_LAN",model=virtio \
--graphics vnc \
--hvm \
--virt-type kvm \
--disk path="$DISK_PATH",bus=virtio,size="$DISK_GB" \
--disk /var/lib/libvirt/boot/vyos-configs.iso,device=cdrom \
--noautoconsole
# Set the VM o autostart on host boot
virsh autostart $VM_NAME

View File

@@ -0,0 +1,288 @@
#!/usr/bin/env bash
set -euo pipefail
# Source our configs
source /opt/staging_scripts/setup-config
# Some statis configs
VM_NAME="vyos"
IMAGES_DIR="/var/lib/libvirt/images"
ISO_PATH="$IMAGES_DIR/vyos.iso"
DISK_PATH="$IMAGES_DIR/${VM_NAME}.qcow2"
# Make sure we are running as root
if [[ $EUID -ne 0 ]]; then
echo "Please run as root (sudo $0)"; exit 1
fi
# Make sure we have all the minimum required interfaces
for IFACE in "$WAN_IF" "$LAN_IF"; do
if ! ip link show "$IFACE" >/dev/null 2>&1; then
echo "Interface $IFACE not found. Adjust WAN_IF/LAN_IF in the script."; exit 1
fi
done
echo ">>> Set the host hostname"
/opt/staging_scripts/set-hostname
echo ">>> Installing virtualization packages..."
apt-get update -y
apt-get install -y qemu-kvm libvirt-daemon-system libvirt-clients virtinst bridge-utils cloud-image-utils libguestfs-tools xorriso genisoimage syslinux-utils
echo ">>> Ensuring libvirtd is running..."
systemctl enable --now libvirtd
echo ">>> Installing Docker..."
sh /opt/staging_scripts/get-docker.sh
mkdir -p "$IMAGES_DIR"
# Get PCI addresses for interfaces
echo ">>> Detecting PCI addresses for network interfaces..."
WAN_PCI=$(basename $(readlink -f /sys/class/net/$WAN_IF/device))
LAN_PCI=$(basename $(readlink -f /sys/class/net/$LAN_IF/device))
if [[ -z "$WAN_PCI" ]] || [[ -z "$LAN_PCI" ]]; then
echo "ERROR: Could not determine PCI addresses for interfaces"
echo "WAN_IF ($WAN_IF): $WAN_PCI"
echo "LAN_IF ($LAN_IF): $LAN_PCI"
exit 1
fi
echo "WAN interface $WAN_IF is at PCI address: $WAN_PCI"
echo "LAN interface $LAN_IF is at PCI address: $LAN_PCI"
# Parse PCI address (format: 0000:04:00.0 -> domain:bus:slot.function)
WAN_DOMAIN=$(echo $WAN_PCI | cut -d: -f1)
WAN_BUS=$(echo $WAN_PCI | cut -d: -f2)
WAN_SLOT=$(echo $WAN_PCI | cut -d: -f3 | cut -d. -f1)
WAN_FUNC=$(echo $WAN_PCI | cut -d. -f2)
LAN_DOMAIN=$(echo $LAN_PCI | cut -d: -f1)
LAN_BUS=$(echo $LAN_PCI | cut -d: -f2)
LAN_SLOT=$(echo $LAN_PCI | cut -d: -f3 | cut -d. -f1)
LAN_FUNC=$(echo $LAN_PCI | cut -d. -f2)
# Enable IOMMU and VFIO
echo ">>> Checking IOMMU support..."
if ! dmesg | grep -q "IOMMU enabled"; then
echo "WARNING: IOMMU may not be enabled. You may need to:"
echo " 1. Enable VT-d/AMD-Vi in BIOS"
echo " 2. Add 'intel_iommu=on' or 'amd_iommu=on' to kernel parameters"
echo " 3. Reboot the system"
echo ""
read -p "Continue anyway? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
echo ">>> Configuring VFIO for PCI passthrough..."
# Load VFIO modules
modprobe vfio
modprobe vfio_pci
modprobe vfio_iommu_type1
# Ensure modules load on boot
cat > /etc/modules-load.d/vfio.conf <<EOF
vfio
vfio_pci
vfio_iommu_type_1
EOF
# Get vendor and device IDs for the interfaces
WAN_VENDOR_DEVICE=$(lspci -n -s $WAN_PCI | awk '{print $3}')
LAN_VENDOR_DEVICE=$(lspci -n -s $LAN_PCI | awk '{print $3}')
echo "WAN device ID: $WAN_VENDOR_DEVICE"
echo "LAN device ID: $LAN_VENDOR_DEVICE"
# Unbind interfaces from current driver and bind to vfio-pci
echo ">>> Unbinding interfaces from host..."
for IFACE in "$WAN_IF" "$LAN_IF"; do
if [[ -e "/sys/class/net/$IFACE" ]]; then
ip link set $IFACE down
DRIVER_PATH=$(readlink -f /sys/class/net/$IFACE/device/driver)
if [[ -n "$DRIVER_PATH" ]]; then
DRIVER_NAME=$(basename $DRIVER_PATH)
PCI_ADDR=$(basename $(readlink -f /sys/class/net/$IFACE/device))
echo "Unbinding $IFACE ($PCI_ADDR) from $DRIVER_NAME"
echo "$PCI_ADDR" > /sys/bus/pci/drivers/$DRIVER_NAME/unbind 2>/dev/null || true
fi
fi
done
# Bind to vfio-pci
echo ">>> Binding interfaces to vfio-pci..."
for PCI_ADDR in "$WAN_PCI" "$LAN_PCI"; do
if [[ ! -e "/sys/bus/pci/drivers/vfio-pci/$PCI_ADDR" ]]; then
echo "$PCI_ADDR" > /sys/bus/pci/drivers/vfio-pci/bind 2>/dev/null || {
# If bind fails, try adding the device ID first
VENDOR_DEVICE=$(lspci -n -s $PCI_ADDR | awk '{print $3}')
echo "$VENDOR_DEVICE" > /sys/bus/pci/drivers/vfio-pci/new_id 2>/dev/null || true
echo "$PCI_ADDR" > /sys/bus/pci/drivers/vfio-pci/bind 2>/dev/null || true
}
fi
echo "Bound $PCI_ADDR to vfio-pci"
done
# Get VyOS ISO
if [[ ! -f "$ISO_PATH" ]]; then
echo ">>> Downloading VyOS ISO to $ISO_PATH"
echo -e "#!/bin/bash\ncurl -fL $ISO_URL -o $ISO_PATH" >download_iso.sh
chmod +x download_iso.sh
./download_iso.sh
rm -f download_iso.sh
# Verify SHA256 checksum
echo ">>> Verifying SHA256 checksum..."
ACTUAL_SHA256=$(sha256sum "$ISO_PATH" | awk '{print $1}')
if [[ "$ACTUAL_SHA256" != "$ISO_SHA256" ]]; then
echo "ERROR: SHA256 checksum mismatch!"
echo "Expected: $ISO_SHA256"
echo "Actual: $ACTUAL_SHA256"
echo "Removing corrupted ISO file..."
rm -f "$ISO_PATH"
exit 1
fi
echo ">>> SHA256 checksum verified successfully"
else
echo ">>> VyOS ISO already present at $ISO_PATH"
# Verify SHA256 checksum of existing file
echo ">>> Verifying SHA256 checksum of existing ISO..."
ACTUAL_SHA256=$(sha256sum "$ISO_PATH" | awk '{print $1}')
if [[ "$ACTUAL_SHA256" != "$ISO_SHA256" ]]; then
echo "WARNING: SHA256 checksum mismatch for existing ISO!"
echo "Expected: $ISO_SHA256"
echo "Actual: $ACTUAL_SHA256"
echo "Removing existing ISO and re-downloading..."
rm -f "$ISO_PATH"
echo -e "#!/bin/bash\ncurl -fL $ISO_URL -o $ISO_PATH" >download_iso.sh
chmod +x download_iso.sh
./download_iso.sh
rm -f download_iso.sh
# Verify SHA256 checksum of new download
echo ">>> Verifying SHA256 checksum..."
ACTUAL_SHA256=$(sha256sum "$ISO_PATH" | awk '{print $1}')
if [[ "$ACTUAL_SHA256" != "$ISO_SHA256" ]]; then
echo "ERROR: SHA256 checksum mismatch!"
echo "Expected: $ISO_SHA256"
echo "Actual: $ACTUAL_SHA256"
echo "Removing corrupted ISO file..."
rm -f "$ISO_PATH"
exit 1
fi
echo ">>> SHA256 checksum verified successfully"
else
echo ">>> SHA256 checksum verified successfully"
fi
fi
# Create an ISO with out example config files
mkisofs -joliet -rock -volid "cidata" -output /var/lib/libvirt/boot/vyos-configs.iso /opt/staging_scripts/vyos-configs/vyos-factory-config
# Create VM disk
if [[ ! -f "$DISK_PATH" ]]; then
echo ">>> Creating disk $DISK_PATH (${DISK_GB}G)"
qemu-img create -f qcow2 "$DISK_PATH" "${DISK_GB}G"
else
echo ">>> Disk already exists at $DISK_PATH"
fi
# If a previous domain with the same name exists, define a new one will fail.
if virsh dominfo "$VM_NAME" >/dev/null 2>&1; then
echo ">>> A VM named '$VM_NAME' already exists. Skipping creation."
echo "You can start it with: virsh start $VM_NAME && virsh console $VM_NAME"
exit 0
fi
# Create & start VM with PCI passthrough and host CPU
echo ">>> Creating VyOS VM '$VM_NAME' with host CPU type and PCI passthrough..."
# Create a temporary XML file for the VM
TEMP_XML=$(mktemp)
cat > "$TEMP_XML" <<EOF
<domain type='kvm'>
<name>$VM_NAME</name>
<memory unit='MiB'>$RAM_MB</memory>
<vcpu placement='static'>$VCPUS</vcpu>
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<boot dev='cdrom'/>
<boot dev='hd'/>
</os>
<features>
<acpi/>
<apic/>
</features>
<cpu mode='host-passthrough' check='none'>
<topology sockets='1' cores='$VCPUS' threads='1'/>
</cpu>
<clock offset='utc'>
<timer name='rtc' tickpolicy='catchup'/>
<timer name='pit' tickpolicy='delay'/>
<timer name='hpet' present='no'/>
</clock>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='$DISK_PATH'/>
<target dev='vda' bus='virtio'/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file='$ISO_PATH'/>
<target dev='hdc' bus='ide'/>
<readonly/>
</disk>
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/boot/vyos-configs.iso'/>
<target dev='sdb' bus='sata'/>
<readonly/>
</disk>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x$WAN_DOMAIN' bus='0x$WAN_BUS' slot='0x$WAN_SLOT' function='0x$WAN_FUNC'/>
</source>
</hostdev>
<hostdev mode='subsystem' type='pci' managed='yes'>
<source>
<address domain='0x$LAN_DOMAIN' bus='0x$LAN_BUS' slot='0x$LAN_SLOT' function='0x$LAN_FUNC'/>
</source>
</hostdev>
<controller type='usb' index='0' model='ich9-ehci1'/>
<controller type='pci' index='0' model='pci-root'/>
<serial type='pty'>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
</serial>
<console type='pty'>
<target type='serial' port='0'/>
</console>
<graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1'>
<listen type='address' address='127.0.0.1'/>
</graphics>
<video>
<model type='cirrus'/>
</video>
</devices>
</domain>
EOF
echo ">>> Defining VM from XML..."
virsh define "$TEMP_XML"
rm "$TEMP_XML"
# Set the VM o autostart on host boot
virsh autostart $VM_NAME

134
iso-files/ucentral-setup.sh Executable file
View File

@@ -0,0 +1,134 @@
#!/usr/bin/env bash
set -e
ACTION="$1"
# ================= CONFIG =================
CONTAINER="ucentral-olg"
IMAGE="mjhnetexp/ucentral-client:olgV8"
BRIDGE="br-wan"
HOST_VETH="veth-${CONTAINER:0:5}-h"
CONT_VETH="veth-${CONTAINER:0:5}-c"
CONT_IF="eth0"
DOCKER_RUN_OPTS="--privileged --network none"
# ==========================================
usage() {
echo "Usage: $0 setup | cleanup | shell"
exit 1
}
[ -z "$ACTION" ] && usage
container_pid() {
docker inspect -f '{{.State.Pid}}' "$CONTAINER" 2>/dev/null
}
container_exists() {
docker inspect "$CONTAINER" &>/dev/null
}
container_running() {
docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null | grep -q true
}
veth_exists() {
ip link show "$HOST_VETH" &>/dev/null
}
attached_to_bridge() {
bridge link show | grep -q "$HOST_VETH"
}
setup() {
echo "[+] Setup container on $BRIDGE"
if ! container_exists; then
echo "[+] Creating container $CONTAINER"
docker run -dit --name "$CONTAINER" $DOCKER_RUN_OPTS "$IMAGE"
fi
if ! container_running; then
echo "[+] Starting container"
docker start "$CONTAINER"
fi
PID=$(container_pid)
[ -z "$PID" ] && { echo "Failed to get container PID"; exit 1; }
if veth_exists && attached_to_bridge; then
echo "[!] Setup already done: container already attached to $BRIDGE"
exit 0
fi
echo "[+] Creating veth pair"
ip link add "$HOST_VETH" type veth peer name "$CONT_VETH"
echo "[+] Attaching host veth to bridge $BRIDGE"
ip link set "$HOST_VETH" master "$BRIDGE"
ip link set "$HOST_VETH" up
echo "[+] Moving container veth into netns"
ip link set "$CONT_VETH" netns "$PID"
echo "[+] Configuring container interface"
nsenter -t "$PID" -n -m -p sh <<EOF
ip link set lo up
ip link set "$CONT_VETH" name "$CONT_IF"
ip link set "$CONT_IF" up
udhcpc -i "$CONT_IF" -b -p /var/run/udhcpc.eth0.pid -s /usr/share/udhcpc/default.script
ubusd &
EOF
echo "[✓] Setup complete"
}
cleanup() {
local did_something=false
if veth_exists; then
echo "[+] Removing veth"
ip link del "$HOST_VETH"
did_something=true
fi
if container_exists; then
echo "[+] Stopping container"
docker stop "$CONTAINER" || true
echo "[+] Removing container"
docker rm "$CONTAINER" || true
did_something=true
fi
if ! $did_something; then
echo "[!] Nothing to cleanup"
else
echo "[✓] Cleanup complete"
fi
}
shell() {
if ! container_exists; then
echo "Container $CONTAINER does not exist"
exit 1
fi
if ! container_running; then
echo "Container $CONTAINER is not running"
exit 1
fi
echo "[+] Opening shell in $CONTAINER"
exec docker exec -it "$CONTAINER" /bin/ash
}
case "$ACTION" in
setup) setup ;;
cleanup) cleanup ;;
shell) shell ;;
*) usage ;;
esac

25
iso-files/user-data.olg Normal file
View File

@@ -0,0 +1,25 @@
#cloud-config
version: 1
identity:
hostname: olg-staging
password: "$y$j9T$Ogm02oaDNP6IIEhxrdLE21$vOQzeg.eQ7PYd0NsBxGhimAQNM6qYo41PIsVratZaV2"
username: olgadm
package_update: false
package_upgrade: false
ssh:
allow-pw: false
authorized-keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqf+y26ldt63J9/ZfskTrwy9AU5ZXJkDzWYH5rDisZRc5JZ8Bn9Uj2KV0k7j6XLyQP8N6anoUepxaVuA0eqOSbpSgX5pgg2qCqPy0bbIbpPrEn0XESvds7+0hY2ekPHlL6uawoICkUskLo6MKC/69lFaeIrkC5li4omYUzzZhN5wpdk65UUM12z71HycF7sg2RHBmJSKtjrZGFdIZFVk1zj3OmOwk52GLdv4rJ+DPtrnhlvaJqqSTT4aWRJTpkGwRrzLeDVr2JoQ6QY1PPOtrrl2bxmvlmPbrA/Up4SpnE+0ZlMk7Td5qnfP1JQmRA4yHgwuWra8IUNVPNN+0qKXGaw== andre.courchesne@intello.com
install-server: true
late-commands:
- echo "Open LAN Gateway (Installed using ISO {{ ISO_VERSION }})" >/target/root/olg_release
- mkdir /target/opt/staging_scripts
- cp -r /cdrom/olg_files/* /target/opt/staging_scripts
- chmod -R +x /target/opt/staging_scripts/*
- cat /target/opt/staging_scripts/admin-bashrc-additions >> /target/etc/bash.bashrc
- echo 'olgadm ALL=(ALL) NOPASSWD:ALL' > /target/etc/sudoers.d/olgadm
- curtin in-target --target /target systemctl disable cloud-init
- curtin in-target --target /target /opt/staging_scripts/set-hostname >>/target/root/set-hostname.log
- cp /target/opt/staging_scripts/01-dhcp-host-admin.yaml /target/etc/netplan/01-dhcp-host-admin.yaml
# shut-down the host to avoid an infinite installer loop
- shutdown -h now

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,62 @@
set interfaces ethernet eth0 address 'dhcp'
set interfaces ethernet eth0 description 'WAN'
set interfaces ethernet eth0 offload gro
set interfaces ethernet eth0 offload gso
set interfaces ethernet eth0 offload sg
set interfaces ethernet eth0 offload tso
set interfaces ethernet eth1 description 'LAN'
set interfaces ethernet eth1 offload gro
set interfaces ethernet eth1 offload gso
set interfaces ethernet eth1 offload sg
set interfaces ethernet eth1 offload tso
set interfaces ethernet eth1 vif 100 address '192.168.100.1/24'
set interfaces ethernet eth1 vif 100 description 'LAN VLAN 100 (Switches)'
set interfaces ethernet eth1 vif 101 address '192.168.101.1/24'
set interfaces ethernet eth1 vif 101 description 'LAN VLAN 101 (APs)'
set interfaces ethernet eth1 vif 1000 address '172.20.0.1/20'
set interfaces ethernet eth1 vif 1000 description 'LAN VLAN 1000 (Guest devices)'
set interfaces loopback lo
set nat source rule 100 description 'NAT out WAN'
set nat source rule 100 outbound-interface name 'eth0'
set nat source rule 100 source address '172.20.0.0/20'
set nat source rule 100 translation address 'masquerade'
set service dhcp-server shared-network-name VLAN100 authoritative
set service dhcp-server shared-network-name VLAN100 subnet 192.168.100.0/24 option default-router '192.168.100.1'
set service dhcp-server shared-network-name VLAN100 subnet 192.168.100.0/24 option name-server '192.168.100.1'
set service dhcp-server shared-network-name VLAN100 subnet 192.168.100.0/24 range 0 start '192.168.100.50'
set service dhcp-server shared-network-name VLAN100 subnet 192.168.100.0/24 range 0 stop '192.168.100.200'
set service dhcp-server shared-network-name VLAN100 subnet 192.168.100.0/24 subnet-id '100'
set service dhcp-server shared-network-name VLAN101 authoritative
set service dhcp-server shared-network-name VLAN101 subnet 192.168.101.0/24 option default-router '192.168.101.1'
set service dhcp-server shared-network-name VLAN101 subnet 192.168.101.0/24 option domain-name 'lan101.local'
set service dhcp-server shared-network-name VLAN101 subnet 192.168.101.0/24 option name-server '192.168.101.1'
set service dhcp-server shared-network-name VLAN101 subnet 192.168.101.0/24 range 0 start '192.168.101.50'
set service dhcp-server shared-network-name VLAN101 subnet 192.168.101.0/24 range 0 stop '192.168.101.200'
set service dhcp-server shared-network-name VLAN101 subnet 192.168.101.0/24 subnet-id '101'
set service dhcp-server shared-network-name VLAN1000 authoritative
set service dhcp-server shared-network-name VLAN1000 subnet 172.20.0.0/20 option default-router '172.20.0.1'
set service dhcp-server shared-network-name VLAN1000 subnet 172.20.0.0/20 option domain-name 'lan200.local'
set service dhcp-server shared-network-name VLAN1000 subnet 172.20.0.0/20 option name-server '172.20.0.1'
set service dhcp-server shared-network-name VLAN1000 subnet 172.20.0.0/20 range 0 start '172.20.0.50'
set service dhcp-server shared-network-name VLAN1000 subnet 172.20.0.0/20 range 0 stop '172.20.15.200'
set service dhcp-server shared-network-name VLAN1000 subnet 172.20.0.0/20 subnet-id '1000'
set service dns forwarding allow-from '192.168.100.0/24'
set service dns forwarding allow-from '192.168.101.0/24'
set service dns forwarding allow-from '172.20.0.0/20'
set service dns forwarding listen-address '192.168.100.1'
set service dns forwarding listen-address '192.168.101.1'
set service dns forwarding listen-address '172.20.0.1'
set service dns forwarding name-server 1.1.1.1
set service dns forwarding name-server 9.9.9.9
set service ntp allow-client address '127.0.0.0/8'
set service ntp allow-client address '169.254.0.0/16'
set service ntp allow-client address '10.0.0.0/8'
set service ntp allow-client address '172.16.0.0/12'
set service ntp allow-client address '192.168.0.0/16'
set service ntp allow-client address '::1/128'
set service ntp allow-client address 'fe80::/10'
set service ntp allow-client address 'fc00::/7'
set service ntp server time1.vyos.net
set service ntp server time2.vyos.net
set service ntp server time3.vyos.net
set service ssh port 22

View File

@@ -1,8 +0,0 @@
.PHONY: all purge
all:
./dock-run.sh ./build.sh $(TARGET)
purge:
cd openwrt && rm -rf * && rm -rf .*
@echo Done

View File

@@ -1,83 +0,0 @@
# 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

View File

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

View File

@@ -1,7 +0,0 @@
repo: https://github.com/openwrt/openwrt.git
branch: openwrt-23.05
revision: e92cf0c46ffe3ac7fca936c18577bfb19eb4ce9e
output_dir: ./output
patch_folders:
- patches

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
#!/bin/sh
/usr/share/ucentral/cloud_discovery.uc $1

View File

@@ -1,519 +0,0 @@
#!/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();

View File

@@ -1,303 +0,0 @@
#!/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());
}

View File

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

View File

@@ -1,17 +0,0 @@
#
# 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

View File

@@ -1,98 +0,0 @@
'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,
});

View File

@@ -1,70 +0,0 @@
'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
});

View File

@@ -1,121 +0,0 @@
'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
});

View File

@@ -1,210 +0,0 @@
'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
});

View File

@@ -1,138 +0,0 @@
'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
});

View File

@@ -1,426 +0,0 @@
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"

View File

@@ -1,385 +0,0 @@
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 ""

View File

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

View File

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

View File

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

View File

@@ -1,62 +0,0 @@
{
"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" ]
}
}
}

View File

@@ -1,36 +0,0 @@
{
"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" ]
}
}
}
}

View File

@@ -1,14 +0,0 @@
#
# 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

View File

@@ -1,152 +0,0 @@
'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');
}
}
});

View File

@@ -1,140 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -1,12 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 582 B

View File

@@ -1,13 +0,0 @@
<%#
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>

View File

@@ -1,67 +0,0 @@
<%#
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>

View File

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

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
#!/bin/sh /etc/rc.common
START=80
boot() {
echo $(cat /etc/openwrt_release | grep DISTRIB_TIP= | cut -d\' -f2) > /tmp/ucentral.version
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,90 +0,0 @@
// 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

View File

@@ -1,108 +0,0 @@
// 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);
}

View File

@@ -1,62 +0,0 @@
// 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;
}

View File

@@ -1,174 +0,0 @@
// 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");
}

View File

@@ -1,164 +0,0 @@
// 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);
}

View File

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

View File

@@ -1,3 +0,0 @@
config defaults
# example for blacklisting individual devices or bridges
# list blacklist eth0

View File

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

View File

@@ -1,15 +0,0 @@
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);
}

View File

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

View File

@@ -1,5 +0,0 @@
#config network
# list ports 'lan1'
# list ports 'lan2'
# list ports 'lan3'
# list ports 'lan4'

View File

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

View File

@@ -1,307 +0,0 @@
#!/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();

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,44 +0,0 @@
# 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,215 +0,0 @@
/*
* 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;
}

View File

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

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