mirror of
https://github.com/Telecominfraproject/olg-installer.git
synced 2026-03-20 03:39:11 +00:00
Compare commits
4 Commits
main
...
minimal-vy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29e3068c0e | ||
|
|
e74f615c36 | ||
|
|
a9173bf602 | ||
|
|
e43625c24e |
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,14 +0,0 @@
|
||||
.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
19
CHANGELOG.md
@@ -1,19 +0,0 @@
|
||||
# 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
133
README.md
@@ -1,130 +1,5 @@
|
||||
# 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.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
# OLG Ucentral Client SDK
|
||||
|
||||
This is a sdk to build ucentral client image to support olg schema and configure VYOS. It can be used to validate OLG's end-to-end working, from accepting configuration from the Cloud controller to configuring VyOS.
|
||||
- vyos/config_prepare.uc: Conversion from templates to VyOS text style configuration.
|
||||
- vyos/https_server_api.uc: This will call VyOS retrieve and load APIs.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 57 KiB |
12
grub.cfg
12
grub.cfg
@@ -1,12 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
network:
|
||||
version: 2
|
||||
ethernets:
|
||||
{{ HOST_ADMIN_PORT }}: # Admin port
|
||||
dhcp4: true
|
||||
dhcp6: false
|
||||
@@ -1,20 +0,0 @@
|
||||
# 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
|
||||
@@ -1,115 +0,0 @@
|
||||
#!/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 ""
|
||||
@@ -1,764 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/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"
|
||||
@@ -1,17 +0,0 @@
|
||||
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"
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,288 +0,0 @@
|
||||
#!/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
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,25 +0,0 @@
|
||||
#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
@@ -1,62 +0,0 @@
|
||||
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
|
||||
8
olg-ucentral-client-sdk/Makefile
Normal file
8
olg-ucentral-client-sdk/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
.PHONY: all purge
|
||||
|
||||
all:
|
||||
./dock-run.sh ./build.sh $(TARGET)
|
||||
|
||||
purge:
|
||||
cd openwrt && rm -rf * && rm -rf .*
|
||||
@echo Done
|
||||
83
olg-ucentral-client-sdk/README.md
Normal file
83
olg-ucentral-client-sdk/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# OpenWiFi AP NOS
|
||||
|
||||
OpenWrt-based access point network operating system (AP NOS) for TIP OpenWiFi.
|
||||
Read more at [openwifi.tip.build](https://openwifi.tip.build/).
|
||||
|
||||
## Building
|
||||
|
||||
### Setting up your build machine
|
||||
|
||||
Building requires a recent Linux installation. Older systems without Python 3.7
|
||||
will have trouble. See this guide for details:
|
||||
https://openwrt.org/docs/guide-developer/toolchain/beginners-build-guide
|
||||
|
||||
Install build packages on Debian/Ubuntu (or see above guide for other systems):
|
||||
```
|
||||
sudo apt install build-essential libncurses5-dev gawk git libssl-dev gettext zlib1g-dev swig unzip time rsync python3 python3-setuptools python3-yaml
|
||||
```
|
||||
|
||||
### Doing a native build on Linux
|
||||
|
||||
Use `./build.sh <target>`, or follow the manual steps below:
|
||||
|
||||
1. Clone and set up the tree. This will create an `openwrt/` directory.
|
||||
```shell
|
||||
./setup.py --setup # for subsequent builds, use --rebase instead
|
||||
```
|
||||
|
||||
2. Select the profile and base package selection. This setup will install the
|
||||
feeds and packages and generate the `.config` file.
|
||||
```shell
|
||||
cd openwrt
|
||||
./scripts/gen_config.py linksys_ea8300
|
||||
```
|
||||
|
||||
3. Build the tree (replace `-j 8` with the number of cores to use).
|
||||
```shell
|
||||
make -j 8 V=s
|
||||
```
|
||||
|
||||
### Build output
|
||||
|
||||
The build results are located in the `openwrt/bin/` directory:
|
||||
|
||||
| Type | Path |
|
||||
| ---------------- | ---------------------------------------------------- |
|
||||
| Firmware images | `openwrt/bin/targets/<target>/<subtarget>/` |
|
||||
| Kernel modules | `openwrt/bin/targets/<target>/<subtarget>/packages/` |
|
||||
| Package binaries | `openwrt/bin/packages/<platform>/<feed>/` |
|
||||
|
||||
## Developer Notes
|
||||
|
||||
### Branching model
|
||||
|
||||
- `main` - Stable dev branch
|
||||
- `next` - Integration branch
|
||||
- `staging-*` - Feature/bug branches
|
||||
- `release/v#.#.#` - Release branches (*major.minor.patch*)
|
||||
|
||||
### Repository structure
|
||||
|
||||
Build files:
|
||||
- `Makefile` - Calls Docker environment per target
|
||||
- `dock-run.sh` - Dockerized build environment
|
||||
- `docker/Dockerfile` - Dockerfile for build image
|
||||
- `build.sh` - Build script
|
||||
- `setup.py` - Clone and set up the tree
|
||||
- `config.yml` - Specifies OpenWrt version and patches to apply
|
||||
|
||||
Directories:
|
||||
- `feeds/` - OpenWiFi feeds
|
||||
- `patches/` - OpenWiFi patches applied during builds
|
||||
- `profiles/` - Per-target kernel configs, packages, and feeds
|
||||
- [wifi-ax](profiles/wifi-ax.yml): Wi-Fi AX packages
|
||||
- [ucentral-ap](profiles/ucentral-ap.yml): uCentral packages
|
||||
- [x64_vm](profiles/x64_vm.yml): x86-64 VM image
|
||||
|
||||
### uCentral packages
|
||||
|
||||
AP-NOS packages implementing the uCentral protocol include the following
|
||||
repositories (refer to the [ucentral](feeds/ucentral/) feed for a full list):
|
||||
- ucentral-client: https://github.com/Telecominfraproject/wlan-ucentral-client
|
||||
- ucentral-schema: https://github.com/Telecominfraproject/wlan-ucentral-schema
|
||||
- ucentral-wifi: https://github.com/blogic/ucentral-wifi
|
||||
27
olg-ucentral-client-sdk/build.sh
Executable file
27
olg-ucentral-client-sdk/build.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
ROOT_PATH=${PWD}
|
||||
BUILD_DIR=${ROOT_PATH}/openwrt
|
||||
TARGET=${1}
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Error: please specify TARGET"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! "$(ls -A $BUILD_DIR)" ]; then
|
||||
python3 setup.py --setup || exit 1
|
||||
|
||||
else
|
||||
python3 setup.py --rebase
|
||||
echo "### OpenWrt repo already setup"
|
||||
fi
|
||||
|
||||
cd ${BUILD_DIR}
|
||||
./scripts/gen_config.py ${TARGET} || exit 1
|
||||
cd -
|
||||
|
||||
echo "### Building image ..."
|
||||
cd $BUILD_DIR
|
||||
make -j$(nproc) V=s
|
||||
7
olg-ucentral-client-sdk/config.yml
Normal file
7
olg-ucentral-client-sdk/config.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
repo: https://github.com/openwrt/openwrt.git
|
||||
branch: openwrt-23.05
|
||||
revision: e92cf0c46ffe3ac7fca936c18577bfb19eb4ce9e
|
||||
output_dir: ./output
|
||||
|
||||
patch_folders:
|
||||
- patches
|
||||
9
olg-ucentral-client-sdk/dock-run.sh
Executable file
9
olg-ucentral-client-sdk/dock-run.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
tag=$(echo ${PWD} | tr / - | cut -b2- | tr A-Z a-z)
|
||||
groups=$(id -G | xargs -n1 echo -n " --group-add ")
|
||||
params="-v ${PWD}:${PWD} --rm -w ${PWD} -u"$(id -u):$(id -g)" $groups -v/etc/passwd:/etc/passwd:ro -v/etc/group:/etc/group:ro -v$HOME/.gitconfig:$HOME/.gitconfig:ro ${tag}"
|
||||
|
||||
docker build --tag=${tag} docker
|
||||
|
||||
docker run $params $@
|
||||
12
olg-ucentral-client-sdk/docker/Dockerfile
Normal file
12
olg-ucentral-client-sdk/docker/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata \
|
||||
&& apt-get install -y \
|
||||
time git-core build-essential gcc-multilib clang \
|
||||
libncurses5-dev zlib1g-dev gawk flex gettext wget unzip python \
|
||||
python3 python3-pip python3-yaml libssl-dev rsync \
|
||||
&& apt-get clean
|
||||
RUN git config --global user.email "you@example.com"
|
||||
RUN git config --global user.name "Your Name"
|
||||
RUN pip3 install kconfiglib
|
||||
30
olg-ucentral-client-sdk/feeds/tip/certificates/Makefile
Normal file
30
olg-ucentral-client-sdk/feeds/tip/certificates/Makefile
Normal file
@@ -0,0 +1,30 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=certificates
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/certificates
|
||||
SECTION:=ucentral
|
||||
CATEGORY:=uCentral
|
||||
TITLE:=TIP DigiCer certificate store
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Compile/Default
|
||||
|
||||
endef
|
||||
Build/Compile = $(Build/Compile/Default)
|
||||
|
||||
define Package/certificates/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,certificates))
|
||||
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/etc/init.d/early_boot
Executable file
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/etc/init.d/early_boot
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=09
|
||||
|
||||
copy_certificates() {
|
||||
[ -f /certificates/key.pem ] || return
|
||||
|
||||
cp /certificates/*.pem /certificates/*.ca /etc/ucentral/ 2>/dev/null || true
|
||||
chown root.network /etc/ucentral/*.pem /etc/ucentral/*.ca
|
||||
chmod 0440 root.network /etc/ucentral/*.pem /etc/ucentral/*.ca
|
||||
[ -f /certificates/gateway.json ] && cp /certificates/gateway.json /etc/ucentral/gateway.flash
|
||||
[ -f /certificates/restrictions.json ] && cp /certificates/restrictions.json /etc/ucentral/
|
||||
[ -f /certificates/sign_pubkey.pem ] && cp /certificates/sign_pubkey.pem /etc/ucentral/
|
||||
country=`cat /certificates/ucentral.defaults | jsonfilter -e '@.country'`
|
||||
[ -z "$country" -a -f /etc/uci-defaults/30_uboot-envtools ] && {
|
||||
(. /etc/uci-defaults/30_uboot-envtools)
|
||||
fw_printenv
|
||||
country=$(fw_printenv -n country)
|
||||
}
|
||||
[ -z "$country" ] && country=US
|
||||
echo "options cfg80211 ieee80211_regdom="$country > /etc/modules.conf
|
||||
echo -n $country > /etc/ucentral/country
|
||||
sync
|
||||
exit 0
|
||||
}
|
||||
|
||||
boot() {
|
||||
case "$(board_name)" in
|
||||
sonicfi,rap6*)
|
||||
touch /tmp/squashfs
|
||||
;;
|
||||
esac
|
||||
[ -f /etc/ucentral/key.pem ] && return
|
||||
/usr/bin/mount_certs
|
||||
copy_certificates
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
uci add system certificates
|
||||
uci set system.@certificates[-1].key=/etc/ucentral/key.pem
|
||||
uci set system.@certificates[-1].cert=/etc/ucentral/operational.pem
|
||||
uci set system.@certificates[-1].ca=/etc/ucentral/operational.ca
|
||||
uci commit
|
||||
@@ -0,0 +1,18 @@
|
||||
generate_certificate_volume() {
|
||||
grep certificates /proc/mtd > /dev/null
|
||||
[ $? -eq 0 ] && return
|
||||
|
||||
ls /dev/ubi0 > /dev/null
|
||||
[ $? -eq 0 ] || return
|
||||
|
||||
ubinfo /dev/ubi0 -N certificates > /dev/null
|
||||
[ $? -eq 0 ] || {
|
||||
ubinfo /dev/ubi0 -N wifi_fw > /dev/null
|
||||
[ $? -eq 0 ] && ubirmvol /dev/ubi0 -N wifi_fw
|
||||
|
||||
ubirsvol /dev/ubi0 -N rootfs_data -s 10MiB
|
||||
ubimkvol /dev/ubi0 -N certificates -S 20
|
||||
}
|
||||
}
|
||||
|
||||
boot_hook_add preinit_main generate_certificate_volume
|
||||
158
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/mount_certs
Executable file
158
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/mount_certs
Executable file
@@ -0,0 +1,158 @@
|
||||
#!/bin/sh
|
||||
|
||||
check_certificates() {
|
||||
[ -f /certificates/cert.pem -a -f /certificates/key.pem ] && exit 0
|
||||
}
|
||||
|
||||
check_certificates
|
||||
|
||||
tar_part_lookup() {
|
||||
part="$(fw_printenv -n cert_part)"
|
||||
if [ "$part" -eq 0 ]; then
|
||||
echo "$2"
|
||||
part=1
|
||||
else
|
||||
echo "$1"
|
||||
part=0
|
||||
fi
|
||||
fw_setenv cert_part $part
|
||||
}
|
||||
|
||||
. /lib/functions.sh
|
||||
mkdir -p /certificates /etc/ucentral/
|
||||
case "$(board_name)" in
|
||||
cig,wf660a)
|
||||
mmc_dev=$(echo $(find_mmc_part "0:ETHPHYFW") | sed 's/^.\{5\}//')
|
||||
[ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates
|
||||
;;
|
||||
cig,wf672)
|
||||
mmc_dev=$(echo $(find_mmc_part "cert") | sed 's/^.\{5\}//')
|
||||
[ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates
|
||||
;;
|
||||
sonicfi,rap7*)
|
||||
if [ "$(board_name)" = "sonicfi,rap7110c-341x" ]; then
|
||||
mmc_dev=$(echo $(find_mmc_part "certificates") | sed 's/^.\{5\}//')
|
||||
[ -n "$mmc_dev" ] && mount -t ext4 /dev/$mmc_dev /certificates
|
||||
else
|
||||
mtd=$(find_mtd_index certificates)
|
||||
[ -n "$mtd" ] && mount -t ext4 /dev/mtdblock$mtd /certificates
|
||||
fi
|
||||
;;
|
||||
sonicfi,rap6*)
|
||||
mtd=$(find_mtd_index certificates)
|
||||
if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then
|
||||
mount -t squashfs /dev/mtdblock$mtd /mnt
|
||||
cp /mnt/* /certificates
|
||||
umount /mnt
|
||||
fi
|
||||
mtd=$(find_mtd_index devinfo)
|
||||
[ -n "$mtd" ] && tar xf /dev/mtdblock$mtd -C /certificates
|
||||
;;
|
||||
udaya,a5-id2|\
|
||||
yuncore,ax820)
|
||||
mtd=$(find_mtd_index certificates)
|
||||
if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then
|
||||
mount -t squashfs /dev/mtdblock$mtd /mnt
|
||||
cp /mnt/* /certificates
|
||||
umount /mnt
|
||||
fi
|
||||
part=$(tar_part_lookup "insta1" "insta2")
|
||||
if [ -n "insta" ]; then
|
||||
mtd=$(find_mtd_index $part)
|
||||
[ -n "$mtd" ] && tar xf /dev/mtdblock$mtd -C /certificates
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
mtd=$(find_mtd_index certificates)
|
||||
|
||||
if [ "$(head -c 4 /dev/mtd$mtd)" == "hsqs" ]; then
|
||||
mount -t squashfs /dev/mtdblock$mtd /certificates
|
||||
else
|
||||
[ -n "$mtd" -a -f /sys/class/mtd/mtd$mtd/oobsize ] && ubiattach -p /dev/mtd$mtd
|
||||
if [ -n "$(ubinfo -a | grep certificates)" ]; then
|
||||
[ -e /dev/ubi0 ] && mount -t ubifs ubi0:certificates /certificates
|
||||
[ -e /dev/ubi1 ] && mount -t ubifs ubi1:certificates /certificates
|
||||
fi
|
||||
fi
|
||||
esac
|
||||
|
||||
check_certificates
|
||||
|
||||
# if we get here no valid certificates were found
|
||||
|
||||
PART_NAME=
|
||||
|
||||
case "$(board_name)" in
|
||||
actiontec,web7200)
|
||||
if grep -q bootselect=0 /proc/cmdline; then
|
||||
PART_NAME=firmware2
|
||||
else
|
||||
PART_NAME=firmware1
|
||||
fi
|
||||
;;
|
||||
edgecore,ecw5211|\
|
||||
edgecore,eap101|\
|
||||
edgecore,eap102|\
|
||||
edgecore,eap104|\
|
||||
edgecore,eap105|\
|
||||
edgecore,eap111|\
|
||||
edgecore,eap112|\
|
||||
edgecore,oap101|\
|
||||
edgecore,oap101e|\
|
||||
edgecore,oap101-6e|\
|
||||
edgecore,oap101e-6e|\
|
||||
edgecore,oap103)
|
||||
if grep -q rootfs1 /proc/cmdline; then
|
||||
PART_NAME=rootfs2
|
||||
else
|
||||
PART_NAME=rootfs1
|
||||
fi
|
||||
;;
|
||||
hfcl,ion4xi|\
|
||||
hfcl,ion4xi_w|\
|
||||
hfcl,ion4x_w|\
|
||||
hfcl,ion4xi_HMR|\
|
||||
hfcl,ion4x|\
|
||||
hfcl,ion4x_2|\
|
||||
hfcl,ion4xi_wp|\
|
||||
hfcl,ion4xe)
|
||||
if grep -q rootfs_1 /proc/cmdline; then
|
||||
PART_NAME=rootfs
|
||||
else
|
||||
PART_NAME=rootfs_1
|
||||
fi
|
||||
;;
|
||||
cig,wf186w|\
|
||||
cig,wf189|\
|
||||
cig,wf189w|\
|
||||
cig,wf189h|\
|
||||
cig,wf186h|\
|
||||
cig,wf196|\
|
||||
cig,wf188n|\
|
||||
emplus,wap380c|\
|
||||
emplus,wap385c|\
|
||||
emplus,wap386v2|\
|
||||
emplus,wap581|\
|
||||
yuncore,ax840|\
|
||||
yuncore,fap655)
|
||||
PART_NAME=rootfs_1
|
||||
;;
|
||||
senao,iap2300m|\
|
||||
senao,iap4300m|\
|
||||
emplus,wap588m|\
|
||||
senao,jeap6500)
|
||||
PART_NAME=ubi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
MTD=$(find_mtd_index $PART_NAME)
|
||||
|
||||
[ -z "$MTD" ] && return 1
|
||||
|
||||
ubiattach -m $MTD -d 3
|
||||
[ -e /dev/ubi3 ] && mount -t ubifs ubi3:certificates /certificates
|
||||
|
||||
check_certificates
|
||||
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/store_certs
Executable file
36
olg-ucentral-client-sdk/feeds/tip/certificates/files/usr/bin/store_certs
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
|
||||
tar_part_lookup() {
|
||||
part="$(fw_printenv -n cert_part)"
|
||||
if [ "$part" -eq 0 ]; then
|
||||
echo "$2"
|
||||
part=1
|
||||
else
|
||||
echo "$1"
|
||||
part=0
|
||||
fi
|
||||
fw_setenv cert_part $part
|
||||
}
|
||||
|
||||
. /lib/functions.sh
|
||||
case "$(board_name)" in
|
||||
udaya,a5-id2|\
|
||||
yuncore,ax820)
|
||||
cd /certificates
|
||||
tar cf /tmp/certs.tar .
|
||||
part=$(tar_part_lookup "insta1" "insta2")
|
||||
mtd=$(find_mtd_index $part)
|
||||
dd if=/tmp/certs.tar of=/dev/mtdblock$mtd
|
||||
;;
|
||||
sonicfi,rap6*)
|
||||
if [ "$(fw_printenv -n store_certs_disabled)" != "1" ]; then
|
||||
cd /certificates
|
||||
tar cf /tmp/certs.tar .
|
||||
mtd=$(find_mtd_index devinfo)
|
||||
block_size=$(cat /sys/class/mtd/mtd$mtd/size)
|
||||
dd if=/tmp/certs.tar of=/tmp/certs_pad.tar bs=$block_size conv=sync
|
||||
mtd write /tmp/certs_pad.tar /dev/mtd$mtd
|
||||
rm -f /tmp/certs.tar /tmp/certs_pad.tar
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
24
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/Makefile
Normal file
24
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=cloud_discovery
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/cloud_discovery
|
||||
SECTION:=ucentral
|
||||
CATEGORY:=uCentral
|
||||
TITLE:=TIP cloud_discovery
|
||||
DEPENDS:=+certificates +bind-dig
|
||||
endef
|
||||
|
||||
Build/Compile=
|
||||
|
||||
define Package/cloud_discovery/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,cloud_discovery))
|
||||
@@ -0,0 +1 @@
|
||||
touch /tmp/ntp.set
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=98
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/cloud_discovery
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger ucentral
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
ubus call cloud reload
|
||||
}
|
||||
|
||||
start_service() {
|
||||
[ -f /etc/ucentral/capabilities.json ] || {
|
||||
mkdir -p /etc/ucentral/
|
||||
/usr/share/ucentral/capabilities.uc
|
||||
}
|
||||
|
||||
local valid=$(cat /etc/ucentral/gateway.json | jsonfilter -e '@["valid"]')
|
||||
[ "$valid" == "true" ] ||
|
||||
/usr/share/ucentral/ucentral.uc /etc/ucentral/ucentral.cfg.0000000001 > /dev/null
|
||||
|
||||
est_client check
|
||||
[ $? -eq 1 ] && {
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
logger The certificate used has a CN that does not match the serial of the device
|
||||
echo The certificate used has a CN that does not match the serial of the device
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
logger ERROR
|
||||
return
|
||||
}
|
||||
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/usr/share/ucentral/cloud_discovery.uc $1
|
||||
519
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/cloud_discovery
Executable file
519
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/cloud_discovery
Executable file
@@ -0,0 +1,519 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log';
|
||||
import { query } from 'resolv';
|
||||
import * as libubus from 'ubus';
|
||||
import * as uloop from 'uloop';
|
||||
import * as libuci from 'uci';
|
||||
import * as math from 'math';
|
||||
import * as fs from 'fs';
|
||||
|
||||
const DISCOVER = 0;
|
||||
const VALIDATING = 1;
|
||||
const ONLINE = 2;
|
||||
const OFFLINE = 3;
|
||||
const ORPHAN = 4;
|
||||
|
||||
const DISCOVER_DHCP = "DHCP";
|
||||
const DISCOVER_FLASH = "FLASH";
|
||||
const DISCOVER_FQDN = "STANDARD_FQDN";
|
||||
const DISCOVER_LOOKUP = "OpenLAN";
|
||||
|
||||
const STANDARD_FQDN = "openwifi.wlan.local";
|
||||
const STANDARD_FQDN_PORT = 15002;
|
||||
|
||||
let ubus = libubus.connect();
|
||||
let uci = libuci.cursor();
|
||||
let state = DISCOVER;
|
||||
let discovery_method = "";
|
||||
let discovery_block_list = [];
|
||||
let validate_time;
|
||||
let offline_time;
|
||||
let orphan_time;
|
||||
let interval;
|
||||
let timeouts = {
|
||||
'offline': 4 * 60 * 60,
|
||||
'validate': 120,
|
||||
'orphan': 2 * 60 * 60,
|
||||
interval: 10000,
|
||||
expiry_interval: 60 * 60 * 1000,
|
||||
expiry_threshold: 1 * 365 * 24 * 60 * 60,
|
||||
};
|
||||
|
||||
ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "cloud_discover");
|
||||
|
||||
ulog(LOG_INFO, 'Start\n');
|
||||
|
||||
uloop.init();
|
||||
|
||||
let cds_server = 'discovery.open-lan.org';
|
||||
|
||||
function detect_certificate_type() {
|
||||
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
|
||||
let issuer = pipe.read("all");
|
||||
pipe.close();
|
||||
|
||||
if (match(issuer, /OpenLAN Demo Birth CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Demo" \n');
|
||||
cds_server = 'discovery-qa.open-lan.org';
|
||||
timeouts.expiry_threshold = 3 * 24 * 60 * 60;
|
||||
} else if (match(issuer, /OpenLAN Birth Issuing CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Production"\n');
|
||||
} else {
|
||||
ulog(LOG_INFO, 'Certificate type is "TIP"\n');
|
||||
}
|
||||
}
|
||||
|
||||
function readjsonfile(path) {
|
||||
let file = fs.readfile(path);
|
||||
if (file)
|
||||
file = json(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
function timeouts_load(){
|
||||
let data = uci.get_all('ucentral', 'timeouts');
|
||||
|
||||
for (let key in [ 'offline', 'validate', 'orphan' ])
|
||||
if (data && data[key])
|
||||
timeouts[key] = +data[key];
|
||||
let time_skew = timeouts.offline / 50 * (math.rand() % 50);
|
||||
timeouts.offline_skew = timeouts.offline + time_skew;
|
||||
ulog(LOG_INFO, 'Randomizing offline time from %d->%d \n', timeouts.offline, timeouts.offline_skew);
|
||||
|
||||
time_skew = timeouts.orphan / 50 * (math.rand() % 50);
|
||||
timeouts.orphan_skew = timeouts.orphan + time_skew;
|
||||
ulog(LOG_INFO, 'Randomizing orphan time from %d->%d \n', timeouts.orphan, timeouts.orphan_skew);
|
||||
}
|
||||
|
||||
function client_start() {
|
||||
ulog(LOG_INFO, '(re)starting client\n');
|
||||
system('/etc/init.d/ucentral restart');
|
||||
}
|
||||
|
||||
function dhcp_restart() {
|
||||
ulog(LOG_INFO, 'restarting dhcp\n');
|
||||
system('killall -USR1 udhcpc');
|
||||
}
|
||||
|
||||
function ntp_restart() {
|
||||
ulog(LOG_INFO, 'restarting ntp\n');
|
||||
system('/etc/init.d/sysntpd restart');
|
||||
}
|
||||
|
||||
function gateway_load() {
|
||||
return readjsonfile('/etc/ucentral/gateway.json');
|
||||
}
|
||||
|
||||
function discovery_state_write() {
|
||||
if (length(discovery_method) == 0)
|
||||
return;
|
||||
|
||||
let discovery_state = {
|
||||
"type": discovery_method,
|
||||
"updated": time()
|
||||
};
|
||||
fs.writefile('/etc/ucentral/discovery.state.json', discovery_state);
|
||||
}
|
||||
|
||||
function gateway_write(data) {
|
||||
let gateway = gateway_load();
|
||||
gateway ??= {};
|
||||
let new = {};
|
||||
let changed = false;
|
||||
for (let key in [ 'server', 'port', 'valid', 'hostname_validate', 'cert', 'ca' ]) {
|
||||
if (exists(data, key))
|
||||
new[key] = data[key];
|
||||
else if (exists(gateway, key))
|
||||
new[key] = gateway[key];
|
||||
if (new[key] != gateway[key])
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
fs.writefile('/etc/ucentral/gateway.json', new);
|
||||
system('sync');
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
function gateway_available() {
|
||||
let gateway = gateway_load();
|
||||
if (!gateway || !gateway.server || !gateway.port)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function dnsmasq_rebind_allow(fqdn) {
|
||||
let config_dir = '/tmp/dnsmasq.d';
|
||||
let config_file = `${config_dir}/cloud-discovery.conf`;
|
||||
|
||||
if (!fs.stat(config_dir))
|
||||
fs.mkdir(config_dir);
|
||||
|
||||
fs.writefile(config_file, `rebind-domain-ok=/${fqdn}/\n`);
|
||||
system('/etc/init.d/dnsmasq reload');
|
||||
}
|
||||
|
||||
function set_state(set) {
|
||||
if (state == set)
|
||||
return;
|
||||
let prev = state;
|
||||
state = set;
|
||||
|
||||
switch(state) {
|
||||
case DISCOVER:
|
||||
ulog(LOG_INFO, 'Setting cloud to undiscovered\n');
|
||||
fs.unlink('/tmp/cloud.json');
|
||||
fs.unlink('/etc/ucentral/gateway.json');
|
||||
gateway_write({ valid: false });
|
||||
dhcp_restart();
|
||||
break;
|
||||
|
||||
case VALIDATING:
|
||||
ulog(LOG_INFO, 'Wait for validation\n');
|
||||
validate_time = time();
|
||||
state = VALIDATING;
|
||||
push(discovery_block_list, discovery_method);
|
||||
break;
|
||||
|
||||
case ONLINE:
|
||||
ulog(LOG_INFO, 'Connected to cloud\n');
|
||||
if (prev == VALIDATING) {
|
||||
ulog(LOG_INFO, 'Setting cloud controller to validated\n');
|
||||
gateway_write({ valid: true });
|
||||
discovery_state_write();
|
||||
discovery_block_list = [];
|
||||
}
|
||||
break;
|
||||
|
||||
case OFFLINE:
|
||||
ulog(LOG_INFO, 'Lost connection to cloud\n');
|
||||
offline_time = time();
|
||||
break;
|
||||
|
||||
case ORPHAN:
|
||||
ulog(LOG_INFO, 'Device is an orphan\n');
|
||||
orphan_time = time();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function discover_dhcp() {
|
||||
let dhcp = readjsonfile('/tmp/cloud.json');
|
||||
if (dhcp?.dhcp_server && dhcp?.dhcp_port) {
|
||||
let fqdn = split(dhcp.dhcp_server, ':')[0];
|
||||
dnsmasq_rebind_allow(fqdn);
|
||||
if (gateway_write({
|
||||
server: dhcp.dhcp_server,
|
||||
port: dhcp.dhcp_port,
|
||||
valid: false,
|
||||
hostname_validate: dhcp.no_validation ? 0 : 1,
|
||||
cert: `/etc/ucentral/${fqdn}.pem`,
|
||||
ca: `/etc/ucentral/${fqdn}.ca`
|
||||
})) {
|
||||
ulog(LOG_INFO, `Discovered cloud via DHCP ${dhcp.dhcp_server}:${dhcp.dhcp_port} - trying EST\n`);
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_DHCP);
|
||||
if (system('/usr/bin/est_client enroll'))
|
||||
return false;
|
||||
ulog(LOG_INFO, `Discovered cloud via DHCP ${dhcp.dhcp_server}:${dhcp.dhcp_port} - starting client\n`);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return !dhcp?.lease;
|
||||
}
|
||||
|
||||
function redirector_lookup() {
|
||||
const path = '/tmp/ucentral.redirector';
|
||||
ulog(LOG_INFO, 'Contact redirector service\n');
|
||||
let serial = uci.get('system', '@system[-1]', 'mac');
|
||||
|
||||
fs.unlink(path);
|
||||
system(`curl -k --cert /etc/ucentral/operational.pem --key /etc/ucentral/key.pem --cacert /etc/ucentral/operational.ca https://${cds_server}/v1/devices/${serial} --output /tmp/ucentral.redirector`);
|
||||
if (!fs.stat(path))
|
||||
return;
|
||||
let redir = readjsonfile(path);
|
||||
if (redir?.controller_endpoint) {
|
||||
let controller_endpoint = split(redir.controller_endpoint, ':');
|
||||
if (gateway_write({
|
||||
server: controller_endpoint[0],
|
||||
port: controller_endpoint[1] || 15002,
|
||||
valid: false,
|
||||
hostname_validate: 1,
|
||||
cert: '/etc/ucentral/operational.pem',
|
||||
ca: '/etc/ucentral/operational.ca'
|
||||
})) {
|
||||
ulog(LOG_INFO, `Discovered cloud via lookup service ${controller_endpoint[0]}:${controller_endpoint[1] || 15002}\n`);
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_LOOKUP);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
}
|
||||
} else {
|
||||
ulog(LOG_INFO, 'Failed to discover cloud endpoint\n');
|
||||
}
|
||||
}
|
||||
|
||||
function discover_flash() {
|
||||
if (!fs.stat('/etc/ucentral/gateway.flash'))
|
||||
return 1;
|
||||
ulog(LOG_INFO, 'Using pre-populated cloud information\n');
|
||||
fs.writefile('/etc/ucentral/gateway.json', fs.readfile('/etc/ucentral/gateway.flash'));
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_FLASH);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function discover_standard_fqdn() {
|
||||
ulog(LOG_INFO, `Trying standard FQDN: ${STANDARD_FQDN}\n`);
|
||||
|
||||
let result = query([STANDARD_FQDN], { type: ['A'] });
|
||||
if (!result || !result[STANDARD_FQDN] || !result[STANDARD_FQDN].A) {
|
||||
ulog(LOG_INFO, `Failed to resolve ${STANDARD_FQDN}\n`);
|
||||
return false;
|
||||
}
|
||||
|
||||
let address = result[STANDARD_FQDN].A[0];
|
||||
ulog(LOG_INFO, `Resolved ${STANDARD_FQDN} to ${address}\n`);
|
||||
|
||||
dnsmasq_rebind_allow(STANDARD_FQDN);
|
||||
|
||||
if (gateway_write({
|
||||
server: STANDARD_FQDN,
|
||||
port: STANDARD_FQDN_PORT,
|
||||
valid: false,
|
||||
hostname_validate: 1,
|
||||
cert: `/etc/ucentral/${STANDARD_FQDN}.pem`,
|
||||
ca: `/etc/ucentral/${STANDARD_FQDN}.ca`
|
||||
})) {
|
||||
ulog(LOG_INFO, `Discovered cloud via standard FQDN ${STANDARD_FQDN}\n`);
|
||||
fs.writefile('/tmp/discovery.method', DISCOVER_FQDN);
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function time_is_valid() {
|
||||
let valid = !!fs.stat('/tmp/ntp.set');
|
||||
if (!valid)
|
||||
ntp_restart();
|
||||
ulog(LOG_INFO, `Time is ${valid ? '': 'not '}valid\n`);
|
||||
return valid;
|
||||
}
|
||||
|
||||
function is_discover_method_blacked() {
|
||||
if (discovery_method in discovery_block_list)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function interval_handler() {
|
||||
printf(`State ${state}\n`);
|
||||
switch(state) {
|
||||
case DISCOVER:
|
||||
if (timeouts.interval < 60000)
|
||||
timeouts.interval += 10000;
|
||||
break;
|
||||
default:
|
||||
timeouts.interval = 10000;
|
||||
break;
|
||||
}
|
||||
|
||||
printf('setting interval to %d\n', timeouts.interval);
|
||||
interval.set(timeouts.interval);
|
||||
|
||||
switch(state) {
|
||||
case ORPHAN:
|
||||
if (time() - orphan_time <= timeouts.orphan_skew)
|
||||
break;
|
||||
orphan_time = time();
|
||||
|
||||
/* fall through */
|
||||
|
||||
case DISCOVER:
|
||||
ulog(LOG_INFO, 'Starting discover\n');
|
||||
|
||||
if (!time_is_valid())
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_DHCP;
|
||||
if (!is_discover_method_blacked() && discover_dhcp())
|
||||
return;
|
||||
|
||||
if (system('/usr/bin/est_client enroll'))
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_FLASH;
|
||||
if (!is_discover_method_blacked() && !discover_flash())
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_FQDN;
|
||||
if (!is_discover_method_blacked() && discover_standard_fqdn())
|
||||
return;
|
||||
|
||||
discovery_method = DISCOVER_LOOKUP;
|
||||
redirector_lookup();
|
||||
|
||||
discovery_block_list = [];
|
||||
break;
|
||||
|
||||
case VALIDATING:
|
||||
if (time() - validate_time <= timeouts.validate)
|
||||
break;
|
||||
ulog(LOG_INFO, 'validation failed, restarting discovery process\n');
|
||||
set_state(DISCOVER);
|
||||
break;
|
||||
|
||||
case OFFLINE:
|
||||
if (time() - offline_time <= timeouts.offline_skew)
|
||||
break;
|
||||
ulog(LOG_INFO, 'offline for too long, setting device as orphaned\n');
|
||||
set_state(ORPHAN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function trigger_reenroll() {
|
||||
ulog(LOG_INFO, 'triggering reenroll\n');
|
||||
|
||||
if (system('/usr/bin/est_client reenroll')) {
|
||||
ulog(LOG_INFO, 'reenroll failed\n');
|
||||
return;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'reenroll succeeded\n');
|
||||
ulog(LOG_INFO, 'stopping client\n');
|
||||
|
||||
system('/etc/init.d/ucentral stop');
|
||||
set_state(DISCOVER);
|
||||
}
|
||||
|
||||
function expiry_handler() {
|
||||
let stat = fs.stat('/etc/ucentral/operational.ca');
|
||||
if (!stat)
|
||||
return;
|
||||
|
||||
let ret = system(`openssl x509 -checkend ${timeouts.expiry_threshold} -noout -in /certificates/operational.pem`);
|
||||
if (!ret) {
|
||||
ulog(LOG_INFO, 'checked certificate expiry - all ok\n');
|
||||
return;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'certificate will expire soon\n');
|
||||
trigger_reenroll();
|
||||
}
|
||||
|
||||
let ubus_methods = {
|
||||
discover: {
|
||||
call: function(req) {
|
||||
set_state(DISCOVER);
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
renew: {
|
||||
call: function(req) {
|
||||
if (state != ONLINE)
|
||||
return;
|
||||
|
||||
ulog(LOG_INFO, 'Validate cloud due to DHCP renew event\n');
|
||||
|
||||
let gateway = gateway_load();
|
||||
let cloud = readjsonfile('/tmp/cloud.json');
|
||||
if (!cloud?.dhcp_server || !cloud?.dhcp_port)
|
||||
return 0;
|
||||
|
||||
if (cloud.dhcp_server != gateway?.server || cloud.dhcp_port != gateway?.port)
|
||||
set_state(DISCOVER);
|
||||
else
|
||||
ulog(LOG_INFO, 'Cloud has not changed\n');
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
online: {
|
||||
call: function(req) {
|
||||
set_state(ONLINE);
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
offline: {
|
||||
call: function(req) {
|
||||
if (state == ONLINE)
|
||||
set_state(OFFLINE);
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
reload: {
|
||||
call: function(req) {
|
||||
timeouts_load();
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
|
||||
}
|
||||
},
|
||||
status: {
|
||||
call: function(req) {
|
||||
const names = [ 'discover', 'validate', 'online', 'offline', 'orphan' ];
|
||||
let ret = { state: names[state] };
|
||||
switch(state){
|
||||
case OFFLINE:
|
||||
ret.since = time() - offline_time;
|
||||
break;
|
||||
case ORPHAN:
|
||||
ret.since = time() - orphan_time;
|
||||
break;
|
||||
case VALIDATING:
|
||||
ret.since = time() - validate_time;;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
args: {},
|
||||
},
|
||||
reenroll: {
|
||||
call: function(req) {
|
||||
trigger_reenroll();
|
||||
return 0;
|
||||
},
|
||||
args: {},
|
||||
},
|
||||
};
|
||||
|
||||
detect_certificate_type();
|
||||
|
||||
if (gateway_available()) {
|
||||
let status = ubus.call('ucentral', 'status');
|
||||
ulog(LOG_INFO, 'cloud is known\n');
|
||||
if (status?.connected) {
|
||||
state = ONLINE;
|
||||
} else {
|
||||
client_start();
|
||||
set_state(VALIDATING);
|
||||
}
|
||||
} else {
|
||||
dhcp_restart();
|
||||
}
|
||||
|
||||
timeouts_load();
|
||||
|
||||
interval = uloop.interval(timeouts.interval, interval_handler);
|
||||
uloop.interval(timeouts.expiry_interval, expiry_handler);
|
||||
|
||||
ubus.publish('cloud', ubus_methods);
|
||||
|
||||
uloop.run();
|
||||
uloop.done();
|
||||
303
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/est_client
Executable file
303
olg-ucentral-client-sdk/feeds/tip/cloud_discovery/files/usr/bin/est_client
Executable file
@@ -0,0 +1,303 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log';
|
||||
import * as fs from 'fs';
|
||||
import * as libuci from 'uci';
|
||||
|
||||
let store_operational_pem = false;
|
||||
let store_operational_ca = false;
|
||||
let est_server = 'est.certificates.open-lan.org';
|
||||
let cert_prefix = 'operational';
|
||||
|
||||
function cert_prefix_determine() {
|
||||
let cloud_config = fs.readfile('/tmp/cloud.json');
|
||||
if (cloud_config) {
|
||||
let cloud = json(cloud_config);
|
||||
if (cloud?.dhcp_server) {
|
||||
let fqdn = split(cloud.dhcp_server, ':')[0];
|
||||
ulog(LOG_INFO, `Using controller-specific cert prefix from cloud.json: ${fqdn}\n`);
|
||||
return fqdn;
|
||||
}
|
||||
}
|
||||
|
||||
let discovery_method = trim(fs.readfile('/tmp/discovery.method') || 'OpenLAN');
|
||||
ulog(LOG_INFO, `Discovery method from file: ${discovery_method}\n`);
|
||||
|
||||
if (discovery_method == 'OpenLAN') {
|
||||
ulog(LOG_INFO, 'Using operational cert prefix\n');
|
||||
return 'operational';
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'Using operational cert prefix as fallback\n');
|
||||
return 'operational';
|
||||
}
|
||||
|
||||
function discover_est_server_via_caa() {
|
||||
let cloud_config = fs.readfile('/tmp/cloud.json');
|
||||
if (!cloud_config)
|
||||
return null;
|
||||
|
||||
let cloud = json(cloud_config);
|
||||
if (!cloud || !cloud.dhcp_server)
|
||||
return null;
|
||||
|
||||
let controller_fqdn = cloud.dhcp_server;
|
||||
let fqdn_parts = split(controller_fqdn, ':');
|
||||
if (length(fqdn_parts) > 0)
|
||||
controller_fqdn = fqdn_parts[0];
|
||||
|
||||
ulog(LOG_INFO, `Attempting CAA lookup for controller FQDN: ${controller_fqdn}\n`);
|
||||
|
||||
let pipe = fs.popen(`dig @localhost ${controller_fqdn} CAA +short | cut -d'"' -f2`);
|
||||
let est_server = pipe.read('all');
|
||||
pipe.close();
|
||||
|
||||
if (!est_server)
|
||||
return null;
|
||||
|
||||
est_server = trim(est_server);
|
||||
if (est_server) {
|
||||
ulog(LOG_INFO, `Found EST server via CAA: ${est_server}\n`);
|
||||
return est_server;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function set_est_server() {
|
||||
let discovered_server = discover_est_server_via_caa();
|
||||
if (discovered_server) {
|
||||
est_server = discovered_server;
|
||||
return;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'No EST server found via CAA, using certificate issuer-based selection\n');
|
||||
|
||||
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
|
||||
let issuer = pipe.read("all");
|
||||
pipe.close();
|
||||
|
||||
if (match(issuer, /OpenLAN Demo Birth CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Demo" \n');
|
||||
est_server = 'qaest.certificates.open-lan.org:8001';
|
||||
} else if (match(issuer, /OpenLAN Birth Issuing CA/)) {
|
||||
ulog(LOG_INFO, 'Certificate type is "Production"\n');
|
||||
} else {
|
||||
ulog(LOG_INFO, 'Certificate type is "TIP"\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv('EST_SERVER'))
|
||||
est_server = getenv('EST_SERVER');
|
||||
|
||||
if (getenv('CERT_PREFIX'))
|
||||
cert_prefix = getenv('CERT_PREFIX');
|
||||
|
||||
ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "est_client");
|
||||
|
||||
function generate_csr(cert) {
|
||||
if (!fs.stat('/tmp/csr.nohdr.p10')) {
|
||||
let pipe = fs.popen(`openssl x509 -in ${cert} -noout -subject`);
|
||||
let subject = pipe.read("all");
|
||||
pipe.close();
|
||||
subject = rtrim(subject);
|
||||
subject = replace(subject, 'subject=', '/');
|
||||
subject = replace(subject, ' = ', '=');
|
||||
subject = replace(subject, ', ', '/');
|
||||
|
||||
let ret = system(`openssl req -subj "${subject}" -new -key /etc/ucentral/key.pem -out /tmp/csr.p10`);
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, 'Failed to generate CSR\n');
|
||||
return 1;
|
||||
}
|
||||
|
||||
let input = fs.open('/tmp/csr.p10', 'r');
|
||||
let output = fs.open('/tmp/csr.nohdr.p10', 'w');
|
||||
let line;
|
||||
while (line = input.read('line')) {
|
||||
if (substr(line, 0, 4) == '----')
|
||||
continue;
|
||||
output.write(line);
|
||||
}
|
||||
input.close();
|
||||
output.close();
|
||||
ulog(LOG_INFO, 'Generated CSR\n');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function store_operational_cert(path, target) {
|
||||
system('mount_certs');
|
||||
system(`cp ${path} /certificates/${target}`);
|
||||
|
||||
ulog(LOG_INFO, `Persistently stored ${target}\n`);
|
||||
}
|
||||
|
||||
function p7_too_pem(src, dst) {
|
||||
let input = fs.readfile(src);
|
||||
let output = fs.open('/tmp/convert.p7', 'w');
|
||||
output.write('-----BEGIN PKCS #7 SIGNED DATA-----\n');
|
||||
output.write(`${input}\n-----END PKCS #7 SIGNED DATA-----`);
|
||||
output.close();
|
||||
|
||||
let ret = system(`openssl pkcs7 -outform PEM -print_certs -in /tmp/convert.p7 -out ${dst}`);
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, 'Failed to convert P7 to PEM\n');
|
||||
return 1;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'Converted P7 to PEM\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function call_est_server(path, cert, target) {
|
||||
if (generate_csr(cert))
|
||||
return 1;
|
||||
|
||||
set_est_server();
|
||||
|
||||
let curl_cmd = 'curl -m 10 -X POST https://' + est_server + '/.well-known/est/' + path + ' -d @/tmp/csr.nohdr.p10 -H "Content-Type: application/pkcs10" --cert ' + cert + ' --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/operational.nohdr.p7';
|
||||
ulog(LOG_INFO, `Executing: ${curl_cmd}\n`);
|
||||
let ret = system(curl_cmd);
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, `Failed to request operational certificate (exit code: ${ret})\n`);
|
||||
return 1;
|
||||
}
|
||||
ulog(LOG_INFO, 'EST succeeded\n');
|
||||
|
||||
return p7_too_pem('/tmp/operational.nohdr.p7', target);
|
||||
}
|
||||
|
||||
|
||||
function simpleenroll() {
|
||||
cert_prefix = cert_prefix_determine();
|
||||
ulog(LOG_INFO, `Checking for certificate: /etc/ucentral/${cert_prefix}.pem\n`);
|
||||
|
||||
if (fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) {
|
||||
ulog(LOG_INFO, 'Operational certificate is present\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulog(LOG_INFO, 'Operational certificate not found, enrolling...\n');
|
||||
if (call_est_server('simpleenroll', '/etc/ucentral/cert.pem', '/etc/ucentral/' + cert_prefix + '.pem'))
|
||||
return 1;
|
||||
|
||||
ulog(LOG_INFO, 'Operational cert acquired\n');
|
||||
store_operational_pem = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function simplereenroll() {
|
||||
cert_prefix = cert_prefix_determine();
|
||||
|
||||
if (!fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) {
|
||||
ulog(LOG_INFO, 'Operational certificate was not found\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (call_est_server('simplereenroll', '/etc/ucentral/' + cert_prefix + '.pem', '/tmp/' + cert_prefix + '.pem'))
|
||||
return 1;
|
||||
|
||||
ulog(LOG_INFO, 'Operational cert updated\n');
|
||||
store_operational_cert('/tmp/' + cert_prefix + '.pem', cert_prefix + '.pem');
|
||||
system('cp /tmp/' + cert_prefix + '.pem /etc/ucentral/');
|
||||
system('store_certs');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function load_operational_ca() {
|
||||
cert_prefix = cert_prefix_determine();
|
||||
|
||||
if (fs.stat('/etc/ucentral/' + cert_prefix + '.ca')) {
|
||||
ulog(LOG_INFO, 'Operational CA is present\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
set_est_server();
|
||||
|
||||
let curl_cmd = 'curl -m 10 -X GET https://' + est_server + '/.well-known/est/cacerts --cert /etc/ucentral/' + cert_prefix + '.pem --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/' + cert_prefix + '.ca.nohdr.p7';
|
||||
ulog(LOG_INFO, `Executing: ${curl_cmd}\n`);
|
||||
let ret = system(curl_cmd);
|
||||
if (!ret)
|
||||
ret = p7_too_pem('/tmp/' + cert_prefix + '.ca.nohdr.p7', '/etc/ucentral/' + cert_prefix + '.ca');
|
||||
if (ret) {
|
||||
ulog(LOG_INFO, `Failed to load CA (exit code: ${ret})\n`);
|
||||
return 1;
|
||||
}
|
||||
system('cat /etc/ucentral/openlan.pem >> /etc/ucentral/' + cert_prefix + '.ca');
|
||||
ulog(LOG_INFO, 'Acquired CA\n');
|
||||
store_operational_ca = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function fwtool() {
|
||||
if (!fs.stat('/etc/ucentral/cert.pem'))
|
||||
return 0;
|
||||
|
||||
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
|
||||
let issuer = pipe.read("all");
|
||||
pipe.close();
|
||||
|
||||
if (!(match(issuer, /OpenLAN/) && match(issuer, /Birth/)))
|
||||
return 0;
|
||||
|
||||
ulog(LOG_INFO, 'The issuer is insta\n');
|
||||
|
||||
let metadata = fs.readfile('/tmp/sysupgrade.meta');
|
||||
if (metadata)
|
||||
metadata = json(metadata);
|
||||
if (!metadata)
|
||||
return 0;
|
||||
|
||||
if (!metadata.est_supported) {
|
||||
ulog(LOG_INFO, 'The image does not support EST\n');
|
||||
return 1;
|
||||
}
|
||||
ulog(LOG_INFO, 'The image supports EST\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function check_cert() {
|
||||
if (!fs.stat('/etc/ucentral/cert.pem'))
|
||||
return 0;
|
||||
let pipe = fs.popen("openssl x509 -in /etc/ucentral/cert.pem -noout -subject -nameopt multiline | grep commonName | awk '{ print $3 }'");
|
||||
let cn = pipe.read("all");
|
||||
pipe.close();
|
||||
if (!cn)
|
||||
return 0;
|
||||
cn = lc(trim(cn));
|
||||
let uci = libuci.cursor();
|
||||
let serial = uci.get('ucentral', 'config', 'serial');
|
||||
return cn != serial;
|
||||
}
|
||||
|
||||
switch(ARGV[0]) {
|
||||
case 'enroll':
|
||||
let ret = simpleenroll();
|
||||
if (!ret)
|
||||
ret = load_operational_ca();
|
||||
if (store_operational_pem)
|
||||
store_operational_cert('/etc/ucentral/' + cert_prefix + '.pem', cert_prefix + '.pem');
|
||||
if (store_operational_ca)
|
||||
store_operational_cert('/etc/ucentral/' + cert_prefix + '.ca', cert_prefix + '.ca');
|
||||
if (store_operational_pem || store_operational_ca)
|
||||
system('store_certs');
|
||||
|
||||
exit(ret);
|
||||
|
||||
case 'reenroll':
|
||||
if (simplereenroll())
|
||||
exit(1);
|
||||
exit(0);
|
||||
|
||||
case 'fwtool':
|
||||
exit(fwtool());
|
||||
|
||||
case 'check':
|
||||
exit(check_cert());
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
import * as libubus from 'ubus';
|
||||
import * as fs from 'fs';
|
||||
|
||||
let cmd = ARGV[0];
|
||||
let ifname = getenv("interface");
|
||||
let opt138 = fs.readfile('/tmp/dhcp-option-138');
|
||||
let opt224 = fs.readfile('/tmp/dhcp-option-224');
|
||||
|
||||
if (cmd != 'bound' && cmd != 'renew')
|
||||
exit(0);
|
||||
|
||||
/*let file = fs.readfile('/etc/ucentral/gateway.json');
|
||||
if (file)
|
||||
file = json(file);
|
||||
file ??= {};
|
||||
if (file.server && file.port && file.valid)
|
||||
exit(0);
|
||||
*/
|
||||
|
||||
let cloud = {
|
||||
lease: true,
|
||||
};
|
||||
if (opt138) {
|
||||
let dhcp = opt138;
|
||||
dhcp = split(dhcp, ':');
|
||||
cloud.dhcp_server = dhcp[0];
|
||||
cloud.dhcp_port = dhcp[1] ?? 15002;
|
||||
cloud.no_validation = true;
|
||||
}
|
||||
if (opt224) {
|
||||
let dhcp = opt224;
|
||||
dhcp = split(dhcp, ':');
|
||||
cloud.dhcp_server = dhcp[0];
|
||||
cloud.dhcp_port = dhcp[1] ?? 15002;
|
||||
}
|
||||
fs.writefile('/tmp/cloud.json', cloud);
|
||||
|
||||
if ((opt138 || opt224) && cmd == 'renew') {
|
||||
let ubus = libubus.connect();
|
||||
ubus.call('cloud', 'renew');
|
||||
}
|
||||
|
||||
exit(0);
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI uCentral Configuration
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
include ../luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require dom';
|
||||
'require rpc';
|
||||
'require session';
|
||||
'require baseclass';
|
||||
|
||||
var callReboot = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'reboot',
|
||||
expect: { result: 0 }
|
||||
});
|
||||
|
||||
function handleReboot(ev) {
|
||||
return callReboot().then(function(res) {
|
||||
if (res != 0) {
|
||||
showError(_('The reboot command failed with code %d').format(res));
|
||||
L.raise('Error', 'Reboot failed');
|
||||
}
|
||||
|
||||
showProgress(_('The system is rebooting in order to attempt applying the remote configuration profile now. If not successful, the device will revert back into the initial provisioning state.'));
|
||||
|
||||
ui.awaitReconnect();
|
||||
}).catch(function(e) { showError(e.message) });
|
||||
}
|
||||
|
||||
function setDirty(isDirty) {
|
||||
if (isDirty) {
|
||||
session.setLocalData('ucentral-dirty', true);
|
||||
ui.showIndicator('ucentral-dirty', _('Reboot required'), showApplySettings);
|
||||
}
|
||||
else {
|
||||
session.setLocalData('ucentral-dirty', null);
|
||||
ui.hideIndicator('ucentral-dirty');
|
||||
}
|
||||
}
|
||||
|
||||
function handleContinue(ev) {
|
||||
setDirty(true);
|
||||
ui.hideModal();
|
||||
}
|
||||
|
||||
function showApplySettings() {
|
||||
ui.showModal(_('Apply Settings'), [
|
||||
E('p', _('The device must be rebooted in order to apply the changed settings. Once the uCentral agent successfully connects to the controller, the remote configuration profile will be applied and the initial provisioning web interface is disabled.')),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', { 'click': handleReboot, 'class': 'btn primary' }, [ _('Apply settings and reboot device now') ]),
|
||||
'\xa0',
|
||||
E('button', { 'click': handleContinue, 'class': 'btn' }, [ _('Continue configuration') ])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
function showProgress(text, timeout) {
|
||||
var dlg = ui.showModal(null, [
|
||||
E('p', { 'class': (timeout > 0) ? null : 'spinning' }, text)
|
||||
]);
|
||||
|
||||
dlg.removeChild(dlg.firstElementChild);
|
||||
|
||||
if (timeout > 0)
|
||||
window.setTimeout(ui.hideModal, timeout);
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
function showError(text) {
|
||||
ui.showModal(_('Error'), [
|
||||
E('p', [ text ]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', { 'class': 'btn', 'click': ui.hideModal }, _('OK'))
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (session.getLocalData('ucentral-dirty'))
|
||||
setDirty(true);
|
||||
|
||||
return baseclass.extend({
|
||||
save: function(serializeFn, ev) {
|
||||
var m = dom.findClassInstance(document.querySelector('.cbi-map'));
|
||||
|
||||
return m.save().then(function() {
|
||||
return fs.write('/etc/ucentral/profile.json', serializeFn(m.data.data));
|
||||
}).then(function() {
|
||||
return fs.exec('/sbin/profileupdate');
|
||||
}).then(function() {
|
||||
showApplySettings();
|
||||
}).catch(function(err) {
|
||||
showError(_('Unable to save settings: %s').format(err));
|
||||
});
|
||||
},
|
||||
|
||||
setDirty: setDirty,
|
||||
showError: showError,
|
||||
showProgress: showProgress,
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var profile = null;
|
||||
|
||||
function serialize(data) {
|
||||
if (!L.isObject(profile.unit))
|
||||
profile.unit = {};
|
||||
|
||||
profile.redirector = data.local.redirector;
|
||||
profile.unit.location = data.local.location;
|
||||
|
||||
return JSON.stringify(profile, null, '\t');
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return L.resolveDefault(fs.read('/etc/ucentral/profile.json'), '').then(function(data) {
|
||||
try { profile = JSON.parse(data); }
|
||||
catch(e) { profile = {}; };
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var m, s, o, data = { local: {
|
||||
redirector: profile.redirector,
|
||||
location: L.isObject(profile.unit) ? profile.unit.location : ''
|
||||
} };
|
||||
|
||||
m = new form.JSONMap(data);
|
||||
m.readonly = !L.hasViewPermission();
|
||||
|
||||
s = m.section(form.NamedSection, 'local', 'local', _('Local settings'),
|
||||
_('The settings on this page specify how the local uCentral client connects to the controller server.'));
|
||||
|
||||
s.option(form.Value, 'redirector', _('Redirector URL'));
|
||||
s.option(form.Value, 'location', _('Unit location'));
|
||||
|
||||
o = s.option(form.Button, '_certs', _('Certificates'));
|
||||
o.inputtitle = _('Upload certificate bundle…');
|
||||
o.onclick = function(ev) {
|
||||
return ui.uploadFile('/tmp/certs.tar').then(function(res) {
|
||||
uctool.showProgress(_('Verifying certificates…'));
|
||||
|
||||
return fs.exec('/sbin/certupdate').then(function(res) {
|
||||
if (res.code) {
|
||||
uctool.showError(_('Certificate validation failed: %s').format(res.stderr || res.stdout));
|
||||
}
|
||||
else {
|
||||
uctool.showProgress(_('Certificates updated.'), 1500);
|
||||
uctool.setDirty(true);
|
||||
}
|
||||
}, function(err) {
|
||||
uctool.showError(_('Unable to verify certificates: %s').format(err));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return m.render();
|
||||
},
|
||||
|
||||
handleSave: uctool.save.bind(uctool, serialize),
|
||||
|
||||
handleSaveApply: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
'use strict';
|
||||
'require rpc';
|
||||
'require view';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var callSystemBoard = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'board'
|
||||
});
|
||||
|
||||
var callSystemInfo = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'info'
|
||||
});
|
||||
|
||||
function progressbar(value, max, byte) {
|
||||
var vn = parseInt(value) || 0,
|
||||
mn = parseInt(max) || 100,
|
||||
fv = byte ? String.format('%1024.2mB', value) : value,
|
||||
fm = byte ? String.format('%1024.2mB', max) : max,
|
||||
pc = Math.floor((100 / mn) * vn);
|
||||
|
||||
return E('div', {
|
||||
'class': 'cbi-progressbar',
|
||||
'title': '%s / %s (%d%%)'.format(fv, fm, pc)
|
||||
}, E('div', { 'style': 'width:%.2f%%'.format(pc) }));
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(callSystemBoard(), {}),
|
||||
L.resolveDefault(callSystemInfo(), {})
|
||||
]);
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
var boardinfo = data[0],
|
||||
systeminfo = data[1],
|
||||
mem = L.isObject(systeminfo.memory) ? systeminfo.memory : {},
|
||||
swap = L.isObject(systeminfo.swap) ? systeminfo.swap : {},
|
||||
datestr = null;
|
||||
|
||||
if (systeminfo.localtime) {
|
||||
var date = new Date(systeminfo.localtime * 1000);
|
||||
|
||||
datestr = '%04d-%02d-%02d %02d:%02d:%02d'.format(
|
||||
date.getUTCFullYear(),
|
||||
date.getUTCMonth() + 1,
|
||||
date.getUTCDate(),
|
||||
date.getUTCHours(),
|
||||
date.getUTCMinutes(),
|
||||
date.getUTCSeconds()
|
||||
);
|
||||
}
|
||||
|
||||
var sysfields = [
|
||||
_('Hostname'), boardinfo.hostname,
|
||||
_('Model'), boardinfo.model,
|
||||
_('Architecture'), boardinfo.system,
|
||||
_('Firmware Version'), (L.isObject(boardinfo.release) ? boardinfo.release.description : '?'),
|
||||
_('Kernel Version'), boardinfo.kernel,
|
||||
_('Local Time'), datestr,
|
||||
_('Uptime'), systeminfo.uptime ? '%t'.format(systeminfo.uptime) : null,
|
||||
_('Load Average'), Array.isArray(systeminfo.load) ? '%.2f, %.2f, %.2f'.format(
|
||||
systeminfo.load[0] / 65535.0,
|
||||
systeminfo.load[1] / 65535.0,
|
||||
systeminfo.load[2] / 65535.0
|
||||
) : null
|
||||
];
|
||||
|
||||
var systable = E('table', { 'class': 'table' });
|
||||
|
||||
for (var i = 0; i < sysfields.length; i += 2) {
|
||||
systable.appendChild(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '33%' }, [ sysfields[i] ]),
|
||||
E('td', { 'class': 'td left' }, [ (sysfields[i + 1] != null) ? sysfields[i + 1] : '?' ])
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
var memfields = [
|
||||
_('Total Available'), (mem.available) ? mem.available : (mem.total && mem.free && mem.buffered) ? mem.free + mem.buffered : null, mem.total,
|
||||
_('Used'), (mem.total && mem.free) ? (mem.total - mem.free) : null, mem.total,
|
||||
_('Buffered'), (mem.total && mem.buffered) ? mem.buffered : null, mem.total
|
||||
];
|
||||
|
||||
if (mem.cached)
|
||||
memfields.push(_('Cached'), mem.cached, mem.total);
|
||||
|
||||
if (swap.total > 0)
|
||||
memfields.push(_('Swap free'), swap.free, swap.total);
|
||||
|
||||
var memtable = E('table', { 'class': 'table' });
|
||||
|
||||
for (var i = 0; i < memfields.length; i += 3) {
|
||||
memtable.appendChild(E('tr', { 'class': 'tr' }, [
|
||||
E('td', { 'class': 'td left', 'width': '33%' }, [ memfields[i] ]),
|
||||
E('td', { 'class': 'td left' }, [
|
||||
(memfields[i + 1] != null) ? progressbar(memfields[i + 1], memfields[i + 2], true) : '?'
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
return E([], [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', _('System')),
|
||||
systable
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', _('Memory')),
|
||||
memtable
|
||||
]),
|
||||
]);
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,210 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require dom';
|
||||
'require form';
|
||||
'require rpc';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var callSystemValidateFirmwareImage = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'validate_firmware_image',
|
||||
params: [ 'path' ],
|
||||
expect: { '': { valid: false, forcable: true } }
|
||||
});
|
||||
|
||||
var callReboot = rpc.declare({
|
||||
object: 'system',
|
||||
method: 'reboot',
|
||||
expect: { result: 0 }
|
||||
});
|
||||
|
||||
var mapdata = { actions: {}, config: {} };
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
var tasks = [
|
||||
fs.trimmed('/proc/mtd'),
|
||||
fs.trimmed('/proc/mounts')
|
||||
];
|
||||
|
||||
return Promise.all(tasks);
|
||||
},
|
||||
|
||||
handleFirstboot: function(ev) {
|
||||
if (!confirm(_('Do you really want to erase all settings?')))
|
||||
return;
|
||||
|
||||
uctool.showProgress(_('The system is erasing the configuration partition now and will reboot itself when finished.'));
|
||||
|
||||
/* Currently the sysupgrade rpc call will not return, hence no promise handling */
|
||||
fs.exec('/sbin/firstboot', [ '-r', '-y' ]);
|
||||
|
||||
ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
|
||||
},
|
||||
|
||||
handleDiagnostics: function(ev) {
|
||||
return fs.exec('/sbin/diagnostic-bundle').then(function(result) {
|
||||
var form = E('form', {
|
||||
method: 'post',
|
||||
action: L.env.cgi_base + '/cgi-download',
|
||||
enctype: 'application/x-www-form-urlencoded'
|
||||
}, [
|
||||
E('input', { 'type': 'hidden', 'name': 'sessionid', 'value': L.env.sessionid }),
|
||||
E('input', { 'type': 'hidden', 'name': 'path', 'value': '/tmp/bundle.maverick.tar.gz' }),
|
||||
E('input', { 'type': 'hidden', 'name': 'filename', 'value': 'bundle.maverick.tar.gz' }),
|
||||
E('input', { 'type': 'hidden', 'name': 'mimetype', 'value': 'application/gzip' })
|
||||
]);
|
||||
|
||||
document.body.appendChild(form);
|
||||
|
||||
form.submit();
|
||||
form.parentNode.removeChild(form);
|
||||
});
|
||||
},
|
||||
|
||||
handleSysupgrade: function(ev) {
|
||||
return ui.uploadFile('/tmp/firmware.bin', ev.target.firstChild)
|
||||
.then(L.bind(function(btn, reply) {
|
||||
btn.firstChild.data = _('Checking image…');
|
||||
uctool.showProgress(_('Verifying the uploaded image file.'));
|
||||
|
||||
return callSystemValidateFirmwareImage('/tmp/firmware.bin')
|
||||
.then(function(res) { return [ reply, res ]; });
|
||||
}, this, ev.target))
|
||||
.then(L.bind(function(btn, reply) {
|
||||
return fs.exec('/sbin/sysupgrade', [ '--test', '/tmp/firmware.bin' ])
|
||||
.then(function(res) { reply.push(res); return reply; });
|
||||
}, this, ev.target))
|
||||
.then(L.bind(function(btn, res) {
|
||||
var is_valid = res[1].valid,
|
||||
body = [];
|
||||
|
||||
if (is_valid) {
|
||||
body.push(E('p', _("The firmware image was uploaded. Compare the checksum and file size listed below with the original file to ensure data integrity. <br /> Click 'Continue' below to start the flash procedure.")));
|
||||
body.push(E('ul', {}, [
|
||||
res[0].size ? E('li', {}, '%s: %1024.2mB'.format(_('Size'), res[0].size)) : '',
|
||||
res[0].checksum ? E('li', {}, '%s: %s'.format(_('MD5'), res[0].checksum)) : '',
|
||||
res[0].sha256sum ? E('li', {}, '%s: %s'.format(_('SHA256'), res[0].sha256sum)) : ''
|
||||
]));
|
||||
}
|
||||
else {
|
||||
body.push(E('p', _("The firmware image is invalid and cannot be flashed. Check the diagnostics below for further details.")));
|
||||
|
||||
if (res[2].stderr)
|
||||
body.push(E('pre', { 'class': 'alert-message' }, [ res[2].stderr ]));
|
||||
}
|
||||
|
||||
var cntbtn = E('button', {
|
||||
'class': 'btn cbi-button-action important',
|
||||
'click': ui.createHandlerFn(this, 'handleSysupgradeConfirm', btn)
|
||||
}, [ _('Continue') ]);
|
||||
if (!is_valid)
|
||||
cntbtn.disabled = true;
|
||||
|
||||
body.push(E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.createHandlerFn(this, function(ev) {
|
||||
return fs.remove('/tmp/firmware.bin').finally(ui.hideModal);
|
||||
})
|
||||
}, [ _('Cancel') ]), ' ', cntbtn
|
||||
]));
|
||||
|
||||
ui.showModal(is_valid ? _('Flash image?') : _('Invalid image'), body);
|
||||
}, this, ev.target))
|
||||
.catch(function(e) { uctool.showError(e.message) })
|
||||
.finally(L.bind(function(btn) {
|
||||
btn.firstChild.data = _('Flash image…');
|
||||
}, this, ev.target));
|
||||
},
|
||||
|
||||
handleSysupgradeConfirm: function(btn, ev) {
|
||||
btn.firstChild.data = _('Flashing…');
|
||||
|
||||
uctool.showProgress(_('The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a few minutes before you try to reconnect. It might be necessary to renew the address of your computer to reach the device again, depending on your settings.'));
|
||||
|
||||
/* Currently the sysupgrade rpc call will not return, hence no promise handling */
|
||||
fs.exec('/sbin/sysupgrade', [ '-n', '/tmp/firmware.bin' ]);
|
||||
|
||||
ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
|
||||
},
|
||||
|
||||
handleReboot: function(ev) {
|
||||
return callReboot().then(function(res) {
|
||||
if (res != 0) {
|
||||
uctool.showError(_('The reboot command failed with code %d').format(res));
|
||||
L.raise('Error', 'Reboot failed');
|
||||
}
|
||||
|
||||
uctool.showProgress(_('The device is rebooting now. This page will try to reconnect automatically once the device is fully booted.'));
|
||||
|
||||
ui.awaitReconnect();
|
||||
})
|
||||
.catch(function(e) { uctool.showError(e.message) });
|
||||
},
|
||||
|
||||
render: function(rpc_replies) {
|
||||
var procmtd = rpc_replies[0],
|
||||
procmounts = rpc_replies[1],
|
||||
has_rootfs_data = (procmtd.match(/"rootfs_data"/) != null) || (procmounts.match("overlayfs:\/overlay \/ ") != null),
|
||||
m, s, o, ss;
|
||||
|
||||
m = new form.JSONMap(mapdata);
|
||||
m.readonly = !L.hasViewPermission();
|
||||
|
||||
s = m.section(form.NamedSection, 'actions');
|
||||
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Reboot device'),
|
||||
_('Issue a reboot and restart the operating system on this device.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
o = ss.option(form.Button, 'reboot');
|
||||
o.inputstyle = 'action important';
|
||||
o.inputtitle = _('Reboot');
|
||||
o.onclick = L.bind(this.handleReboot, this);
|
||||
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Reset to defaults'),
|
||||
_('Reset the system to its initial state and discard any configuration changes.'));
|
||||
ss = o.subsection;
|
||||
|
||||
if (has_rootfs_data) {
|
||||
o = ss.option(form.Button, 'reset');
|
||||
o.inputstyle = 'negative important';
|
||||
o.inputtitle = _('Perform reset');
|
||||
o.onclick = this.handleFirstboot;
|
||||
}
|
||||
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Firmware upgrade'),
|
||||
_('Upload a compatible firmware image here to upgrade the running system.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
o = ss.option(form.Button, 'sysupgrade');
|
||||
o.inputstyle = 'action important';
|
||||
o.inputtitle = _('Flash image…');
|
||||
o.onclick = L.bind(this.handleSysupgrade, this);
|
||||
|
||||
o = s.option(form.SectionValue, 'actions', form.NamedSection, 'actions', 'actions', _('Diagnostic bundle'),
|
||||
_('Download the default diagnostic bundle from the AP.'));
|
||||
|
||||
ss = o.subsection;
|
||||
|
||||
o = ss.option(form.Button, 'Diagnostic');
|
||||
o.inputstyle = 'action important';
|
||||
o.inputtitle = _('Download Diagnostics');
|
||||
o.onclick = L.bind(this.handleDiagnostics, this);
|
||||
|
||||
|
||||
return m.render();
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,138 @@
|
||||
'use strict';
|
||||
'require view';
|
||||
'require form';
|
||||
'require fs';
|
||||
'require tools.ucentral as uctool';
|
||||
|
||||
var profile = null;
|
||||
|
||||
function serialize(data) {
|
||||
if (data.broadband.protocol != 'default')
|
||||
profile.broadband = Object.assign({}, data.broadband);
|
||||
else
|
||||
delete profile.broadband;
|
||||
|
||||
return JSON.stringify(profile, function(key, val) {
|
||||
return (key.charAt(0) != '.') ? val : undefined;
|
||||
}, '\t');
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function() {
|
||||
return L.resolveDefault(fs.read('/etc/ucentral/profile.json'), '').then(function(data) {
|
||||
try { profile = JSON.parse(data); }
|
||||
catch(e) { profile = {}; };
|
||||
|
||||
if (!L.isObject(profile.broadband))
|
||||
profile.broadband = { protocol: 'default' };
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var m, s, o, data = { broadband: {} };
|
||||
|
||||
m = new form.JSONMap(data);
|
||||
m.readonly = !L.hasViewPermission();
|
||||
|
||||
s = m.section(form.NamedSection, 'broadband', 'broadband', _('Uplink configuration'),
|
||||
_('The uplink settings allow overriding the WAN connection properties of the local device.'));
|
||||
|
||||
o = s.option(form.ListValue, 'protocol', _('Connection'));
|
||||
o.value('default', _('Use default cloud settings'));
|
||||
o.value('static', _('Static address configuration'));
|
||||
o.value('dhcp', _('Address configuration via DHCP'));
|
||||
o.value('pppoe', _('Address configuration via PPPoE'));
|
||||
o.value('wwan', _('Cellular network connection'));
|
||||
o.value('wds', _('WiFi WDS uplink'));
|
||||
|
||||
o = s.option(form.ListValue, 'modem-type', _('Modem type'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.value('wwan', _('Automatic', 'Automatic modem type selection'));
|
||||
o.value('mbim', 'MBIM');
|
||||
o.value('qmi', 'QMI');
|
||||
|
||||
o = s.option(form.Value, 'access-point-name', _('APN', 'Cellular access point name'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.validate = function(section_id, value) {
|
||||
if (!/^[a-zA-Z0-9\-.]*[a-zA-Z0-9]$/.test(value))
|
||||
return _('Invalid APN provided');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
o = s.option(form.Value, 'pin-code', _('PIN'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.datatype = 'and(uinteger,minlength(4),maxlength(8))';
|
||||
|
||||
o = s.option(form.ListValue, 'authentication-type', _('Authentication'));
|
||||
o.depends('protocol', 'wwan');
|
||||
o.value('', _('No authentication'));
|
||||
o.value('pap-chap', 'PAP/CHAP');
|
||||
o.value('chap', 'CHAP');
|
||||
o.value('pap', 'PAP');
|
||||
|
||||
o = s.option(form.Value, 'user-name', _('Username'));
|
||||
o.depends('authentication-type', 'pap-chap');
|
||||
o.depends('authentication-type', 'chap');
|
||||
o.depends('authentication-type', 'pap');
|
||||
o.depends('protocol', 'pppoe');
|
||||
|
||||
o = s.option(form.Value, 'password', _('Password'));
|
||||
o.depends('authentication-type', 'pap-chap');
|
||||
o.depends('authentication-type', 'chap');
|
||||
o.depends('authentication-type', 'pap');
|
||||
o.depends('protocol', 'pppoe');
|
||||
o.password = true;
|
||||
|
||||
o = s.option(form.Value, 'ipv4-address', _('IPv4 Address'), _('Address and mask in CIDR notation.'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'or(cidr4,ipnet4)';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'ipv4-gateway', _('IPv4 Gateway'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'ip4addr("nomask")';
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'ipv6-address', _('IPv6 Address'), _('Address and mask in CIDR notation.'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'or(cidr6,ipnet6)';
|
||||
|
||||
o = s.option(form.Value, 'ipv6-gateway', _('IPv6 Gateway'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'ip6addr("nomask")';
|
||||
|
||||
o = s.option(form.DynamicList, 'use-dns', _('DNS Servers'));
|
||||
o.depends('protocol', 'static');
|
||||
o.datatype = 'ipaddr("nomask")';
|
||||
|
||||
o = s.option(form.Value, 'ssid', _('SSID'));
|
||||
o.depends('protocol', 'wds');
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.Value, 'passphrase', _('Passphrase'));
|
||||
o.depends('protocol', 'wds');
|
||||
o.password = true;
|
||||
o.rmempty = false;
|
||||
o.datatype = "rangelength(8, 31)";
|
||||
|
||||
o = s.option(form.ListValue, 'encryption', _('Encryption'));
|
||||
o.depends('protocol', 'wds');
|
||||
o.value('psk', 'PSK');
|
||||
o.value('psk-mixed', 'PSK-Mixed');
|
||||
o.value('psk2', 'PSK2');
|
||||
o.value('sae', 'SAE');
|
||||
o.value('sae-mixed', 'SAE-Mixed');
|
||||
o.password = true;
|
||||
|
||||
for (var i = 0; i < s.children.length; i++)
|
||||
data.broadband[s.children[i].option] = profile.broadband[s.children[i].option];
|
||||
|
||||
return m.render();
|
||||
},
|
||||
|
||||
handleSave: uctool.save.bind(uctool, serialize),
|
||||
|
||||
handleSaveApply: null,
|
||||
handleReset: null
|
||||
});
|
||||
@@ -0,0 +1,426 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Project-Id-Version: \n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"X-Generator: Poedit 2.4.2\n"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:53
|
||||
msgctxt "Cellular access point name"
|
||||
msgid "APN"
|
||||
msgstr "APN"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "Address and mask in CIDR notation."
|
||||
msgstr "Adresse und Netzmaske in CIDR-Notation."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:43
|
||||
msgid "Address configuration via DHCP"
|
||||
msgstr "Adresskonfiguration mittels DHCP"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:44
|
||||
msgid "Address configuration via PPPoE"
|
||||
msgstr "Adresskonfiguration mittels PPPoE"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:49
|
||||
msgid "Apply Settings"
|
||||
msgstr "Einstellungen anwenden"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:52
|
||||
msgid "Apply settings and reboot device now"
|
||||
msgstr "Anwenden und Gerät jetzt neu starten"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:60
|
||||
msgid "Architecture"
|
||||
msgstr "Architektur"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:66
|
||||
msgid "Authentication"
|
||||
msgstr "Authentifizierung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:49
|
||||
msgctxt "Automatic modem type selection"
|
||||
msgid "Automatic"
|
||||
msgstr "automatisch"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:85
|
||||
msgid "Buffered"
|
||||
msgstr "Gepuffert"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:89
|
||||
msgid "Cached"
|
||||
msgstr "Gecached"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:92
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:45
|
||||
msgid "Cellular network connection"
|
||||
msgstr "Mobilfunkverbindung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:51
|
||||
msgid "Certificate validation failed: %s"
|
||||
msgstr "Validierung der Zertifikate fehlgeschlagen: %s"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:43
|
||||
msgid "Certificates"
|
||||
msgstr "Zertifikate"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:54
|
||||
msgid "Certificates updated."
|
||||
msgstr "Zertifikate aktualisiert."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:51
|
||||
msgid "Checking image…"
|
||||
msgstr "Prüfe Imagedatei…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:40
|
||||
msgid "Connection"
|
||||
msgstr "Verbindung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:84
|
||||
msgid "Continue"
|
||||
msgstr "Fortfahren"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:54
|
||||
msgid "Continue configuration"
|
||||
msgstr "Konfiguration fortsetzen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:104
|
||||
msgid "DNS Servers"
|
||||
msgstr "DNS-Server"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:37
|
||||
msgid "Do you really want to erase all settings?"
|
||||
msgstr "Sollen wirklich alle Systemeinstellungen zurückgesetzt werden?"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:73
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:61
|
||||
msgid "Firmware Version"
|
||||
msgstr "Firmware-Version"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:163
|
||||
msgid "Firmware upgrade"
|
||||
msgstr "Firmware Update"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Flash image?"
|
||||
msgstr "Image flashen?"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:99
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:170
|
||||
msgid "Flash image…"
|
||||
msgstr "Image flashen…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:104
|
||||
msgid "Flashing…"
|
||||
msgstr "Schreibt…"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/rpcd/acl.d/luci-mod-ucentral.json:3
|
||||
msgid "Grant access to ucentral configuration"
|
||||
msgstr "Zugriff auf uCentral-Konfigurationseinstellungen ermöglichen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:58
|
||||
msgid "Hostname"
|
||||
msgstr "Hostname"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
msgid "IPv4 Address"
|
||||
msgstr "IPv4-Adresse"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:91
|
||||
msgid "IPv4 Gateway"
|
||||
msgstr "IPv4-Gateway"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "IPv6 Address"
|
||||
msgstr "IPv6-Adresse"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:100
|
||||
msgid "IPv6 Gateway"
|
||||
msgstr "IPv6-Gateway"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:57
|
||||
msgid "Invalid APN provided"
|
||||
msgstr "Ungültige APN angegeben"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Invalid image"
|
||||
msgstr "Ungültige Image-Datei"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:141
|
||||
msgid "Issue a reboot and restart the operating system on this device."
|
||||
msgstr "Einen Reboot auslösen und das Betriebssystem des Gerätes neu starten."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:62
|
||||
msgid "Kernel Version"
|
||||
msgstr "Kernel-Version"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:65
|
||||
msgid "Load Average"
|
||||
msgstr "Systemlast"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:63
|
||||
msgid "Local Time"
|
||||
msgstr "Lokalzeit"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:37
|
||||
msgid "Local settings"
|
||||
msgstr "Lokale Einstellungen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:69
|
||||
msgid "MD5"
|
||||
msgstr "MD5"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:112
|
||||
msgid "Memory"
|
||||
msgstr "Arbeitsspeicher"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:59
|
||||
msgid "Model"
|
||||
msgstr "Modell"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:47
|
||||
msgid "Modem type"
|
||||
msgstr "Modemtyp"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:68
|
||||
msgid "No authentication"
|
||||
msgstr "keine Authentifizierung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:76
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:62
|
||||
msgid "PIN"
|
||||
msgstr "OIN"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:79
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:158
|
||||
msgid "Perform reset"
|
||||
msgstr "System zurücksetzen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:147
|
||||
msgid "Reboot"
|
||||
msgstr "Reboot"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:140
|
||||
msgid "Reboot device"
|
||||
msgstr "Gerät neu starten"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:35
|
||||
msgid "Reboot required"
|
||||
msgstr "Neustart erforderlich"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:40
|
||||
msgid "Redirector URL"
|
||||
msgstr "Redirector-URL"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:152
|
||||
msgid ""
|
||||
"Reset the system to its initial state and discard any configuration changes."
|
||||
msgstr ""
|
||||
"Das System auf Grundeinstellungen zurücksetzen und sämtliche "
|
||||
"Konfigurationsänderungen verwerfen."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:151
|
||||
msgid "Reset to defaults"
|
||||
msgstr "Grundeinstellungen wiederherstellen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:70
|
||||
msgid "SHA256"
|
||||
msgstr "SHA256"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:40
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:68
|
||||
msgid "Size"
|
||||
msgstr "Größe"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:42
|
||||
msgid "Static address configuration"
|
||||
msgstr "Statische Adresskonfiguration"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:16
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:92
|
||||
msgid "Swap free"
|
||||
msgstr "Freier Auslagerungsspeicher"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:108
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:52
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:121
|
||||
msgid ""
|
||||
"The device is rebooting now. This page will try to reconnect automatically "
|
||||
"once the device is fully booted."
|
||||
msgstr ""
|
||||
"Das Gerät startet jetzt neu. Diese Seite wird versuchen sich automatisch neu "
|
||||
"zu verbinden sobald das Gerät wieder voll hochgefahren ist."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:50
|
||||
msgid ""
|
||||
"The device must be rebooted in order to apply the changed settings. Once the "
|
||||
"uCentral agent successfully connects to the controller, the remote "
|
||||
"configuration profile will be applied and the initial provisioning web "
|
||||
"interface is disabled."
|
||||
msgstr ""
|
||||
"Das Gerät muss neu gestartet werden um die geänderten Einstellungen "
|
||||
"anzuwenden. Sobald sich der uCentral-Client nach dem Neustart erfolgreich "
|
||||
"mit dem Controller verbindet, wird das entfernte Konfigurationsprofil für "
|
||||
"dieses Gerät angewendet und das initiale Provisionierungs-Webinterface "
|
||||
"deaktiviert."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:74
|
||||
msgid ""
|
||||
"The firmware image is invalid and cannot be flashed. Check the diagnostics "
|
||||
"below for further details."
|
||||
msgstr ""
|
||||
"Die Firmware-Datei ist ungültig und kann nicht geflasht werden. Die "
|
||||
"nachfolgenden Diagnosemeldungen enthalten weitere Details."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:66
|
||||
msgid ""
|
||||
"The firmware image was uploaded. Compare the checksum and file size listed "
|
||||
"below with the original file to ensure data integrity. <br /> Click "
|
||||
"'Continue' below to start the flash procedure."
|
||||
msgstr ""
|
||||
"Die Firmware-Datei wurde hochgeladen. Die Prüfsummen und Dateigröße mit der "
|
||||
"Originaldatei vergleichen um die Integrität des Images sicherzustellen.<br /"
|
||||
"> \"Fortfahren\" anklicken um die Upgrade-Prozedur zu starten."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:22
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:117
|
||||
msgid "The reboot command failed with code %d"
|
||||
msgstr "Das Reboot-Kommando wurde mit Fehlercode %d abgebrochen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:38
|
||||
msgid ""
|
||||
"The settings on this page specify how the local uCentral client connects to "
|
||||
"the controller server."
|
||||
msgstr ""
|
||||
"Die Einstellungen auf dieser Seite beeinflussen die Verbindung des uCentral "
|
||||
"Clients zum Controller-Server."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:40
|
||||
msgid ""
|
||||
"The system is erasing the configuration partition now and will reboot itself "
|
||||
"when finished."
|
||||
msgstr ""
|
||||
"Das System löscht nun die Konfigurationspartition und startet das Gerät neu "
|
||||
"sobald die Prozedur abgeschlossen ist."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:106
|
||||
msgid ""
|
||||
"The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a "
|
||||
"few minutes before you try to reconnect. It might be necessary to renew the "
|
||||
"address of your computer to reach the device again, depending on your "
|
||||
"settings."
|
||||
msgstr ""
|
||||
"Das System-Upgrade läuft jetzt.<br />DAS GERÄT NICHT AUSSCHALTEN!<br /"
|
||||
">Einige Minuten warten, bevor ein Verbindungsversuch unternommen wird. Ja "
|
||||
"nach Netzwerktopologie kann es nötig sein, die lokalen Adresseinstellungen "
|
||||
"des Computers zu verändern bevor wieder eine Verbindung zum Gerät möglich "
|
||||
"ist."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:26
|
||||
msgid ""
|
||||
"The system is rebooting in order to attempt applying the remote "
|
||||
"configuration profile now. If not successful, the device will revert back "
|
||||
"into the initial provisioning state."
|
||||
msgstr ""
|
||||
"Das System startet jetzt neu um zu versuchen das entfernte "
|
||||
"Konfigurationsprofil anzuwenden. Im Fehlerfall wird das Gerät in den "
|
||||
"initialen Provisionierungs-Zustand zurückversetzt."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:38
|
||||
msgid ""
|
||||
"The uplink settings allow overriding the WAN connection properties of the "
|
||||
"local device."
|
||||
msgstr ""
|
||||
"Die Uplink-Einstellungen ermöglichen es die WAN-Verbindungseigenschaften des "
|
||||
"lokalen Gerätes zu überschreiben."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:83
|
||||
msgid "Total Available"
|
||||
msgstr "Gesamt verfügbar"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:95
|
||||
msgid "Unable to save settings: %s"
|
||||
msgstr "Einstellungen konnten nicht gespeichert werden: %s"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:58
|
||||
msgid "Unable to verify certificates: %s"
|
||||
msgstr "Zertifikate konnten nicht verifiziert werden: %s"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:41
|
||||
msgid "Unit location"
|
||||
msgstr "Gerätestandort"
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:28
|
||||
msgid "Uplink"
|
||||
msgstr "Uplink"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:37
|
||||
msgid "Uplink configuration"
|
||||
msgstr "Uplink-Einstellungen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:164
|
||||
msgid "Upload a compatible firmware image here to upgrade the running system."
|
||||
msgstr ""
|
||||
"Kompatible Firmware-Datei hier hochladen um das laufende System zu "
|
||||
"aktualisieren."
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:44
|
||||
msgid "Upload certificate bundle…"
|
||||
msgstr "Zertifikatsarchiv hochladen…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:64
|
||||
msgid "Uptime"
|
||||
msgstr "Laufzeit"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:41
|
||||
msgid "Use default cloud settings"
|
||||
msgstr "Cloud-Einstellungen nutzen"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:84
|
||||
msgid "Used"
|
||||
msgstr "In Benutzung"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:73
|
||||
msgid "Username"
|
||||
msgstr "Benutzername"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:47
|
||||
msgid "Verifying certificates…"
|
||||
msgstr "Überprüfe Zertifikate…"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:52
|
||||
msgid "Verifying the uploaded image file."
|
||||
msgstr "Überprüfe die hochgeladene Image-Datei."
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:3
|
||||
msgid "uCentral"
|
||||
msgstr "uCentral"
|
||||
@@ -0,0 +1,385 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:53
|
||||
msgctxt "Cellular access point name"
|
||||
msgid "APN"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "Address and mask in CIDR notation."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:43
|
||||
msgid "Address configuration via DHCP"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:44
|
||||
msgid "Address configuration via PPPoE"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:49
|
||||
msgid "Apply Settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:52
|
||||
msgid "Apply settings and reboot device now"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:60
|
||||
msgid "Architecture"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:66
|
||||
msgid "Authentication"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:49
|
||||
msgctxt "Automatic modem type selection"
|
||||
msgid "Automatic"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:85
|
||||
msgid "Buffered"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:89
|
||||
msgid "Cached"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:92
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:45
|
||||
msgid "Cellular network connection"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:51
|
||||
msgid "Certificate validation failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:43
|
||||
msgid "Certificates"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:54
|
||||
msgid "Certificates updated."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:51
|
||||
msgid "Checking image…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:40
|
||||
msgid "Connection"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:84
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:54
|
||||
msgid "Continue configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:104
|
||||
msgid "DNS Servers"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:37
|
||||
msgid "Do you really want to erase all settings?"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:73
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:61
|
||||
msgid "Firmware Version"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:163
|
||||
msgid "Firmware upgrade"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Flash image?"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:99
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:170
|
||||
msgid "Flash image…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:104
|
||||
msgid "Flashing…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/rpcd/acl.d/luci-mod-ucentral.json:3
|
||||
msgid "Grant access to ucentral configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:58
|
||||
msgid "Hostname"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:86
|
||||
msgid "IPv4 Address"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:91
|
||||
msgid "IPv4 Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:96
|
||||
msgid "IPv6 Address"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:100
|
||||
msgid "IPv6 Gateway"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:57
|
||||
msgid "Invalid APN provided"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:95
|
||||
msgid "Invalid image"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:141
|
||||
msgid "Issue a reboot and restart the operating system on this device."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:62
|
||||
msgid "Kernel Version"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:65
|
||||
msgid "Load Average"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:63
|
||||
msgid "Local Time"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:37
|
||||
msgid "Local settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:69
|
||||
msgid "MD5"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:112
|
||||
msgid "Memory"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:59
|
||||
msgid "Model"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:47
|
||||
msgid "Modem type"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:68
|
||||
msgid "No authentication"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:76
|
||||
msgid "OK"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:62
|
||||
msgid "PIN"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:79
|
||||
msgid "Password"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:158
|
||||
msgid "Perform reset"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:147
|
||||
msgid "Reboot"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:140
|
||||
msgid "Reboot device"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:35
|
||||
msgid "Reboot required"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:40
|
||||
msgid "Redirector URL"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:152
|
||||
msgid ""
|
||||
"Reset the system to its initial state and discard any configuration changes."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:151
|
||||
msgid "Reset to defaults"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:70
|
||||
msgid "SHA256"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:40
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:68
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:42
|
||||
msgid "Static address configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:16
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:92
|
||||
msgid "Swap free"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:108
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:52
|
||||
msgid "System"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:121
|
||||
msgid ""
|
||||
"The device is rebooting now. This page will try to reconnect automatically "
|
||||
"once the device is fully booted."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:50
|
||||
msgid ""
|
||||
"The device must be rebooted in order to apply the changed settings. Once the "
|
||||
"uCentral agent successfully connects to the controller, the remote "
|
||||
"configuration profile will be applied and the initial provisioning web "
|
||||
"interface is disabled."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:74
|
||||
msgid ""
|
||||
"The firmware image is invalid and cannot be flashed. Check the diagnostics "
|
||||
"below for further details."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:66
|
||||
msgid ""
|
||||
"The firmware image was uploaded. Compare the checksum and file size listed "
|
||||
"below with the original file to ensure data integrity. <br /> Click "
|
||||
"'Continue' below to start the flash procedure."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:22
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:117
|
||||
msgid "The reboot command failed with code %d"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:38
|
||||
msgid ""
|
||||
"The settings on this page specify how the local uCentral client connects to "
|
||||
"the controller server."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:40
|
||||
msgid ""
|
||||
"The system is erasing the configuration partition now and will reboot itself "
|
||||
"when finished."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:106
|
||||
msgid ""
|
||||
"The system is flashing now.<br /> DO NOT POWER OFF THE DEVICE!<br /> Wait a "
|
||||
"few minutes before you try to reconnect. It might be necessary to renew the "
|
||||
"address of your computer to reach the device again, depending on your "
|
||||
"settings."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:26
|
||||
msgid ""
|
||||
"The system is rebooting in order to attempt applying the remote "
|
||||
"configuration profile now. If not successful, the device will revert back "
|
||||
"into the initial provisioning state."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:38
|
||||
msgid ""
|
||||
"The uplink settings allow overriding the WAN connection properties of the "
|
||||
"local device."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:83
|
||||
msgid "Total Available"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/tools/ucentral.js:95
|
||||
msgid "Unable to save settings: %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:58
|
||||
msgid "Unable to verify certificates: %s"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:41
|
||||
msgid "Unit location"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:28
|
||||
msgid "Uplink"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:37
|
||||
msgid "Uplink configuration"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:164
|
||||
msgid "Upload a compatible firmware image here to upgrade the running system."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:44
|
||||
msgid "Upload certificate bundle…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:64
|
||||
msgid "Uptime"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:41
|
||||
msgid "Use default cloud settings"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/status.js:84
|
||||
msgid "Used"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/uplink.js:73
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/settings.js:47
|
||||
msgid "Verifying certificates…"
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/htdocs/luci-static/resources/view/ucentral/system.js:52
|
||||
msgid "Verifying the uploaded image file."
|
||||
msgstr ""
|
||||
|
||||
#: modules/luci-mod-ucentral/root/usr/share/luci/menu.d/luci-mod-ucentral.json:3
|
||||
msgid "uCentral"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
|
||||
# make sure we have a tar file
|
||||
[ -f /tmp/certs.tar ] || exit 1
|
||||
|
||||
. /lib/functions.sh
|
||||
|
||||
# amke sure the cert partition is mounted
|
||||
mount_certs
|
||||
|
||||
# make sure that this is a UBI volume
|
||||
ubi=$(grep certificates /proc/mounts | tail -n 1 | grep ubi)
|
||||
[ -z "$ubi" ] && exit 1
|
||||
|
||||
# extract the certificates
|
||||
mkdir -p /tmp/certs
|
||||
tar x -C /tmp/certs -f /tmp/certs.tar
|
||||
|
||||
# make sure the required files exist
|
||||
[ -f /tmp/certs/key.pem -a -f /tmp/certs/cert.pem ] || exit 1
|
||||
|
||||
# copy the certificates to /etc
|
||||
cp /tmp/certs/*.pem /certificates
|
||||
|
||||
# remove old operational certs
|
||||
rm /certificates/operational.*
|
||||
|
||||
# copy dev-id or gateway.json
|
||||
for a in gateway.json; do
|
||||
if [ -f /tmp/certs/$a ]; then
|
||||
cp /tmp/certs/$a /certificates
|
||||
else
|
||||
rm -f /certificates/$a
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/ucode
|
||||
push(REQUIRE_SEARCH_PATH, '/usr/share/ucentral/*.uc');
|
||||
let bundle = require('bundle');
|
||||
|
||||
bundle.init('maverick');
|
||||
include('/usr/share/ucentral/diagnostic.uc', { bundle });
|
||||
bundle.complete();
|
||||
system('chmod +r /tmp/bundle.maverick.tar.gz');
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
REDIRECTOR=$(cat /etc/ucentral/profile.json | jsonfilter -e '@.redirector')
|
||||
if [ -n "$REDIRECTOR" ]; then
|
||||
uci -c /etc/config-shadow/ set ucentral.config.server="$REDIRECTOR"
|
||||
uci -c /etc/config-shadow/ commit ucentral
|
||||
/etc/init.d/firstcontact disable
|
||||
/etc/init.d/ucentral enable
|
||||
else
|
||||
rm /etc/ucentral/redirector.json
|
||||
/etc/init.d/firstcontact enable
|
||||
/etc/init.d/ucentral disable
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"ucentral": {
|
||||
"title": "uCentral",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "firstchild",
|
||||
"recurse": true
|
||||
},
|
||||
"auth": {
|
||||
"methods": [ "cookie:sysauth", "cookie:sysauth_http", "cookie:sysauth_https" ],
|
||||
"login": true
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/status": {
|
||||
"title": "Status",
|
||||
"order": 1,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/status"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/uplink": {
|
||||
"title": "Uplink",
|
||||
"order": 2,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/uplink"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/settings": {
|
||||
"title": "Settings",
|
||||
"order": 3,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/settings"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
},
|
||||
|
||||
"ucentral/system": {
|
||||
"title": "System",
|
||||
"order": 4,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ucentral/system"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-mod-ucentral" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"luci-mod-ucentral": {
|
||||
"description": "Grant access to ucentral configuration",
|
||||
"read": {
|
||||
"cgi-io": [ "download" ],
|
||||
"file": {
|
||||
"/etc/ucentral/profile.json": [ "read" ],
|
||||
"/proc/mounts": [ "read" ],
|
||||
"/proc/mtd": [ "read" ],
|
||||
"/tmp/bundle.maverick.tar.gz": [ "read" ]
|
||||
},
|
||||
"ubus": {
|
||||
"file": [ "read" ],
|
||||
"system": [ "board", "info" ]
|
||||
}
|
||||
},
|
||||
"write": {
|
||||
"cgi-io": [ "upload" ],
|
||||
"file": {
|
||||
"/etc/ucentral/profile.json": [ "write" ],
|
||||
"/sbin/certupdate": [ "exec" ],
|
||||
"/sbin/diagnostic-bundle": [ "exec" ],
|
||||
"/sbin/firstboot -r -y": [ "exec" ],
|
||||
"/sbin/profileupdate": [ "exec" ],
|
||||
"/sbin/sysupgrade -n /tmp/firmware.bin": [ "exec" ],
|
||||
"/sbin/sysupgrade --test /tmp/firmware.bin": [ "exec" ],
|
||||
"/tmp/certs.tar": [ "write" ],
|
||||
"/tmp/firmware.bin": [ "write" ]
|
||||
},
|
||||
"ubus": {
|
||||
"file": [ "exec", "remove", "write" ],
|
||||
"system": [ "reboot", "validate_firmware_image" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# Copyright (C) 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI theme for uCentral
|
||||
LUCI_DEPENDS:=+luci-lua-runtime
|
||||
|
||||
include ../luci.mk
|
||||
|
||||
# call BuildPackage - OpenWrt buildroot signature
|
||||
@@ -0,0 +1,152 @@
|
||||
'use strict';
|
||||
'require baseclass';
|
||||
'require ui';
|
||||
|
||||
return baseclass.extend({
|
||||
__init__: function() {
|
||||
ui.menu.load().then(L.bind(this.render, this));
|
||||
},
|
||||
|
||||
render: function(tree) {
|
||||
var menu = document.querySelector('#mainmenu'),
|
||||
nav = document.querySelector('#menubar > .navigation'),
|
||||
node = tree,
|
||||
url = '';
|
||||
|
||||
this.renderModeMenu(node);
|
||||
|
||||
if (L.env.dispatchpath.length >= 3) {
|
||||
for (var i = 0; i < 3 && node; i++) {
|
||||
node = node.children[L.env.dispatchpath[i]];
|
||||
url = url + (url ? '/' : '') + L.env.dispatchpath[i];
|
||||
}
|
||||
|
||||
if (node)
|
||||
this.renderTabMenu(node, url);
|
||||
}
|
||||
|
||||
if (menu.firstElementChild) {
|
||||
nav.addEventListener('click', ui.createHandlerFn(this, 'handleSidebarToggle'));
|
||||
nav.style.visibility = 'visible';
|
||||
}
|
||||
},
|
||||
|
||||
handleMenuExpand: function(ev) {
|
||||
var a = ev.target, ul1 = a.parentNode.parentNode, ul2 = a.nextElementSibling;
|
||||
|
||||
document.querySelectorAll('ul.mainmenu.l1 > li.active').forEach(function(li) {
|
||||
if (li !== a.parentNode)
|
||||
li.classList.remove('active');
|
||||
});
|
||||
|
||||
if (!ul2)
|
||||
return;
|
||||
|
||||
if (ul2.parentNode.offsetLeft + ul2.offsetWidth <= ul1.offsetLeft + ul1.offsetWidth)
|
||||
ul2.classList.add('align-left');
|
||||
|
||||
ul1.classList.add('active');
|
||||
a.parentNode.classList.add('active');
|
||||
a.blur();
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
},
|
||||
|
||||
renderMainMenu: function(tree, url, level) {
|
||||
var l = (level || 0) + 1,
|
||||
ul = E('ul', { 'class': 'mainmenu l%d'.format(l) }),
|
||||
children = ui.menu.getChildren(tree);
|
||||
|
||||
if (children.length == 0 || l > 2)
|
||||
return E([]);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.dispatchpath[l] == children[i].name),
|
||||
isReadonly = children[i].readonly,
|
||||
activeClass = 'mainmenu-item-%s%s'.format(children[i].name, isActive ? ' selected' : '');
|
||||
|
||||
ul.appendChild(E('li', { 'class': activeClass }, [
|
||||
E('a', {
|
||||
'href': L.url(url, children[i].name),
|
||||
'click': (l == 1) ? ui.createHandlerFn(this, 'handleMenuExpand') : null
|
||||
}, [ _(children[i].title) ]),
|
||||
this.renderMainMenu(children[i], url + '/' + children[i].name, l)
|
||||
]));
|
||||
}
|
||||
|
||||
if (l == 1)
|
||||
document.querySelector('#mainmenu').appendChild(E('div', [ ul ]));
|
||||
|
||||
return ul;
|
||||
},
|
||||
|
||||
renderModeMenu: function(tree, root) {
|
||||
var menu = document.querySelector('#modemenu'),
|
||||
children = ui.menu.getChildren(tree);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.requestpath.length ? children[i].name == L.env.requestpath[+!!root] : i == 0),
|
||||
isUcentral = (!root && children[i].name == 'ucentral');
|
||||
|
||||
if (root || children.length > 1)
|
||||
menu.appendChild(E('div', { 'class': isActive ? 'active' : null }, [
|
||||
E('a', { 'href': root ? L.url(root, children[i].name) : L.url(children[i].name) }, [ _(children[i].title) ])
|
||||
]));
|
||||
|
||||
if (isUcentral && isActive)
|
||||
this.renderModeMenu(children[i], children[i].name);
|
||||
else if (isActive)
|
||||
this.renderMainMenu(children[i], children[i].name);
|
||||
}
|
||||
|
||||
if (menu.children.length > 1)
|
||||
menu.style.display = '';
|
||||
},
|
||||
|
||||
renderTabMenu: function(tree, url, level) {
|
||||
var container = document.querySelector('#tabmenu'),
|
||||
l = (level || 0) + 1,
|
||||
ul = E('ul', { 'class': 'cbi-tabmenu' }),
|
||||
children = ui.menu.getChildren(tree),
|
||||
activeNode = null;
|
||||
|
||||
if (children.length == 0)
|
||||
return E([]);
|
||||
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var isActive = (L.env.dispatchpath[l + 2] == children[i].name),
|
||||
activeClass = isActive ? ' cbi-tab' : '',
|
||||
className = 'tabmenu-item-%s %s'.format(children[i].name, activeClass);
|
||||
|
||||
ul.appendChild(E('li', { 'class': className }, [
|
||||
E('a', { 'href': L.url(url, children[i].name) }, [ _(children[i].title) ] )
|
||||
]));
|
||||
|
||||
if (isActive)
|
||||
activeNode = children[i];
|
||||
}
|
||||
|
||||
container.appendChild(ul);
|
||||
container.style.display = '';
|
||||
|
||||
if (activeNode)
|
||||
container.appendChild(this.renderTabMenu(activeNode, url + '/' + activeNode.name, l));
|
||||
|
||||
return ul;
|
||||
},
|
||||
|
||||
handleSidebarToggle: function(ev) {
|
||||
var btn = ev.currentTarget,
|
||||
bar = document.querySelector('#mainmenu');
|
||||
|
||||
if (btn.classList.contains('active')) {
|
||||
btn.classList.remove('active');
|
||||
bar.classList.remove('active');
|
||||
}
|
||||
else {
|
||||
btn.classList.add('active');
|
||||
bar.classList.add('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 251.2 114.2" style="enable-background:new 0 0 251.2 114.2;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FED206;}
|
||||
.st1{fill:#EB6F53;}
|
||||
.st2{fill:#3BA9B6;}
|
||||
.st3{fill:#414141;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M219.6,43.3C219.5,43.3,219.5,43.3,219.6,43.3c-1.3,0-2.2-1-2.2-2.2c0-0.2,0-0.4,0-0.6
|
||||
c0-11.9-9.7-21.6-21.6-21.6c-0.2,0-0.4,0-0.6,0c-1.2,0-2.2-0.9-2.2-2.1c0-1.2,0.9-2.2,2.1-2.2c0.2,0,0.5,0,0.7,0
|
||||
c14.3,0,25.9,11.6,25.9,25.9c0,0.2,0,0.5,0,0.7C221.7,42.4,220.7,43.3,219.6,43.3z"/>
|
||||
<path class="st1" d="M212.1,43.3C212,43.3,212,43.3,212.1,43.3c-1.3-0.1-2.2-1.1-2.2-2.3c0-0.2,0-0.4,0-0.6
|
||||
c0-7.7-6.3-14.1-14.1-14.1c-0.2,0-0.4,0-0.6,0c-1.2,0.1-2.2-0.9-2.3-2.1c0-1.2,0.9-2.2,2.1-2.3c0.3,0,0.5,0,0.8,0
|
||||
c10.2,0,18.4,8.3,18.4,18.4c0,0.2,0,0.5,0,0.8C214.2,42.4,213.2,43.3,212.1,43.3z"/>
|
||||
<path class="st2" d="M204.3,43.3c-0.1,0-0.1,0-0.2,0c-1.2-0.1-2.1-1.1-2-2.3c0-0.2,0-0.4,0-0.5c0-3.5-2.8-6.3-6.3-6.3
|
||||
c-0.1,0-0.3,0-0.5,0c-1.2,0.1-2.3-0.8-2.3-2c-0.1-1.2,0.8-2.3,2-2.3c0.3,0,0.6,0,0.9,0c5.9,0,10.7,4.8,10.7,10.7c0,0.3,0,0.5,0,0.9
|
||||
C206.4,42.4,205.4,43.3,204.3,43.3z"/>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st3" d="M61.9,89.9v-4.7h-1.7v-0.9h4.4v0.9h-1.7v4.7H61.9z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M65.6,89.9v-5.6h3.8v0.9h-2.9v1.4h2.8v0.9h-2.8V89h2.9v0.9H65.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M70.7,89.9v-5.6h1V89h2.5v0.9H70.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M74.9,89.9v-5.6h3.8v0.9h-2.9v1.4h2.8v0.9h-2.8V89h2.9v0.9H74.9z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M79.8,87.1c0-1.7,1.3-2.9,2.9-2.9c1.1,0,1.8,0.6,2.2,1.3l-0.8,0.4c-0.3-0.5-0.8-0.8-1.4-0.8
|
||||
c-1.1,0-1.9,0.8-1.9,2c0,1.2,0.8,2,1.9,2c0.6,0,1.1-0.4,1.4-0.8l0.8,0.4c-0.4,0.7-1.1,1.3-2.2,1.3C81.1,90,79.8,88.8,79.8,87.1z
|
||||
"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M85.5,87.1c0-1.7,1.2-2.9,2.9-2.9c1.7,0,2.9,1.2,2.9,2.9S90,90,88.3,90C86.7,90,85.5,88.8,85.5,87.1z
|
||||
M90.2,87.1c0-1.2-0.7-2-1.9-2c-1.1,0-1.9,0.9-1.9,2c0,1.1,0.7,2,1.9,2C89.5,89.1,90.2,88.3,90.2,87.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M96.9,89.9v-4.3l-1.7,4.3h-0.4l-1.7-4.3v4.3h-1v-5.6h1.4l1.5,3.8l1.5-3.8h1.4v5.6H96.9z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M103,89.9v-5.6h1v5.6H103z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M109.7,89.9l-2.9-4v4h-1v-5.6h1l2.9,3.9v-3.9h1v5.6H109.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M112.4,89.9v-5.6h3.8v0.9h-2.9v1.4h2.8v0.9h-2.8v2.4H112.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M120.3,89.9l-1.2-2.1h-1v2.1h-1v-5.6h2.5c1.1,0,1.8,0.7,1.8,1.8c0,1-0.7,1.5-1.3,1.6l1.4,2.2H120.3z
|
||||
M120.4,86.1c0-0.5-0.4-0.9-1-0.9h-1.4V87h1.4C120,87,120.4,86.6,120.4,86.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M126.6,89.9l-0.4-1.1h-2.6l-0.4,1.1h-1.1l2.2-5.6h1.2l2.2,5.6H126.6z M124.9,85.3l-1,2.7h2L124.9,85.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M131.4,89.9v-5.6h2.1c1.1,0,1.7,0.8,1.7,1.6c0,0.9-0.6,1.6-1.7,1.6h-1.6v2.3H131.4z M134.7,86
|
||||
c0-0.7-0.5-1.2-1.2-1.2h-1.6v2.4h1.6C134.2,87.2,134.7,86.6,134.7,86z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M139.4,89.9l-1.6-2.3h-1.2v2.3h-0.5v-5.6h2.1c1,0,1.7,0.6,1.7,1.6c0,1-0.7,1.6-1.6,1.6l1.6,2.3H139.4z
|
||||
M139.4,86c0-0.7-0.5-1.2-1.2-1.2h-1.6v2.4h1.6C138.9,87.2,139.4,86.7,139.4,86z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M141.2,87.1c0-1.6,1.1-2.9,2.7-2.9c1.6,0,2.7,1.3,2.7,2.9c0,1.6-1.1,2.9-2.7,2.9
|
||||
C142.3,90,141.2,88.8,141.2,87.1z M146.1,87.1c0-1.4-0.9-2.5-2.2-2.5c-1.4,0-2.2,1-2.2,2.5c0,1.4,0.9,2.5,2.2,2.5
|
||||
C145.2,89.6,146.1,88.5,146.1,87.1z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M147,89.3l0.3-0.4c0.3,0.3,0.6,0.6,1.1,0.6c0.8,0,1.2-0.5,1.2-1.3v-4h0.5v4c0,1.2-0.8,1.7-1.7,1.7
|
||||
C147.9,90,147.4,89.8,147,89.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M151.8,89.9v-5.6h3.5v0.4h-3.1v2.1h3v0.4h-3v2.2h3.1v0.4H151.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M156.3,87.1c0-1.7,1.3-2.9,2.8-2.9c0.9,0,1.6,0.4,2,1l-0.4,0.3c-0.4-0.5-1-0.8-1.6-0.8
|
||||
c-1.3,0-2.3,1-2.3,2.5c0,1.4,1,2.5,2.3,2.5c0.7,0,1.3-0.3,1.6-0.8l0.4,0.3c-0.5,0.6-1.2,1-2,1C157.5,90,156.3,88.8,156.3,87.1z"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M163.5,89.9v-5.2h-1.8v-0.4h4.1v0.4H164v5.2H163.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<polygon class="st3" points="33.7,86.5 41.2,79 48.6,86.5 49.8,86.5 41.2,77.9 32.6,86.5 "/>
|
||||
<polygon class="st3" points="48.6,87.8 41.2,95.2 33.7,87.8 32.6,87.8 41.2,96.4 49.8,87.8 "/>
|
||||
<polygon class="st3" points="40.3,86.5 47.8,79 55.3,86.5 56.4,86.5 47.8,77.9 39.2,86.5 "/>
|
||||
<polygon class="st3" points="55.3,87.8 47.8,95.2 40.3,87.8 39.2,87.8 47.8,96.4 56.4,87.8 "/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M51.2,41.3c2,1.1,3.6,2.6,4.7,4.5c1.1,1.9,1.7,4,1.7,6.4c0,2.3-0.6,4.5-1.7,6.4c-1.1,1.9-2.7,3.4-4.7,4.6
|
||||
c-2,1.1-4.2,1.7-6.6,1.7c-2.4,0-4.6-0.6-6.6-1.7c-2-1.1-3.6-2.6-4.7-4.6c-1.1-1.9-1.7-4.1-1.7-6.4c0-2.3,0.6-4.5,1.7-6.4
|
||||
c1.1-1.9,2.7-3.4,4.7-4.5c2-1.1,4.2-1.6,6.6-1.6C47,39.6,49.2,40.2,51.2,41.3z M40.5,44.9c-1.3,0.7-2.3,1.7-3,3
|
||||
c-0.7,1.3-1.1,2.7-1.1,4.2s0.4,3,1.1,4.2c0.8,1.3,1.8,2.3,3,3c1.3,0.7,2.7,1.1,4.1,1.1c1.5,0,2.8-0.4,4.1-1.1c1.3-0.7,2.3-1.8,3-3
|
||||
c0.7-1.3,1.1-2.7,1.1-4.2s-0.4-2.9-1.1-4.2c-0.7-1.3-1.7-2.3-3-3c-1.3-0.7-2.6-1.1-4.1-1.1C43.2,43.8,41.8,44.2,40.5,44.9z"/>
|
||||
<path class="st3" d="M76.9,46.8c1.3,0.8,2.4,1.9,3.1,3.4c0.7,1.4,1.1,3.1,1.1,5c0,1.9-0.4,3.5-1.1,4.9c-0.7,1.4-1.8,2.5-3.1,3.3
|
||||
c-1.3,0.8-2.9,1.2-4.6,1.2c-1.4,0-2.6-0.3-3.7-0.8c-1.1-0.5-2-1.3-2.7-2.4v9.8h-4.6V45.7H66v3.1c0.7-1.1,1.5-1.8,2.6-2.4
|
||||
c1.1-0.5,2.3-0.8,3.7-0.8C74,45.6,75.6,46,76.9,46.8z M75.1,59.1c1-1.1,1.5-2.4,1.5-4.1c0-1.7-0.5-3-1.5-4.1
|
||||
c-1-1.1-2.2-1.6-3.8-1.6c-1.6,0-2.8,0.5-3.8,1.6c-1,1-1.5,2.4-1.5,4.1c0,1.7,0.5,3,1.5,4.1c1,1.1,2.3,1.6,3.8,1.6
|
||||
C72.8,60.7,74.1,60.2,75.1,59.1z"/>
|
||||
<path class="st3" d="M99.3,48.1c1.5,1.7,2.3,4.1,2.3,7.2c0,0.6,0,1.1,0,1.4H87.7c0.3,1.3,0.9,2.4,1.9,3.1c0.9,0.8,2.1,1.1,3.5,1.1
|
||||
c1,0,1.9-0.2,2.7-0.5c0.9-0.4,1.6-0.9,2.3-1.6l2.5,2.6c-0.9,1-2.1,1.8-3.4,2.4c-1.3,0.6-2.8,0.8-4.5,0.8c-1.9,0-3.6-0.4-5.1-1.2
|
||||
c-1.5-0.8-2.6-1.9-3.4-3.3c-0.8-1.4-1.2-3.1-1.2-5c0-1.9,0.4-3.5,1.2-5c0.8-1.4,1.9-2.6,3.4-3.4c1.4-0.8,3.1-1.2,4.9-1.2
|
||||
C95.5,45.6,97.8,46.4,99.3,48.1z M97.4,53.6c0-1.4-0.5-2.5-1.4-3.3c-0.9-0.8-2-1.2-3.4-1.2c-1.3,0-2.4,0.4-3.3,1.2
|
||||
c-0.9,0.8-1.5,1.9-1.7,3.3H97.4z"/>
|
||||
<path class="st3" d="M121.5,47.5c1.2,1.3,1.9,3.1,1.9,5.3v11.7h-4.6V54.1c0-1.3-0.4-2.3-1.1-3.1c-0.7-0.8-1.8-1.1-3-1.1
|
||||
c-1.5,0-2.7,0.5-3.6,1.5s-1.3,2.3-1.3,3.8v9.2h-4.5V45.7h4.5v3.5c1.3-2.4,3.5-3.6,6.7-3.7C118.5,45.5,120.2,46.2,121.5,47.5z"/>
|
||||
<path class="st3" d="M156.5,39.9h4.9l-8.3,24.5h-4.9l-5.6-18.6l-5.7,18.6h-4.8l-8.3-24.5h5l5.8,19.4l5.7-19.4h4.6l5.8,19.5
|
||||
L156.5,39.9z"/>
|
||||
<path class="st3" d="M168,38.4c0.5,0.5,0.7,1.2,0.7,2c0,0.8-0.2,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.9,0.8c-0.7,0-1.4-0.3-1.9-0.8
|
||||
c-0.5-0.5-0.7-1.2-0.7-1.9c0-0.8,0.2-1.4,0.7-2c0.5-0.5,1.1-0.8,1.9-0.8C166.9,37.7,167.6,37.9,168,38.4z M164,45.7h4.5v18.7H164
|
||||
V45.7z"/>
|
||||
<path class="st3" d="M174,39.9h16.9l0,4.1h-12.2v6.6h11.1v4.1h-11.1v9.7H174V39.9z"/>
|
||||
<path class="st3" d="M197.9,38.4c0.5,0.5,0.7,1.2,0.7,2c0,0.8-0.2,1.4-0.7,1.9c-0.5,0.5-1.1,0.8-1.9,0.8c-0.7,0-1.4-0.3-1.9-0.8
|
||||
c-0.5-0.5-0.7-1.2-0.7-1.9c0-0.8,0.2-1.4,0.7-2c0.5-0.5,1.1-0.8,1.9-0.8C196.8,37.7,197.4,37.9,197.9,38.4z M193.8,45.7h4.5v18.7
|
||||
h-4.5V45.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="132 132 264 264">
|
||||
<defs>
|
||||
<radialGradient id="g" cx="0%" cy="0%" r="60%">
|
||||
<stop offset=".8" style="stop-opacity:1" />
|
||||
<stop offset="1" style="stop-opacity:.5" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path style="fill:url(#g)" d="M 264 132 A 132 132 0 0 0 132 264 A 132 132 0 0 0 264 396 A 132 132 0 0 0 396 264 A 132 132 0 0 0 264 132 z M 264 170 A 94 94 0 0 1 359 264 A 94 94 0 0 1 264 359 A 94 94 0 0 1 170 264 A 94 94 0 0 1 264 170 z " />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 582 B |
@@ -0,0 +1,13 @@
|
||||
<%#
|
||||
Copyright 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">L.require('menu-ucentral')</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,67 @@
|
||||
<%#
|
||||
Copyright 2021 Jo-Philipp Wich <jo@mein.io>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%
|
||||
local sys = require "luci.sys"
|
||||
local util = require "luci.util"
|
||||
local http = require "luci.http"
|
||||
local disp = require "luci.dispatcher"
|
||||
local ver = require "luci.version"
|
||||
|
||||
local boardinfo = util.ubus("system", "board") or { }
|
||||
|
||||
local node = disp.context.dispatched
|
||||
local path = table.concat(disp.context.path, "-")
|
||||
|
||||
http.prepare_content("text/html; charset=UTF-8")
|
||||
-%>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%=luci.i18n.context.lang%>">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Script-Type" content="text/javascript" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="<%=media%>/cascade.css" />
|
||||
<link rel="icon" href="<%=media%>/logo.svg" type="image/svg+xml" />
|
||||
<script type="text/javascript" src="<%=url('admin/translations', luci.i18n.context.lang)%><%# ?v=PKG_VERSION %>"></script>
|
||||
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
|
||||
<title><%=striptags( (boardinfo.hostname or "?") .. ( (node and node.title) and ' - ' .. translate(node.title) or '')) %> - LuCI</title>
|
||||
<% if css then %><style title="text/css">
|
||||
<%= css %>
|
||||
</style>
|
||||
<% end -%>
|
||||
</head>
|
||||
<body class="lang_<%=luci.i18n.context.lang%>" data-page="<%= pcdata(path) %>">
|
||||
|
||||
<p class="skiplink">
|
||||
<span id="skiplink1"><a href="#navigation"><%:Skip to navigation%></a></span>
|
||||
<span id="skiplink2"><a href="#content"><%:Skip to content%></a></span>
|
||||
</p>
|
||||
|
||||
|
||||
<div id="page">
|
||||
<div id="menubar">
|
||||
<h2 class="navigation" style="visibility:hidden"><a id="navigation" name="navigation"><%:Navigation%></a></h2>
|
||||
<img src="<%=media%>/logo.svg" />
|
||||
<span id="indicators"></span>
|
||||
</div>
|
||||
|
||||
<div id="modemenu" style="display:none"></div>
|
||||
|
||||
<div id="maincontainer">
|
||||
<div id="mainmenu"></div>
|
||||
|
||||
<div id="maincontent">
|
||||
<%- if luci.sys.process.info("uid") == 0 and luci.sys.user.getuser("root") and not luci.sys.user.getpasswd("root") and path ~= "admin-system-admin-password" then -%>
|
||||
<div class="alert-message warning">
|
||||
<h4><%:No password set!%></h4>
|
||||
<p><%:There is no password set on this router. Please configure a root password to protect the web interface.%></p>
|
||||
<% if disp.lookup("admin/system/admin") then %>
|
||||
<div class="right"><a class="btn" href="<%=url("admin/system/admin")%>"><%:Go to password configuration...%></a></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%- end -%>
|
||||
|
||||
<div id="tabmenu" style="display:none"></div>
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$PKG_UPGRADE" != 1 ]; then
|
||||
uci get luci.themes.uCentral >/dev/null 2>&1 || \
|
||||
uci batch <<-EOF
|
||||
set luci.themes.uCentral=/luci-static/ucentral
|
||||
set luci.main.mediaurlbase=/luci-static/ucentral
|
||||
commit luci
|
||||
EOF
|
||||
fi
|
||||
|
||||
exit 0
|
||||
294
olg-ucentral-client-sdk/feeds/tip/luci/luci.mk
Normal file
294
olg-ucentral-client-sdk/feeds/tip/luci/luci.mk
Normal file
@@ -0,0 +1,294 @@
|
||||
#
|
||||
# Copyright (C) 2008-2015 The LuCI Team <luci@lists.subsignal.org>
|
||||
#
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
|
||||
LUCI_NAME?=$(notdir ${CURDIR})
|
||||
LUCI_TYPE?=$(word 2,$(subst -, ,$(LUCI_NAME)))
|
||||
LUCI_BASENAME?=$(patsubst luci-$(LUCI_TYPE)-%,%,$(LUCI_NAME))
|
||||
LUCI_LANGUAGES:=$(sort $(filter-out templates,$(notdir $(wildcard ${CURDIR}/po/*))))
|
||||
LUCI_DEFAULTS:=$(notdir $(wildcard ${CURDIR}/root/etc/uci-defaults/*))
|
||||
LUCI_PKGARCH?=$(if $(realpath src/Makefile),,all)
|
||||
|
||||
# Language code titles
|
||||
LUCI_LANG.bg=български (Bulgarian)
|
||||
LUCI_LANG.bn_BD=বাংলা (Bengali)
|
||||
LUCI_LANG.ca=Català (Catalan)
|
||||
LUCI_LANG.cs=Čeština (Czech)
|
||||
LUCI_LANG.de=Deutsch (German)
|
||||
LUCI_LANG.el=Ελληνικά (Greek)
|
||||
LUCI_LANG.en=English
|
||||
LUCI_LANG.es=Español (Spanish)
|
||||
LUCI_LANG.fr=Français (French)
|
||||
LUCI_LANG.he=עִבְרִית (Hebrew)
|
||||
LUCI_LANG.hi=हिंदी (Hindi)
|
||||
LUCI_LANG.hu=Magyar (Hungarian)
|
||||
LUCI_LANG.it=Italiano (Italian)
|
||||
LUCI_LANG.ja=日本語 (Japanese)
|
||||
LUCI_LANG.ko=한국어 (Korean)
|
||||
LUCI_LANG.mr=Marāṭhī (Marathi)
|
||||
LUCI_LANG.ms=Bahasa Melayu (Malay)
|
||||
LUCI_LANG.nb_NO=Norsk (Norwegian)
|
||||
LUCI_LANG.pl=Polski (Polish)
|
||||
LUCI_LANG.pt_BR=Português do Brasil (Brazilian Portuguese)
|
||||
LUCI_LANG.pt=Português (Portuguese)
|
||||
LUCI_LANG.ro=Română (Romanian)
|
||||
LUCI_LANG.ru=Русский (Russian)
|
||||
LUCI_LANG.sk=Slovenčina (Slovak)
|
||||
LUCI_LANG.sv=Svenska (Swedish)
|
||||
LUCI_LANG.tr=Türkçe (Turkish)
|
||||
LUCI_LANG.uk=Українська (Ukrainian)
|
||||
LUCI_LANG.vi=Tiếng Việt (Vietnamese)
|
||||
LUCI_LANG.zh_Hans=简体中文 (Chinese Simplified)
|
||||
LUCI_LANG.zh_Hant=繁體中文 (Chinese Traditional)
|
||||
|
||||
# Submenu titles
|
||||
LUCI_MENU.col=1. Collections
|
||||
LUCI_MENU.mod=2. Modules
|
||||
LUCI_MENU.app=3. Applications
|
||||
LUCI_MENU.theme=4. Themes
|
||||
LUCI_MENU.proto=5. Protocols
|
||||
LUCI_MENU.lib=6. Libraries
|
||||
|
||||
# Language aliases
|
||||
LUCI_LC_ALIAS.bn_BD=bn
|
||||
LUCI_LC_ALIAS.nb_NO=no
|
||||
LUCI_LC_ALIAS.pt_BR=pt-br
|
||||
LUCI_LC_ALIAS.zh_Hans=zh-cn
|
||||
LUCI_LC_ALIAS.zh_Hant=zh-tw
|
||||
|
||||
|
||||
PKG_NAME?=$(LUCI_NAME)
|
||||
|
||||
|
||||
# 1: everything expect po subdir or only po subdir
|
||||
define findrev
|
||||
$(shell \
|
||||
if git log -1 >/dev/null 2>/dev/null; then \
|
||||
set -- $$(git log -1 --format="%ct %h" --abbrev=7 -- $(if $(1),. ':(exclude)po',po)); \
|
||||
if [ -n "$$1" ]; then
|
||||
secs="$$(($$1 % 86400))"; \
|
||||
yday="$$(date --utc --date="@$$1" "+%y.%j")"; \
|
||||
printf 'git-%s.%05d-%s' "$$yday" "$$secs" "$$2"; \
|
||||
else \
|
||||
echo "unknown"; \
|
||||
fi; \
|
||||
else \
|
||||
ts=$$(find . -type f $(if $(1),-not) -path './po/*' -printf '%T@\n' 2>/dev/null | sort -rn | head -n1 | cut -d. -f1); \
|
||||
if [ -n "$$ts" ]; then \
|
||||
secs="$$(($$ts % 86400))"; \
|
||||
date="$$(date --utc --date="@$$ts" "+%y%m%d")"; \
|
||||
printf '%s.%05d' "$$date" "$$secs"; \
|
||||
else \
|
||||
echo "unknown"; \
|
||||
fi; \
|
||||
fi \
|
||||
)
|
||||
endef
|
||||
|
||||
PKG_PO_VERSION?=$(if $(DUMP),x,$(strip $(call findrev)))
|
||||
PKG_SRC_VERSION?=$(if $(DUMP),x,$(strip $(call findrev,1)))
|
||||
|
||||
PKG_GITBRANCH?=$(if $(DUMP),x,$(strip $(shell \
|
||||
variant="LuCI"; \
|
||||
if git log -1 >/dev/null 2>/dev/null; then \
|
||||
branch="$$(git branch --remote --verbose --no-abbrev --contains 2>/dev/null | \
|
||||
sed -rne 's|^[^/]+/([^ ]+) [a-f0-9]{40} .+$$|\1|p' | head -n1)"; \
|
||||
if [ "$$branch" != "master" ]; then \
|
||||
variant="LuCI $$branch branch"; \
|
||||
else \
|
||||
variant="LuCI Master"; \
|
||||
fi; \
|
||||
fi; \
|
||||
echo "$$variant" \
|
||||
)))
|
||||
|
||||
PKG_RELEASE?=1
|
||||
PKG_INSTALL:=$(if $(realpath src/Makefile),1)
|
||||
PKG_BUILD_DEPENDS += lua/host luci-base/host LUCI_CSSTIDY:csstidy/host LUCI_SRCDIET:luasrcdiet/host $(LUCI_BUILD_DEPENDS)
|
||||
PKG_CONFIG_DEPENDS += CONFIG_LUCI_SRCDIET CONFIG_LUCI_JSMIN CONFIG_LUCI_CSSTIDY
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=luci
|
||||
CATEGORY:=LuCI
|
||||
SUBMENU:=$(if $(LUCI_MENU.$(LUCI_TYPE)),$(LUCI_MENU.$(LUCI_TYPE)),$(LUCI_MENU.app))
|
||||
TITLE:=$(if $(LUCI_TITLE),$(LUCI_TITLE),LuCI $(LUCI_NAME) $(LUCI_TYPE))
|
||||
DEPENDS:=$(LUCI_DEPENDS)
|
||||
VERSION:=$(if $(PKG_VERSION),$(PKG_VERSION),$(PKG_SRC_VERSION))
|
||||
$(if $(LUCI_EXTRA_DEPENDS),EXTRA_DEPENDS:=$(LUCI_EXTRA_DEPENDS))
|
||||
$(if $(LUCI_PKGARCH),PKGARCH:=$(LUCI_PKGARCH))
|
||||
endef
|
||||
|
||||
ifneq ($(LUCI_DESCRIPTION),)
|
||||
define Package/$(PKG_NAME)/description
|
||||
$(strip $(LUCI_DESCRIPTION))
|
||||
endef
|
||||
endif
|
||||
|
||||
# Language selection for luci-base
|
||||
ifeq ($(PKG_NAME),luci-base)
|
||||
define Package/luci-base/config
|
||||
config LUCI_SRCDIET
|
||||
bool "Minify Lua sources"
|
||||
default n
|
||||
|
||||
config LUCI_JSMIN
|
||||
bool "Minify JavaScript sources"
|
||||
default y
|
||||
|
||||
config LUCI_CSSTIDY
|
||||
bool "Minify CSS files"
|
||||
default y
|
||||
|
||||
menu "Translations"$(foreach lang,$(LUCI_LANGUAGES),
|
||||
|
||||
config LUCI_LANG_$(lang)
|
||||
tristate "$(shell echo '$(LUCI_LANG.$(lang))' | sed -e 's/^.* (\(.*\))$$/\1/') ($(lang))")
|
||||
|
||||
endmenu
|
||||
endef
|
||||
endif
|
||||
|
||||
define Build/Prepare
|
||||
for d in luasrc htdocs root src; do \
|
||||
if [ -d ./$$$$d ]; then \
|
||||
mkdir -p $(PKG_BUILD_DIR)/$$$$d; \
|
||||
$(CP) ./$$$$d/* $(PKG_BUILD_DIR)/$$$$d/; \
|
||||
fi; \
|
||||
done
|
||||
$(call Build/Prepare/Default)
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
ifneq ($(wildcard ${CURDIR}/src/Makefile),)
|
||||
MAKE_PATH := src/
|
||||
MAKE_VARS += FPIC="$(FPIC)" LUCI_VERSION="$(PKG_SRC_VERSION)" LUCI_GITBRANCH="$(PKG_GITBRANCH)"
|
||||
|
||||
define Build/Compile
|
||||
$(call Build/Compile/Default,clean compile)
|
||||
endef
|
||||
else
|
||||
define Build/Compile
|
||||
endef
|
||||
endif
|
||||
|
||||
HTDOCS = /www
|
||||
LUA_LIBRARYDIR = /usr/lib/lua
|
||||
LUCI_LIBRARYDIR = $(LUA_LIBRARYDIR)/luci
|
||||
|
||||
define SrcDiet
|
||||
$(FIND) $(1) -type f -name '*.lua' | while read src; do \
|
||||
if LUA_PATH="$(STAGING_DIR_HOSTPKG)/lib/lua/5.1/?.lua" luasrcdiet --noopt-binequiv -o "$$$$src.o" "$$$$src"; \
|
||||
then mv "$$$$src.o" "$$$$src"; fi; \
|
||||
done
|
||||
endef
|
||||
|
||||
define JsMin
|
||||
$(FIND) $(1) -type f -name '*.js' | while read src; do \
|
||||
if jsmin < "$$$$src" > "$$$$src.o"; \
|
||||
then mv "$$$$src.o" "$$$$src"; fi; \
|
||||
done
|
||||
endef
|
||||
|
||||
define CssTidy
|
||||
$(FIND) $(1) -type f -name '*.css' | while read src; do \
|
||||
if csstidy "$$$$src" --template=highest --remove_last_semicolon=true "$$$$src.o"; \
|
||||
then mv "$$$$src.o" "$$$$src"; fi; \
|
||||
done
|
||||
endef
|
||||
|
||||
define SubstituteVersion
|
||||
$(FIND) $(1) -type f -name '*.htm' | while read src; do \
|
||||
$(SED) 's/<%# *\([^ ]*\)PKG_VERSION *%>/\1$(if $(PKG_VERSION),$(PKG_VERSION),$(PKG_SRC_VERSION))/g' \
|
||||
-e 's/"\(<%= *\(media\|resource\) *%>[^"]*\.\(js\|css\)\)"/"\1?v=$(if $(PKG_VERSION),$(PKG_VERSION),$(PKG_SRC_VERSION))"/g' \
|
||||
"$$$$src"; \
|
||||
done
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
if [ -d $(PKG_BUILD_DIR)/luasrc ]; then \
|
||||
$(INSTALL_DIR) $(1)$(LUCI_LIBRARYDIR); \
|
||||
cp -pR $(PKG_BUILD_DIR)/luasrc/* $(1)$(LUCI_LIBRARYDIR)/; \
|
||||
$(FIND) $(1)$(LUCI_LIBRARYDIR)/ -type f -name '*.luadoc' | $(XARGS) rm; \
|
||||
$(if $(CONFIG_LUCI_SRCDIET),$(call SrcDiet,$(1)$(LUCI_LIBRARYDIR)/),true); \
|
||||
$(call SubstituteVersion,$(1)$(LUCI_LIBRARYDIR)/); \
|
||||
else true; fi
|
||||
if [ -d $(PKG_BUILD_DIR)/htdocs ]; then \
|
||||
$(INSTALL_DIR) $(1)$(HTDOCS); \
|
||||
cp -pR $(PKG_BUILD_DIR)/htdocs/* $(1)$(HTDOCS)/; \
|
||||
$(if $(CONFIG_LUCI_JSMIN),$(call JsMin,$(1)$(HTDOCS)/),true); \
|
||||
$(if $(CONFIG_LUCI_CSSTIDY),$(call CssTidy,$(1)$(HTDOCS)/),true); \
|
||||
else true; fi
|
||||
if [ -d $(PKG_BUILD_DIR)/root ]; then \
|
||||
$(INSTALL_DIR) $(1)/; \
|
||||
cp -pR $(PKG_BUILD_DIR)/root/* $(1)/; \
|
||||
else true; fi
|
||||
if [ -d $(PKG_BUILD_DIR)/src ]; then \
|
||||
$(call Build/Install/Default) \
|
||||
$(CP) $(PKG_INSTALL_DIR)/* $(1)/; \
|
||||
else true; fi
|
||||
endef
|
||||
|
||||
ifndef Package/$(PKG_NAME)/postinst
|
||||
define Package/$(PKG_NAME)/postinst
|
||||
[ -n "$${IPKG_INSTROOT}" ] || {$(foreach script,$(LUCI_DEFAULTS),
|
||||
(. /etc/uci-defaults/$(script)) && rm -f /etc/uci-defaults/$(script))
|
||||
rm -f /tmp/luci-indexcache
|
||||
rm -rf /tmp/luci-modulecache/
|
||||
killall -HUP rpcd 2>/dev/null
|
||||
exit 0
|
||||
}
|
||||
endef
|
||||
endif
|
||||
|
||||
|
||||
LUCI_BUILD_PACKAGES := $(PKG_NAME)
|
||||
|
||||
# 1: LuCI language code
|
||||
# 2: BCP 47 language tag
|
||||
define LuciTranslation
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
SECTION:=luci
|
||||
CATEGORY:=LuCI
|
||||
TITLE:=$(PKG_NAME) - $(1) translation
|
||||
HIDDEN:=1
|
||||
DEFAULT:=LUCI_LANG_$(2)||(ALL&&m)
|
||||
DEPENDS:=$(PKG_NAME)
|
||||
VERSION:=$(PKG_PO_VERSION)
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)/description
|
||||
Translation for $(PKG_NAME) - $(LUCI_LANG.$(2))
|
||||
endef
|
||||
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)/install
|
||||
$$(INSTALL_DIR) $$(1)/etc/uci-defaults
|
||||
echo "uci set luci.languages.$(subst -,_,$(1))='$(LUCI_LANG.$(2))'; uci commit luci" \
|
||||
> $$(1)/etc/uci-defaults/luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
$$(INSTALL_DIR) $$(1)$(LUCI_LIBRARYDIR)/i18n
|
||||
$(foreach po,$(wildcard ${CURDIR}/po/$(2)/*.po), \
|
||||
po2lmo $(po) \
|
||||
$$(1)$(LUCI_LIBRARYDIR)/i18n/$(basename $(notdir $(po))).$(1).lmo;)
|
||||
endef
|
||||
|
||||
define Package/luci-i18n-$(LUCI_BASENAME)-$(1)/postinst
|
||||
[ -n "$$$${IPKG_INSTROOT}" ] || {
|
||||
(. /etc/uci-defaults/luci-i18n-$(LUCI_BASENAME)-$(1)) && rm -f /etc/uci-defaults/luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
exit 0
|
||||
}
|
||||
endef
|
||||
|
||||
LUCI_BUILD_PACKAGES += luci-i18n-$(LUCI_BASENAME)-$(1)
|
||||
|
||||
endef
|
||||
|
||||
$(foreach lang,$(LUCI_LANGUAGES),$(eval $(call LuciTranslation,$(firstword $(LUCI_LC_ALIAS.$(lang)) $(lang)),$(lang))))
|
||||
$(foreach pkg,$(LUCI_BUILD_PACKAGES),$(eval $(call BuildPackage,$(pkg))))
|
||||
34
olg-ucentral-client-sdk/feeds/tip/tip-defaults/Makefile
Normal file
34
olg-ucentral-client-sdk/feeds/tip/tip-defaults/Makefile
Normal file
@@ -0,0 +1,34 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=tip-defaults
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=BSD-3-Clause
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/tip-defaults
|
||||
SECTION:=ucentral
|
||||
CATEGORY:=uCentral
|
||||
TITLE:=tip-defaults
|
||||
endef
|
||||
|
||||
define Package/tip-defaults/description
|
||||
The default configuration of the AP.
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
endef
|
||||
|
||||
define Build/Compile/Default
|
||||
|
||||
endef
|
||||
Build/Compile = $(Build/Compile/Default)
|
||||
|
||||
define Package/tip-defaults/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,tip-defaults))
|
||||
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=80
|
||||
|
||||
boot() {
|
||||
echo $(cat /etc/openwrt_release | grep DISTRIB_TIP= | cut -d\' -f2) > /tmp/ucentral.version
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFajCCA1KgAwIBAgICDnowDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUT3BlbkxBTiBEZW1vIFJvb3QgQ0EwHhcNMjUwMjIxMTUwMDAwWhcNMjYwMjIxMTUwMDAwWjAgMR4wHAYDVQQDExVPcGVuTEFOIERlbW8gQmlydGggQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVWIyySul6Fv4wl1O+DQpaLRa0p+Az5L/jcqTpdVf6w+8tlmeIY9C28uDQoDjewrIkvf3lcfK86nshs02s9ehqZUnEP8+GvKM19x3JbWxeTvWwFirjHir4x897iQ606bAMbrHHtntI9ZyBZyXDGeElGJxJQNX+0d50SFq609cB3yxpBPJ67ag+4Oq0uHgROHjEQMrfwLwlAune0c1fjQDrN14PDNjMZHvvhc/pkAHxR1PP6LOFNV5NuQ58tC5N7R2EqqFbIJ8VZgcagrGRYuAuFFTaV+D7RIt9xGTuWlCyxHI7VkRBJ1mRoEr4GOrP9QFjBD8NzNK+/wnR/fZwhpEnRsgHiI33wKHBDg+l3r8tvRzuB5X6Gl/SfuAeaoCuDHMncTjQg1zGhyEwjQhUe4RY3w+yHAjeeOE6c5spOMDDdaBibkzLmSjXztuLeAdzsUcD3fvGeOvh9vG14TKEmF8puNkqEcc0W8NyUWKFdr9umdJEMbaRSSsMGtp8bDj3Ddh4PhEJrIFeo89+HwXhU6sk+wzE9BULTohahsfwOV/08t1cZ3Q04Oj1KI+4YWu8BJns5gX35rQ8GIbkXQwfvFMwqmbg+ij2o9HWdkSL4bcqW/83Ho+31ce210rVGPK9cav0CjA2Eexgxi45cbgnfoade74Qa5zXboJEBmp7rbo4swIDAQABo4GuMIGrMB8GA1UdIwQYMBaAFDzIg8eyTI3xc4A2R60f8HanhBZDMB0GA1UdDgQWBBS5xC3inqLQl+vxzn9PsjNzlZ5hYDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vZGVtby5jZXJ0aWZpY2F0ZS5maS9jcmwvT3BlbkxBTkRlbW9Sb290Q0EuY3JsMA0GCSqGSIb3DQEBCwUAA4ICAQA9DJEjsDLqtSFkF0XTWfzbebXA+X8++Qmiukrw0s2LRx798ce0mVITRAFDLf78BeUYF0B+PQ8hgq4fWyFsXRgZVrITd1BszT3LJ2r/6xWQJVpVHzLqKgIlW/PY/uTUz+xqR0Ev6hYrmrjfya0K0XEZZqxkmrTrcECaA3RCFkWQl9ZUlb9BClmdhayO8x1XpJplIYAMKVuoPL9IUQH6HUPFnzlPNQHIK9gcFACtgPVWCJg3IAvSLa41KpRxTDwGFvlrNKtkBlGRYhFGCHWXXZn8fdQHW9vykkkfPOaPR/AVyuRzfAT6wbtVWSy38BurSdqSCuNQPQBfF2vMeUGwNbD/7B4tYrWVtnIbgxRPKvX0o3mZkKry6BJf2m/AKWA16W+i4ojnPRORLTTq9cEZ0WL6NRHCgMrbWaCs/+ADTErqK6cv7GhoOVEiqugvnz93dit2IXg4zdDJ4hF9ZSlicwgZKVvMqnNQ1iiXezIQBehgYcWwXRIfdrRPe88HgkySuDZ2lkKYdc+oTc6e7upRh4Kh2ZSipcRb6ehPan533jnQJyU8A9vFAJiQfZZ4lD3tcsqlsDnlu5YEDYSjcfnkyOH/Mlx5VVTWYGvqNNVKRDw2slSxKwVCobkcF/2dAxP9DqOaGaCnMeOaR7kMaBm5d1fwb+bCl9usQAELjZBv2vAH8g==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFIDCCAwigAwIBAgICDnkwDQYJKoZIhvcNAQELBQAwHzEdMBsGA1UEAwwUT3BlbkxBTiBEZW1vIFJvb3QgQ0EwHhcNMjUwMjIxMTUwMDAwWhcNMjYwMjIxMTUwMDAwWjAfMR0wGwYDVQQDDBRPcGVuTEFOIERlbW8gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMjExylKdJWoJu9mOHPJ6yZFXKe1lE467G65acpS2FKIWnPVFjNCmATMpkMOIFzEFwyFdbQjzOidtiL+73zlE52lOJpXCfOcxDFqDYDJJ8//J1/gQWsBaKpSvgLiHU/0awkQg+yJYZpj8YZa4NkFe+zTjQScSfOsqPPb3rZ7DOQ2BKAhjVShKmVbtNil0iO0zm8vE8DNkktTNMREp2pzb8MbCAgfOkwlrby6T+rV3TvmjThGdFUb5lWDFxWtlF8W0SUII9qj7p5TdGpryeLsO0nZTBtS4HxZNdvmKOHfgcRHmSZIJigB2NzKLNrXF9JBW0WnUSwZJZAG2C1RTx6lADILPueuusyfR/hZ3koKi4PHnSiTwQghzia9K9QjNHq5z9R9ZoCnhBg1VyU4LKmp862L0sIp2vgnOYunEIi9aCYBaDwo+0FuVjZuXyDIatwVuA7TN5IWPHA6XLdOt1mmkeYy1Ldr4XHjdondhtOyeei1UFXmyyLm2+kmRYfTm91TqYmNzRgbRV2NHO50AmsnBknX4Rv3gishGe0+dV5yFcUwZud0z2rSCkuoai5tKrPT+6Y6NqkT9u9HFifIBXnLwEzVUqHRtW6SuWj2DClVQIXIUZtFnhY4GuTuf6DlzgnXO58oDVCZmCW4ULIpbqGeRsvBHR8Sw5JXP/1+TMUYhE8TAgMBAAGjZjBkMB8GA1UdIwQYMBaAFDzIg8eyTI3xc4A2R60f8HanhBZDMB0GA1UdDgQWBBQ8yIPHskyN8XOANketH/B2p4QWQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAgEAkHZ5KR8IOrdfMFy+iOvauvZxfQ84LL6TpB2FQKDjneJUdd7c29UJJFNW/0mp4Gc6jKZab6J8Dx/pNnbH0RqFjGjeRGtJ4Sk0G7gf9zw1S7qut5WJDcisM9l/wXC+zy/KSKKPQmbt0grWOtU7+NNPh1YU76hIrInq/u2sVZyKH8SXQ957fbJk6BX6JTKyNEn05AB6rNSrbOWo8sy2MlcJ7bBsrWYI1t6GcWFh4b36bLu7/dKJWpyFNXXIkKJsgMEDpEQae56+fSSDo0KRNtYB82fNZDIQlGK81rGJWNzAahM+3GD1tgk/3ZVugfaJhcBpoHHKNOGqZAvtirLAIDocno7AzqoeIz974Rh2Olsl2/arApYPyyfi8PMYuFe/d4h+Wie8n+jh5n48lZ2Ve4PK+j+QHD6tTZS4f0bGnPL1puMxzQloltuQWgLDeVfEgrc3snLvjOg8aDzWm/es85lP8XcyW54U4t3JmrNUC2C7v+Uafx7cL7eDeunhs+BRhtGV+IUmjub2IrpqZp3zZqn+LVRdYJIy/qHhjS5+ImckXkFojOmeWhfmEmYSuNP8Oa6cGuXp829qnbxLh9Qzi3TfXV883KLse4kL5Zl7gBA/4hz2hVMyGJ8fY+VvzbaTuOXyvKJ+rGZCTcRSeotBLnIevVMiL7SqOEwN0j4Mfbznfq8=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFFTCCAv2gAwIBAgICAxIwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPT3BlbkxBTiBSb290IENBMCAXDTI1MDUxNDA4NDcxMFoYDzIwNTUwNTE0MDg0NzEwWjAaMRgwFgYDVQQDDA9PcGVuTEFOIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGibJ04A55kSURTBSKgcBmLnND2I5wws1taKqqU9aaRhB7NtvMHwh2voH9b1brUiulZaZwTN/9kzd4AnXeKQ+0u5tV7Ofk0fzF2MK47n17TS30Yenqc4NuQEKdpKK/pM3VvOEppR/bqtgyLtDmbDnmFOx+zTj/+smTgouwA+Iier0P4s5OohYxn/bjOqwQbHbU79VpGBIWv6/kt55AhH7zvsqqKHkrzTxnsRBv3SBIufrjJr9PIhZBLDrqr56P6KgAi0eoutNt2ToiJbE0WfjU7GI1RSiSN5bGj1zXhjNVzQWs1H9QzRf3c9pl3+haHQZ7FZ1UqiTRewmbNrQ6I9k81au3SttUlb87MyAuDSzatkiq7CjQ8VE1J6te6ZBt2zWpUhHsR/Lg7g3eOw5dL4oZJdK5GgGu/MUajLUXifIqM13Mvg0VTzDhN69VLXLSL0gPcicsQCwJuAza1IC/VqmBGx19fAkyJhOurCXWOgisi0g1+xzPKRphUNwMPUf8vBVOM/Vc6xDIvwVGE3+eWXyhixneFlSpAI03nWWjpwWXihTBoxbfRXO3Y/ilJqrgFN+U4PJcCPA+Wo7ThH0mgX6bOTPcgXMUzT3v3FF6Bx5/PNV3kYrw2yLzribUiS6AGvVGnW4hX2Z6OQvA/aHME8KF+6y6m4pC7FkUjVaRlzWu/wIDAQABo2MwYTAfBgNVHSMEGDAWgBSUaFuoOPk4QLByZP47kj4p1IbCJjAdBgNVHQ4EFgQUlGhbqDj5OECwcmT+O5I+KdSGwiYwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAB+/RUC2X6eVoPsFNMkaXO5Iib/ub0JoWhODQm8j2Mr5dpGXESSpXjfDcqDOLuJbWWoflXBLdr8BsVCBqOA9YgCX0H8Br7dUWmCScixxLW0he592/424EvdwifxcKHZLjv9CKV5Txhqnm2djc5RY/nTH5MYVrIh/If2TNO5ydDP6+vgy9GQ4en04VK7rz+PW17O8l7k9/lOmYptZmHgSDAPj/cT3PlG+McqaI5rMSHeEHlzH+PvgWjtSeEhF4FwFBXroDl4/yb4l2JB8bqAZ3vsOXSkigFcZh5MXPe+zuSSW+G8iLr4xoi0CFsP2DaHEyxgqP4B1FtE9nFPo6cvWbwqTVT7QSzqfH+jPJuQvpFXeRF5UFegNZTFT5/uFFPamihakFslEYxeJey1y+OJdLcP6ef87ruSt8amsq56OAETYpnW4JFowlEh0C+QwLGHGGY6WrOgHY/90hJmPgXBdBVg/IoOhzbvk5A+LqZDvxV2/rLNfClw8Kr3g5e8obcB6dWgMCy2z+us0H79ucnmhzQKsjpxM9T1ncHovAQfiD3jVqfHULY53avh0wIAjosoTGbe8dyx80quHe+16qWan7C9idXeAYYJXbZt5hs6hLw4I8M1LsjTg6vwsqiaHZpsmDyyQLdFjNJldG7aosfS9F+BIpuwijF+1dashL0CPsbIJ
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGBzCCA++gAwIBAgICCQYwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPT3BlbkxBTiBSb290IENBMB4XDTI1MDUxNDA4NTY0MVoXDTQ1MDUxNDA5MjY0MVowJDEiMCAGA1UEAwwZT3BlbkxBTiBTZXJ2ZXIgSXNzdWluZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALSdJpzwPfQM9oHBGt6w8UDLDJNznxI7cpfl0u0xVCHN1YY7onpwxFVkFRzUx/JrQ/tbEGZH19XtngaCZ91KbGbqVao9S32H0tyn2t3eTJ5h+klJ7+7YAbZr8UfOi3nG4bZzNSa5dDBPaNPvI51byKDN7siXXnALV3f0l6lZgDpLQco/E7ANU3lslUVjVNALfFUEonDyP7XV+lFAyidpjIn6dRn7oYs3SUwkzZUntYJAhAykmxXMWox+85gDkdb+2O3G8ci0uHVbb0A9LP+MeIhzxHgnnAMfWLfEZexdmEd2PwVHaz/D2Xp/gYrpPDTsbqWjQ9NmgdASwqN5j8BuJ8vHDVBVCztVDltm6JPw3Y6GQPN1LmiSLUzst7VYpydUJRDHYIAKJhT9DYxQ126VfiyMo6Xl4IQO8YZ/J6r8yR7gyvyUiBW+wvvC1bCY5+VuI4P/cY+6iA1qwC1SOWjYlccy+tbfGj9zr32Qf27e9RXSAkcATHen1rc/9AGEeAuSpKrzhmZIIvM4+EtYgbBvf91NkP51zbGpvsAbfWN/ecNmqH9SeyrrVgv68Z34hMijCcvJNyIvloo3nkb/gHYV4tAiwTTrX13Rio/8qNF4nwHLsjw0t7jEyRiXdOciePyhGbtdicuiUxrShzbGY7ID0yNwyTKcJYhorL/8r+YFpsXrAgMBAAGjggFLMIIBRzAfBgNVHSMEGDAWgBSUaFuoOPk4QLByZP47kj4p1IbCJjAdBgNVHQ4EFgQUBwUkiaCh5hdY+ZH6O8NmEE/nH5EwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwRwYDVR0fBEAwPjA8oDqgOIY2aHR0cDovL2NybC5jZXJ0aWZpY2F0ZXMub3Blbi1sYW4ub3JnL29wZW5sYW5yb290Y2EuY3JsMIGXBggrBgEFBQcBAQSBijCBhzBEBggrBgEFBQcwAoY4aHR0cDovL2NlcnRzLmNlcnRpZmljYXRlcy5vcGVuLWxhbi5vcmcvb3BlbmxhbnJvb3RjYS5jZXIwPwYIKwYBBQUHMAGGM2h0dHA6Ly9vY3NwLmNlcnRpZmljYXRlcy5vcGVuLWxhbi5vcmcvb3BlbmxhbnJvb3RjYTANBgkqhkiG9w0BAQsFAAOCAgEAqEk5ZJdpMVr2U0YhmqEU6gqxEeih9MWKcQfmsT/lhf5m5V7VuLMc3r+EBCsPssw60umdQcAU2IPlJXLAeWwdRyY7ZNNwQVgl9GBI/CM2b7x18+12/llCdXW9FOagdChTuuhwRnGTt71jcrJkleQyEYhqwwIEN82hxq4HSZO6XJDev4IsMRF00+qt8biJcf7OVGOSLoyiU6Dm/EzxoB+DZf3HdUc0vzfVjD4Im+yYzqXuwWV6c9oIBQH6obzaqlpg926CtEBFR8E1LQe93ahMvF7pExpIOkE5PTuqONvy7Xn3Ui8NRxHhmm8j/unql6bUTGENz9s68n8Im7weq6awC9Hfu8aGWjcnXI7tsDY5uJEguP5fSwCUrdTE85XgPgPHeKaIwBZsyRZTqVSvbky+c15Yv6ITXLWoA0AUxz9ste3WpqiWCNJVI90MCruSYKdpXGV0KU3QQXJDMKhHJBF5DLpuKiboFfh9O8pB7B4/tJ76JpAc6Z0rfaQUo2vxSpb3Sbd/IHNcL08zB8Ay+YUBULspxe+1StKthmCzCHI9DOhIgeASyNBpcL7uZPjCXiYGhUuzsFGv4sQ+d267Jyvql/Piw/vYg1k2aVBfdIoIU4TpIEVyQqPz4aAW+0SgL7OM+/zD9jxn3gVdusCpmHcoTzOfZRriH0FGIeDSQydpOJU=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,75 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEcTCCA1mgAwIBAgIUJFhIMlIJHJ7hW4gEzZuLBUaWjNcwDQYJKoZIhvcNAQEL
|
||||
BQAwbDELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
|
||||
dCwgSW5jLjEMMAoGA1UECxMDVElQMSkwJwYDVQQDEyBUZWxlY29tIEluZnJhIFBy
|
||||
b2plY3QgSXNzdWluZyBDQTAeFw0yMTA0MjUyMDMzNTRaFw0yNjA0MTMyMjM4NDZa
|
||||
MCMxITAfBgNVBAMTGGNhY2VydHMub25lLmRpZ2ljZXJ0LmNvbTCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAJwKRHdkdEQkp32bNi9TdgN4FNRG0nRppguQ
|
||||
mdCysJHA6/SuyAXNwKSbENysjFrcBkfYTlALjvIMqSu4d26ix6Mv4HnVxLjDzapV
|
||||
TZhOhfxIbRQa3HNieNup2vMi8jJvgwLcK/4CwhBJsbEMkB5lbyL8UnCBxzW9GGbM
|
||||
IvurvDFkUDUpUmiFg47nTpjub79KME6NqK38DxKzlUHvJge1TKFM73kZ3YkfWExQ
|
||||
yRQPRiU5KxMi/Wkr30FOf/rMTx4XNacOgyTJvzcStGwrlr0iGr8eLC1/XVXoOQz3
|
||||
0lyOeUzTB+HPU1Z2JrbPW5PnGxcQ0f7v/3qkWV1B2wuvFcQk+D0CAwEAAaOCAVIw
|
||||
ggFOMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFIj2Mhdk10e46DeI+aEZKSSK8Hj+
|
||||
MB8GA1UdIwQYMBaAFLMbVLjgR6s98ziA5Dzl/QBhbdHoMA4GA1UdDwEB/wQEAwIE
|
||||
8DAWBgNVHSUBAf8EDDAKBggrBgEFBQcDAjCBhgYIKwYBBQUHAQEEejB4MCgGCCsG
|
||||
AQUFBzABhhxodHRwOi8vb2NzcC5vbmUuZGlnaWNlcnQuY29tMEwGCCsGAQUFBzAC
|
||||
hkBodHRwOi8vY2FjZXJ0cy5vbmUuZGlnaWNlcnQuY29tL1RlbGVjb21JbmZyYVBy
|
||||
b2plY3RJc3N1aW5nQ0EuY3J0ME0GA1UdHwRGMEQwQqBAoD6GPGh0dHA6Ly9jcmwu
|
||||
b25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQcm9qZWN0SXNzdWluZ0NBLmNy
|
||||
bDANBgkqhkiG9w0BAQsFAAOCAQEADlFwshNPkeI2Gl6ooIauZL9d+6k+RWa5RTle
|
||||
JWziYL23XVEBT11+dvp4IB9HwVw5dByl3XAfTd1r4qyncwgXQpc6j2X8e45E8izI
|
||||
z2S1zhLMe1bA2lOiZz/sdpbonvxIHdiISyQI7q3mWQsvNkpkbjivjxLAJTcGPmOS
|
||||
gc/95YL+2xqPV45XAnPcl5qkLThtmb57Xst1sLWiSS2fUId6HMVuCgZa5su+aAl9
|
||||
iMXv9YfHcvyfwXBaOtoBlItyMGl60uy0E/Fr5uEhEWi53EIqhty6KQckQBB7wdjQ
|
||||
eiXNI5Ox5cf+TFdesuKPaoEn3WNpFL9PCA3S5nGegJlZQ4N9Eg==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEnDCCA4SgAwIBAgIUVpyCUx1MUeUwxg+7I1BvGFTz7HkwDQYJKoZIhvcNAQEL
|
||||
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
|
||||
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
|
||||
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjUxMjZaFw0yNjA0MTMyMjM4NDZaMGwx
|
||||
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
|
||||
Yy4xDDAKBgNVBAsTA1RJUDEpMCcGA1UEAxMgVGVsZWNvbSBJbmZyYSBQcm9qZWN0
|
||||
IElzc3VpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtKBrq
|
||||
qd2aKVSk25KfL5xHu8X7/8rJrz3IvyPuVKWhk/N1zabot3suBcGaYNKjnRHxg78R
|
||||
yKwKzajKYWtiQFqztu24g16LQeAnoUxZnF6a0z3JkkRPsz14A2y8TUhdEe1tx+UU
|
||||
4VGsk3n+FMmOQHL+79FO57zQC1LwylgfLSltrI6mF3jowVUQvnwzKhUzT87AJ6EO
|
||||
ndK/q0T/Bgi+aI39zfVOjJjsTJwghvrmYW3iarP1THSKxeib2s02bZKrvvHa5HL4
|
||||
UI8+LvREpVZl4mzt1z6Nl344Y6f+UeJlYa/Ci0jJqaXJmyVnUbAz+c0i5JfwAVn3
|
||||
YQzfC4eLnZCmdF8zAgMBAAGjggE3MIIBMzAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBSzG1S44EerPfM4gOQ85f0AYW3R6DAfBgNVHSMEGDAWgBQCRpZgebFT9qny
|
||||
98WfIUDk6ZEB+jAOBgNVHQ8BAf8EBAMCAYYwgYMGCCsGAQUFBwEBBHcwdTAoBggr
|
||||
BgEFBQcwAYYcaHR0cDovL29jc3Aub25lLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcw
|
||||
AoY9aHR0cDovL2NhY2VydHMub25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQ
|
||||
cm9qZWN0Um9vdENBLmNydDBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLm9u
|
||||
ZS5kaWdpY2VydC5jb20vVGVsZWNvbUluZnJhUHJvamVjdFJvb3RDQS5jcmwwDQYJ
|
||||
KoZIhvcNAQELBQADggEBAFbz+K94bHIkBMJqps0dApniUmOn0pO6Q6cGh47UP/kX
|
||||
IiPIsnYgG+hqYD/qtsiqJhaWi0hixRWn38UmvZxMRk27aSTGE/TWx0JTC3qDGsSe
|
||||
XkUagumbSfmS0ZyiTwMPeGAjXwyzGorqZWeA95eKfImntMiOf3E7//GK0K7HpCx8
|
||||
IPCnLZsZD2q/mLyBsduImFIRQJbLAhwIxpcd1qYJk+BlGFL+HtBpEbq6JxW2Xy+v
|
||||
DpNWc2WIsUTle0rTc9JNJrLX4ChUJmKqf8obKHap3Xh3//qw/jDB9pOAinA33FLJ
|
||||
EmCnwBvQr9mfNmPBGMYZVU8cPruDQJ57GjmmvdisbJY=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDojCCAoqgAwIBAgIUPVYBpqNbcLYygF6Mx+qxSWwQyFowDQYJKoZIhvcNAQEL
|
||||
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
|
||||
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
|
||||
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjQyNDRaFw0zMTA0MTMyMjM4NDZaMGkx
|
||||
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
|
||||
Yy4xDDAKBgNVBAsTA1RJUDEmMCQGA1UEAxMdVGVsZWNvbSBJbmZyYSBQcm9qZWN0
|
||||
IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGCibwf5u
|
||||
AAwZ+1H8U0e3u2V+0d2gSctucoK86XwUmfe1V2a/qlCYZd29r80IuN1IIeB0naIm
|
||||
KnK/MzXW87clF6tFd1+HzEvmlY/W4KyIXalVCTEzirFSvBEG2oZpM0yC3AefytAO
|
||||
aOpA00LaM3xTfTqMKIRhJBuLy0I4ANUVG6ixVebbGuc78IodleqiLoWy2Q9QHyEO
|
||||
t/7hZndJhiVogh0PveRhho45EbsACu7ymDY+JhlIleevqwlE3iQoq0YcmYADHno6
|
||||
Eq8vcwLpZFxihupUafkd1T3WJYQAJf9coCjBu2qIhNgrcrGD8R9fGswwNRzMRMpX
|
||||
720+GjcDW3bJAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAJG
|
||||
lmB5sVP2qfL3xZ8hQOTpkQH6MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEAVjl9dm4epG9NUYnagT9sg7scVQEPfz3Lt6w1NXJXgD8mAUlK0jXmEyvM
|
||||
dCPD4514n+8+lM7US8fh+nxc7jO//LwK17Wm9FblgjNFR7+anv0Q99T9fP19DLlF
|
||||
PSNHL2emogy1bl1lLTAoj8nxg2wVKPDSHBGviQ5LR9fsWUIJDv9Bs5k0qWugWYSj
|
||||
19S6qnHeskRDB8MqRLhKMG82oDVLerSnhD0P6HjySBHgTTU7/tYS/OZr1jI6MPbG
|
||||
L+/DtiR5fDVMNdBSGU89UNTi0wHY9+RFuNlIuvZC+x/swF0V9R5mN+ywquTPtDLA
|
||||
5IOM7ItsRmen6u3qu+JXros54e4juQ==
|
||||
-----END CERTIFICATE-----
|
||||
36
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/Makefile
Normal file
36
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/Makefile
Normal file
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (C) 2021 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=atfpolicy
|
||||
PKG_VERSION:=1
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
define Package/atfpolicy
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=A simple daemon for handling airtime fairness prioritization
|
||||
DEPENDS:=+libubox +libubus +libnl-tiny
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny
|
||||
|
||||
define Package/atfpolicy/install
|
||||
$(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/init.d $(1)/etc/config
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/atfpolicy $(1)/usr/sbin/
|
||||
$(INSTALL_BIN) ./files/atfpolicy.init $(1)/etc/init.d/atfpolicy
|
||||
$(INSTALL_DATA) ./files/atfpolicy.conf $(1)/etc/config/atfpolicy
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,atfpolicy))
|
||||
@@ -0,0 +1,8 @@
|
||||
config defaults
|
||||
option vo_queue_weight 4
|
||||
option update_pkt_threshold 100
|
||||
option bulk_percent_thresh 50
|
||||
option prio_percent_thresh 30
|
||||
option weight_normal 256
|
||||
option weight_prio 512
|
||||
option weight_bulk 128
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (c) 2021 OpenWrt.org
|
||||
|
||||
START=50
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/atfpolicy
|
||||
|
||||
add_option() {
|
||||
local type="$1"
|
||||
local name="$2"
|
||||
|
||||
config_get val "$cfg" "$name"
|
||||
|
||||
[ -n "$val" ] && json_add_$type "$name" "$val"
|
||||
}
|
||||
|
||||
add_defaults() {
|
||||
cfg="$1"
|
||||
|
||||
json_add_boolean reset 1
|
||||
|
||||
add_option int vo_queue_weight
|
||||
add_option int update_pkt_threshold
|
||||
add_option int bulk_percent_thresh
|
||||
add_option int prio_percent_thresh
|
||||
add_option int weight_normal
|
||||
add_option int weight_prio
|
||||
add_option int weight_bulk
|
||||
}
|
||||
|
||||
|
||||
reload_service() {
|
||||
json_init
|
||||
|
||||
config_load atfpolicy
|
||||
|
||||
config_foreach add_defaults defaults
|
||||
|
||||
ubus call atfpolicy config "$(json_dump)"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger atfpolicy
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 2 wait_for atfpolicy
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
PROJECT(atfpolicy C)
|
||||
|
||||
ADD_DEFINITIONS(-Os -Wall -Wno-unknown-warning-option -Wno-array-bounds -Wno-format-truncation -Werror --std=gnu99)
|
||||
|
||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||
|
||||
find_library(nl NAMES nl-tiny)
|
||||
ADD_EXECUTABLE(atfpolicy main.c ubus.c interface.c nl80211.c)
|
||||
TARGET_LINK_LIBRARIES(atfpolicy ${nl} ubox ubus)
|
||||
|
||||
INSTALL(TARGETS atfpolicy
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}
|
||||
)
|
||||
90
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/atf.h
Normal file
90
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/atf.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#ifndef __ATF_H
|
||||
#define __ATF_H
|
||||
|
||||
#include <net/if.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libubox/avl.h>
|
||||
|
||||
#define ATF_AVG_SCALE 12
|
||||
|
||||
#define ATF_AVG_WEIGHT_FACTOR 3
|
||||
#define ATF_AVG_WEIGHT_DIV 4
|
||||
|
||||
#define MAC_ADDR_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
#define MAC_ADDR_DATA(_a) \
|
||||
((const uint8_t *)(_a))[0], \
|
||||
((const uint8_t *)(_a))[1], \
|
||||
((const uint8_t *)(_a))[2], \
|
||||
((const uint8_t *)(_a))[3], \
|
||||
((const uint8_t *)(_a))[4], \
|
||||
((const uint8_t *)(_a))[5]
|
||||
|
||||
#define D(format, ...) do { \
|
||||
if (debug_flag) \
|
||||
fprintf(stderr, "DEBUG: %s(%d) " format "\n", __func__, __LINE__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
struct atf_config {
|
||||
int voice_queue_weight;
|
||||
int min_pkt_thresh;
|
||||
|
||||
int bulk_percent_thresh;
|
||||
int prio_percent_thresh;
|
||||
|
||||
int weight_normal;
|
||||
int weight_prio;
|
||||
int weight_bulk;
|
||||
};
|
||||
|
||||
struct atf_interface {
|
||||
struct avl_node avl;
|
||||
|
||||
char ifname[IFNAMSIZ + 1];
|
||||
uint32_t ubus_obj;
|
||||
|
||||
struct avl_tree stations;
|
||||
};
|
||||
|
||||
struct atf_stats {
|
||||
uint64_t bulk, normal, prio;
|
||||
};
|
||||
|
||||
struct atf_station {
|
||||
struct avl_node avl;
|
||||
uint8_t macaddr[6];
|
||||
bool present;
|
||||
|
||||
uint8_t stats_idx;
|
||||
struct atf_stats stats[2];
|
||||
|
||||
uint16_t avg_bulk;
|
||||
uint16_t avg_prio;
|
||||
|
||||
int weight;
|
||||
};
|
||||
|
||||
extern struct atf_config config;
|
||||
extern int debug_flag;
|
||||
|
||||
void reset_config(void);
|
||||
|
||||
struct atf_interface *atf_interface_get(const char *ifname);
|
||||
void atf_interface_sta_update(struct atf_interface *iface);
|
||||
struct atf_station *atf_interface_sta_get(struct atf_interface *iface, uint8_t *macaddr);
|
||||
void atf_interface_sta_changed(struct atf_interface *iface, struct atf_station *sta);
|
||||
void atf_interface_sta_flush(struct atf_interface *iface);
|
||||
void atf_interface_update_all(void);
|
||||
|
||||
int atf_ubus_init(void);
|
||||
void atf_ubus_stop(void);
|
||||
void atf_ubus_set_sta_weight(struct atf_interface *iface, struct atf_station *sta);
|
||||
|
||||
int atf_nl80211_init(void);
|
||||
int atf_nl80211_interface_update(struct atf_interface *iface);
|
||||
|
||||
#endif
|
||||
108
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/interface.c
Normal file
108
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/interface.c
Normal file
@@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libubox/avl-cmp.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
static AVL_TREE(interfaces, avl_strcmp, false, NULL);
|
||||
|
||||
#ifndef container_of_safe
|
||||
#define container_of_safe(ptr, type, member) \
|
||||
(ptr ? container_of(ptr, type, member) : NULL)
|
||||
#endif
|
||||
|
||||
static int avl_macaddr_cmp(const void *k1, const void *k2, void *ptr)
|
||||
{
|
||||
return memcmp(k1, k2, 6);
|
||||
}
|
||||
|
||||
void atf_interface_sta_update(struct atf_interface *iface)
|
||||
{
|
||||
struct atf_station *sta;
|
||||
|
||||
avl_for_each_element(&iface->stations, sta, avl)
|
||||
sta->present = false;
|
||||
}
|
||||
|
||||
struct atf_station *atf_interface_sta_get(struct atf_interface *iface, uint8_t *macaddr)
|
||||
{
|
||||
struct atf_station *sta;
|
||||
|
||||
sta = avl_find_element(&iface->stations, macaddr, sta, avl);
|
||||
if (sta)
|
||||
goto out;
|
||||
|
||||
sta = calloc(1, sizeof(*sta));
|
||||
memcpy(sta->macaddr, macaddr, sizeof(sta->macaddr));
|
||||
sta->avl.key = sta->macaddr;
|
||||
sta->weight = -1;
|
||||
avl_insert(&iface->stations, &sta->avl);
|
||||
|
||||
out:
|
||||
sta->present = true;
|
||||
return sta;
|
||||
}
|
||||
|
||||
void atf_interface_sta_flush(struct atf_interface *iface)
|
||||
{
|
||||
struct atf_station *sta, *tmp;
|
||||
|
||||
avl_for_each_element_safe(&iface->stations, sta, avl, tmp) {
|
||||
if (sta->present)
|
||||
continue;
|
||||
|
||||
avl_delete(&iface->stations, &sta->avl);
|
||||
free(sta);
|
||||
}
|
||||
}
|
||||
|
||||
void atf_interface_sta_changed(struct atf_interface *iface, struct atf_station *sta)
|
||||
{
|
||||
int weight;
|
||||
|
||||
if (sta->avg_prio > config.prio_percent_thresh)
|
||||
weight = config.weight_prio;
|
||||
else if (sta->avg_prio > config.bulk_percent_thresh)
|
||||
weight = config.weight_bulk;
|
||||
else
|
||||
weight = config.weight_normal;
|
||||
|
||||
if (sta->weight == weight)
|
||||
return;
|
||||
|
||||
sta->weight = weight;
|
||||
atf_ubus_set_sta_weight(iface, sta);
|
||||
}
|
||||
|
||||
struct atf_interface *atf_interface_get(const char *ifname)
|
||||
{
|
||||
struct atf_interface *iface;
|
||||
|
||||
iface = avl_find_element(&interfaces, ifname, iface, avl);
|
||||
if (iface)
|
||||
return iface;
|
||||
|
||||
if (strlen(ifname) + 1 > sizeof(iface->ifname))
|
||||
return NULL;
|
||||
|
||||
iface = calloc(1, sizeof(*iface));
|
||||
strcpy(iface->ifname, ifname);
|
||||
iface->avl.key = iface->ifname;
|
||||
avl_init(&iface->stations, avl_macaddr_cmp, false, NULL);
|
||||
avl_insert(&interfaces, &iface->avl);
|
||||
|
||||
return iface;
|
||||
}
|
||||
|
||||
void atf_interface_update_all(void)
|
||||
{
|
||||
struct atf_interface *iface, *tmp;
|
||||
|
||||
avl_for_each_element_safe(&interfaces, iface, avl, tmp)
|
||||
atf_nl80211_interface_update(iface);
|
||||
}
|
||||
62
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/main.c
Normal file
62
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/main.c
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
struct atf_config config;
|
||||
int debug_flag;
|
||||
|
||||
void reset_config(void)
|
||||
{
|
||||
memset(&config, 0, sizeof(config));
|
||||
|
||||
config.voice_queue_weight = 4;
|
||||
config.min_pkt_thresh = 100;
|
||||
|
||||
config.bulk_percent_thresh = (50 << ATF_AVG_SCALE) / 100;
|
||||
config.prio_percent_thresh = (30 << ATF_AVG_SCALE) / 100;
|
||||
|
||||
config.weight_normal = 256;
|
||||
config.weight_bulk = 128;
|
||||
config.weight_prio = 512;
|
||||
}
|
||||
|
||||
static void atf_update_cb(struct uloop_timeout *t)
|
||||
{
|
||||
atf_interface_update_all();
|
||||
uloop_timeout_set(t, 1000);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static struct uloop_timeout update_timer = {
|
||||
.cb = atf_update_cb,
|
||||
};
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, argv, "d")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
debug_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reset_config();
|
||||
uloop_init();
|
||||
atf_ubus_init();
|
||||
atf_nl80211_init();
|
||||
atf_update_cb(&update_timer);
|
||||
uloop_run();
|
||||
atf_ubus_stop();
|
||||
uloop_done();
|
||||
|
||||
return 0;
|
||||
}
|
||||
174
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/nl80211.c
Normal file
174
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/nl80211.c
Normal file
@@ -0,0 +1,174 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <linux/nl80211.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <unl.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
static struct unl unl;
|
||||
|
||||
static void
|
||||
atf_parse_tid_stats(struct atf_interface *iface, struct atf_stats *stats,
|
||||
int tid, struct nlattr *attr)
|
||||
{
|
||||
struct nlattr *tb[NL80211_TID_STATS_MAX + 1];
|
||||
uint64_t msdu;
|
||||
|
||||
if (nla_parse_nested(tb, NL80211_TID_STATS_MAX, attr, NULL))
|
||||
return;
|
||||
|
||||
if (!tb[NL80211_TID_STATS_TX_MSDU])
|
||||
return;
|
||||
|
||||
msdu = nla_get_u64(tb[NL80211_TID_STATS_TX_MSDU]);
|
||||
switch (tid) {
|
||||
case 0:
|
||||
case 3:
|
||||
/* BE */
|
||||
stats->normal += msdu;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
/* BK */
|
||||
stats->bulk += msdu;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
/* VI */
|
||||
stats->prio += msdu;
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
stats->prio += msdu * config.voice_queue_weight;
|
||||
/* VO */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t atf_stats_total(struct atf_stats *stats)
|
||||
{
|
||||
return stats->normal + stats->prio + stats->bulk;
|
||||
}
|
||||
|
||||
static void atf_stats_diff(struct atf_stats *dest, struct atf_stats *cur, struct atf_stats *prev)
|
||||
{
|
||||
dest->normal = cur->normal - prev->normal;
|
||||
dest->prio = cur->prio - prev->prio;
|
||||
dest->bulk = cur->bulk - prev->bulk;
|
||||
}
|
||||
|
||||
static uint16_t atf_stats_avg(uint16_t avg, uint64_t cur, uint32_t total)
|
||||
{
|
||||
cur <<= ATF_AVG_SCALE;
|
||||
cur /= total;
|
||||
|
||||
if (!avg)
|
||||
return (uint16_t)cur;
|
||||
|
||||
avg *= ATF_AVG_WEIGHT_FACTOR;
|
||||
avg += cur * (ATF_AVG_WEIGHT_DIV - ATF_AVG_WEIGHT_FACTOR);
|
||||
avg /= ATF_AVG_WEIGHT_DIV;
|
||||
|
||||
if (!avg)
|
||||
avg = 1;
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
|
||||
static void atf_sta_update_avg(struct atf_station *sta, struct atf_stats *cur)
|
||||
{
|
||||
uint64_t total = atf_stats_total(cur);
|
||||
|
||||
D("sta "MAC_ADDR_FMT" total pkts: total=%d bulk=%d normal=%d prio=%d",
|
||||
MAC_ADDR_DATA(sta->macaddr), (uint32_t)total,
|
||||
(uint32_t)cur->bulk, (uint32_t)cur->normal, (uint32_t)cur->prio);
|
||||
if (total < config.min_pkt_thresh)
|
||||
return;
|
||||
|
||||
sta->avg_bulk = atf_stats_avg(sta->avg_bulk, cur->bulk, total);
|
||||
sta->avg_prio = atf_stats_avg(sta->avg_prio, cur->prio, total);
|
||||
D("avg bulk=%d prio=%d",
|
||||
(sta->avg_bulk * 100) >> ATF_AVG_SCALE,
|
||||
(sta->avg_prio * 100) >> ATF_AVG_SCALE);
|
||||
sta->stats_idx = !sta->stats_idx;
|
||||
}
|
||||
|
||||
static int
|
||||
atf_sta_cb(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct atf_interface *iface = arg;
|
||||
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||
struct nlattr *tb[NL80211_ATTR_MAX + 1];
|
||||
struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
|
||||
struct atf_station *sta;
|
||||
struct atf_stats *stats, diff = {};
|
||||
struct nlattr *cur;
|
||||
int idx = 0;
|
||||
int rem;
|
||||
|
||||
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
|
||||
genlmsg_attrlen(gnlh, 0), NULL);
|
||||
|
||||
if (!tb[NL80211_ATTR_STA_INFO] || !tb[NL80211_ATTR_MAC])
|
||||
return NL_SKIP;
|
||||
|
||||
if (nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
|
||||
tb[NL80211_ATTR_STA_INFO], NULL))
|
||||
return NL_SKIP;
|
||||
|
||||
if (!sinfo[NL80211_STA_INFO_TID_STATS])
|
||||
return NL_SKIP;
|
||||
|
||||
sta = atf_interface_sta_get(iface, nla_data(tb[NL80211_ATTR_MAC]));
|
||||
if (!sta)
|
||||
return NL_SKIP;
|
||||
|
||||
stats = &sta->stats[sta->stats_idx];
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
nla_for_each_nested(cur, sinfo[NL80211_STA_INFO_TID_STATS], rem)
|
||||
atf_parse_tid_stats(iface, stats, idx++, cur);
|
||||
|
||||
atf_stats_diff(&diff, stats, &sta->stats[!sta->stats_idx]);
|
||||
atf_sta_update_avg(sta, &diff);
|
||||
atf_interface_sta_changed(iface, sta);
|
||||
|
||||
return NL_SKIP;
|
||||
}
|
||||
|
||||
int atf_nl80211_interface_update(struct atf_interface *iface)
|
||||
{
|
||||
struct nl_msg *msg;
|
||||
int ifindex;
|
||||
|
||||
ifindex = if_nametoindex(iface->ifname);
|
||||
if (!ifindex)
|
||||
return -1;
|
||||
|
||||
atf_interface_sta_update(iface);
|
||||
|
||||
msg = unl_genl_msg(&unl, NL80211_CMD_GET_STATION, true);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
|
||||
unl_genl_request(&unl, msg, atf_sta_cb, iface);
|
||||
|
||||
atf_interface_sta_flush(iface);
|
||||
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int atf_nl80211_init(void)
|
||||
{
|
||||
return unl_genl_init(&unl, "nl80211");
|
||||
}
|
||||
164
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/ubus.c
Normal file
164
olg-ucentral-client-sdk/feeds/ucentral/atfpolicy/src/ubus.c
Normal file
@@ -0,0 +1,164 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
|
||||
*/
|
||||
#include <libubus.h>
|
||||
|
||||
#include "atf.h"
|
||||
|
||||
#define HOSTAPD_PREFIX "hostapd."
|
||||
|
||||
static struct ubus_auto_conn conn;
|
||||
static struct blob_buf b;
|
||||
|
||||
enum {
|
||||
ATF_CONFIG_RESET,
|
||||
ATF_CONFIG_VO_Q_WEIGHT,
|
||||
ATF_CONFIG_MIN_PKT_THRESH,
|
||||
ATF_CONFIG_BULK_PERCENT_THR,
|
||||
ATF_CONFIG_PRIO_PERCENT_THR,
|
||||
|
||||
ATF_CONFIG_WEIGHT_NORMAL,
|
||||
ATF_CONFIG_WEIGHT_PRIO,
|
||||
ATF_CONFIG_WEIGHT_BULK,
|
||||
__ATF_CONFIG_MAX
|
||||
};
|
||||
|
||||
static const struct blobmsg_policy atf_config_policy[__ATF_CONFIG_MAX] = {
|
||||
[ATF_CONFIG_VO_Q_WEIGHT] = { "vo_queue_weight", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_MIN_PKT_THRESH] = { "update_pkt_threshold", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_BULK_PERCENT_THR] = { "bulk_percent_thresh", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_PRIO_PERCENT_THR] = { "prio_percent_thresh", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_WEIGHT_NORMAL] = { "weight_normal", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_WEIGHT_PRIO] = { "weight_prio", BLOBMSG_TYPE_INT32 },
|
||||
[ATF_CONFIG_WEIGHT_BULK] = { "weight_bulk", BLOBMSG_TYPE_INT32 },
|
||||
};
|
||||
|
||||
static int
|
||||
atf_ubus_config(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *tb[__ATF_CONFIG_MAX];
|
||||
struct blob_attr *cur;
|
||||
static const struct {
|
||||
int id;
|
||||
int *field;
|
||||
} field_map[] = {
|
||||
{ ATF_CONFIG_VO_Q_WEIGHT, &config.voice_queue_weight },
|
||||
{ ATF_CONFIG_MIN_PKT_THRESH, &config.min_pkt_thresh },
|
||||
{ ATF_CONFIG_BULK_PERCENT_THR, &config.bulk_percent_thresh },
|
||||
{ ATF_CONFIG_PRIO_PERCENT_THR, &config.prio_percent_thresh },
|
||||
{ ATF_CONFIG_WEIGHT_NORMAL, &config.weight_normal },
|
||||
{ ATF_CONFIG_WEIGHT_PRIO, &config.weight_prio },
|
||||
{ ATF_CONFIG_WEIGHT_BULK, &config.weight_bulk },
|
||||
};
|
||||
bool reset = false;
|
||||
int i;
|
||||
|
||||
blobmsg_parse(atf_config_policy, __ATF_CONFIG_MAX, tb,
|
||||
blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if ((cur = tb[ATF_CONFIG_RESET]) != NULL)
|
||||
reset = blobmsg_get_bool(cur);
|
||||
|
||||
if (reset)
|
||||
reset_config();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(field_map); i++) {
|
||||
if ((cur = tb[field_map[i].id]) != NULL)
|
||||
*(field_map[i].field) = blobmsg_get_u32(cur);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct ubus_method atf_methods[] = {
|
||||
UBUS_METHOD("config", atf_ubus_config, atf_config_policy),
|
||||
};
|
||||
|
||||
static struct ubus_object_type atf_object_type =
|
||||
UBUS_OBJECT_TYPE("atfpolicy", atf_methods);
|
||||
|
||||
static struct ubus_object atf_object = {
|
||||
.name = "atfpolicy",
|
||||
.type = &atf_object_type,
|
||||
.methods = atf_methods,
|
||||
.n_methods = ARRAY_SIZE(atf_methods),
|
||||
};
|
||||
|
||||
static void
|
||||
atf_ubus_add_interface(struct ubus_context *ctx, const char *name)
|
||||
{
|
||||
struct atf_interface *iface;
|
||||
|
||||
iface = atf_interface_get(name + strlen(HOSTAPD_PREFIX));
|
||||
if (!iface)
|
||||
return;
|
||||
|
||||
iface->ubus_obj = 0;
|
||||
ubus_lookup_id(ctx, name, &iface->ubus_obj);
|
||||
D("add interface %s", name + strlen(HOSTAPD_PREFIX));
|
||||
}
|
||||
|
||||
static void
|
||||
atf_ubus_lookup_cb(struct ubus_context *ctx, struct ubus_object_data *obj,
|
||||
void *priv)
|
||||
{
|
||||
if (!strncmp(obj->path, HOSTAPD_PREFIX, strlen(HOSTAPD_PREFIX)))
|
||||
atf_ubus_add_interface(ctx, obj->path);
|
||||
}
|
||||
|
||||
void atf_ubus_set_sta_weight(struct atf_interface *iface, struct atf_station *sta)
|
||||
{
|
||||
D("set sta "MAC_ADDR_FMT" weight=%d", MAC_ADDR_DATA(sta->macaddr), sta->weight);
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_printf(&b, "sta", MAC_ADDR_FMT, MAC_ADDR_DATA(sta->macaddr));
|
||||
blobmsg_add_u32(&b, "weight", sta->weight);
|
||||
if (ubus_invoke(&conn.ctx, iface->ubus_obj, "update_airtime", b.head, NULL, NULL, 100))
|
||||
D("set airtime weight failed");
|
||||
}
|
||||
|
||||
static void
|
||||
atf_ubus_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
static const struct blobmsg_policy policy =
|
||||
{ "path", BLOBMSG_TYPE_STRING };
|
||||
struct ubus_object_data obj;
|
||||
struct blob_attr *attr;
|
||||
|
||||
blobmsg_parse(&policy, 1, &attr, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if (!attr)
|
||||
return;
|
||||
|
||||
obj.path = blobmsg_get_string(attr);
|
||||
atf_ubus_lookup_cb(ctx, &obj, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
ubus_connect_handler(struct ubus_context *ctx)
|
||||
{
|
||||
static struct ubus_event_handler ev = {
|
||||
.cb = atf_ubus_event_cb
|
||||
};
|
||||
|
||||
ubus_add_object(ctx, &atf_object);
|
||||
ubus_register_event_handler(ctx, &ev, "ubus.object.add");
|
||||
ubus_lookup(ctx, "hostapd.*", atf_ubus_lookup_cb, NULL);
|
||||
}
|
||||
|
||||
int atf_ubus_init(void)
|
||||
{
|
||||
conn.cb = ubus_connect_handler;
|
||||
ubus_auto_connect(&conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void atf_ubus_stop(void)
|
||||
{
|
||||
ubus_auto_shutdown(&conn);
|
||||
}
|
||||
62
olg-ucentral-client-sdk/feeds/ucentral/bridger/Makefile
Normal file
62
olg-ucentral-client-sdk/feeds/ucentral/bridger/Makefile
Normal file
@@ -0,0 +1,62 @@
|
||||
#
|
||||
# Copyright (C) 2022 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=bridger
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=https://github.com/nbd168/bridger
|
||||
PKG_SOURCE_DATE:=2024-04-22
|
||||
PKG_SOURCE_VERSION:=40b1c5b6be4e73a6749cf2997c664520eb20055d
|
||||
PKG_MIRROR_HASH:=83e514f93fa3b8f1d7d3b13df6196a3d176521185dbbcdeccc8ef4d999f74cda
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
PKG_BUILD_DEPENDS:=bpf-headers
|
||||
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
include $(INCLUDE_DIR)/bpf.mk
|
||||
include $(INCLUDE_DIR)/nls.mk
|
||||
|
||||
define Package/bridger
|
||||
SECTION:=utils
|
||||
CATEGORY:=Base system
|
||||
TITLE:=Bridge forwarding accelerator
|
||||
DEPENDS:=+libbpf +libubox +libubus +libnl-tiny +kmod-sched-core +kmod-sched-flower +kmod-sched-bpf +kmod-sched-act-vlan $(BPF_DEPENDS)
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
-I$(STAGING_DIR)/usr/include/libnl-tiny \
|
||||
-I$(STAGING_DIR)/usr/include
|
||||
|
||||
CMAKE_OPTIONS += \
|
||||
-DLIBNL_LIBS=-lnl-tiny
|
||||
|
||||
define Build/Compile
|
||||
$(call CompileBPF,$(PKG_BUILD_DIR)/bridger-bpf.c)
|
||||
$(Build/Compile/Default)
|
||||
endef
|
||||
|
||||
define Package/bridger/install
|
||||
$(INSTALL_DIR) \
|
||||
$(1)/etc/config \
|
||||
$(1)/etc/init.d \
|
||||
$(1)/lib/bpf \
|
||||
$(1)/usr/sbin
|
||||
$(INSTALL_DATA) $(PKG_BUILD_DIR)/bridger-bpf.o $(1)/lib/bpf
|
||||
$(INSTALL_BIN) \
|
||||
$(PKG_INSTALL_DIR)/usr/bin/bridger \
|
||||
$(1)/usr/sbin/
|
||||
$(INSTALL_DATA) ./files/bridger.conf $(1)/etc/config/bridger
|
||||
$(INSTALL_BIN) ./files/bridger.init $(1)/etc/init.d/bridger
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,bridger))
|
||||
@@ -0,0 +1,3 @@
|
||||
config defaults
|
||||
# example for blacklisting individual devices or bridges
|
||||
# list blacklist eth0
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (c) 2021 OpenWrt.org
|
||||
|
||||
START=19
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/bridger
|
||||
|
||||
add_blacklist() {
|
||||
cfg="$1"
|
||||
|
||||
config_get blacklist "$cfg" blacklist
|
||||
for i in $blacklist; do
|
||||
json_add_string "" "$i"
|
||||
done
|
||||
}
|
||||
|
||||
reload_service() {
|
||||
config_load bridger
|
||||
|
||||
json_init
|
||||
json_add_string name "config"
|
||||
json_add_array devices
|
||||
config_foreach add_blacklist defaults
|
||||
json_close_array
|
||||
|
||||
ubus call bridger set_blacklist "$(json_dump)"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger bridger
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
service_started() {
|
||||
ubus -t 10 wait_for bridger
|
||||
[ $? = 0 ] && reload_service
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
Index: bridger-2023-05-12-3159bbe0/bpf.c
|
||||
===================================================================
|
||||
--- bridger-2023-05-12-3159bbe0.orig/bpf.c
|
||||
+++ bridger-2023-05-12-3159bbe0/bpf.c
|
||||
@@ -42,6 +42,10 @@ void bridger_bpf_flow_upload(struct brid
|
||||
if (bpf_map_lookup_elem(map_offload, &flow->key, &val) == 0)
|
||||
flow->offload.packets = val.packets;
|
||||
|
||||
+ if ((flow->key.ifindex == flow->offload.target_port) &&
|
||||
+ (flow->key.vlan == flow->offload.vlan)) {
|
||||
+ return;
|
||||
+ }
|
||||
bpf_map_update_elem(map_offload, &flow->key, &flow->offload, BPF_ANY);
|
||||
}
|
||||
|
||||
27
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/Makefile
Normal file
27
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/Makefile
Normal file
@@ -0,0 +1,27 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ieee8021x
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
|
||||
PKG_SOURCE_DATE:=2023-10-05
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/ieee8021x
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Wired 802.1x
|
||||
DEPENDS:=+libubox +libubus +libuci
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
|
||||
endef
|
||||
|
||||
define Package/ieee8021x/install
|
||||
$(CP) ./files/* $(1)
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,ieee8021x))
|
||||
@@ -0,0 +1,5 @@
|
||||
#config network
|
||||
# list ports 'lan1'
|
||||
# list ports 'lan2'
|
||||
# list ports 'lan3'
|
||||
# list ports 'lan4'
|
||||
21
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/etc/init.d/ieee8021x
Executable file
21
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/etc/init.d/ieee8021x
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=80
|
||||
USE_PROCD=1
|
||||
PROG=/usr/bin/ieee8021x.uc
|
||||
|
||||
reload_service() {
|
||||
ubus call ieee8021x reload
|
||||
restart
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger ieee8021x
|
||||
}
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command "$PROG"
|
||||
procd_set_param respawn
|
||||
procd_close_instance
|
||||
}
|
||||
307
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/usr/bin/ieee8021x.uc
Executable file
307
olg-ucentral-client-sdk/feeds/ucentral/ieee8021x/files/usr/bin/ieee8021x.uc
Executable file
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/ucode
|
||||
|
||||
let ubus = require('ubus').connect();
|
||||
let uci = require('uci').cursor();
|
||||
let uloop = require('uloop');
|
||||
let rtnl = require('rtnl');
|
||||
let fs = require('fs');
|
||||
let log = require("log");
|
||||
let hapd_subscriber;
|
||||
let device_subscriber;
|
||||
let ifaces = {};
|
||||
|
||||
/* load UCI */
|
||||
let config;
|
||||
function config_load() {
|
||||
config = uci.get_all('ieee8021x', '@config[0]');
|
||||
config.ports = {};
|
||||
for (let k, port in uci.get_all('ieee8021x')) {
|
||||
if (port['.type'] != 'port')
|
||||
continue;
|
||||
config.ports[k] = port;
|
||||
}
|
||||
}
|
||||
|
||||
/* set a wired ports auth state */
|
||||
function netifd_handle_iface(name, auth_status, vlan) {
|
||||
let msg = { name, auth_status, auth_vlans: [ ifaces[name].default_vlan + ':u' ]};
|
||||
if (ifaces[name].upstream && vlan)
|
||||
msg.auth_vlans = [ vlan + ':u' ];
|
||||
ubus.call('network.device', 'set_state', msg);
|
||||
if (auth_status && ifaces[name].upstream && vlan) {
|
||||
for (let wan in ifaces[name].wan_ports) {
|
||||
let msg = {
|
||||
name: wan,
|
||||
vlan: [ `${vlan}:t` ]
|
||||
};
|
||||
ubus.call('network.interface.up_none', 'add_device', msg);
|
||||
ubus.call('udevstats', 'add_device', { device: wan, vlan });
|
||||
}
|
||||
}
|
||||
ifaces[name].authenticated = auth_status;
|
||||
}
|
||||
|
||||
/* handle events from hostapd */
|
||||
function hapd_subscriber_notify_cb(notify) {
|
||||
switch(notify.type) {
|
||||
case 'sta-authorized':
|
||||
log.syslog(LOG_USER, "authenticated station");
|
||||
push(ifaces[notify.data.ifname].lladdr, notify.data.address);
|
||||
netifd_handle_iface(notify.data.ifname, true, notify.data.vlan);
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remove arp and rate limit for a client */
|
||||
function flush_iface(name) {
|
||||
/* flush all arp entries */
|
||||
let neighs = rtnl.request(rtnl.const.RTM_GETNEIGH, rtnl.const.NLM_F_DUMP, { dev: name });
|
||||
for (let neigh in neighs)
|
||||
if (neigh.lladdr in ifaces[name].lladdr) {
|
||||
rtnl.request(rtnl.const.RTM_DELNEIGH, 0, { dst: neigh.dst, dev: neigh.dev, family: neigh.family });
|
||||
ubus.call('ratelimit', 'client_delete', { device: neigh.dev, address: neigh.lladdr });
|
||||
}
|
||||
ifaces[name].lladdr = [];
|
||||
}
|
||||
|
||||
/* generate a hostapd configuration */
|
||||
function hostapd_start(iface) {
|
||||
if (!fs.stat("/sys/class/net/" + iface)) {
|
||||
log.syslog(LOG_ERR, "Interface ${iface} does not exist yet");
|
||||
return;
|
||||
}
|
||||
if (!config.auth_server_addr) {
|
||||
log.syslog(LOG_ERR, "Auth server address is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
let path = '/var/run/hostapd-' + iface + '.conf';
|
||||
let file = fs.open(path, 'w+');
|
||||
|
||||
file.write('driver=wired\n');
|
||||
file.write('ieee8021x=1\n');
|
||||
file.write('eap_reauth_period=0\n');
|
||||
file.write('ctrl_interface=/var/run/hostapd\n');
|
||||
file.write('interface=' + iface + '\n');
|
||||
file.write('ca_cert=' + config.ca + '\n');
|
||||
file.write('server_cert=' + config.cert + '\n');
|
||||
file.write('private_key=' + config.key + '\n');
|
||||
file.write('dynamic_vlan=1\n');
|
||||
file.write('vlan_no_bridge=1\n');
|
||||
file.write('vlan_naming=1\n');
|
||||
|
||||
if (config.auth_server_addr) {
|
||||
file.write('dynamic_own_ip_addr=1\n');
|
||||
file.write('auth_server_addr=' + config.auth_server_addr + '\n');
|
||||
file.write('auth_server_port=' + config.auth_server_port + '\n');
|
||||
file.write('auth_server_shared_secret=' + config.auth_server_secret + '\n');
|
||||
if (config.acct_server_addr && config.acct_server_port && config.acct_server_secret + '\n') {
|
||||
file.write('acct_server_addr=' + config.acct_server_addr + '\n');
|
||||
file.write('acct_server_port=' + config.acct_server_port + '\n');
|
||||
file.write('acct_server_shared_secret=' + config.acct_server_secret + '\n');
|
||||
}
|
||||
if (config.nas_identifier)
|
||||
file.write('nas_identifier=${config.nas_identifier}\n');
|
||||
if (config.coa_server_addr && config.coa_server_port && config.coa_server_secret) {
|
||||
file.write('radius_das_client=' + config.coa_server_addr + ' ' + config.coa_server_secret + '\n');
|
||||
file.write('radius_das_port=' + config.coa_server_port + '\n');
|
||||
}
|
||||
if (+config.mac_address_bypass)
|
||||
file.write('macaddr_acl=2\n');
|
||||
} else {
|
||||
file.write('eap_server=1\n');
|
||||
file.write('eap_user_file=/var/run/hostapd-ieee8021x.eap_user\n');
|
||||
}
|
||||
file.close();
|
||||
|
||||
/* is hostapd already running ? */
|
||||
/*
|
||||
* For certains scenarios, we need to remove and add
|
||||
* instead of reload (reload did not work).
|
||||
* Consider the following scenario -
|
||||
* Say on CIG 186w as an example
|
||||
* eth0.4086 interface exists with some non-ieee8021x config.
|
||||
* Push ieee8021x config. In general the flow is that
|
||||
* reload_config is called followed by invocation of services (from ucentral-schema)
|
||||
* Services inovation does n't wait until the config reloaded ie in this context
|
||||
* ieee8021x service is invoked much before the network interfaces are recreated.
|
||||
* That is not correct. To handle this, we capture link-up events
|
||||
* and remove the existing interface (in hostapd as shown below) and add again
|
||||
*/
|
||||
|
||||
if (ifaces[iface].hostapd) {
|
||||
log.syslog(LOG_USER, "Remove the config ${iface}");
|
||||
ubus.call('hostapd', 'config_remove', { iface: iface });
|
||||
}
|
||||
log.syslog(LOG_USER, "Add config (clear the old one) " + iface);
|
||||
ubus.call('hostapd', 'config_add', { iface: iface, config: path });
|
||||
system('ifconfig ' + iface + ' up');
|
||||
// Subscribe to corresponding hostapd if it is (re)added
|
||||
hapd_subscriber.subscribe("hostapd." + iface);
|
||||
}
|
||||
|
||||
/* build a list of all running and new interfaces */
|
||||
/* handle events from netifd */
|
||||
function device_subscriber_notify_cb(notify) {
|
||||
switch(notify.type) {
|
||||
case 'link_down':
|
||||
if (!ifaces[notify.data.name])
|
||||
break;
|
||||
|
||||
/* de-auth all clients */
|
||||
ubus.call(ifaces[notify.data.name].path, 'del_clients');
|
||||
ifaces[notify.data.name].authenticated = false;
|
||||
flush_iface(notify.data.name);
|
||||
ubus.call('ratelimit', 'device_delete', { device: notify.data.name });
|
||||
break;
|
||||
case 'link_up':
|
||||
if (!ifaces[notify.data.name])
|
||||
break;
|
||||
log.syslog(LOG_USER, "starting iface ${notify.data.name}");
|
||||
hostapd_start(notify.data.name);
|
||||
break;
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function subscriber_remove_cb(remove) {
|
||||
}
|
||||
|
||||
/* track and subscribe to a hostapd/netifd object */
|
||||
function ubus_unsub_object(add, id, path) {
|
||||
let object = split(path, '.');
|
||||
|
||||
if (path == 'network.device' && add) {
|
||||
device_subscriber.subscribe(path);
|
||||
}
|
||||
|
||||
let object_hostapd = object[0];
|
||||
let object_ifaces = object[1];
|
||||
|
||||
if (length(object) > 2) {
|
||||
// For swconfig platforms
|
||||
// The interface name is of the form eth0.4086 etc
|
||||
object_ifaces = object[1] + "." + object[2];
|
||||
}
|
||||
|
||||
if (object_hostapd != 'hostapd' || !ifaces[object_ifaces])
|
||||
return;
|
||||
if (add) {
|
||||
log.syslog(LOG_USER, "adding " + path);
|
||||
hapd_subscriber.subscribe(path);
|
||||
ifaces[object_ifaces].hostapd = true;
|
||||
ifaces[object_ifaces].path = path;
|
||||
} else {
|
||||
// Mark the port as unauthorized. but dont delete it
|
||||
// ifaces contains the configured (from uci config) ports
|
||||
netifd_handle_iface(object_ifaces, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* try to add all enumerated objects */
|
||||
function ubus_listener_cb(event, payload) {
|
||||
ubus_unsub_object(event == 'ubus.object.add', payload.id, payload.path);
|
||||
}
|
||||
|
||||
/* setup the ubus object listener, allowing us to track hostapd objects that get added and removed */
|
||||
function ubus_listener_init() {
|
||||
/* setup the notification listener */
|
||||
hapd_subscriber = ubus.subscriber(hapd_subscriber_notify_cb, subscriber_remove_cb);
|
||||
device_subscriber = ubus.subscriber(device_subscriber_notify_cb, subscriber_remove_cb);
|
||||
|
||||
/* enumerate all existing ojects */
|
||||
let list = ubus.list();
|
||||
for (let k, path in list)
|
||||
ubus_unsub_object(true, 0, path);
|
||||
|
||||
/* register add/remove handlers */
|
||||
ubus.listener('ubus.object.add', ubus_listener_cb);
|
||||
ubus.listener('ubus.object.remove', ubus_listener_cb);
|
||||
}
|
||||
|
||||
function prepare_ifaces() {
|
||||
for (let k, v in ifaces)
|
||||
v.active = false;
|
||||
|
||||
for (let k, port in config.ports) {
|
||||
ifaces[port.iface] ??= {};
|
||||
ifaces[port.iface].active = true;
|
||||
ifaces[port.iface].authenticated = false;
|
||||
ifaces[port.iface].default_vlan = +port.vlan;
|
||||
ifaces[port.iface].wan_ports = port.wan_ports;
|
||||
ifaces[port.iface].lladdr = [];
|
||||
}
|
||||
|
||||
for (let iface, v in ifaces) {
|
||||
if (v.active)
|
||||
continue;
|
||||
ubus.call('hostapd', 'config_remove', { iface });
|
||||
delete ifaces[iface];
|
||||
system('ifconfig ' + iface + ' down');
|
||||
}
|
||||
}
|
||||
|
||||
/* start all active interfaces */
|
||||
function start_ifaces() {
|
||||
for (let iface, v in ifaces)
|
||||
hostapd_start(iface);
|
||||
}
|
||||
|
||||
/* shutdown all interfaces */
|
||||
function shutdown() {
|
||||
for (let iface, v in ifaces) {
|
||||
log.syslog(LOG_USER, "shutdown");
|
||||
ubus.call('hostapd', 'config_remove', { iface });
|
||||
netifd_handle_iface(iface, false);
|
||||
flush_iface(iface);
|
||||
ubus.call('ratelimit', 'device_delete', { device: iface });
|
||||
}
|
||||
}
|
||||
|
||||
/* the object published on ubus */
|
||||
let ubus_methods = {
|
||||
dump: {
|
||||
call: function(req) {
|
||||
return ifaces;
|
||||
},
|
||||
args: {
|
||||
}
|
||||
},
|
||||
|
||||
reload: {
|
||||
call: function(req) {
|
||||
config_load();
|
||||
prepare_ifaces();
|
||||
start_ifaces();
|
||||
return 0;
|
||||
},
|
||||
args: {
|
||||
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/* handle rtnl events for carrier-down events */
|
||||
function rtnl_cb(msg) {
|
||||
if (msg.cmd != rtnl.const.RTM_NEWNEIGH)
|
||||
return;
|
||||
if (!ifaces[msg.msg.dev] || ifaces[msg.msg.dev].authenticated)
|
||||
return;
|
||||
ubus.call(ifaces[msg.msg.dev].path, 'mac_auth', { addr: msg.msg.lladdr });
|
||||
}
|
||||
|
||||
log.openlog("ieee8021x", log.LOG_PID, log.LOG_USER);
|
||||
uloop.init();
|
||||
ubus.publish('ieee8021x', ubus_methods);
|
||||
config_load();
|
||||
rtnl.listener(rtnl_cb, null, [ rtnl.const.RTNLGRP_NEIGH ]);
|
||||
prepare_ifaces();
|
||||
ubus_listener_init();
|
||||
start_ifaces();
|
||||
uloop.run();
|
||||
uloop.done();
|
||||
shutdown();
|
||||
log.closelog();
|
||||
103
olg-ucentral-client-sdk/feeds/ucentral/libwebsockets/Makefile
Normal file
103
olg-ucentral-client-sdk/feeds/ucentral/libwebsockets/Makefile
Normal file
@@ -0,0 +1,103 @@
|
||||
#
|
||||
# Copyright (C) 2014-2015 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=libwebsockets
|
||||
PKG_VERSION:=4.1.4
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/warmcat/libwebsockets/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=00da77b4b89db3e0b1a2778f756f08d55d7f6b1632e18d981fc399c412866147
|
||||
|
||||
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
|
||||
|
||||
PKG_LICENSE:=LGPL-2.1+exception
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
CMAKE_INSTALL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
|
||||
CMAKE_OPTIONS += -DLWS_IPV6=$(if $(CONFIG_IPV6),ON,OFF)
|
||||
CMAKE_OPTIONS += -DLWS_WITHOUT_TESTAPPS=ON
|
||||
|
||||
# other options worth noting
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_EXTENSIONS=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_DAEMONIZE=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_SERVER=ON
|
||||
# CMAKE_OPTIONS += -DLWS_WITHOUT_DEBUG=ON
|
||||
|
||||
|
||||
define Package/libwebsockets/Default
|
||||
SECTION:=libs
|
||||
CATEGORY:=Libraries
|
||||
TITLE:=libwebsockets
|
||||
DEPENDS:=+zlib +libcap
|
||||
URL:=https://libwebsockets.org
|
||||
MAINTAINER:=Karl Palsson <karlp@etactica.com>
|
||||
PROVIDES:= libwebsockets
|
||||
endef
|
||||
|
||||
define Package/libwebsockets-openssl
|
||||
$(call Package/libwebsockets/Default)
|
||||
TITLE += (OpenSSL)
|
||||
DEPENDS += +libopenssl
|
||||
VARIANT:=openssl
|
||||
endef
|
||||
|
||||
define Package/libwebsockets-mbedtls
|
||||
$(call Package/$(PKG_NAME)/Default)
|
||||
TITLE += (mbedTLS)
|
||||
DEPENDS += +libmbedtls
|
||||
VARIANT:=mbedtls
|
||||
endef
|
||||
|
||||
define Package/libwebsockets-full
|
||||
$(call Package/libwebsockets/Default)
|
||||
TITLE += (Full - OpenSSL, libuv, plugins, CGI)
|
||||
DEPENDS += +libopenssl +libuv
|
||||
VARIANT:=full
|
||||
endef
|
||||
|
||||
ifeq ($(BUILD_VARIANT),openssl)
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_CLIENT_CERTS=/etc/ssl/certs
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_SUPPORT=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_SSL=ON
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_VARIANT),mbedtls)
|
||||
CMAKE_OPTIONS += -DLWS_WITH_MBEDTLS=1
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_VARIANT),full)
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_CLIENT_CERTS=/etc/ssl/certs
|
||||
CMAKE_OPTIONS += -DLWS_OPENSSL_SUPPORT=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_SSL=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_LIBUV=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_PLUGINS=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_SERVER_STATUS=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_ACCESS_LOG=ON
|
||||
CMAKE_OPTIONS += -DLWS_WITH_CGI=ON
|
||||
CMAKE_OPTIONS += -DLWS_UNIX_SOCK=ON
|
||||
endif
|
||||
|
||||
define Package/libwebsockets/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libwebsockets.so* $(1)/usr/lib/
|
||||
endef
|
||||
|
||||
Package/libwebsockets-mbedtls/install = $(Package/libwebsockets/install)
|
||||
Package/libwebsockets-openssl/install = $(Package/libwebsockets/install)
|
||||
Package/libwebsockets-full/install = $(Package/libwebsockets/install)
|
||||
|
||||
$(eval $(call BuildPackage,libwebsockets-openssl))
|
||||
$(eval $(call BuildPackage,libwebsockets-mbedtls))
|
||||
$(eval $(call BuildPackage,libwebsockets-full))
|
||||
@@ -0,0 +1,16 @@
|
||||
Index: libwebsockets-4.1.4/CMakeLists.txt
|
||||
===================================================================
|
||||
--- libwebsockets-4.1.4.orig/CMakeLists.txt
|
||||
+++ libwebsockets-4.1.4/CMakeLists.txt
|
||||
@@ -709,10 +709,10 @@ if ((CMAKE_COMPILER_IS_GNUCC OR CMAKE_CO
|
||||
endif()
|
||||
endif()
|
||||
|
||||
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations" )
|
||||
if (COMPILER_IS_CLANG)
|
||||
|
||||
# otherwise osx blows a bunch of openssl deprecated api errors
|
||||
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations" )
|
||||
if (UNIX AND LWS_HAVE_PTHREAD_H)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread -Wno-error=unused-command-line-argument" )
|
||||
endif()
|
||||
@@ -0,0 +1,30 @@
|
||||
menu "Configuration"
|
||||
depends on PACKAGE_modemmanager
|
||||
|
||||
config MODEMMANAGER_WITH_MBIM
|
||||
bool "Include MBIM support"
|
||||
default y
|
||||
help
|
||||
Compile ModemManager with MBIM support
|
||||
|
||||
config MODEMMANAGER_WITH_QMI
|
||||
bool "Include QMI support"
|
||||
default y
|
||||
help
|
||||
Compile ModemManager with QMI support
|
||||
|
||||
config MODEMMANAGER_WITH_QRTR
|
||||
bool "Include QRTR support"
|
||||
default y
|
||||
depends on MODEMMANAGER_WITH_QMI
|
||||
select LIBQMI_WITH_QRTR_GLIB
|
||||
help
|
||||
Compile ModemManager with QRTR support
|
||||
|
||||
config MODEMMANAGER_WITH_AT_COMMAND_VIA_DBUS
|
||||
bool "Allow AT commands via DBus"
|
||||
default n
|
||||
help
|
||||
Compile ModemManager allowing AT commands without debug flag
|
||||
|
||||
endmenu
|
||||
140
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/Makefile
Normal file
140
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/Makefile
Normal file
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# Copyright (C) 2016 Velocloud Inc.
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=modemmanager
|
||||
PKG_SOURCE_VERSION:=1.20.6
|
||||
PKG_RELEASE:=12
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://gitlab.freedesktop.org/mobile-broadband/ModemManager.git
|
||||
PKG_MIRROR_HASH:=e90103e2e42bb826bbbac83937a9a69f50348cd6ce0d8da655a12b65494ce7c9
|
||||
|
||||
PKG_MAINTAINER:=Nicholas Smith <nicholas@nbembedded.com>
|
||||
PKG_LICENSE:=GPL-2.0-or-later
|
||||
PKG_LICENSE_FILES:=COPYING
|
||||
|
||||
PKG_BUILD_DEPENDS:=glib2/host libxslt/host
|
||||
PKG_BUILD_FLAGS:=gc-sections
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/nls.mk
|
||||
include $(INCLUDE_DIR)/meson.mk
|
||||
|
||||
TARGET_CFLAGS += -fno-merge-all-constants -fmerge-constants
|
||||
|
||||
define Package/modemmanager/config
|
||||
source "$(SOURCE)/Config.in"
|
||||
endef
|
||||
|
||||
define Package/modemmanager
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Control utility for any kind of mobile broadband modem
|
||||
URL:=https://www.freedesktop.org/wiki/Software/ModemManager
|
||||
DEPENDS:= \
|
||||
$(INTL_DEPENDS) \
|
||||
+glib2 \
|
||||
+dbus \
|
||||
+ppp \
|
||||
+MODEMMANAGER_WITH_MBIM:libmbim \
|
||||
+MODEMMANAGER_WITH_QMI:libqmi \
|
||||
+MODEMMANAGER_WITH_QRTR:libqrtr-glib
|
||||
endef
|
||||
|
||||
define Package/modemmanager/description
|
||||
ModemManager is a D-Bus-activated service which allows controlling mobile
|
||||
broadband modems. Add kernel modules for your modems as needed.
|
||||
Select Utilities/usb-modeswitch if needed.
|
||||
endef
|
||||
|
||||
MESON_ARGS += \
|
||||
-Dudev=false \
|
||||
-Dudevdir=/lib/udev \
|
||||
-Dtests=false \
|
||||
-Dsystemdsystemunitdir=no \
|
||||
-Dsystemd_suspend_resume=false \
|
||||
-Dsystemd_journal=false \
|
||||
-Dpolkit=no \
|
||||
-Dintrospection=false \
|
||||
-Dman=false \
|
||||
-Dbash_completion=false \
|
||||
-Db_lto=true \
|
||||
-Dmbim=$(if $(CONFIG_MODEMMANAGER_WITH_MBIM),true,false) \
|
||||
-Dqmi=$(if $(CONFIG_MODEMMANAGER_WITH_QMI),true,false) \
|
||||
-Dqrtr=$(if $(CONFIG_MODEMMANAGER_WITH_QRTR),true,false) \
|
||||
-Dat_command_via_dbus=$(if $(CONFIG_MODEMMANAGER_WITH_AT_COMMAND_VIA_DBUS),true,false)
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(1)/usr/include/ModemManager
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/include/ModemManager/*.h $(1)/usr/include/ModemManager
|
||||
$(INSTALL_DIR) $(1)/usr/include/libmm-glib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/include/libmm-glib/*.h $(1)/usr/include/libmm-glib
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libmm-glib.so* $(1)/usr/lib
|
||||
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/ModemManager.pc $(1)/usr/lib/pkgconfig
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/mm-glib.pc $(1)/usr/lib/pkgconfig
|
||||
$(INSTALL_DIR) $(1)/usr/share/dbus-1/interfaces
|
||||
$(CP) $(PKG_BUILD_DIR)/introspection/org.freedesktop.ModemManager1.* $(1)/usr/share/dbus-1/interfaces
|
||||
endef
|
||||
|
||||
define Package/modemmanager/install
|
||||
$(INSTALL_DIR) $(1)/lib/udev/rules.d
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/lib/udev/rules.d/*.rules $(1)/lib/udev/rules.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ModemManager $(1)/usr/sbin
|
||||
$(INSTALL_BIN) ./files/usr/sbin/ModemManager-wrapper $(1)/usr/sbin
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/mmcli $(1)/usr/bin
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libmm-glib.so.* $(1)/usr/lib
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/ModemManager
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/ModemManager/libmm-shared-*.so* $(1)/usr/lib/ModemManager
|
||||
$(CP) $(PKG_INSTALL_DIR)/usr/lib/ModemManager/libmm-plugin-*.so* $(1)/usr/lib/ModemManager
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/lib/ModemManager/connection.d
|
||||
$(INSTALL_BIN) ./files/10-report-down $(1)/usr/lib/ModemManager/connection.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/dbus-1/system.d
|
||||
$(INSTALL_CONF) $(PKG_INSTALL_DIR)/etc/dbus-1/system.d/org.freedesktop.ModemManager1.conf $(1)/etc/dbus-1/system.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/dbus-1/system-services
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/dbus-1/system-services/org.freedesktop.ModemManager1.service $(1)/usr/share/dbus-1/system-services
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/ModemManager
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/ModemManager/*.conf $(1)/usr/share/ModemManager
|
||||
$(INSTALL_DATA) ./files/modemmanager.common $(1)/usr/share/ModemManager
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/share/ModemManager/fcc-unlock.available.d
|
||||
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/ModemManager/fcc-unlock.available.d/* $(1)/usr/share/ModemManager/fcc-unlock.available.d
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/modemmanager.init $(1)/etc/init.d/modemmanager
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/usb
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-usb $(1)/etc/hotplug.d/usb
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/net
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-net $(1)/etc/hotplug.d/net
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/tty
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-tty $(1)/etc/hotplug.d/tty
|
||||
|
||||
$(INSTALL_DIR) $(1)/etc/hotplug.d/wwan
|
||||
$(INSTALL_DATA) ./files/25-modemmanager-wwan $(1)/etc/hotplug.d/wwan
|
||||
|
||||
$(INSTALL_DIR) $(1)/lib/netifd/proto
|
||||
$(INSTALL_BIN) ./files/modemmanager.proto $(1)/lib/netifd/proto/modemmanager.sh
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,modemmanager))
|
||||
@@ -0,0 +1,44 @@
|
||||
# OpenWrt ModemManager
|
||||
|
||||
## Description
|
||||
|
||||
Cellular modem control and connectivity
|
||||
|
||||
Optional libraries libmbim and libqmi are available.
|
||||
Your modem may require additional kernel modules and/or the usb-modeswitch
|
||||
package.
|
||||
|
||||
## Usage
|
||||
|
||||
Once installed, you can configure the 2G/3G/4G modem connections directly in
|
||||
/etc/config/network as in the following example:
|
||||
|
||||
config interface 'broadband'
|
||||
option device '/sys/devices/platform/soc/20980000.usb/usb1/1-1/1-1.2/1-1.2.1'
|
||||
option proto 'modemmanager'
|
||||
option apn 'ac.vodafone.es'
|
||||
option allowedauth 'pap chap'
|
||||
option username 'vodafone'
|
||||
option password 'vodafone'
|
||||
option pincode '7423'
|
||||
option iptype 'ipv4'
|
||||
option plmn '214001'
|
||||
option lowpower '1'
|
||||
option signalrate '30'
|
||||
option allow_roaming '1'
|
||||
|
||||
Only 'device' and 'proto' are mandatory options, the remaining ones are all
|
||||
optional.
|
||||
|
||||
The 'allowedauth' option allows limiting the list of authentication protocols.
|
||||
It is given as a space-separated list of values, including any of the
|
||||
following: 'pap', 'chap', 'mschap', 'mschapv2' or 'eap'. It will default to
|
||||
allowing all protocols.
|
||||
|
||||
The 'iptype' option supports any of these values: 'ipv4', 'ipv6' or 'ipv4v6'.
|
||||
It will default to 'ipv4' if not given.
|
||||
|
||||
The 'plmn' option allows to set the network operator MCCMNC.
|
||||
|
||||
The 'signalrate' option set's the signal refresh rate (in seconds) for the device.
|
||||
You can call signal info with command: mmcli -m 0 --signal-get
|
||||
35
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/10-report-down
Executable file
35
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/10-report-down
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
# 2022 Aleksander Morgado <aleksander@aleksander.es>
|
||||
#
|
||||
# Automatically report to netifd that the underlying modem
|
||||
# is really disconnected
|
||||
#
|
||||
# require program name and at least 4 arguments
|
||||
[ $# -lt 4 ] && exit 1
|
||||
|
||||
MODEM_PATH="$1"
|
||||
BEARER_PATH="$2"
|
||||
INTERFACE="$3"
|
||||
STATE="$4"
|
||||
|
||||
[ "${STATE}" = "disconnected" ] || exit 0
|
||||
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
. /lib/netifd/netifd-proto.sh
|
||||
INCLUDE_ONLY=1 . /lib/netifd/proto/modemmanager.sh
|
||||
|
||||
MODEM_STATUS=$(mmcli --modem="${MODEM_PATH}" --output-keyvalue)
|
||||
[ -n "${MODEM_STATUS}" ] || exit 1
|
||||
|
||||
MODEM_DEVICE=$(modemmanager_get_field "${MODEM_STATUS}" "modem.generic.device")
|
||||
[ -n "${MODEM_DEVICE}" ] || exit 2
|
||||
|
||||
CFG=$(mm_get_modem_config "${MODEM_DEVICE}")
|
||||
[ -n "${CFG}" ] || exit 3
|
||||
|
||||
logger -t "modemmanager" "interface ${CFG} (network device ${INTERFACE}) ${STATE}"
|
||||
proto_init_update $INTERFACE 0
|
||||
proto_send_update $CFG
|
||||
exit 0
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016 Velocloud Inc
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# Load common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
# We require a interface name
|
||||
[ -n "${INTERFACE}" ] || exit
|
||||
|
||||
# Always make sure the rundir exists
|
||||
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
|
||||
|
||||
# Report network interface
|
||||
mm_log "info" "${ACTION} network interface ${INTERFACE}: event processed"
|
||||
mm_report_event "${ACTION}" "${INTERFACE}" "net" "/sys${DEVPATH}"
|
||||
|
||||
# Look for an associated cdc-wdm interface
|
||||
|
||||
cdcwdm=""
|
||||
|
||||
case "${ACTION}" in
|
||||
"add") cdcwdm=$(mm_track_cdcwdm "${INTERFACE}") ;;
|
||||
"remove") cdcwdm=$(mm_untrack_cdcwdm "${INTERFACE}") ;;
|
||||
esac
|
||||
|
||||
# Report cdc-wdm device, if any
|
||||
[ -n "${cdcwdm}" ] && {
|
||||
mm_log "info" "${ACTION} cdc interface ${cdcwdm}: custom event processed"
|
||||
mm_report_event "${ACTION}" "${cdcwdm}" "usbmisc" "/sys${DEVPATH}"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016 Velocloud Inc
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# Load hotplug common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
# We require a device name
|
||||
[ -n "$DEVNAME" ] || exit
|
||||
|
||||
# Always make sure the rundir exists
|
||||
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
|
||||
|
||||
# Report TTY
|
||||
mm_log "info" "${ACTION} serial interface ${DEVNAME}: event processed"
|
||||
mm_report_event "${ACTION}" "${DEVNAME}" "tty" "/sys${DEVPATH}"
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# We need to process only full USB device removal events, we don't
|
||||
# want to process specific interface removal events.
|
||||
[ "$ACTION" = remove ] || exit
|
||||
[ -z "${INTERFACE}" ] || exit
|
||||
|
||||
# Load common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
mm_clear_modem_wait_status "/sys${DEVPATH}"
|
||||
mm_cleanup_interface_by_sysfspath "/sys${DEVPATH}"
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
# Load hotplug common utilities
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
# We require a device name
|
||||
[ -n "$DEVNAME" ] || exit
|
||||
|
||||
# Always make sure the rundir exists
|
||||
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
|
||||
|
||||
# Report wwan
|
||||
mm_log "info" "${ACTION} wwan control port ${DEVNAME}: event processed"
|
||||
mm_report_event "${ACTION}" "${DEVNAME}" "wwan" "/sys${DEVPATH}"
|
||||
@@ -0,0 +1,376 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016 Velocloud Inc
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
################################################################################
|
||||
|
||||
. /lib/functions.sh
|
||||
. /lib/netifd/netifd-proto.sh
|
||||
|
||||
################################################################################
|
||||
# Runtime state
|
||||
|
||||
MODEMMANAGER_RUNDIR="/var/run/modemmanager"
|
||||
MODEMMANAGER_PID_FILE="${MODEMMANAGER_RUNDIR}/modemmanager.pid"
|
||||
MODEMMANAGER_CDCWDM_CACHE="${MODEMMANAGER_RUNDIR}/cdcwdm.cache"
|
||||
MODEMMANAGER_SYSFS_CACHE="${MODEMMANAGER_RUNDIR}/sysfs.cache"
|
||||
MODEMMANAGER_EVENTS_CACHE="${MODEMMANAGER_RUNDIR}/events.cache"
|
||||
|
||||
################################################################################
|
||||
# Common logging
|
||||
|
||||
mm_log() {
|
||||
local level="$1"; shift
|
||||
logger -p "daemon.${level}" -t "ModemManager[$$]" "hotplug: $*"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Receives as input argument the full sysfs path of the device
|
||||
# Returns the physical device sysfs path
|
||||
#
|
||||
# NOTE: this method only works when the device exists, i.e. it cannot be used
|
||||
# on removal hotplug events
|
||||
|
||||
mm_find_physdev_sysfs_path() {
|
||||
local tmp_path="$1"
|
||||
|
||||
while true; do
|
||||
tmp_path=$(dirname "${tmp_path}")
|
||||
|
||||
# avoid infinite loops iterating
|
||||
[ -z "${tmp_path}" ] || [ "${tmp_path}" = "/" ] && return
|
||||
|
||||
# For USB devices, the physical device will be that with a idVendor
|
||||
# and idProduct pair of files
|
||||
[ -f "${tmp_path}"/idVendor ] && [ -f "${tmp_path}"/idProduct ] && {
|
||||
tmp_path=$(readlink -f "$tmp_path")
|
||||
echo "${tmp_path}"
|
||||
return
|
||||
}
|
||||
|
||||
# For PCI devices, the physical device will be that with a vendor
|
||||
# and device pair of files
|
||||
[ -f "${tmp_path}"/vendor ] && [ -f "${tmp_path}"/device ] && {
|
||||
tmp_path=$(readlink -f "$tmp_path")
|
||||
echo "${tmp_path}"
|
||||
return
|
||||
}
|
||||
done
|
||||
}
|
||||
|
||||
################################################################################
|
||||
|
||||
# Returns the cdc-wdm name retrieved from sysfs
|
||||
mm_track_cdcwdm() {
|
||||
local wwan="$1"
|
||||
local cdcwdm
|
||||
|
||||
cdcwdm=$(ls "/sys/class/net/${wwan}/device/usbmisc/")
|
||||
[ -n "${cdcwdm}" ] || return
|
||||
|
||||
# We have to cache it for later, as we won't be able to get the
|
||||
# associated cdc-wdm device on a remove event
|
||||
echo "${wwan} ${cdcwdm}" >> "${MODEMMANAGER_CDCWDM_CACHE}"
|
||||
|
||||
echo "${cdcwdm}"
|
||||
}
|
||||
|
||||
# Returns the cdc-wdm name retrieved from the cache
|
||||
mm_untrack_cdcwdm() {
|
||||
local wwan="$1"
|
||||
local cdcwdm
|
||||
|
||||
# Look for the cached associated cdc-wdm device
|
||||
[ -f "${MODEMMANAGER_CDCWDM_CACHE}" ] || return
|
||||
|
||||
cdcwdm=$(awk -v wwan="${wwan}" '!/^#/ && $0 ~ wwan { print $2 }' "${MODEMMANAGER_CDCWDM_CACHE}")
|
||||
[ -n "${cdcwdm}" ] || return
|
||||
|
||||
# Remove from cache
|
||||
sed -i "/${wwan} ${cdcwdm}/d" "${MODEMMANAGER_CDCWDM_CACHE}"
|
||||
|
||||
echo "${cdcwdm}"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# ModemManager needs some time from the ports being added until a modem object
|
||||
# is exposed in DBus. With the logic here we do an explicit wait of N seconds
|
||||
# for ModemManager to expose the new modem object, making sure that the wait is
|
||||
# unique per device (i.e. per physical device sysfs path).
|
||||
|
||||
# Gets the modem wait status as retrieved from the cache
|
||||
mm_get_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
|
||||
# If no sysfs cache file, we're done
|
||||
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] || return
|
||||
|
||||
# Get status of the sysfs path
|
||||
awk -v sysfspath="${sysfspath}" '!/^#/ && $0 ~ sysfspath { print $2 }' "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
|
||||
# Clear the modem wait status from the cache, if any
|
||||
mm_clear_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
|
||||
local escaped_sysfspath
|
||||
|
||||
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] && {
|
||||
# escape '/', '\' and '&' for sed...
|
||||
escaped_sysfspath=$(echo "$sysfspath" | sed -e 's/[\/&]/\\&/g')
|
||||
sed -i "/${escaped_sysfspath}/d" "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
}
|
||||
|
||||
# Sets the modem wait status in the cache
|
||||
mm_set_modem_wait_status() {
|
||||
local sysfspath="$1"
|
||||
local status="$2"
|
||||
|
||||
# Remove sysfs line before adding the new one with the new state
|
||||
mm_clear_modem_wait_status "${sysfspath}"
|
||||
|
||||
# Add the new status
|
||||
echo "${sysfspath} ${status}" >> "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
}
|
||||
|
||||
# Callback for config_foreach()
|
||||
mm_get_modem_config_foreach_cb() {
|
||||
local cfg="$1"
|
||||
local sysfspath="$2"
|
||||
|
||||
local proto
|
||||
config_get proto "${cfg}" proto
|
||||
[ "${proto}" = modemmanager ] || return 0
|
||||
|
||||
local dev
|
||||
dev=$(uci_get network "${cfg}" device)
|
||||
[ "${dev}" = "${sysfspath}" ] || return 0
|
||||
|
||||
echo "${cfg}"
|
||||
}
|
||||
|
||||
# Returns the name of the interface configured for this device
|
||||
mm_get_modem_config() {
|
||||
local sysfspath="$1"
|
||||
|
||||
# Look for configuration for the given sysfs path
|
||||
config_load network
|
||||
config_foreach mm_get_modem_config_foreach_cb interface "${sysfspath}"
|
||||
}
|
||||
|
||||
# Callback for config_foreach()
|
||||
mm_get_wwan_config_foreach_cb() {
|
||||
local cfg="$1"
|
||||
local sysfspath="$2"
|
||||
|
||||
local proto
|
||||
config_get proto "${cfg}" proto
|
||||
[ "${proto}" = "wwan" ] || return 0
|
||||
|
||||
local dev devname devpath
|
||||
dev=$(uci_get network "${cfg}" device)
|
||||
devname=$(basename "${dev}")
|
||||
devpath=$(find /sys/devices -name "${devname}")
|
||||
[[ "${devpath}" = "${sysfspath}*" ]] || return 0
|
||||
|
||||
echo "${cfg}"
|
||||
}
|
||||
|
||||
# Returns the name of the interface configured for this device
|
||||
mm_get_wwan_config() {
|
||||
local sysfspath="$1"
|
||||
|
||||
# Look for configuration for the given sysfs path
|
||||
config_load network
|
||||
config_foreach mm_get_wwan_config_foreach_cb interface "${sysfspath}"
|
||||
}
|
||||
|
||||
# Wait for a modem in the specified sysfspath
|
||||
mm_wait_for_modem() {
|
||||
local cfg="$1"
|
||||
local sysfspath="$2"
|
||||
|
||||
# TODO: config max wait
|
||||
local n=45
|
||||
local step=5
|
||||
|
||||
while [ $n -ge 0 ]; do
|
||||
[ -d "${sysfspath}" ] || {
|
||||
mm_log "error" "ignoring modem detection request: no device at ${sysfspath}"
|
||||
proto_set_available "${cfg}" 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check if the modem exists at the given sysfs path
|
||||
if ! mmcli -m "${sysfspath}" > /dev/null 2>&1
|
||||
then
|
||||
mm_log "error" "modem not detected at sysfs path"
|
||||
else
|
||||
mm_log "info" "modem exported successfully at ${sysfspath}"
|
||||
mm_log "info" "setting interface '${cfg}' as available"
|
||||
proto_set_available "${cfg}" 1
|
||||
ifup "${cfg}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep $step
|
||||
n=$((n-step))
|
||||
done
|
||||
|
||||
mm_log "error" "timed out waiting for the modem to get exported at ${sysfspath}"
|
||||
proto_set_available "${cfg}" 0
|
||||
return 2
|
||||
}
|
||||
|
||||
mm_report_modem_wait() {
|
||||
local sysfspath=$1
|
||||
|
||||
local parent_sysfspath status
|
||||
|
||||
parent_sysfspath=$(mm_find_physdev_sysfs_path "$sysfspath")
|
||||
[ -n "${parent_sysfspath}" ] || {
|
||||
mm_log "error" "parent device sysfspath not found"
|
||||
return
|
||||
}
|
||||
|
||||
status=$(mm_get_modem_wait_status "${parent_sysfspath}")
|
||||
case "${status}" in
|
||||
"")
|
||||
local cfg
|
||||
|
||||
cfg=$(mm_get_modem_config "${parent_sysfspath}")
|
||||
[ -z "${cfg}" ] && cfg=$(mm_get_wwan_config "${parent_sysfspath}")
|
||||
if [ -n "${cfg}" ]; then
|
||||
mm_log "info" "interface '${cfg}' is set to configure device '${parent_sysfspath}'"
|
||||
mm_log "info" "now waiting for modem at sysfs path ${parent_sysfspath}"
|
||||
mm_set_modem_wait_status "${parent_sysfspath}" "processed"
|
||||
# Launch subshell for the explicit wait
|
||||
( mm_wait_for_modem "${cfg}" "${parent_sysfspath}" ) > /dev/null 2>&1 &
|
||||
fi
|
||||
;;
|
||||
"processed")
|
||||
mm_log "info" "already waiting for modem at sysfs path ${parent_sysfspath}"
|
||||
;;
|
||||
"ignored")
|
||||
;;
|
||||
*)
|
||||
mm_log "error" "unknown status read for device at sysfs path ${parent_sysfspath}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Cleanup interfaces
|
||||
|
||||
mm_cleanup_interface_cb() {
|
||||
local cfg="$1"
|
||||
|
||||
local proto
|
||||
config_get proto "${cfg}" proto
|
||||
[ "${proto}" = modemmanager ] || return 0
|
||||
|
||||
proto_set_available "${cfg}" 0
|
||||
}
|
||||
|
||||
mm_cleanup_interfaces() {
|
||||
config_load network
|
||||
config_foreach mm_cleanup_interface_cb interface
|
||||
}
|
||||
|
||||
mm_cleanup_interface_by_sysfspath() {
|
||||
local dev="$1"
|
||||
|
||||
local cfg
|
||||
cfg=$(mm_get_modem_config "$dev")
|
||||
[ -n "${cfg}" ] || return
|
||||
|
||||
mm_log "info" "setting interface '$cfg' as unavailable"
|
||||
proto_set_available "${cfg}" 0
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Event reporting
|
||||
|
||||
# Receives as input the action, the device name and the subsystem
|
||||
mm_report_event() {
|
||||
local action="$1"
|
||||
local name="$2"
|
||||
local subsystem="$3"
|
||||
local sysfspath="$4"
|
||||
|
||||
# Do not save virtual devices
|
||||
local virtual
|
||||
virtual="$(echo "$sysfspath" | cut -d'/' -f4)"
|
||||
[ "$virtual" = "virtual" ] && {
|
||||
mm_log "debug" "sysfspath is a virtual device ($sysfspath)"
|
||||
return
|
||||
}
|
||||
|
||||
# Track/untrack events in cache
|
||||
case "${action}" in
|
||||
"add")
|
||||
# On add events, store event details in cache (if not exists yet)
|
||||
grep -qs "${name},${subsystem}" "${MODEMMANAGER_EVENTS_CACHE}" || \
|
||||
echo "${action},${name},${subsystem},${sysfspath}" >> "${MODEMMANAGER_EVENTS_CACHE}"
|
||||
;;
|
||||
"remove")
|
||||
# On remove events, remove old events from cache (match by subsystem+name)
|
||||
sed -i "/${name},${subsystem}/d" "${MODEMMANAGER_EVENTS_CACHE}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Report the event
|
||||
mm_log "debug" "event reported: action=${action}, name=${name}, subsystem=${subsystem}"
|
||||
mmcli --report-kernel-event="action=${action},name=${name},subsystem=${subsystem}" 1>/dev/null 2>&1 &
|
||||
|
||||
# Wait for added modem if a sysfspath is given
|
||||
[ -n "${sysfspath}" ] && [ "$action" = "add" ] && mm_report_modem_wait "${sysfspath}"
|
||||
}
|
||||
|
||||
mm_report_event_from_cache_line() {
|
||||
local event_line="$1"
|
||||
|
||||
local action name subsystem sysfspath
|
||||
action=$(echo "${event_line}" | awk -F ',' '{ print $1 }')
|
||||
name=$(echo "${event_line}" | awk -F ',' '{ print $2 }')
|
||||
subsystem=$(echo "${event_line}" | awk -F ',' '{ print $3 }')
|
||||
sysfspath=$(echo "${event_line}" | awk -F ',' '{ print $4 }')
|
||||
|
||||
mm_log "debug" "cached event found: action=${action}, name=${name}, subsystem=${subsystem}, sysfspath=${sysfspath}"
|
||||
mm_report_event "${action}" "${name}" "${subsystem}" "${sysfspath}"
|
||||
}
|
||||
|
||||
mm_report_events_from_cache() {
|
||||
# Remove the sysfs cache
|
||||
rm -f "${MODEMMANAGER_SYSFS_CACHE}"
|
||||
|
||||
local n=60
|
||||
local step=1
|
||||
local mmrunning=0
|
||||
|
||||
# Wait for ModemManager to be available in the bus
|
||||
while [ $n -ge 0 ]; do
|
||||
sleep $step
|
||||
mm_log "info" "checking if ModemManager is available..."
|
||||
|
||||
if ! mmcli -L >/dev/null 2>&1
|
||||
then
|
||||
mm_log "info" "ModemManager not yet available"
|
||||
else
|
||||
mmrunning=1
|
||||
break
|
||||
fi
|
||||
n=$((n-step))
|
||||
done
|
||||
|
||||
[ ${mmrunning} -eq 1 ] || {
|
||||
mm_log "error" "couldn't report initial kernel events: ModemManager not running"
|
||||
return
|
||||
}
|
||||
|
||||
# Report cached kernel events
|
||||
while IFS= read -r event_line; do
|
||||
mm_report_event_from_cache_line "${event_line}"
|
||||
done < ${MODEMMANAGER_EVENTS_CACHE}
|
||||
}
|
||||
38
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.init
Executable file
38
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.init
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
USE_PROCD=1
|
||||
START=70
|
||||
|
||||
LOG_LEVEL="INFO"
|
||||
|
||||
stop_service() {
|
||||
# Load common utils
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
# Set all configured interfaces as unavailable
|
||||
mm_cleanup_interfaces
|
||||
}
|
||||
|
||||
start_service() {
|
||||
# Setup ModemManager service
|
||||
#
|
||||
# We will make sure that the rundir always exists, and we initially cleanup
|
||||
# all interfaces flagging them as unavailable.
|
||||
#
|
||||
# The cached events processing will wait for MM to be available in DBus
|
||||
# and will make sure all ports are re-notified to ModemManager every time
|
||||
# it starts.
|
||||
#
|
||||
# All these commands need to be executed on every MM start, even after
|
||||
# procd-triggered respawns, which is why this is wrapped in a startup
|
||||
# wrapper script called '/usr/sbin/ModemManager-wrapper'.
|
||||
#
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/sbin/ModemManager-wrapper
|
||||
procd_append_param command --log-level="$LOG_LEVEL"
|
||||
[ "$LOG_LEVEL" = "DEBUG" ] && procd_append_param command --debug
|
||||
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
|
||||
procd_set_param pidfile "${MODEMMANAGER_PID_FILE}"
|
||||
procd_close_instance
|
||||
}
|
||||
745
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.proto
Executable file
745
olg-ucentral-client-sdk/feeds/ucentral/modemmanager/files/modemmanager.proto
Executable file
@@ -0,0 +1,745 @@
|
||||
#!/bin/sh
|
||||
# Copyright (C) 2016-2019 Aleksander Morgado <aleksander@aleksander.es>
|
||||
|
||||
[ -x /usr/bin/mmcli ] || exit 0
|
||||
[ -x /usr/sbin/pppd ] || exit 0
|
||||
|
||||
[ -n "$INCLUDE_ONLY" ] || {
|
||||
. /lib/functions.sh
|
||||
. ../netifd-proto.sh
|
||||
. ./ppp.sh
|
||||
init_proto "$@"
|
||||
}
|
||||
|
||||
cdr2mask ()
|
||||
{
|
||||
# Number of args to shift, 255..255, first non-255 byte, zeroes
|
||||
set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
|
||||
if [ "$1" -gt 1 ]
|
||||
then
|
||||
shift "$1"
|
||||
else
|
||||
shift
|
||||
fi
|
||||
echo "${1-0}"."${2-0}"."${3-0}"."${4-0}"
|
||||
}
|
||||
|
||||
# This method expects as first argument a list of key-value pairs, as returned by mmcli --output-keyvalue
|
||||
# The second argument must be exactly the name of the field to read
|
||||
#
|
||||
# Sample output:
|
||||
# $ mmcli -m 0 -K
|
||||
# modem.dbus-path : /org/freedesktop/ModemManager1/Modem/0
|
||||
# modem.generic.device-identifier : ed6eff2e3e0f90463da1c2a755b2acacd1335752
|
||||
# modem.generic.manufacturer : Dell Inc.
|
||||
# modem.generic.model : DW5821e Snapdragon X20 LTE
|
||||
# modem.generic.revision : T77W968.F1.0.0.4.0.GC.009\n026
|
||||
# modem.generic.carrier-configuration : GCF
|
||||
# modem.generic.carrier-configuration-revision : 08E00009
|
||||
# modem.generic.hardware-revision : DW5821e Snapdragon X20 LTE
|
||||
# ....
|
||||
modemmanager_get_field() {
|
||||
local list=$1
|
||||
local field=$2
|
||||
local value=""
|
||||
|
||||
[ -z "${list}" ] || [ -z "${field}" ] && return
|
||||
|
||||
# there is always at least a whitespace after each key, and we use that as part of the
|
||||
# key matching we do (e.g. to avoid getting 'modem.generic.state-failed-reason' as a result
|
||||
# when grepping for 'modem.generic.state'.
|
||||
line=$(echo "${list}" | grep "${field} ")
|
||||
value=$(echo ${line#*:})
|
||||
|
||||
# not found?
|
||||
[ -n "${value}" ] || return 2
|
||||
|
||||
# only print value if set
|
||||
[ "${value}" != "--" ] && echo "${value}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# build a comma-separated list of values from the list
|
||||
modemmanager_get_multivalue_field() {
|
||||
local list=$1
|
||||
local field=$2
|
||||
local value=""
|
||||
local length idx item
|
||||
|
||||
[ -z "${list}" ] || [ -z "${field}" ] && return
|
||||
|
||||
length=$(modemmanager_get_field "${list}" "${field}.length")
|
||||
[ -n "${length}" ] || return 0
|
||||
[ "$length" -ge 1 ] || return 0
|
||||
|
||||
idx=1
|
||||
while [ $idx -le "$length" ]; do
|
||||
item=$(modemmanager_get_field "${list}" "${field}.value\[$idx\]")
|
||||
[ -n "${item}" ] && [ "${item}" != "--" ] && {
|
||||
[ -n "${value}" ] && value="${value}, "
|
||||
value="${value}${item}"
|
||||
}
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
|
||||
# nothing built?
|
||||
[ -n "${value}" ] || return 2
|
||||
|
||||
# only print value if set
|
||||
echo "${value}"
|
||||
return 0
|
||||
}
|
||||
|
||||
modemmanager_cleanup_connection() {
|
||||
local modemstatus="$1"
|
||||
|
||||
local bearercount idx bearerpath
|
||||
|
||||
bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
|
||||
|
||||
# do nothing if no bearers reported
|
||||
[ -n "${bearercount}" ] && [ "$bearercount" -ge 1 ] && {
|
||||
# explicitly disconnect just in case
|
||||
mmcli --modem="${device}" --simple-disconnect >/dev/null 2>&1
|
||||
# and remove all bearer objects, if any found
|
||||
idx=1
|
||||
while [ $idx -le "$bearercount" ]; do
|
||||
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[$idx\]")
|
||||
mmcli --modem "${device}" --delete-bearer="${bearerpath}" >/dev/null 2>&1
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
modemmanager_connected_method_ppp_ipv4() {
|
||||
local interface="$1"
|
||||
local ttyname="$2"
|
||||
local username="$3"
|
||||
local password="$4"
|
||||
local allowedauth="$5"
|
||||
|
||||
# all auth types are allowed unless a user given list is given
|
||||
local authopts
|
||||
local pap=1
|
||||
local chap=1
|
||||
local mschap=1
|
||||
local mschapv2=1
|
||||
local eap=1
|
||||
|
||||
[ -n "$allowedauth" ] && {
|
||||
pap=0 chap=0 mschap=0 mschapv2=0 eap=0
|
||||
for auth in $allowedauth; do
|
||||
case $auth in
|
||||
"pap") pap=1 ;;
|
||||
"chap") chap=1 ;;
|
||||
"mschap") mschap=1 ;;
|
||||
"mschapv2") mschapv2=1 ;;
|
||||
"eap") eap=1 ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
[ $pap -eq 1 ] || append authopts "refuse-pap"
|
||||
[ $chap -eq 1 ] || append authopts "refuse-chap"
|
||||
[ $mschap -eq 1 ] || append authopts "refuse-mschap"
|
||||
[ $mschapv2 -eq 1 ] || append authopts "refuse-mschap-v2"
|
||||
[ $eap -eq 1 ] || append authopts "refuse-eap"
|
||||
|
||||
proto_run_command "${interface}" /usr/sbin/pppd \
|
||||
"${ttyname}" \
|
||||
115200 \
|
||||
nodetach \
|
||||
noaccomp \
|
||||
nobsdcomp \
|
||||
nopcomp \
|
||||
novj \
|
||||
noauth \
|
||||
$authopts \
|
||||
${username:+ user "$username"} \
|
||||
${password:+ password "$password"} \
|
||||
lcp-echo-failure 5 \
|
||||
lcp-echo-interval 15 \
|
||||
lock \
|
||||
crtscts \
|
||||
nodefaultroute \
|
||||
usepeerdns \
|
||||
ipparam "${interface}" \
|
||||
ip-up-script /lib/netifd/ppp-up \
|
||||
ip-down-script /lib/netifd/ppp-down
|
||||
}
|
||||
|
||||
modemmanager_disconnected_method_ppp_ipv4() {
|
||||
local interface="$1"
|
||||
|
||||
echo "running disconnection (ppp method)"
|
||||
|
||||
[ -n "${ERROR}" ] && {
|
||||
local errorstring
|
||||
errorstring=$(ppp_exitcode_tostring "${ERROR}")
|
||||
case "$ERROR" in
|
||||
0)
|
||||
;;
|
||||
2)
|
||||
proto_notify_error "$interface" "$errorstring"
|
||||
proto_block_restart "$interface"
|
||||
;;
|
||||
*)
|
||||
proto_notify_error "$interface" "$errorstring"
|
||||
;;
|
||||
esac
|
||||
} || echo "pppd result code not given"
|
||||
|
||||
proto_kill_command "$interface"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_dhcp_ipv4() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local metric="$3"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
proto_send_update "${interface}"
|
||||
|
||||
json_init
|
||||
json_add_string name "${interface}_4"
|
||||
json_add_string ifname "@${interface}"
|
||||
json_add_string proto "dhcp"
|
||||
proto_add_dynamic_defaults
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
json_close_object
|
||||
ubus call network add_dynamic "$(json_dump)"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_static_ipv4() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local address="$3"
|
||||
local prefix="$4"
|
||||
local gateway="$5"
|
||||
local mtu="$6"
|
||||
local dns1="$7"
|
||||
local dns2="$8"
|
||||
local metric="$9"
|
||||
|
||||
local mask=""
|
||||
|
||||
[ -n "${address}" ] || {
|
||||
proto_notify_error "${interface}" ADDRESS_MISSING
|
||||
return
|
||||
}
|
||||
|
||||
[ -n "${prefix}" ] || {
|
||||
proto_notify_error "${interface}" PREFIX_MISSING
|
||||
return
|
||||
}
|
||||
mask=$(cdr2mask "${prefix}")
|
||||
|
||||
[ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
echo "adding IPv4 address ${address}, netmask ${mask}"
|
||||
proto_add_ipv4_address "${address}" "${mask}"
|
||||
[ -n "${gateway}" ] && {
|
||||
echo "adding default IPv4 route via ${gateway}"
|
||||
proto_add_ipv4_route "0.0.0.0" "0" "${gateway}" "${address}"
|
||||
}
|
||||
[ -n "${dns1}" ] && {
|
||||
echo "adding primary DNS at ${dns1}"
|
||||
proto_add_dns_server "${dns1}"
|
||||
}
|
||||
[ -n "${dns2}" ] && {
|
||||
echo "adding secondary DNS at ${dns2}"
|
||||
proto_add_dns_server "${dns2}"
|
||||
}
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
proto_send_update "${interface}"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_dhcp_ipv6() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local metric="$3"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
proto_send_update "${interface}"
|
||||
|
||||
json_init
|
||||
json_add_string name "${interface}_6"
|
||||
json_add_string ifname "@${interface}"
|
||||
json_add_string proto "dhcpv6"
|
||||
proto_add_dynamic_defaults
|
||||
json_add_string extendprefix 1 # RFC 7278: Extend an IPv6 /64 Prefix to LAN
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
json_close_object
|
||||
ubus call network add_dynamic "$(json_dump)"
|
||||
}
|
||||
|
||||
modemmanager_connected_method_static_ipv6() {
|
||||
local interface="$1"
|
||||
local wwan="$2"
|
||||
local address="$3"
|
||||
local prefix="$4"
|
||||
local gateway="$5"
|
||||
local mtu="$6"
|
||||
local dns1="$7"
|
||||
local dns2="$8"
|
||||
local metric="$9"
|
||||
|
||||
[ -n "${address}" ] || {
|
||||
proto_notify_error "${interface}" ADDRESS_MISSING
|
||||
return
|
||||
}
|
||||
|
||||
[ -n "${prefix}" ] || {
|
||||
proto_notify_error "${interface}" PREFIX_MISSING
|
||||
return
|
||||
}
|
||||
|
||||
[ -n "${mtu}" ] && /sbin/ip link set dev "${wwan}" mtu "${mtu}"
|
||||
|
||||
proto_init_update "${wwan}" 1
|
||||
proto_set_keep 1
|
||||
echo "adding IPv6 address ${address}, prefix ${prefix}"
|
||||
proto_add_ipv6_address "${address}" "128"
|
||||
proto_add_ipv6_prefix "${address}/${prefix}"
|
||||
[ -n "${gateway}" ] && {
|
||||
echo "adding default IPv6 route via ${gateway}"
|
||||
proto_add_ipv6_route "${gateway}" "128"
|
||||
proto_add_ipv6_route "::0" "0" "${gateway}" "" "" "${address}/${prefix}"
|
||||
}
|
||||
[ -n "${dns1}" ] && {
|
||||
echo "adding primary DNS at ${dns1}"
|
||||
proto_add_dns_server "${dns1}"
|
||||
}
|
||||
[ -n "${dns2}" ] && {
|
||||
echo "adding secondary DNS at ${dns2}"
|
||||
proto_add_dns_server "${dns2}"
|
||||
}
|
||||
[ -n "$metric" ] && json_add_int metric "${metric}"
|
||||
proto_send_update "${interface}"
|
||||
}
|
||||
|
||||
modemmanager_disconnected_method_common() {
|
||||
local interface="$1"
|
||||
|
||||
echo "running disconnection (common)"
|
||||
|
||||
proto_init_update "*" 0
|
||||
proto_send_update "${interface}"
|
||||
}
|
||||
|
||||
proto_modemmanager_init_config() {
|
||||
available=1
|
||||
no_device=1
|
||||
proto_config_add_string device
|
||||
proto_config_add_string apn
|
||||
proto_config_add_string 'allowedauth:list(string)'
|
||||
proto_config_add_string username
|
||||
proto_config_add_string password
|
||||
proto_config_add_string allowedmode
|
||||
proto_config_add_string preferredmode
|
||||
proto_config_add_string pincode
|
||||
proto_config_add_string iptype
|
||||
proto_config_add_string plmn
|
||||
proto_config_add_int signalrate
|
||||
proto_config_add_boolean lowpower
|
||||
proto_config_add_boolean allow_roaming
|
||||
proto_config_add_defaults
|
||||
}
|
||||
|
||||
# Append param to the global 'connectargs' variable.
|
||||
append_param() {
|
||||
local param="$1"
|
||||
|
||||
[ -z "$param" ] && return
|
||||
[ -z "$connectargs" ] || connectargs="${connectargs},"
|
||||
connectargs="${connectargs}${param}"
|
||||
}
|
||||
|
||||
modemmanager_set_allowed_mode() {
|
||||
local device="$1"
|
||||
local interface="$2"
|
||||
local allowedmode="$3"
|
||||
|
||||
echo "setting allowed mode to '${allowedmode}'"
|
||||
mmcli --modem="${device}" --set-allowed-modes="${allowedmode}" || {
|
||||
proto_notify_error "${interface}" MM_INVALID_ALLOWED_MODES_LIST
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
modemmanager_set_preferred_mode() {
|
||||
local device="$1"
|
||||
local interface="$2"
|
||||
local allowedmode="$3"
|
||||
local preferredmode="$4"
|
||||
|
||||
[ -z "${preferredmode}" ] && {
|
||||
echo "no preferred mode configured"
|
||||
proto_notify_error "${interface}" MM_NO_PREFERRED_MODE_CONFIGURED
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
|
||||
[ -z "${allowedmode}" ] && {
|
||||
echo "no allowed mode configured"
|
||||
proto_notify_error "${interface}" MM_NO_ALLOWED_MODE_CONFIGURED
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "setting preferred mode to '${preferredmode}' (${allowedmode})"
|
||||
mmcli --modem="${device}" \
|
||||
--set-preferred-mode="${preferredmode}" \
|
||||
--set-allowed-modes="${allowedmode}" || {
|
||||
proto_notify_error "${interface}" MM_FAILED_SETTING_PREFERRED_MODE
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
exec_at_command() {
|
||||
local at_command=$1
|
||||
local serial_dev=$2
|
||||
|
||||
local resp_file="/tmp/at_response.txt"
|
||||
local cmd_timeout=1
|
||||
local retry_limit=3
|
||||
local i=0
|
||||
local socat_pid reader_pid
|
||||
|
||||
while [ $i -lt ${retry_limit} ]; do
|
||||
# Flush old data
|
||||
> ${resp_file}
|
||||
|
||||
echo "${at_command}" | socat - /dev/${serial_dev},crnl &
|
||||
socat_pid=$!
|
||||
|
||||
{
|
||||
cat /dev/${serial_dev} > ${resp_file} &
|
||||
reader_pid=$!
|
||||
sleep ${cmd_timeout}
|
||||
kill -9 ${reader_pid} 2>/dev/null
|
||||
}
|
||||
|
||||
kill -9 ${socat_pid} 2>/dev/null
|
||||
|
||||
case $(cat ${resp_file}) in
|
||||
*OK*)
|
||||
echo "AT command executed successfully"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo -n "AT command failed or no response"
|
||||
[ -n "$i" ] && echo ", retry $i" || echo
|
||||
;;
|
||||
esac
|
||||
|
||||
let "i++"
|
||||
done
|
||||
|
||||
rm -f ${resp_file}
|
||||
}
|
||||
|
||||
_mm_get_processed_device() {
|
||||
local mm_rundir="/var/run/modemmanager"
|
||||
local mm_sysfs_cache="${mm_rundir}/sysfs.cache"
|
||||
|
||||
awk -v status="processed" '!/^#/ && $0 ~ status {print $1}' "${mm_sysfs_cache}"
|
||||
}
|
||||
|
||||
proto_modemmanager_quectel_setup() {
|
||||
local apn username password pdptype
|
||||
local manufacturer ser_port context_id context_type at_command
|
||||
json_get_vars apn username password pdptype
|
||||
|
||||
local device=$(_mm_get_processed_device)
|
||||
[ -n "${device}" ] || {
|
||||
echo "No processed device"
|
||||
return 1
|
||||
}
|
||||
|
||||
# validate that ModemManager is handling the modem at the sysfs path
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
modempath=$(modemmanager_get_field "${modemstatus}" "modem.dbus-path")
|
||||
[ -n "${modempath}" ] || {
|
||||
echo "Device not managed by ModemManager"
|
||||
return 1
|
||||
}
|
||||
echo "modem available at ${modempath}"
|
||||
|
||||
# workaround for Quectel embedded TCP/IP Stack
|
||||
manufacturer=$(modemmanager_get_field "${modemstatus}" "modem.generic.manufacturer")
|
||||
ser_port=$(mmcli --modem="${device}" -K | grep modem.generic.ports | grep tty | awk '{print $3}' | head -n 1)
|
||||
if [ "${manufacturer}" = "Quectel" ]; then
|
||||
echo "setup TCP/IP Context"
|
||||
case "${pdptype}" in
|
||||
ip)
|
||||
context_type=1
|
||||
;;
|
||||
ipv6)
|
||||
context_type=2
|
||||
;;
|
||||
ipv4v6|*)
|
||||
context_type=3
|
||||
;;
|
||||
esac
|
||||
if [ -n "${username}" ] && [ -n "${password}" ]; then
|
||||
at_command="at+qicsgp=1,${context_type},\"${apn}\",\"${username}\",\"${password}\",3"
|
||||
else
|
||||
at_command="at+qicsgp=1,${context_type},\"${apn}\",\"\",\"\",0"
|
||||
fi
|
||||
exec_at_command "${at_command}" "${ser_port}"
|
||||
fi
|
||||
}
|
||||
|
||||
proto_modemmanager_setup() {
|
||||
local interface="$1"
|
||||
|
||||
local modempath modemstatus bearercount bearerpath connectargs bearerstatus beareriface
|
||||
local bearermethod_ipv4 bearermethod_ipv6 auth cliauth
|
||||
local operatorname operatorid registration accesstech signalquality
|
||||
local allowedmode preferredmode
|
||||
|
||||
local device apn allowedauth username password pincode
|
||||
local iptype plmn metric signalrate allow_roaming
|
||||
|
||||
local address prefix gateway mtu dns1 dns2
|
||||
|
||||
json_get_vars device apn allowedauth username password
|
||||
json_get_vars pincode iptype plmn metric signalrate allow_roaming
|
||||
json_get_vars allowedmode preferredmode
|
||||
|
||||
# validate sysfs path given in config
|
||||
[ -n "${device}" ] || {
|
||||
echo "No device specified"
|
||||
proto_notify_error "${interface}" NO_DEVICE
|
||||
proto_set_available "${interface}" 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# validate that ModemManager is handling the modem at the sysfs path
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
modempath=$(modemmanager_get_field "${modemstatus}" "modem.dbus-path")
|
||||
[ -n "${modempath}" ] || {
|
||||
echo "Device not managed by ModemManager"
|
||||
proto_notify_error "${interface}" DEVICE_NOT_MANAGED
|
||||
proto_set_available "${interface}" 0
|
||||
return 1
|
||||
}
|
||||
echo "modem available at ${modempath}"
|
||||
|
||||
[ -z "${allowedmode}" ] || {
|
||||
case "$allowedmode" in
|
||||
"2g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "2g"
|
||||
;;
|
||||
"3g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "3g"
|
||||
;;
|
||||
"4g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "4g"
|
||||
;;
|
||||
"5g")
|
||||
modemmanager_set_allowed_mode "$device" \
|
||||
"$interface" "5g"
|
||||
;;
|
||||
*)
|
||||
modemmanager_set_preferred_mode "$device" \
|
||||
"$interface" "${allowedmode}" "${preferredmode}"
|
||||
;;
|
||||
esac
|
||||
# check error for allowed_mode and preferred_mode function call
|
||||
[ "$?" -ne "0" ] && return 1
|
||||
}
|
||||
|
||||
# always cleanup before attempting a new connection, just in case
|
||||
modemmanager_cleanup_connection "${modemstatus}"
|
||||
|
||||
# if allowedauth list given, build option string
|
||||
for auth in $allowedauth; do
|
||||
cliauth="${cliauth}${cliauth:+|}$auth"
|
||||
done
|
||||
|
||||
# setup connect args; APN mandatory (even if it may be empty)
|
||||
echo "starting connection with apn '${apn}'..."
|
||||
proto_notify_error "${interface}" MM_CONNECT_IN_PROGRESS
|
||||
|
||||
# setup allow-roaming parameter
|
||||
if [ -n "${allow_roaming}" ] && [ "${allow_roaming}" -eq 0 ];then
|
||||
allow_roaming="no"
|
||||
else
|
||||
# allowed unless a user set the opposite
|
||||
allow_roaming="yes"
|
||||
fi
|
||||
|
||||
# Append options to 'connectargs' variable
|
||||
append_param "apn=${apn}"
|
||||
append_param "allow-roaming=${allow_roaming}"
|
||||
append_param "${iptype:+ip-type=${iptype}}"
|
||||
append_param "${plmn:+operator-id=${plmn}}"
|
||||
append_param "${cliauth:+allowed-auth=${cliauth}}"
|
||||
append_param "${username:+user=${username}}"
|
||||
append_param "${password:+password=${password}}"
|
||||
append_param "${pincode:+pin=${pincode}}"
|
||||
|
||||
mmcli --modem="${device}" --timeout 120 --simple-connect="${connectargs}" || {
|
||||
proto_notify_error "${interface}" MM_CONNECT_FAILED
|
||||
proto_block_restart "${interface}"
|
||||
return 1
|
||||
}
|
||||
|
||||
# check if Signal refresh rate is set
|
||||
if [ -n "${signalrate}" ] && [ "${signalrate}" -eq "${signalrate}" ] 2>/dev/null; then
|
||||
echo "setting signal refresh rate to ${signalrate} seconds"
|
||||
mmcli --modem="${device}" --signal-setup="${signalrate}"
|
||||
else
|
||||
echo "signal refresh rate is not set"
|
||||
fi
|
||||
|
||||
# log additional useful information
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
operatorname=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-name")
|
||||
[ -n "${operatorname}" ] && echo "network operator name: ${operatorname}"
|
||||
operatorid=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-code")
|
||||
[ -n "${operatorid}" ] && echo "network operator MCCMNC: ${operatorid}"
|
||||
registration=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.registration-state")
|
||||
[ -n "${registration}" ] && echo "registration type: ${registration}"
|
||||
accesstech=$(modemmanager_get_multivalue_field "${modemstatus}" "modem.generic.access-technologies")
|
||||
[ -n "${accesstech}" ] && echo "access technology: ${accesstech}"
|
||||
signalquality=$(modemmanager_get_field "${modemstatus}" "modem.generic.signal-quality.value")
|
||||
[ -n "${signalquality}" ] && echo "signal quality: ${signalquality}%"
|
||||
|
||||
# we won't like it if there are more than one bearers, as that would mean the
|
||||
# user manually created them, and that's unsupported by this proto
|
||||
bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
|
||||
[ -n "${bearercount}" ] && [ "$bearercount" -eq 1 ] || {
|
||||
proto_notify_error "${interface}" INVALID_BEARER_LIST
|
||||
return 1
|
||||
}
|
||||
|
||||
# load connected bearer information
|
||||
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
|
||||
bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
|
||||
|
||||
# load network interface and method information
|
||||
beareriface=$(modemmanager_get_field "${bearerstatus}" "bearer.status.interface")
|
||||
bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
|
||||
bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
|
||||
|
||||
# setup IPv4
|
||||
[ -n "${bearermethod_ipv4}" ] && {
|
||||
echo "IPv4 connection setup required in interface ${interface}: ${bearermethod_ipv4}"
|
||||
case "${bearermethod_ipv4}" in
|
||||
"dhcp")
|
||||
modemmanager_connected_method_dhcp_ipv4 "${interface}" "${beareriface}" "${metric}"
|
||||
;;
|
||||
"static")
|
||||
address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.address")
|
||||
prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.prefix")
|
||||
gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.gateway")
|
||||
mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.mtu")
|
||||
dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[1\]")
|
||||
dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[2\]")
|
||||
modemmanager_connected_method_static_ipv4 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
|
||||
;;
|
||||
"ppp")
|
||||
modemmanager_connected_method_ppp_ipv4 "${interface}" "${beareriface}" "${username}" "${password}" "${allowedauth}"
|
||||
;;
|
||||
*)
|
||||
proto_notify_error "${interface}" UNKNOWN_METHOD
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# setup IPv6
|
||||
# note: if using ipv4v6, both IPv4 and IPv6 settings will have the same MTU and metric values reported
|
||||
[ -n "${bearermethod_ipv6}" ] && {
|
||||
echo "IPv6 connection setup required in interface ${interface}: ${bearermethod_ipv6}"
|
||||
case "${bearermethod_ipv6}" in
|
||||
"dhcp")
|
||||
modemmanager_connected_method_dhcp_ipv6 "${interface}" "${beareriface}" "${metric}"
|
||||
;;
|
||||
"static")
|
||||
address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.address")
|
||||
prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.prefix")
|
||||
gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.gateway")
|
||||
mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.mtu")
|
||||
dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[1\]")
|
||||
dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[2\]")
|
||||
modemmanager_connected_method_static_ipv6 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
|
||||
;;
|
||||
"ppp")
|
||||
proto_notify_error "${interface}" "unsupported method"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
proto_notify_error "${interface}" UNKNOWN_METHOD
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
proto_modemmanager_teardown() {
|
||||
local interface="$1"
|
||||
|
||||
local modemstatus bearerpath errorstring
|
||||
local bearermethod_ipv4 bearermethod_ipv6
|
||||
|
||||
local device lowpower iptype
|
||||
json_get_vars device lowpower iptype
|
||||
|
||||
echo "stopping network"
|
||||
|
||||
# load connected bearer information, just the first one should be ok
|
||||
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
|
||||
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
|
||||
[ -n "${bearerpath}" ] || {
|
||||
echo "couldn't load bearer path: disconnecting anyway"
|
||||
mmcli --modem="${device}" --simple-disconnect >/dev/null 2>&1
|
||||
return
|
||||
}
|
||||
|
||||
# load bearer connection methods
|
||||
bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
|
||||
bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
|
||||
[ -n "${bearermethod_ipv4}" ] &&
|
||||
echo "IPv4 connection teardown required in interface ${interface}: ${bearermethod_ipv4}"
|
||||
bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
|
||||
[ -n "${bearermethod_ipv6}" ] &&
|
||||
echo "IPv6 connection teardown required in interface ${interface}: ${bearermethod_ipv6}"
|
||||
|
||||
# disconnection handling only requires special treatment in IPv4/PPP
|
||||
[ "${bearermethod_ipv4}" = "ppp" ] && modemmanager_disconnected_method_ppp_ipv4 "${interface}"
|
||||
modemmanager_disconnected_method_common "${interface}"
|
||||
|
||||
# disconnect
|
||||
mmcli --modem="${device}" --simple-disconnect ||
|
||||
proto_notify_error "${interface}" DISCONNECT_FAILED
|
||||
|
||||
# disable
|
||||
mmcli --modem="${device}" --disable
|
||||
|
||||
# low power, only if requested
|
||||
[ "${lowpower:-0}" -lt 1 ] ||
|
||||
mmcli --modem="${device}" --set-power-state-low
|
||||
|
||||
proto_init_update "*" 0
|
||||
proto_send_update "$interface"
|
||||
}
|
||||
|
||||
[ -n "$INCLUDE_ONLY" ] || {
|
||||
add_protocol modemmanager
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
|
||||
trap_with_arg() {
|
||||
func="$1" ; shift
|
||||
for sig ; do
|
||||
# shellcheck disable=SC2064
|
||||
trap "$func $sig" "$sig"
|
||||
done
|
||||
}
|
||||
|
||||
func_trap() {
|
||||
logger "ModemManager-wrapper[$$]" "Sending signal ${1}..."
|
||||
kill "-${1}" "$CHILD" 2>/dev/null
|
||||
}
|
||||
|
||||
main() {
|
||||
. /usr/share/ModemManager/modemmanager.common
|
||||
|
||||
trap_with_arg func_trap INT TERM KILL
|
||||
|
||||
mkdir -p "${MODEMMANAGER_RUNDIR}"
|
||||
chmod 0755 "${MODEMMANAGER_RUNDIR}"
|
||||
mm_cleanup_interfaces
|
||||
|
||||
/usr/sbin/ModemManager "$@" 1>/dev/null 2>/dev/null &
|
||||
CHILD="$!"
|
||||
|
||||
mm_report_events_from_cache
|
||||
|
||||
wait "$CHILD"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
42
olg-ucentral-client-sdk/feeds/ucentral/poe/Makefile
Normal file
42
olg-ucentral-client-sdk/feeds/ucentral/poe/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=poe
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
ifeq ($(CONFIG_TARGET_ipq50xx_generic_DEVICE_sonicfi_rap630w_311g),y)
|
||||
TARGET_CFLAGS += -DPLATFORM_EWW631_B1=1
|
||||
endif
|
||||
|
||||
define Package/poe
|
||||
SECTION:=utils
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Turn on/off PoE ports with TSP23861 chipset
|
||||
DEPENDS:= +libubox +libubus +libuci +libi2c
|
||||
endef
|
||||
|
||||
define Package/poe/description
|
||||
Turn on/off PoE ports
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
|
||||
define Package/poe/install
|
||||
$(INSTALL_DIR) $(1)
|
||||
$(INSTALL_DIR) $(1)/etc/config $(1)/etc/init.d
|
||||
$(INSTALL_BIN) ./files/poe.init $(1)/etc/init.d/poe
|
||||
$(INSTALL_BIN) ./files/poe.config $(1)/etc/config/poe
|
||||
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/tps23861-poe-ctrl $(1)/usr/bin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,poe))
|
||||
33
olg-ucentral-client-sdk/feeds/ucentral/poe/files/poe.init
Normal file
33
olg-ucentral-client-sdk/feeds/ucentral/poe/files/poe.init
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=10
|
||||
|
||||
tps23861_poe_ctrl () {
|
||||
local section="$1"
|
||||
local num mode
|
||||
config_get num "$section" port_num
|
||||
config_get mode "$section" admin_mode
|
||||
|
||||
if [ "$mode" == "1" ]; then
|
||||
output=$(tps23861-poe-ctrl -p "${num}" -P on)
|
||||
echo "<6>${output}" > "/dev/kmsg"
|
||||
else
|
||||
output=$(tps23861-poe-ctrl -p "${num}" -P off)
|
||||
echo "<6>${output}" > "/dev/kmsg"
|
||||
fi
|
||||
}
|
||||
|
||||
start(){
|
||||
. /lib/functions.sh
|
||||
|
||||
board=$(board_name)
|
||||
case $board in
|
||||
sonicfi,rap630w-311g|\
|
||||
cybertan,eww631-b1)
|
||||
config_load poe
|
||||
config_foreach tps23861_poe_ctrl port
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
26
olg-ucentral-client-sdk/feeds/ucentral/poe/src/Makefile
Normal file
26
olg-ucentral-client-sdk/feeds/ucentral/poe/src/Makefile
Normal file
@@ -0,0 +1,26 @@
|
||||
CFLAGS += -Wall -g
|
||||
|
||||
INCLUDES =
|
||||
|
||||
LDFLAGS = -lubus -lubox -li2c
|
||||
|
||||
LIBS =
|
||||
|
||||
SRCS = tps23861-poe-ctrl.c \
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
MAIN = tps23861-poe-ctrl
|
||||
|
||||
|
||||
all: $(MAIN)
|
||||
|
||||
$(MAIN): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LDFLAGS) $(LIBS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
clean:
|
||||
$(RM) *.o *~ $(MAIN) $(TEST)
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* User-space daemon formonitoring and managing PoE ports with
|
||||
* TI TPS23861 chips. based on the Linux Kernel TPS23861
|
||||
* HWMON driver.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* Standard input/output definitions */
|
||||
#include <string.h> /* String function definitions */
|
||||
#include <unistd.h> /* UNIX standard function definitions */
|
||||
#include <fcntl.h> /* File control definitions */
|
||||
#include <errno.h> /* Error number definitions */
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h> /* uapi/linux/i2c-dev.h */
|
||||
|
||||
#include <libubox/ulog.h>
|
||||
|
||||
#define TPS23861_I2C_ADDR 0x20
|
||||
#define DETECT_CLASS_RESTART 0x18
|
||||
#define POWER_ENABLE 0x19
|
||||
#define POWER_ON_SHIFT 0
|
||||
#define POWER_OFF_SHIFT 4
|
||||
|
||||
typedef unsigned char u8;
|
||||
|
||||
#if defined(PLATFORM_EWW631_B1)
|
||||
#define TPS23861_NUM_PORTS 1
|
||||
#endif
|
||||
|
||||
#define CONVERT_PORT_NUM(x) (1 << ((u8)x-1))
|
||||
|
||||
unsigned int PORT_POWER_STATUS[TPS23861_NUM_PORTS];
|
||||
|
||||
int i2c_handler = -1;
|
||||
#define ULOG_DBG(fmt, ...) ulog(LOG_DEBUG, fmt, ## __VA_ARGS__)
|
||||
|
||||
int open_device(void)
|
||||
{
|
||||
int fd, fset;
|
||||
|
||||
fd = open("/dev/i2c-0", O_RDWR);
|
||||
fset = fcntl(fd, F_SETFL, 0);
|
||||
if (fset < 0)
|
||||
printf("fcntl failed!\n");
|
||||
|
||||
//if (isatty(STDIN_FILENO) == 0)
|
||||
// printf("standard input is not a terminal device\n");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int access_salve(int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if((ret = ioctl(fd, I2C_SLAVE, TPS23861_I2C_ADDR)) < 0)
|
||||
{
|
||||
printf("%s: Failed to access slave bus[%s]\n",__func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
// Write to an I2C slave device's register:
|
||||
int i2c_write(u8 slave_addr, u8 reg, u8 data)
|
||||
{
|
||||
u8 outbuf[2];
|
||||
|
||||
struct i2c_msg msgs[1];
|
||||
struct i2c_rdwr_ioctl_data msgset[1];
|
||||
|
||||
outbuf[0] = reg;
|
||||
outbuf[1] = data;
|
||||
|
||||
msgs[0].addr = slave_addr;
|
||||
msgs[0].flags = 0;
|
||||
msgs[0].len = 2;
|
||||
msgs[0].buf = outbuf;
|
||||
|
||||
msgset[0].msgs = msgs;
|
||||
msgset[0].nmsgs = 1;
|
||||
|
||||
if (ioctl(i2c_handler, I2C_RDWR, &msgset) < 0) {
|
||||
perror("ioctl(I2C_RDWR) in i2c_write");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void poe_set_PowerOnOff(u8 port, u8 on_off) {
|
||||
u8 value;
|
||||
u8 portBit;
|
||||
portBit = CONVERT_PORT_NUM(port+1);
|
||||
|
||||
if(on_off == 0) {
|
||||
value = (portBit << POWER_OFF_SHIFT);
|
||||
PORT_POWER_STATUS[port] = 0;
|
||||
} else {
|
||||
value = (portBit << POWER_ON_SHIFT);
|
||||
PORT_POWER_STATUS[port] = 1;
|
||||
}
|
||||
|
||||
ULOG_DBG("set Port%d Power Status [%d] portBit 0x[%x] value 0x[%x]\n", port+1, PORT_POWER_STATUS[port], portBit, value);
|
||||
|
||||
if(i2c_write(TPS23861_I2C_ADDR, POWER_ENABLE, value) < 0)
|
||||
{
|
||||
ULOG_ERR("Set port%d power on-off error (0x19)\n", port);
|
||||
}
|
||||
}
|
||||
|
||||
void RestartPortDetectClass(u8 port)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
value = (1 << port) | (1 << (port + 4));
|
||||
ULOG_DBG("RestartPortDetectClass value 0x%x\n", value);
|
||||
if(i2c_write(TPS23861_I2C_ADDR, DETECT_CLASS_RESTART, value) < 0) {
|
||||
ULOG_ERR("Set port%d detection and class on error\n",port);
|
||||
}
|
||||
}
|
||||
|
||||
int usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s -p <1-3> -P <on|off> [options]\n"
|
||||
"Required options:\n"
|
||||
" -p <1-3>: Select port number (Only port 1 is supported)\n"
|
||||
" -P <on|off>: Set PSE function state <on|off>\n"
|
||||
"Optional options:\n"
|
||||
" -d Enable debug mode\n"
|
||||
"\n", progname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setPSE(int port ,char *optarg)
|
||||
{
|
||||
int ret = 0;
|
||||
i2c_handler = open_device();
|
||||
if (i2c_handler < 0) {
|
||||
ULOG_ERR("open i2c-0 device error!\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
ret = access_salve(i2c_handler);
|
||||
if (ret < 0)
|
||||
{
|
||||
ULOG_ERR("The i2c-0 access error\n");
|
||||
goto EXIT;
|
||||
}
|
||||
|
||||
if(!strncmp("on", optarg, 2)) {
|
||||
printf("Enable port%d PSE function\n", port);
|
||||
RestartPortDetectClass(port-1);
|
||||
}
|
||||
else if (!strncmp("off", optarg, 3)) {
|
||||
printf("Disable port%d PSE function\n", port);
|
||||
poe_set_PowerOnOff(port-1, 0);
|
||||
}
|
||||
else {
|
||||
ULOG_ERR("[Set] Do not accept this optarg!!!\n");
|
||||
ret = 1;
|
||||
}
|
||||
EXIT:
|
||||
close(i2c_handler);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ch, ret = 0, port = 0;
|
||||
char *PSE = NULL;
|
||||
if (argc == 1) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
|
||||
ulog_open(ULOG_STDIO | ULOG_SYSLOG, LOG_DAEMON, "tps23861");
|
||||
ulog_threshold(LOG_INFO);
|
||||
|
||||
while ((ch = getopt(argc, argv, "dp:P:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
printf("tps23861-i2c-control ulog_threshold set to debug level\n");
|
||||
ulog_threshold(LOG_DEBUG);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
PSE = optarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = usage(argv[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (port < 1 || port > 3) {
|
||||
ret = usage(argv[0]);
|
||||
}
|
||||
else {
|
||||
if (PSE) {
|
||||
setPSE(port, PSE);
|
||||
}
|
||||
else {
|
||||
ret = usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
76
olg-ucentral-client-sdk/feeds/ucentral/qosify/Makefile
Normal file
76
olg-ucentral-client-sdk/feeds/ucentral/qosify/Makefile
Normal file
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (C) 2021 OpenWrt.org
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
PKG_NAME:=qosify
|
||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/qosify.git
|
||||
PKG_MIRROR_HASH:=648e648655097d7063b3a2119b054cf9ee4a120a194cdf5fb5df8e03c9207c83
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_DATE:=2023-07-20
|
||||
PKG_SOURCE_VERSION:=850cc271083d0ad4c3b2eefddb61f376390ddf62
|
||||
PKG_RELEASE:=$(AUTORELEASE)
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
PKG_BUILD_DEPENDS:=bpf-headers
|
||||
PKG_FLAGS:=nonshared
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include $(INCLUDE_DIR)/cmake.mk
|
||||
include $(INCLUDE_DIR)/bpf.mk
|
||||
include $(INCLUDE_DIR)/nls.mk
|
||||
|
||||
define Package/qosify
|
||||
SECTION:=utils
|
||||
CATEGORY:=Base system
|
||||
TITLE:=A simple QoS solution based eBPF + CAKE
|
||||
DEPENDS:=+libbpf +libubox +libubus +libnl-tiny +kmod-sched-cake +kmod-sched-bpf +kmod-ifb +tc $(BPF_DEPENDS)
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += \
|
||||
-Wno-error=deprecated-declarations \
|
||||
-I$(STAGING_DIR)/usr/include/libnl-tiny \
|
||||
-I$(STAGING_DIR)/usr/include
|
||||
|
||||
CMAKE_OPTIONS += \
|
||||
-DLIBNL_LIBS=-lnl-tiny
|
||||
|
||||
define Build/Compile
|
||||
$(call CompileBPF,$(PKG_BUILD_DIR)/qosify-bpf.c)
|
||||
$(Build/Compile/Default)
|
||||
endef
|
||||
|
||||
define Package/qosify/conffiles
|
||||
/etc/config/qosify
|
||||
/etc/qosify/00-defaults.conf
|
||||
endef
|
||||
|
||||
define Package/qosify/install
|
||||
$(INSTALL_DIR) \
|
||||
$(1)/lib/bpf \
|
||||
$(1)/usr/sbin \
|
||||
$(1)/etc/init.d \
|
||||
$(1)/etc/config \
|
||||
$(1)/etc/qosify \
|
||||
$(1)/etc/hotplug.d/net \
|
||||
$(1)/etc/hotplug.d/iface
|
||||
$(INSTALL_DATA) $(PKG_BUILD_DIR)/qosify-bpf.o $(1)/lib/bpf
|
||||
$(INSTALL_BIN) \
|
||||
$(PKG_INSTALL_DIR)/usr/bin/qosify \
|
||||
./files/qosify-status \
|
||||
$(1)/usr/sbin/
|
||||
$(INSTALL_BIN) ./files/qosify.init $(1)/etc/init.d/qosify
|
||||
$(INSTALL_DATA) ./files/qosify-defaults.conf $(1)/etc/qosify/00-defaults.conf
|
||||
$(INSTALL_DATA) ./files/qosify.conf $(1)/etc/config/qosify
|
||||
$(INSTALL_DATA) ./files/qosify.hotplug $(1)/etc/hotplug.d/net/10-qosify
|
||||
$(INSTALL_DATA) ./files/qosify.hotplug $(1)/etc/hotplug.d/iface/10-qosify
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,qosify))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user