merge raw and fix conflict
|
@ -2,7 +2,6 @@ version: 2
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
prepare:
|
||||||
machine: true
|
machine: true
|
||||||
timezone: Europe/Paris
|
|
||||||
|
|
||||||
working_directory: ~/
|
working_directory: ~/
|
||||||
|
|
||||||
|
@ -13,9 +12,8 @@ jobs:
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -yq build-essential git unzip ncurses-dev libz-dev libssl-dev python subversion gettext gawk wget curl rsync perl
|
sudo apt-get install -yq build-essential git unzip ncurses-dev libz-dev libssl-dev python subversion gettext gawk wget curl rsync perl
|
||||||
|
|
||||||
build_x86:
|
build_x86_64:
|
||||||
machine: true
|
machine: true
|
||||||
timezone: Europe/Paris
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- OMR_VERSION: $CIRCLE_TAG
|
- OMR_VERSION: $CIRCLE_TAG
|
||||||
|
@ -27,8 +25,65 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: cache
|
name: cache
|
||||||
command: |
|
command: |
|
||||||
echo "cache 5 $OMR_TARGET" > /tmp/cache-target
|
echo "cache 10 $OMR_TARGET" > /tmp/cache-target
|
||||||
echo "cache 9 $OMR_TARGET $OMR_VERSION" > /tmp/cache-version
|
echo "cache 14 $OMR_TARGET $OMR_VERSION" > /tmp/cache-version
|
||||||
|
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- cache-{{ checksum "/tmp/cache-version" }}
|
||||||
|
- cache-{{ checksum "/tmp/cache-target" }}
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Build toolchain
|
||||||
|
no_output_timeout: 30m
|
||||||
|
command: |
|
||||||
|
git clone https://github.com/ysurac/openmptcprouter || true
|
||||||
|
cd openmptcprouter
|
||||||
|
git pull || true
|
||||||
|
export OMR_PATH="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/${CIRCLE_BRANCH:-$CIRCLE_TAG}"
|
||||||
|
export OMR_FEED_URL="$CIRCLE_REPOSITORY_URL"
|
||||||
|
export OMR_FEED_SRC="${CIRCLE_BRANCH:-$CIRCLE_TAG}"
|
||||||
|
sh build.sh prepare {tools,toolchain}/install -j2
|
||||||
|
echo -e "$OMR_PRIVKEY" > ~/openmptcprouter/$OMR_TARGET/source/key-build
|
||||||
|
echo -e "$OMR_PUBKEY" > ~/openmptcprouter/$OMR_TARGET/source/key-build.pub
|
||||||
|
|
||||||
|
- save_cache:
|
||||||
|
key: cache-{{ checksum "/tmp/cache-target" }}
|
||||||
|
paths:
|
||||||
|
- openmptcprouter
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Build
|
||||||
|
no_output_timeout: 30m
|
||||||
|
command: make IGNORE_ERRORS=m -C ~/openmptcprouter/$OMR_TARGET/source package/{compile,install,index} target/install -j2
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Deploy
|
||||||
|
command: |
|
||||||
|
ssh -p ${OMR_DEPLOY_PORT:-22} deploy@$OMR_DEPLOY_HOST mkdir -p deploy/release
|
||||||
|
rsync -av --delete-after ~/openmptcprouter/$OMR_TARGET/source/bin/ -e "ssh -q -p ${OMR_DEPLOY_PORT:-22}" deploy@$OMR_DEPLOY_HOST:deploy/release/$OMR_TARGET
|
||||||
|
rm -rf ~/openmptcprouter/$OMR_TARGET/source/bin
|
||||||
|
|
||||||
|
- save_cache:
|
||||||
|
key: cache-{{ checksum "/tmp/cache-version" }}
|
||||||
|
paths:
|
||||||
|
- openmptcprouter
|
||||||
|
|
||||||
|
build_x86:
|
||||||
|
machine: true
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- OMR_VERSION: $CIRCLE_TAG
|
||||||
|
- OMR_TARGET: x86
|
||||||
|
|
||||||
|
working_directory: ~/
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: cache
|
||||||
|
command: |
|
||||||
|
echo "cache 10 $OMR_TARGET" > /tmp/cache-target
|
||||||
|
echo "cache 14 $OMR_TARGET $OMR_VERSION" > /tmp/cache-version
|
||||||
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
|
@ -73,7 +128,6 @@ jobs:
|
||||||
|
|
||||||
build_rpi3:
|
build_rpi3:
|
||||||
machine: true
|
machine: true
|
||||||
timezone: Europe/Paris
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- OMR_VERSION: $CIRCLE_TAG
|
- OMR_VERSION: $CIRCLE_TAG
|
||||||
|
@ -85,8 +139,65 @@ jobs:
|
||||||
- run:
|
- run:
|
||||||
name: cache
|
name: cache
|
||||||
command: |
|
command: |
|
||||||
echo "cache 7 $OMR_TARGET" > /tmp/cache-target
|
echo "cache 14 $OMR_TARGET" > /tmp/cache-target
|
||||||
echo "cache 11 $OMR_TARGET $OMR_VERSION" > /tmp/cache-version
|
echo "cache 18 $OMR_TARGET $OMR_VERSION" > /tmp/cache-version
|
||||||
|
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- cache-{{ checksum "/tmp/cache-version" }}
|
||||||
|
- cache-{{ checksum "/tmp/cache-target" }}
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Build toolchain
|
||||||
|
no_output_timeout: 30m
|
||||||
|
command: |
|
||||||
|
git clone https://github.com/ysurac/openmptcprouter || true
|
||||||
|
cd openmptcprouter
|
||||||
|
git pull || true
|
||||||
|
export OMR_PATH="$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/${CIRCLE_BRANCH:-$CIRCLE_TAG}"
|
||||||
|
export OMR_FEED_URL="$CIRCLE_REPOSITORY_URL"
|
||||||
|
export OMR_FEED_SRC="${CIRCLE_BRANCH:-$CIRCLE_TAG}"
|
||||||
|
sh build.sh prepare {tools,toolchain}/install -j2
|
||||||
|
echo -e "$OMR_PRIVKEY" > ~/openmptcprouter/$OMR_TARGET/source/key-build
|
||||||
|
echo -e "$OMR_PUBKEY" > ~/openmptcprouter/$OMR_TARGET/source/key-build.pub
|
||||||
|
|
||||||
|
- save_cache:
|
||||||
|
key: cache-{{ checksum "/tmp/cache-target" }}
|
||||||
|
paths:
|
||||||
|
- openmptcprouter
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Build
|
||||||
|
no_output_timeout: 30m
|
||||||
|
command: make IGNORE_ERRORS=m -C ~/openmptcprouter/$OMR_TARGET/source package/{compile,install,index} target/compile -j2 package/compile -j2 target/install -j2
|
||||||
|
|
||||||
|
- run:
|
||||||
|
name: Deploy
|
||||||
|
command: |
|
||||||
|
ssh -p ${OMR_DEPLOY_PORT:-22} deploy@$OMR_DEPLOY_HOST mkdir -p deploy/release
|
||||||
|
rsync -av --delete-after ~/openmptcprouter/$OMR_TARGET/source/bin/ -e "ssh -q -p ${OMR_DEPLOY_PORT:-22}" deploy@$OMR_DEPLOY_HOST:deploy/release/$OMR_TARGET
|
||||||
|
rm -rf ~/openmptcprouter/$OMR_TARGET/source/bin
|
||||||
|
|
||||||
|
- save_cache:
|
||||||
|
key: cache-{{ checksum "/tmp/cache-version" }}
|
||||||
|
paths:
|
||||||
|
- openmptcprouter
|
||||||
|
|
||||||
|
build_rpi2:
|
||||||
|
machine: true
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- OMR_VERSION: $CIRCLE_TAG
|
||||||
|
- OMR_TARGET: rpi2
|
||||||
|
|
||||||
|
working_directory: ~/
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: cache
|
||||||
|
command: |
|
||||||
|
echo "cache 14 $OMR_TARGET" > /tmp/cache-target
|
||||||
|
echo "cache 18 $OMR_TARGET $OMR_VERSION" > /tmp/cache-version
|
||||||
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
|
@ -134,9 +245,15 @@ workflows:
|
||||||
main:
|
main:
|
||||||
jobs:
|
jobs:
|
||||||
- prepare
|
- prepare
|
||||||
|
- build_x86_64:
|
||||||
|
requires:
|
||||||
|
- prepare
|
||||||
- build_x86:
|
- build_x86:
|
||||||
requires:
|
requires:
|
||||||
- prepare
|
- prepare
|
||||||
- build_rpi3:
|
- build_rpi3:
|
||||||
requires:
|
requires:
|
||||||
- prepare
|
- prepare
|
||||||
|
- build_rpi2:
|
||||||
|
requires:
|
||||||
|
- prepare
|
||||||
|
|
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Thanks for your contribution to OpenMPTCProuter!
|
||||||
|
|
||||||
|
You need to follow contributing rules.
|
||||||
|
|
||||||
|
Please remove this message before posting the pull request.
|
|
@ -1,5 +1,7 @@
|
||||||
#
|
#
|
||||||
# Copyright (C) 2010-2015 OpenWrt.org
|
# Copyright (C) 2010-2015 OpenWrt.org
|
||||||
|
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||||
|
# - Added gateway setting
|
||||||
#
|
#
|
||||||
# This is free software, licensed under the GNU General Public License v2.
|
# This is free software, licensed under the GNU General Public License v2.
|
||||||
# See /LICENSE for more information.
|
# See /LICENSE for more information.
|
||||||
|
@ -8,7 +10,7 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=6in4
|
PKG_NAME:=6in4
|
||||||
PKG_VERSION:=26
|
PKG_VERSION:=270
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_LICENSE:=GPL-2.0
|
PKG_LICENSE:=GPL-2.0
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ proto_6in4_setup() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
( proto_add_host_dependency "$cfg" "$peeraddr" "$tunlink" )
|
[ -n "$tunlink" ] && ( proto_add_host_dependency "$cfg" "$peeraddr" "$tunlink" )
|
||||||
|
|
||||||
[ -z "$ipaddr" ] && {
|
[ -z "$ipaddr" ] && {
|
||||||
local wanif="$tunlink"
|
local wanif="$tunlink"
|
||||||
|
|
82
CLA-entity.md
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
OpenMPTCProuter Entity Contributor License Agreement
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to OpenMPTCProuter ("We" or "Us").
|
||||||
|
|
||||||
|
This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please submit a pull request with a file under the `/contributors` directory indicating your acceptance of this agreement.
|
||||||
|
|
||||||
|
This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us.
|
||||||
|
|
||||||
|
## 1. Definitions
|
||||||
|
|
||||||
|
"You" means any Legal Entity on behalf of whom a Contribution has been received by Us. "Legal Entity" means an entity which is not a natural person. "Affiliates" means other Legal Entities that control, are controlled by, or under common control with that Legal Entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such Legal Entity, whether by contract or otherwise, (ii) ownership of fifty percent (50%) or more of the outstanding shares or securities which vote to elect the management or other persons who direct such Legal Entity or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"Contribution" means any work of authorship that is Submitted by You to Us in which You own or assert ownership of the Copyright. If You do not own the Copyright in the entire work of authorship, please follow the instructions in .
|
||||||
|
|
||||||
|
"Copyright" means all rights protecting works of authorship owned or controlled by You or Your Affiliates, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence including any extensions by You.
|
||||||
|
|
||||||
|
"Material" means the work of authorship which is made available by Us to third parties. When this Agreement covers more than one software project, the Material means the work of authorship to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material.
|
||||||
|
|
||||||
|
"Submit" means any form of electronic, verbal, or written communication sent to Us or our representatives, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us for the purpose of discussing and improving the Material, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||||
|
|
||||||
|
"Submission Date" means the date on which You Submit a Contribution to Us.
|
||||||
|
|
||||||
|
"Effective Date" means the date You execute this Agreement or the date You first Submit a Contribution to Us, whichever is earlier.
|
||||||
|
|
||||||
|
## 2. Grant of Rights
|
||||||
|
|
||||||
|
2.1 Copyright License
|
||||||
|
|
||||||
|
(a) You retain ownership of the Copyright in Your Contribution and have the same rights to use or license the Contribution which You would have had without entering into the Agreement.
|
||||||
|
|
||||||
|
(b) To the maximum extent permitted by the relevant law, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable license under the Copyright covering the Contribution, with the right to sublicense such rights through multiple tiers of sublicensees, to reproduce, modify, display, perform and distribute the Contribution as part of the Material; provided that this license is conditioned upon compliance with Section 2.3.
|
||||||
|
|
||||||
|
2.2 Patent License
|
||||||
|
|
||||||
|
For patent claims including, without limitation, method, process, and apparatus claims which You or Your Affiliates own, control or have the right to grant, now or in the future, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable patent license, with the right to sublicense these rights to multiple tiers of sublicensees, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with the Material (and portions of such combination). This license is granted only to the extent that the exercise of the licensed rights infringes such patent claims; and provided that this license is conditioned upon compliance with Section 2.3.
|
||||||
|
|
||||||
|
2.3 Outbound License
|
||||||
|
|
||||||
|
Based on the grant of rights in Sections 2.1 and 2.2, if We include Your Contribution in a Material, We may license the Contribution under any license, including copyleft, permissive, commercial, or proprietary licenses. As a condition on the exercise of this right, We agree to also license the Contribution under the terms of the license or licenses which We are using for the Material on the Submission Date.
|
||||||
|
|
||||||
|
2.4 Moral Rights. If moral rights apply to the Contribution, to the maximum extent permitted by law, You waive and agree not to assert such moral rights against Us or our successors in interest, or any of our licensees, either direct or indirect.
|
||||||
|
|
||||||
|
2.5 Our Rights. You acknowledge that We are not obligated to use Your Contribution as part of the Material and may decide to include any Contribution We consider appropriate.
|
||||||
|
|
||||||
|
2.6 Reservation of Rights. Any rights not expressly licensed under this section are expressly reserved by You.
|
||||||
|
|
||||||
|
## 3. Agreement
|
||||||
|
|
||||||
|
You confirm that:
|
||||||
|
|
||||||
|
(a) You have the legal authority to enter into this Agreement.
|
||||||
|
|
||||||
|
(b) You or Your Affiliates own the Copyright and patent claims covering the Contribution which are required to grant the rights under Section 2.
|
||||||
|
|
||||||
|
(c) The grant of rights under Section 2 does not violate any grant of rights which You or Your Affiliates have made to third parties.
|
||||||
|
|
||||||
|
(d) You have followed the instructions in , if You do not own the Copyright in the entire work of authorship Submitted.
|
||||||
|
|
||||||
|
## 4. Disclaimer
|
||||||
|
|
||||||
|
EXCEPT FOR THE EXPRESS WARRANTIES IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION TO THE MINIMUM PERIOD PERMITTED BY LAW.
|
||||||
|
|
||||||
|
## 5. Consequential Damage Waiver
|
||||||
|
|
||||||
|
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
|
||||||
|
|
||||||
|
## 6. Miscellaneous
|
||||||
|
|
||||||
|
6.1 This Agreement will be governed by and construed in accordance with the laws of excluding its conflicts of law provisions. Under certain circumstances, the governing law in this section might be superseded by the United Nations Convention on Contracts for the International Sale of Goods ("UN Convention") and the parties intend to avoid the application of the UN Convention to this Agreement and, thus, exclude the application of the UN Convention in its entirety to this Agreement.
|
||||||
|
|
||||||
|
6.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings.
|
||||||
|
|
||||||
|
6.3 If You or We assign the rights or obligations received through this Agreement to a third party, as a condition of the assignment, that third party must agree in writing to abide by all the rights and obligations in the Agreement.
|
||||||
|
|
||||||
|
6.4 The failure of either party to require performance by the other party of any provision of this Agreement in one situation shall not affect the right of a party to require such performance at any time in the future. A waiver of performance under a provision in one situation shall not be considered a waiver of the performance of the provision in the future or a waiver of the provision in its entirety.
|
||||||
|
|
||||||
|
6.5 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and which is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law.
|
||||||
|
|
||||||
|
This agreement is derived from the Project Harmony CLA generator:
|
||||||
|
http://www.harmonyagreements.org/
|
||||||
|
Harmony (HA-CLA-E-ANY) Version 1.0
|
83
CLA-individual.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
OpenMPTCProuter Individual Contributor License Agreement
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to OpenMPTCProuter ("We" or "Us").
|
||||||
|
|
||||||
|
This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please submit a pull request with a file under the `/contributors` directory indicating your acceptance of this agreement.
|
||||||
|
|
||||||
|
This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us.
|
||||||
|
|
||||||
|
## 1. Definitions
|
||||||
|
|
||||||
|
"You" means the individual who Submits a Contribution to Us.
|
||||||
|
|
||||||
|
"Contribution" means any work of authorship that is Submitted by You to Us in which You own or assert ownership of the Copyright. If You do not own the Copyright in the entire work of authorship, please follow the instructions in .
|
||||||
|
|
||||||
|
"Copyright" means all rights protecting works of authorship owned or controlled by You, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence including any extensions by You.
|
||||||
|
|
||||||
|
"Material" means the work of authorship which is made available by Us to third parties. When this Agreement covers more than one software project, the Material means the work of authorship to which the Contribution was Submitted. After You Submit the Contribution, it may be included in the Material.
|
||||||
|
|
||||||
|
"Submit" means any form of electronic, verbal, or written communication sent to Us or our representatives, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us for the purpose of discussing and improving the Material, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
|
||||||
|
|
||||||
|
"Submission Date" means the date on which You Submit a Contribution to Us.
|
||||||
|
|
||||||
|
"Effective Date" means the date You execute this Agreement or the date You first Submit a Contribution to Us, whichever is earlier.
|
||||||
|
|
||||||
|
## 2. Grant of Rights
|
||||||
|
|
||||||
|
2.1 Copyright License
|
||||||
|
|
||||||
|
(a) You retain ownership of the Copyright in Your Contribution and have the same rights to use or license the Contribution which You would have had without entering into the Agreement.
|
||||||
|
|
||||||
|
(b) To the maximum extent permitted by the relevant law, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable license under the Copyright covering the Contribution, with the right to sublicense such rights through multiple tiers of sublicensees, to reproduce, modify, display, perform and distribute the Contribution as part of the Material; provided that this license is conditioned upon compliance with Section 2.3.
|
||||||
|
|
||||||
|
2.2 Patent License
|
||||||
|
|
||||||
|
For patent claims including, without limitation, method, process, and apparatus claims which You own, control or have the right to grant, now or in the future, You grant to Us a perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable patent license, with the right to sublicense these rights to multiple tiers of sublicensees, to make, have made, use, sell, offer for sale, import and otherwise transfer the Contribution and the Contribution in combination with the Material (and portions of such combination). This license is granted only to the extent that the exercise of the licensed rights infringes such patent claims; and provided that this license is conditioned upon compliance with Section 2.3.
|
||||||
|
|
||||||
|
2.3 Outbound License
|
||||||
|
|
||||||
|
Based on the grant of rights in Sections 2.1 and 2.2, if We include Your Contribution in a Material, We may license the Contribution under any license, including copyleft, permissive, commercial, or proprietary licenses. As a condition on the exercise of this right, We agree to also license the Contribution under the terms of the license or licenses which We are using for the Material on the Submission Date.
|
||||||
|
|
||||||
|
2.4 Moral Rights. If moral rights apply to the Contribution, to the maximum extent permitted by law, You waive and agree not to assert such moral rights against Us or our successors in interest, or any of our licensees, either direct or indirect.
|
||||||
|
|
||||||
|
2.5 Our Rights. You acknowledge that We are not obligated to use Your Contribution as part of the Material and may decide to include any Contribution We consider appropriate.
|
||||||
|
|
||||||
|
2.6 Reservation of Rights. Any rights not expressly licensed under this section are expressly reserved by You.
|
||||||
|
|
||||||
|
3. Agreement
|
||||||
|
|
||||||
|
You confirm that:
|
||||||
|
|
||||||
|
(a) You have the legal authority to enter into this Agreement.
|
||||||
|
|
||||||
|
(b) You own the Copyright and patent claims covering the Contribution which are required to grant the rights under Section 2.
|
||||||
|
|
||||||
|
(c) The grant of rights under Section 2 does not violate any grant of rights which You have made to third parties, including Your employer. If You are an employee, You have had Your employer approve this Agreement or sign the Entity version of this document. If You are less than eighteen years old, please have Your parents or guardian sign the Agreement.
|
||||||
|
|
||||||
|
(d) You have followed the instructions in , if You do not own the Copyright in the entire work of authorship Submitted.
|
||||||
|
|
||||||
|
## 4. Disclaimer
|
||||||
|
|
||||||
|
EXCEPT FOR THE EXPRESS WARRANTIES IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US. TO THE EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED IN DURATION TO THE MINIMUM PERIOD PERMITTED BY LAW.
|
||||||
|
|
||||||
|
## 5. Consequential Damage Waiver
|
||||||
|
|
||||||
|
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR OTHERWISE) UPON WHICH THE CLAIM IS BASED.
|
||||||
|
|
||||||
|
## 6. Miscellaneous
|
||||||
|
|
||||||
|
6.1 This Agreement will be governed by and construed in accordance with the laws of excluding its conflicts of law provisions. Under certain circumstances, the governing law in this section might be superseded by the United Nations Convention on Contracts for the International Sale of Goods ("UN Convention") and the parties intend to avoid the application of the UN Convention to this Agreement and, thus, exclude the application of the UN Convention in its entirety to this Agreement.
|
||||||
|
|
||||||
|
6.2 This Agreement sets out the entire agreement between You and Us for Your Contributions to Us and overrides all other agreements or understandings.
|
||||||
|
|
||||||
|
6.3 If You or We assign the rights or obligations received through this Agreement to a third party, as a condition of the assignment, that third party must agree in writing to abide by all the rights and obligations in the Agreement.
|
||||||
|
|
||||||
|
6.4 The failure of either party to require performance by the other party of any provision of this Agreement in one situation shall not affect the right of a party to require such performance at any time in the future. A waiver of performance under a provision in one situation shall not be considered a waiver of the performance of the provision in the future or a waiver of the provision in its entirety.
|
||||||
|
|
||||||
|
6.5 If any provision of this Agreement is found void and unenforceable, such provision will be replaced to the extent possible with a provision that comes closest to the meaning of the original provision and which is enforceable. The terms and conditions set forth in this Agreement shall apply notwithstanding any failure of essential purpose of this Agreement or any limited remedy to the maximum extent possible under law.
|
||||||
|
|
||||||
|
This agreement is derived from the Project Harmony CLA generator:
|
||||||
|
http://www.harmonyagreements.org/
|
||||||
|
Harmony (HA-CLA-I-ANY) Version 1.0
|
||||||
|
|
74
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity and
|
||||||
|
orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at contact@openmptcprouter.com. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
|
[homepage]: http://contributor-covenant.org
|
||||||
|
[version]: http://contributor-covenant.org/version/1/4/
|
5
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Contribution
|
||||||
|
|
||||||
|
When submitting a pull request for the first time, you will need to agree to the contributor license agreement (for individuals or entities). To do this, in the pull request please create a file with a name like /contributors/{github_username}.md, and in the content of that file indicate your agreement. An example of what that file should contain can be seen in example agreement file.
|
||||||
|
|
||||||
|
(This method of CLA "signing" is borrowed from Medium's open source project.)
|
47
README.md
|
@ -60,9 +60,9 @@ Added support to gateway set by user for 6in4. Used for IPv6 over the glorytun I
|
||||||
## luci-omr-bypass
|
## luci-omr-bypass
|
||||||
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-bypass](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-bypass)
|
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-bypass](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-bypass)
|
||||||
|
|
||||||
*Description:* Luci interface to bypass domains with shadowsocks
|
*Description:* Luci interface to bypass domains, IPs and networks with shadowsocks
|
||||||
|
|
||||||
Domains added are bypassed when shadowsocks is used. This can be used when VPS IP is blacklisted from some sites.
|
Domains, IPs, networks and protocol (using DPI) added are bypassed when shadowsocks is used. This can be used when VPS IP is blacklisted from some sites.
|
||||||
|
|
||||||
|
|
||||||
## omr-tracker
|
## omr-tracker
|
||||||
|
@ -73,6 +73,18 @@ Domains added are bypassed when shadowsocks is used. This can be used when VPS I
|
||||||
This is used for OpenMPTCProuter failover.
|
This is used for OpenMPTCProuter failover.
|
||||||
|
|
||||||
|
|
||||||
|
## omr-6in4
|
||||||
|
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/omr-6in4](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/omr-6in4)
|
||||||
|
|
||||||
|
*Description:* Set tunnel configuration by tracking tunnel configuration.
|
||||||
|
|
||||||
|
|
||||||
|
## omr-update
|
||||||
|
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/omr-update](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/omr-update)
|
||||||
|
|
||||||
|
*Description:* Update old config with new settings.
|
||||||
|
|
||||||
|
|
||||||
## luci-omr-tracker
|
## luci-omr-tracker
|
||||||
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-tracker](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-tracker)
|
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-tracker](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/luci-app-omr-tracker)
|
||||||
|
|
||||||
|
@ -97,3 +109,34 @@ Interface to omr-tracker.
|
||||||
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/mptcp](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/mptcp)
|
*Source:* [https://github.com/Ysurac/openmptcprouter-feeds/tree/master/mptcp](https://github.com/Ysurac/openmptcprouter-feeds/tree/master/mptcp)
|
||||||
|
|
||||||
*Description:* This package set all MPTCP settings
|
*Description:* This package set all MPTCP settings
|
||||||
|
|
||||||
|
|
||||||
|
## ndisc6
|
||||||
|
*Source:* [http://www.remlab.net/files/ndisc6](http://www.remlab.net/files/ndisc6)
|
||||||
|
|
||||||
|
*Description:* An ICMPv6 neighbour discovery tool
|
||||||
|
|
||||||
|
This is used to check if there is no other IPv6 route announced on the network
|
||||||
|
|
||||||
|
|
||||||
|
## mlvpn
|
||||||
|
*Source:* [https://github.com/markfoodyburton/MLVPN/tree/new-reorder](https://github.com/markfoodyburton/MLVPN/tree/new-reorder)
|
||||||
|
|
||||||
|
*Description:* Multi-link VPN
|
||||||
|
|
||||||
|
This is an other way to aggregate same latency connections
|
||||||
|
|
||||||
|
|
||||||
|
## ndpi-filter
|
||||||
|
*Source:* [https://github.com/vel21ripn/nDPI](https://github.com/vel21ripn/nDPI)
|
||||||
|
|
||||||
|
*Description:* Open Source Deep Packet Inspection Software Toolkit
|
||||||
|
|
||||||
|
This is used to bypass a protocol
|
||||||
|
|
||||||
|
|
||||||
|
## tracebox
|
||||||
|
*Source:* [https://github.com/tracebox/tracebox](https://github.com/tracebox/tracebox)
|
||||||
|
|
||||||
|
*Description:* A middlebox detection tool
|
||||||
|
|
||||||
|
|
9
contributors/example.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
2018-05-19
|
||||||
|
|
||||||
|
I hereby agree to the terms of the "OpenMPTCProuter Individual Contributor License Agreement", with MD5 checksum bc827a07eb93611d793ddb7c75083c00.
|
||||||
|
|
||||||
|
I furthermore declare that I am authorized and able to make this agreement and sign this declaration.
|
||||||
|
|
||||||
|
Signed,
|
||||||
|
|
||||||
|
John Doe https://github.com/johndoe
|
|
@ -1,6 +1,6 @@
|
||||||
#
|
#
|
||||||
# Copyright (C) 2015 OVH
|
# Copyright (C) 2015 OVH
|
||||||
# Copyright (C) 2017 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||||
#
|
#
|
||||||
# This is free software, licensed under the GNU General Public License v2.
|
# This is free software, licensed under the GNU General Public License v2.
|
||||||
# See /LICENSE for more information.
|
# See /LICENSE for more information.
|
||||||
|
@ -8,13 +8,16 @@
|
||||||
|
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_SOURCE_PROTO:=git
|
||||||
|
PKG_SOURCE_URL:=https://github.com/angt/glorytun.git
|
||||||
|
PKG_SOURCE_VERSION:=c15343f8f6fb275fe9bed56ca251bb801ea5d67b
|
||||||
|
|
||||||
PKG_NAME:=glorytun-udp
|
PKG_NAME:=glorytun-udp
|
||||||
PKG_VERSION:=0.0.98-mud
|
PKG_VERSION:=0.0.99-mud
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=17
|
||||||
PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz
|
|
||||||
PKG_SOURCE_URL:=https://github.com/angt/glorytun/releases/download/v$(PKG_VERSION)
|
PKG_FIXUP:=autoreconf
|
||||||
PKG_BUILD_DIR:=$(BUILD_DIR)/glorytun-$(PKG_VERSION)
|
|
||||||
PKG_HASH:=34a02c83efbfa1742d639643eeeeeb35f43611c92e2fd93f2eedba065c2f9417
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
define Package/$(PKG_NAME)
|
define Package/$(PKG_NAME)
|
||||||
|
@ -42,3 +45,4 @@ define Package/$(PKG_NAME)/install
|
||||||
endef
|
endef
|
||||||
|
|
||||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ validate_section() {
|
||||||
}
|
}
|
||||||
|
|
||||||
start_instance() {
|
start_instance() {
|
||||||
local enable key host port dev listener proto bind bindport mtu mtuauto chacha20
|
local enable key host port listener proto bind bindport mtu mtuauto chacha20 dev
|
||||||
|
|
||||||
validate_section "${1}" || {
|
validate_section "${1}" || {
|
||||||
_err "validation failed"
|
_err "validation failed"
|
||||||
|
@ -45,24 +45,30 @@ start_instance() {
|
||||||
[ "${proto}" = "udp" ] || return 1
|
[ "${proto}" = "udp" ] || return 1
|
||||||
|
|
||||||
[ -n "${key}" ] || return 1
|
[ -n "${key}" ] || return 1
|
||||||
|
[ "${key}" != "secretkey" ] || return 1
|
||||||
[ -n "${port}" ] || return 1
|
[ -n "${port}" ] || return 1
|
||||||
[ -n "${dev}" ] || return 1
|
[ -n "${dev}" ] || return 1
|
||||||
|
|
||||||
echo "${key}" > /tmp/${PROG_NAME}-${1}.key
|
echo "${key}" > /tmp/${PROG_NAME}-${1}.key
|
||||||
key=""
|
key=""
|
||||||
|
|
||||||
|
if [ "$(uci -q get network.omrvpn)" != "" ]; then
|
||||||
|
uci -q set network.omrvpn.ifname=$dev
|
||||||
|
uci -q commit
|
||||||
|
fi
|
||||||
|
|
||||||
_log "starting ${PROG_NAME} ${1} instance $*"
|
_log "starting ${PROG_NAME} ${1} instance $*"
|
||||||
|
|
||||||
procd_open_instance
|
procd_open_instance
|
||||||
|
|
||||||
procd_set_param command ${PROG} \
|
procd_set_param command ${PROG} \
|
||||||
${bind:+bind "$bind"} \
|
${bind:+bind "$bind"} \
|
||||||
${bindport:+ "$port"} \
|
${bindport:+ "$bindport"} \
|
||||||
${host:+to "$host"} \
|
${host:+to "$host"} \
|
||||||
${port:+ "$port"} \
|
${port:+ "$port"} \
|
||||||
${dev:+dev "$dev"} \
|
${dev:+dev "$dev"} \
|
||||||
keyfile /tmp/${PROG_NAME}-${1}.key \
|
keyfile /tmp/${PROG_NAME}-${1}.key \
|
||||||
${mtu:+mtu "$mtu"}
|
persist
|
||||||
|
|
||||||
[ "${chacha20}" = "1" ] && procd_append_param command chacha
|
[ "${chacha20}" = "1" ] && procd_append_param command chacha
|
||||||
|
|
||||||
|
@ -73,13 +79,37 @@ start_instance() {
|
||||||
procd_set_param stderr 1
|
procd_set_param stderr 1
|
||||||
|
|
||||||
procd_close_instance
|
procd_close_instance
|
||||||
|
|
||||||
|
#config_load network
|
||||||
|
#config_foreach add_glorytun_path interface
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
add_glorytun_path() {
|
||||||
|
case "$1" in
|
||||||
|
loopback|lan|if0|*tun*|ifb*) return ;;
|
||||||
|
esac
|
||||||
|
local multipath
|
||||||
|
network_get_ipaddr ipaddr "$1"
|
||||||
|
config_get multipath "$1" multipath "off"
|
||||||
|
case "$multipath" in
|
||||||
|
master|on) glorytun-udp path "$ipaddr" dev $dev up ;;
|
||||||
|
backup) glorytun-udp path "$ipaddr" dev $dev backup ;;
|
||||||
|
*) glorytun-udp path "$ipaddr" dev $dev down ;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
start_service() {
|
start_service() {
|
||||||
|
local dev
|
||||||
config_load glorytun
|
config_load glorytun
|
||||||
config_foreach start_instance glorytun
|
config_foreach start_instance glorytun
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reload_service() {
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
service_triggers() {
|
service_triggers() {
|
||||||
procd_add_reload_trigger glorytun network
|
procd_add_reload_trigger glorytun network
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=glorytun
|
PKG_NAME:=glorytun
|
||||||
PKG_VERSION:=0.0.35
|
PKG_VERSION:=0.0.35
|
||||||
PKG_RELEASE:=5
|
PKG_RELEASE:=6
|
||||||
PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=glorytun-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/angt/glorytun/releases/download/v$(PKG_VERSION)
|
PKG_SOURCE_URL:=https://github.com/angt/glorytun/releases/download/v$(PKG_VERSION)
|
||||||
PKG_HASH:=49e4d8ea4ff2990300b37947b0bd0da3c8e0985bc6eddf29f4146306188fff64
|
PKG_HASH:=49e4d8ea4ff2990300b37947b0bd0da3c8e0985bc6eddf29f4146306188fff64
|
||||||
|
|
|
@ -3,7 +3,8 @@ config glorytun 'vpn'
|
||||||
option host '127.0.0.1'
|
option host '127.0.0.1'
|
||||||
option port '65001'
|
option port '65001'
|
||||||
option dev 'tun0'
|
option dev 'tun0'
|
||||||
option key 'secretkey'
|
option key ''
|
||||||
option mptcp '1'
|
option mptcp '1'
|
||||||
option proto 'tcp'
|
option proto 'tcp'
|
||||||
option chacha20 '1'
|
option chacha20 '1'
|
||||||
|
option mtuauto '1'
|
||||||
|
|
|
@ -42,12 +42,17 @@ start_instance() {
|
||||||
[ "${enable}" = "1" ] || return 1
|
[ "${enable}" = "1" ] || return 1
|
||||||
[ "${proto}" = "tcp" ] || return 1
|
[ "${proto}" = "tcp" ] || return 1
|
||||||
[ -n "${key}" ] || return 1
|
[ -n "${key}" ] || return 1
|
||||||
|
[ "${key}" != "secretkey" ] || return 1
|
||||||
[ -n "${port}" ] || return 1
|
[ -n "${port}" ] || return 1
|
||||||
[ -n "${dev}" ] || return 1
|
[ -n "${dev}" ] || return 1
|
||||||
|
|
||||||
echo "${key}" > /tmp/${PROG_NAME}-${1}.key
|
echo "${key}" > /tmp/${PROG_NAME}-${1}.key
|
||||||
key=""
|
key=""
|
||||||
|
|
||||||
|
if [ "$(uci -q get network.omrvpn)" != "" ]; then
|
||||||
|
uci -q set network.omrvpn.ifname=${dev}
|
||||||
|
uci -q commit network
|
||||||
|
fi
|
||||||
_log "starting ${PROG_NAME} ${1} instance $*"
|
_log "starting ${PROG_NAME} ${1} instance $*"
|
||||||
|
|
||||||
procd_open_instance
|
procd_open_instance
|
||||||
|
@ -64,7 +69,7 @@ start_instance() {
|
||||||
|
|
||||||
procd_append_param command \
|
procd_append_param command \
|
||||||
retry count -1 const 5000000 \
|
retry count -1 const 5000000 \
|
||||||
timeout 9000 \
|
timeout 40000 \
|
||||||
keepalive count 5 idle 60 interval 2
|
keepalive count 5 idle 60 interval 2
|
||||||
|
|
||||||
procd_set_param respawn 0 30 0
|
procd_set_param respawn 0 30 0
|
||||||
|
|
|
@ -7,10 +7,10 @@ require("luci.model.uci")
|
||||||
|
|
||||||
|
|
||||||
local basicParams = {
|
local basicParams = {
|
||||||
--
|
--
|
||||||
-- Widget, Name, Default(s), Description
|
-- Widget, Name, Default(s), Description
|
||||||
--
|
--
|
||||||
|
|
||||||
{ Flag,"enable",0, translate("Enable") },
|
{ Flag,"enable",0, translate("Enable") },
|
||||||
{ Value,"port",65001, translate("TCP port # for both local and remote") },
|
{ Value,"port",65001, translate("TCP port # for both local and remote") },
|
||||||
{ Value,"dev","tun0", translate("Interface name") },
|
{ Value,"dev","tun0", translate("Interface name") },
|
||||||
|
|
|
@ -17,9 +17,9 @@ set_default() {
|
||||||
local iface
|
local iface
|
||||||
config_get iface "$config" dev
|
config_get iface "$config" dev
|
||||||
[ "$iface" = "$DEVICE" ] && {
|
[ "$iface" = "$DEVICE" ] && {
|
||||||
config_get localip "$config" localip
|
config_get localip "$config" localip
|
||||||
config_get remoteip "$config" remoteip
|
config_get remoteip "$config" remoteip
|
||||||
[ "$remoteip" != "" ] && [ "$localip" != "" ] && ip addr add $localip peer $remoteip dev $DEVICE
|
[ "$remoteip" != "" ] && [ "$localip" != "" ] && ifconfig $DEVICE $localip pointopoint $remoteip up
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
56
luci-app-glorytun/root/etc/uci-defaults/1200-luci-glorytun
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete ucitrack.@glorytun[-1]
|
||||||
|
add ucitrack glorytun
|
||||||
|
set ucitrack.@glorytun[-1].init=glorytun
|
||||||
|
set ucitrack.@glorytun[-1].affects=glorytun-udp
|
||||||
|
delete ucitrack.@glorytun-udp[-1]
|
||||||
|
add ucitrack glorytun-udp
|
||||||
|
set ucitrack.@glorytun-udp[-1].init=glorytun-udp
|
||||||
|
commit ucitrack
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "$(uci -q get network.glorytun)" = "" ] && [ "$(uci -q get network.omrvpn)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
delete network.glorytun
|
||||||
|
set network.glorytun=interface
|
||||||
|
set network.glorytun.ifname=tun0
|
||||||
|
set network.glorytun.proto=dhcp
|
||||||
|
set network.glorytun.ip4table=vpn
|
||||||
|
set network.glorytun.multipath=off
|
||||||
|
set network.glorytun.leasetime=12h
|
||||||
|
set network.glorytun.mtu=1280
|
||||||
|
commit network
|
||||||
|
EOF
|
||||||
|
# set network.glorytun.proto=static
|
||||||
|
# set network.glorytun.ipaddr=10.0.0.2
|
||||||
|
# set network.glorytun.netmask=255.255.255.0
|
||||||
|
# set network.glorytun.gateway=10.0.0.1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(uci -q show firewall | grep glorytun)" = "" ] && [ "$(uci -q get network.omrvpn)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
set firewall.zone_vpn=zone
|
||||||
|
set firewall.zone_vpn.name=vpn
|
||||||
|
set firewall.zone_vpn.network=glorytun
|
||||||
|
set firewall.zone_vpn.masq=1
|
||||||
|
set firewall.zone_vpn.input=REJECT
|
||||||
|
set firewall.zone_vpn.forward=ACCEPT
|
||||||
|
set firewall.zone_vpn.output=ACCEPT
|
||||||
|
commit firewall
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
if [ "$(uci -q show firewall | grep Allow-All-LAN-to-VPN)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
add firewall rule
|
||||||
|
set firewall.@rule[-1].enabled='1'
|
||||||
|
set firewall.@rule[-1].target='ACCEPT'
|
||||||
|
set firewall.@rule[-1].name='Allow-All-LAN-to-VPN'
|
||||||
|
set firewall.@rule[-1].dest='vpn'
|
||||||
|
set firewall.@rule[-1].src='lan'
|
||||||
|
commit firewall
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
rm -f /tmp/luci-indexcache
|
||||||
|
exit 0
|
|
@ -1,50 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
uci -q batch <<-EOF >/dev/null
|
|
||||||
delete ucitrack.@glorytun[-1]
|
|
||||||
add ucitrack glorytun
|
|
||||||
set ucitrack.@glorytun[-1].init=glorytun
|
|
||||||
set ucitrack.@glorytun[-1].affects=glorytun-udp
|
|
||||||
delete ucitrack.@glorytun-udp[-1]
|
|
||||||
add ucitrack glorytun-udp
|
|
||||||
set ucitrack.@glorytun-udp[-1].init=glorytun-udp
|
|
||||||
commit ucitrack
|
|
||||||
EOF
|
|
||||||
|
|
||||||
uci -q batch <<-EOF >/dev/null
|
|
||||||
delete network.glorytun=interface
|
|
||||||
set network.glorytun=interface
|
|
||||||
set network.glorytun.ifname=tun0
|
|
||||||
set network.glorytun.proto=dhcp
|
|
||||||
set network.glorytun.ip4table=vpn
|
|
||||||
set network.glorytun.multipath=off
|
|
||||||
set network.glorytun.leasetime=12h
|
|
||||||
commit network
|
|
||||||
EOF
|
|
||||||
uci -q batch <<-EOF >/dev/null
|
|
||||||
add firewall zone
|
|
||||||
set firewall.@zone[-1].name=vpn
|
|
||||||
set firewall.@zone[-1].network=glorytun
|
|
||||||
set firewall.@zone[-1].masq=1
|
|
||||||
set firewall.@zone[-1].input=REJECT
|
|
||||||
set firewall.@zone[-1].forward=REJECT
|
|
||||||
set firewall.@zone[-1].output=ACCEPT
|
|
||||||
set firewall.allow_dhcp_request_vpn=rule
|
|
||||||
set firewall.allow_dhcp_request_vpn.name=Allow-DHCP-Request-VPN
|
|
||||||
set firewall.allow_dhcp_request_vpn.src=glorytun
|
|
||||||
set firewall.allow_dhcp_request_vpn.proto=udp
|
|
||||||
set firewall.allow_dhcp_request_vpn.dest_port=67
|
|
||||||
set firewall.allow_dhcp_request_vpn.target=ACCEPT
|
|
||||||
set firewall.allow_dhcp_request_vpn.family=ipv4
|
|
||||||
set firewall.redirect_vpn_to_lan=redirect
|
|
||||||
set firewall.redirect_vpn_to_lan.name=Redirect-VPN-to-LAN
|
|
||||||
set firewall.redirect_vpn_to_lan.src=vpn
|
|
||||||
set firewall.redirect_vpn_to_lan.dest=lan
|
|
||||||
set firewall.redirect_vpn_to_lan.proto=all
|
|
||||||
set firewall.redirect_vpn_to_lan.enabled=1
|
|
||||||
set firewall.redirect_vpn_to_lan.src_dip=192.168.100.1
|
|
||||||
commit firewall
|
|
||||||
EOF
|
|
||||||
|
|
||||||
rm -f /tmp/luci-indexcache
|
|
||||||
exit 0
|
|
15
luci-app-iperf/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI Support for iPerf3
|
||||||
|
LUCI_DEPENDS:=+iperf3-ssl
|
||||||
|
|
||||||
|
PKG_LICENSE:=GPLv3
|
||||||
|
|
||||||
|
include ../luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
50
luci-app-iperf/luasrc/controller/iperf.lua
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
local uci = luci.model.uci.cursor()
|
||||||
|
local ut = require "luci.util"
|
||||||
|
|
||||||
|
module("luci.controller.iperf", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
--entry({"admin", "openmptcprouter", "iperf"}, cbi("iperf"), _("iperf"))
|
||||||
|
entry({"admin", "services", "iperf"}, alias("admin", "services", "iperf", "test"), _("iPerf"),8)
|
||||||
|
entry({"admin", "services", "iperf", "test"}, template("iperf/test"), nil,1)
|
||||||
|
entry({"admin", "services", "iperf", "run_test"}, post("run_test")).leaf = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function run_test(server,proto,mode,updown,omit,parallel,transmit,bitrate)
|
||||||
|
luci.http.prepare_content("text/plain")
|
||||||
|
local iperf
|
||||||
|
local addr = uci:get("iperf",server,"host")
|
||||||
|
local ports = uci:get("iperf",server,"ports")
|
||||||
|
local ipv = "4"
|
||||||
|
if proto == "ipv6" then
|
||||||
|
local ipv = "6"
|
||||||
|
end
|
||||||
|
|
||||||
|
local t={}
|
||||||
|
for pt in ports:gmatch("([^,%s]+)") do
|
||||||
|
table.insert(t,pt)
|
||||||
|
end
|
||||||
|
local port = t[ math.random( #t ) ]
|
||||||
|
if mode == "tcp" then
|
||||||
|
if updown == "upload" then
|
||||||
|
iperf = io.popen("iperf3 -c %s -P %s -%s -p %s -O %s -t %s -J" % {ut.shellquote(addr),parallel,ipv,port,omit,transmit})
|
||||||
|
else
|
||||||
|
iperf = io.popen("iperf3 -c %s -P %s -%s -p %s -O %s -R -t %s -J" % {ut.shellquote(addr),parallel,ipv,port,omit,transmit})
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if updown == "upload" then
|
||||||
|
iperf = io.popen("iperf3 -c %s -P %s -%s -p %s -O %s -t %s -u -b %s -J" % {ut.shellquote(addr),parallel,ipv,port,omit,transmit,bitrate})
|
||||||
|
else
|
||||||
|
iperf = io.popen("iperf3 -c %s -P %s -%s -p %s -O %s -R -t %s -u -b %s -J" % {ut.shellquote(addr),parallel,ipv,port,omit,transmit,bitrate})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if iperf then
|
||||||
|
while true do
|
||||||
|
local ln = iperf:read("*l")
|
||||||
|
if not ln then break end
|
||||||
|
luci.http.write(ln)
|
||||||
|
luci.http.write("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
174
luci-app-iperf/luasrc/view/iperf/test.htm
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
<%+header%>
|
||||||
|
|
||||||
|
<%
|
||||||
|
local uci = require("luci.model.uci").cursor()
|
||||||
|
%>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-18.193.28471-ee087a1"></script>
|
||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
var stxhr = new XHR();
|
||||||
|
|
||||||
|
function update_speed(field, proto, mode,omit,parallel,transmit,bitrate)
|
||||||
|
{
|
||||||
|
update_upload(field,proto,mode,omit,parallel,transmit,bitrate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_upload(field, proto, mode,omit,parallel,transmit,bitrate)
|
||||||
|
{
|
||||||
|
var tool = field.name;
|
||||||
|
var addr = field.value;
|
||||||
|
|
||||||
|
var upload = document.getElementById('iperf-upload');
|
||||||
|
|
||||||
|
if (upload)
|
||||||
|
{
|
||||||
|
upload.innerHTML =
|
||||||
|
'<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> ' + '<%:Upload%> - ' +
|
||||||
|
'<%:Waiting for command to complete...%>'
|
||||||
|
;
|
||||||
|
|
||||||
|
stxhr.post('<%=url('admin/services/iperf')%>/run_test' + '/' + addr + '/' + proto + '/' + mode + '/upload' + '/' + omit + '/' + parallel + '/' + transmit + '/' + bitrate, { token: '<%=token%>' },
|
||||||
|
function(x)
|
||||||
|
{
|
||||||
|
if (x.responseText)
|
||||||
|
{
|
||||||
|
var response = JSON.parse(x.responseText);
|
||||||
|
if (response.error)
|
||||||
|
{
|
||||||
|
upload.innerHTML = String.format('<%:Upload%> - <pre>%s</pre>', response.error );
|
||||||
|
} else {
|
||||||
|
var sent_speed = (response.end.sum_sent.bits_per_second/1000000);
|
||||||
|
var received_speed = (response.end.sum_received.bits_per_second/1000000);
|
||||||
|
var server = response.start.connecting_to.host;
|
||||||
|
upload.innerHTML = String.format('<pre><%:Upload%> - Server: %s - Sender: %sMb/s - Receiver: %sMb/s</pre>', server, sent_speed.toFixed(2), received_speed.toFixed(2) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upload.innerHTML = '<%:Upload%> - <span class="error"><%:Bad address specified!%></span>';
|
||||||
|
}
|
||||||
|
update_download(field,proto,mode,omit,parallel,transmit,bitrate);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function update_download(field, proto, mode,omit,parallel,transmit,bitrate)
|
||||||
|
{
|
||||||
|
var tool = field.name;
|
||||||
|
var addr = field.value;
|
||||||
|
|
||||||
|
var download = document.getElementById('iperf-download');
|
||||||
|
|
||||||
|
if (download)
|
||||||
|
{
|
||||||
|
download.innerHTML =
|
||||||
|
'<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> ' + '<%:Download%> - ' +
|
||||||
|
'<%:Waiting for command to complete...%>'
|
||||||
|
;
|
||||||
|
|
||||||
|
stxhr.post('<%=url('admin/services/iperf')%>/run_test' + '/' + addr + '/' + proto + '/' + mode + '/download' + '/' + omit + '/' + parallel + '/' + transmit + '/' + bitrate, { token: '<%=token%>' },
|
||||||
|
function(x)
|
||||||
|
{
|
||||||
|
if (x.responseText)
|
||||||
|
{
|
||||||
|
var response = JSON.parse(x.responseText);
|
||||||
|
if (response.error)
|
||||||
|
{
|
||||||
|
download.innerHTML = String.format('<%:Download%> - <pre>%s</pre>', response.error );
|
||||||
|
} else {
|
||||||
|
var sent_speed = (response.end.sum_sent.bits_per_second/1000000);
|
||||||
|
var received_speed = (response.end.sum_received.bits_per_second/1000000);
|
||||||
|
var server = response.start.connecting_to.host;
|
||||||
|
download.innerHTML = String.format('<pre><%:Download%> - Server: %s - Sender: %sMb/s - Receiver: %sMb/s</pre>', server, sent_speed.toFixed(2), received_speed.toFixed(2) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
download.innerHTML = '<%:Download%> - <span class="error"><%:Bad address specified!%></span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//]]></script>
|
||||||
|
|
||||||
|
<% if stderr and #stderr > 0 then %><pre class="error"><%=pcdata(stderr)%></pre><% end %>
|
||||||
|
<form class="inline" method="post" action="<%=url('admin/services/iperf/run_test')%>">
|
||||||
|
<div class="cbi-map">
|
||||||
|
<h2 name="content"><%:iPerf speed tests%></h2>
|
||||||
|
<div class="cbi-map-descr"><%:This iPerf interface is in bêta. No support for this.%></div>
|
||||||
|
<fieldset class="cbi-section" id="networks">
|
||||||
|
<legend><%:Settings%></legend>
|
||||||
|
<div class="cbi-section-descr"></div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Mode of operation%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<select class="cbi-input-select" name="mode">
|
||||||
|
<option value="tcp">TCP</option>
|
||||||
|
<option value="udp">UDP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Internet protocol%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<select class="cbi-input-select" name="proto">
|
||||||
|
<option value="ipv4">IPv4</option>
|
||||||
|
<option value="ipv6">IPv6</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Target bitrate (Mbits/s)%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input name="bitrate" data-type="uinteger" type="text" class="cbi-input-text" value="0"/>
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:0 for unlimited. Need to be limited for UDP test%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Number of parallel client streams to run%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input name="parallel" data-type="uinteger" type="text" class="cbi-input-text" value="1"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Omit the first n seconds%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input name="omit" data-type="uinteger" type="text" class="cbi-input-text" value="3"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Time to transmit for (s)%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input name="transmit" data-type="uinteger" type="text" class="cbi-input-text" value="5"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Server%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<select class="cbi-input-select" name="addr">
|
||||||
|
<%
|
||||||
|
uci:foreach("iperf","server", function(s)
|
||||||
|
local server = s[".name"]
|
||||||
|
%>
|
||||||
|
<option value="<%=server%>"><%=string.gsub(server,"_","-")%></option>
|
||||||
|
<%
|
||||||
|
end)
|
||||||
|
%>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="button" value="<%:Test%>" class="cbi-button cbi-button-apply" onclick="update_speed(this.form.addr,this.form.proto.value,this.form.mode.value,this.form.omit.value,this.form.parallel.value,this.form.transmit.value,this.form.bitrate.value)" />
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="cbi-section">
|
||||||
|
<span id="iperf-upload"></span>
|
||||||
|
<span id="iperf-download"></span>
|
||||||
|
</div>
|
||||||
|
<%+footer%>
|
96
luci-app-iperf/root/etc/config/iperf
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
config server 'bouygues'
|
||||||
|
option host 'bouygues.iperf.fr'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '1'
|
||||||
|
option speed '10000'
|
||||||
|
option ports '5200,5201,5202,5203,5204,5205,5206,5207,5208,5209'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '0'
|
||||||
|
option location 'Europe'
|
||||||
|
|
||||||
|
config server 'online_ipv4'
|
||||||
|
option host 'ping.online.net'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '0'
|
||||||
|
option speed '10000'
|
||||||
|
option ports '5200,5201,5202,5203,5204,5205,5206,5207,5208,5209'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'Europe'
|
||||||
|
|
||||||
|
config server 'online_ipv6'
|
||||||
|
option host 'ping.online.net'
|
||||||
|
option ipv4 '0'
|
||||||
|
option ipv6 '1'
|
||||||
|
option speed '10000'
|
||||||
|
option ports '5200,5201,5202,5203,5204,5205,5206,5207,5208,5209'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'Europe'
|
||||||
|
|
||||||
|
config server 'serverius'
|
||||||
|
option host 'speedtest.serverius.net'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '1'
|
||||||
|
option speed '10000'
|
||||||
|
option ports '5002'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'Europe'
|
||||||
|
|
||||||
|
config server 'eenet'
|
||||||
|
option host 'iperf.eenet.ee'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '0'
|
||||||
|
option ports '5201'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'Europe'
|
||||||
|
|
||||||
|
config server 'volia'
|
||||||
|
option host 'iperf.volia.net'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '0'
|
||||||
|
option ports '5201'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'Europe'
|
||||||
|
|
||||||
|
config server 'it_north'
|
||||||
|
option host 'iperf.it-north.net'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '0'
|
||||||
|
option speed '1000'
|
||||||
|
option ports '5200,5201,5202,5203,5204,5205,5206,5207,5208,5209'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'Asia'
|
||||||
|
|
||||||
|
config server 'biznet'
|
||||||
|
option host 'iperf.biznetnetworkds.com'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '1'
|
||||||
|
option speed '1000'
|
||||||
|
option ports '5201,5202,5203'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '0'
|
||||||
|
option location 'Asia'
|
||||||
|
|
||||||
|
config server 'scottlinux'
|
||||||
|
option host 'iperf.scottlinux.com'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '1'
|
||||||
|
option speed '1000'
|
||||||
|
option ports '5201'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'America'
|
||||||
|
|
||||||
|
config server 'he'
|
||||||
|
option host 'iperf.he.net'
|
||||||
|
option ipv4 '1'
|
||||||
|
option ipv6 '1'
|
||||||
|
option ports '5201'
|
||||||
|
option tcp '1'
|
||||||
|
option udp '1'
|
||||||
|
option location 'America'
|
15
luci-app-mlvpn/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#
|
||||||
|
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI Support for MLVPN
|
||||||
|
LUCI_DEPENDS:=+mlvpn
|
||||||
|
|
||||||
|
PKG_LICENSE:=GPLv3
|
||||||
|
|
||||||
|
include ../luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
6
luci-app-mlvpn/luasrc/controller/mlvpn.lua
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module("luci.controller.mlvpn", package.seeall)
|
||||||
|
|
||||||
|
function index()
|
||||||
|
--entry({"admin", "openmptcprouter", "mlvpn"}, cbi("mlvpn"), _("MLVPN"))
|
||||||
|
entry({"admin", "services", "mlvpn"}, cbi("mlvpn"), _("MLVPN"))
|
||||||
|
end
|
77
luci-app-mlvpn/luasrc/model/cbi/mlvpn.lua
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
local net = require "luci.model.network".init()
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local ifaces = sys.net:devices()
|
||||||
|
local m, s, o
|
||||||
|
|
||||||
|
m = Map("mlvpn", translate("MLVPN"))
|
||||||
|
|
||||||
|
s = m:section(TypedSection, "mlvpn", translate("Settings"))
|
||||||
|
s.anonymous = true
|
||||||
|
s.addremove = false
|
||||||
|
|
||||||
|
o = s:option(Flag, "enable", translate("Enable"))
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "timeout", translate("Timeout (s)"))
|
||||||
|
o.placeholder = "30"
|
||||||
|
o.default = "30"
|
||||||
|
o.datatype = "uinteger"
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "reorder_buffer_size", translate("Reorder buffer size"))
|
||||||
|
o.placeholder = "64"
|
||||||
|
o.default = "64"
|
||||||
|
o.datatype = "uinteger"
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "loss_tolerance", translate("Loss tolerance"))
|
||||||
|
o.placeholder = "50"
|
||||||
|
o.default = "50"
|
||||||
|
o.datatype = "uinteger"
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "host", translate("Remote host"))
|
||||||
|
o.placeholder = "128.128.128.128"
|
||||||
|
o.default = "128.128.128.128"
|
||||||
|
o.datatype = "host"
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "firstport", translate("First remote port"),translate("Interface will increase port used beginning with this"))
|
||||||
|
o.default = "65201"
|
||||||
|
o.datatype = "port"
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "password", translate("Password"))
|
||||||
|
o.password = true
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
|
||||||
|
o = s:option(Value, "interface_name", translate("Interface name"))
|
||||||
|
o.placeholder = "mlvpn0"
|
||||||
|
o.default = "mlvpn0"
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
--o = s:option(Value, "mode", translate("Mode"))
|
||||||
|
--o:value("client")
|
||||||
|
--o:value("server")
|
||||||
|
--o.default = "client"
|
||||||
|
--o.rmempty = false
|
||||||
|
|
||||||
|
|
||||||
|
--s = m:section(TypedSection, "interface", translate("Interfaces"))
|
||||||
|
--s.template_addremove = "mlvpn/cbi-select-add"
|
||||||
|
--s.addremove = true
|
||||||
|
--s.add_select_options = { }
|
||||||
|
--s.add_select_options[''] = ''
|
||||||
|
--for _, iface in ipairs(ifaces) do
|
||||||
|
-- if not (iface == "lo" or iface:match("^ifb.*")) then
|
||||||
|
-- s.add_select_options[iface] = iface
|
||||||
|
-- end
|
||||||
|
--end
|
||||||
|
|
||||||
|
--o = s:option(Value, "port", translate("Remote/Bind port"))
|
||||||
|
--o.placeholder = "65201"
|
||||||
|
--o.default = "65201"
|
||||||
|
--o.datatype = "port"
|
||||||
|
|
||||||
|
return m
|
10
luci-app-mlvpn/luasrc/view/mlvpn/cbi-select-add.htm
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="cbi-section-create">
|
||||||
|
<% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
|
||||||
|
<select class="cbi-section-create-name" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.select">
|
||||||
|
<%- for k, v in luci.util.kspairs(self.add_select_options) do %>
|
||||||
|
<option value="<%=k%>"><%=luci.util.pcdata(v)%></option>
|
||||||
|
<% end -%>
|
||||||
|
</select>
|
||||||
|
<input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>" title="<%:Add%>" />
|
||||||
|
<% if self.invalid_cts then %><br /><%:Invalid%></div><% end %>
|
||||||
|
</div>
|
61
luci-app-mlvpn/po/fr/mlvpn.po
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"POT-Creation-Date: \n"
|
||||||
|
"PO-Revision-Date: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Poedit 2.0.6\n"
|
||||||
|
"Last-Translator: Ycarus <ycarus@zugaina.org>\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
"Language: fr\n"
|
||||||
|
|
||||||
|
msgid "Add"
|
||||||
|
msgstr "Ajouter"
|
||||||
|
|
||||||
|
msgid "Enable"
|
||||||
|
msgstr "Activé"
|
||||||
|
|
||||||
|
msgid "First remote port"
|
||||||
|
msgstr "Premier port distant"
|
||||||
|
|
||||||
|
msgid "Interface name"
|
||||||
|
msgstr "Nom de l'interface"
|
||||||
|
|
||||||
|
msgid "Interface will increase port used beginning with this"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Interfaces"
|
||||||
|
msgstr "Interfaces"
|
||||||
|
|
||||||
|
msgid "Invalid"
|
||||||
|
msgstr "Invalide"
|
||||||
|
|
||||||
|
msgid "Loss tolerance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "MLVPN"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Password"
|
||||||
|
msgstr "Mot de passe"
|
||||||
|
|
||||||
|
msgid "Remote host"
|
||||||
|
msgstr "Nom de l'hôte distant ou adresse IP"
|
||||||
|
|
||||||
|
msgid "Remote/Bind port"
|
||||||
|
msgstr "Port de connexion distant"
|
||||||
|
|
||||||
|
msgid "Reorder buffer size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr "Paramètres"
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr "Délais d'attente (s)"
|
50
luci-app-mlvpn/po/templates/mlvpn.pot
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||||
|
|
||||||
|
msgid "Add"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Enable"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "First remote port"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Interface name"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Interface will increase port used beginning with this"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Interfaces"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Invalid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Loss tolerance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "MLVPN"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Mode"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Remote host"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Remote/Bind port"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Reorder buffer size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Timeout (s)"
|
||||||
|
msgstr ""
|
|
@ -1,127 +1,127 @@
|
||||||
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
-- Copyright 2008 Steven Barth <steven@midlink.org>
|
||||||
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
|
-- Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
|
||||||
-- Copyright 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
-- Copyright 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||||
-- Licensed to the public under the Apache License 2.0.
|
-- Licensed to the public under the Apache License 2.0.
|
||||||
|
|
||||||
module("luci.controller.mptcp", package.seeall)
|
module("luci.controller.mptcp", package.seeall)
|
||||||
|
|
||||||
function index()
|
function index()
|
||||||
entry({"admin", "network", "mptcp"}, alias("admin", "network", "mptcp", "settings"), _("MPTCP"))
|
entry({"admin", "network", "mptcp"}, alias("admin", "network", "mptcp", "settings"), _("MPTCP"))
|
||||||
entry({"admin", "network", "mptcp", "settings"}, cbi("mptcp"), _("Settings"),2).leaf = true
|
entry({"admin", "network", "mptcp", "settings"}, cbi("mptcp"), _("Settings"),2).leaf = true
|
||||||
entry({"admin", "network", "mptcp", "bandwidth"}, template("mptcp/multipath"), _("Bandwidth"), 3).leaf = true
|
entry({"admin", "network", "mptcp", "bandwidth"}, template("mptcp/multipath"), _("Bandwidth"), 3).leaf = true
|
||||||
entry({"admin", "network", "mptcp", "multipath_bandwidth"}, call("multipath_bandwidth")).leaf = true
|
entry({"admin", "network", "mptcp", "multipath_bandwidth"}, call("multipath_bandwidth")).leaf = true
|
||||||
entry({"admin", "network", "mptcp", "interface_bandwidth"}, call("interface_bandwidth")).leaf = true
|
entry({"admin", "network", "mptcp", "interface_bandwidth"}, call("interface_bandwidth")).leaf = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function interface_bandwidth(iface)
|
function interface_bandwidth(iface)
|
||||||
luci.http.prepare_content("application/json")
|
luci.http.prepare_content("application/json")
|
||||||
local bwc = io.popen("luci-bwc -i %q 2>/dev/null" % iface)
|
local bwc = io.popen("luci-bwc -i %q 2>/dev/null" % iface)
|
||||||
if bwc then
|
if bwc then
|
||||||
luci.http.write("[")
|
luci.http.write("[")
|
||||||
while true do
|
while true do
|
||||||
local ln = bwc:read("*l")
|
local ln = bwc:read("*l")
|
||||||
if not ln then break end
|
if not ln then break end
|
||||||
luci.http.write(ln)
|
luci.http.write(ln)
|
||||||
end
|
end
|
||||||
luci.http.write("]")
|
luci.http.write("]")
|
||||||
bwc:close()
|
bwc:close()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function string.split(input, delimiter)
|
function string.split(input, delimiter)
|
||||||
input = tostring(input)
|
input = tostring(input)
|
||||||
delimiter = tostring(delimiter)
|
delimiter = tostring(delimiter)
|
||||||
if (delimiter=='') then return false end
|
if (delimiter=='') then return false end
|
||||||
local pos,arr = 0, {}
|
local pos,arr = 0, {}
|
||||||
-- for each divider found
|
-- for each divider found
|
||||||
for st,sp in function() return string.find(input, delimiter, pos, true) end do
|
for st,sp in function() return string.find(input, delimiter, pos, true) end do
|
||||||
table.insert(arr, string.sub(input, pos, st - 1))
|
table.insert(arr, string.sub(input, pos, st - 1))
|
||||||
pos = sp + 1
|
pos = sp + 1
|
||||||
end
|
end
|
||||||
table.insert(arr, string.sub(input, pos))
|
table.insert(arr, string.sub(input, pos))
|
||||||
return arr
|
return arr
|
||||||
end
|
end
|
||||||
|
|
||||||
function multipath_bandwidth()
|
function multipath_bandwidth()
|
||||||
local result = { };
|
local result = { };
|
||||||
local uci = luci.model.uci.cursor()
|
local uci = luci.model.uci.cursor()
|
||||||
local multipath="";
|
local multipath="";
|
||||||
local proto="";
|
local proto="";
|
||||||
local res={ };
|
local res={ };
|
||||||
local str="";
|
local str="";
|
||||||
local tmpstr="";
|
local tmpstr="";
|
||||||
|
|
||||||
for _, dev in luci.util.vspairs(luci.sys.net.devices()) do
|
for _, dev in luci.util.vspairs(luci.sys.net.devices()) do
|
||||||
if dev ~= "lo" then
|
if dev ~= "lo" then
|
||||||
if dev == "eth0.2" then
|
if dev == "eth0.2" then
|
||||||
multipath = uci:get("network", "wan", "multipath")
|
multipath = uci:get("network", "wan", "multipath")
|
||||||
elseif dev == "4g-wwan0" then
|
elseif dev == "4g-wwan0" then
|
||||||
multipath = uci:get("network", "wwan0", "multipath")
|
multipath = uci:get("network", "wwan0", "multipath")
|
||||||
else
|
else
|
||||||
multipath = uci:get("network", dev, "multipath")
|
multipath = uci:get("network", dev, "multipath")
|
||||||
end
|
end
|
||||||
if multipath == "on" or multipath == "master" or multipath == "backup" or multipath == "handover" then
|
if multipath == "on" or multipath == "master" or multipath == "backup" or multipath == "handover" then
|
||||||
result[dev] = "[" .. string.gsub((luci.sys.exec("luci-bwc -i %q 2>/dev/null" % dev)), '[\r\n]', '') .. "]"
|
result[dev] = "[" .. string.gsub((luci.sys.exec("luci-bwc -i %q 2>/dev/null" % dev)), '[\r\n]', '') .. "]"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
---先初始化求和数组
|
---先初始化求和数组
|
||||||
res["total"]={ };
|
res["total"]={ };
|
||||||
for i=1,60 do
|
for i=1,60 do
|
||||||
res["total"][i]={}
|
res["total"][i]={}
|
||||||
for j=1,5 do
|
for j=1,5 do
|
||||||
res["total"][i][j]=0
|
res["total"][i][j]=0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--遍历所有接口表求和
|
--遍历所有接口表求和
|
||||||
for key,value in pairs(result) do
|
for key,value in pairs(result) do
|
||||||
res[key]={}
|
res[key]={}
|
||||||
value=(string.gsub(value, "^%[%[", ""))
|
value=(string.gsub(value, "^%[%[", ""))
|
||||||
value=(string.gsub(value, "%]%]", ""))
|
value=(string.gsub(value, "%]%]", ""))
|
||||||
local temp1 = string.split(value, "],")
|
local temp1 = string.split(value, "],")
|
||||||
res[key][1]=temp1[1]
|
res[key][1]=temp1[1]
|
||||||
for i=2,60 do
|
for i=2,60 do
|
||||||
res[key][i]={}
|
res[key][i]={}
|
||||||
res[key][i]=(string.gsub(temp1[i], "%[", " "))
|
res[key][i]=(string.gsub(temp1[i], "%[", " "))
|
||||||
end
|
end
|
||||||
for i=1,60 do
|
for i=1,60 do
|
||||||
res[key][i] = string.split(res[key][i], ",")
|
res[key][i] = string.split(res[key][i], ",")
|
||||||
for j=1,5 do
|
for j=1,5 do
|
||||||
if "string"== type(res[key][i][j]) then
|
if "string"== type(res[key][i][j]) then
|
||||||
res[key][i][j]= tonumber(res[key][i][j])
|
res[key][i][j]= tonumber(res[key][i][j])
|
||||||
end
|
end
|
||||||
if "string"==type(res["total"][i][j]) then
|
if "string"==type(res["total"][i][j]) then
|
||||||
res["total"][i][j]= tonumber(res["total"][i][j])
|
res["total"][i][j]= tonumber(res["total"][i][j])
|
||||||
end
|
end
|
||||||
if j ==1 then
|
if j ==1 then
|
||||||
res["total"][i][j] = res[key][i][j]
|
res["total"][i][j] = res[key][i][j]
|
||||||
else
|
else
|
||||||
res["total"][i][j] = res["total"][i][j] + res[key][i][j]
|
res["total"][i][j] = res["total"][i][j] + res[key][i][j]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
---数值类型转成字符串
|
---数值类型转成字符串
|
||||||
for i=1,60 do
|
for i=1,60 do
|
||||||
for j=1,5 do
|
for j=1,5 do
|
||||||
if "number"== type(res["total"][i][j]) then
|
if "number"== type(res["total"][i][j]) then
|
||||||
res["total"][i][j]= tostring(res["total"][i][j])
|
res["total"][i][j]= tostring(res["total"][i][j])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
---数组转成字符串
|
---数组转成字符串
|
||||||
for i=1,60 do
|
for i=1,60 do
|
||||||
if i == 60 then
|
if i == 60 then
|
||||||
tmpstr = "["..table.concat(res["total"][i], ",")
|
tmpstr = "["..table.concat(res["total"][i], ",")
|
||||||
else
|
else
|
||||||
tmpstr = "["..table.concat(res["total"][i], ",").."],"
|
tmpstr = "["..table.concat(res["total"][i], ",").."],"
|
||||||
end
|
end
|
||||||
str = str..tmpstr
|
str = str..tmpstr
|
||||||
end
|
end
|
||||||
str = "["..str.."]]"
|
str = "["..str.."]]"
|
||||||
result["total"]=str
|
result["total"]=str
|
||||||
|
|
||||||
luci.http.prepare_content("application/json")
|
luci.http.prepare_content("application/json")
|
||||||
luci.http.write_json(result)
|
luci.http.write_json(result)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,20 +3,20 @@ local sys = require "luci.sys"
|
||||||
local ifaces = sys.net:devices()
|
local ifaces = sys.net:devices()
|
||||||
local m, s, o
|
local m, s, o
|
||||||
|
|
||||||
m = Map("network", translate("MPTCP"), translate("Networks MPTCP settings"))
|
m = Map("network", translate("MPTCP"), translate("Networks MPTCP settings. Visit <a href='http://multipath-tcp.org/pmwiki.php/Users/ConfigureMPTCP'>http://multipath-tcp.org/pmwiki.php/Users/ConfigureMPTCP</a> for help."))
|
||||||
|
|
||||||
s = m:section(TypedSection, "globals")
|
s = m:section(TypedSection, "globals")
|
||||||
local mtcpg = s:option(ListValue, "multipath", translate("Multipath TCP"))
|
local mtcpg = s:option(ListValue, "multipath", translate("Multipath TCP"))
|
||||||
mtcpg:value("enable", translate("enable"))
|
mtcpg:value("enable", translate("enable"))
|
||||||
mtcpg:value("disable", translate("disable"))
|
mtcpg:value("disable", translate("disable"))
|
||||||
local mtcpck = s:option(ListValue, "mptcp_checksum", translate("Multipath TCP checksum"))
|
local mtcpck = s:option(ListValue, "mptcp_checksum", translate("Multipath TCP checksum"))
|
||||||
mtcpck:value("enable", translate("enable"))
|
mtcpck:value(1, translate("enable"))
|
||||||
mtcpck:value("disable", translate("disable"))
|
mtcpck:value(0, translate("disable"))
|
||||||
local mtcppm = s:option(ListValue, "mptcp_path_manager", translate("Multipath TCP path-manager"))
|
local mtcppm = s:option(ListValue, "mptcp_path_manager", translate("Multipath TCP path-manager"))
|
||||||
mtcppm:value("default", translate("default"))
|
mtcppm:value("default", translate("default"))
|
||||||
mtcppm:value("fullmesh", translate("fullmesh"))
|
mtcppm:value("fullmesh", translate("fullmesh"))
|
||||||
mtcppm:value("ndiffports", translate("ndiffports"))
|
mtcppm:value("ndiffports", translate("ndiffports"))
|
||||||
mtcppm:value("blinder", translate("blinder"))
|
mtcppm:value("binder", translate("binder"))
|
||||||
local mtcpsch = s:option(ListValue, "mptcp_scheduler", translate("Multipath TCP scheduler"))
|
local mtcpsch = s:option(ListValue, "mptcp_scheduler", translate("Multipath TCP scheduler"))
|
||||||
mtcpsch:value("default", translate("default"))
|
mtcpsch:value("default", translate("default"))
|
||||||
mtcpsch:value("roundrobin", translate("round-robin"))
|
mtcpsch:value("roundrobin", translate("round-robin"))
|
||||||
|
@ -29,6 +29,16 @@ local availablecong = sys.exec("sysctl net.ipv4.tcp_available_congestion_control
|
||||||
for cong in string.gmatch(availablecong, "[^%s]+") do
|
for cong in string.gmatch(availablecong, "[^%s]+") do
|
||||||
congestion:value(cong, translate(cong))
|
congestion:value(cong, translate(cong))
|
||||||
end
|
end
|
||||||
|
local mtcpfm_subflows = s:option(Value, "mptcp_fullmesh_num_subflows", translate("Fullmesh subflows for each pair of IP addresses"))
|
||||||
|
mtcpfm_subflows.datatype = "uinteger"
|
||||||
|
mtcpfm_subflows.rmempty = false
|
||||||
|
local mtcpfm_createonerr = s:option(ListValue, "mptcp_fullmesh_create_on_err", translate("Re-create fullmesh subflows after a timeout"))
|
||||||
|
mtcpfm_createonerr:value(1, translate("enable"))
|
||||||
|
mtcpfm_createonerr:value(0, translate("disable"))
|
||||||
|
|
||||||
|
local mtcpnd_subflows = s:option(Value, "mptcp_ndiffports_num_subflows", translate("ndiffports subflows number"))
|
||||||
|
mtcpnd_subflows.datatype = "uinteger"
|
||||||
|
mtcpnd_subflows.rmempty = false
|
||||||
|
|
||||||
s = m:section(TypedSection, "interface", translate("Interfaces Settings"))
|
s = m:section(TypedSection, "interface", translate("Interfaces Settings"))
|
||||||
mptcp = s:option(ListValue, "multipath", translate("Multipath TCP"), translate("One interface must be set as master"))
|
mptcp = s:option(ListValue, "multipath", translate("Multipath TCP"), translate("One interface must be set as master"))
|
||||||
|
|
|
@ -9,6 +9,7 @@ include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI Support for nginx load balancing
|
LUCI_TITLE:=LuCI Support for nginx load balancing
|
||||||
LUCI_DEPENDS:=+nginx
|
LUCI_DEPENDS:=+nginx
|
||||||
|
KCONFIG:=CONFIG_NGINX_STREAM
|
||||||
|
|
||||||
PKG_LICENSE:=MIT
|
PKG_LICENSE:=MIT
|
||||||
|
|
||||||
|
|
|
@ -6,29 +6,20 @@ else
|
||||||
m = Map("nginx-ha", translate("Nginx High Availability"), "%s - %s" %{translate("Nginx High Availability"), translate("NOT RUNNING")})
|
m = Map("nginx-ha", translate("Nginx High Availability"), "%s - %s" %{translate("Nginx High Availability"), translate("NOT RUNNING")})
|
||||||
end
|
end
|
||||||
|
|
||||||
s = m:section(TypedSection, "general", translate("General Setting"))
|
s = m:section(TypedSection, "nginxha", translate("Settings"))
|
||||||
s.anonymous = true
|
s.addremove = true
|
||||||
|
|
||||||
o = s:option(Flag, "enable", translate("Enable"))
|
o = s:option(Flag, "enable", translate("Enable"))
|
||||||
o.rmempty = false
|
o.rmempty = false
|
||||||
|
|
||||||
o = s:option(Value, "startup_delay", translate("Startup Delay"))
|
|
||||||
o:value(0, translate("Not enabled"))
|
|
||||||
for _, v in ipairs({5, 10, 15, 25, 40}) do
|
|
||||||
o:value(v, translate("%u seconds") %{v})
|
|
||||||
end
|
|
||||||
o.datatype = "uinteger"
|
|
||||||
o.default = 0
|
|
||||||
o.rmempty = false
|
|
||||||
|
|
||||||
o = s:option(Value, "listen", translate("Listen Address:Port"))
|
o = s:option(Value, "listen", translate("Listen Address:Port"))
|
||||||
o.placeholder = "0.0.0.0:6666"
|
o.placeholder = "0.0.0.0:6666"
|
||||||
o.default = "0.0.0.0:6666"
|
o.default = "0.0.0.0:6666"
|
||||||
o.rmempty = false
|
o.rmempty = false
|
||||||
|
|
||||||
o = s:option(Value, "timeout", translate("Timeout Connect (ms)"))
|
o = s:option(Value, "timeout", translate("Timeout Connect (ms)"))
|
||||||
o.placeholder = "666"
|
o.placeholder = "1000"
|
||||||
o.default = "666"
|
o.default = "1000"
|
||||||
o.datatype = "range(33, 10000)"
|
o.datatype = "range(33, 10000)"
|
||||||
o.rmempty = false
|
o.rmempty = false
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
|
|
||||||
config general 'general'
|
config nginxha 'ShadowSocks'
|
||||||
option enable '0'
|
option enable '0'
|
||||||
option retries '1'
|
option retries '1'
|
||||||
option timeout '1000'
|
option timeout '1000'
|
||||||
option listen '0.0.0.0:65101'
|
option listen '0.0.0.0:65101'
|
||||||
option startup_delay '5'
|
|
||||||
list upstreams '1.2.3.4:65101 weight=1 max_fails=3 fail_timeout=30s'
|
list upstreams '1.2.3.4:65101 weight=1 max_fails=3 fail_timeout=30s'
|
||||||
|
|
||||||
|
config nginxha 'VPN'
|
||||||
|
option enable '0'
|
||||||
|
option retries '1'
|
||||||
|
option timeout '1000'
|
||||||
|
option listen '0.0.0.0:65001'
|
||||||
|
list upstreams '1.2.3.4:65001 weight=1 max_fails=3 fail_timeout=30s'
|
||||||
|
|
|
@ -20,11 +20,10 @@ _err() {
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_section() {
|
validate_section() {
|
||||||
uci_validate_section nginx-ha general "${1}" \
|
uci_validate_section nginx-ha nginxha "${1}" \
|
||||||
'enable:bool:0' \
|
'enable:bool:0' \
|
||||||
'retries:uinteger:3' \
|
'retries:uinteger:3' \
|
||||||
'timeout:uinteger:4000' \
|
'timeout:uinteger:4000' \
|
||||||
'startup_delay:uinteger:5' \
|
|
||||||
'listen:string' \
|
'listen:string' \
|
||||||
'upstreams:list(string)'
|
'upstreams:list(string)'
|
||||||
}
|
}
|
||||||
|
@ -33,12 +32,6 @@ genline_srv(){
|
||||||
echo " server $1;"
|
echo " server $1;"
|
||||||
}
|
}
|
||||||
|
|
||||||
boot() {
|
|
||||||
local delay=$(uci -q get $NAME.general.startup_delay)
|
|
||||||
(sleep ${delay:-0} && start >/dev/null 2>&1) &
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
start_instance() {
|
start_instance() {
|
||||||
local enable retries timeout startup_delay listen upstreams
|
local enable retries timeout startup_delay listen upstreams
|
||||||
|
|
||||||
|
@ -49,6 +42,28 @@ start_instance() {
|
||||||
|
|
||||||
[ "$enable" = 1 ] || return 1
|
[ "$enable" = 1 ] || return 1
|
||||||
|
|
||||||
|
stream="${stream}
|
||||||
|
upstream ${1} {
|
||||||
|
zone dynamic 64k;
|
||||||
|
$(config_list_foreach "${1}" "upstreams" genline_srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen ${listen:-0.0.0.0:6666} udp;
|
||||||
|
listen ${listen:-0.0.0.0:6666} so_keepalive=off;
|
||||||
|
proxy_pass ${1};
|
||||||
|
}
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
local stream=""
|
||||||
|
config_load nginx-ha
|
||||||
|
config_foreach start_instance nginxha
|
||||||
|
|
||||||
|
[ -z "$stream" ] && return
|
||||||
|
|
||||||
|
mkdir -p /var/log/nginx
|
||||||
mkdir -p /var/etc
|
mkdir -p /var/etc
|
||||||
cat <<-EOF > /var/etc/$PROG_NAME.cfg
|
cat <<-EOF > /var/etc/$PROG_NAME.cfg
|
||||||
user nobody nogroup;
|
user nobody nogroup;
|
||||||
|
@ -62,19 +77,7 @@ start_instance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
stream {
|
stream {
|
||||||
upstream allservers {
|
${stream}
|
||||||
zone dynamic 64k;
|
|
||||||
$(config_list_foreach "${1}" "upstreams" genline_srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen ${listen:-0.0.0.0:6666} udp;
|
|
||||||
proxy_pass allservers;
|
|
||||||
}
|
|
||||||
server {
|
|
||||||
listen ${listen:-0.0.0.0:6666};
|
|
||||||
proxy_pass allservers;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -83,11 +86,8 @@ start_instance() {
|
||||||
procd_set_param file /var/etc/$PROG_NAME.cfg
|
procd_set_param file /var/etc/$PROG_NAME.cfg
|
||||||
procd_set_param respawn
|
procd_set_param respawn
|
||||||
procd_close_instance
|
procd_close_instance
|
||||||
}
|
|
||||||
|
|
||||||
start_service() {
|
|
||||||
config_load nginx-ha
|
|
||||||
config_foreach start_instance general
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reload_service() {
|
reload_service() {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI Interface to bypass domains
|
LUCI_TITLE:=LuCI Interface to bypass domains
|
||||||
LUCI_DEPENDS:=+dnsmasq-full +shadowsocks-libev-ss-rules
|
LUCI_DEPENDS:=+dnsmasq-full +shadowsocks-libev-ss-rules +iptables-mod-ndpi +iptables-mod-extra +kmod-ipt-ndpi +iptables
|
||||||
|
|
||||||
PKG_LICENSE:=GPLv3
|
PKG_LICENSE:=GPLv3
|
||||||
|
|
||||||
|
|
|
@ -17,25 +17,35 @@ function bypass_add()
|
||||||
local ip_ipset = {}
|
local ip_ipset = {}
|
||||||
for _, k in pairs(hosts) do
|
for _, k in pairs(hosts) do
|
||||||
if k ~= "" then
|
if k ~= "" then
|
||||||
if dt.ipaddr(k) then
|
if dt.ipmask(k) then
|
||||||
table.insert(ip_ipset, k)
|
table.insert(ip_ipset, k)
|
||||||
else
|
else
|
||||||
domains_ipset = domains_ipset .. '/' .. k
|
domains_ipset = domains_ipset .. '/' .. k
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ucic:delete("omr-bypass","ips","ip")
|
ucic:set_list("omr-bypass","ips","ip",ip_ipset)
|
||||||
if table.getn(ip_ipset) > 0 then
|
|
||||||
for _, i in pairs(ip_ipset) do
|
local dpi = luci.http.formvalue("cbid.omr-bypass.dpi")
|
||||||
ucic:set_list("omr-bypass","ips","ip",ip_ipset)
|
if dpi ~= "" then
|
||||||
|
if (type(dpi) ~= "table") then
|
||||||
|
dpi = {dpi}
|
||||||
end
|
end
|
||||||
|
ucic:set_list("omr-bypass","dpi","proto",dpi)
|
||||||
|
else
|
||||||
|
ucic:delete("omr-bypass","dpi","proto")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local interface = luci.http.formvalue("cbid.omr-bypass.interface") or ""
|
||||||
|
ucic:set("omr-bypass","defaults","ifname",interface)
|
||||||
|
|
||||||
ucic:save("omr-bypass")
|
ucic:save("omr-bypass")
|
||||||
ucic:commit("omr-bypass")
|
ucic:commit("omr-bypass")
|
||||||
ucic:set_list("dhcp",ucic:get_first("dhcp","dnsmasq"),"ipset",domains_ipset .. "/ss_rules_dst_bypass")
|
ucic:set_list("dhcp",ucic:get_first("dhcp","dnsmasq"),"ipset",domains_ipset .. "/ss_rules_dst_bypass")
|
||||||
ucic:save("dhcp")
|
ucic:save("dhcp")
|
||||||
ucic:commit("dhcp")
|
ucic:commit("dhcp")
|
||||||
--luci.sys.exec("/etc/init.d/dnsmasq restart")
|
--luci.sys.exec("/etc/init.d/dnsmasq restart")
|
||||||
|
luci.sys.exec("/etc/init.d/omr-bypass restart")
|
||||||
luci.http.redirect(luci.dispatcher.build_url("admin/services/omr-bypass"))
|
luci.http.redirect(luci.dispatcher.build_url("admin/services/omr-bypass"))
|
||||||
return
|
return
|
||||||
end
|
end
|
|
@ -1,13 +1,18 @@
|
||||||
<%+header%>
|
<%+header%>
|
||||||
|
|
||||||
<script type="text/javascript" src="<%=resource%>/cbi.js" data-strings="{"path":{"resource":"\/luci-static\/resources","browser":"\/cgi-bin\/luci\/admin\/filebrowser"},"label":{"choose":"-- Choisir --","custom":"-- autre --"}}"></script>
|
<script type="text/javascript" src="<%=resource%>/cbi.js" data-strings="{"path":{"resource":"\/luci-static\/resources","browser":"\/cgi-bin\/luci\/admin\/filebrowser"}}"></script>
|
||||||
|
|
||||||
<%
|
<%
|
||||||
local uci = require("luci.model.uci").cursor()
|
local uci = require("luci.model.uci").cursor()
|
||||||
local hosts = uci:get_list("dhcp", uci:get_first("dhcp","dnsmasq"), "ipset")
|
local hosts = uci:get_list("dhcp", uci:get_first("dhcp","dnsmasq"), "ipset")
|
||||||
local ips = uci:get_list("omr-bypass", "ips", "ip")
|
local ips = uci:get_list("omr-bypass", "ips", "ip")
|
||||||
|
local dpi = uci:get_list("omr-bypass", "dpi", "proto")
|
||||||
|
local tmpfile = os.tmpname()
|
||||||
|
local dpi_available_proto = luci.util.execi("cat /proc/net/xt_ndpi/proto | awk '{print $3}' | sort -u | head -n -1")
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local ifaces = sys.net:devices()
|
||||||
|
local bypassif = uci:get("omr-bypass","defaults","ifname") or ""
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<% if stderr and #stderr > 0 then %><pre class="error"><%=pcdata(stderr)%></pre><% end %>
|
<% if stderr and #stderr > 0 then %><pre class="error"><%=pcdata(stderr)%></pre><% end %>
|
||||||
<form class="inline" method="post" action="<%=url('admin/services/omr-bypass/add')%>">
|
<form class="inline" method="post" action="<%=url('admin/services/omr-bypass/add')%>">
|
||||||
<div class="cbi-map">
|
<div class="cbi-map">
|
||||||
|
@ -25,7 +30,8 @@
|
||||||
for hst in string.gmatch(host,"([^/]*)/") do
|
for hst in string.gmatch(host,"([^/]*)/") do
|
||||||
if hst ~= "" then
|
if hst ~= "" then
|
||||||
%>
|
%>
|
||||||
<input class="cbi-input-text" value="<%=hst%>" data-update="change" type="text" id="cbid.omr-bypass.hosts.<%=j%>" name="cbid.omr-bypass.hosts" placeholder="google.com" /><br />
|
<input class="cbi-input-text" value="<%=hst%>" data-update="change" type="text" id="cbid.omr-bypass.hosts.<%=j%>" name="cbid.omr-bypass.hosts" placeholder="google.com" />
|
||||||
|
<br />
|
||||||
<%
|
<%
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -34,14 +40,87 @@
|
||||||
j = j+1
|
j = j+1
|
||||||
%>
|
%>
|
||||||
<input class="cbi-input-text" value="<%=ip%>" data-update="change" type="text" id="cbid.omr-bypass.hosts.<%=j%>" name="cbid.omr-bypass.hosts" placeholder="google.com" /><br />
|
<input class="cbi-input-text" value="<%=ip%>" data-update="change" type="text" id="cbid.omr-bypass.hosts.<%=j%>" name="cbid.omr-bypass.hosts" placeholder="google.com" /><br />
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:You need to use OpenMPTCProuter as DNS server when you want to bypass a domain%>
|
||||||
|
</div>
|
||||||
<%
|
<%
|
||||||
end
|
end
|
||||||
if j == 1 then
|
if j == 1 then
|
||||||
%>
|
%>
|
||||||
<input class="cbi-input-text" value="" data-update="change" type="text" id="cbid.omr-bypass.hosts.1" name="cbid.omr-bypass.hosts" placeholder="google.com" /><br />
|
<input class="cbi-input-text" value="" data-update="change" type="text" id="cbid.omr-bypass.hosts.1" name="cbid.omr-bypass.hosts" placeholder="google.com" />
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:You need to use OpenMPTCProuter as DNS server when you want to bypass a domain%>
|
||||||
|
</div>
|
||||||
<%
|
<%
|
||||||
end
|
end
|
||||||
%>
|
%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="cbi-section" id="dpi">
|
||||||
|
<div class="cbi-section-descr"><%:Set protocols you want to bypass.%></div>
|
||||||
|
<div class="cbi-value cbi-value-last" id="cbi-omr-tracker-dpi" data-depends="[]" data-index="<%=table.getn(dpi)%>">
|
||||||
|
<label class="cbi-value-title" for="cbid.omr-tracker.dpi"><%:Protocol%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<%
|
||||||
|
local allprt=""""
|
||||||
|
local protos = {}
|
||||||
|
for l in io.lines("/proc/net/xt_ndpi/proto") do
|
||||||
|
local a,b,c,d = l:match('(%w+) (%w+)')
|
||||||
|
if b ~= "2" and not string.match(b,"custom") then
|
||||||
|
table.insert(protos,b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(protos)
|
||||||
|
for _,b in ipairs(protos) do
|
||||||
|
allprt=allprt .. ","" .. b .. """
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
|
||||||
|
|
||||||
|
<div data-prefix="cbid.omr-bypass.dpi" data-browser-path="" data-dynlist="[[<%=allprt%>],[<%=allprt%>],null,false]">
|
||||||
|
<%
|
||||||
|
local k = 1
|
||||||
|
for _ , proto in pairs(dpi) do
|
||||||
|
k = k+1
|
||||||
|
%>
|
||||||
|
<input class="cbi-input-text" id="cbid.omr-bypass.dpi.<%=k%>" name="cbid.omr-bypass.dpi" data-update="change" value="<%=proto%>" /><br />
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
if k == 1 then
|
||||||
|
%>
|
||||||
|
<input class="cbi-input-text" id="cbid.omr-bypass.dpi.<%=k%>" name="cbid.omr-bypass.dpi" data-update="change" /><br />
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<div class="cbi-section-descr"><%:Set interface you want to use for bypassed traffic.%></div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title">Interface</label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<select class="cbi-input-select" name="cbid.omr-bypass.interface" size="1">
|
||||||
|
<option value="" <% if iface == bypassif then %>selected="selected"<% end %>><%=iface%></option>
|
||||||
|
<%
|
||||||
|
for _, iface in ipairs(ifaces) do
|
||||||
|
if not (iface == "lo" or iface:match("^ifb.*")) then
|
||||||
|
%>
|
||||||
|
<option value="<%=iface%>" <% if iface == bypassif then %>selected="selected"<% end %>><%=iface%></option>
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
</select>
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:If empty, multipath master interface is used if up else any other up interface.%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,11 +18,37 @@ msgstr "Contourne"
|
||||||
msgid "Domain, IP or network"
|
msgid "Domain, IP or network"
|
||||||
msgstr "Domaine, IP ou réseau"
|
msgstr "Domaine, IP ou réseau"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"If empty, multipath master interface is used if up else any other up "
|
||||||
|
"interface."
|
||||||
|
msgstr ""
|
||||||
|
"Si vide, l'interface définie en tant que maître multipath est utilisée si "
|
||||||
|
"elle fonctionne, sinon une autre interface sera utilisée."
|
||||||
|
|
||||||
msgid "OMR-Bypass"
|
msgid "OMR-Bypass"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Protocol"
|
||||||
|
msgstr "Protocole"
|
||||||
|
|
||||||
msgid "Set domains name, ips or networks you want to bypass."
|
msgid "Set domains name, ips or networks you want to bypass."
|
||||||
msgstr "Configurer les domaines, adresses IPs ou réseaux que vous voulez contourner."
|
msgstr ""
|
||||||
|
"Configurer les domaines, adresses IPs ou réseaux que vous voulez contourner."
|
||||||
|
|
||||||
|
msgid "Set interface you want to use for bypassed traffic."
|
||||||
|
msgstr ""
|
||||||
|
"Configurer l'interface que vous souhaitez utiliser pour le trafic à "
|
||||||
|
"contourner."
|
||||||
|
|
||||||
|
msgid "Set protocols you want to bypass."
|
||||||
|
msgstr "Configurer les protocoles que vous voulez contourner."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"You need to use OpenMPTCProuter as DNS server when you want to bypass a "
|
||||||
|
"domain"
|
||||||
|
msgstr ""
|
||||||
|
"Vous devez utiliser OpenMPTCProuter en tant que serveur DNS quand vous "
|
||||||
|
"souhaitez contourner un domaine"
|
||||||
|
|
||||||
#~ msgid "Domains"
|
#~ msgid "Domains"
|
||||||
#~ msgstr "Domaines"
|
#~ msgstr "Domaines"
|
||||||
|
|
|
@ -7,8 +7,27 @@ msgstr ""
|
||||||
msgid "Domain, IP or network"
|
msgid "Domain, IP or network"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"If empty, multipath master interface is used if up else any other up "
|
||||||
|
"interface."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "OMR-Bypass"
|
msgid "OMR-Bypass"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Protocol"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Set domains name, ips or networks you want to bypass."
|
msgid "Set domains name, ips or networks you want to bypass."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Set interface you want to use for bypassed traffic."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Set protocols you want to bypass."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"You need to use OpenMPTCProuter as DNS server when you want to bypass a "
|
||||||
|
"domain"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -1 +1,5 @@
|
||||||
config bypass 'ips'
|
config defaults 'defaults'
|
||||||
|
|
||||||
|
config bypass 'ips'
|
||||||
|
|
||||||
|
config bypass 'dpi'
|
||||||
|
|
|
@ -1,22 +1,62 @@
|
||||||
#!/bin/sh /etc/rc.common
|
#!/bin/sh /etc/rc.common
|
||||||
# shellcheck disable=SC2039
|
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||||
# vim: set noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 :
|
|
||||||
|
|
||||||
# shellcheck disable=SC2034
|
START=99
|
||||||
{
|
STOP=10
|
||||||
START=90
|
USE_PROCD=1
|
||||||
STOP=10
|
|
||||||
USE_PROCD=1
|
. /usr/lib/unbound/iptools.sh
|
||||||
}
|
|
||||||
|
|
||||||
_bypass_ip() {
|
_bypass_ip() {
|
||||||
local ip="$1"
|
local ip="$1"
|
||||||
ipset add ss_rules_dst_bypass $ip
|
valid_ip4=$( valid_subnet4 $ip)
|
||||||
|
valid_ip6=$( valid_subnet6 $ip)
|
||||||
|
if [ "$valid_ip4" = "ok" ]; then
|
||||||
|
ipset add ss_rules_dst_bypass $ip
|
||||||
|
elif [ "$valid_ip6" = "ok" ]; then
|
||||||
|
ipset add ss_rules6_dst_bypass $ip
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_bypass_proto() {
|
||||||
|
local proto="$1"
|
||||||
|
[ -z "$proto" ] && return
|
||||||
|
ndpi_rules="-A omr-bypass-dpi -m ndpi --$proto -j MARK --set-mark 0x539
|
||||||
|
$ndpi_rules"
|
||||||
}
|
}
|
||||||
|
|
||||||
start_service() {
|
start_service() {
|
||||||
|
ipset -q flush ss_rules_dst_bypass > /dev/null 2>&1
|
||||||
|
ipset -q --exist restore <<-EOF
|
||||||
|
create ss_rules_dst_bypass hash:net hashsize 64
|
||||||
|
EOF
|
||||||
|
|
||||||
config_load omr-bypass
|
config_load omr-bypass
|
||||||
config_list_foreach ips "ip" _bypass_ip
|
config_list_foreach ips "ip" _bypass_ip
|
||||||
|
|
||||||
|
ip rule add prio 1 fwmark 0x539 lookup 991337 > /dev/null 2>&1
|
||||||
|
|
||||||
|
if [ "$(iptables -w 40 -t mangle -L | grep 'match-set ss_rules_dst_bypass dst MARK set')" = "" ]; then
|
||||||
|
iptables-restore --wait=60 --noflush <<-EOF
|
||||||
|
*mangle
|
||||||
|
-A PREROUTING -m set --match-set ss_rules_dst_bypass dst -j MARK --set-mark 0x539
|
||||||
|
COMMIT
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
iptables-save --counters | grep -v omr-bypass-dpi | iptables-restore --counters
|
||||||
|
local ndpi_rules=""
|
||||||
|
config_list_foreach dpi "proto" _bypass_proto
|
||||||
|
ndpi_rules=$(echo $ndpi_rules | awk 'NF')
|
||||||
|
if [ "$ndpi_rules" != "" ]; then
|
||||||
|
iptables-restore --wait=60 --noflush <<-EOF
|
||||||
|
*mangle
|
||||||
|
:omr-bypass-dpi -
|
||||||
|
-A PREROUTING -m addrtype ! --dst-type LOCAL -j omr-bypass-dpi
|
||||||
|
$ndpi_rules
|
||||||
|
COMMIT
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
service_triggers() {
|
service_triggers() {
|
||||||
|
@ -24,6 +64,5 @@ service_triggers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
reload_service() {
|
reload_service() {
|
||||||
stop
|
|
||||||
start
|
start
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,22 @@ uci -q batch <<-EOF >/dev/null
|
||||||
commit ucitrack
|
commit ucitrack
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
if [ "$(uci -q get omr-bypass.dpi)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
set omr-bypass.dpi=bypass
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(uci -q get omr-bypass.default)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
set omr-bypass.defaults=defaults
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(uci -q get ucitrack.@shadowsocks-libev[-1].affects)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF >/dev/null
|
||||||
|
set ucitrack.@shadowsocks-libev[-1].affects=omr-bypass
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
rm -f /tmp/luci-indexcache
|
rm -f /tmp/luci-indexcache
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
@ -5,7 +5,7 @@ local m, s, o
|
||||||
|
|
||||||
m = Map("omr-tracker", translate("OMR-Tracker"))
|
m = Map("omr-tracker", translate("OMR-Tracker"))
|
||||||
|
|
||||||
s = m:section(TypedSection, "shadowsocks", translate("ShadowSocks tracker Settings"), translate("Detect if ShadowSocks is down and stop traffic redirection over it"))
|
s = m:section(TypedSection, "shadowsocks", translate("ShadowSocks tracker Settings"), translate("Detect if ShadowSocks is down and stop traffic redirection over it."))
|
||||||
s.anonymous = true
|
s.anonymous = true
|
||||||
s.addremove = false
|
s.addremove = false
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ o.default = "2"
|
||||||
o.datatype = "range(1, 100)"
|
o.datatype = "range(1, 100)"
|
||||||
o.rmempty = false
|
o.rmempty = false
|
||||||
|
|
||||||
o = s:option(DynamicList, "hosts", translate("Hosts"))
|
o = s:option(DynamicList, "hosts", translate("Hosts"), translate("IPs or domains must be available over http"))
|
||||||
o.placeholder = "bing.com"
|
o.placeholder = "bing.com"
|
||||||
o.default = { "bing.com", "google.com" }
|
o.default = { "bing.com", "google.com" }
|
||||||
o.rmempty = false
|
o.rmempty = false
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI Support for OpenMPTCProuter
|
LUCI_TITLE:=LuCI Support for OpenMPTCProuter
|
||||||
LUCI_DEPENDS:=+luci-lib-json
|
LUCI_DEPENDS:=+luci-lib-json +rdisc6 +curl +whois +bind-dig
|
||||||
PKG_LICENSE:=GPLv3
|
PKG_LICENSE:=GPLv3
|
||||||
|
|
||||||
include ../luci/luci.mk
|
include ../luci/luci.mk
|
||||||
|
|
After Width: | Height: | Size: 5.4 KiB |
|
@ -1,6 +1,7 @@
|
||||||
local tools = require "luci.tools.status"
|
local tools = require "luci.tools.status"
|
||||||
local sys = require "luci.sys"
|
local sys = require "luci.sys"
|
||||||
local json = require("luci.json")
|
local json = require("luci.json")
|
||||||
|
local fs = require("nixio.fs")
|
||||||
local ucic = luci.model.uci.cursor()
|
local ucic = luci.model.uci.cursor()
|
||||||
module("luci.controller.openmptcprouter", package.seeall)
|
module("luci.controller.openmptcprouter", package.seeall)
|
||||||
|
|
||||||
|
@ -22,30 +23,39 @@ function wizard_add()
|
||||||
local gostatus = true
|
local gostatus = true
|
||||||
if add_interface ~= "" then
|
if add_interface ~= "" then
|
||||||
local i = 1
|
local i = 1
|
||||||
|
local multipath_master = false
|
||||||
ucic:foreach("network", "interface", function(s)
|
ucic:foreach("network", "interface", function(s)
|
||||||
local sectionname = s[".name"]
|
local sectionname = s[".name"]
|
||||||
if sectionname:match("^wan(%d+)$") then
|
if sectionname:match("^wan(%d+)$") then
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
|
if ucic:get("network",sectionname,"multipath") == "master" then
|
||||||
|
multipath_master = true
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
local defif = ucic:get("network","wan1","ifname") or "eth0"
|
local defif = ucic:get("network","wan1_dev","ifname") or "eth0"
|
||||||
ucic:set("network","wan" .. i,"interface")
|
ucic:set("network","wan" .. i,"interface")
|
||||||
ucic:set("network","wan" .. i,"ifname",defif)
|
ucic:set("network","wan" .. i,"ifname",defif)
|
||||||
ucic:set("network","wan" .. i,"proto","static")
|
ucic:set("network","wan" .. i,"proto","static")
|
||||||
ucic:set("network","wan" .. i,"type","macvlan")
|
ucic:set("network","wan" .. i,"type","macvlan")
|
||||||
ucic:set("network","wan" .. i,"ip4table","wan")
|
ucic:set("network","wan" .. i,"ip4table","wan")
|
||||||
ucic:set("network","wan" .. i,"multipath","on")
|
if multipath_master then
|
||||||
|
ucic:set("network","wan" .. i,"multipath","on")
|
||||||
|
else
|
||||||
|
ucic:set("network","wan" .. i,"multipath","master")
|
||||||
|
end
|
||||||
ucic:set("network","wan" .. i,"defaultroute","0")
|
ucic:set("network","wan" .. i,"defaultroute","0")
|
||||||
ucic:save("network")
|
ucic:save("network")
|
||||||
ucic:commit("network")
|
ucic:commit("network")
|
||||||
-- Dirty way to add new interface to firewall...
|
-- Dirty way to add new interface to firewall...
|
||||||
luci.sys.call("uci -q add_list firewall.@zone[1].network=wan" .. i)
|
luci.sys.call("uci -q add_list firewall.@zone[1].network=wan" .. i)
|
||||||
|
luci.sys.call("uci -q commit firewall")
|
||||||
|
|
||||||
luci.sys.call("/etc/init.d/macvlan restart >/dev/null 2>/dev/null")
|
luci.sys.call("/etc/init.d/macvlan restart >/dev/null 2>/dev/null")
|
||||||
gostatus = false
|
gostatus = false
|
||||||
end
|
end
|
||||||
|
|
||||||
local delete_intf = luci.http.formvaluetable("delete")
|
local delete_intf = luci.http.formvaluetable("delete") or ""
|
||||||
if delete_intf ~= "" then
|
if delete_intf ~= "" then
|
||||||
for intf, _ in pairs(delete_intf) do
|
for intf, _ in pairs(delete_intf) do
|
||||||
ucic:delete("network",intf)
|
ucic:delete("network",intf)
|
||||||
|
@ -55,34 +65,6 @@ function wizard_add()
|
||||||
end
|
end
|
||||||
gostatus = false
|
gostatus = false
|
||||||
end
|
end
|
||||||
|
|
||||||
local server_ip = luci.http.formvalue("server_ip")
|
|
||||||
|
|
||||||
-- Set ShadowSocks settings
|
|
||||||
local shadowsocks_key = luci.http.formvalue("shadowsocks_key")
|
|
||||||
if shadowsocks_key ~= "" then
|
|
||||||
ucic:set("shadowsocks-libev","sss0","server",server_ip)
|
|
||||||
ucic:set("shadowsocks-libev","sss0","key",shadowsocks_key)
|
|
||||||
ucic:set("shadowsocks-libev","sss0","method","aes-256-cfb")
|
|
||||||
ucic:set("shadowsocks-libev","sss0","server_port","65101")
|
|
||||||
ucic:set("shadowsocks-libev","sss0","disabled",0)
|
|
||||||
ucic:save("shadowsocks-libev")
|
|
||||||
ucic:commit("shadowsocks-libev")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set Glorytun TCP settings
|
|
||||||
local glorytun_key = luci.http.formvalue("glorytun_key")
|
|
||||||
if glorytun_key ~= "" then
|
|
||||||
ucic:set("glorytun","vpn","host",server_ip)
|
|
||||||
ucic:set("glorytun","vpn","port","65001")
|
|
||||||
ucic:set("glorytun","vpn","key",glorytun_key)
|
|
||||||
ucic:set("glorytun","vpn","enable",1)
|
|
||||||
ucic:set("glorytun","vpn","mptcp",1)
|
|
||||||
ucic:set("glorytun","vpn","chacha20",1)
|
|
||||||
ucic:set("glorytun","vpn","proto","tcp")
|
|
||||||
ucic:save("glorytun")
|
|
||||||
ucic:commit("glorytun")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Set interfaces settings
|
-- Set interfaces settings
|
||||||
local interfaces = luci.http.formvaluetable("intf")
|
local interfaces = luci.http.formvaluetable("intf")
|
||||||
|
@ -93,12 +75,221 @@ function wizard_add()
|
||||||
ucic:set("network",intf,"ipaddr",ipaddr)
|
ucic:set("network",intf,"ipaddr",ipaddr)
|
||||||
ucic:set("network",intf,"netmask",netmask)
|
ucic:set("network",intf,"netmask",netmask)
|
||||||
ucic:set("network",intf,"gateway",gateway)
|
ucic:set("network",intf,"gateway",gateway)
|
||||||
|
|
||||||
|
local downloadspeed = luci.http.formvalue("cbid.sqm.%s.download" % intf) or ""
|
||||||
|
local uploadspeed = luci.http.formvalue("cbid.sqm.%s.upload" % intf) or ""
|
||||||
|
if downloadspeed ~= "" and uploadspeed ~= "" then
|
||||||
|
ucic:set("sqm",intf,"download",downloadspeed)
|
||||||
|
ucic:set("sqm",intf,"upload",uploadspeed)
|
||||||
|
ucic:set("sqm",intf,"enabled","1")
|
||||||
|
else
|
||||||
|
ucic:set("sqm",intf,"enabled","0")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
ucic:save("sqm")
|
||||||
|
ucic:commit("sqm")
|
||||||
ucic:save("network")
|
ucic:save("network")
|
||||||
ucic:commit("network")
|
ucic:commit("network")
|
||||||
|
|
||||||
|
-- Get VPN set by default
|
||||||
|
local default_vpn = luci.http.formvalue("default_vpn") or "glorytun_tcp"
|
||||||
|
local vpn_port = ""
|
||||||
|
local vpn_intf = ""
|
||||||
|
if default_vpn:match("^glorytun.*") then
|
||||||
|
vpn_port = 65001
|
||||||
|
vpn_intf = "tun0"
|
||||||
|
elseif default_vpn == "mlvpn" then
|
||||||
|
vpn_port = 65201
|
||||||
|
vpn_intf = "mlvpn0"
|
||||||
|
elseif default_vpn == "openvpn" then
|
||||||
|
vpn_port = 65301
|
||||||
|
vpn_intf = "tun0"
|
||||||
|
end
|
||||||
|
if vpn_intf ~= "" then
|
||||||
|
ucic:set("network","omrvpn","ifname",vpn_intf)
|
||||||
|
ucic:save("network")
|
||||||
|
ucic:commit("network")
|
||||||
|
end
|
||||||
|
ucic:set("openmptcprouter","settings","vpn",default_vpn)
|
||||||
|
ucic:save("openmptcprouter")
|
||||||
|
ucic:commit("openmptcprouter")
|
||||||
|
|
||||||
|
-- Get all servers ips
|
||||||
|
local server_ip = luci.http.formvalue("server_ip") or ""
|
||||||
|
-- We have an IP, so set it everywhere
|
||||||
|
if server_ip ~= "" then
|
||||||
|
local ss_ip
|
||||||
|
-- Check if we have more than one IP, in this case use Nginx HA
|
||||||
|
if (type(server_ip) == "table") then
|
||||||
|
local ss_servers = {}
|
||||||
|
local vpn_servers = {}
|
||||||
|
local k = 0
|
||||||
|
for _, ip in pairs(server_ip) do
|
||||||
|
if k == 0 then
|
||||||
|
ss_ip=ip
|
||||||
|
table.insert(ss_servers,ip .. ":65101 max_fails=3 fail_timeout=30s")
|
||||||
|
if vpn_port ~= "" then
|
||||||
|
table.insert(vpn_servers,ip .. ":" .. vpn_port .. " max_fails=3 fail_timeout=30s")
|
||||||
|
end
|
||||||
|
ucic:set("qos","serverin","srchost",ip)
|
||||||
|
ucic:set("qos","serverout","dsthost",ip)
|
||||||
|
ucic:save("qos")
|
||||||
|
ucic:commit("qos")
|
||||||
|
else
|
||||||
|
table.insert(ss_servers,ip .. ":65101 backup")
|
||||||
|
if vpn_port ~= "" then
|
||||||
|
table.insert(vpn_servers,ip .. ":" .. vpn_port .. " backup")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
k = k + 1
|
||||||
|
end
|
||||||
|
ucic:set("nginx-ha","ShadowSocks","enable","1")
|
||||||
|
ucic:set("nginx-ha","VPN","enable","1")
|
||||||
|
ucic:set("nginx-ha","ShadowSocks","upstreams",ss_servers)
|
||||||
|
ucic:set("nginx-ha","VPN","upstreams",vpn_servers)
|
||||||
|
ucic:save("nginx-ha")
|
||||||
|
ucic:commit("nginx-ha")
|
||||||
|
server_ip = "127.0.0.1"
|
||||||
|
ucic:set("shadowsocks-libev","sss0","server",ss_ip)
|
||||||
|
ucic:save("shadowsocks-libev")
|
||||||
|
ucic:commit("shadowsocks-libev")
|
||||||
|
else
|
||||||
|
ucic:set("nginx-ha","ShadowSocks","enable","0")
|
||||||
|
ucic:set("nginx-ha","VPN","enable","0")
|
||||||
|
ucic:set("qos","serverin","srchost",server_ip)
|
||||||
|
ucic:set("qos","serverout","dsthost",server_ip)
|
||||||
|
ucic:save("qos")
|
||||||
|
ucic:commit("qos")
|
||||||
|
ucic:set("shadowsocks-libev","sss0","server",server_ip)
|
||||||
|
ucic:save("shadowsocks-libev")
|
||||||
|
ucic:commit("shadowsocks-libev")
|
||||||
|
end
|
||||||
|
ucic:set("glorytun","vpn","host",server_ip)
|
||||||
|
ucic:save("glorytun")
|
||||||
|
ucic:commit("glorytun")
|
||||||
|
ucic:set("mlvpn","general","host",server_ip)
|
||||||
|
ucic:save("mlvpn")
|
||||||
|
ucic:commit("mlvpn")
|
||||||
|
luci.sys.call("uci -q del openvpn.omr.remote")
|
||||||
|
luci.sys.call("uci -q add_list openvpn.omr.remote=" .. server_ip)
|
||||||
|
ucic:save("openvpn")
|
||||||
|
ucic:commit("openvpn")
|
||||||
|
ucic:set("qos","serverin","srchost",server_ip)
|
||||||
|
ucic:set("qos","serverout","dsthost",server_ip)
|
||||||
|
ucic:save("qos")
|
||||||
|
ucic:commit("qos")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set ShadowSocks settings
|
||||||
|
local shadowsocks_key = luci.http.formvalue("shadowsocks_key")
|
||||||
|
if shadowsocks_key ~= "" then
|
||||||
|
ucic:set("shadowsocks-libev","sss0","key",shadowsocks_key)
|
||||||
|
ucic:set("shadowsocks-libev","sss0","method","chacha20")
|
||||||
|
ucic:set("shadowsocks-libev","sss0","server_port","65101")
|
||||||
|
ucic:set("shadowsocks-libev","sss0","disabled",0)
|
||||||
|
ucic:save("shadowsocks-libev")
|
||||||
|
ucic:commit("shadowsocks-libev")
|
||||||
|
else
|
||||||
|
ucic:set("shadowsocks-libev","sss0","key","")
|
||||||
|
ucic:set("shadowsocks-libev","sss0","disabled",1)
|
||||||
|
ucic:save("shadowsocks-libev")
|
||||||
|
ucic:commit("shadowsocks-libev")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set Glorytun settings
|
||||||
|
if default_vpn:match("^glorytun.*") then
|
||||||
|
ucic:set("glorytun","vpn","enable",1)
|
||||||
|
ucic:save("glorytun")
|
||||||
|
ucic:commit("glorytun")
|
||||||
|
else
|
||||||
|
ucic:set("glorytun","vpn","enable",0)
|
||||||
|
ucic:save("glorytun")
|
||||||
|
ucic:commit("glorytun")
|
||||||
|
end
|
||||||
|
|
||||||
|
local glorytun_key = luci.http.formvalue("glorytun_key")
|
||||||
|
if glorytun_key ~= "" then
|
||||||
|
ucic:set("glorytun","vpn","port","65001")
|
||||||
|
ucic:set("glorytun","vpn","key",glorytun_key)
|
||||||
|
ucic:set("glorytun","vpn","mptcp",1)
|
||||||
|
ucic:set("glorytun","vpn","chacha20",1)
|
||||||
|
if default_vpn == "glorytun_udp" then
|
||||||
|
ucic:set("glorytun","vpn","proto","udp")
|
||||||
|
else
|
||||||
|
ucic:set("glorytun","vpn","proto","tcp")
|
||||||
|
end
|
||||||
|
ucic:save("glorytun")
|
||||||
|
ucic:commit("glorytun")
|
||||||
|
else
|
||||||
|
ucic:set("glorytun","vpn","key","")
|
||||||
|
ucic:set("glorytun","vpn","enable",0)
|
||||||
|
ucic:set("glorytun","vpn","proto","tcp")
|
||||||
|
ucic:save("glorytun")
|
||||||
|
ucic:commit("glorytun")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set MLVPN settings
|
||||||
|
if default_vpn == "mlvpn" then
|
||||||
|
ucic:set("mlvpn","general","enable",1)
|
||||||
|
ucic:save("mlvpn")
|
||||||
|
ucic:commit("mlvpn")
|
||||||
|
else
|
||||||
|
ucic:set("mlvpn","general","enable",0)
|
||||||
|
ucic:save("mlvpn")
|
||||||
|
ucic:commit("mlvpn")
|
||||||
|
end
|
||||||
|
|
||||||
|
local mlvpn_password = luci.http.formvalue("mlvpn_password")
|
||||||
|
if mlvpn_password ~= "" then
|
||||||
|
ucic:set("mlvpn","general","password",mlvpn_password)
|
||||||
|
ucic:set("mlvpn","general","firstport","65201")
|
||||||
|
ucic:set("mlvpn","general","interface_name","mlvpn0")
|
||||||
|
ucic:save("mlvpn")
|
||||||
|
ucic:commit("mlvpn")
|
||||||
|
else
|
||||||
|
ucic:set("mlvpn","general","enable",0)
|
||||||
|
ucic:set("mlvpn","general","password","")
|
||||||
|
ucic:save("mlvpn")
|
||||||
|
ucic:commit("mlvpn")
|
||||||
|
end
|
||||||
|
|
||||||
|
local openvpn_key = luci.http.formvalue("openvpn_key")
|
||||||
|
if openvpn_key ~= "" then
|
||||||
|
local openvpn_key_path = "/etc/luci-uploads/openvpn.key"
|
||||||
|
local fp
|
||||||
|
luci.http.setfilehandler(
|
||||||
|
function(meta, chunk, eof)
|
||||||
|
if not fp and meta and meta.name == "openvpn_key" then
|
||||||
|
fp = io.open(openvpn_key_path, "w")
|
||||||
|
end
|
||||||
|
if fp and chunk then
|
||||||
|
fp:write(chunk)
|
||||||
|
end
|
||||||
|
if fp and eof then
|
||||||
|
fp:close()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
ucic:set("openvpn","omr","secret",openvpn_key_path)
|
||||||
|
ucic:commit("openvpn")
|
||||||
|
end
|
||||||
|
|
||||||
|
if default_vpn == "openvpn" then
|
||||||
|
ucic:set("openvpn","omr","enabled",1)
|
||||||
|
ucic:save("openvpn")
|
||||||
|
ucic:commit("openvpn")
|
||||||
|
else
|
||||||
|
ucic:set("openvpn","omr","enabled",0)
|
||||||
|
ucic:save("openvpn")
|
||||||
|
ucic:commit("openvpn")
|
||||||
|
end
|
||||||
|
|
||||||
luci.sys.call("(env -i /bin/ubus call network reload) >/dev/null 2>/dev/null")
|
luci.sys.call("(env -i /bin/ubus call network reload) >/dev/null 2>/dev/null")
|
||||||
|
luci.sys.call("/etc/init.d/shadowsocks restart >/dev/null 2>/dev/null")
|
||||||
luci.sys.call("/etc/init.d/glorytun restart >/dev/null 2>/dev/null")
|
luci.sys.call("/etc/init.d/glorytun restart >/dev/null 2>/dev/null")
|
||||||
if gostatus then
|
luci.sys.call("/etc/init.d/glorytun-udp restart >/dev/null 2>/dev/null")
|
||||||
|
luci.sys.call("/etc/init.d/mlvpn restart >/dev/null 2>/dev/null")
|
||||||
|
luci.sys.call("/etc/init.d/openvpn restart >/dev/null 2>/dev/null")
|
||||||
|
if gostatus == true then
|
||||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/openmptcprouter/status"))
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/openmptcprouter/status"))
|
||||||
else
|
else
|
||||||
luci.http.redirect(luci.dispatcher.build_url("admin/system/openmptcprouter/wizard"))
|
luci.http.redirect(luci.dispatcher.build_url("admin/system/openmptcprouter/wizard"))
|
||||||
|
@ -110,20 +301,39 @@ function settings_add()
|
||||||
-- Set tcp_keepalive_time
|
-- Set tcp_keepalive_time
|
||||||
local tcp_keepalive_time = luci.http.formvalue("tcp_keepalive_time")
|
local tcp_keepalive_time = luci.http.formvalue("tcp_keepalive_time")
|
||||||
luci.sys.exec("sysctl -w net.ipv4.tcp_keepalive_time=%s" % tcp_keepalive_time)
|
luci.sys.exec("sysctl -w net.ipv4.tcp_keepalive_time=%s" % tcp_keepalive_time)
|
||||||
luci.sys.exec("sed -i 's:^net.ipv4.tcp_keepalive_time = [0-9]*:net.ipv4.tcp_keepalive_time=%s:' /etc/sysctl.d/zzz_openmptcprouter.conf" % tcp_keepalive_time)
|
luci.sys.exec("sed -i 's:^net.ipv4.tcp_keepalive_time=[0-9]*:net.ipv4.tcp_keepalive_time=%s:' /etc/sysctl.d/zzz_openmptcprouter.conf" % tcp_keepalive_time)
|
||||||
|
|
||||||
|
-- Set tcp_fin_timeout
|
||||||
|
local tcp_fin_timeout = luci.http.formvalue("tcp_fin_timeout")
|
||||||
|
luci.sys.exec("sysctl -w net.ipv4.tcp_fin_timeoute=%s" % tcp_fin_timeout)
|
||||||
|
luci.sys.exec("sed -i 's:^net.ipv4.tcp_fin_timeout=[0-9]*:net.ipv4.tcp_fin_timeout=%s:' /etc/sysctl.d/zzz_openmptcprouter.conf" % tcp_fin_timeout)
|
||||||
|
|
||||||
-- Disable IPv6
|
-- Disable IPv6
|
||||||
local disable_ipv6 = luci.http.formvalue("disable_ipv6") or 0
|
local disable_ipv6 = luci.http.formvalue("disable_ipv6") or 0
|
||||||
luci.sys.exec("sysctl -w net.ipv6.conf.all.disable_ipv6=%s" % disable_ipv6)
|
luci.sys.exec("sysctl -w net.ipv6.conf.all.disable_ipv6=%s" % disable_ipv6)
|
||||||
luci.sys.exec("sed -i 's:^net.ipv6.conf.all.disable_ipv6 = [0-9]*:net.ipv6.conf.all.disable_ipv6=%s:' /etc/sysctl.d/zzz_openmptcprouter.conf" % disable_ipv6)
|
luci.sys.exec("sed -i 's:^net.ipv6.conf.all.disable_ipv6=[0-9]*:net.ipv6.conf.all.disable_ipv6=%s:' /etc/sysctl.d/zzz_openmptcprouter.conf" % disable_ipv6)
|
||||||
ucic:set("firewall",ucic:get_first("firewall","defaults"),"disable_ipv6",disable_ipv6)
|
ucic:set("firewall",ucic:get_first("firewall","defaults"),"disable_ipv6",disable_ipv6)
|
||||||
ucic:save("firewall")
|
ucic:save("firewall")
|
||||||
ucic:commit("firewall")
|
ucic:commit("firewall")
|
||||||
|
ucic:set("dhcp","lan","ra_default",disable_ipv6)
|
||||||
if disable_ipv6 == 1 then
|
if disable_ipv6 == 1 then
|
||||||
|
luci.sys.call("uci -q del dhcp.lan.dhcpv6")
|
||||||
|
luci.sys.call("uci -q del dhcp.lan.ra")
|
||||||
ucic:set("shadowsocks-libev","hi","local_address","0.0.0.0")
|
ucic:set("shadowsocks-libev","hi","local_address","0.0.0.0")
|
||||||
else
|
else
|
||||||
|
ucic:set("dhcp","lan","dhcpv6","server")
|
||||||
|
ucic:set("dhcp","lan","ra","server")
|
||||||
ucic:set("shadowsocks-libev","hi","local_address","::")
|
ucic:set("shadowsocks-libev","hi","local_address","::")
|
||||||
end
|
end
|
||||||
|
ucic:save("dhcp")
|
||||||
|
ucic:commit("dhcp")
|
||||||
|
|
||||||
|
local obfs = luci.http.formvalue("obfs") or 0
|
||||||
|
ucic:foreach("shadowsocks-libev", "ss_redir", function (section)
|
||||||
|
ucic:set("shadowsocks-libev",section[".name"],"obfs",obfs)
|
||||||
|
end)
|
||||||
|
ucic:set("shadowsocks-libev","tracker","obfs",obfs)
|
||||||
|
|
||||||
ucic:save("shadowsocks-libev")
|
ucic:save("shadowsocks-libev")
|
||||||
ucic:commit("shadowsocks-libev")
|
ucic:commit("shadowsocks-libev")
|
||||||
|
|
||||||
|
@ -160,17 +370,65 @@ end
|
||||||
|
|
||||||
function get_ip(interface)
|
function get_ip(interface)
|
||||||
local dump = require("luci.util").ubus("network.interface.%s" % interface, "status", {})
|
local dump = require("luci.util").ubus("network.interface.%s" % interface, "status", {})
|
||||||
local ip
|
local ip = ""
|
||||||
if dump and dump['ipv4-address'] then
|
if dump and dump['ipv4-address'] then
|
||||||
local _, ipv4address
|
local _, ipv4address
|
||||||
for _, ipv4address in ipairs(dump['ipv4-address']) do
|
for _, ipv4address in ipairs(dump['ipv4-address']) do
|
||||||
ip = dump['ipv4-address'][_].address
|
ip = dump['ipv4-address'][_].address
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if ip == "" then
|
||||||
|
local dump = require("luci.util").ubus("network.interface.%s_4" % interface, "status", {})
|
||||||
|
if dump and dump['ipv4-address'] then
|
||||||
|
local _, ipv4address
|
||||||
|
for _, ipv4address in ipairs(dump['ipv4-address']) do
|
||||||
|
ip = dump['ipv4-address'][_].address
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
return ip
|
return ip
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This function come from OverTheBox by OVH with very small changes
|
function get_device(interface)
|
||||||
|
local dump = require("luci.util").ubus("network.interface.%s" % interface, "status", {})
|
||||||
|
return dump['l3_device']
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_gateway(interface)
|
||||||
|
local gateway = ""
|
||||||
|
local dump = nil
|
||||||
|
|
||||||
|
dump = require("luci.util").ubus("network.interface.%s" % interface, "status", {})
|
||||||
|
|
||||||
|
if dump and dump.route then
|
||||||
|
local _, route
|
||||||
|
for _, route in ipairs(dump.route) do
|
||||||
|
if dump.route[_].target == "0.0.0.0" then
|
||||||
|
gateway = dump.route[_].nexthop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if gateway == "" then
|
||||||
|
dump = require("luci.util").ubus("network.interface.%s_4" % interface, "status", {})
|
||||||
|
|
||||||
|
if dump and dump.route then
|
||||||
|
local _, route
|
||||||
|
for _, route in ipairs(dump.route) do
|
||||||
|
if dump.route[_].target == "0.0.0.0" then
|
||||||
|
gateway = dump.route[_].nexthop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return gateway
|
||||||
|
end
|
||||||
|
|
||||||
|
-- This function come from OverTheBox by OVH with some changes
|
||||||
|
-- Copyright 2015 OVH <OverTheBox@ovh.net>
|
||||||
|
-- Simon Lelievre (simon.lelievre@corp.ovh.com)
|
||||||
|
-- Sebastien Duponcheel <sebastien.duponcheel@ovh.net>
|
||||||
|
-- Under GPL3+
|
||||||
function interfaces_status()
|
function interfaces_status()
|
||||||
local ut = require "luci.util"
|
local ut = require "luci.util"
|
||||||
local ntm = require "luci.model.network".init()
|
local ntm = require "luci.model.network".init()
|
||||||
|
@ -181,22 +439,35 @@ function interfaces_status()
|
||||||
-- OpenMPTCProuter info
|
-- OpenMPTCProuter info
|
||||||
mArray.openmptcprouter = {}
|
mArray.openmptcprouter = {}
|
||||||
mArray.openmptcprouter["version"] = ut.trim(sys.exec("cat /etc/os-release | grep VERSION= | sed -e 's:VERSION=::'"))
|
mArray.openmptcprouter["version"] = ut.trim(sys.exec("cat /etc/os-release | grep VERSION= | sed -e 's:VERSION=::'"))
|
||||||
-- Check that requester is in same network
|
|
||||||
mArray.openmptcprouter["service_addr"] = uci:get("shadowsocks", "proxy", "server") or "0.0.0.0"
|
mArray.openmptcprouter["service_addr"] = uci:get("shadowsocks-libev", "proxy", "server") or "0.0.0.0"
|
||||||
mArray.openmptcprouter["local_addr"] = uci:get("network", "lan", "ipaddr")
|
mArray.openmptcprouter["local_addr"] = uci:get("network", "lan", "ipaddr")
|
||||||
|
|
||||||
-- shadowsocksaddr
|
|
||||||
mArray.openmptcprouter["ss_addr"] = sys.exec("curl -s -4 --socks5 127.0.0.1:1111 -m 5 http://ip.openmptcprouter.com")
|
|
||||||
-- wanaddr
|
|
||||||
mArray.openmptcprouter["wan_addr"] = sys.exec("wget -4 -qO- -T 1 http://ip.openmptcprouter.com")
|
|
||||||
|
|
||||||
-- dns
|
-- dns
|
||||||
mArray.openmptcprouter["dns"] = false
|
mArray.openmptcprouter["dns"] = false
|
||||||
local dns_test = sys.exec("dig openmptcprouter.com | grep 'ANWER: 0'")
|
local dns_test = sys.exec("dig openmptcprouter.com | grep 'ANSWER: 0'")
|
||||||
if dns_test == "" then
|
if dns_test == "" then
|
||||||
mArray.openmptcprouter["dns"] = true
|
mArray.openmptcprouter["dns"] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
mArray.openmptcprouter["ss_addr"] = ""
|
||||||
|
--mArray.openmptcprouter["ss_addr6"] = ""
|
||||||
|
mArray.openmptcprouter["wan_addr"] = ""
|
||||||
|
mArray.openmptcprouter["wan_addr6"] = ""
|
||||||
|
local tracker_ip = ""
|
||||||
|
if mArray.openmptcprouter["dns"] == true then
|
||||||
|
-- shadowsocksaddr
|
||||||
|
tracker_ip = uci:get("shadowsocks-libev","tracker","local_address") or ""
|
||||||
|
local tracker_port = uci:get("shadowsocks-libev","tracker","local_port")
|
||||||
|
if tracker_ip ~= "" then
|
||||||
|
mArray.openmptcprouter["ss_addr"] = sys.exec("curl -s -4 --socks5 " .. tracker_ip .. ":" .. tracker_port .. " -m 3 http://ip.openmptcprouter.com")
|
||||||
|
--mArray.openmptcprouter["ss_addr6"] = sys.exec("curl -s -6 --socks5 " .. tracker_ip .. ":" .. tracker_port .. " -m 3 http://ipv6.openmptcprouter.com")
|
||||||
|
end
|
||||||
|
-- wanaddr
|
||||||
|
mArray.openmptcprouter["wan_addr"] = sys.exec("wget -4 -qO- -T 1 http://ip.openmptcprouter.com")
|
||||||
|
mArray.openmptcprouter["wan_addr6"] = sys.exec("wget -6 -qO- -T 1 http://ipv6.openmptcprouter.com")
|
||||||
|
end
|
||||||
|
|
||||||
mArray.openmptcprouter["remote_addr"] = luci.http.getenv("REMOTE_ADDR") or ""
|
mArray.openmptcprouter["remote_addr"] = luci.http.getenv("REMOTE_ADDR") or ""
|
||||||
mArray.openmptcprouter["remote_from_lease"] = false
|
mArray.openmptcprouter["remote_from_lease"] = false
|
||||||
local leases=tools.dhcp_leases()
|
local leases=tools.dhcp_leases()
|
||||||
|
@ -209,23 +480,50 @@ function interfaces_status()
|
||||||
|
|
||||||
-- Check openmptcprouter service are running
|
-- Check openmptcprouter service are running
|
||||||
mArray.openmptcprouter["tun_service"] = false
|
mArray.openmptcprouter["tun_service"] = false
|
||||||
if string.find(sys.exec("/usr/bin/pgrep '^(/usr/sbin/)?glorytun(-udp)?$'"), "%d+") then
|
if string.find(sys.exec("/usr/bin/pgrep '^(/usr/sbin/)?glorytun(-udp)?$'"), "%d+") or string.find(sys.exec("/usr/bin/pgrep '^(/usr/sbin/)?mlvpn?$'"), "%d+") or string.find(sys.exec("/usr/bin/pgrep '^(/usr/sbin/)?openvpn?$'"), "%d+") then
|
||||||
mArray.openmptcprouter["tun_service"] = true
|
mArray.openmptcprouter["tun_service"] = true
|
||||||
mArray.openmptcprouter["tun_ip"] = get_ip("glorytun")
|
mArray.openmptcprouter["tun_ip"] = get_ip("omrvpn")
|
||||||
local tunnel_ping_test = ut.trim(sys.exec("ping -W 1 -c 1 10.0.0.1 | grep '100% packet loss'"))
|
local tun_dev = uci:get("network","omrvpn","ifname")
|
||||||
if tunnel_ping_test == "" then
|
if tun_dev == "" then
|
||||||
mArray.openmptcprouter["tun_state"] = 'UP'
|
tun_dev = get_device("omrvpn")
|
||||||
else
|
end
|
||||||
mArray.openmptcprouter["tun_state"] = 'DOWN'
|
if tun_dev ~= "" then
|
||||||
|
local peer = get_gateway("omrvpn")
|
||||||
|
if peer == "" then
|
||||||
|
peer = ut.trim(sys.exec("ip -4 r list dev " .. tun_dev .. " | grep kernel | awk '/proto kernel/ {print $1}' | grep -v / | tr -d '\n'"))
|
||||||
|
end
|
||||||
|
if peer ~= "" then
|
||||||
|
local tunnel_ping_test = ut.trim(sys.exec("ping -W 1 -c 1 " .. peer .. " -I " .. tun_dev .. " | grep '100% packet loss'"))
|
||||||
|
if tunnel_ping_test == "" then
|
||||||
|
mArray.openmptcprouter["tun_state"] = 'UP'
|
||||||
|
else
|
||||||
|
mArray.openmptcprouter["tun_state"] = 'DOWN'
|
||||||
|
end
|
||||||
|
local tunnel_ping6_test = ut.trim(sys.exec("ping6 -W 1 -c 1 fe80::a00:1 -I 6in4-omr6in4 | grep '100% packet loss'"))
|
||||||
|
if tunnel_ping6_test == "" then
|
||||||
|
mArray.openmptcprouter["tun6_state"] = 'UP'
|
||||||
|
else
|
||||||
|
mArray.openmptcprouter["tun6_state"] = 'DOWN'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
mArray.openmptcprouter["tun_state"] = 'DOWN'
|
||||||
|
mArray.openmptcprouter["tun6_state"] = 'DOWN'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check Shadowsocks is running
|
||||||
mArray.openmptcprouter["socks_service"] = false
|
mArray.openmptcprouter["socks_service"] = false
|
||||||
if string.find(sys.exec("/usr/bin/pgrep ss-redir"), "%d+") then
|
if string.find(sys.exec("/usr/bin/pgrep ss-redir"), "%d+") then
|
||||||
mArray.openmptcprouter["socks_service"] = true
|
mArray.openmptcprouter["socks_service"] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
mArray.openmptcprouter["socks_service_enabled"] = true
|
||||||
|
local ss_server = uci:get("shadowsocks-libev","sss0","disabled") or "0"
|
||||||
|
if ss_server == "1" then
|
||||||
|
mArray.openmptcprouter["socks_service_enabled"] = false
|
||||||
|
end
|
||||||
|
|
||||||
-- Add DHCP infos by parsing dnsmasq config file
|
-- Add DHCP infos by parsing dnsmasq config file
|
||||||
mArray.openmptcprouter.dhcpd = {}
|
mArray.openmptcprouter.dhcpd = {}
|
||||||
dnsmasq = ut.trim(sys.exec("cat /var/etc/dnsmasq.conf*"))
|
dnsmasq = ut.trim(sys.exec("cat /var/etc/dnsmasq.conf*"))
|
||||||
|
@ -249,6 +547,7 @@ function interfaces_status()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parse mptcp kernel info
|
-- Parse mptcp kernel info
|
||||||
local mptcp = {}
|
local mptcp = {}
|
||||||
local fullmesh = ut.trim(sys.exec("cat /proc/net/mptcp_fullmesh"))
|
local fullmesh = ut.trim(sys.exec("cat /proc/net/mptcp_fullmesh"))
|
||||||
|
@ -275,61 +574,165 @@ function interfaces_status()
|
||||||
local ipaddr = net:ipaddr()
|
local ipaddr = net:ipaddr()
|
||||||
local gateway = section['gateway'] or ""
|
local gateway = section['gateway'] or ""
|
||||||
local multipath = section['multipath']
|
local multipath = section['multipath']
|
||||||
|
local enabled = section['auto']
|
||||||
|
|
||||||
--if not ipaddr or not gateway then return end
|
--if not ipaddr or not gateway then return end
|
||||||
-- Don't show if0 in the overview
|
-- Don't show if0 in the overview
|
||||||
--if interface == "lo" then return end
|
--if interface == "lo" then return end
|
||||||
|
|
||||||
local ifname = section['ifname'] or ""
|
local ifname = section['ifname'] or ""
|
||||||
|
if ifname == "" then
|
||||||
|
ifname = get_device(interface)
|
||||||
|
end
|
||||||
|
|
||||||
--if multipath == "off" and not ifname:match("^tun.*") then return end
|
--if multipath == "off" and not ifname:match("^tun.*") then return end
|
||||||
if multipath == "off" then return end
|
if multipath == "off" then return end
|
||||||
|
|
||||||
local asn
|
if enabled == "0" then return end
|
||||||
|
|
||||||
local connectivity
|
local connectivity
|
||||||
local multipath_state = ut.trim(sys.exec("multipath " .. ifname .. " | grep deactivated"))
|
|
||||||
if multipath_state == "" and ifname ~= "" then
|
if ifname ~= "" and ifname ~= nil then
|
||||||
connectivity = 'OK'
|
if fs.access("/sys/class/net/" .. ifname) then
|
||||||
|
local multipath_state = ut.trim(sys.exec("multipath " .. ifname .. " | grep deactivated"))
|
||||||
|
if multipath_state == "" then
|
||||||
|
connectivity = 'OK'
|
||||||
|
else
|
||||||
|
connectivity = 'ERROR'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
connectivity = 'ERROR'
|
||||||
|
end
|
||||||
else
|
else
|
||||||
connectivity = 'ERROR'
|
connectivity = 'ERROR'
|
||||||
end
|
end
|
||||||
|
|
||||||
local gw_ping
|
if ipaddr == "" then
|
||||||
if gateway ~= "" then
|
connectivity = 'ERROR'
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detect WAN gateway status
|
||||||
|
local gw_ping = 'UP'
|
||||||
|
if gateway == "" then
|
||||||
|
gateway = get_gateway(interface)
|
||||||
|
end
|
||||||
|
if connectivity ~= "ERROR" and gateway == "" and ifname ~= nil then
|
||||||
|
if fs.access("/sys/class/net/" .. ifname) then
|
||||||
|
gateway = ut.trim(sys.exec("ip -4 r list dev " .. ifname .. " | grep kernel | awk '/proto kernel/ {print $1}' | grep -v / | tr -d '\n'"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if connectivity ~= "ERROR" and gateway ~= "" then
|
||||||
local gw_ping_test = ut.trim(sys.exec("ping -W 1 -c 1 " .. gateway .. " | grep '100% packet loss'"))
|
local gw_ping_test = ut.trim(sys.exec("ping -W 1 -c 1 " .. gateway .. " | grep '100% packet loss'"))
|
||||||
if gw_ping_test == "" then
|
if gw_ping_test ~= "" then
|
||||||
gw_ping = 'UP'
|
|
||||||
else
|
|
||||||
gw_ping = 'DOWN'
|
gw_ping = 'DOWN'
|
||||||
|
if connectivity == "OK" then
|
||||||
|
connectivity = 'WARNING'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
gw_ping = 'DOWN'
|
||||||
|
connectivity = 'ERROR'
|
||||||
|
end
|
||||||
|
|
||||||
|
local latency = ""
|
||||||
|
local server_ping = ''
|
||||||
|
if connectivity ~= "ERROR" and ifname ~= "" and gateway ~= "" and gw_ping ~= "DOWN" and ifname ~= nil and mArray.openmptcprouter["wan_addr"] ~= "" then
|
||||||
|
local server_ping_test = sys.exec("ping -W 1 -c 1 -I " .. ifname .. " " .. mArray.openmptcprouter["wan_addr"])
|
||||||
|
local server_ping_result = ut.trim(sys.exec("echo '" .. server_ping_test .. "' | grep '100% packet loss'"))
|
||||||
|
if server_ping_result ~= "" then
|
||||||
|
server_ping = 'DOWN'
|
||||||
if connectivity == "OK" then
|
if connectivity == "OK" then
|
||||||
connectivity = 'WARNING'
|
connectivity = 'WARNING'
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
server_ping = 'UP'
|
||||||
|
latency = ut.trim(sys.exec("echo '" .. server_ping_test .. "' | cut -d '/' -s -f4 | cut -d '.' -f1"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local publicIP = "-"
|
local multipath_available
|
||||||
|
if connectivity ~= "ERROR" and mArray.openmptcprouter["dns"] == true and ifname ~= nil and ifname ~= "" and gateway ~= "" and gw_ping == "UP" then
|
||||||
|
-- Test if multipath can work on the connection
|
||||||
|
local multipath_available_state = ut.trim(sys.exec("omr-mptcp-intf " .. ifname .. " | grep 'Nay, Nay, Nay'"))
|
||||||
|
if multipath_available_state == "" then
|
||||||
|
multipath_available = 'OK'
|
||||||
|
else
|
||||||
|
multipath_available = 'ERROR'
|
||||||
|
if mArray.openmptcprouter["socks_service"] == true and connectivity == "OK" then
|
||||||
|
connectivity = 'ERROR'
|
||||||
|
elseif connectivity == "OK" then
|
||||||
|
connectivity = 'WARNING'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
multipath_available = 'NO CHECK'
|
||||||
|
end
|
||||||
|
|
||||||
local latency = "-"
|
|
||||||
|
-- Detect if WAN get an IPv6
|
||||||
|
local ipv6_discover = 'NONE'
|
||||||
|
if ifname ~= nil then
|
||||||
|
if tonumber((sys.exec("sysctl net.ipv6.conf.all.disable_ipv6")):match(" %d+")) == 0 then
|
||||||
|
local ipv6_result = _ipv6_discover(ifname)
|
||||||
|
if type(ipv6_result) == "table" and #ipv6_result > 0 then
|
||||||
|
local ipv6_addr_test
|
||||||
|
for k,v in ipairs(ipv6_result) do
|
||||||
|
if v.RecursiveDnsServer then
|
||||||
|
if type(v.RecursiveDnsServer) ~= "table" then
|
||||||
|
ipv6_addr_test = sys.exec('ip -6 addr | grep ' .. v.RecursiveDnsServer)
|
||||||
|
if ipv6_addr_test == "" then
|
||||||
|
ipv6_discover = 'DETECTED'
|
||||||
|
if connectivity == "OK" then
|
||||||
|
connectivity = 'WARNING'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i,j in ipairs(ipv6_result) do
|
||||||
|
ipv6_addr_test = sys.exec('ip -6 addr | grep ' .. j)
|
||||||
|
if ipv6_addr_test == "" then
|
||||||
|
ipv6_discover = 'DETECTED'
|
||||||
|
if connectivity == "OK" then
|
||||||
|
connectivity = 'WARNING'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local publicIP = ut.trim(sys.exec("omr-ip-intf " .. ifname))
|
||||||
|
local whois = ""
|
||||||
|
if publicIP ~= "" then
|
||||||
|
whois = ut.trim(sys.exec("whois " .. publicIP .. " | grep -i 'netname' | awk '{print $2}'"))
|
||||||
|
end
|
||||||
|
|
||||||
local data = {
|
local data = {
|
||||||
label = section['label'] or interface,
|
label = section['label'] or interface,
|
||||||
name = interface,
|
name = interface,
|
||||||
link = net:adminlink(),
|
link = net:adminlink(),
|
||||||
ifname = ifname,
|
ifname = ifname,
|
||||||
ipaddr = ipaddr,
|
ipaddr = ipaddr,
|
||||||
gateway = gateway,
|
gateway = gateway,
|
||||||
multipath = section['multipath'],
|
multipath = section['multipath'],
|
||||||
status = connectivity,
|
status = connectivity,
|
||||||
wanip = publicIP,
|
wanip = publicIP,
|
||||||
latency = latency,
|
latency = latency,
|
||||||
whois = asn and asn.as_description or "unknown",
|
whois = whois or "unknown",
|
||||||
qos = section['trafficcontrol'],
|
qos = section['trafficcontrol'],
|
||||||
download = section['download'],
|
download = section['download'],
|
||||||
upload = section['upload'],
|
upload = section['upload'],
|
||||||
gw_ping = gw_ping,
|
gw_ping = gw_ping,
|
||||||
|
server_ping = server_ping,
|
||||||
|
ipv6_discover = ipv6_discover,
|
||||||
|
multipath_available = multipath_available,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ifname:match("^tun.*") then
|
if ifname ~= nil and ifname:match("^tun.*") then
|
||||||
|
table.insert(mArray.tunnels, data);
|
||||||
|
elseif ifname ~= nil and ifname:match("^mlvpn.*") then
|
||||||
table.insert(mArray.tunnels, data);
|
table.insert(mArray.tunnels, data);
|
||||||
else
|
else
|
||||||
table.insert(mArray.wans, data);
|
table.insert(mArray.wans, data);
|
||||||
|
@ -338,4 +741,65 @@ function interfaces_status()
|
||||||
|
|
||||||
luci.http.prepare_content("application/json")
|
luci.http.prepare_content("application/json")
|
||||||
luci.http.write_json(mArray)
|
luci.http.write_json(mArray)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- This come from OverTheBox by OVH
|
||||||
|
-- Copyright 2015 OVH <OverTheBox@ovh.net>
|
||||||
|
-- Simon Lelievre (simon.lelievre@corp.ovh.com)
|
||||||
|
-- Sebastien Duponcheel <sebastien.duponcheel@ovh.net>
|
||||||
|
-- Under GPL3+
|
||||||
|
function _ipv6_discover(interface)
|
||||||
|
local result = {}
|
||||||
|
|
||||||
|
--local ra6_list = (sys.exec("rdisc6 -nm " .. interface))
|
||||||
|
local ra6_list = (sys.exec("rdisc6 -n1 -r1 " .. interface))
|
||||||
|
-- dissect results
|
||||||
|
local lines = {}
|
||||||
|
local index = {}
|
||||||
|
ra6_list:gsub('[^\r\n]+', function(c)
|
||||||
|
table.insert(lines, c)
|
||||||
|
if c:match("Hop limit") then
|
||||||
|
table.insert(index, #lines)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
local ra6_result = {}
|
||||||
|
for k,v in ipairs(index) do
|
||||||
|
local istart = v
|
||||||
|
local iend = index[k+1] or #lines
|
||||||
|
|
||||||
|
local entry = {}
|
||||||
|
for i=istart,iend - 1 do
|
||||||
|
local level = lines[i]:find('%w')
|
||||||
|
local line = lines[i]:sub(level)
|
||||||
|
|
||||||
|
local param, value
|
||||||
|
if line:match('^from') then
|
||||||
|
param, value = line:match('(from)%s+(.*)$')
|
||||||
|
else
|
||||||
|
param, value = line:match('([^:]+):(.*)$')
|
||||||
|
-- Capitalize param name and remove spaces
|
||||||
|
param = param:gsub("(%a)([%w_']*)", function(first, rest) return first:upper()..rest:lower() end):gsub("[%s-]",'')
|
||||||
|
param = param:gsub("%.$", '')
|
||||||
|
-- Remove text between brackets, seconds and spaces
|
||||||
|
value = value:lower()
|
||||||
|
value = value:gsub("%(.*%)", '')
|
||||||
|
value = value:gsub("%s-seconds%s-", '')
|
||||||
|
value = value:gsub("^%s+", '')
|
||||||
|
value = value:gsub("%s+$", '')
|
||||||
|
end
|
||||||
|
|
||||||
|
if entry[param] == nil then
|
||||||
|
entry[param] = value
|
||||||
|
elseif type(entry[param]) == "table" then
|
||||||
|
table.insert(entry[param], value)
|
||||||
|
else
|
||||||
|
old = entry[param]
|
||||||
|
entry[param] = {}
|
||||||
|
table.insert(entry[param], old)
|
||||||
|
table.insert(entry[param], value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(ra6_result, entry)
|
||||||
|
end
|
||||||
|
return ra6_result
|
||||||
|
end
|
||||||
|
|
|
@ -13,12 +13,28 @@
|
||||||
<input type="text" name="tcp_keepalive_time" class="cbi-input-text" value="<%=tonumber((luci.sys.exec("sysctl net.ipv4.tcp_keepalive_time")):match(" %d+"))%>">
|
<input type="text" name="tcp_keepalive_time" class="cbi-input-text" value="<%=tonumber((luci.sys.exec("sysctl net.ipv4.tcp_keepalive_time")):match(" %d+"))%>">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:IPv4 TCP FIN timeout%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="text" name="tcp_fin_timeout" class="cbi-input-text" value="<%=tonumber((luci.sys.exec("sysctl net.ipv4.tcp_fin_timeout")):match(" %d+"))%>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="cbi-value">
|
<div class="cbi-value">
|
||||||
<label class="cbi-value-title"><%:Disable IPv6%></label>
|
<label class="cbi-value-title"><%:Disable IPv6%></label>
|
||||||
<div class="cbi-value-field">
|
<div class="cbi-value-field">
|
||||||
<input type="checkbox" name="disable_ipv6" class="cbi-input-checkbox" value="1" <% if tonumber((luci.sys.exec("sysctl net.ipv6.conf.all.disable_ipv6")):match(" %d+")) == 1 then %>checked<% end %>>
|
<input type="checkbox" name="disable_ipv6" class="cbi-input-checkbox" value="1" <% if tonumber((luci.sys.exec("sysctl net.ipv6.conf.all.disable_ipv6")):match(" %d+")) == 1 then %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Enable ShadowSocks OBFS%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="checkbox" name="obfs" class="cbi-input-checkbox" value="1" <% if luci.model.uci.cursor():get("shadowsocks-libev","tracker","obfs") == "1" then %>checked<% end %>>
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:OBFS must be enabled on VPS%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<% if nixio.fs.access("/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq") then %>
|
<% if nixio.fs.access("/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq") then %>
|
||||||
<fieldset class="cbi-section" id="system">
|
<fieldset class="cbi-section" id="system">
|
||||||
|
@ -57,7 +73,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="cbi-page-actions">
|
<div class="cbi-page-actions">
|
||||||
<input type="hidden" name="token" value="<%=token%>" />
|
<input type="hidden" name="token" value="<%=token%>" />
|
||||||
<button class="btn" type="submit">Submit</button>
|
<input class="cbi-button cbi-button-apply" type="submit" value="<%:Save & Apply%>" /> <input class="cbi-button cbi-button-reset" type="button" value="Reset" onclick="location.href='<%=url('admin/system/openmptcprouter/settings')%>'" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<%+footer%>
|
<%+footer%>
|
||||||
|
|
|
@ -22,13 +22,14 @@
|
||||||
-- Copyright 2018 Ycarus (Yannick Chabanois) ycarus@zugaina.org
|
-- Copyright 2018 Ycarus (Yannick Chabanois) ycarus@zugaina.org
|
||||||
--
|
--
|
||||||
-- Small changes to make this work with OpenMPTCProuter
|
-- Small changes to make this work with OpenMPTCProuter
|
||||||
|
-- New features: DNS detection, IPv6 route received,...
|
||||||
-%>
|
-%>
|
||||||
<%+header%>
|
<%+header%>
|
||||||
<link rel="stylesheet" type="text/css" href="<%=resource%>/openmptcprouter/css/wanstatus.css?v=git-18.120.38690-2678b12"/>
|
<link rel="stylesheet" type="text/css" href="<%=resource%>/openmptcprouter/css/wanstatus.css?v=git-18.120.38690-2678b12"/>
|
||||||
<script type="text/javascript" src="<%=resource%>/seedrandom.js?v=git-18.120.38690-2678b12"></script>
|
<script type="text/javascript" src="<%=resource%>/seedrandom.js?v=git-18.120.38690-2678b12"></script>
|
||||||
<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-18.120.38690-2678b12"></script>
|
<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-18.120.38690-2678b12"></script>
|
||||||
<script type="text/javascript">//<![CDATA[
|
<script type="text/javascript">//<![CDATA[
|
||||||
XHR.poll(15, '/cgi-bin/luci/admin/system/openmptcprouter/interfaces_status', null,
|
XHR.poll(20, '/cgi-bin/luci/admin/system/openmptcprouter/interfaces_status', null,
|
||||||
function(x, mArray)
|
function(x, mArray)
|
||||||
{
|
{
|
||||||
var status = document.getElementById('openmptcprouter_status');
|
var status = document.getElementById('openmptcprouter_status');
|
||||||
|
@ -71,18 +72,18 @@
|
||||||
}
|
}
|
||||||
if (mArray.openmptcprouter.loadavg)
|
if (mArray.openmptcprouter.loadavg)
|
||||||
{
|
{
|
||||||
content += "Load : " + mArray.openmptcprouter.loadavg;
|
content += "Load: " + mArray.openmptcprouter.loadavg;
|
||||||
content += "<br />";
|
content += "<br />";
|
||||||
}
|
}
|
||||||
if (mArray.openmptcprouter.core_temp)
|
if (mArray.openmptcprouter.core_temp)
|
||||||
{
|
{
|
||||||
content += "Core temp : " + (mArray.openmptcprouter.core_temp / 1000).toFixed(1) + " °";
|
content += "Core temp: " + (mArray.openmptcprouter.core_temp / 1000).toFixed(1) + " °";
|
||||||
content += "<br />";
|
content += "<br />";
|
||||||
}
|
}
|
||||||
if (mArray.openmptcprouter.uptime)
|
if (mArray.openmptcprouter.uptime)
|
||||||
{
|
{
|
||||||
var date = new Date(null);
|
var date = new Date(null);
|
||||||
content += "Uptime : " + String.format('%t', mArray.openmptcprouter.uptime);
|
content += "Uptime: " + String.format('%t', mArray.openmptcprouter.uptime);
|
||||||
content += "<br />";
|
content += "<br />";
|
||||||
}
|
}
|
||||||
if (mArray.openmptcprouter.dhcpd)
|
if (mArray.openmptcprouter.dhcpd)
|
||||||
|
@ -101,9 +102,14 @@
|
||||||
temp += String.format('lan (%s)', mArray.openmptcprouter.local_addr);
|
temp += String.format('lan (%s)', mArray.openmptcprouter.local_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mArray.openmptcprouter.socks_service == false)
|
if (mArray.openmptcprouter.socks_service_enabled == true) {
|
||||||
{
|
if (mArray.openmptcprouter.socks_service == false)
|
||||||
statusMessage += 'ShadowSocks is not running<br/>';
|
{
|
||||||
|
statusMessage += 'ShadowSocks is not running<br/>';
|
||||||
|
} else if (mArray.openmptcprouter.ss_addr == "")
|
||||||
|
{
|
||||||
|
statusMessage += 'ShadowSocks not working<br/>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mArray.openmptcprouter.tun_service == false)
|
if (mArray.openmptcprouter.tun_service == false)
|
||||||
{
|
{
|
||||||
|
@ -117,20 +123,37 @@
|
||||||
{
|
{
|
||||||
statusMessage += 'DNS issue: can\'t resolve hostname<br/>';
|
statusMessage += 'DNS issue: can\'t resolve hostname<br/>';
|
||||||
}
|
}
|
||||||
if (mArray.openmptcprouter.ss_addr == "")
|
if (mArray.openmptcprouter.wan_addr6)
|
||||||
{
|
{
|
||||||
statusMessage += 'ShadowSocks not working<br/>';
|
content += "IPv6: " + mArray.openmptcprouter.wan_addr6;
|
||||||
|
content += "<br />";
|
||||||
}
|
}
|
||||||
|
|
||||||
if(statusMessage !== "")
|
if(statusMessage !== "")
|
||||||
{
|
{
|
||||||
statusMessageClass = "error";
|
statusMessageClass = "error";
|
||||||
statusIcon = "<%=resource%>/openmptcprouter/images/statusError.png";
|
statusIcon = "<%=resource%>/openmptcprouter/images/statusError.png";
|
||||||
} else if (mArray.openmptcprouter.tun_state == "DOWN")
|
} else {
|
||||||
{
|
if (mArray.openmptcprouter.socks_service_enabled == false)
|
||||||
statusMessageClass = "warning";
|
{
|
||||||
statusIcon = "<%=resource%>/openmptcprouter/images/statusWarning.png";
|
statusMessage += 'ShadowSocks is DISABLED<br/>';
|
||||||
statusMessage += 'Glorytun VPN tunnel DOWN';
|
}
|
||||||
|
if (mArray.openmptcprouter.tun_state == "DOWN")
|
||||||
|
{
|
||||||
|
statusMessage += 'VPN tunnel DOWN<br/>';
|
||||||
|
}
|
||||||
|
if (mArray.openmptcprouter.tun6_state == "DOWN")
|
||||||
|
{
|
||||||
|
statusMessage += 'VPN IPv6 tunnel DOWN<br/>';
|
||||||
|
} else if (mArray.openmptcprouter.wan_addr6 == '')
|
||||||
|
{
|
||||||
|
statusMessage += 'No IPv6 access<br/>';
|
||||||
|
}
|
||||||
|
if (statusMessage !== "")
|
||||||
|
{
|
||||||
|
statusMessageClass = "warning";
|
||||||
|
statusIcon = "<%=resource%>/openmptcprouter/images/statusWarning.png";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
temp += getNetworkNodeTemplate(equipmentIcon, statusIcon, title, statusMessageClass,statusMessage,content);
|
temp += getNetworkNodeTemplate(equipmentIcon, statusIcon, title, statusMessageClass,statusMessage,content);
|
||||||
temp += '</a></td>'
|
temp += '</a></td>'
|
||||||
|
@ -138,6 +161,7 @@
|
||||||
if (mArray.wans)
|
if (mArray.wans)
|
||||||
{
|
{
|
||||||
temp += '<td><ul>';
|
temp += '<td><ul>';
|
||||||
|
var master = 0;
|
||||||
for( var i = 0; i < mArray.wans.length; i++ )
|
for( var i = 0; i < mArray.wans.length; i++ )
|
||||||
{
|
{
|
||||||
// Get link color
|
// Get link color
|
||||||
|
@ -173,9 +197,16 @@
|
||||||
var ipaddr = mArray.wans[i].ipaddr;
|
var ipaddr = mArray.wans[i].ipaddr;
|
||||||
var whois = mArray.wans[i].whois;
|
var whois = mArray.wans[i].whois;
|
||||||
var multipath = mArray.wans[i].multipath;
|
var multipath = mArray.wans[i].multipath;
|
||||||
|
if(multipath == 'master')
|
||||||
|
{
|
||||||
|
master++;
|
||||||
|
}
|
||||||
var latency = mArray.wans[i].latency;
|
var latency = mArray.wans[i].latency;
|
||||||
var gateway = mArray.wans[i].gateway;
|
var gateway = mArray.wans[i].gateway;
|
||||||
var gw_ping = mArray.wans[i].gw_ping;
|
var gw_ping = mArray.wans[i].gw_ping;
|
||||||
|
var server_ping = mArray.wans[i].server_ping;
|
||||||
|
var ipv6_discover = mArray.wans[i].ipv6_discover;
|
||||||
|
var multipath_available = mArray.wans[i].multipath_available;
|
||||||
// Generate template
|
// Generate template
|
||||||
if(mArray.openmptcprouter.remote_from_lease == true && mArray.wans.length == 1)
|
if(mArray.openmptcprouter.remote_from_lease == true && mArray.wans.length == 1)
|
||||||
{
|
{
|
||||||
|
@ -188,14 +219,53 @@
|
||||||
var title = mArray.wans[i].label + " (" + mArray.wans[i].gateway + ")";
|
var title = mArray.wans[i].label + " (" + mArray.wans[i].gateway + ")";
|
||||||
//var content = String.format('%s<br />wan address: <strong>%s</strong><br />whois: %s<br />latency: %s ms<br />multipath: %s', stat, wanip, whois, latency, multipath);
|
//var content = String.format('%s<br />wan address: <strong>%s</strong><br />whois: %s<br />latency: %s ms<br />multipath: %s', stat, wanip, whois, latency, multipath);
|
||||||
var content = "";
|
var content = "";
|
||||||
if(gw_ping == 'DOWN')
|
if(wanip !== '')
|
||||||
|
{
|
||||||
|
content += String.format('wan address: <strong>%s</strong><br />', wanip);
|
||||||
|
}
|
||||||
|
if(whois !== '')
|
||||||
|
{
|
||||||
|
content += String.format('whois: %s<br />', whois);
|
||||||
|
}
|
||||||
|
if(latency !== '')
|
||||||
|
{
|
||||||
|
content += String.format('latency: %s ms<br />', latency);
|
||||||
|
}
|
||||||
|
if(ipaddr == '')
|
||||||
|
{
|
||||||
|
statusMessage += 'No IP defined<br />'
|
||||||
|
}
|
||||||
|
if(gateway == '')
|
||||||
|
{
|
||||||
|
statusMessage += 'No gateway defined<br />'
|
||||||
|
} else if(gw_ping == 'DOWN')
|
||||||
{
|
{
|
||||||
statusMessage += 'Gateway DOWN<br />'
|
statusMessage += 'Gateway DOWN<br />'
|
||||||
|
} else if(multipath_available == 'ERROR')
|
||||||
|
{
|
||||||
|
statusMessage += 'Multipath blocked on the connection<br />'
|
||||||
}
|
}
|
||||||
content += String.format('ip address: <strong>%s</strong><br />multipath: %s', ipaddr,multipath);
|
if(server_ping == 'DOWN')
|
||||||
|
{
|
||||||
|
statusMessage += 'No Server ping response after 1 second<br />'
|
||||||
|
}
|
||||||
|
if(multipath == 'master' && master > 1)
|
||||||
|
{
|
||||||
|
statusMessage += 'Multipath master already defined<br />';
|
||||||
|
statusMessageClass = "error";
|
||||||
|
}
|
||||||
|
if(ipv6_discover == 'DETECTED')
|
||||||
|
{
|
||||||
|
statusMessage += 'IPv6 route received<br />'
|
||||||
|
}
|
||||||
|
if(ipaddr != '')
|
||||||
|
{
|
||||||
|
content += String.format('ip address: <strong>%s</strong><br />', ipaddr);
|
||||||
|
}
|
||||||
|
content += String.format('multipath: %s<br />',multipath);
|
||||||
if(mArray.wans[i].qos && mArray.wans[i].download > 0 && mArray.wans[i].upload > 0)
|
if(mArray.wans[i].qos && mArray.wans[i].download > 0 && mArray.wans[i].upload > 0)
|
||||||
{
|
{
|
||||||
content += String.format('<br />traffic control: %s/%s kbps (%s)', mArray.wans[i].download, mArray.wans[i].upload, mArray.wans[i].qos)
|
content += String.format('traffic control: %s/%s kbps (%s)', mArray.wans[i].download, mArray.wans[i].upload, mArray.wans[i].qos)
|
||||||
}
|
}
|
||||||
temp += getNetworkNodeTemplate(equipmentIcon, statusIcon, title, statusMessageClass,statusMessage,content);
|
temp += getNetworkNodeTemplate(equipmentIcon, statusIcon, title, statusMessageClass,statusMessage,content);
|
||||||
|
|
||||||
|
@ -302,6 +372,6 @@
|
||||||
<h2><%:Network overview%></h2>
|
<h2><%:Network overview%></h2>
|
||||||
<fieldset id="interface_field" class="cbi-section">
|
<fieldset id="interface_field" class="cbi-section">
|
||||||
<!-- <legend><%:Network overview%></legen> -->
|
<!-- <legend><%:Network overview%></legen> -->
|
||||||
<div id="openmptcprouter_status"></div>
|
<div id="openmptcprouter_status"><img src="<%=resource%>/spinner.gif" /></div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<%+footer%>
|
<%+footer%>
|
|
@ -3,33 +3,115 @@
|
||||||
<%
|
<%
|
||||||
local uci = require("luci.model.uci").cursor()
|
local uci = require("luci.model.uci").cursor()
|
||||||
local net = require "luci.model.network".init()
|
local net = require "luci.model.network".init()
|
||||||
|
local fs = require "nixio.fs"
|
||||||
local ifaces = net:get_interfaces()
|
local ifaces = net:get_interfaces()
|
||||||
|
local servers_ip = {}
|
||||||
|
local server_ip = uci:get("shadowsocks-libev","sss0","server")
|
||||||
|
if server_ip == '127.0.0.1' then
|
||||||
|
local upstreams = uci:get("nginx-ha","ShadowSocks","upstreams")
|
||||||
|
for _, up in pairs(upstreams) do
|
||||||
|
local a = up:match("^([^:]+):")
|
||||||
|
table.insert(servers_ip,a)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(servers_ip,server_ip)
|
||||||
|
end
|
||||||
%>
|
%>
|
||||||
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
|
<script type="text/javascript" src="<%=resource%>/cbi.js" data-strings="{"path":{"resource":"\/luci-static\/resources","browser":"\/cgi-bin\/luci\/admin\/filebrowser"}}"></script>
|
||||||
|
|
||||||
<% if stderr and #stderr > 0 then %><pre class="error"><%=pcdata(stderr)%></pre><% end %>
|
<% if stderr and #stderr > 0 then %><pre class="error"><%=pcdata(stderr)%></pre><% end %>
|
||||||
<form class="inline" method="post" action="<%=url('admin/system/openmptcprouter/wizard_add')%>">
|
<form class="inline" method="post" action="<%=url('admin/system/openmptcprouter/wizard_add')%>" enctype="multipart/form-data">
|
||||||
<div class="cbi-map">
|
<div class="cbi-map">
|
||||||
<h2 name="content"><%:Wizard%></h2>
|
<h2 name="content"><%:Wizard%></h2>
|
||||||
<fieldset class="cbi-section" id="server">
|
<fieldset class="cbi-section" id="server">
|
||||||
<legend><%:Server settings%></legend>
|
<legend><%:Server settings%></legend>
|
||||||
<div class="cbi-section-descr"><%:Put the values given by OpenMPTCProuter VPS script.%></div>
|
<div class="cbi-section-descr"><%:Put the values given by OpenMPTCProuter VPS script.%></div>
|
||||||
<div class="cbi-value">
|
<div class="cbi-value cbi-value-last" id="server_ip" data-depends="[]" data-index="0">
|
||||||
<label class="cbi-value-title"><%:Server IP%></label>
|
<label class="cbi-value-title" for="server_ip"><%:Server IP%></label>
|
||||||
<div class="cbi-value-field">
|
<div class="cbi-value-field">
|
||||||
<input type="text" name="server_ip" placeholder="Server IP" class="cbi-input-text" value="<%=uci:get("shadowsocks-libev","sss0","server")%>" data-type="ip4addr">
|
<%
|
||||||
|
local has_nginxha = fs.access("/etc/config/nginx-ha")
|
||||||
|
if has_nginxha then
|
||||||
|
%>
|
||||||
|
<div data-prefix="server_ip" data-browser-path="" data-dynlist="[[],[],null,false]" data-placeholder="123.123.123.123">
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
local k = 0
|
||||||
|
for _,server in ipairs(servers_ip) do
|
||||||
|
k = k+1
|
||||||
|
%>
|
||||||
|
<input name="server_ip" id="server_ip.<%=k%>" placeholder="Server IP" class="cbi-input-text" value="<%=server%>" data-type="ip4addr">
|
||||||
|
<br />
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:Server IP will be set for ShadowSocks, Glorytun, OpenVPN and MLVPN%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br />
|
||||||
<div class="cbi-value">
|
<div class="cbi-value">
|
||||||
<label class="cbi-value-title"><%:ShadowSocks key%></label>
|
<label class="cbi-value-title"><%:ShadowSocks key%></label>
|
||||||
<div class="cbi-value-field">
|
<div class="cbi-value-field">
|
||||||
<input type="text" name="shadowsocks_key" placeholder="ShadowSocks key" class="cbi-input-text" value="<%=uci:get("shadowsocks-libev","sss0","key")%>" data-type="base64">
|
<input type="text" name="shadowsocks_key" placeholder="ShadowSocks key" class="cbi-input-text" value="<%=uci:get("shadowsocks-libev","sss0","key")%>" data-type="base64">
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:ShadowSocks is used for TCP%>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<% if nixio.fs.access("/usr/sbin/glorytun") or nixio.fs.access("/usr/sbin/glorytun-udp") then %>
|
||||||
<div class="cbi-value">
|
<div class="cbi-value">
|
||||||
<label class="cbi-value-title"><%:Glorytun key%></label>
|
<label class="cbi-value-title"><%:Glorytun key%></label>
|
||||||
<div class="cbi-value-field">
|
<div class="cbi-value-field">
|
||||||
<input type="text" name="glorytun_key" placeholder="Glorytun key" class="cbi-input-text" value="<%=uci:get("glorytun","vpn","key")%>">
|
<input type="text" name="glorytun_key" placeholder="Glorytun key" class="cbi-input-text" value="<%=uci:get("glorytun","vpn","key")%>">
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:Glorytun TCP is used by default for UDP and ICMP%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if nixio.fs.access("/usr/sbin/mlvpn") then %>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:MLVPN password%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="text" name="mlvpn_password" placeholder="MLVPN password" class="cbi-input-text" value="<%=uci:get("mlvpn","general","password")%>">
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:MLVPN can replace Glorytun with connections with same latency%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if nixio.fs.access("/usr/sbin/openvpn") then %>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:OpenVPN key%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="file" name="openvpn_key" class="cbi-input-file">
|
||||||
|
<input type="text" class="cbi-input-text" data-update="change" value="<%=uci:get("openvpn","omr","secret")%>" />
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:You need to upload OpenVPN key file generated by OpenMPTCProuter VPS script to use OpenVPN TCP%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Default VPN%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<select class="cbi-input-select" name="default_vpn" size="1">
|
||||||
|
<% if nixio.fs.access("/usr/sbin/glorytun") then %><option value="glorytun_tcp" <% if uci:get("glorytun","vpn","enable") == "1" and uci:get("glorytun","vpn","proto") == "tcp" then %>selected="selected"<% end %>>Glorytun TCP</option><% end %>
|
||||||
|
<% if nixio.fs.access("/usr/sbin/glorytun-udp") then %><option value="glorytun_udp" <% if uci:get("glorytun","vpn","enable") == "1" and uci:get("glorytun","vpn","proto") == "udp" then %>selected="selected"<% end %>>Glorytun UDP</option><% end %>
|
||||||
|
<% if nixio.fs.access("/usr/sbin/mlvpn") then %><option value="mlvpn" <% if uci:get("mlvpn","general","enable") == "1" then %>selected="selected"<% end %>>MLVPN</option><% end %>
|
||||||
|
<% if nixio.fs.access("/usr/sbin/openvpn") then %><option value="openvpn" <% if uci:get("openvpn","omr","enabled") == "1" then %>selected="selected"<% end %>>OpenVPN</option><% end %>
|
||||||
|
<option value="none" <% if uci:get("openmptcprouter","settings","vpn") == "none" then %>selected="selected"<% end %>>None</option>
|
||||||
|
</select>
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:Set the default VPN used for UDP and ICMP when ShadowSocks is enabled, for all traffic if ShadowSocks is disabled.%>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -54,7 +136,6 @@
|
||||||
<input type="text" name="cbid.network.<%=ifname%>.ipaddr" class="cbi-input-text" value="<%=uci:get("network",ifname,"ipaddr")%>" data-type="ip4addr">
|
<input type="text" name="cbid.network.<%=ifname%>.ipaddr" class="cbi-input-text" value="<%=uci:get("network",ifname,"ipaddr")%>" data-type="ip4addr">
|
||||||
<br />
|
<br />
|
||||||
<div class="cbi-value-description">
|
<div class="cbi-value-description">
|
||||||
<span class="cbi-value-helpicon"><img src="/luci-static/resources/cbi/help.gif" alt="help" /></span>
|
|
||||||
<%:Set an IP in the same network as the modem%>
|
<%:Set an IP in the same network as the modem%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,11 +152,36 @@
|
||||||
<input type="text" name="cbid.network.<%=ifname%>.gateway" class="cbi-input-text" value="<%=uci:get("network",ifname,"gateway")%>" data-type="ip4addr">
|
<input type="text" name="cbid.network.<%=ifname%>.gateway" class="cbi-input-text" value="<%=uci:get("network",ifname,"gateway")%>" data-type="ip4addr">
|
||||||
<br />
|
<br />
|
||||||
<div class="cbi-value-description">
|
<div class="cbi-value-description">
|
||||||
<span class="cbi-value-helpicon"><img src="/luci-static/resources/cbi/help.gif" alt="help" /></span>
|
|
||||||
<%:Set here IP of the modem%>
|
<%:Set here IP of the modem%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<%
|
||||||
|
if nixio.fs.access("/etc/init.d/sqm") then
|
||||||
|
%>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Download speed (Kb/s)%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="text" name="cbid.sqm.<%=ifname%>.download" class="cbi-input-text" value="<%=uci:get("sqm",ifname,"download")%>" data-type="uinteger">
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:Set value between 80-95% of max download speed link. Used for SQM. Empty to disable.%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cbi-value">
|
||||||
|
<label class="cbi-value-title"><%:Upload speed (Kb/s)%></label>
|
||||||
|
<div class="cbi-value-field">
|
||||||
|
<input type="text" name="cbid.sqm.<%=ifname%>.upload" class="cbi-input-text" value="<%=uci:get("sqm",ifname,"upload")%>" data-type="uinteger">
|
||||||
|
<br />
|
||||||
|
<div class="cbi-value-description">
|
||||||
|
<%:Set value between 80-95% of max upload speed link. Used for SQM. Empty to disable.%>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
%>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<%
|
<%
|
||||||
end
|
end
|
||||||
|
@ -89,7 +195,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="cbi-page-actions">
|
<div class="cbi-page-actions">
|
||||||
<input type="hidden" name="token" value="<%=token%>" />
|
<input type="hidden" name="token" value="<%=token%>" />
|
||||||
<button class="btn" type="submit">Submit</button>
|
<input class="cbi-button cbi-button-apply" type="submit" value="<%:Save & Apply%>" /> <input class="cbi-button cbi-button-reset" type="button" value="Reset" onclick="location.href='<%=url('admin/system/openmptcprouter/wizard')%>'" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<script type="text/javascript">cbi_init();</script>
|
||||||
<%+footer%>
|
<%+footer%>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"POT-Creation-Date: \n"
|
"POT-Creation-Date: \n"
|
||||||
"PO-Revision-Date: \n"
|
"PO-Revision-Date: \n"
|
||||||
|
"Last-Translator: Ycarus <ycarus@zugaina.org>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
|
"Language: fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Generator: Poedit 2.0.6\n"
|
"X-Generator: Poedit 2.0.6\n"
|
||||||
"Last-Translator: Ycarus <ycarus@zugaina.org>\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
"Language: fr\n"
|
|
||||||
|
|
||||||
msgid "Add an interface"
|
msgid "Add an interface"
|
||||||
msgstr "Ajouter une interface"
|
msgstr "Ajouter une interface"
|
||||||
|
@ -18,12 +18,18 @@ msgstr "Ajouter une interface"
|
||||||
msgid "Advanced Settings"
|
msgid "Advanced Settings"
|
||||||
msgstr "Configuration avancé"
|
msgstr "Configuration avancé"
|
||||||
|
|
||||||
|
msgid "Default VPN"
|
||||||
|
msgstr "VPN par défaut"
|
||||||
|
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr "Supprimer"
|
msgstr "Supprimer"
|
||||||
|
|
||||||
msgid "Disable IPv6"
|
msgid "Disable IPv6"
|
||||||
msgstr "Désactiver IPv6"
|
msgstr "Désactiver IPv6"
|
||||||
|
|
||||||
|
msgid "Glorytun TCP is used by default for UDP and ICMP"
|
||||||
|
msgstr "Glorytun est utilisé par défaut pour UDP et ICMP"
|
||||||
|
|
||||||
msgid "Glorytun key"
|
msgid "Glorytun key"
|
||||||
msgstr "Clef Glorytun"
|
msgstr "Clef Glorytun"
|
||||||
|
|
||||||
|
@ -37,11 +43,17 @@ msgid "IPv4 gateway"
|
||||||
msgstr "Passerelle IPv4"
|
msgstr "Passerelle IPv4"
|
||||||
|
|
||||||
msgid "IPv4 netmask"
|
msgid "IPv4 netmask"
|
||||||
msgstr "Masque de sous-réseau IPv6"
|
msgstr "Masque de sous-réseau IPv4"
|
||||||
|
|
||||||
msgid "Interfaces settings"
|
msgid "Interfaces settings"
|
||||||
msgstr "Paramètres des interfaces"
|
msgstr "Paramètres des interfaces"
|
||||||
|
|
||||||
|
msgid "MLVPN can replace Glorytun with connections with same latency"
|
||||||
|
msgstr "MLVPN peut remplacer Glorytun pour les connexions avec la même latence"
|
||||||
|
|
||||||
|
msgid "MLVPN password"
|
||||||
|
msgstr "Mot de passe MLVPN"
|
||||||
|
|
||||||
msgid "Maximum scaling CPU frequency"
|
msgid "Maximum scaling CPU frequency"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -57,6 +69,9 @@ msgstr "Paramètres réseaux"
|
||||||
msgid "OpenMPTCProuter"
|
msgid "OpenMPTCProuter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "OpenVPN key"
|
||||||
|
msgstr "Clef OpenVPN"
|
||||||
|
|
||||||
msgid "Put the values given by OpenMPTCProuter VPS script."
|
msgid "Put the values given by OpenMPTCProuter VPS script."
|
||||||
msgstr "Mettez les valeurs données par le script OpenMPTCProuter VPS."
|
msgstr "Mettez les valeurs données par le script OpenMPTCProuter VPS."
|
||||||
|
|
||||||
|
@ -66,6 +81,10 @@ msgstr ""
|
||||||
msgid "Server IP"
|
msgid "Server IP"
|
||||||
msgstr "IP du serveur"
|
msgstr "IP du serveur"
|
||||||
|
|
||||||
|
msgid "Server IP will be set for ShadowSocks, Glorytun, OpenVPN and MLVPN"
|
||||||
|
msgstr ""
|
||||||
|
"L'IP du serveur sera configuré pour ShadowSocks, Glorytun, OpenVPN et MLVPN"
|
||||||
|
|
||||||
msgid "Server settings"
|
msgid "Server settings"
|
||||||
msgstr "Paramètres du serveur"
|
msgstr "Paramètres du serveur"
|
||||||
|
|
||||||
|
@ -75,9 +94,19 @@ msgstr "Mettez une IP dans le même réseau que le modem"
|
||||||
msgid "Set here IP of the modem"
|
msgid "Set here IP of the modem"
|
||||||
msgstr "Mettez ici l'IP du modem"
|
msgstr "Mettez ici l'IP du modem"
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Set the default VPN used for UDP and ICMP when ShadowSocks is enabled, for "
|
||||||
|
"all traffic if ShadowSocks is disabled."
|
||||||
|
msgstr ""
|
||||||
|
"Configure le VPN utilisé par défaut pour UDP et ICMP quand ShadowSocks est "
|
||||||
|
"activé, pour tout le trafic quand ShadowSocks est désactivé."
|
||||||
|
|
||||||
msgid "Settings Wizard"
|
msgid "Settings Wizard"
|
||||||
msgstr "Assistant de configuration"
|
msgstr "Assistant de configuration"
|
||||||
|
|
||||||
|
msgid "ShadowSocks is used for TCP"
|
||||||
|
msgstr "ShadowSocks est utilisé pour le TCP"
|
||||||
|
|
||||||
msgid "ShadowSocks key"
|
msgid "ShadowSocks key"
|
||||||
msgstr "Clef de ShadowSocks"
|
msgstr "Clef de ShadowSocks"
|
||||||
|
|
||||||
|
@ -91,4 +120,23 @@ msgid "Wizard"
|
||||||
msgstr "Assistant"
|
msgstr "Assistant"
|
||||||
|
|
||||||
msgid "You must disable DHCP on your modems and set IP in different networks."
|
msgid "You must disable DHCP on your modems and set IP in different networks."
|
||||||
msgstr "Vous devez désactiver DHCP sur vos modems et configurer leurs IP dans des réseaux différents."
|
msgstr ""
|
||||||
|
"Vous devez désactiver DHCP sur vos modems et configurer leurs IP dans des "
|
||||||
|
"réseaux différents."
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"You need to upload OpenVPN key file generated by OpenMPTCProuter VPS script "
|
||||||
|
"to use OpenVPN TCP"
|
||||||
|
msgstr ""
|
||||||
|
"Vous devez ajouter le fichier contenant la clef OpenVPN générée par le "
|
||||||
|
"script OpenMPTCProuter VPS pour utiliser OpenVPN TCP"
|
||||||
|
|
||||||
|
#~ msgid "Glorytun is used for UDP and ICMP"
|
||||||
|
#~ msgstr "Glorytun est utilisé pour UDP et ICMP"
|
||||||
|
|
||||||
|
#~ msgid "MLVPN can replace Glorytun with connection with same latency"
|
||||||
|
#~ msgstr ""
|
||||||
|
#~ "MLVPN peut remplacer Glorytun pour les connexions avec la même latence"
|
||||||
|
|
||||||
|
#~ msgid "Server IP will be set for ShadowSocks, Glorytun and MLVPN"
|
||||||
|
#~ msgstr "L'IP du serveur sera configuré pour ShadowSocks, Glorytun et MLVPN"
|
||||||
|
|
|
@ -7,12 +7,18 @@ msgstr ""
|
||||||
msgid "Advanced Settings"
|
msgid "Advanced Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Default VPN"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Disable IPv6"
|
msgid "Disable IPv6"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Glorytun TCP is used by default for UDP and ICMP"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Glorytun key"
|
msgid "Glorytun key"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -31,6 +37,12 @@ msgstr ""
|
||||||
msgid "Interfaces settings"
|
msgid "Interfaces settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "MLVPN can replace Glorytun with connections with same latency"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "MLVPN password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Maximum scaling CPU frequency"
|
msgid "Maximum scaling CPU frequency"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -46,6 +58,9 @@ msgstr ""
|
||||||
msgid "OpenMPTCProuter"
|
msgid "OpenMPTCProuter"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "OpenVPN key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Put the values given by OpenMPTCProuter VPS script."
|
msgid "Put the values given by OpenMPTCProuter VPS script."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -55,6 +70,9 @@ msgstr ""
|
||||||
msgid "Server IP"
|
msgid "Server IP"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Server IP will be set for ShadowSocks, Glorytun, OpenVPN and MLVPN"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Server settings"
|
msgid "Server settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -64,9 +82,17 @@ msgstr ""
|
||||||
msgid "Set here IP of the modem"
|
msgid "Set here IP of the modem"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"Set the default VPN used for UDP and ICMP when ShadowSocks is enabled, for "
|
||||||
|
"all traffic if ShadowSocks is disabled."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Settings Wizard"
|
msgid "Settings Wizard"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "ShadowSocks is used for TCP"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "ShadowSocks key"
|
msgid "ShadowSocks key"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -81,3 +107,8 @@ msgstr ""
|
||||||
|
|
||||||
msgid "You must disable DHCP on your modems and set IP in different networks."
|
msgid "You must disable DHCP on your modems and set IP in different networks."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid ""
|
||||||
|
"You need to upload OpenVPN key file generated by OpenMPTCProuter VPS script "
|
||||||
|
"to use OpenVPN TCP"
|
||||||
|
msgstr ""
|
||||||
|
|
5
luci-app-openmptcprouter/root/bin/omr-ip-intf
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
checkip=$(dig +short A ip.openmptcprouter.com | tr -d "\n")
|
||||||
|
ipset add ss_rules_dst_bypass $checkip > /dev/null 2>&1
|
||||||
|
curl -s -4 -m 3 --interface $1 http://ip.openmptcprouter.com
|
||||||
|
ipset del ss_rules_dst_bypass $checkip > /dev/null 2>&1
|
5
luci-app-openmptcprouter/root/bin/omr-mptcp-intf
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
multipathip=$(dig +short A multipath-tcp.org | tr -d "\n")
|
||||||
|
ipset add ss_rules_dst_bypass $multipathip > /dev/null 2>&1
|
||||||
|
curl -s -4 -m 3 --interface $1 http://www.multipath-tcp.org
|
||||||
|
ipset del ss_rules_dst_bypass $multipathip > /dev/null 2>&1
|
|
@ -27,6 +27,7 @@ start_service() {
|
||||||
}
|
}
|
||||||
# remove sysctl already defined in /etc/sysctl.d/
|
# remove sysctl already defined in /etc/sysctl.d/
|
||||||
sed -i -e '/tcp_fin_timeout/d' -e '/tcp_keepalive_time/d' -e '/nf_conntrack_max/d' /etc/sysctl.conf
|
sed -i -e '/tcp_fin_timeout/d' -e '/tcp_keepalive_time/d' -e '/nf_conntrack_max/d' /etc/sysctl.conf
|
||||||
|
sed -i -e '/tcp_fin_timeout/d' -e '/tcp_keepalive_time/d' -e '/nf_conntrack_max/d' /etc/sysctl.d/10-default.conf
|
||||||
}
|
}
|
||||||
|
|
||||||
reload_service() {
|
reload_service() {
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
net.ipv4.tcp_keepalive_time = 1200
|
net.ipv4.tcp_keepalive_time=7200
|
||||||
net.ipv6.conf.all.disable_ipv6 = 0
|
net.ipv6.conf.all.disable_ipv6=0
|
||||||
|
net.ipv4.tcp_fin_timeout=40
|
|
@ -4,4 +4,20 @@ uci -q batch <<-EOF
|
||||||
add ucitrack openmptcprouter
|
add ucitrack openmptcprouter
|
||||||
set ucitrack.@openmptcprouter[-1].init=openmptcprouter
|
set ucitrack.@openmptcprouter[-1].init=openmptcprouter
|
||||||
commit ucitrack
|
commit ucitrack
|
||||||
EOF
|
EOF
|
||||||
|
if [ "$(uci -q get qos.serverin)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF
|
||||||
|
set qos.serverin=classify
|
||||||
|
set qos.serverin.target='Priority'
|
||||||
|
set qos.serverout=classify
|
||||||
|
set qos.serverout.target='Priority'
|
||||||
|
commit qos
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
if [ "$(uci -q get qos.serverin.target)" = "" ]; then
|
||||||
|
uci -q batch <<-EOF
|
||||||
|
set qos.serverin.target='Priority'
|
||||||
|
set qos.serverout.target='Priority'
|
||||||
|
commit qos
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
|
@ -7,6 +7,7 @@ local ut = require("luci.util")
|
||||||
local sys = require("luci.sys")
|
local sys = require("luci.sys")
|
||||||
local ds = require("luci.dispatcher")
|
local ds = require("luci.dispatcher")
|
||||||
local nw = require("luci.model.network")
|
local nw = require("luci.model.network")
|
||||||
|
local ucic = luci.model.uci.cursor()
|
||||||
nw.init()
|
nw.init()
|
||||||
module("luci.model.shadowsocks-libev", function(m)
|
module("luci.model.shadowsocks-libev", function(m)
|
||||||
setmetatable(m, {__index=function (self, k)
|
setmetatable(m, {__index=function (self, k)
|
||||||
|
@ -24,7 +25,7 @@ function values_actions(o)
|
||||||
end
|
end
|
||||||
|
|
||||||
function values_redir(o, xmode)
|
function values_redir(o, xmode)
|
||||||
o.map.uci.foreach("shadowsocks-libev", "ss_redir", function(sdata)
|
ucic:foreach("shadowsocks-libev", "ss_redir", function(sdata)
|
||||||
local disabled = ucival_to_bool(sdata["disabled"])
|
local disabled = ucival_to_bool(sdata["disabled"])
|
||||||
local sname = sdata[".name"]
|
local sname = sdata[".name"]
|
||||||
local mode = sdata["mode"] or "tcp_only"
|
local mode = sdata["mode"] or "tcp_only"
|
||||||
|
@ -38,7 +39,7 @@ function values_redir(o, xmode)
|
||||||
end
|
end
|
||||||
|
|
||||||
function values_serverlist(o)
|
function values_serverlist(o)
|
||||||
o.map.uci.foreach("shadowsocks-libev", "server", function(sdata)
|
ucic:foreach("shadowsocks-libev", "server", function(sdata)
|
||||||
local sname = sdata[".name"]
|
local sname = sdata[".name"]
|
||||||
local server = sdata["server"]
|
local server = sdata["server"]
|
||||||
local server_port = sdata["server_port"]
|
local server_port = sdata["server_port"]
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
<div class="cbi-section-create cbi-tblsection-create">
|
<div class="cbi-section-create cbi-tblsection-create">
|
||||||
<br />
|
<div>
|
||||||
<table class="cbi-section-table">
|
<select class="cbi-input-select" id="_newinst.type" name="_newinst.type">
|
||||||
<tr class="cbi-section-table-row">
|
<option value="_dummy">-- instance type --</option>
|
||||||
<td class="cbi-section-table-cell" style="width:140px">
|
<option value="ss_local">ss-local</option>
|
||||||
<select class="cbi-input-select" id="_newinst.type" name="_newinst.type">
|
<option value="ss_tunnel">ss-tunnel</option>
|
||||||
<option value="_dummy">-- instance type --</option>
|
<option value="ss_redir">ss-redir</option>
|
||||||
<option value="ss_local">ss-local</option>
|
<option value="ss_server">ss-server</option>
|
||||||
<option value="ss_tunnel">ss-tunnel</option>
|
</select>
|
||||||
<option value="ss_redir">ss-redir</option>
|
</div>
|
||||||
<option value="ss_server">ss-server</option>
|
<div>
|
||||||
</select>
|
<input type="text" class="cbi-input-text" id="_newinst.name" name="_newinst.name" placeholder="<%:Name%>"/>
|
||||||
</td>
|
</div>
|
||||||
<td class="cbi-section-table-cell" style="width:110px">
|
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>" value="<%:Add%>" />
|
||||||
<input type="text" class="cbi-input-text" id="_newinst.name" name="_newinst.name" placeholder="<%:Name%>"/>
|
|
||||||
</td>
|
|
||||||
<td class="cbi-section-table-cell left">
|
|
||||||
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>" value="<%:Add%>" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">//<![CDATA[
|
<script type="text/javascript">//<![CDATA[
|
||||||
XHR.poll(5, '<%=url('admin/services/shadowsocks-libev/status')%>', null,
|
XHR.poll(5, '<%=url('admin/services/shadowsocks-libev/status')%>', null,
|
||||||
|
|
|
@ -14,12 +14,13 @@ LUCI_BASENAME:=base
|
||||||
LUCI_TITLE:=LuCI core libraries
|
LUCI_TITLE:=LuCI core libraries
|
||||||
LUCI_DEPENDS:=+lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +luci-lib-jsonc +liblucihttp-lua
|
LUCI_DEPENDS:=+lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +luci-lib-jsonc +liblucihttp-lua
|
||||||
|
|
||||||
PKG_SOURCE:=LuaSrcDiet-0.12.1.tar.bz2
|
|
||||||
PKG_SOURCE_URL:=https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/luasrcdiet
|
|
||||||
PKG_HASH:=ed7680f2896269ae8633756e7edcf09050812f78c8f49e280e63c30d14f35aea
|
|
||||||
PKG_LICENSE:=Apache-2.0
|
|
||||||
|
|
||||||
HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/LuaSrcDiet-0.12.1
|
PKG_SOURCE:=v1.0.0.tar.gz
|
||||||
|
PKG_SOURCE_URL:=https://github.com/jirutka/luasrcdiet/archive/
|
||||||
|
PKG_HASH:=48162e63e77d009f5848f18a5cabffbdfc867d0e5e73c6d407f6af5d6880151b
|
||||||
|
PKG_LICENSE:=MIT
|
||||||
|
|
||||||
|
HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/luasrcdiet-1.0.0
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/host-build.mk
|
include $(INCLUDE_DIR)/host-build.mk
|
||||||
|
|
||||||
|
@ -36,13 +37,14 @@ endef
|
||||||
|
|
||||||
define Host/Compile
|
define Host/Compile
|
||||||
$(MAKE) -C src/ clean po2lmo
|
$(MAKE) -C src/ clean po2lmo
|
||||||
$(MAKE) -C $(HOST_BUILD_DIR) bin/LuaSrcDiet.lua
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Host/Install
|
define Host/Install
|
||||||
$(INSTALL_DIR) $(1)/bin
|
$(INSTALL_DIR) $(1)/bin
|
||||||
|
$(INSTALL_DIR) $(1)/lib/lua/5.1
|
||||||
$(INSTALL_BIN) src/po2lmo $(1)/bin/po2lmo
|
$(INSTALL_BIN) src/po2lmo $(1)/bin/po2lmo
|
||||||
$(INSTALL_BIN) $(HOST_BUILD_DIR)/bin/LuaSrcDiet.lua $(1)/bin/LuaSrcDiet
|
$(INSTALL_BIN) $(HOST_BUILD_DIR)/bin/luasrcdiet $(1)/bin/luasrcdiet
|
||||||
|
$(CP) $(HOST_BUILD_DIR)/luasrcdiet $(1)/lib/lua/5.1/
|
||||||
endef
|
endef
|
||||||
|
|
||||||
$(eval $(call HostBuild))
|
$(eval $(call HostBuild))
|
||||||
|
|
|
@ -465,31 +465,16 @@ function cbi_d_add(field, dep, index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cbi_d_checkvalue(target, ref) {
|
function cbi_d_checkvalue(target, ref) {
|
||||||
var t = document.getElementById(target);
|
var value = null,
|
||||||
var value;
|
query = 'input[id="'+target+'"], input[name="'+target+'"], ' +
|
||||||
|
'select[id="'+target+'"], select[name="'+target+'"]';
|
||||||
|
|
||||||
if (!t) {
|
document.querySelectorAll(query).forEach(function(i) {
|
||||||
var tl = document.getElementsByName(target);
|
if (value === null && ((i.type !== 'radio' && i.type !== 'checkbox') || i.checked === true))
|
||||||
|
value = i.value;
|
||||||
|
});
|
||||||
|
|
||||||
if( tl.length > 0 && (tl[0].type == 'radio' || tl[0].type == 'checkbox'))
|
return (((value !== null) ? value : "") == ref);
|
||||||
for( var i = 0; i < tl.length; i++ )
|
|
||||||
if( tl[i].checked ) {
|
|
||||||
value = tl[i].value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value ? value : "";
|
|
||||||
} else if (!t.value) {
|
|
||||||
value = "";
|
|
||||||
} else {
|
|
||||||
value = t.value;
|
|
||||||
|
|
||||||
if (t.type == "checkbox") {
|
|
||||||
value = t.checked ? value : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (value == ref)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cbi_d_check(deps) {
|
function cbi_d_check(deps) {
|
||||||
|
@ -634,6 +619,26 @@ function cbi_init() {
|
||||||
node.getAttribute('data-type'));
|
node.getAttribute('data-type'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.cbi-dropdown').forEach(function(s) {
|
||||||
|
cbi_dropdown_init(s);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.cbi-tooltip:not(:empty)').forEach(function(s) {
|
||||||
|
s.parentNode.classList.add('cbi-tooltip-container');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.cbi-section-remove > input[name^="cbi.rts"]').forEach(function(i) {
|
||||||
|
var handler = function(ev) {
|
||||||
|
var bits = this.name.split(/\./),
|
||||||
|
section = document.getElementById('cbi-' + bits[2] + '-' + bits[3]);
|
||||||
|
|
||||||
|
section.style.opacity = (ev.type === 'mouseover') ? 0.5 : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
i.addEventListener('mouseover', handler);
|
||||||
|
i.addEventListener('mouseout', handler);
|
||||||
|
});
|
||||||
|
|
||||||
cbi_d_update();
|
cbi_d_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,9 +830,9 @@ function cbi_dynlist_init(parent, datatype, optional, choices)
|
||||||
t.placeholder = holder;
|
t.placeholder = holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
var b = document.createElement('img');
|
var b = E('div', {
|
||||||
b.src = cbi_strings.path.resource + ((i+1) < values.length ? '/cbi/remove.gif' : '/cbi/add.gif');
|
class: 'cbi-button cbi-button-' + ((i+1) < values.length ? 'remove' : 'add')
|
||||||
b.className = 'cbi-image-button';
|
}, (i+1) < values.length ? '×' : '+');
|
||||||
|
|
||||||
parent.appendChild(t);
|
parent.appendChild(t);
|
||||||
parent.appendChild(b);
|
parent.appendChild(b);
|
||||||
|
@ -993,8 +998,7 @@ function cbi_dynlist_init(parent, datatype, optional, choices)
|
||||||
input = input.previousSibling;
|
input = input.previousSibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (se.src.indexOf('remove') > -1)
|
if (se.classList.contains('cbi-button-remove')) {
|
||||||
{
|
|
||||||
input.value = '';
|
input.value = '';
|
||||||
|
|
||||||
cbi_dynlist_keydown({
|
cbi_dynlist_keydown({
|
||||||
|
@ -1002,8 +1006,7 @@ function cbi_dynlist_init(parent, datatype, optional, choices)
|
||||||
keyCode: 8
|
keyCode: 8
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
cbi_dynlist_keydown({
|
cbi_dynlist_keydown({
|
||||||
target: input,
|
target: input,
|
||||||
keyCode: 13
|
keyCode: 13
|
||||||
|
@ -1243,51 +1246,53 @@ function cbi_validate_field(cbid, optional, type)
|
||||||
|
|
||||||
function cbi_row_swap(elem, up, store)
|
function cbi_row_swap(elem, up, store)
|
||||||
{
|
{
|
||||||
var tr = elem.parentNode;
|
var tr = findParent(elem.parentNode, '.cbi-section-table-row');
|
||||||
while (tr && tr.nodeName.toLowerCase() != 'tr')
|
|
||||||
tr = tr.parentNode;
|
|
||||||
|
|
||||||
if (!tr)
|
if (!tr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var table = tr.parentNode;
|
tr.classList.remove('flash');
|
||||||
while (table && table.nodeName.toLowerCase() != 'table')
|
|
||||||
table = table.parentNode;
|
|
||||||
|
|
||||||
if (!table)
|
if (up) {
|
||||||
return false;
|
var prev = tr.previousElementSibling;
|
||||||
|
|
||||||
var s = up ? 3 : 2;
|
if (prev && prev.classList.contains('cbi-section-table-row'))
|
||||||
var e = up ? table.rows.length : table.rows.length - 1;
|
tr.parentNode.insertBefore(tr, prev);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var next = tr.nextElementSibling ? tr.nextElementSibling.nextElementSibling : null;
|
||||||
|
|
||||||
for (var idx = s; idx < e; idx++)
|
if (next && next.classList.contains('cbi-section-table-row'))
|
||||||
{
|
tr.parentNode.insertBefore(tr, next);
|
||||||
if (table.rows[idx] == tr)
|
else if (!next)
|
||||||
{
|
tr.parentNode.appendChild(tr);
|
||||||
if (up)
|
else
|
||||||
tr.parentNode.insertBefore(table.rows[idx], table.rows[idx-1]);
|
return;
|
||||||
else
|
|
||||||
tr.parentNode.insertBefore(table.rows[idx+1], table.rows[idx]);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids = [ ];
|
var ids = [ ];
|
||||||
for (idx = 2; idx < table.rows.length; idx++)
|
|
||||||
{
|
|
||||||
table.rows[idx].className = table.rows[idx].className.replace(
|
|
||||||
/cbi-rowstyle-[12]/, 'cbi-rowstyle-' + (1 + (idx % 2))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (table.rows[idx].id && table.rows[idx].id.match(/-([^\-]+)$/) )
|
for (var i = 0, n = 0; i < tr.parentNode.childNodes.length; i++) {
|
||||||
ids.push(RegExp.$1);
|
var node = tr.parentNode.childNodes[i];
|
||||||
|
if (node.classList && node.classList.contains('cbi-section-table-row')) {
|
||||||
|
node.classList.remove('cbi-rowstyle-1');
|
||||||
|
node.classList.remove('cbi-rowstyle-2');
|
||||||
|
node.classList.add((n++ % 2) ? 'cbi-rowstyle-2' : 'cbi-rowstyle-1');
|
||||||
|
|
||||||
|
if (/-([^\-]+)$/.test(node.id))
|
||||||
|
ids.push(RegExp.$1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var input = document.getElementById(store);
|
var input = document.getElementById(store);
|
||||||
if (input)
|
if (input)
|
||||||
input.value = ids.join(' ');
|
input.value = ids.join(' ');
|
||||||
|
|
||||||
|
window.scrollTo(0, tr.offsetTop);
|
||||||
|
window.setTimeout(function() { tr.classList.add('flash'); }, 1);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1311,56 +1316,26 @@ function cbi_tag_last(container)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String.prototype.serialize = function()
|
function cbi_submit(elem, name, value, action)
|
||||||
{
|
{
|
||||||
var o = this;
|
var form = elem.form || findParent(elem, 'form');
|
||||||
switch(typeof(o))
|
|
||||||
{
|
|
||||||
case 'object':
|
|
||||||
// null
|
|
||||||
if( o == null )
|
|
||||||
{
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
// array
|
if (!form)
|
||||||
else if( o.length )
|
return false;
|
||||||
{
|
|
||||||
var i, s = '';
|
|
||||||
|
|
||||||
for( var i = 0; i < o.length; i++ )
|
if (action)
|
||||||
s += (s ? ', ' : '') + String.serialize(o[i]);
|
form.action = action;
|
||||||
|
|
||||||
return '[ ' + s + ' ]';
|
if (name) {
|
||||||
}
|
var hidden = form.querySelector('input[type="hidden"][name="%s"]'.format(name)) ||
|
||||||
|
E('input', { type: 'hidden', name: name });
|
||||||
|
|
||||||
// object
|
hidden.value = value || '1';
|
||||||
else
|
form.appendChild(hidden);
|
||||||
{
|
|
||||||
var k, s = '';
|
|
||||||
|
|
||||||
for( k in o )
|
|
||||||
s += (s ? ', ' : '') + k + ': ' + String.serialize(o[k]);
|
|
||||||
|
|
||||||
return '{ ' + s + ' }';
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'string':
|
|
||||||
// complex string
|
|
||||||
if( o.match(/[^a-zA-Z0-9_,.: -]/) )
|
|
||||||
return 'decodeURIComponent("' + encodeURIComponent(o) + '")';
|
|
||||||
|
|
||||||
// simple string
|
|
||||||
else
|
|
||||||
return '"' + o + '"';
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return o.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.submit();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String.prototype.format = function()
|
String.prototype.format = function()
|
||||||
|
@ -1473,10 +1448,6 @@ String.prototype.format = function()
|
||||||
subst = esc(param, quot_esc);
|
subst = esc(param, quot_esc);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'j':
|
|
||||||
subst = String.serialize(param);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
var td = 0;
|
var td = 0;
|
||||||
var th = 0;
|
var th = 0;
|
||||||
|
@ -1543,14 +1514,6 @@ String.prototype.nobr = function()
|
||||||
return this.replace(/[\s\n]+/g, ' ');
|
return this.replace(/[\s\n]+/g, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
String.serialize = function()
|
|
||||||
{
|
|
||||||
var a = [ ];
|
|
||||||
for (var i = 1; i < arguments.length; i++)
|
|
||||||
a.push(arguments[i]);
|
|
||||||
return ''.serialize.apply(arguments[0], a);
|
|
||||||
}
|
|
||||||
|
|
||||||
String.format = function()
|
String.format = function()
|
||||||
{
|
{
|
||||||
var a = [ ];
|
var a = [ ];
|
||||||
|
@ -1566,3 +1529,640 @@ String.nobr = function()
|
||||||
a.push(arguments[i]);
|
a.push(arguments[i]);
|
||||||
return ''.nobr.apply(arguments[0], a);
|
return ''.nobr.apply(arguments[0], a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window.NodeList && !NodeList.prototype.forEach) {
|
||||||
|
NodeList.prototype.forEach = function (callback, thisArg) {
|
||||||
|
thisArg = thisArg || window;
|
||||||
|
for (var i = 0; i < this.length; i++) {
|
||||||
|
callback.call(thisArg, this[i], i, this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var dummyElem, domParser;
|
||||||
|
|
||||||
|
function isElem(e)
|
||||||
|
{
|
||||||
|
return (typeof(e) === 'object' && e !== null && 'nodeType' in e);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toElem(s)
|
||||||
|
{
|
||||||
|
var elem;
|
||||||
|
|
||||||
|
try {
|
||||||
|
domParser = domParser || new DOMParser();
|
||||||
|
elem = domParser.parseFromString(s, 'text/html').body.firstChild;
|
||||||
|
}
|
||||||
|
catch(e) {}
|
||||||
|
|
||||||
|
if (!elem) {
|
||||||
|
try {
|
||||||
|
dummyElem = dummyElem || document.createElement('div');
|
||||||
|
dummyElem.innerHTML = s;
|
||||||
|
elem = dummyElem.firstChild;
|
||||||
|
}
|
||||||
|
catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return elem || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findParent(node, selector)
|
||||||
|
{
|
||||||
|
while (node)
|
||||||
|
if (node.msMatchesSelector && node.msMatchesSelector(selector))
|
||||||
|
return node;
|
||||||
|
else if (node.matches && node.matches(selector))
|
||||||
|
return node;
|
||||||
|
else
|
||||||
|
node = node.parentNode;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function E()
|
||||||
|
{
|
||||||
|
var html = arguments[0],
|
||||||
|
attr = (arguments[1] instanceof Object && !Array.isArray(arguments[1])) ? arguments[1] : null,
|
||||||
|
data = attr ? arguments[2] : arguments[1],
|
||||||
|
elem;
|
||||||
|
|
||||||
|
if (isElem(html))
|
||||||
|
elem = html;
|
||||||
|
else if (html.charCodeAt(0) === 60)
|
||||||
|
elem = toElem(html);
|
||||||
|
else
|
||||||
|
elem = document.createElement(html);
|
||||||
|
|
||||||
|
if (!elem)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (attr)
|
||||||
|
for (var key in attr)
|
||||||
|
if (attr.hasOwnProperty(key) && attr[key] !== null && attr[key] !== undefined)
|
||||||
|
elem.setAttribute(key, attr[key]);
|
||||||
|
|
||||||
|
if (typeof(data) === 'function')
|
||||||
|
data = data(elem);
|
||||||
|
|
||||||
|
if (isElem(data)) {
|
||||||
|
elem.appendChild(data);
|
||||||
|
}
|
||||||
|
else if (Array.isArray(data)) {
|
||||||
|
for (var i = 0; i < data.length; i++)
|
||||||
|
if (isElem(data[i]))
|
||||||
|
elem.appendChild(data[i]);
|
||||||
|
else
|
||||||
|
elem.appendChild(document.createTextNode('' + data[i]));
|
||||||
|
}
|
||||||
|
else if (data !== null && data !== undefined) {
|
||||||
|
elem.innerHTML = '' + data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof(window.CustomEvent) !== 'function') {
|
||||||
|
function CustomEvent(event, params) {
|
||||||
|
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
||||||
|
var evt = document.createEvent('CustomEvent');
|
||||||
|
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomEvent.prototype = window.Event.prototype;
|
||||||
|
window.CustomEvent = CustomEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
CBIDropdown = {
|
||||||
|
openDropdown: function(sb) {
|
||||||
|
var st = window.getComputedStyle(sb, null),
|
||||||
|
ul = sb.querySelector('ul'),
|
||||||
|
li = ul.querySelectorAll('li'),
|
||||||
|
sel = ul.querySelector('[selected]'),
|
||||||
|
rect = sb.getBoundingClientRect(),
|
||||||
|
h = sb.clientHeight - parseFloat(st.paddingTop) - parseFloat(st.paddingBottom),
|
||||||
|
mh = this.dropdown_items * h,
|
||||||
|
eh = Math.min(mh, li.length * h);
|
||||||
|
|
||||||
|
document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
|
||||||
|
s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
|
||||||
|
});
|
||||||
|
|
||||||
|
ul.style.maxHeight = mh + 'px';
|
||||||
|
sb.setAttribute('open', '');
|
||||||
|
|
||||||
|
ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
|
||||||
|
ul.querySelectorAll('[selected] input[type="checkbox"]').forEach(function(c) {
|
||||||
|
c.checked = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ul.style.top = ul.style.bottom = '';
|
||||||
|
ul.style[((sb.getBoundingClientRect().top + eh) > window.innerHeight) ? 'bottom' : 'top'] = rect.height + 'px';
|
||||||
|
ul.classList.add('dropdown');
|
||||||
|
|
||||||
|
var pv = ul.cloneNode(true);
|
||||||
|
pv.classList.remove('dropdown');
|
||||||
|
pv.classList.add('preview');
|
||||||
|
|
||||||
|
sb.insertBefore(pv, ul.nextElementSibling);
|
||||||
|
|
||||||
|
li.forEach(function(l) {
|
||||||
|
l.setAttribute('tabindex', 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.lastElementChild.setAttribute('tabindex', 0);
|
||||||
|
|
||||||
|
this.setFocus(sb, sel || li[0], true);
|
||||||
|
},
|
||||||
|
|
||||||
|
closeDropdown: function(sb, no_focus) {
|
||||||
|
if (!sb.hasAttribute('open'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pv = sb.querySelector('ul.preview'),
|
||||||
|
ul = sb.querySelector('ul.dropdown'),
|
||||||
|
li = ul.querySelectorAll('li');
|
||||||
|
|
||||||
|
li.forEach(function(l) { l.removeAttribute('tabindex'); });
|
||||||
|
sb.lastElementChild.removeAttribute('tabindex');
|
||||||
|
|
||||||
|
sb.removeChild(pv);
|
||||||
|
sb.removeAttribute('open');
|
||||||
|
sb.style.width = sb.style.height = '';
|
||||||
|
|
||||||
|
ul.classList.remove('dropdown');
|
||||||
|
|
||||||
|
if (!no_focus)
|
||||||
|
this.setFocus(sb, sb);
|
||||||
|
|
||||||
|
this.saveValues(sb, ul);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleItem: function(sb, li, force_state) {
|
||||||
|
if (li.hasAttribute('unselectable'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.multi) {
|
||||||
|
var cbox = li.querySelector('input[type="checkbox"]'),
|
||||||
|
items = li.parentNode.querySelectorAll('li'),
|
||||||
|
label = sb.querySelector('ul.preview'),
|
||||||
|
sel = li.parentNode.querySelectorAll('[selected]').length,
|
||||||
|
more = sb.querySelector('.more'),
|
||||||
|
ndisplay = this.display_items,
|
||||||
|
n = 0;
|
||||||
|
|
||||||
|
if (li.hasAttribute('selected')) {
|
||||||
|
if (force_state !== true) {
|
||||||
|
if (sel > 1 || this.optional) {
|
||||||
|
li.removeAttribute('selected');
|
||||||
|
cbox.checked = cbox.disabled = false;
|
||||||
|
sel--;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cbox.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (force_state !== false) {
|
||||||
|
li.setAttribute('selected', '');
|
||||||
|
cbox.checked = true;
|
||||||
|
cbox.disabled = false;
|
||||||
|
sel++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (label.firstElementChild)
|
||||||
|
label.removeChild(label.firstElementChild);
|
||||||
|
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
items[i].removeAttribute('display');
|
||||||
|
if (items[i].hasAttribute('selected')) {
|
||||||
|
if (ndisplay-- > 0) {
|
||||||
|
items[i].setAttribute('display', n++);
|
||||||
|
label.appendChild(items[i].cloneNode(true));
|
||||||
|
}
|
||||||
|
var c = items[i].querySelector('input[type="checkbox"]');
|
||||||
|
if (c)
|
||||||
|
c.disabled = (sel == 1 && !this.optional);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ndisplay < 0)
|
||||||
|
sb.setAttribute('more', '');
|
||||||
|
else
|
||||||
|
sb.removeAttribute('more');
|
||||||
|
|
||||||
|
if (ndisplay === this.display_items)
|
||||||
|
sb.setAttribute('empty', '');
|
||||||
|
else
|
||||||
|
sb.removeAttribute('empty');
|
||||||
|
|
||||||
|
more.innerHTML = (ndisplay === this.display_items) ? this.placeholder : '···';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var sel = li.parentNode.querySelector('[selected]');
|
||||||
|
if (sel) {
|
||||||
|
sel.removeAttribute('display');
|
||||||
|
sel.removeAttribute('selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
li.setAttribute('display', 0);
|
||||||
|
li.setAttribute('selected', '');
|
||||||
|
|
||||||
|
this.closeDropdown(sb, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveValues(sb, li.parentNode);
|
||||||
|
},
|
||||||
|
|
||||||
|
transformItem: function(sb, li) {
|
||||||
|
var cbox = E('form', {}, E('input', { type: 'checkbox', tabindex: -1, onclick: 'event.preventDefault()' })),
|
||||||
|
label = E('label');
|
||||||
|
|
||||||
|
while (li.firstChild)
|
||||||
|
label.appendChild(li.firstChild);
|
||||||
|
|
||||||
|
li.appendChild(cbox);
|
||||||
|
li.appendChild(label);
|
||||||
|
},
|
||||||
|
|
||||||
|
saveValues: function(sb, ul) {
|
||||||
|
var sel = ul.querySelectorAll('[selected]'),
|
||||||
|
div = sb.lastElementChild;
|
||||||
|
|
||||||
|
while (div.lastElementChild)
|
||||||
|
div.removeChild(div.lastElementChild);
|
||||||
|
|
||||||
|
sel.forEach(function (s) {
|
||||||
|
div.appendChild(E('input', {
|
||||||
|
type: 'hidden',
|
||||||
|
name: s.hasAttribute('name') ? s.getAttribute('name') : (sb.getAttribute('name') || ''),
|
||||||
|
value: s.hasAttribute('value') ? s.getAttribute('value') : s.innerText
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
cbi_d_update();
|
||||||
|
},
|
||||||
|
|
||||||
|
setFocus: function(sb, elem, scroll) {
|
||||||
|
if (sb && sb.hasAttribute && sb.hasAttribute('locked-in'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
document.querySelectorAll('.focus').forEach(function(e) {
|
||||||
|
if (e.nodeName.toLowerCase() !== 'input') {
|
||||||
|
e.classList.remove('focus');
|
||||||
|
e.blur();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (elem) {
|
||||||
|
elem.focus();
|
||||||
|
elem.classList.add('focus');
|
||||||
|
|
||||||
|
if (scroll)
|
||||||
|
elem.parentNode.scrollTop = elem.offsetTop - elem.parentNode.offsetTop;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
createItems: function(sb, value) {
|
||||||
|
var sbox = this,
|
||||||
|
val = (value || '').trim().split(/\s+/),
|
||||||
|
ul = sb.querySelector('ul');
|
||||||
|
|
||||||
|
if (!sbox.multi)
|
||||||
|
val.length = Math.min(val.length, 1);
|
||||||
|
|
||||||
|
val.forEach(function(item) {
|
||||||
|
var new_item = null;
|
||||||
|
|
||||||
|
ul.childNodes.forEach(function(li) {
|
||||||
|
if (li.getAttribute && li.getAttribute('value') === item)
|
||||||
|
new_item = li;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!new_item) {
|
||||||
|
var markup,
|
||||||
|
tpl = sb.querySelector(sbox.template);
|
||||||
|
|
||||||
|
if (tpl)
|
||||||
|
markup = (tpl.textContent || tpl.innerHTML || tpl.firstChild.data).replace(/^<!--|-->$/, '').trim();
|
||||||
|
else
|
||||||
|
markup = '<li value="{{value}}">{{value}}</li>';
|
||||||
|
|
||||||
|
new_item = E(markup.replace(/{{value}}/g, item));
|
||||||
|
|
||||||
|
if (sbox.multi) {
|
||||||
|
sbox.transformItem(sb, new_item);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var old = ul.querySelector('li[created]');
|
||||||
|
if (old)
|
||||||
|
ul.removeChild(old);
|
||||||
|
|
||||||
|
new_item.setAttribute('created', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
new_item = ul.insertBefore(new_item, ul.lastElementChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
sbox.toggleItem(sb, new_item, true);
|
||||||
|
sbox.setFocus(sb, new_item, true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
closeAllDropdowns: function() {
|
||||||
|
document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
|
||||||
|
s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function cbi_dropdown_init(sb) {
|
||||||
|
if (!(this instanceof cbi_dropdown_init))
|
||||||
|
return new cbi_dropdown_init(sb);
|
||||||
|
|
||||||
|
this.multi = sb.hasAttribute('multiple');
|
||||||
|
this.optional = sb.hasAttribute('optional');
|
||||||
|
this.placeholder = sb.getAttribute('placeholder') || '---';
|
||||||
|
this.display_items = parseInt(sb.getAttribute('display-items') || 3);
|
||||||
|
this.dropdown_items = parseInt(sb.getAttribute('dropdown-items') || 5);
|
||||||
|
this.create = sb.getAttribute('item-create') || '.create-item-input';
|
||||||
|
this.template = sb.getAttribute('item-template') || 'script[type="item-template"]';
|
||||||
|
|
||||||
|
var sbox = this,
|
||||||
|
ul = sb.querySelector('ul'),
|
||||||
|
items = ul.querySelectorAll('li'),
|
||||||
|
more = sb.appendChild(E('span', { class: 'more', tabindex: -1 }, '···')),
|
||||||
|
open = sb.appendChild(E('span', { class: 'open', tabindex: -1 }, '▾')),
|
||||||
|
canary = sb.appendChild(E('div')),
|
||||||
|
create = sb.querySelector(this.create),
|
||||||
|
ndisplay = this.display_items,
|
||||||
|
n = 0;
|
||||||
|
|
||||||
|
if (this.multi) {
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
sbox.transformItem(sb, items[i]);
|
||||||
|
|
||||||
|
if (items[i].hasAttribute('selected') && ndisplay-- > 0)
|
||||||
|
items[i].setAttribute('display', n++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var sel = sb.querySelectorAll('[selected]');
|
||||||
|
|
||||||
|
sel.forEach(function(s) {
|
||||||
|
s.removeAttribute('selected');
|
||||||
|
});
|
||||||
|
|
||||||
|
var s = sel[0] || items[0];
|
||||||
|
if (s) {
|
||||||
|
s.setAttribute('selected', '');
|
||||||
|
s.setAttribute('display', n++);
|
||||||
|
}
|
||||||
|
|
||||||
|
ndisplay--;
|
||||||
|
|
||||||
|
if (this.optional && !ul.querySelector('li[value=""]')) {
|
||||||
|
var placeholder = E('li', { placeholder: '' }, this.placeholder);
|
||||||
|
ul.firstChild ? ul.insertBefore(placeholder, ul.firstChild) : ul.appendChild(placeholder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sbox.saveValues(sb, ul);
|
||||||
|
|
||||||
|
ul.setAttribute('tabindex', -1);
|
||||||
|
sb.setAttribute('tabindex', 0);
|
||||||
|
|
||||||
|
if (ndisplay < 0)
|
||||||
|
sb.setAttribute('more', '')
|
||||||
|
else
|
||||||
|
sb.removeAttribute('more');
|
||||||
|
|
||||||
|
if (ndisplay === this.display_items)
|
||||||
|
sb.setAttribute('empty', '')
|
||||||
|
else
|
||||||
|
sb.removeAttribute('empty');
|
||||||
|
|
||||||
|
more.innerHTML = (ndisplay === this.display_items) ? this.placeholder : '···';
|
||||||
|
|
||||||
|
|
||||||
|
sb.addEventListener('click', function(ev) {
|
||||||
|
if (!this.hasAttribute('open')) {
|
||||||
|
if (ev.target.nodeName.toLowerCase() !== 'input')
|
||||||
|
sbox.openDropdown(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var li = findParent(ev.target, 'li');
|
||||||
|
if (li && li.parentNode.classList.contains('dropdown'))
|
||||||
|
sbox.toggleItem(this, li);
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.addEventListener('keydown', function(ev) {
|
||||||
|
if (ev.target.nodeName.toLowerCase() === 'input')
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.hasAttribute('open')) {
|
||||||
|
switch (ev.keyCode) {
|
||||||
|
case 37:
|
||||||
|
case 38:
|
||||||
|
case 39:
|
||||||
|
case 40:
|
||||||
|
sbox.openDropdown(this);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var active = findParent(document.activeElement, 'li');
|
||||||
|
|
||||||
|
switch (ev.keyCode) {
|
||||||
|
case 27:
|
||||||
|
sbox.closeDropdown(this);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 13:
|
||||||
|
if (active) {
|
||||||
|
if (!active.hasAttribute('selected'))
|
||||||
|
sbox.toggleItem(this, active);
|
||||||
|
sbox.closeDropdown(this);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 32:
|
||||||
|
if (active) {
|
||||||
|
sbox.toggleItem(this, active);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 38:
|
||||||
|
if (active && active.previousElementSibling) {
|
||||||
|
sbox.setFocus(this, active.previousElementSibling);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 40:
|
||||||
|
if (active && active.nextElementSibling) {
|
||||||
|
sbox.setFocus(this, active.nextElementSibling);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.addEventListener('cbi-dropdown-close', function(ev) {
|
||||||
|
sbox.closeDropdown(this, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ('ontouchstart' in window) {
|
||||||
|
sb.addEventListener('touchstart', function(ev) { ev.stopPropagation(); });
|
||||||
|
window.addEventListener('touchstart', sbox.closeAllDropdowns);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sb.addEventListener('mouseover', function(ev) {
|
||||||
|
if (!this.hasAttribute('open'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var li = findParent(ev.target, 'li');
|
||||||
|
if (li) {
|
||||||
|
if (li.parentNode.classList.contains('dropdown'))
|
||||||
|
sbox.setFocus(this, li);
|
||||||
|
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.addEventListener('focus', function(ev) {
|
||||||
|
document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
|
||||||
|
if (s !== this || this.hasAttribute('open'))
|
||||||
|
s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
canary.addEventListener('focus', function(ev) {
|
||||||
|
sbox.closeDropdown(this.parentNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('mouseover', sbox.setFocus);
|
||||||
|
window.addEventListener('click', sbox.closeAllDropdowns);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (create) {
|
||||||
|
create.addEventListener('keydown', function(ev) {
|
||||||
|
switch (ev.keyCode) {
|
||||||
|
case 13:
|
||||||
|
sbox.createItems(sb, this.value);
|
||||||
|
ev.preventDefault();
|
||||||
|
this.value = '';
|
||||||
|
this.blur();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
create.addEventListener('focus', function(ev) {
|
||||||
|
var cbox = findParent(this, 'li').querySelector('input[type="checkbox"]');
|
||||||
|
if (cbox) cbox.checked = true;
|
||||||
|
sb.setAttribute('locked-in', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
create.addEventListener('blur', function(ev) {
|
||||||
|
var cbox = findParent(this, 'li').querySelector('input[type="checkbox"]');
|
||||||
|
if (cbox) cbox.checked = false;
|
||||||
|
sb.removeAttribute('locked-in');
|
||||||
|
});
|
||||||
|
|
||||||
|
var li = findParent(create, 'li');
|
||||||
|
|
||||||
|
li.setAttribute('unselectable', '');
|
||||||
|
li.addEventListener('click', function(ev) {
|
||||||
|
this.querySelector(sbox.create).focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cbi_dropdown_init.prototype = CBIDropdown;
|
||||||
|
|
||||||
|
function cbi_update_table(table, data, placeholder) {
|
||||||
|
target = isElem(table) ? table : document.querySelector(table);
|
||||||
|
|
||||||
|
if (!isElem(target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
target.querySelectorAll('.tr.table-titles, .cbi-section-table-titles').forEach(function(thead) {
|
||||||
|
var titles = [];
|
||||||
|
|
||||||
|
thead.querySelectorAll('.th').forEach(function(th) {
|
||||||
|
titles.push(th);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
var n = 0, rows = target.querySelectorAll('.tr');
|
||||||
|
|
||||||
|
data.forEach(function(row) {
|
||||||
|
var trow = E('div', { 'class': 'tr' });
|
||||||
|
|
||||||
|
for (var i = 0; i < titles.length; i++) {
|
||||||
|
var text = (titles[i].innerText || '').trim();
|
||||||
|
var td = trow.appendChild(E('div', {
|
||||||
|
'class': titles[i].className,
|
||||||
|
'data-title': (text !== '') ? text : null
|
||||||
|
}, row[i] || ''));
|
||||||
|
|
||||||
|
td.classList.remove('th');
|
||||||
|
td.classList.add('td');
|
||||||
|
}
|
||||||
|
|
||||||
|
trow.classList.add('cbi-rowstyle-%d'.format((n++ % 2) ? 2 : 1));
|
||||||
|
|
||||||
|
if (rows[n])
|
||||||
|
target.replaceChild(trow, rows[n]);
|
||||||
|
else
|
||||||
|
target.appendChild(trow);
|
||||||
|
});
|
||||||
|
|
||||||
|
while (rows[++n])
|
||||||
|
target.removeChild(rows[n]);
|
||||||
|
|
||||||
|
if (placeholder && target.firstElementChild === target.lastElementChild) {
|
||||||
|
var trow = target.appendChild(E('div', { 'class': 'tr placeholder' }));
|
||||||
|
var td = trow.appendChild(E('div', { 'class': titles[0].className }, placeholder));
|
||||||
|
|
||||||
|
td.classList.remove('th');
|
||||||
|
td.classList.add('td');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thead.parentNode.style.display = 'none';
|
||||||
|
|
||||||
|
thead.parentNode.querySelectorAll('.tr, .cbi-section-table-row').forEach(function(trow) {
|
||||||
|
if (trow !== thead) {
|
||||||
|
var n = 0;
|
||||||
|
trow.querySelectorAll('.th, .td').forEach(function(td) {
|
||||||
|
if (n < titles.length) {
|
||||||
|
var text = (titles[n++].innerText || '').trim();
|
||||||
|
if (text !== '')
|
||||||
|
td.setAttribute('data-title', text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
thead.parentNode.style.display = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelectorAll('.table').forEach(cbi_update_table);
|
||||||
|
});
|
||||||
|
|
BIN
luci-base/htdocs/luci-static/resources/icons/alias.png
Normal file
After Width: | Height: | Size: 706 B |
BIN
luci-base/htdocs/luci-static/resources/icons/alias_disabled.png
Normal file
After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 888 B |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
|
@ -43,6 +43,7 @@ XHR = function()
|
||||||
{
|
{
|
||||||
this.reinit();
|
this.reinit();
|
||||||
|
|
||||||
|
var ts = Date.now();
|
||||||
var xhr = this._xmlHttp;
|
var xhr = this._xmlHttp;
|
||||||
var code = this._encode(data);
|
var code = this._encode(data);
|
||||||
|
|
||||||
|
@ -64,15 +65,11 @@ XHR = function()
|
||||||
if (xhr.readyState == 4) {
|
if (xhr.readyState == 4) {
|
||||||
var json = null;
|
var json = null;
|
||||||
if (xhr.getResponseHeader("Content-Type") == "application/json") {
|
if (xhr.getResponseHeader("Content-Type") == "application/json") {
|
||||||
try {
|
try { json = JSON.parse(xhr.responseText); }
|
||||||
json = eval('(' + xhr.responseText + ')');
|
catch(e) { json = null; }
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
json = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(xhr, json);
|
callback(xhr, json, Date.now() - ts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,13 +80,21 @@ XHR = function()
|
||||||
{
|
{
|
||||||
this.reinit();
|
this.reinit();
|
||||||
|
|
||||||
|
var ts = Date.now();
|
||||||
var xhr = this._xmlHttp;
|
var xhr = this._xmlHttp;
|
||||||
var code = this._encode(data);
|
var code = this._encode(data);
|
||||||
|
|
||||||
xhr.onreadystatechange = function()
|
xhr.onreadystatechange = function()
|
||||||
{
|
{
|
||||||
if (xhr.readyState == 4)
|
if (xhr.readyState == 4) {
|
||||||
callback(xhr);
|
var json = null;
|
||||||
|
if (xhr.getResponseHeader("Content-Type") == "application/json") {
|
||||||
|
try { json = JSON.parse(xhr.responseText); }
|
||||||
|
catch(e) { json = null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(xhr, json, Date.now() - ts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xhr.open('POST', url, true);
|
xhr.open('POST', url, true);
|
||||||
|
@ -188,7 +193,7 @@ XHR.poll = function(interval, url, data, callback, post)
|
||||||
for (var i = 0, e = XHR._q[0]; i < XHR._q.length; e = XHR._q[++i])
|
for (var i = 0, e = XHR._q[0]; i < XHR._q.length; e = XHR._q[++i])
|
||||||
{
|
{
|
||||||
if (!(XHR._t % e.interval) && !e.xhr.busy())
|
if (!(XHR._t % e.interval) && !e.xhr.busy())
|
||||||
e.xhr[post ? 'post' : 'get'](e.url, e.data, e.callback, e.interval * 1000 - 5);
|
e.xhr[post ? 'post' : 'get'](e.url, e.data, e.callback, e.interval * 1000 * 5 - 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
XHR._t++;
|
XHR._t++;
|
||||||
|
@ -204,7 +209,6 @@ XHR.poll = function(interval, url, data, callback, post)
|
||||||
};
|
};
|
||||||
|
|
||||||
XHR._q.push(e);
|
XHR._q.push(e);
|
||||||
XHR.run();
|
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
@ -260,3 +264,5 @@ XHR.running = function()
|
||||||
{
|
{
|
||||||
return !!(XHR._r && XHR._i);
|
return !!(XHR._r && XHR._i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', XHR.run);
|
||||||
|
|
|
@ -388,21 +388,21 @@ function Map.parse(self, readinput, ...)
|
||||||
|
|
||||||
if self.save then
|
if self.save then
|
||||||
self:_run_hooks("on_save", "on_before_save")
|
self:_run_hooks("on_save", "on_before_save")
|
||||||
|
local i, config
|
||||||
for i, config in ipairs(self.parsechain) do
|
for i, config in ipairs(self.parsechain) do
|
||||||
self.uci:save(config)
|
self.uci:save(config)
|
||||||
end
|
end
|
||||||
self:_run_hooks("on_after_save")
|
self:_run_hooks("on_after_save")
|
||||||
if (not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply") then
|
if (not self.proceed and self.flow.autoapply) or luci.http.formvalue("cbi.apply") then
|
||||||
self:_run_hooks("on_before_commit")
|
self:_run_hooks("on_before_commit")
|
||||||
for i, config in ipairs(self.parsechain) do
|
if self.apply_on_parse == false then
|
||||||
self.uci:commit(config)
|
for i, config in ipairs(self.parsechain) do
|
||||||
|
self.uci:commit(config)
|
||||||
-- Refresh data because commit changes section names
|
end
|
||||||
self.uci:load(config)
|
|
||||||
end
|
end
|
||||||
self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
|
self:_run_hooks("on_commit", "on_after_commit", "on_before_apply")
|
||||||
if self.apply_on_parse then
|
if self.apply_on_parse == true or self.apply_on_parse == false then
|
||||||
self.uci:apply(self.parsechain)
|
self.uci:apply(self.apply_on_parse)
|
||||||
self:_run_hooks("on_apply", "on_after_apply")
|
self:_run_hooks("on_apply", "on_after_apply")
|
||||||
else
|
else
|
||||||
-- This is evaluated by the dispatcher and delegated to the
|
-- This is evaluated by the dispatcher and delegated to the
|
||||||
|
@ -1226,13 +1226,14 @@ function TypedSection.parse(self, novld)
|
||||||
local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype
|
local stval = RESORT_PREFIX .. self.config .. "." .. self.sectiontype
|
||||||
local order = self.map:formvalue(stval)
|
local order = self.map:formvalue(stval)
|
||||||
if order and #order > 0 then
|
if order and #order > 0 then
|
||||||
local sid
|
local sids, sid = { }, nil
|
||||||
local num = 0
|
|
||||||
for sid in util.imatch(order) do
|
for sid in util.imatch(order) do
|
||||||
self.map.uci:reorder(self.config, sid, num)
|
sids[#sids+1] = sid
|
||||||
num = num + 1
|
end
|
||||||
|
if #sids > 0 then
|
||||||
|
self.map.uci:reorder(self.config, sids)
|
||||||
|
self.changed = true
|
||||||
end
|
end
|
||||||
self.changed = (num > 0)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1416,6 +1417,12 @@ function AbstractValue.parse(self, section, novld)
|
||||||
self:add_error(section, "invalid", val_err)
|
self:add_error(section, "invalid", val_err)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.alias then
|
||||||
|
self.section.aliased = self.section.aliased or {}
|
||||||
|
self.section.aliased[section] = self.section.aliased[section] or {}
|
||||||
|
self.section.aliased[section][self.alias] = true
|
||||||
|
end
|
||||||
|
|
||||||
if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
|
if fvalue and (self.forcewrite or not (fvalue == cvalue)) then
|
||||||
if self:write(section, fvalue) then
|
if self:write(section, fvalue) then
|
||||||
-- Push events
|
-- Push events
|
||||||
|
@ -1425,10 +1432,16 @@ function AbstractValue.parse(self, section, novld)
|
||||||
end
|
end
|
||||||
else -- Unset the UCI or error
|
else -- Unset the UCI or error
|
||||||
if self.rmempty or self.optional then
|
if self.rmempty or self.optional then
|
||||||
if self:remove(section) then
|
if not self.alias or
|
||||||
-- Push events
|
not self.section.aliased or
|
||||||
self.section.changed = true
|
not self.section.aliased[section] or
|
||||||
--luci.util.append(self.map.events, self.events)
|
not self.section.aliased[section][self.alias]
|
||||||
|
then
|
||||||
|
if self:remove(section) then
|
||||||
|
-- Push events
|
||||||
|
self.section.changed = true
|
||||||
|
--luci.util.append(self.map.events, self.events)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
elseif cvalue ~= fvalue and not novld then
|
elseif cvalue ~= fvalue and not novld then
|
||||||
-- trigger validator with nil value to get custom user error msg.
|
-- trigger validator with nil value to get custom user error msg.
|
||||||
|
@ -1454,7 +1467,7 @@ function AbstractValue.cfgvalue(self, section)
|
||||||
if self.tag_error[section] then
|
if self.tag_error[section] then
|
||||||
value = self:formvalue(section)
|
value = self:formvalue(section)
|
||||||
else
|
else
|
||||||
value = self.map:get(section, self.option)
|
value = self.map:get(section, self.alias or self.option)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not value then
|
if not value then
|
||||||
|
@ -1495,12 +1508,12 @@ AbstractValue.transform = AbstractValue.validate
|
||||||
|
|
||||||
-- Write to UCI
|
-- Write to UCI
|
||||||
function AbstractValue.write(self, section, value)
|
function AbstractValue.write(self, section, value)
|
||||||
return self.map:set(section, self.option, value)
|
return self.map:set(section, self.alias or self.option, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove from UCI
|
-- Remove from UCI
|
||||||
function AbstractValue.remove(self, section)
|
function AbstractValue.remove(self, section)
|
||||||
return self.map:del(section, self.option)
|
return self.map:del(section, self.alias or self.option)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -1833,6 +1846,15 @@ function DynamicList.formvalue(self, section)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
DropDown = class(MultiValue)
|
||||||
|
|
||||||
|
function DropDown.__init__(self, ...)
|
||||||
|
ListValue.__init__(self, ...)
|
||||||
|
self.template = "cbi/dropdown"
|
||||||
|
self.delimiter = " "
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
TextValue - A multi-line value
|
TextValue - A multi-line value
|
||||||
rows: Rows
|
rows: Rows
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
-- Copyright 2010 Jo-Philipp Wich <jow@openwrt.org>
|
|
||||||
-- Licensed to the public under the Apache License 2.0.
|
|
||||||
|
|
||||||
module("luci.controller.admin.servicectl", package.seeall)
|
|
||||||
|
|
||||||
function index()
|
|
||||||
entry({"servicectl"}, alias("servicectl", "status")).sysauth = "root"
|
|
||||||
entry({"servicectl", "status"}, call("action_status")).leaf = true
|
|
||||||
entry({"servicectl", "restart"}, post("action_restart")).leaf = true
|
|
||||||
end
|
|
||||||
|
|
||||||
function action_status()
|
|
||||||
local data = nixio.fs.readfile("/var/run/luci-reload-status")
|
|
||||||
if data then
|
|
||||||
luci.http.write("/etc/config/")
|
|
||||||
luci.http.write(data)
|
|
||||||
else
|
|
||||||
luci.http.write("finish")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function action_restart(args)
|
|
||||||
local uci = require "luci.model.uci".cursor()
|
|
||||||
if args then
|
|
||||||
local service
|
|
||||||
local services = { }
|
|
||||||
|
|
||||||
for service in args:gmatch("[%w_-]+") do
|
|
||||||
services[#services+1] = service
|
|
||||||
end
|
|
||||||
|
|
||||||
local command = uci:apply(services, true)
|
|
||||||
if nixio.fork() == 0 then
|
|
||||||
local i = nixio.open("/dev/null", "r")
|
|
||||||
local o = nixio.open("/dev/null", "w")
|
|
||||||
|
|
||||||
nixio.dup(i, nixio.stdin)
|
|
||||||
nixio.dup(o, nixio.stdout)
|
|
||||||
|
|
||||||
i:close()
|
|
||||||
o:close()
|
|
||||||
|
|
||||||
nixio.exec("/bin/sh", unpack(command))
|
|
||||||
else
|
|
||||||
luci.http.write("OK")
|
|
||||||
os.exit(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -182,6 +182,7 @@ local function session_retrieve(sid, allowed_users)
|
||||||
(not allowed_users or
|
(not allowed_users or
|
||||||
util.contains(allowed_users, sdat.values.username))
|
util.contains(allowed_users, sdat.values.username))
|
||||||
then
|
then
|
||||||
|
uci:set_session_id(sid)
|
||||||
return sid, sdat.values
|
return sid, sdat.values
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -357,7 +358,7 @@ function dispatch(request)
|
||||||
elseif key == "REQUEST_URI" then
|
elseif key == "REQUEST_URI" then
|
||||||
return build_url(unpack(ctx.requestpath))
|
return build_url(unpack(ctx.requestpath))
|
||||||
elseif key == "FULL_REQUEST_URI" then
|
elseif key == "FULL_REQUEST_URI" then
|
||||||
local url = { http.getenv("SCRIPT_NAME"), http.getenv("PATH_INFO") }
|
local url = { http.getenv("SCRIPT_NAME") or "", http.getenv("PATH_INFO") }
|
||||||
local query = http.getenv("QUERY_STRING")
|
local query = http.getenv("QUERY_STRING")
|
||||||
if query and #query > 0 then
|
if query and #query > 0 then
|
||||||
url[#url+1] = "?"
|
url[#url+1] = "?"
|
||||||
|
@ -428,7 +429,9 @@ function dispatch(request)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sid, build_url() })
|
http.header("Set-Cookie", 'sysauth=%s; path=%s; HttpOnly%s' %{
|
||||||
|
sid, build_url(), http.getenv("HTTPS") == "on" and "; secure" or ""
|
||||||
|
})
|
||||||
http.redirect(build_url(unpack(ctx.requestpath)))
|
http.redirect(build_url(unpack(ctx.requestpath)))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -504,10 +507,11 @@ function dispatch(request)
|
||||||
else
|
else
|
||||||
ok, err = util.copcall(target, unpack(args))
|
ok, err = util.copcall(target, unpack(args))
|
||||||
end
|
end
|
||||||
assert(ok,
|
if not ok then
|
||||||
"Failed to execute " .. (type(c.target) == "function" and "function" or c.target.type or "unknown") ..
|
error500("Failed to execute " .. (type(c.target) == "function" and "function" or c.target.type or "unknown") ..
|
||||||
" dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
|
" dispatcher target for entry '/" .. table.concat(request, "/") .. "'.\n" ..
|
||||||
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"))
|
"The called action terminated with an exception:\n" .. tostring(err or "(unknown)"))
|
||||||
|
end
|
||||||
else
|
else
|
||||||
local root = node()
|
local root = node()
|
||||||
if not root or not root.target then
|
if not root or not root.target then
|
||||||
|
@ -699,15 +703,22 @@ function _create_node(path)
|
||||||
local last = table.remove(path)
|
local last = table.remove(path)
|
||||||
local parent = _create_node(path)
|
local parent = _create_node(path)
|
||||||
|
|
||||||
c = {nodes={}, auto=true}
|
c = {nodes={}, auto=true, inreq=true}
|
||||||
-- the node is "in request" if the request path matches
|
|
||||||
-- at least up to the length of the node path
|
local _, n
|
||||||
if parent.inreq and context.path[#path+1] == last then
|
for _, n in ipairs(path) do
|
||||||
c.inreq = true
|
if context.path[_] ~= n then
|
||||||
|
c.inreq = false
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
c.inreq = c.inreq and (context.path[#path + 1] == last)
|
||||||
|
|
||||||
parent.nodes[last] = c
|
parent.nodes[last] = c
|
||||||
context.treecache[name] = c
|
context.treecache[name] = c
|
||||||
end
|
end
|
||||||
|
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -908,7 +919,6 @@ local function _cbi(self, ...)
|
||||||
for i, res in ipairs(maps) do
|
for i, res in ipairs(maps) do
|
||||||
res:render({
|
res:render({
|
||||||
firstmap = (i == 1),
|
firstmap = (i == 1),
|
||||||
applymap = applymap,
|
|
||||||
redirect = redirect,
|
redirect = redirect,
|
||||||
messages = messages,
|
messages = messages,
|
||||||
pageaction = pageaction,
|
pageaction = pageaction,
|
||||||
|
@ -918,11 +928,12 @@ local function _cbi(self, ...)
|
||||||
|
|
||||||
if not config.nofooter then
|
if not config.nofooter then
|
||||||
tpl.render("cbi/footer", {
|
tpl.render("cbi/footer", {
|
||||||
flow = config,
|
flow = config,
|
||||||
pageaction = pageaction,
|
pageaction = pageaction,
|
||||||
redirect = redirect,
|
redirect = redirect,
|
||||||
state = state,
|
state = state,
|
||||||
autoapply = config.autoapply
|
autoapply = config.autoapply,
|
||||||
|
trigger_apply = applymap
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@ local table, ipairs, pairs, type, tostring, tonumber, error =
|
||||||
|
|
||||||
module "luci.http"
|
module "luci.http"
|
||||||
|
|
||||||
HTTP_MAX_CONTENT = 1024*8 -- 8 kB maximum content size
|
HTTP_MAX_CONTENT = 1024*100 -- 100 kB maximum content size
|
||||||
|
|
||||||
context = util.threadlocal()
|
context = util.threadlocal()
|
||||||
|
|
||||||
|
@ -416,7 +416,7 @@ function mimedecode_message_body(src, msg, file_cb)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end)
|
end, HTTP_MAX_CONTENT)
|
||||||
|
|
||||||
return ltn12.pump.all(src, function (chunk)
|
return ltn12.pump.all(src, function (chunk)
|
||||||
len = len + (chunk and #chunk or 0)
|
len = len + (chunk and #chunk or 0)
|
||||||
|
@ -460,7 +460,7 @@ function urldecode_message_body(src, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end)
|
end, HTTP_MAX_CONTENT)
|
||||||
|
|
||||||
return ltn12.pump.all(src, function (chunk)
|
return ltn12.pump.all(src, function (chunk)
|
||||||
len = len + (chunk and #chunk or 0)
|
len = len + (chunk and #chunk or 0)
|
||||||
|
|
|
@ -23,6 +23,22 @@ IFACE_PATTERNS_VIRTUAL = { }
|
||||||
IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^gretap%d", "^ip6gre%d", "^ip6tnl%d", "^tunl%d", "^lo$" }
|
IFACE_PATTERNS_IGNORE = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^gretap%d", "^ip6gre%d", "^ip6tnl%d", "^tunl%d", "^lo$" }
|
||||||
IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
|
IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
|
||||||
|
|
||||||
|
IFACE_ERRORS = {
|
||||||
|
CONNECT_FAILED = lng.translate("Connection attempt failed"),
|
||||||
|
INVALID_ADDRESS = lng.translate("IP address in invalid"),
|
||||||
|
INVALID_GATEWAY = lng.translate("Gateway address is invalid"),
|
||||||
|
INVALID_LOCAL_ADDRESS = lng.translate("Local IP address is invalid"),
|
||||||
|
MISSING_ADDRESS = lng.translate("IP address is missing"),
|
||||||
|
MISSING_PEER_ADDRESS = lng.translate("Peer address is missing"),
|
||||||
|
NO_DEVICE = lng.translate("Network device is not present"),
|
||||||
|
NO_IFACE = lng.translate("Unable to determine device name"),
|
||||||
|
NO_IFNAME = lng.translate("Unable to determine device name"),
|
||||||
|
NO_WAN_ADDRESS = lng.translate("Unable to determine external IP address"),
|
||||||
|
NO_WAN_LINK = lng.translate("Unable to determine upstream interface"),
|
||||||
|
PEER_RESOLVE_FAIL = lng.translate("Unable to resolve peer host name"),
|
||||||
|
PIN_FAILED = lng.translate("PIN code rejected")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protocol = utl.class()
|
protocol = utl.class()
|
||||||
|
|
||||||
|
@ -495,6 +511,17 @@ function register_pattern_virtual(self, pat)
|
||||||
IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
|
IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function register_error_code(self, code, message)
|
||||||
|
if type(code) == "string" and
|
||||||
|
type(message) == "string" and
|
||||||
|
not IFACE_ERRORS[code]
|
||||||
|
then
|
||||||
|
IFACE_ERRORS[code] = message
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
function has_ipv6(self)
|
function has_ipv6(self)
|
||||||
return nfs.access("/proc/net/ipv6_route")
|
return nfs.access("/proc/net/ipv6_route")
|
||||||
|
@ -520,6 +547,13 @@ end
|
||||||
function get_network(self, n)
|
function get_network(self, n)
|
||||||
if n and _uci:get("network", n) == "interface" then
|
if n and _uci:get("network", n) == "interface" then
|
||||||
return network(n)
|
return network(n)
|
||||||
|
elseif n then
|
||||||
|
local stat = utl.ubus("network.interface", "status", { interface = n })
|
||||||
|
if type(stat) == "table" and
|
||||||
|
type(stat.proto) == "string"
|
||||||
|
then
|
||||||
|
return network(n, stat.proto)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -532,6 +566,23 @@ function get_networks(self)
|
||||||
nls[s['.name']] = network(s['.name'])
|
nls[s['.name']] = network(s['.name'])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local dump = utl.ubus("network.interface", "dump", { })
|
||||||
|
if type(dump) == "table" and
|
||||||
|
type(dump.interface) == "table"
|
||||||
|
then
|
||||||
|
local _, net
|
||||||
|
for _, net in ipairs(dump.interface) do
|
||||||
|
if type(net) == "table" and
|
||||||
|
type(net.proto) == "string" and
|
||||||
|
type(net.interface) == "string"
|
||||||
|
then
|
||||||
|
if not nls[net.interface] then
|
||||||
|
nls[net.interface] = network(net.interface, net.proto)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local n
|
local n
|
||||||
for n in utl.kspairs(nls) do
|
for n in utl.kspairs(nls) do
|
||||||
nets[#nets+1] = nls[n]
|
nets[#nets+1] = nls[n]
|
||||||
|
@ -929,6 +980,16 @@ function protocol.metric(self)
|
||||||
return self:_ubus("metric") or 0
|
return self:_ubus("metric") or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function protocol.zonename(self)
|
||||||
|
local d = self:_ubus("data")
|
||||||
|
|
||||||
|
if type(d) == "table" and type(d.zone) == "string" then
|
||||||
|
return d.zone
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
function protocol.ipaddr(self)
|
function protocol.ipaddr(self)
|
||||||
local addrs = self:_ubus("ipv4-address")
|
local addrs = self:_ubus("ipv4-address")
|
||||||
return addrs and #addrs > 0 and addrs[1].address
|
return addrs and #addrs > 0 and addrs[1].address
|
||||||
|
@ -1043,6 +1104,22 @@ function protocol.ip6prefix(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function protocol.errors(self)
|
||||||
|
local _, err, rv
|
||||||
|
local errors = self:_ubus("errors")
|
||||||
|
if type(errors) == "table" then
|
||||||
|
for _, err in ipairs(errors) do
|
||||||
|
if type(err) == "table" and
|
||||||
|
type(err.code) == "string"
|
||||||
|
then
|
||||||
|
rv = rv or { }
|
||||||
|
rv[#rv+1] = IFACE_ERRORS[err.code] or lng.translatef("Unknown error (%s)", err.code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
function protocol.is_bridge(self)
|
function protocol.is_bridge(self)
|
||||||
return (not self:is_virtual() and self:type() == "bridge")
|
return (not self:is_virtual() and self:type() == "bridge")
|
||||||
end
|
end
|
||||||
|
@ -1063,6 +1140,24 @@ function protocol.is_floating(self)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function protocol.is_dynamic(self)
|
||||||
|
return (self:_ubus("dynamic") == true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function protocol.is_alias(self)
|
||||||
|
local ifn, parent = nil, nil
|
||||||
|
|
||||||
|
for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
|
||||||
|
if #ifn > 1 and ifn:byte(1) == 64 then
|
||||||
|
parent = ifn:sub(2)
|
||||||
|
elseif parent ~= nil then
|
||||||
|
parent = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return parent
|
||||||
|
end
|
||||||
|
|
||||||
function protocol.is_empty(self)
|
function protocol.is_empty(self)
|
||||||
if self:is_floating() then
|
if self:is_floating() then
|
||||||
return false
|
return false
|
||||||
|
@ -1081,6 +1176,10 @@ function protocol.is_empty(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function protocol.is_up(self)
|
||||||
|
return (self:_ubus("up") == true)
|
||||||
|
end
|
||||||
|
|
||||||
function protocol.add_interface(self, ifname)
|
function protocol.add_interface(self, ifname)
|
||||||
ifname = _M:ifnameof(ifname)
|
ifname = _M:ifnameof(ifname)
|
||||||
if ifname and not self:is_floating() then
|
if ifname and not self:is_floating() then
|
||||||
|
@ -1116,12 +1215,16 @@ function protocol.get_interface(self)
|
||||||
_bridge["br-" .. self.sid] = true
|
_bridge["br-" .. self.sid] = true
|
||||||
return interface("br-" .. self.sid, self)
|
return interface("br-" .. self.sid, self)
|
||||||
else
|
else
|
||||||
local ifn = nil
|
local ifn = self:_ubus("l3_device") or self:_ubus("device")
|
||||||
local num = { }
|
if ifn then
|
||||||
|
return interface(ifn, self)
|
||||||
|
end
|
||||||
|
|
||||||
for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
|
for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
|
||||||
ifn = ifn:match("^[^:/]+")
|
ifn = ifn:match("^[^:/]+")
|
||||||
return ifn and interface(ifn, self)
|
return ifn and interface(ifn, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
ifn = _wifi_netid_by_netname(self.sid)
|
ifn = _wifi_netid_by_netname(self.sid)
|
||||||
return ifn and interface(ifn, self)
|
return ifn and interface(ifn, self)
|
||||||
end
|
end
|
||||||
|
@ -1245,7 +1348,9 @@ function interface.ip6addrs(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function interface.type(self)
|
function interface.type(self)
|
||||||
if self.wif or _wifi_iface(self.ifname) then
|
if self.ifname and self.ifname:byte(1) == 64 then
|
||||||
|
return "alias"
|
||||||
|
elseif self.wif or _wifi_iface(self.ifname) then
|
||||||
return "wifi"
|
return "wifi"
|
||||||
elseif _bridge[self.ifname] then
|
elseif _bridge[self.ifname] then
|
||||||
return "bridge"
|
return "bridge"
|
||||||
|
@ -1273,7 +1378,7 @@ function interface.get_i18n(self)
|
||||||
return "%s: %s %q" %{
|
return "%s: %s %q" %{
|
||||||
lng.translate("Wireless Network"),
|
lng.translate("Wireless Network"),
|
||||||
self.wif:active_mode(),
|
self.wif:active_mode(),
|
||||||
self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
|
self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id() or "?"
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return "%s: %q" %{ self:get_type_i18n(), self:name() }
|
return "%s: %q" %{ self:get_type_i18n(), self:name() }
|
||||||
|
@ -1282,7 +1387,9 @@ end
|
||||||
|
|
||||||
function interface.get_type_i18n(self)
|
function interface.get_type_i18n(self)
|
||||||
local x = self:type()
|
local x = self:type()
|
||||||
if x == "wifi" then
|
if x == "alias" then
|
||||||
|
return lng.translate("Alias Interface")
|
||||||
|
elseif x == "wifi" then
|
||||||
return lng.translate("Wireless Adapter")
|
return lng.translate("Wireless Adapter")
|
||||||
elseif x == "bridge" then
|
elseif x == "bridge" then
|
||||||
return lng.translate("Bridge")
|
return lng.translate("Bridge")
|
||||||
|
@ -1335,7 +1442,11 @@ function interface.bridge_stp(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function interface.is_up(self)
|
function interface.is_up(self)
|
||||||
return self:_ubus("up") or false
|
local up = self:_ubus("up")
|
||||||
|
if up == nil then
|
||||||
|
up = (self:type() == "alias")
|
||||||
|
end
|
||||||
|
return up or false
|
||||||
end
|
end
|
||||||
|
|
||||||
function interface.is_bridge(self)
|
function interface.is_bridge(self)
|
||||||
|
@ -1428,7 +1539,7 @@ function wifidev.hwmodes(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wifidev.get_i18n(self)
|
function wifidev.get_i18n(self)
|
||||||
local t = "Generic"
|
local t = self.iwinfo.hardware_name or "Generic"
|
||||||
if self.iwinfo.type == "wl" then
|
if self.iwinfo.type == "wl" then
|
||||||
t = "Broadcom"
|
t = "Broadcom"
|
||||||
end
|
end
|
||||||
|
@ -1601,7 +1712,7 @@ end
|
||||||
function wifinet.ifname(self)
|
function wifinet.ifname(self)
|
||||||
local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
|
local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
|
||||||
if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
|
if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
|
||||||
ifname = self.wdev
|
ifname = self.netid
|
||||||
end
|
end
|
||||||
return ifname
|
return ifname
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ local table = require "table"
|
||||||
|
|
||||||
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
||||||
local require, getmetatable, assert = require, getmetatable, assert
|
local require, getmetatable, assert = require, getmetatable, assert
|
||||||
local error, pairs, ipairs = error, pairs, ipairs
|
local error, pairs, ipairs, select = error, pairs, ipairs, select
|
||||||
local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
|
local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
|
||||||
|
|
||||||
-- The typical workflow for UCI is: Get a cursor instance from the
|
-- The typical workflow for UCI is: Get a cursor instance from the
|
||||||
|
@ -106,7 +106,7 @@ function changes(self, config)
|
||||||
local _, change
|
local _, change
|
||||||
for _, change in ipairs(changes) do
|
for _, change in ipairs(changes) do
|
||||||
local operation, section, option, value = unpack(change)
|
local operation, section, option, value = unpack(change)
|
||||||
if option and value and operation ~= "add" then
|
if option and operation ~= "add" then
|
||||||
res[package][section] = res[package][section] or { }
|
res[package][section] = res[package][section] or { }
|
||||||
|
|
||||||
if operation == "list-add" then
|
if operation == "list-add" then
|
||||||
|
@ -143,22 +143,129 @@ function commit(self, config)
|
||||||
return (err == nil), ERRSTR[err]
|
return (err == nil), ERRSTR[err]
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
function apply(self, rollback)
|
||||||
function apply(self, configs, command)
|
local _, err
|
||||||
local _, config
|
|
||||||
|
|
||||||
assert(not command, "Apply command not supported anymore")
|
if rollback then
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local conf = require "luci.config"
|
||||||
|
local timeout = tonumber(conf and conf.apply and conf.apply.rollback or 30) or 0
|
||||||
|
|
||||||
if type(configs) == "table" then
|
_, err = call("apply", {
|
||||||
for _, config in ipairs(configs) do
|
timeout = (timeout > 30) and timeout or 30,
|
||||||
call("service", "event", {
|
rollback = true
|
||||||
type = "config.change",
|
})
|
||||||
data = { package = config }
|
|
||||||
|
if not err then
|
||||||
|
local now = os.time()
|
||||||
|
local token = sys.uniqueid(16)
|
||||||
|
|
||||||
|
util.ubus("session", "set", {
|
||||||
|
ubus_rpc_session = "00000000000000000000000000000000",
|
||||||
|
values = {
|
||||||
|
rollback = {
|
||||||
|
token = token,
|
||||||
|
session = session_id,
|
||||||
|
timeout = now + timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return token
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_, err = call("changes", {})
|
||||||
|
|
||||||
|
if not err then
|
||||||
|
if type(_) == "table" and type(_.changes) == "table" then
|
||||||
|
local k, v
|
||||||
|
for k, v in pairs(_.changes) do
|
||||||
|
_, err = call("commit", { config = k })
|
||||||
|
if err then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not err then
|
||||||
|
_, err = call("apply", { rollback = false })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return (err == nil), ERRSTR[err]
|
||||||
|
end
|
||||||
|
|
||||||
|
function confirm(self, token)
|
||||||
|
local is_pending, time_remaining, rollback_sid, rollback_token = self:rollback_pending()
|
||||||
|
|
||||||
|
if is_pending then
|
||||||
|
if token ~= rollback_token then
|
||||||
|
return false, "Permission denied"
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, err = util.ubus("uci", "confirm", {
|
||||||
|
ubus_rpc_session = rollback_sid
|
||||||
|
})
|
||||||
|
|
||||||
|
if not err then
|
||||||
|
util.ubus("session", "set", {
|
||||||
|
ubus_rpc_session = "00000000000000000000000000000000",
|
||||||
|
values = { rollback = {} }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return (err == nil), ERRSTR[err]
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, "No data"
|
||||||
|
end
|
||||||
|
|
||||||
|
function rollback(self)
|
||||||
|
local is_pending, time_remaining, rollback_sid = self:rollback_pending()
|
||||||
|
|
||||||
|
if is_pending then
|
||||||
|
local _, err = util.ubus("uci", "rollback", {
|
||||||
|
ubus_rpc_session = rollback_sid
|
||||||
|
})
|
||||||
|
|
||||||
|
if not err then
|
||||||
|
util.ubus("session", "set", {
|
||||||
|
ubus_rpc_session = "00000000000000000000000000000000",
|
||||||
|
values = { rollback = {} }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return (err == nil), ERRSTR[err]
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, "No data"
|
||||||
|
end
|
||||||
|
|
||||||
|
function rollback_pending(self)
|
||||||
|
local rv, err = util.ubus("session", "get", {
|
||||||
|
ubus_rpc_session = "00000000000000000000000000000000",
|
||||||
|
keys = { "rollback" }
|
||||||
|
})
|
||||||
|
|
||||||
|
local now = os.time()
|
||||||
|
|
||||||
|
if type(rv) == "table" and
|
||||||
|
type(rv.values) == "table" and
|
||||||
|
type(rv.values.rollback) == "table" and
|
||||||
|
type(rv.values.rollback.token) == "string" and
|
||||||
|
type(rv.values.rollback.session) == "string" and
|
||||||
|
type(rv.values.rollback.timeout) == "number" and
|
||||||
|
rv.values.rollback.timeout > now
|
||||||
|
then
|
||||||
|
return true,
|
||||||
|
rv.values.rollback.timeout - now,
|
||||||
|
rv.values.rollback.session,
|
||||||
|
rv.values.rollback.token
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, ERRSTR[err]
|
||||||
end
|
end
|
||||||
]]
|
|
||||||
|
|
||||||
|
|
||||||
function foreach(self, config, stype, callback)
|
function foreach(self, config, stype, callback)
|
||||||
|
@ -310,15 +417,15 @@ function add(self, config, stype)
|
||||||
return self:section(config, stype)
|
return self:section(config, stype)
|
||||||
end
|
end
|
||||||
|
|
||||||
function set(self, config, section, option, value)
|
function set(self, config, section, option, ...)
|
||||||
if value == nil then
|
if select('#', ...) == 0 then
|
||||||
local sname, err = self:section(config, option, section)
|
local sname, err = self:section(config, option, section)
|
||||||
return (not not sname), err
|
return (not not sname), err
|
||||||
else
|
else
|
||||||
local _, err = call("set", {
|
local _, err = call("set", {
|
||||||
config = config,
|
config = config,
|
||||||
section = section,
|
section = section,
|
||||||
values = { [option] = value }
|
values = { [option] = select(1, ...) }
|
||||||
})
|
})
|
||||||
return (err == nil), ERRSTR[err]
|
return (err == nil), ERRSTR[err]
|
||||||
end
|
end
|
||||||
|
@ -425,59 +532,3 @@ function delete_all(self, config, stype, comparator)
|
||||||
|
|
||||||
return (err == nil), ERRSTR[err]
|
return (err == nil), ERRSTR[err]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function apply(self, configlist, command)
|
|
||||||
configlist = self:_affected(configlist)
|
|
||||||
if command then
|
|
||||||
return { "/sbin/luci-reload", unpack(configlist) }
|
|
||||||
else
|
|
||||||
return os.execute("/sbin/luci-reload %s >/dev/null 2>&1"
|
|
||||||
% util.shellquote(table.concat(configlist, " ")))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return a list of initscripts affected by configuration changes.
|
|
||||||
function _affected(self, configlist)
|
|
||||||
configlist = type(configlist) == "table" and configlist or { configlist }
|
|
||||||
|
|
||||||
-- Resolve dependencies
|
|
||||||
local reloadlist = { }
|
|
||||||
|
|
||||||
local function _resolve_deps(name)
|
|
||||||
local reload = { name }
|
|
||||||
local deps = { }
|
|
||||||
|
|
||||||
self:foreach("ucitrack", name,
|
|
||||||
function(section)
|
|
||||||
if section.affects then
|
|
||||||
for i, aff in ipairs(section.affects) do
|
|
||||||
deps[#deps+1] = aff
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local i, dep
|
|
||||||
for i, dep in ipairs(deps) do
|
|
||||||
local j, add
|
|
||||||
for j, add in ipairs(_resolve_deps(dep)) do
|
|
||||||
reload[#reload+1] = add
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return reload
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Collect initscripts
|
|
||||||
local j, config
|
|
||||||
for j, config in ipairs(configlist) do
|
|
||||||
local i, e
|
|
||||||
for i, e in ipairs(_resolve_deps(config)) do
|
|
||||||
if not util.contains(reloadlist, e) then
|
|
||||||
reloadlist[#reloadlist+1] = e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return reloadlist
|
|
||||||
end
|
|
||||||
|
|
|
@ -28,12 +28,63 @@ Create a new Cursor initialized to the state directory.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
---[[
|
---[[
|
||||||
Applies UCI configuration changes
|
Applies UCI configuration changes.
|
||||||
|
|
||||||
|
If the rollback parameter is set to true, the apply function will invoke the
|
||||||
|
rollback mechanism which causes the configuration to be automatically reverted
|
||||||
|
if no confirm() call occurs within a certain timeout.
|
||||||
|
|
||||||
|
The current default timeout is 30s and can be increased using the
|
||||||
|
"luci.apply.timeout" uci configuration key.
|
||||||
|
|
||||||
@class function
|
@class function
|
||||||
@name Cursor.apply
|
@name Cursor.apply
|
||||||
@param configlist List of UCI configurations
|
@param rollback Enable rollback mechanism
|
||||||
@param command Don't apply only return the command
|
@return Boolean whether operation succeeded
|
||||||
|
]]
|
||||||
|
|
||||||
|
---[[
|
||||||
|
Confirms UCI apply process.
|
||||||
|
|
||||||
|
If a previous UCI apply with rollback has been invoked using apply(true),
|
||||||
|
this function confirms the process and cancels the pending rollback timer.
|
||||||
|
|
||||||
|
If no apply with rollback session is active, the function has no effect and
|
||||||
|
returns with a "No data" error.
|
||||||
|
|
||||||
|
@class function
|
||||||
|
@name Cursor.confirm
|
||||||
|
@return Boolean whether operation succeeded
|
||||||
|
]]
|
||||||
|
|
||||||
|
---[[
|
||||||
|
Cancels UCI apply process.
|
||||||
|
|
||||||
|
If a previous UCI apply with rollback has been invoked using apply(true),
|
||||||
|
this function cancels the process and rolls back the configuration to the
|
||||||
|
pre-apply state.
|
||||||
|
|
||||||
|
If no apply with rollback session is active, the function has no effect and
|
||||||
|
returns with a "No data" error.
|
||||||
|
|
||||||
|
@class function
|
||||||
|
@name Cursor.rollback
|
||||||
|
@return Boolean whether operation succeeded
|
||||||
|
]]
|
||||||
|
|
||||||
|
---[[
|
||||||
|
Checks whether a pending rollback is scheduled.
|
||||||
|
|
||||||
|
If a previous UCI apply with rollback has been invoked using apply(true),
|
||||||
|
and has not been confirmed or rolled back yet, this function returns true
|
||||||
|
and the remaining time until rollback in seconds. If no rollback is pending,
|
||||||
|
the function returns false. On error, the function returns false and an
|
||||||
|
additional string describing the error.
|
||||||
|
|
||||||
|
@class function
|
||||||
|
@name Cursor.rollback_pending
|
||||||
|
@return Boolean whether rollback is pending
|
||||||
|
@return Remaining time in seconds
|
||||||
]]
|
]]
|
||||||
|
|
||||||
---[[
|
---[[
|
||||||
|
|
|
@ -6,9 +6,25 @@ module("luci.tools.status", package.seeall)
|
||||||
local uci = require "luci.model.uci".cursor()
|
local uci = require "luci.model.uci".cursor()
|
||||||
local ipc = require "luci.ip"
|
local ipc = require "luci.ip"
|
||||||
|
|
||||||
|
local function duid_to_mac(duid)
|
||||||
|
local b1, b2, b3, b4, b5, b6
|
||||||
|
|
||||||
|
-- DUID-LLT / Ethernet
|
||||||
|
if type(duid) == "string" and #duid == 28 then
|
||||||
|
b1, b2, b3, b4, b5, b6 = duid:match("^00010001(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)%x%x%x%x%x%x%x%x$")
|
||||||
|
|
||||||
|
-- DUID-LL / Ethernet
|
||||||
|
elseif type(duid) == "string" and #duid == 20 then
|
||||||
|
b1, b2, b3, b4, b5, b6 = duid:match("^00030001(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)$")
|
||||||
|
end
|
||||||
|
|
||||||
|
return b1 and ipc.checkmac(table.concat({ b1, b2, b3, b4, b5, b6 }, ":"))
|
||||||
|
end
|
||||||
|
|
||||||
local function dhcp_leases_common(family)
|
local function dhcp_leases_common(family)
|
||||||
local rv = { }
|
local rv = { }
|
||||||
local nfs = require "nixio.fs"
|
local nfs = require "nixio.fs"
|
||||||
|
local sys = require "luci.sys"
|
||||||
local leasefile = "/tmp/dhcp.leases"
|
local leasefile = "/tmp/dhcp.leases"
|
||||||
|
|
||||||
uci:foreach("dhcp", "dnsmasq",
|
uci:foreach("dhcp", "dnsmasq",
|
||||||
|
@ -87,6 +103,22 @@ local function dhcp_leases_common(family)
|
||||||
fd:close()
|
fd:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if family == 6 then
|
||||||
|
local _, lease
|
||||||
|
local hosts = sys.net.host_hints()
|
||||||
|
for _, lease in ipairs(rv) do
|
||||||
|
local mac = duid_to_mac(lease.duid)
|
||||||
|
local host = mac and hosts[mac]
|
||||||
|
if host then
|
||||||
|
if not lease.name then
|
||||||
|
lease.host_hint = host.name or host.ipv4 or host.ipv6
|
||||||
|
elseif host.name and lease.hostname ~= host.name then
|
||||||
|
lease.host_hint = host.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -113,6 +145,11 @@ function wifi_networks()
|
||||||
|
|
||||||
local net
|
local net
|
||||||
for _, net in ipairs(dev:get_wifinets()) do
|
for _, net in ipairs(dev:get_wifinets()) do
|
||||||
|
local a, an = nil, 0
|
||||||
|
for _, a in pairs(net:assoclist() or {}) do
|
||||||
|
an = an + 1
|
||||||
|
end
|
||||||
|
|
||||||
rd.networks[#rd.networks+1] = {
|
rd.networks[#rd.networks+1] = {
|
||||||
name = net:shortname(),
|
name = net:shortname(),
|
||||||
link = net:adminlink(),
|
link = net:adminlink(),
|
||||||
|
@ -128,10 +165,10 @@ function wifi_networks()
|
||||||
noise = net:noise(),
|
noise = net:noise(),
|
||||||
bitrate = net:bitrate(),
|
bitrate = net:bitrate(),
|
||||||
ifname = net:ifname(),
|
ifname = net:ifname(),
|
||||||
assoclist = net:assoclist(),
|
|
||||||
country = net:country(),
|
country = net:country(),
|
||||||
txpower = net:txpower(),
|
txpower = net:txpower(),
|
||||||
txpoweroff = net:txpower_offset(),
|
txpoweroff = net:txpower_offset(),
|
||||||
|
num_assoc = an,
|
||||||
disabled = (dev:get("disabled") == "1" or
|
disabled = (dev:get("disabled") == "1" or
|
||||||
net:get("disabled") == "1")
|
net:get("disabled") == "1")
|
||||||
}
|
}
|
||||||
|
@ -165,7 +202,6 @@ function wifi_network(id)
|
||||||
noise = net:noise(),
|
noise = net:noise(),
|
||||||
bitrate = net:bitrate(),
|
bitrate = net:bitrate(),
|
||||||
ifname = net:ifname(),
|
ifname = net:ifname(),
|
||||||
assoclist = net:assoclist(),
|
|
||||||
country = net:country(),
|
country = net:country(),
|
||||||
txpower = net:txpower(),
|
txpower = net:txpower(),
|
||||||
txpoweroff = net:txpower_offset(),
|
txpoweroff = net:txpower_offset(),
|
||||||
|
@ -182,6 +218,52 @@ function wifi_network(id)
|
||||||
return { }
|
return { }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function wifi_assoclist()
|
||||||
|
local sys = require "luci.sys"
|
||||||
|
local ntm = require "luci.model.network".init()
|
||||||
|
local hosts = sys.net.host_hints()
|
||||||
|
|
||||||
|
local assoc = {}
|
||||||
|
local _, dev, net, bss
|
||||||
|
|
||||||
|
for _, dev in ipairs(ntm:get_wifidevs()) do
|
||||||
|
local radioname = dev:get_i18n()
|
||||||
|
|
||||||
|
for _, net in ipairs(dev:get_wifinets()) do
|
||||||
|
local netname = net:shortname()
|
||||||
|
local netlink = net:adminlink()
|
||||||
|
local ifname = net:ifname()
|
||||||
|
|
||||||
|
for _, bss in pairs(net:assoclist() or {}) do
|
||||||
|
local host = hosts[_]
|
||||||
|
|
||||||
|
bss.bssid = _
|
||||||
|
bss.ifname = ifname
|
||||||
|
bss.radio = radioname
|
||||||
|
bss.name = netname
|
||||||
|
bss.link = netlink
|
||||||
|
|
||||||
|
bss.host_name = (host) and (host.name or host.ipv4 or host.ipv6)
|
||||||
|
bss.host_hint = (host and host.name and (host.ipv4 or host.ipv6)) and (host.ipv4 or host.ipv6)
|
||||||
|
|
||||||
|
assoc[#assoc+1] = bss
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(assoc, function(a, b)
|
||||||
|
if a.radio ~= b.radio then
|
||||||
|
return a.radio < b.radio
|
||||||
|
elseif a.ifname ~= b.ifname then
|
||||||
|
return a.ifname < b.ifname
|
||||||
|
else
|
||||||
|
return a.bssid < b.bssid
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return assoc
|
||||||
|
end
|
||||||
|
|
||||||
function switch_status(devs)
|
function switch_status(devs)
|
||||||
local dev
|
local dev
|
||||||
local switches = { }
|
local switches = { }
|
||||||
|
|
|
@ -16,7 +16,7 @@ local _ubus = require "ubus"
|
||||||
local _ubus_connection = nil
|
local _ubus_connection = nil
|
||||||
|
|
||||||
local getmetatable, setmetatable = getmetatable, setmetatable
|
local getmetatable, setmetatable = getmetatable, setmetatable
|
||||||
local rawget, rawset, unpack = rawget, rawset, unpack
|
local rawget, rawset, unpack, select = rawget, rawset, unpack, select
|
||||||
local tostring, type, assert, error = tostring, type, assert, error
|
local tostring, type, assert, error = tostring, type, assert, error
|
||||||
local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
|
local ipairs, pairs, next, loadstring = ipairs, pairs, next, loadstring
|
||||||
local require, pcall, xpcall = require, pcall, xpcall
|
local require, pcall, xpcall = require, pcall, xpcall
|
||||||
|
@ -100,6 +100,8 @@ end
|
||||||
-- Scope manipulation routines
|
-- Scope manipulation routines
|
||||||
--
|
--
|
||||||
|
|
||||||
|
coxpt = setmetatable({}, { __mode = "kv" })
|
||||||
|
|
||||||
local tl_meta = {
|
local tl_meta = {
|
||||||
__mode = "k",
|
__mode = "k",
|
||||||
|
|
||||||
|
@ -645,6 +647,17 @@ local ubus_codes = {
|
||||||
"CONNECTION_FAILED"
|
"CONNECTION_FAILED"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local function ubus_return(...)
|
||||||
|
if select('#', ...) == 2 then
|
||||||
|
local rv, err = select(1, ...), select(2, ...)
|
||||||
|
if rv == nil and type(err) == "number" then
|
||||||
|
return nil, err, ubus_codes[err]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ...
|
||||||
|
end
|
||||||
|
|
||||||
function ubus(object, method, data)
|
function ubus(object, method, data)
|
||||||
if not _ubus_connection then
|
if not _ubus_connection then
|
||||||
_ubus_connection = _ubus.connect()
|
_ubus_connection = _ubus.connect()
|
||||||
|
@ -655,8 +668,7 @@ function ubus(object, method, data)
|
||||||
if type(data) ~= "table" then
|
if type(data) ~= "table" then
|
||||||
data = { }
|
data = { }
|
||||||
end
|
end
|
||||||
local rv, err = _ubus_connection:call(object, method, data)
|
return ubus_return(_ubus_connection:call(object, method, data))
|
||||||
return rv, err, ubus_codes[err]
|
|
||||||
elseif object then
|
elseif object then
|
||||||
return _ubus_connection:signatures(object)
|
return _ubus_connection:signatures(object)
|
||||||
else
|
else
|
||||||
|
@ -697,73 +709,69 @@ function checklib(fullpathexe, wantedlib)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Coroutine safe xpcall and pcall versions
|
||||||
--
|
--
|
||||||
-- Coroutine safe xpcall and pcall versions modified for Luci
|
-- Encapsulates the protected calls with a coroutine based loop, so errors can
|
||||||
-- original version:
|
-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
|
||||||
-- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
|
-- yielding inside the call to pcall or xpcall.
|
||||||
--
|
--
|
||||||
-- Copyright © 2005 Kepler Project.
|
-- Authors: Roberto Ierusalimschy and Andre Carregal
|
||||||
-- Permission is hereby granted, free of charge, to any person obtaining a
|
-- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas
|
||||||
-- copy of this software and associated documentation files (the "Software"),
|
|
||||||
-- to deal in the Software without restriction, including without limitation
|
|
||||||
-- the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
-- and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
-- Software is furnished to do so, subject to the following conditions:
|
|
||||||
--
|
--
|
||||||
-- The above copyright notice and this permission notice shall be
|
-- Copyright 2005 - Kepler Project
|
||||||
-- included in all copies or substantial portions of the Software.
|
|
||||||
--
|
--
|
||||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
|
||||||
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
-------------------------------------------------------------------------------
|
||||||
-- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
||||||
-- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
||||||
-- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
local performResume, handleReturnValue
|
-------------------------------------------------------------------------------
|
||||||
local oldpcall, oldxpcall = pcall, xpcall
|
-- Implements xpcall with coroutines
|
||||||
coxpt = {}
|
-------------------------------------------------------------------------------
|
||||||
setmetatable(coxpt, {__mode = "kv"})
|
local coromap = setmetatable({}, { __mode = "k" })
|
||||||
|
|
||||||
-- Identity function for copcall
|
local function handleReturnValue(err, co, status, ...)
|
||||||
local function copcall_id(trace, ...)
|
|
||||||
return ...
|
|
||||||
end
|
|
||||||
|
|
||||||
-- values of either the function or the error handler
|
|
||||||
function coxpcall(f, err, ...)
|
|
||||||
local res, co = oldpcall(coroutine.create, f)
|
|
||||||
if not res then
|
|
||||||
local params = {...}
|
|
||||||
local newf = function() return f(unpack(params)) end
|
|
||||||
co = coroutine.create(newf)
|
|
||||||
end
|
|
||||||
local c = coroutine.running()
|
|
||||||
coxpt[co] = coxpt[c] or c or 0
|
|
||||||
|
|
||||||
return performResume(err, co, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- values of the function or the error object
|
|
||||||
function copcall(f, ...)
|
|
||||||
return coxpcall(f, copcall_id, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Handle return value of protected call
|
|
||||||
function handleReturnValue(err, co, status, ...)
|
|
||||||
if not status then
|
if not status then
|
||||||
return false, err(debug.traceback(co, (...)), ...)
|
return false, err(debug.traceback(co, (...)), ...)
|
||||||
end
|
end
|
||||||
|
if coroutine.status(co) == 'suspended' then
|
||||||
if coroutine.status(co) ~= 'suspended' then
|
return performResume(err, co, coroutine.yield(...))
|
||||||
|
else
|
||||||
return true, ...
|
return true, ...
|
||||||
end
|
end
|
||||||
|
|
||||||
return performResume(err, co, coroutine.yield(...))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Resume execution of protected function call
|
|
||||||
function performResume(err, co, ...)
|
function performResume(err, co, ...)
|
||||||
return handleReturnValue(err, co, coroutine.resume(co, ...))
|
return handleReturnValue(err, co, coroutine.resume(co, ...))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function id(trace, ...)
|
||||||
|
return trace
|
||||||
|
end
|
||||||
|
|
||||||
|
function coxpcall(f, err, ...)
|
||||||
|
local current = coroutine.running()
|
||||||
|
if not current then
|
||||||
|
if err == id then
|
||||||
|
return pcall(f, ...)
|
||||||
|
else
|
||||||
|
if select("#", ...) > 0 then
|
||||||
|
local oldf, params = f, { ... }
|
||||||
|
f = function() return oldf(unpack(params)) end
|
||||||
|
end
|
||||||
|
return xpcall(f, err)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local res, co = pcall(coroutine.create, f)
|
||||||
|
if not res then
|
||||||
|
local newf = function(...) return f(...) end
|
||||||
|
co = coroutine.create(newf)
|
||||||
|
end
|
||||||
|
coromap[co] = current
|
||||||
|
coxpt[co] = coxpt[current] or current or 0
|
||||||
|
return performResume(err, co, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function copcall(f, ...)
|
||||||
|
return coxpcall(f, id, ...)
|
||||||
|
end
|
||||||
|
|
228
luci-base/luasrc/view/cbi/apply_widget.htm
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
<% export("cbi_apply_widget", function(redirect_ok, rollback_token) -%>
|
||||||
|
<style type="text/css">
|
||||||
|
#cbi_apply_overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: none;
|
||||||
|
z-index: 20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cbi_apply_overlay .alert-message {
|
||||||
|
position: relative;
|
||||||
|
top: 10%;
|
||||||
|
width: 60%;
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
min-height: 32px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cbi_apply_overlay .alert-message > h4,
|
||||||
|
#cbi_apply_overlay .alert-message > p,
|
||||||
|
#cbi_apply_overlay .alert-message > div {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cbi_apply_overlay .alert-message > img {
|
||||||
|
margin-right: 1em;
|
||||||
|
flex-basis: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.apply-overlay-active {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.apply-overlay-active #cbi_apply_overlay {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="<%=resource%>/cbi.js?v=git-18.138.59467-72fe5dd"></script>
|
||||||
|
<script type="text/javascript">//<![CDATA[
|
||||||
|
var xhr = new XHR(),
|
||||||
|
uci_apply_auth = { sid: '<%=luci.dispatcher.context.authsession%>', token: '<%=token%>' },
|
||||||
|
uci_apply_rollback = <%=math.max(luci.config and luci.config.apply and luci.config.apply.rollback or 30, 30)%>,
|
||||||
|
uci_apply_holdoff = <%=math.max(luci.config and luci.config.apply and luci.config.apply.holdoff or 4, 1)%>,
|
||||||
|
uci_apply_timeout = <%=math.max(luci.config and luci.config.apply and luci.config.apply.timeout or 5, 1)%>,
|
||||||
|
uci_apply_display = <%=math.max(luci.config and luci.config.apply and luci.config.apply.display or 1.5, 1)%>,
|
||||||
|
uci_confirm_auth = <% if rollback_token then %>{ token: '<%=rollback_token%>' }<% else %>null<% end %>,
|
||||||
|
was_xhr_poll_running = false;
|
||||||
|
|
||||||
|
function uci_status_message(type, content) {
|
||||||
|
var overlay = document.getElementById('cbi_apply_overlay') || document.body.appendChild(E('<div id="cbi_apply_overlay"><div class="alert-message"></div></div>')),
|
||||||
|
message = overlay.querySelector('.alert-message');
|
||||||
|
|
||||||
|
if (message && type) {
|
||||||
|
if (!message.classList.contains(type)) {
|
||||||
|
message.classList.remove('notice');
|
||||||
|
message.classList.remove('warning');
|
||||||
|
message.classList.add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content)
|
||||||
|
message.innerHTML = content;
|
||||||
|
|
||||||
|
document.body.classList.add('apply-overlay-active');
|
||||||
|
|
||||||
|
if (!was_xhr_poll_running) {
|
||||||
|
was_xhr_poll_running = XHR.running();
|
||||||
|
XHR.halt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.body.classList.remove('apply-overlay-active');
|
||||||
|
|
||||||
|
if (was_xhr_poll_running)
|
||||||
|
XHR.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uci_rollback(checked) {
|
||||||
|
if (checked) {
|
||||||
|
uci_status_message('warning',
|
||||||
|
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
|
||||||
|
'<%:Failed to confirm apply within %ds, waiting for rollback…%>'.format(uci_apply_rollback));
|
||||||
|
|
||||||
|
var call = function(r, data, duration) {
|
||||||
|
if (r.status === 204) {
|
||||||
|
uci_status_message('warning',
|
||||||
|
'<h4><%:Configuration has been rolled back!%></h4>' +
|
||||||
|
'<p><%:The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.%></p>'.format(uci_apply_rollback) +
|
||||||
|
'<div class="right">' +
|
||||||
|
'<input type="button" class="btn" onclick="uci_status_message(false)" value="<%:Dismiss%>" /> ' +
|
||||||
|
'<input type="button" class="btn cbi-button-action important" onclick="uci_revert()" value="<%:Revert changes%>" /> ' +
|
||||||
|
'<input type="button" class="btn cbi-button-negative important" onclick="uci_apply(false)" value="<%:Apply unchecked%>" />' +
|
||||||
|
'</div>');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
|
||||||
|
window.setTimeout(function() {
|
||||||
|
xhr.post('<%=url("admin/uci/confirm")%>', uci_apply_auth, call, uci_apply_timeout * 1000);
|
||||||
|
}, delay);
|
||||||
|
};
|
||||||
|
|
||||||
|
call({ status: 0 });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uci_status_message('warning',
|
||||||
|
'<h4><%:Device unreachable!%></h4>' +
|
||||||
|
'<p><%:Could not regain access to the device after applying the configuration changes. You might need to reconnect if you modified network related settings such as the IP address or wireless security credentials.%></p>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uci_confirm(checked, deadline) {
|
||||||
|
var tt;
|
||||||
|
var ts = Date.now();
|
||||||
|
|
||||||
|
uci_status_message('notice');
|
||||||
|
|
||||||
|
var call = function(r, data, duration) {
|
||||||
|
if (Date.now() >= deadline) {
|
||||||
|
uci_rollback(checked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (r && (r.status === 200 || r.status === 204)) {
|
||||||
|
var indicator = document.querySelector('.uci_change_indicator');
|
||||||
|
if (indicator) indicator.style.display = 'none';
|
||||||
|
|
||||||
|
uci_status_message('notice', '<%:Configuration has been applied.%>');
|
||||||
|
|
||||||
|
window.clearTimeout(tt);
|
||||||
|
window.setTimeout(function() {
|
||||||
|
<% if redirect_ok then -%>
|
||||||
|
location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
|
||||||
|
<%- else -%>
|
||||||
|
window.location = window.location.href.split('#')[0];
|
||||||
|
<% end %>
|
||||||
|
}, uci_apply_display * 1000);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
|
||||||
|
window.setTimeout(function() {
|
||||||
|
xhr.post('<%=url("admin/uci/confirm")%>', uci_confirm_auth, call, uci_apply_timeout * 1000);
|
||||||
|
}, delay);
|
||||||
|
};
|
||||||
|
|
||||||
|
var tick = function() {
|
||||||
|
var now = Date.now();
|
||||||
|
|
||||||
|
uci_status_message('notice',
|
||||||
|
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
|
||||||
|
'<%:Waiting for configuration to get applied… %ds%>'.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0)));
|
||||||
|
|
||||||
|
if (now >= deadline)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tt = window.setTimeout(tick, 1000 - (now - ts));
|
||||||
|
ts = now;
|
||||||
|
};
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
/* wait a few seconds for the settings to become effective */
|
||||||
|
window.setTimeout(call, Math.max(uci_apply_holdoff * 1000 - ((ts + uci_apply_rollback * 1000) - deadline), 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function uci_apply(checked) {
|
||||||
|
uci_status_message('notice',
|
||||||
|
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
|
||||||
|
'<%:Starting configuration apply…%>');
|
||||||
|
|
||||||
|
xhr.post('<%=url("admin/uci")%>/' + (checked ? 'apply_rollback' : 'apply_unchecked'), uci_apply_auth, function(r, tok) {
|
||||||
|
if (r.status === (checked ? 200 : 204)) {
|
||||||
|
if (checked && tok !== null && typeof(tok) === 'object' && typeof(tok.token) === 'string')
|
||||||
|
uci_confirm_auth = tok;
|
||||||
|
|
||||||
|
uci_confirm(checked, Date.now() + uci_apply_rollback * 1000);
|
||||||
|
}
|
||||||
|
else if (checked && r.status === 204) {
|
||||||
|
uci_status_message('notice', '<%:There are no changes to apply.%>');
|
||||||
|
window.setTimeout(function() {
|
||||||
|
<% if redirect_ok then -%>
|
||||||
|
location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
|
||||||
|
<%- else -%>
|
||||||
|
uci_status_message(false);
|
||||||
|
<%- end %>
|
||||||
|
}, uci_apply_display * 1000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uci_status_message('warning', '<%_Apply request failed with status <code>%h</code>%>'.format(r.responseText || r.statusText || r.status));
|
||||||
|
window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function uci_revert() {
|
||||||
|
uci_status_message('notice',
|
||||||
|
'<img src="<%=resource%>/icons/loading.gif" alt="" style="vertical-align:middle" /> ' +
|
||||||
|
'<%:Reverting configuration…%>');
|
||||||
|
|
||||||
|
xhr.post('<%=url("admin/uci/revert")%>', uci_apply_auth, function(r) {
|
||||||
|
if (r.status === 200) {
|
||||||
|
uci_status_message('notice', '<%:Changes have been reverted.%>');
|
||||||
|
window.setTimeout(function() {
|
||||||
|
<% if redirect_ok then -%>
|
||||||
|
location.href = decodeURIComponent('<%=luci.util.urlencode(redirect_ok)%>');
|
||||||
|
<%- else -%>
|
||||||
|
window.location = window.location.href.split('#')[0];
|
||||||
|
<%- end %>
|
||||||
|
}, uci_apply_display * 1000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uci_status_message('warning', '<%_Revert request failed with status <code>%h</code>%>'.format(r.statusText || r.status));
|
||||||
|
window.setTimeout(function() { uci_status_message(false); }, uci_apply_display * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//]]></script>
|
||||||
|
<%- end) %>
|
|
@ -1,43 +0,0 @@
|
||||||
<% export("cbi_apply_xhr", function(id, configs, redirect) -%>
|
|
||||||
<fieldset class="cbi-section" id="cbi-apply-<%=id%>">
|
|
||||||
<legend><%:Applying changes%></legend>
|
|
||||||
<script type="text/javascript">//<![CDATA[
|
|
||||||
var apply_xhr = new XHR();
|
|
||||||
|
|
||||||
apply_xhr.post('<%=url('servicectl/restart', table.concat(configs, ","))%>', { token: '<%=token%>' },
|
|
||||||
function() {
|
|
||||||
var checkfinish = function() {
|
|
||||||
apply_xhr.get('<%=url('servicectl/status')%>', null,
|
|
||||||
function(x) {
|
|
||||||
if( x.responseText == 'finish' )
|
|
||||||
{
|
|
||||||
var e = document.getElementById('cbi-apply-<%=id%>-status');
|
|
||||||
if( e )
|
|
||||||
{
|
|
||||||
e.innerHTML = '<%:Configuration applied.%>';
|
|
||||||
window.setTimeout(function() {
|
|
||||||
e.parentNode.style.display = 'none';
|
|
||||||
<% if redirect then %>location.href='<%=redirect%>';<% end %>
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var e = document.getElementById('cbi-apply-<%=id%>-status');
|
|
||||||
if( e && x.responseText ) e.innerHTML = x.responseText;
|
|
||||||
|
|
||||||
window.setTimeout(checkfinish, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.setTimeout(checkfinish, 1000);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
//]]></script>
|
|
||||||
|
|
||||||
<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" />
|
|
||||||
<span id="cbi-apply-<%=id%>-status"><%:Waiting for changes to be applied...%></span>
|
|
||||||
</fieldset>
|
|
||||||
<%- end) %>
|
|
|
@ -1,6 +1,6 @@
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
<% if self:cfgvalue(section) ~= false then %>
|
<% if self:cfgvalue(section) ~= false then %>
|
||||||
<input class="cbi-button cbi-input-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
|
<input class="cbi-button cbi-button-<%=self.inputstyle or "button" %>" type="submit"<%= attr("name", cbid) .. attr("id", cbid) .. attr("value", self.inputtitle or self.title)%> />
|
||||||
<% else %>
|
<% else %>
|
||||||
-
|
-
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
|
|
|
@ -1,2 +1,12 @@
|
||||||
<td class="cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>">
|
<%-
|
||||||
|
local title = luci.util.trim(striptags(self.title))
|
||||||
|
local descr = luci.util.trim(striptags(self.description))
|
||||||
|
local ftype = self.typename or (self.template and self.template:gsub("^.+/", ""))
|
||||||
|
-%>
|
||||||
|
<div class="td cbi-value-field<% if self.error and self.error[section] then %> cbi-value-error<% end %>"<%=
|
||||||
|
attr("data-name", self.option) ..
|
||||||
|
ifattr(ftype and #ftype > 0, "data-type", ftype) ..
|
||||||
|
ifattr(title and #title > 0, "data-title", title) ..
|
||||||
|
ifattr(descr and #descr > 0, "data-description", descr)
|
||||||
|
%>>
|
||||||
<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>">
|
<div id="cbi-<%=self.config.."-"..section.."-"..self.option%>" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>">
|
||||||
|
|
54
luci-base/luasrc/view/cbi/dropdown.htm
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<%+cbi/valueheader%>
|
||||||
|
|
||||||
|
<%-
|
||||||
|
local selected = { }
|
||||||
|
|
||||||
|
if self.multiple then
|
||||||
|
local val
|
||||||
|
for val in luci.util.imatch(self:cfgvalue(section)) do
|
||||||
|
selected[val] = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
selected[self:cfgvalue(section)] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not next(selected) and self.default then
|
||||||
|
selected[self.default] = true
|
||||||
|
end
|
||||||
|
-%>
|
||||||
|
|
||||||
|
<div class="cbi-dropdown"<%=
|
||||||
|
attr("name", cbid) ..
|
||||||
|
attr("display-items", self.display or self.size or 3) ..
|
||||||
|
attr("dropdown-items", self.dropdown or self.display or self.size or 5) ..
|
||||||
|
attr("placeholder", self.placeholder or translate("-- please select --")) ..
|
||||||
|
ifattr(self.multiple, "multiple", "multiple") ..
|
||||||
|
ifattr(self.optional or self.rmempty, "optional", "optional")
|
||||||
|
%>>
|
||||||
|
<ul>
|
||||||
|
<% local i, key; for i, key in pairs(self.keylist) do %>
|
||||||
|
<li<%=
|
||||||
|
attr("data-index", i) ..
|
||||||
|
attr("data-depends", self:deplist2json(section, self.deplist[i])) ..
|
||||||
|
attr("value", key) ..
|
||||||
|
ifattr(selected[key], "selected", "selected")
|
||||||
|
%>>
|
||||||
|
<%=pcdata(self.vallist[i])%>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
<% if self.custom then %>
|
||||||
|
<li>
|
||||||
|
<input type="password" style="display:none" />
|
||||||
|
<input class="create-item-input" type="text"<%=
|
||||||
|
attr("placeholder", self.custom ~= true and
|
||||||
|
self.custom or
|
||||||
|
(self.multiple and
|
||||||
|
translate("Enter custom values") or
|
||||||
|
translate("Enter custom value")))
|
||||||
|
%> />
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%+cbi/valuefooter%>
|
|
@ -14,46 +14,59 @@
|
||||||
local def = fwm:get_defaults()
|
local def = fwm:get_defaults()
|
||||||
local zone = fwm:get_zone(value)
|
local zone = fwm:get_zone(value)
|
||||||
local empty = true
|
local empty = true
|
||||||
|
|
||||||
|
local function render_zone(zone)
|
||||||
|
-%>
|
||||||
|
<label class="zonebadge" style="background-color:<%=zone:get_color()%>">
|
||||||
|
<strong><%=zone:name()%></strong>
|
||||||
|
<div class="cbi-tooltip">
|
||||||
|
<%-
|
||||||
|
local zempty = true
|
||||||
|
for _, net in ipairs(zone:get_networks()) do
|
||||||
|
net = nwm:get_network(net)
|
||||||
|
if net then
|
||||||
|
zempty = false
|
||||||
|
-%>
|
||||||
|
<span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>: 
|
||||||
|
<%
|
||||||
|
local nempty = true
|
||||||
|
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
|
||||||
|
nempty = false
|
||||||
|
%>
|
||||||
|
<img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
||||||
|
<% end %>
|
||||||
|
<% if nempty then %><em><%:(empty)%></em><% end %>
|
||||||
|
</span>
|
||||||
|
<%- end end -%>
|
||||||
|
<% if zempty then %><span class="ifacebadge"><em><%:(empty)%></em></span><% end %>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<%-
|
||||||
|
end
|
||||||
-%>
|
-%>
|
||||||
|
|
||||||
<% if zone then %>
|
<% if zone then %>
|
||||||
<div style="white-space:nowrap">
|
<div class="zone-forwards">
|
||||||
<label class="zonebadge" style="background-color:<%=zone:get_color()%>">
|
<div class="zone-src">
|
||||||
<strong><%=zone:name()%>:</strong>
|
<%=render_zone(zone)%>
|
||||||
<%-
|
</div>
|
||||||
local zempty = true
|
<span>⇒</span>
|
||||||
for _, net in ipairs(zone:get_networks()) do
|
<div class="zone-dest">
|
||||||
net = nwm:get_network(net)
|
<%
|
||||||
if net then
|
for _, fwd in ipairs(zone:get_forwardings_by("src")) do
|
||||||
zempty = false
|
fz = fwd:dest_zone()
|
||||||
-%>
|
if fz then
|
||||||
<span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
|
empty = false
|
||||||
<%
|
render_zone(fz)
|
||||||
local nempty = true
|
end
|
||||||
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
|
end
|
||||||
nempty = false
|
if empty then
|
||||||
%>
|
%>
|
||||||
<img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
|
||||||
<% end %>
|
|
||||||
<% if nempty then %><em><%:(empty)%></em><% end %>
|
|
||||||
</span>
|
|
||||||
<%- end end -%>
|
|
||||||
<%- if zempty then %><em><%:(empty)%></em><% end -%>
|
|
||||||
</label>
|
|
||||||
 ⇒ 
|
|
||||||
<% for _, fwd in ipairs(zone:get_forwardings_by("src")) do
|
|
||||||
fz = fwd:dest_zone()
|
|
||||||
if fz then
|
|
||||||
empty = false %>
|
|
||||||
<label class="zonebadge" style="background-color:<%=fz:get_color()%>">
|
|
||||||
<strong><%=fz:name()%></strong>
|
|
||||||
</label> 
|
|
||||||
<% end end %>
|
|
||||||
<% if empty then %>
|
|
||||||
<label class="zonebadge zonebadge-empty">
|
<label class="zonebadge zonebadge-empty">
|
||||||
<strong><%=zone:forward():upper()%></strong>
|
<strong><%=zone:forward():upper()%></strong>
|
||||||
</label>
|
</label>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
|
@ -24,26 +24,42 @@
|
||||||
end
|
end
|
||||||
-%>
|
-%>
|
||||||
|
|
||||||
<span>
|
<div class="cbi-dropdown" dropdown-items="5" placeholder="<%:-- please select -- %>"<%=
|
||||||
<ul style="margin:0; list-style-type:none; text-align:left">
|
attr("name", cbid) ..
|
||||||
|
ifattr(self.widget == "checkbox", "multiple", "multiple") ..
|
||||||
|
ifattr(self.rmempty or self.optional, "optional", "optional")
|
||||||
|
%>>
|
||||||
|
<script type="item-template"><!--
|
||||||
|
<li value="{{value}}">
|
||||||
|
<span class="zonebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">
|
||||||
|
<strong>{{value}}:</strong><em>(<%:create%>)</em>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
--></script>
|
||||||
|
<ul>
|
||||||
<% if self.allowlocal then %>
|
<% if self.allowlocal then %>
|
||||||
<li style="padding:0.5em">
|
<li value=""<%=ifattr(checked[""], "selected", "selected")%>>
|
||||||
<input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_empty") .. attr("name", cbid) .. attr("value", "") .. ifattr(checked[""], "checked", "checked")%> />  
|
<span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
|
||||||
<label<%=attr("for", cbid .. "_empty")%>></label>
|
|
||||||
<label<%=attr("for", cbid .. "_empty")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
|
|
||||||
<strong><%:Device%></strong>
|
<strong><%:Device%></strong>
|
||||||
<% if self.allowany and self.allowlocal then %>(<%:input%>)<% end %>
|
<% if self.allowany and self.allowlocal then -%>
|
||||||
</label>
|
(<%= self.alias ~= "dest"
|
||||||
|
and translate("output") or translate("input") %>)
|
||||||
|
<%- end %>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<% elseif self.widget ~= "checkbox" and (self.rmempty or self.optional) then %>
|
||||||
|
<li value=""<%=ifattr(checked[""], "selected", "selected")%>>
|
||||||
|
<span class="zonebadge">
|
||||||
|
<em><%:unspecified%></em>
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if self.allowany then %>
|
<% if self.allowany then %>
|
||||||
<li style="padding:0.5em">
|
<li value="*"<%=ifattr(checked["*"], "selected", "selected")%>>
|
||||||
<input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_any") .. attr("name", cbid) .. attr("value", "*") .. ifattr(checked["*"], "checked", "checked")%> />  
|
<span style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
|
||||||
<label<%=attr("for", cbid .. "_any")%>></label>
|
|
||||||
<label<%=attr("for", cbid .. "_any")%> style="background-color:<%=fwm.zone.get_color()%>" class="zonebadge">
|
|
||||||
<strong><%:Any zone%></strong>
|
<strong><%:Any zone%></strong>
|
||||||
<% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
|
<% if self.allowany and self.allowlocal then %>(<%:forward%>)<% end %>
|
||||||
</label>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<%
|
<%
|
||||||
|
@ -51,45 +67,42 @@
|
||||||
if zone:name() ~= self.exclude then
|
if zone:name() ~= self.exclude then
|
||||||
selected = selected or (value == zone:name())
|
selected = selected or (value == zone:name())
|
||||||
%>
|
%>
|
||||||
<li style="padding:0.5em">
|
<li<%=attr("value", zone:name()) .. ifattr(checked[zone:name()], "selected", "selected")%>>
|
||||||
<input class="cbi-input-radio" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "." .. zone:name()) .. attr("name", cbid) .. attr("value", zone:name()) .. ifattr(checked[zone:name()], "checked", "checked")%> />  
|
<span style="background-color:<%=zone:get_color()%>" class="zonebadge">
|
||||||
<label<%=attr("for", cbid .. "." .. zone:name())%>></label>
|
|
||||||
<label<%=attr("for", cbid .. "." .. zone:name())%> style="background-color:<%=zone:get_color()%>" class="zonebadge">
|
|
||||||
<strong><%=zone:name()%>:</strong>
|
<strong><%=zone:name()%>:</strong>
|
||||||
<%
|
<%-
|
||||||
local zempty = true
|
local zempty = true
|
||||||
for _, net in ipairs(zone:get_networks()) do
|
for _, net in ipairs(zone:get_networks()) do
|
||||||
net = nwm:get_network(net)
|
net = nwm:get_network(net)
|
||||||
if net then
|
if net then
|
||||||
zempty = false
|
zempty = false
|
||||||
%>
|
-%>
|
||||||
<span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
|
<span class="ifacebadge<% if net:name() == self.network then %> ifacebadge-active<% end %>"><%=net:name()%>:
|
||||||
<%
|
<%-
|
||||||
local nempty = true
|
local nempty = true
|
||||||
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
|
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
|
||||||
nempty = false
|
nempty = false
|
||||||
%>
|
%>
|
||||||
<img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
<img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if nempty then %><em><%:(empty)%></em><% end %>
|
<% if nempty then %><em><%:(empty)%></em><% end -%>
|
||||||
</span>
|
</span>
|
||||||
<% end end %>
|
<%- end end -%>
|
||||||
<% if zempty then %><em><%:(empty)%></em><% end %>
|
<%- if zempty then %><em><%:(empty)%></em><% end -%>
|
||||||
</label>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<% end end %>
|
<% end end %>
|
||||||
|
|
||||||
<% if self.widget ~= "checkbox" and not self.nocreate then %>
|
<% if self.widget ~= "checkbox" and not self.nocreate then %>
|
||||||
<li style="padding:0.5em">
|
<li value="-">
|
||||||
<input class="cbi-input-radio" data-update="click change" type="radio"<%=attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not selected, "checked", "checked")%> />  
|
<span class="zonebadge">
|
||||||
<label<%=attr("for", cbid .. "_new")%>></label>
|
<em><%:create%>:</em>
|
||||||
<div onclick="document.getElementById('<%=cbid%>_new').checked=true" class="zonebadge" style="background-color:<%=fwm.zone.get_color()%>">
|
<input type="password" style="display:none" />
|
||||||
<em><%:unspecified -or- create:%> </em>
|
<input class="create-item-input" type="text" />
|
||||||
<input type="text"<%=attr("name", cbid .. ".newzone") .. ifattr(not selected, "value", luci.http.formvalue(cbid .. ".newzone") or self.default)%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
|
</span>
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
</span>
|
</div>
|
||||||
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -1,25 +1,39 @@
|
||||||
<%- if pageaction then -%>
|
<%
|
||||||
<div class="cbi-page-actions">
|
local display_back = (redirect and not flow.hidebackbtn)
|
||||||
<% if redirect and not flow.hidebackbtn then %>
|
local display_skip = (flow.skip)
|
||||||
<div style="float:left">
|
local display_apply = (not autoapply and not flow.hideapplybtn)
|
||||||
<input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" />
|
local display_save = (not flow.hidesavebtn)
|
||||||
</div>
|
local display_reset = (not flow.hideresetbtn)
|
||||||
<% end %>
|
|
||||||
|
if pageaction and
|
||||||
|
(display_back or display_skip or display_apply or display_save or display_reset)
|
||||||
|
then
|
||||||
|
%><div class="cbi-page-actions"><%
|
||||||
|
|
||||||
|
if display_back then
|
||||||
|
%><input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_skip then
|
||||||
|
%><input class="cbi-button cbi-button-skip" type="button" value="<%:Skip%>" onclick="cbi_submit(this, 'cbi.skip')" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_apply then
|
||||||
|
%><input class="cbi-button cbi-button-apply" type="button" value="<%:Save & Apply%>" onclick="cbi_submit(this, 'cbi.apply')" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_save then
|
||||||
|
%><input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_reset then
|
||||||
|
%><input class="cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
|
||||||
<% if flow.skip then %>
|
|
||||||
<input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
|
|
||||||
<% end %>
|
|
||||||
<% if not autoapply and not flow.hideapplybtn then %>
|
|
||||||
<input class="cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
|
|
||||||
<% end %>
|
|
||||||
<% if not flow.hidesavebtn then %>
|
|
||||||
<input class="cbi-button cbi-button-save" type="submit" value="<%:Save%>" />
|
|
||||||
<% end %>
|
|
||||||
<% if not flow.hideresetbtn then %>
|
|
||||||
<input class="cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<%- end -%>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script type="text/javascript">cbi_init();</script>
|
<script type="text/javascript">cbi_init();</script>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<br />
|
<br />
|
||||||
<%- end %>
|
<%- end %>
|
||||||
<div class="cbi-value-description">
|
<div class="cbi-value-description">
|
||||||
<span class="cbi-value-helpicon"><img src="<%=resource%>/cbi/help.gif" alt="<%:help%>" /></span>
|
|
||||||
<%=self.description%>
|
<%=self.description%>
|
||||||
</div>
|
</div>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
<%+header%>
|
<%+header%>
|
||||||
<form method="post" name="cbi" action="<%=REQUEST_URI%>" enctype="multipart/form-data" onreset="return cbi_validate_reset(this)" onsubmit="return cbi_validate_form(this, '<%:Some fields are invalid, cannot save values!%>')">
|
<form method="post" name="cbi" action="<%=REQUEST_URI%>" enctype="multipart/form-data" onreset="return cbi_validate_reset(this)" onsubmit="return cbi_validate_form(this, '<%:Some fields are invalid, cannot save values!%>')"<%=
|
||||||
|
attr("data-strings", luci.util.serialize_json({
|
||||||
|
label = {
|
||||||
|
choose = translate('-- Please choose --'),
|
||||||
|
custom = translate('-- custom --'),
|
||||||
|
},
|
||||||
|
path = {
|
||||||
|
resource = resource,
|
||||||
|
browser = url("admin/filebrowser")
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
%>>
|
||||||
<div>
|
<div>
|
||||||
<script type="text/javascript" src="<%=resource%>/cbi.js"<%=
|
|
||||||
attr("data-strings", luci.util.serialize_json({
|
|
||||||
label = {
|
|
||||||
choose = translate('-- Please choose --'),
|
|
||||||
custom = translate('-- custom --'),
|
|
||||||
},
|
|
||||||
path = {
|
|
||||||
resource = resource,
|
|
||||||
browser = url("admin/filebrowser")
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
%>></script>
|
|
||||||
<input type="hidden" name="token" value="<%=token%>" />
|
<input type="hidden" name="token" value="<%=token%>" />
|
||||||
<input type="hidden" name="cbi.submit" value="1" />
|
<input type="hidden" name="cbi.submit" value="1" />
|
||||||
<input type="submit" value="<%:Save%>" class="hidden" />
|
<input type="submit" value="<%:Save%>" class="hidden" />
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
<%- if firstmap and messages then local msg; for _, msg in ipairs(messages) do -%>
|
<%- if firstmap and messages then local msg; for _, msg in ipairs(messages) do -%>
|
||||||
<div class="errorbox"><%=pcdata(msg)%></div>
|
<div class="alert-message warning"><%=pcdata(msg)%></div>
|
||||||
<%- end end -%>
|
<%- end end -%>
|
||||||
|
|
||||||
<%-+cbi/apply_xhr-%>
|
|
||||||
|
|
||||||
<div class="cbi-map" id="cbi-<%=self.config%>">
|
<div class="cbi-map" id="cbi-<%=self.config%>">
|
||||||
<% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
|
<% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
|
||||||
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
|
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
|
||||||
<%- if firstmap and applymap then cbi_apply_xhr(self.config, parsechain, redirect) end -%>
|
|
||||||
|
|
||||||
<% if self.tabbed then %>
|
<% if self.tabbed then %>
|
||||||
<ul class="cbi-tabmenu map">
|
<ul class="cbi-tabmenu map">
|
||||||
<%- self.selected_tab = luci.http.formvalue("tab.m-" .. self.config) %>
|
<%- self.selected_tab = luci.http.formvalue("tab.m-" .. self.config) %>
|
||||||
|
@ -20,7 +16,6 @@
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
</ul>
|
</ul>
|
||||||
<br />
|
|
||||||
<% for i, section in ipairs(self.children) do %>
|
<% for i, section in ipairs(self.children) do %>
|
||||||
<div class="cbi-tabcontainer" id="container.m-<%=self.config%>.<%=section.section or section.sectiontype%>"<% if section.sectiontype ~= self.selected_tab then %> style="display:none"<% end %>>
|
<div class="cbi-tabcontainer" id="container.m-<%=self.config%>.<%=section.section or section.sectiontype%>"<% if section.sectiontype ~= self.selected_tab then %> style="display:none"<% end %>>
|
||||||
<% section:render() %>
|
<% section:render() %>
|
||||||
|
@ -42,6 +37,4 @@
|
||||||
<% else %>
|
<% else %>
|
||||||
<%- self:render_children() %>
|
<%- self:render_children() %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<br />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,71 +19,73 @@
|
||||||
|
|
||||||
if value then
|
if value then
|
||||||
for value in utl.imatch(value) do
|
for value in utl.imatch(value) do
|
||||||
checked[value] = true
|
for value in utl.imatch(value) do
|
||||||
|
checked[value] = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local n = self.network and net:get_network(self.network)
|
local n = self.network and net:get_network(self.network)
|
||||||
if n then
|
if n then
|
||||||
local i
|
local a = n:is_alias()
|
||||||
for _, i in ipairs(n:get_interfaces() or { n:get_interface() }) do
|
if a then
|
||||||
checked[i:name()] = true
|
checked['@' .. a] = true
|
||||||
|
else
|
||||||
|
local i
|
||||||
|
for _, i in ipairs(n:get_interfaces() or { n:get_interface() }) do
|
||||||
|
checked[i:name()] = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-%>
|
-%>
|
||||||
|
|
||||||
<input type="hidden" name="<%=cbeid%>" value="1" />
|
<input type="hidden" name="<%=cbeid%>" value="1" />
|
||||||
<ul style="margin:0; list-style-type:none">
|
|
||||||
<% for _, iface in ipairs(ifaces) do
|
<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%=
|
||||||
local link = iface:adminlink()
|
attr("name", cbid) ..
|
||||||
if (not self.nobridges or not iface:is_bridge()) and
|
ifattr(self.widget == "checkbox", "multiple", "multiple") ..
|
||||||
(not self.noinactive or iface:is_up()) and
|
ifattr(self.widget == "checkbox", "optional", "optional")
|
||||||
iface:name() ~= self.exclude
|
%>>
|
||||||
then %>
|
<script type="item-template"><!--
|
||||||
<li>
|
<li value="{{value}}">
|
||||||
<input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
|
<img title="<%:Custom Interface%>: "{{value}}"" src="<%=resource%>/icons/ethernet_disabled.png" />
|
||||||
attr("type", self.widget or "radio") ..
|
<span class="hide-open">{{value}}</span>
|
||||||
attr("id", cbid .. "." .. iface:name()) ..
|
<span class="hide-close"><%:Custom Interface%>: "{{value}}"</span>
|
||||||
attr("name", cbid) .. attr("value", iface:name()) ..
|
</li>
|
||||||
ifattr(checked[iface:name()], "checked", "checked")
|
--></script>
|
||||||
%> />
|
<ul>
|
||||||
<%- if not self.widget or self.widget == "checkbox" or self.widget == "radio" then -%>
|
<% for _, iface in ipairs(ifaces) do
|
||||||
<label<%=attr("for", cbid .. "." .. iface:name())%>></label>
|
if (not self.noaliases or iface:type() ~= "alias") and
|
||||||
<%- end -%>
|
(not self.nobridges or not iface:is_bridge()) and
|
||||||
 
|
(not self.noinactive or iface:is_up()) and
|
||||||
<label<%=attr("for", cbid .. "." .. iface:name())%>>
|
iface:name() ~= self.exclude
|
||||||
<% if link then -%><a href="<%=link%>"><% end -%>
|
then %>
|
||||||
<img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
<li<%=
|
||||||
<% if link then -%></a><% end -%>
|
attr("value", iface:name()) ..
|
||||||
<%=pcdata(iface:get_i18n())%>
|
ifattr(checked[iface:name()], "selected", "selected")
|
||||||
<% local ns = iface:get_networks(); if #ns > 0 then %>(
|
%>>
|
||||||
<%- local i, n; for i, n in ipairs(ns) do -%>
|
<img<%=attr("title", iface:get_i18n())%> src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
||||||
<%-= (i>1) and ', ' -%>
|
<span class="hide-open"><%=pcdata(iface:name())%></span>
|
||||||
<a href="<%=n:adminlink()%>"><%=n:name()%></a>
|
<span class="hide-close">
|
||||||
<%- end -%>
|
<%=pcdata(iface:get_i18n())%>
|
||||||
)<% end %>
|
<% local ns = iface:get_networks(); if #ns > 0 then %>(
|
||||||
</label>
|
<%- local i, n; for i, n in ipairs(ns) do -%>
|
||||||
</li>
|
<%-= (i>1) and ', ' -%>
|
||||||
<% end end %>
|
<a href="<%=n:adminlink()%>"><%=n:name()%></a>
|
||||||
<% if not self.nocreate then %>
|
<%- end -%>
|
||||||
<li>
|
)<% end %>
|
||||||
<input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
|
</span>
|
||||||
attr("type", self.widget or "radio") ..
|
</li>
|
||||||
attr("id", cbid .. "_custom") ..
|
<% end end %>
|
||||||
attr("name", cbid) ..
|
<% if not self.nocreate then %>
|
||||||
attr("value", " ")
|
<li value="">
|
||||||
%> />
|
<img title="<%:Custom Interface%>" src="<%=resource%>/icons/ethernet_disabled.png" />
|
||||||
<%- if not self.widget or self.widget == "checkbox" or self.widget == "radio" then -%>
|
<span><%:Custom Interface%>:</span>
|
||||||
<label<%=attr("for", cbid .. "_custom")%>></label>
|
<input type="password" style="display:none" />
|
||||||
<%- end -%>
|
<input class="create-item-input" type="text" />
|
||||||
 
|
</li>
|
||||||
<label<%=attr("for", cbid .. "_custom")%>>
|
<% end %>
|
||||||
<img title="<%:Custom Interface%>" style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/ethernet_disabled.png" />
|
</ul>
|
||||||
<%:Custom Interface%>:
|
</div>
|
||||||
</label>
|
|
||||||
<input type="text" style="width:50px" onfocus="document.getElementById('<%=cbid%>_custom').checked=true" onblur="var x=document.getElementById('<%=cbid%>_custom'); x.value=this.value; x.checked=true" />
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -20,66 +20,62 @@
|
||||||
end
|
end
|
||||||
-%>
|
-%>
|
||||||
|
|
||||||
<ul style="margin:0; list-style-type:none; text-align:left">
|
<div class="cbi-dropdown" display-items="5" placeholder="<%:-- please select -- %>"<%=
|
||||||
<% for _, net in ipairs(networks) do
|
attr("name", cbid) ..
|
||||||
if (net:name() ~= "loopback") and
|
ifattr(self.widget == "checkbox", "multiple", "multiple") ..
|
||||||
(net:name() ~= self.exclude) and
|
ifattr(self.widget == "checkbox", "optional", "optional")
|
||||||
(not self.novirtual or not net:is_virtual())
|
%>>
|
||||||
then %>
|
<script type="item-template"><!--
|
||||||
<li style="padding:0.25em 0">
|
<li value="{{value}}">
|
||||||
<input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
|
<span class="ifacebadge" style="background:repeating-linear-gradient(45deg,rgba(204,204,204,0.5),rgba(204,204,204,0.5) 5px,rgba(255,255,255,0.5) 5px,rgba(255,255,255,0.5) 10px)">
|
||||||
attr("type", self.widget or "radio") ..
|
{{value}}: <em>(<%:create%>)</em>
|
||||||
attr("id", cbid .. "." .. net:name()) ..
|
</span>
|
||||||
attr("name", cbid) .. attr("value", net:name()) ..
|
</li>
|
||||||
ifattr(checked[net:name()], "checked", "checked")
|
--></script>
|
||||||
%> />  
|
<ul>
|
||||||
<label<%=attr("for", cbid .. "." .. net:name())%>>
|
<% if self.widget ~= "checkbox" then %>
|
||||||
|
<li value=""<%= ifattr(not value, "selected", "selected") %>>
|
||||||
|
<em><%:unspecified%></em>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% for _, net in ipairs(networks) do
|
||||||
|
if (net:name() ~= "loopback") and
|
||||||
|
(net:name() ~= self.exclude) and
|
||||||
|
(not self.novirtual or not net:is_virtual())
|
||||||
|
then %>
|
||||||
|
<li<%= attr("value", net:name()) .. ifattr(checked[net:name()], "selected", "selected") %>>
|
||||||
<span class="ifacebadge"><%=net:name()%>:
|
<span class="ifacebadge"><%=net:name()%>:
|
||||||
<%
|
<%
|
||||||
local empty = true
|
local empty = true
|
||||||
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
|
for _, iface in ipairs(net:is_bridge() and net:get_interfaces() or { net:get_interface() }) do
|
||||||
if not iface:is_bridge() then
|
if not iface:is_bridge() then
|
||||||
empty = false
|
empty = false
|
||||||
%>
|
-%>
|
||||||
<img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
<img<%=attr("title", iface:get_i18n())%> style="width:16px; height:16px; vertical-align:middle" src="<%=resource%>/icons/<%=iface:type()%><%=iface:is_up() and "" or "_disabled"%>.png" />
|
||||||
<% end end %>
|
<%- end end %>
|
||||||
<% if empty then %><em><%:(no interfaces attached)%></em><% end %>
|
<% if empty then %>
|
||||||
|
<em class="hide-close"><%:(no interfaces attached)%></em>
|
||||||
|
<em class="hide-open">-</em>
|
||||||
|
<% end %>
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</li>
|
||||||
</li>
|
<% end end %>
|
||||||
<% end end %>
|
|
||||||
|
|
||||||
<% if not self.nocreate then %>
|
<% if not self.nocreate then %>
|
||||||
<li style="padding:0.25em 0">
|
<li value="-"<%= ifattr(not value and self.widget ~= "checkbox", "selected", "selected") %>>
|
||||||
<input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=attr("type", self.widget or "radio") .. attr("id", cbid .. "_new") .. attr("name", cbid) .. attr("value", "-") .. ifattr(not value and self.widget ~= "checkbox", "checked", "checked")%> />  
|
<em>
|
||||||
<%- if not self.widget or self.widget == "checkbox" or self.widget == "radio" then -%>
|
|
||||||
<label<%=attr("for", cbid .. "_new")%>></label>
|
|
||||||
<%- end -%>
|
|
||||||
<div style="padding:0.5em; display:inline">
|
|
||||||
<label<%=attr("for", cbid .. "_new")%>><em>
|
|
||||||
<%- if self.widget == "checkbox" then -%>
|
<%- if self.widget == "checkbox" then -%>
|
||||||
<%:create:%>
|
<%:create:%>
|
||||||
<%- else -%>
|
<%- else -%>
|
||||||
<%:unspecified -or- create:%>
|
<%:unspecified -or- create:%>
|
||||||
<%- end -%> </em></label>
|
<%- end -%>
|
||||||
|
</em>
|
||||||
<input style="display:none" type="password" />
|
<input style="display:none" type="password" />
|
||||||
<input style="width:6em" type="text"<%=attr("name", cbid .. ".newnet")%> onfocus="document.getElementById('<%=cbid%>_new').checked=true" />
|
<input class="create-item-input" type="text" />
|
||||||
</div>
|
</li>
|
||||||
</li>
|
<% end %>
|
||||||
<% elseif self.widget ~= "checkbox" and self.unspecified then %>
|
</ul>
|
||||||
<li style="padding:0.25em 0">
|
</div>
|
||||||
<input class="cbi-input-<%=self.widget or "radio"%>" data-update="click change"<%=
|
|
||||||
attr("type", self.widget or "radio") ..
|
|
||||||
attr("id", cbid .. "_uns") ..
|
|
||||||
attr("name", cbid) ..
|
|
||||||
attr("value", "") ..
|
|
||||||
ifattr(not value or #value == 0, "checked", "checked")
|
|
||||||
%> />  
|
|
||||||
<div style="padding:0.5em; display:inline">
|
|
||||||
<label<%=attr("for", cbid .. "_uns")%>><em><%:unspecified%></em></label>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<% if self:cfgvalue(self.section) then section = self.section %>
|
<% if self:cfgvalue(self.section) then section = self.section %>
|
||||||
<fieldset class="cbi-section">
|
<div class="cbi-section">
|
||||||
<% if self.title and #self.title > 0 then -%>
|
<% if self.title and #self.title > 0 then -%>
|
||||||
<legend><%=self.title%></legend>
|
<legend><%=self.title%></legend>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
@ -15,17 +15,16 @@
|
||||||
<div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
|
<div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
|
||||||
<%+cbi/ucisection%>
|
<%+cbi/ucisection%>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
</div>
|
||||||
</fieldset>
|
|
||||||
<% elseif self.addremove then %>
|
<% elseif self.addremove then %>
|
||||||
<% if self.template_addremove then include(self.template_addremove) else -%>
|
<% if self.template_addremove then include(self.template_addremove) else -%>
|
||||||
<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.section%>">
|
<div class="cbi-section" id="cbi-<%=self.config%>-<%=self.section%>">
|
||||||
<% if self.title and #self.title > 0 then -%>
|
<% if self.title and #self.title > 0 then -%>
|
||||||
<legend><%=self.title%></legend>
|
<legend><%=self.title%></legend>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
<div class="cbi-section-descr"><%=self.description%></div>
|
<div class="cbi-section-descr"><%=self.description%></div>
|
||||||
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cns.<%=self.config%>.<%=self.section%>" value="<%:Add%>" />
|
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cns.<%=self.config%>.<%=self.section%>" value="<%:Add%>" />
|
||||||
</fieldset>
|
</div>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<!-- /nsection -->
|
<!-- /nsection -->
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<fieldset class="cbi-section">
|
<div class="cbi-section">
|
||||||
<% if self.title and #self.title > 0 then -%>
|
<% if self.title and #self.title > 0 then -%>
|
||||||
<legend><%=self.title%></legend>
|
<legend><%=self.title%></legend>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
@ -25,8 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
</div>
|
||||||
</fieldset>
|
|
||||||
<%-
|
<%-
|
||||||
if type(self.hidden) == "table" then
|
if type(self.hidden) == "table" then
|
||||||
for k, v in pairs(self.hidden) do
|
for k, v in pairs(self.hidden) do
|
||||||
|
|
|
@ -1,59 +1,77 @@
|
||||||
<% if not self.embedded then %>
|
<%
|
||||||
<form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>">
|
if not self.embedded then
|
||||||
<div>
|
%><form method="post" enctype="multipart/form-data" action="<%=REQUEST_URI%>">
|
||||||
<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
|
<input type="hidden" name="token" value="<%=token%>" />
|
||||||
<input type="hidden" name="token" value="<%=token%>" />
|
<input type="hidden" name="cbi.submit" value="1" /><%
|
||||||
<input type="hidden" name="cbi.submit" value="1" />
|
end
|
||||||
</div>
|
|
||||||
<% end %>
|
%><div class="cbi-map" id="cbi-<%=self.config%>"><%
|
||||||
<div class="cbi-map" id="cbi-<%=self.config%>">
|
|
||||||
<% if self.title and #self.title > 0 then %><h2 name="content"><%=self.title%></h2><% end %>
|
if self.title and #self.title > 0 then
|
||||||
<% if self.description and #self.description > 0 then %><div class="cbi-map-descr"><%=self.description%></div><% end %>
|
%><h2 name="content"><%=self.title%></h2><%
|
||||||
<% self:render_children() %>
|
end
|
||||||
<br />
|
|
||||||
</div>
|
if self.description and #self.description > 0 then
|
||||||
<%- if self.message then %>
|
%><div class="cbi-map-descr"><%=self.description%></div><%
|
||||||
<div><%=self.message%></div>
|
end
|
||||||
<%- end %>
|
|
||||||
<%- if self.errmessage then %>
|
self:render_children()
|
||||||
<div class="error"><%=self.errmessage%></div>
|
|
||||||
<%- end %>
|
%></div><%
|
||||||
<% if not self.embedded then %>
|
|
||||||
<div class="cbi-page-actions">
|
if self.message then
|
||||||
<%-
|
%><div class="alert-message notice"><%=self.message%></div><%
|
||||||
if type(self.hidden) == "table" then
|
end
|
||||||
for k, v in pairs(self.hidden) do
|
|
||||||
-%>
|
if self.errmessage then
|
||||||
<input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" />
|
%><div class="alert-message warning"><%=self.errmessage%></div><%
|
||||||
<%-
|
end
|
||||||
|
|
||||||
|
if not self.embedded then
|
||||||
|
if type(self.hidden) == "table" then
|
||||||
|
local k, v
|
||||||
|
for k, v in pairs(self.hidden) do
|
||||||
|
%><input type="hidden" id="<%=k%>" name="<%=k%>" value="<%=pcdata(v)%>" /><%
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local display_back = (redirect)
|
||||||
|
local display_cancel = (self.cancel ~= false and self.on_cancel)
|
||||||
|
local display_skip = (self.flow and self.flow.skip)
|
||||||
|
local display_submit = (self.submit ~= false)
|
||||||
|
local display_reset = (self.reset ~= false)
|
||||||
|
|
||||||
|
if display_back or display_cancel or display_skip or display_submit or display_reset then
|
||||||
|
%><div class="cbi-page-actions"><%
|
||||||
|
|
||||||
|
if display_back then
|
||||||
|
%><input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_cancel then
|
||||||
|
local label = pcdata(self.cancel or translate("Cancel"))
|
||||||
|
%><input class="cbi-button cbi-button-link" type="button" value="<%=label%>" onclick="cbi_submit(this, 'cbi.cancel')" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_skip then
|
||||||
|
%><input class="cbi-button cbi-button-neutral" type="button" value="<%:Skip%>" onclick="cbi_submit(this, 'cbi.skip')" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_submit then
|
||||||
|
local label = pcdata(self.submit or translate("Submit"))
|
||||||
|
%><input class="cbi-button cbi-button-save" type="submit" value="<%=label%>" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
if display_reset then
|
||||||
|
local label = pcdata(self.reset or translate("Reset"))
|
||||||
|
%><input class="cbi-button cbi-button-reset" type="reset" value="<%=label%>" /> <%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></form><%
|
||||||
end
|
end
|
||||||
%>
|
%>
|
||||||
<% if redirect then %>
|
|
||||||
<div style="float:left">
|
|
||||||
<input class="cbi-button cbi-button-link" type="button" value="<%:Back to Overview%>" onclick="location.href='<%=pcdata(redirect)%>'" />
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
<%- if self.flow and self.flow.skip then %>
|
|
||||||
<input class="cbi-button cbi-button-skip" type="submit" name="cbi.skip" value="<%:Skip%>" />
|
|
||||||
<% end %>
|
|
||||||
<%- if self.submit ~= false then %>
|
|
||||||
<input class="cbi-button cbi-button-save" type="submit" value="
|
|
||||||
<%- if not self.submit then -%><%-:Submit-%><%-else-%><%=self.submit%><%end-%>
|
|
||||||
" />
|
|
||||||
<% end %>
|
|
||||||
<%- if self.reset ~= false then %>
|
|
||||||
<input class="cbi-button cbi-button-reset" type="reset" value="
|
|
||||||
<%- if not self.reset then -%><%-:Reset-%><%-else-%><%=self.reset%><%end-%>
|
|
||||||
" />
|
|
||||||
<% end %>
|
|
||||||
<%- if self.cancel ~= false and self.on_cancel then %>
|
|
||||||
<input class="cbi-button cbi-button-reset" type="submit" name="cbi.cancel" value="
|
|
||||||
<%- if not self.cancel then -%><%-:Cancel-%><%-else-%><%=self.cancel%><%end-%>
|
|
||||||
" />
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<script type="text/javascript">cbi_init();</script>
|
<script type="text/javascript">cbi_init();</script>
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<%-
|
<%-
|
||||||
local rowcnt = 1
|
local rowcnt = 0
|
||||||
|
|
||||||
function rowstyle()
|
function rowstyle()
|
||||||
rowcnt = rowcnt + 1
|
rowcnt = rowcnt + 1
|
||||||
return (rowcnt % 2) + 1
|
if rowcnt % 2 == 0 then
|
||||||
|
return " cbi-rowstyle-1"
|
||||||
|
else
|
||||||
|
return " cbi-rowstyle-2"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function width(o)
|
function width(o)
|
||||||
|
@ -14,86 +19,138 @@ function width(o)
|
||||||
end
|
end
|
||||||
return ''
|
return ''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local has_titles = false
|
||||||
|
local has_descriptions = false
|
||||||
|
|
||||||
|
local anonclass = (not self.anonymous or self.sectiontitle) and "named" or "anonymous"
|
||||||
|
local titlename = ifattr(not self.anonymous or self.sectiontitle, "data-title", translate("Name"))
|
||||||
|
|
||||||
|
local i, k
|
||||||
|
for i, k in pairs(self.children) do
|
||||||
|
if not k.typename then
|
||||||
|
k.typename = k.template and k.template:gsub("^.+/", "") or ""
|
||||||
|
end
|
||||||
|
|
||||||
|
if not has_titles and k.title and #k.title > 0 then
|
||||||
|
has_titles = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not has_descriptions and k.description and #k.description > 0 then
|
||||||
|
has_descriptions = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function render_titles()
|
||||||
|
if not has_titles then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
%><div class="tr cbi-section-table-titles <%=anonclass%>"<%=titlename%>><%
|
||||||
|
|
||||||
|
local i, k
|
||||||
|
for i, k in ipairs(self.children) do
|
||||||
|
if not k.optional then
|
||||||
|
%><div class="th cbi-section-table-cell"<%=
|
||||||
|
width(k) .. attr('data-type', k.typename) %>><%
|
||||||
|
|
||||||
|
if k.titleref then
|
||||||
|
%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=k.titleref%>"><%
|
||||||
|
end
|
||||||
|
|
||||||
|
write(k.title)
|
||||||
|
|
||||||
|
if k.titleref then
|
||||||
|
%></a><%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.sortable or self.extedit or self.addremove then
|
||||||
|
%><div class="th cbi-section-table-cell cbi-section-actions"></div><%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
|
||||||
|
rowcnt = rowcnt + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function render_descriptions()
|
||||||
|
if not has_descriptions then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
%><div class="tr cbi-section-table-descr <%=anonclass%>"><%
|
||||||
|
|
||||||
|
local i, k
|
||||||
|
for i, k in ipairs(self.children) do
|
||||||
|
if not k.optional then
|
||||||
|
%><div class="th cbi-section-table-cell"<%=
|
||||||
|
width(k) .. attr("data-type", k.typename) %>><%
|
||||||
|
|
||||||
|
write(k.description)
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.sortable or self.extedit or self.addremove then
|
||||||
|
%><div class="th cbi-section-table-cell cbi-section-actions"></div><%
|
||||||
|
end
|
||||||
|
|
||||||
|
%></div><%
|
||||||
|
|
||||||
|
rowcnt = rowcnt + 1
|
||||||
|
end
|
||||||
|
|
||||||
-%>
|
-%>
|
||||||
|
|
||||||
<!-- tblsection -->
|
<!-- tblsection -->
|
||||||
<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
|
<div class="cbi-section cbi-tblsection" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
|
||||||
<% if self.title and #self.title > 0 then -%>
|
<% if self.title and #self.title > 0 then -%>
|
||||||
<legend><%=self.title%></legend>
|
<h3><%=self.title%></h3>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
<%- if self.sortable then -%>
|
<%- if self.sortable then -%>
|
||||||
<input type="hidden" id="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" name="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" value="" />
|
<input type="hidden" id="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" name="cbi.sts.<%=self.config%>.<%=self.sectiontype%>" value="" />
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
<div class="cbi-section-descr"><%=self.description%></div>
|
<div class="cbi-section-descr"><%=self.description%></div>
|
||||||
<div class="cbi-section-node">
|
<div class="table cbi-section-table">
|
||||||
<%- local count = 0 -%>
|
<%-
|
||||||
<table class="cbi-section-table">
|
render_titles()
|
||||||
<tr class="cbi-section-table-titles">
|
render_descriptions()
|
||||||
<%- if not self.anonymous then -%>
|
|
||||||
<%- if self.sectionhead then -%>
|
|
||||||
<th class="cbi-section-table-cell"><%=self.sectionhead%></th>
|
|
||||||
<%- else -%>
|
|
||||||
<th> </th>
|
|
||||||
<%- end -%>
|
|
||||||
<%- count = count +1; end -%>
|
|
||||||
<%- for i, k in pairs(self.children) do if not k.optional then -%>
|
|
||||||
<th class="cbi-section-table-cell"<%=width(k)%>>
|
|
||||||
<%- if k.titleref then -%><a title="<%=self.titledesc or translate('Go to relevant configuration page')%>" class="cbi-title-ref" href="<%=k.titleref%>"><%- end -%>
|
|
||||||
<%-=k.title-%>
|
|
||||||
<%- if k.titleref then -%></a><%- end -%>
|
|
||||||
</th>
|
|
||||||
<%- count = count + 1; end; end; if self.sortable then -%>
|
|
||||||
<th class="cbi-section-table-cell"><%:Sort%></th>
|
|
||||||
<%- count = count + 1; end; if self.extedit or self.addremove then -%>
|
|
||||||
<th class="cbi-section-table-cell"> </th>
|
|
||||||
<%- count = count + 1; end -%>
|
|
||||||
</tr>
|
|
||||||
<tr class="cbi-section-table-descr">
|
|
||||||
<%- if not self.anonymous then -%>
|
|
||||||
<%- if self.sectiondesc then -%>
|
|
||||||
<th class="cbi-section-table-cell"><%=self.sectiondesc%></th>
|
|
||||||
<%- else -%>
|
|
||||||
<th></th>
|
|
||||||
<%- end -%>
|
|
||||||
<%- end -%>
|
|
||||||
<%- for i, k in pairs(self.children) do if not k.optional then -%>
|
|
||||||
<th class="cbi-section-table-cell"<%=width(k)%>><%=k.description%></th>
|
|
||||||
<%- end; end; if self.sortable then -%>
|
|
||||||
<th class="cbi-section-table-cell"></th>
|
|
||||||
<%- end; if self.extedit or self.addremove then -%>
|
|
||||||
<th class="cbi-section-table-cell"></th>
|
|
||||||
<%- end -%>
|
|
||||||
</tr>
|
|
||||||
<%- local isempty = true
|
|
||||||
for i, k in ipairs(self:cfgsections()) do
|
|
||||||
section = k
|
|
||||||
isempty = false
|
|
||||||
scope = { valueheader = "cbi/cell_valueheader", valuefooter = "cbi/cell_valuefooter" }
|
|
||||||
-%>
|
|
||||||
<tr class="cbi-section-table-row<% if self.extedit or self.rowcolors then %> cbi-rowstyle-<%=rowstyle()%><% end %>" id="cbi-<%=self.config%>-<%=section%>">
|
|
||||||
<% if not self.anonymous then -%>
|
|
||||||
<th><h3><%=(type(self.sectiontitle) == "function") and self:sectiontitle(section) or k%></h3></th>
|
|
||||||
<%- end %>
|
|
||||||
|
|
||||||
|
local isempty, section, i, k = true, nil, nil
|
||||||
|
for i, k in ipairs(self:cfgsections()) do
|
||||||
|
isempty = false
|
||||||
|
section = k
|
||||||
|
|
||||||
<%-
|
local sectionname = striptags((type(self.sectiontitle) == "function") and self:sectiontitle(section) or k)
|
||||||
for k, node in ipairs(self.children) do
|
local sectiontitle = ifattr(sectionname and (not self.anonymous or self.sectiontitle), "data-title", sectionname)
|
||||||
if not node.optional then
|
local colorclass = (self.extedit or self.rowcolors) and rowstyle() or ""
|
||||||
node:render(section, scope or {})
|
local scope = {
|
||||||
end
|
valueheader = "cbi/cell_valueheader",
|
||||||
|
valuefooter = "cbi/cell_valuefooter"
|
||||||
|
}
|
||||||
|
-%>
|
||||||
|
<div class="tr cbi-section-table-row<%=colorclass%>" id="cbi-<%=self.config%>-<%=section%>"<%=sectiontitle%>>
|
||||||
|
<%-
|
||||||
|
local node
|
||||||
|
for k, node in ipairs(self.children) do
|
||||||
|
if not node.optional then
|
||||||
|
node:render(section, scope or {})
|
||||||
end
|
end
|
||||||
-%>
|
end
|
||||||
|
-%>
|
||||||
|
|
||||||
<%- if self.sortable then -%>
|
<%- if self.sortable or self.extedit or self.addremove then -%>
|
||||||
<td class="cbi-section-table-cell">
|
<div class="td cbi-section-table-cell nowrap cbi-section-actions">
|
||||||
<input class="cbi-button cbi-button-up" type="button" value="" onclick="return cbi_row_swap(this, true, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" alt="<%:Move up%>" title="<%:Move up%>" />
|
<div>
|
||||||
<input class="cbi-button cbi-button-down" type="button" value="" onclick="return cbi_row_swap(this, false, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" alt="<%:Move down%>" title="<%:Move down%>" />
|
<%- if self.sortable then -%>
|
||||||
</td>
|
<input class="cbi-button cbi-button-up" type="button" value="<%:Up%>" onclick="return cbi_row_swap(this, true, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" title="<%:Move up%>" />
|
||||||
<%- end -%>
|
<input class="cbi-button cbi-button-down" type="button" value="<%:Down%>" onclick="return cbi_row_swap(this, false, 'cbi.sts.<%=self.config%>.<%=self.sectiontype%>')" title="<%:Move down%>" />
|
||||||
|
<% end; if self.extedit then -%>
|
||||||
<%- if self.extedit or self.addremove then -%>
|
|
||||||
<td class="cbi-section-table-cell">
|
|
||||||
<%- if self.extedit then -%>
|
|
||||||
<input class="cbi-button cbi-button-edit" type="button" value="<%:Edit%>"
|
<input class="cbi-button cbi-button-edit" type="button" value="<%:Edit%>"
|
||||||
<%- if type(self.extedit) == "string" then
|
<%- if type(self.extedit) == "string" then
|
||||||
%> onclick="location.href='<%=self.extedit:format(section)%>'"
|
%> onclick="location.href='<%=self.extedit:format(section)%>'"
|
||||||
|
@ -101,45 +158,46 @@ end
|
||||||
%> onclick="location.href='<%=self:extedit(section)%>'"
|
%> onclick="location.href='<%=self:extedit(section)%>'"
|
||||||
<%- end
|
<%- end
|
||||||
%> alt="<%:Edit%>" title="<%:Edit%>" />
|
%> alt="<%:Edit%>" title="<%:Edit%>" />
|
||||||
<%- end; if self.addremove then %>
|
<% end; if self.addremove then %>
|
||||||
<input class="cbi-button cbi-button-remove" type="submit" value="<%:Delete%>" onclick="this.form.cbi_state='del-section'; return true" name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" />
|
<input class="cbi-button cbi-button-remove" type="submit" value="<%:Delete%>" onclick="this.form.cbi_state='del-section'; return true" name="cbi.rts.<%=self.config%>.<%=k%>" alt="<%:Delete%>" title="<%:Delete%>" />
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
</td>
|
</div>
|
||||||
<%- end -%>
|
</div>
|
||||||
</tr>
|
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
|
</div>
|
||||||
|
<%- end -%>
|
||||||
|
|
||||||
<%- if isempty then -%>
|
<%- if isempty then -%>
|
||||||
<tr class="cbi-section-table-row">
|
<div class="tr cbi-section-table-row placeholder">
|
||||||
<td colspan="<%=count%>"><em><br /><%:This section contains no values yet%></em></td>
|
<div class="td"><em><%:This section contains no values yet%></em></div>
|
||||||
</tr>
|
</div>
|
||||||
<%- end -%>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<% if self.error then %>
|
|
||||||
<div class="cbi-section-error">
|
|
||||||
<ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
|
|
||||||
<li><%=pcdata(e):gsub("\n","<br />")%></li>
|
|
||||||
<%- end end %></ul>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%- if self.addremove then -%>
|
|
||||||
<% if self.template_addremove then include(self.template_addremove) else -%>
|
|
||||||
<div class="cbi-section-create cbi-tblsection-create">
|
|
||||||
<% if self.anonymous then %>
|
|
||||||
<input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
|
|
||||||
<% else %>
|
|
||||||
<% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
|
|
||||||
<input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" data-type="uciname" data-optional="true" />
|
|
||||||
<input class="cbi-button cbi-button-add" type="submit" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" title="<%:Add%>" />
|
|
||||||
<% if self.invalid_cts then -%>
|
|
||||||
<br /><%:Invalid%></div>
|
|
||||||
<%- end %>
|
|
||||||
<% end %>
|
|
||||||
</div>
|
|
||||||
<%- end %>
|
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
|
||||||
|
<% if self.error then %>
|
||||||
|
<div class="cbi-section-error">
|
||||||
|
<ul><% for _, c in pairs(self.error) do for _, e in ipairs(c) do -%>
|
||||||
|
<li><%=pcdata(e):gsub("\n","<br />")%></li>
|
||||||
|
<%- end end %></ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%- if self.addremove then -%>
|
||||||
|
<% if self.template_addremove then include(self.template_addremove) else -%>
|
||||||
|
<div class="cbi-section-create cbi-tblsection-create">
|
||||||
|
<% if self.anonymous then %>
|
||||||
|
<input class="cbi-button cbi-button-add" type="submit" value="<%:Add%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" title="<%:Add%>" />
|
||||||
|
<% else %>
|
||||||
|
<% if self.invalid_cts then -%>
|
||||||
|
<div class="cbi-section-error"><%:Invalid%></div>
|
||||||
|
<%- end %>
|
||||||
|
<div>
|
||||||
|
<input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" data-type="uciname" data-optional="true" />
|
||||||
|
</div>
|
||||||
|
<input class="cbi-button cbi-button-add" type="submit" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" title="<%:Add%>" />
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<%- end %>
|
||||||
|
<%- end -%>
|
||||||
|
</div>
|
||||||
<!-- /tblsection -->
|
<!-- /tblsection -->
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<fieldset class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
|
<div class="cbi-section" id="cbi-<%=self.config%>-<%=self.sectiontype%>">
|
||||||
<% if self.title and #self.title > 0 then -%>
|
<% if self.title and #self.title > 0 then -%>
|
||||||
<legend><%=self.title%></legend>
|
<legend><%=self.title%></legend>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
@ -20,10 +20,9 @@
|
||||||
|
|
||||||
<%+cbi/tabmenu%>
|
<%+cbi/tabmenu%>
|
||||||
|
|
||||||
<fieldset class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
|
<div class="cbi-section-node<% if self.tabs then %> cbi-section-node-tabbed<% end %>" id="cbi-<%=self.config%>-<%=section%>">
|
||||||
<%+cbi/ucisection%>
|
<%+cbi/ucisection%>
|
||||||
</fieldset>
|
</div>
|
||||||
<br />
|
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
|
||||||
<% if isempty then -%>
|
<% if isempty then -%>
|
||||||
|
@ -36,14 +35,15 @@
|
||||||
<% if self.anonymous then -%>
|
<% if self.anonymous then -%>
|
||||||
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" value="<%:Add%>" />
|
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" value="<%:Add%>" />
|
||||||
<%- else -%>
|
<%- else -%>
|
||||||
<% if self.invalid_cts then -%><div class="cbi-section-error"><% end %>
|
|
||||||
<input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>.<%=section%>" data-type="uciname" data-optional="true" />
|
|
||||||
<input type="submit" class="cbi-button cbi-button-add" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" />
|
|
||||||
<% if self.invalid_cts then -%>
|
<% if self.invalid_cts then -%>
|
||||||
<br /><%:Invalid%></div>
|
<div class="cbi-section-error"><%:Invalid%></div>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
|
<div>
|
||||||
|
<input type="text" class="cbi-section-create-name" id="cbi.cts.<%=self.config%>.<%=self.sectiontype%>." name="cbi.cts.<%=self.config%>.<%=self.sectiontype%>." data-type="uciname" data-optional="true" />
|
||||||
|
</div>
|
||||||
|
<input class="cbi-button cbi-button-add" type="submit" onclick="this.form.cbi_state='add-section'; return true" value="<%:Add%>" title="<%:Add%>" />
|
||||||
<%- end %>
|
<%- end %>
|
||||||
</div>
|
</div>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
</fieldset>
|
</div>
|
||||||
|
|
|
@ -32,25 +32,25 @@
|
||||||
|
|
||||||
<% if self.optionals[section] and #self.optionals[section] > 0 or self.dynamic then %>
|
<% if self.optionals[section] and #self.optionals[section] > 0 or self.dynamic then %>
|
||||||
<div class="cbi-optionals" data-index="<%=#self.children + 1%>">
|
<div class="cbi-optionals" data-index="<%=#self.children + 1%>">
|
||||||
<%
|
<%-
|
||||||
if self.dynamic then
|
if self.dynamic then
|
||||||
local keys, vals, name, opt = { }, { }
|
local keys, vals, name, opt = { }, { }
|
||||||
for name, opt in pairs(self.optionals[section]) do
|
for name, opt in pairs(self.optionals[section]) do
|
||||||
keys[#keys+1] = name
|
keys[#keys+1] = name
|
||||||
vals[#vals+1] = opt.title
|
vals[#vals+1] = opt.title
|
||||||
end
|
end
|
||||||
%>
|
-%>
|
||||||
<input type="text" id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>" data-type="uciname" data-optional="true"<%=
|
<input type="text" id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>" data-type="uciname" data-optional="true"<%=
|
||||||
ifattr(#keys > 0, "data-choices", luci.util.json_encode({keys, vals}))
|
ifattr(#keys > 0, "data-choices", luci.util.json_encode({keys, vals}))
|
||||||
%> />
|
%> />
|
||||||
<% else %>
|
<%- else -%>
|
||||||
<select id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>" data-optionals="true">
|
<select id="cbi.opt.<%=self.config%>.<%=section%>" name="cbi.opt.<%=self.config%>.<%=section%>" data-optionals="true">
|
||||||
<option><%: -- Additional Field -- %></option>
|
<option><%: -- Additional Field -- %></option>
|
||||||
<% for key, val in pairs(self.optionals[section]) do -%>
|
<% for key, val in pairs(self.optionals[section]) do -%>
|
||||||
<option id="cbi-<%=self.config.."-"..section.."-"..val.option%>" value="<%=val.option%>" data-index="<%=val.index%>" data-depends="<%=pcdata(val:deplist2json(section))%>"><%=striptags(val.title)%></option>
|
<option id="cbi-<%=self.config.."-"..section.."-"..val.option%>" value="<%=val.option%>" data-index="<%=val.index%>" data-depends="<%=pcdata(val:deplist2json(section))%>"><%=striptags(val.title)%></option>
|
||||||
<%- end %>
|
<%- end %>
|
||||||
</select>
|
</select>
|
||||||
<% end %>
|
<%- end -%>
|
||||||
<input type="submit" class="cbi-button cbi-button-fieldadd" value="<%:Add%>" />
|
<input type="submit" class="cbi-button cbi-button-fieldadd" value="<%:Add%>" />
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<%:Uploaded File%> (<%=t.byte_format(s.size)%>)
|
<%:Uploaded File%> (<%=t.byte_format(s.size)%>)
|
||||||
<% if self.unsafeupload then %>
|
<% if self.unsafeupload then %>
|
||||||
<input type="hidden"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
|
<input type="hidden"<%= attr("value", v) .. attr("name", cbid) .. attr("id", cbid) %> />
|
||||||
<input class="cbi-button cbi-input-image" type="image" value="<%:Replace entry%>" name="cbi.rlf.<%=section .. "." .. self.option%>" alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" />
|
<input class="cbi-button cbi-button-image" type="image" value="<%:Replace entry%>" name="cbi.rlf.<%=section .. "." .. self.option%>" alt="<%:Replace entry%>" title="<%:Replace entry%>" src="<%=resource%>/cbi/reload.gif" />
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
<%+cbi/valueheader%>
|
<%+cbi/valueheader%>
|
||||||
|
<%- if self.password then -%>
|
||||||
|
<input type="password" style="position:absolute; left:-4000px"<%=
|
||||||
|
attr("name", "password." .. cbid)
|
||||||
|
%> />
|
||||||
|
<%- end -%>
|
||||||
<input data-update="change"<%=
|
<input data-update="change"<%=
|
||||||
attr("id", cbid) ..
|
attr("id", cbid) ..
|
||||||
attr("name", cbid) ..
|
attr("name", cbid) ..
|
||||||
attr("type", self.password and "password" or "text") ..
|
attr("type", self.password and "password" or "text") ..
|
||||||
attr("class", self.password and "cbi-input-password" or "cbi-input-text") ..
|
attr("class", self.password and "cbi-input-password" or "cbi-input-text") ..
|
||||||
attr("value", self:cfgvalue(section) or self.default) ..
|
attr("value", self:cfgvalue(section) or self.default) ..
|
||||||
|
ifattr(self.password, "autocomplete", "new-password") ..
|
||||||
ifattr(self.size, "size") ..
|
ifattr(self.size, "size") ..
|
||||||
ifattr(self.placeholder, "placeholder") ..
|
ifattr(self.placeholder, "placeholder") ..
|
||||||
ifattr(self.readonly, "readonly") ..
|
ifattr(self.readonly, "readonly") ..
|
||||||
|
@ -14,5 +20,7 @@
|
||||||
ifattr(self.combobox_manual, "data-manual", self.combobox_manual) ..
|
ifattr(self.combobox_manual, "data-manual", self.combobox_manual) ..
|
||||||
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
|
ifattr(#self.keylist > 0, "data-choices", { self.keylist, self.vallist })
|
||||||
%> />
|
%> />
|
||||||
<% if self.password then %><img src="<%=resource%>/cbi/reload.gif" style="vertical-align:middle" title="<%:Reveal/hide password%>" onclick="var e = document.getElementById('<%=cbid%>'); e.type = (e.type=='password') ? 'text' : 'password';" /><% end %>
|
<%- if self.password then -%>
|
||||||
|
<div class="cbi-button cbi-button-neutral" title="<%:Reveal/hide password%>" onclick="var e = this.previousElementSibling; e.type = (e.type === 'password') ? 'text' : 'password'">∗</div>
|
||||||
|
<% end %>
|
||||||
<%+cbi/valuefooter%>
|
<%+cbi/valuefooter%>
|
||||||
|
|