Compare commits

..

12 Commits

Author SHA1 Message Date
TIP Automation User
4405c01b73 Chg: update image tag in helm values to v2.5.3 2022-09-15 11:37:18 +00:00
Dmitry Dunaev
c1db70d8c4 Merge pull request #196 from Telecominfraproject/feature/wifi-10842--docker-compose--iptocountry-2-5
[WIFI-10842] Add: docker-compose support for iptocountry
2022-09-15 14:29:54 +03:00
Dmitry Dunaev
ef6e4809f5 [WIFI-10842] Add: docker-compose support for iptocountry
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-09-15 14:29:04 +03:00
TIP Automation User
4b190a571f Chg: update image tag in helm values to v2.5.2 2022-07-26 10:29:43 +00:00
Dmitry Dunaev
c6b84434ea [WIFI-1998] Add: gracefull ingress deprecation
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-07-21 17:40:21 +03:00
Dmitry Dunaev
acad0cd99f Merge pull request #96 from Telecominfraproject/fix/wifi-9174--dep-charts-2.5
[WIFI-9174] Fix: switch from deprecated bitnami charts to mirrored ones
2022-06-03 15:52:43 +03:00
Dmitry Dunaev
7f675733df [WIFI-9174] Fix: switch from deprecated bitnami charts to mirrored ones
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-06-03 15:51:56 +03:00
Johann Hoffmann
4f1d05467f Update image tag in helm values to v2.5.1
Signed-off-by: Johann Hoffmann <johann.hoffmann@mailbox.org>
2022-04-29 19:06:38 +02:00
Johann Hoffmann
27bffaa734 Update image tag in helm values to v2.5.1-RC1
Signed-off-by: Johann Hoffmann <johann.hoffmann@mailbox.org>
2022-04-29 18:10:44 +02:00
stephb9959
b005b57137 Fixing https://telecominfraproject.atlassian.net/browse/WIFI-7812 2022-04-28 14:28:19 -07:00
TIP Automation User
2bae52e67e Chg: update image tag in helm values to v2.5.0 2022-03-30 13:48:16 +00:00
TIP Automation User
7766fe08cd Chg: update image tag in helm values to v2.5.0-RC1 2022-02-11 16:02:37 +00:00
314 changed files with 20209 additions and 79428 deletions

View File

@@ -1,178 +1,7 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: true
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Always
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...
BasedOnStyle: LLVM
TabWidth: 4
IndentWidth: 4
UseTab: Always
ColumnLimit: 100
Language: Cpp

View File

@@ -13,7 +13,6 @@ on:
pull_request:
branches:
- master
- 'release/*'
defaults:
run:
@@ -21,13 +20,13 @@ defaults:
jobs:
docker:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
env:
DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
DOCKER_REGISTRY_USERNAME: ucentral
steps:
- name: Checkout actions repo
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
repository: Telecominfraproject/.github
path: github
@@ -40,16 +39,6 @@ jobs:
registry_user: ucentral
registry_password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
- name: Notify on failure via Slack
if: failure() && github.ref == 'refs/heads/master'
uses: rtCamp/action-slack-notify@v2
env:
SLACK_USERNAME: GitHub Actions failure notifier
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: "${{ job.status }}"
SLACK_ICON: https://raw.githubusercontent.com/quintessence/slack-icons/master/images/github-logo-slack-icon.png
SLACK_TITLE: Docker build failed for OWGW service
trigger-testing:
if: startsWith(github.ref, 'refs/pull/')
runs-on: ubuntu-latest
@@ -58,10 +47,10 @@ jobs:
- name: Get base branch name and set as output
id: get_base_branch
run: |
echo "branch=$(echo ${GITHUB_BASE_REF##*/} | sed 's/master/main/g')" >> $GITHUB_OUTPUT
echo ::set-output name=branch::$(echo ${GITHUB_BASE_REF##*/} | sed 's/master/main/g')
- name: Checkout actions repo
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
repository: Telecominfraproject/.github
path: github
@@ -76,26 +65,4 @@ jobs:
workflow: ow_docker-compose.yml
token: ${{ secrets.WLAN_TESTING_PAT }}
ref: master
inputs: '{"deployment_version": "${{ env.BASE_BRANCH }}", "owgw_version": "${{ github.sha }}", "owsec_version": "${{ env.BASE_BRANCH }}", "owfms_version": "${{ env.BASE_BRANCH }}", "owprov_version": "${{ env.BASE_BRANCH }}", "owanalytics_version": "${{ env.BASE_BRANCH }}", "owsub_version": "${{ env.BASE_BRANCH }}", "microservice": "owgw"}'
trigger-deploy-to-dev:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
needs:
- docker
steps:
- name: Checkout actions repo
uses: actions/checkout@v3
with:
repository: Telecominfraproject/.github
path: github
- name: Trigger deployment of the latest version to dev instance and wait for result
uses: ./github/composite-actions/trigger-workflow-and-wait
with:
owner: Telecominfraproject
repo: wlan-testing
workflow: ucentralgw-dev-deployment.yaml
token: ${{ secrets.WLAN_TESTING_PAT }}
ref: master
inputs: '{"force_latest": "true"}'
inputs: '{"owgw_version": "${{ github.sha }}", "owgwui_version": "${{ env.BASE_BRANCH }}", "owsec_version": "${{ env.BASE_BRANCH }}", "owfms_version": "${{ env.BASE_BRANCH }}", "owprov_version": "main", "owprovui_version": "main"}'

View File

@@ -17,10 +17,4 @@ jobs:
- name: Cleanup Docker image with PR branch tag
run: |
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
if [[ ! $PR_BRANCH_TAG =~ (main|master|release-*) ]]; then
echo "PR branch is $PR_BRANCH_TAG, deleting Docker image"
curl -s -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owgw/$PR_BRANCH_TAG"
else
echo "PR branch is $PR_BRANCH_TAG, not deleting Docker image"
fi
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owgw/$PR_BRANCH_TAG"

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout actions repo
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
repository: Telecominfraproject/.github
path: github

View File

@@ -1,41 +0,0 @@
name: Update OpenAPI docs on GitHub Pages
on:
push:
paths:
- 'openapi/**'
branches:
- master
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
docsgen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Generate static HTML page with docs from OpenAPI definition
run: |
docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:v6.2.1 generate -i https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralgw/master/openapi/owgw.yaml -g html2 --skip-validate-spec -o /local/
- name: Update OpenAPI docs
run: |
mkdir tmp-docs
mv index.html tmp-docs/index.html
mkdir -p ~/.ssh
ssh-keyscan -H github.com >> ~/.ssh/known_hosts
echo https://tip-automation:${{ secrets.GIT_PUSH_PAT }}@github.com > ~/.git-credentials
git config --global credential.helper store
git config --global user.email "tip-automation@telecominfraproject.com"
git config --global user.name "TIP Automation User"
git pull
git checkout gh-pages || git checkout -b gh-pages
rm -rf docs
mv tmp-docs docs
git add docs
git commit -m'Update OpenAPI docs for GitHub pages'
git push --set-upstream origin gh-pages

View File

@@ -1,46 +0,0 @@
name: Release chart package
on:
push:
tags:
- 'v*'
defaults:
run:
shell: bash
jobs:
helm-package:
runs-on: ubuntu-latest
env:
HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
HELM_REPO_USERNAME: ucentral
steps:
- name: Checkout uCentral assembly chart repo
uses: actions/checkout@v3
with:
path: wlan-cloud-ucentralgw
- name: Build package
working-directory: wlan-cloud-ucentralgw/helm
run: |
helm plugin install https://github.com/aslafy-z/helm-git --version 0.10.0
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm dependency update
mkdir dist
helm package . -d dist
- name: Generate GitHub release body
working-directory: wlan-cloud-ucentralgw/helm
run: |
pip3 install yq -q
echo "Docker image - tip-tip-wlan-cloud-ucentral.jfrog.io/owgw:$GITHUB_REF_NAME" > release.txt
echo "Helm charted may be attached to this release" >> release.txt
echo "Deployment artifacts may be found in https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/$GITHUB_REF_NAME" >> release.txt
- name: Create GitHub release
uses: softprops/action-gh-release@v1
with:
body_path: wlan-cloud-ucentralgw/helm/release.txt
files: wlan-cloud-ucentralgw/helm/dist/*

4
.gitignore vendored
View File

@@ -21,12 +21,10 @@ _deps
/docker-compose/.env
/docker-compose/.env_*
/cmake-build/
/uploads/
test_scripts/curl/token.json
.vscode/c_cpp_properties.json
test_scripts/curl/result.json
*.swp
helm/charts/*
!helm/charts/.gitkeep
/portal-test/
/src/ow_version.h
.vscode/*

2
.idea/.gitignore generated vendored
View File

@@ -6,5 +6,3 @@
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/
# GitHub Copilot persisted chat sessions
/copilot/chatSessions

3
.idea/misc.xml generated
View File

@@ -1,8 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.9 (wlan-cloud-ucentralgw)" />
</component>
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="CidrRootsConfiguration">
<excludeRoots>

2
.idea/ucentral.iml generated
View File

@@ -2,7 +2,7 @@
<module classpath="CMake" type="CPP_MODULE" version="4">
<component name="FacetManager">
<facet type="Python" name="Python facet">
<configuration sdkName="Python 3.9 (wlan-cloud-ucentralgw)" />
<configuration sdkName="Python 3.9 (venv)" />
</facet>
</component>
</module>

1
.idea/vcs.xml generated
View File

@@ -2,6 +2,5 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/rapidjson-test" vcs="Git" />
</component>
</project>

View File

@@ -1,196 +0,0 @@
# Building from source
In order to build OWGW, you will need to install its dependencies, which includes the following:
- cmake
- boost
- POCO 1.10.1 or later
- a C++17 compiler
- openssl
- libpq-dev (PortgreSQL development libraries)
- mysql-client (MySQL client)
- librdkafka
- cppkafka
The build is done in 2 parts. The first part is to build a local copy of the framework tailored to your environment. This
framework is called [Poco](https://github.com/pocoproject/poco). The version used in this project has a couple of fixes
from the master copy needed for cmake. Please use the version of this [Poco fix](https://github.com/Telecominfraproject/wlan-cloud-lib-poco). Building
Poco may take several minutes depending on the platform you are building on.
## Ubuntu
These instructions have proven to work on Ubuntu 20.4.
```bash
sudo apt install git cmake g++ libssl-dev libmariadb-dev \
libpq-dev libaprutil1-dev apache2-dev libboost-all-dev \
librdkafka-dev // default-libmysqlclient-dev \
nlohmann-json-dev
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
cd poco
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ../..
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
cd cppkafka
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ../..
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
cd valijson
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ../..
git clone https://github.com/fmtlib/fmt --branch 9.0.0 /fmtlib
cd fmtlib
mkdir cmake-build
cd cmake-build
cmake ..
make
make install
cd ../..
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
cd wlan-cloud-ucentralgw
mkdir cmake-build
cd cmake-build
cmake ..
make -j 8
cd ../..
```
## Fedora
The following instructions have proven to work on Fedora 33
```bash
sudo yum install cmake g++ openssl-devel mysql-devel mysql apr-util-devel boost boost-devel \
yaml-cpp-devel lua-devel
sudo dnf install postgresql.x86_64 librdkafka-devel
sudo dnf install postgresql-devel json-devel
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
cd poco
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ../..
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
cd cppkafka
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ../..
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
cd valijson
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ../..
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
cd wlan-cloud-ucentralgw
mkdir cmake-build
cd cmake-build
cmake ..
make
cd ../..
```
## macOS Build
The following instructions have proven to work on macOS Big Sur. You need to install [Homebrew](https://brew.sh/). You must also have installed [XCode for OS X](https://www.freecodecamp.org/news/how-to-download-and-install-xcode/).
```bash
brew install openssl \
cmake \
libpq \
mysql-client \
apr \
apr-util \
boost \
yaml-cpp \
postgresql \
librdkafka \
nlohmann-json \
fmt
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
pushd poco
mkdir cmake-build
push cmake-build
cmake -DOPENSSL_ROOT_DIR=</path/to/openssl> -DENABLE_NETSSL=1 -DENABLE_JWT=1 -DENABLE_CRYPTO=1 ..
cmake --build . --config Release
sudo cmake --build . --target install
popd
popd
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
pushd cppkafka
mkdir cmake-build
pushd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
popd
popd
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
pushd valijson
mkdir cmake-build
pushd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
popd
popd
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
pushd wlan-cloud-ucentralgw
mkdir cmake-build
pushd cmake-build
cmake ..
make -j
popd
popd
```
## Raspberry
The build on a rPI takes a while. You can shorten that build time and requirements by disabling all the larger database
support. You can build with only SQLite support by not installing the packages for PostgreSQL, and MySQL by
adding -DSMALL_BUILD=1 on the cmake build line.
```bash
sudo apt install git cmake g++ libssl-dev libaprutil1-dev apache2-dev \
libboost-all-dev libyaml-cpp-dev
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
cd poco
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ../..
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
cd wlan-cloud-ucentralgw
mkdir cmake-build
cd cmake-build
cmake -DSMALL_BUILD=1 ..
make
cd ../..
```

77
CLI.md
View File

@@ -7,139 +7,139 @@ cli help
## The commands
### getdevice `serial`
### getdevice <serial>
Get the device JSON document.
### deletedevice `serial`
### deletedevice <serial>
Delete the device.
### createdevice `serial` `cfg` `MAC`
### createdevice <serial> <cfg> <MAC>
Create a device using the default configuration.
- `serial`: device serial number
- `cfg`: JSON config file name
- `MAC`: string MAC Address
### getdevicestatus `serial`
### getdevicestatus <serial>
Get the device status JSON document.
- `serial`: device serial number
### getstats `serial`
### getstats <serial>
Get statistics for the device.
- `serial`: device serial number
### gethealthchecks `serial`
### gethealthchecks <serial>
Get healthchecks for a device.
- `serial`: device serial number
### newesthealthchecks `serial`
### newesthealthchecks <serial>
Get newest healthchecks for a device.
- `serial`: device serial number
### lasthealthcheck `serial`
### lasthealthcheck <serial>
Get the last healthcheck
- `serial`: device serial number
### getcapababilities `serial`
### getcapababilities <serial>
Get the device capabilities JSON document.
- `serial`: device serial number
### deletecapababilities `serial`
### deletecapababilities <serial>
Delete the device capabilities JSON.
- `serial`: device serial number
### reboot `serial`
### reboot <serial>
Reboot the device.
- `serial`: device serial number
### upgrade `serial` `URI`
### upgrade <serial> <URI>
Do firmware upgrade for a device.
- `serial`: device serial number
- `URI`: complete URI where the upgrade file exists. No validation is performed.
### leds `serial` `pattern` `duration`
### leds <serial> <pattern> <duration>
Activate LEDs a device.
- `serial`: device serial number
- `pattern`: on/off/blink
- `duration`: number in seconds
### configure `serial` `cfg`
### configure <serial> <cfg>
Change configuration for a device.
- `serial`: device serial number
- `cfg`: JSON config file name
### factory `serial` `keep_redirector`
### factory <serial> <keep_redirector>
Do factory reset for device.
- `serial`: device serial number
- `keep_redirector`: true=keep redirector, false=reset redirector
### request `serial` `message`
### request <serial> <message>
Force a message from the device.
- `serial`: device serial number
- `message`: state or healthcheck
### wifiscan `serial` `verbose`
### wifiscan <serial> <verbose>
Do wifiscan for a device.
- `serial`: device serial number
- `verbose`: verbose=true/false
### telemetry `serial`
### telemetry <serial>
Start `telemetry` stream for a device.
### trace `serial` `duration` `network`
### trace <serial> <duration> <network>
Launch a remote trace for a device.
- `serial`: device serial number
- `duration`: number in seconds
- `network`: which network to perform trace on: lan or wan
### getcommand `command-uuid`
### getcommand <command-uuid>
Get the command JSON document.
- `command-uuid`: command UUID
### deletecommand `command-uuid`
### deletecommand <command-uuid>
Delete the command.
- `command-uuid`: command UUID
### newestcommands `serial`
### newestcommands <serial>
Get the newest commands for a device.
- `serial`: device serial number
### listdevices
List devices.
### listcommands `serial`
### listcommands <serial>
List commands for a specific device.
- `serial`: device serial number
### deletecommands `serial`
### deletecommands <serial>
Delete commands for a device.
- `serial`: device serial number
### getlogs `serial`
### getlogs <serial>
Get logs for the device.
- `serial`: device serial number
### newestlogs `serial`
### newestlogs <serial>
Get the latest logs for the device.
- `serial`: device serial number
### deletelogs `serial`
### deletelogs <serial>
Delete logs for the device.
- `serial`: device serial number
### eventqueue `serial`
### eventqueue <serial>
Request event queue for the device.
- `serial`: device serial number
### listdefaultconfigs
List default configurations.
### createdefaultconfig `name` `ids` `cfg`
### createdefaultconfig <name> <ids> <cfg>
Create a default configuration
- `name`: unique name, no spaces
- `ids`: comma separated list of models
- `cfg`: JSON config file name
### addblacklistdevice `serial` `reason`
### addblacklistdevice <serial> <reason>
Add a device to the black list
- `serial`: serial number of the device to add
- `reason`: reason for blacklisting
@@ -147,7 +147,7 @@ Add a device to the black list
### getblacklist
List all blacklisted devices
### deleteblacklistdevice `serial`
### deleteblacklistdevice <serial>
Add a device to the black list.
- `serial`: device serial number
@@ -157,7 +157,7 @@ Get the number of devices in the DB.
### deviceserialnumbers
Get only the serial numbers.
### selectdevices `serial_list`
### selectdevices <serial_list>
Get a list of devices based on a list.
- `serial_list`: serial numbers (must be comma separated).
@@ -179,30 +179,27 @@ Get the list of subsystems.
### systeminfo
Get basic system information.
### reloadsubsystem `subsystem name`
### reloadsubsystem <subsystem name>
Reload the configuration for a subsystem.### getfile <uuid>
Get the file associated with trace command <uuid>.
- `uuid`: UUID of file to retrieve
### rtty `serial number`
### rtty <serial>
Get the details for an rtty session.
- `serial`: device serial number
### lifetimestats `serial number`
### lifetimestats <serial>
Get the lifetime stats counters for a device
- `serial`: device serial number
### laststats `serial number`
### laststats <serial>
Get the last statistics for a device.
- `serial`: device serial number
### neweststats `serial number`
### neweststats <serial>
Get the newest statistics for a device.
- `serial`: device serial number
### deviceping `serial number`
This will return you the end-to-end latency from command-line to return value.
## Notes
To pass additional flags to the CURL command, create an environment variable called FLAGS and git ve the values you
want. For example, for force all call to use IPv6, set FLAGS=\"-6\", for verbose mode and IPv6, set FLAGS=\"-6 -v\"

View File

@@ -1,8 +1,7 @@
cmake_minimum_required(VERSION 3.13)
project(owgw VERSION 4.1.1)
project(owgw VERSION 2.5.0)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD 17)
if(UNIX AND APPLE)
set(OPENSSL_ROOT_DIR /usr/local/opt/openssl)
@@ -31,32 +30,28 @@ else()
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/build ${BUILD_NUM})
endif()
if(ASAN)
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
add_compile_options(-fsanitize=undefined)
add_link_options(-fsanitize=undefined)
endif()
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_RESULT
OUTPUT_VARIABLE GIT_HASH)
if(NOT GIT_RESULT EQUAL "0")
message(FATAL_ERROR "git rev-parse --short HEAD failed with ${GIT_RESULT}")
message(FATAL_ERROR "git describe --always --tags failed with ${GIT_RESULT}")
endif()
string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}")
endif()
add_definitions(-DTIP_GATEWAY_SERVICE="1" -DPOCO_LOG_DEBUG="1" -DBOOST_NO_CXX98_FUNCTION_BASE=1)
add_definitions(-DTIP_GATEWAY_SERVICE="1")
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost REQUIRED system)
find_package(OpenSSL REQUIRED)
find_package(ZLIB REQUIRED)
find_package(fmt REQUIRED)
find_package(nlohmann_json REQUIRED)
# find_package(valijson REQUIRED)
find_package(nlohmann_json_schema_validator REQUIRED)
if(SMALL_BUILD)
find_package(Poco REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite)
@@ -71,8 +66,6 @@ include_directories(/usr/local/include /usr/local/opt/openssl/include src inclu
configure_file(src/ow_version.h.in ${PROJECT_SOURCE_DIR}/src/ow_version.h @ONLY)
add_compile_options(-Wall -Wextra)
add_executable( owgw
build
src/ow_version.h.in
@@ -81,55 +74,14 @@ add_executable( owgw
src/framework/MicroService.h
src/framework/OpenWifiTypes.h
src/framework/orm.h
src/framework/RESTAPI_errors.h
src/framework/RESTAPI_protocol.h
src/framework/StorageClass.h
src/framework/MicroServiceErrorHandler.h
src/framework/UI_WebSocketClientServer.cpp
src/framework/UI_WebSocketClientServer.h
src/framework/UI_WebSocketClientNotifications.cpp
src/framework/UI_WebSocketClientNotifications.h
src/framework/utils.h
src/framework/utils.cpp
src/framework/AppServiceRegistry.h
src/framework/SubSystemServer.cpp
src/framework/SubSystemServer.h
src/framework/RESTAPI_utils.h
src/framework/AuthClient.cpp
src/framework/AuthClient.h
src/framework/MicroServiceNames.h
src/framework/MicroServiceFuncs.h
src/framework/OpenAPIRequests.cpp
src/framework/OpenAPIRequests.h
src/framework/MicroServiceFuncs.cpp
src/framework/ALBserver.cpp
src/framework/ALBserver.h
src/framework/KafkaManager.cpp
src/framework/KafkaManager.h
src/framework/RESTAPI_RateLimiter.h
src/framework/WebSocketLogger.h
src/framework/RESTAPI_GenericServerAccounting.h
src/framework/CIDR.h
src/framework/RESTAPI_Handler.cpp
src/framework/RESTAPI_Handler.h
src/framework/RESTAPI_ExtServer.h
src/framework/RESTAPI_ExtServer.cpp
src/framework/RESTAPI_IntServer.cpp
src/framework/RESTAPI_IntServer.h
src/framework/RESTAPI_SystemCommand.h
src/framework/RESTAPI_WebSocketServer.h
src/framework/RESTAPI_SystemConfiguration.h
src/framework/EventBusManager.cpp
src/framework/EventBusManager.h
src/framework/RESTAPI_PartHandler.h
src/framework/MicroService.cpp
src/framework/MicroServiceExtra.h
src/framework/uCentral_Protocol.h
src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp
src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h
src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp
src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp
src/RESTObjects/RESTAPI_CertObjects.cpp src/RESTObjects/RESTAPI_CertObjects.h
src/RESTObjects/RESTAPI_OWLSobjects.cpp src/RESTObjects/RESTAPI_OWLSobjects.h
src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h
src/RESTObjects/RESTAPI_AnalyticsObjects.cpp src/RESTObjects/RESTAPI_AnalyticsObjects.h
src/RESTObjects/RESTAPI_SubObjects.cpp src/RESTObjects/RESTAPI_SubObjects.h
src/RESTAPI/RESTAPI_devices_handler.cpp src/RESTAPI/RESTAPI_devices_handler.h
src/RESTAPI/RESTAPI_device_handler.cpp src/RESTAPI/RESTAPI_device_handler.h
src/RESTAPI/RESTAPI_device_commandHandler.cpp src/RESTAPI/RESTAPI_device_commandHandler.h
@@ -145,20 +97,16 @@ add_executable( owgw
src/RESTAPI/RESTAPI_RPC.cpp src/RESTAPI/RESTAPI_RPC.h
src/RESTAPI/RESTAPI_deviceDashboardHandler.cpp src/RESTAPI/RESTAPI_deviceDashboardHandler.h
src/RESTAPI/RESTAPI_telemetryWebSocket.cpp src/RESTAPI/RESTAPI_telemetryWebSocket.h
src/RESTAPI/RESTAPI_scripts_handler.cpp src/RESTAPI/RESTAPI_scripts_handler.h
src/RESTAPI/RESTAPI_script_handler.cpp src/RESTAPI/RESTAPI_script_handler.h
src/RESTAPI/RESTAPI_regulatory.cpp src/RESTAPI/RESTAPI_regulatory.h
src/RESTAPI/RESTAPI_radiussessions_handler.cpp src/RESTAPI/RESTAPI_radiussessions_handler.h
src/RESTAPI/RESTAPI_webSocketServer.cpp src/RESTAPI/RESTAPI_webSocketServer.h
src/storage/storage_blacklist.cpp src/storage/storage_tables.cpp src/storage/storage_logs.cpp
src/storage/storage_command.cpp src/storage/storage_healthcheck.cpp src/storage/storage_statistics.cpp
src/storage/storage_device.cpp src/storage/storage_capabilities.cpp src/storage/storage_defconfig.cpp
src/storage/storage_scripts.cpp src/storage/storage_scripts.h
src/storage/storage_tables.cpp
src/RESTAPI/RESTAPI_routers.cpp
src/Daemon.cpp src/Daemon.h
src/AP_WS_Server.cpp src/AP_WS_Server.h
src/WS_Server.cpp src/WS_Server.h
src/StorageService.cpp src/StorageService.h
src/DeviceRegistry.cpp src/DeviceRegistry.h
src/CommandManager.cpp src/CommandManager.h
src/CentralConfig.cpp src/CentralConfig.h
src/FileUploader.cpp src/FileUploader.h
@@ -169,52 +117,7 @@ add_executable( owgw
src/TelemetryStream.cpp src/TelemetryStream.h
src/framework/ConfigurationValidator.cpp src/framework/ConfigurationValidator.h
src/ConfigurationCache.h
src/CapabilitiesCache.h src/FindCountry.h
src/rttys/RTTYS_server.cpp
src/rttys/RTTYS_server.h
src/rttys/RTTYS_WebServer.cpp
src/rttys/RTTYS_WebServer.h src/RESTAPI/RESTAPI_device_helper.h
src/SDKcalls.cpp
src/SDKcalls.h
src/StateUtils.cpp src/StateUtils.h
src/AP_WS_Reactor_Pool.h
src/AP_WS_Connection.h
src/AP_WS_Connection.cpp
src/TelemetryClient.h src/TelemetryClient.cpp
src/RESTAPI/RESTAPI_iptocountry_handler.cpp src/RESTAPI/RESTAPI_iptocountry_handler.h
src/framework/ow_constants.h
src/GwWebSocketClient.cpp src/GwWebSocketClient.h
src/RADIUS_proxy_server.cpp src/RADIUS_proxy_server.h
src/RESTAPI/RESTAPI_radiusProxyConfig_handler.cpp src/RESTAPI/RESTAPI_radiusProxyConfig_handler.h
src/ParseWifiScan.h
src/RADIUS_helpers.h
src/VenueBroadcaster.h
src/sdks/sdk_prov.h
src/AP_WS_Process_connect.cpp
src/AP_WS_Process_state.cpp
src/AP_WS_Process_healthcheck.cpp
src/AP_WS_Process_log.cpp
src/AP_WS_Process_crashlog.cpp
src/AP_WS_Process_ping.cpp
src/AP_WS_Process_cfgpending.cpp
src/AP_WS_Process_recovery.cpp
src/AP_WS_Process_deviceupdate.cpp
src/AP_WS_Process_telemetry.cpp
src/AP_WS_Process_venuebroadcast.cpp
src/RADIUS_Destination.h
src/UI_GW_WebSocketNotifications.cpp src/UI_GW_WebSocketNotifications.h
src/framework/RESTAPI_SystemConfiguration.h
src/ScriptManager.cpp src/ScriptManager.h
src/SignatureMgr.h
src/AP_WS_Process_event.cpp
src/AP_WS_Process_wifiscan.cpp
src/AP_WS_Process_alarm.cpp
src/GWKafkaEvents.cpp src/GWKafkaEvents.h
src/RegulatoryInfo.cpp src/RegulatoryInfo.h
src/RADIUSSessionTracker.cpp src/RADIUSSessionTracker.h
src/libs/Scheduler.h src/libs/InterruptableSleep.h src/libs/ctpl_stl.h src/libs/Cron.h
src/GenericScheduler.cpp src/GenericScheduler.h src/framework/default_device_types.h src/AP_WS_Process_rebootLog.cpp src/AP_WS_ConfigAutoUpgrader.cpp src/AP_WS_ConfigAutoUpgrader.h src/RESTAPI/RESTAPI_default_firmwares.cpp src/RESTAPI/RESTAPI_default_firmwares.h src/RESTAPI/RESTAPI_default_firmware.cpp src/RESTAPI/RESTAPI_default_firmware.h src/storage/storage_def_firmware.cpp src/firmware_revision_cache.h src/sdks/sdk_fms.h
src/AP_WS_LookForUpgrade.cpp)
src/CapabilitiesCache.h src/FindCountry.h src/rttys/RTTYS_server.cpp src/rttys/RTTYS_server.h src/rttys/RTTYS_device.cpp src/rttys/RTTYS_device.h src/rttys/RTTYS_ClientConnection.cpp src/rttys/RTTYS_ClientConnection.h src/rttys/RTTYS_WebServer.cpp src/rttys/RTTYS_WebServer.h src/RESTAPI/RESTAPI_device_helper.h src/SDKcalls.cpp src/SDKcalls.h src/StateUtils.cpp src/StateUtils.h src/WS_ReactorPool.h src/WS_Connection.h src/WS_Connection.cpp src/TelemetryClient.h src/TelemetryClient.cpp src/RESTAPI/RESTAPI_iptocountry_handler.cpp src/RESTAPI/RESTAPI_iptocountry_handler.h)
if(NOT SMALL_BUILD)
@@ -225,19 +128,14 @@ INSTALL(TARGETS owgw
)
target_link_libraries(owgw PUBLIC
${Poco_LIBRARIES}
${ZLIB_LIBRARIES}
)
${Poco_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES})
if(NOT SMALL_BUILD)
target_link_libraries(owgw PUBLIC
${MySQL_LIBRARIES}
${ZLIB_LIBRARIES}
CppKafka::cppkafka
fmt::fmt
resolv
)
${MySQL_LIBRARIES} ${ZLIB_LIBRARIES}
CppKafka::cppkafka
nlohmann_json_schema_validator
)
if(UNIX AND NOT APPLE)
target_link_libraries(owgw PUBLIC PocoJSON)
endif()
endif()
endif()

32
CODING_STYLE.md Normal file
View File

@@ -0,0 +1,32 @@
# Coding Style
I just want ot make sure we all follow the same rules when contributing
code back into this tree.
## Version of C++
This project is based on the C++17 standard. If compiles as-is on most platforms
using either clang or g++. Do not use C++21 features for now. I would love to do some
of the new stuff but let's wait for these features to be available on
all compilers first. `coroutine` has to wait.
## Naming
Naming of pretty much anything uses Pascal naming. I know... You might not be a big fan or have
fallen to the JS gods and use camelNaming. Well, let's all make an effort to keep
this coherent. Member variable naming adds a `_` at the end of the vars. Try to
keep this standard going. Sometimes you must override a base class function and then of course
you need to follow the base class. Let's be real...
## File Sizes
Do you best to keep your file sizes < 300 lines. It just makes the code more readable
and shortens compile times.
## This is a cmake project
This is a cmake project and you need to adhere to the cmake rules. If you need
to add a package to the CMakeList, you need to ensure that the package is available
on all required platforms and compiles. Remember that this project runs on Linux, OS X,
and the Raspberry PI.
## Licensed packages
When adding a package, you must also state the licensing for the package. MIT, BSD, Apache licenses
are acceptable. No commercial licenses are allowed.

View File

@@ -1,415 +0,0 @@
# Controller Configuration Parameters
## OWGW Specific Parameters
### Websocket parameters
This is the crucial section. I bet that 97.4% of all your problems will come from here, and it's boring. So put some good music on,
give the kids the iPad, get a cup of coffee, and pay attention. Every field will be explained.
```properties
ucentral.websocket.host.0.backlog = 500
ucentral.websocket.host.0.rootca = $OWGW_ROOT/certs/root.pem
ucentral.websocket.host.0.issuer = $OWGW_ROOT/certs/issuer.pem
ucentral.websocket.host.0.cert = $OWGW_ROOT/certs/websocket-cert.pem
ucentral.websocket.host.0.key = $OWGW_ROOT/certs/websocket-key.pem
ucentral.websocket.host.0.clientcas = $OWGW_ROOT/certs/clientcas.pem
ucentral.websocket.host.0.cas = $OWGW_ROOT/certs/cas
ucentral.websocket.host.0.address = *
ucentral.websocket.host.0.port = 15002
ucentral.websocket.host.0.security = strict
ucentral.websocket.host.0.key.password = mypassword
ucentral.websocket.maxreactors = 20
```
#### ucentral.websocket.host.0.backlog
This is the number of concurrent devices you are expecting to call all at once. Not the current number of devices. This is how many will connect in the same exact second.
Take the total number of devices you have and divide by 100. That's a good rule of thumb. Never go above 500.
#### ucentral.websocket.host.0.rootca
This is the root file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem)
#### ucentral.websocket.host.0.issuer
This is the issuer file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/issuer.pem)
#### ucentral.websocket.host.0.cert
This is a `pem` file that you will receive from Digicert for the gateway itself. This is the certificate for the gateway.
#### ucentral.websocket.host.0.key
This is a `pem` file that you will receive from Digicert for the gateway itself. The is the private key for the gateway.
#### ucentral.websocket.host.0.clientcas
This is a `pem` file that contains both the issuer and the root CA certificates. You can find it You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/clientcas.pem)
#### ucentral.websocket.host.0.cas
This is a directory where you will copy your own `cert.pem`, the `root.pem`, and the `issuer.pem` files.
#### ucentral.websocket.host.0.address
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
#### ucentral.websocket.host.0.port
Leave to 15002 for now.
#### ucentral.websocket.host.0.security
Leave this as strict for now for devices.
#### ucentral.websocket.host.0.key.password
If you key file uses a password, please enter it here.
#### ucentral.websocket.maxreactors
A single reactor can handle between 1000-2000 devices. Never leave this smaller than 5 or larger than 50.
### File uploader parameters
Certain commands may require the Access Point to upload a file into the Controller. For this reason, there is a special embedded HTTP
server to receive these files.
```properties
openwifi.fileuploader.host.0.backlog = 100
openwifi.fileuploader.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
openwifi.fileuploader.host.0.security = relaxed
openwifi.fileuploader.host.0.address = *
openwifi.fileuploader.host.0.name = ucentral.dpaas.arilia.com
openwifi.fileuploader.host.0.port = 16003
openwifi.fileuploader.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
openwifi.fileuploader.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
openwifi.fileuploader.host.0.key.password = mypassword
openwifi.fileuploader.path = $OWGW_ROOT/uploads
openwifi.fileuploader.maxsize = 10000
openwifi.fileuploader.uri = https://ucentral.dpaas.arilia.com:16003
```
#### openwifi.fileuploader.host.0.backlog
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
#### openwifi.fileuploader.host.0.rootca
This is the root file of your own certificate CA in `pem` format.
#### openwifi.fileuploader.host.0.cert
This is your own server certificate in `pem` format..
#### openwifi.fileuploader.host.0.key
This is the private key associated with your own certificate in `pem` format.
#### openwifi.intfileuploaderernal.host.0.address
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
#### openwifi.fileuploader.host.0.port
The port on which the REST API server is listening. By default, this is 16003.
#### openwifi.fileuploader.host.0.security
Leave this as `relaxed` for now for devices.
#### openwifi.fileuploader.host.0.key.password
If you key file uses a password, please enter it here.
#### openwifi.fileuploader.path
This is the location where the files will be stored temporarily before processing. This `path` must exist.
#### openwifi.fileuploader.maxsize
This is the maximum uploaded file size. The default maximum size if 10MB. This size is in KB.
#### openwifi.fileuploader.uri
This is the URI that will be passed to the AP. You must make sure that the AP can resolve this URI.
### OUI Service
The controller has a built-in OUI resolver for MAC addresses. The GW will periodically load this file to obtain the latest.
This is ths URI for this file.
```properties
oui.download.uri = https://standards-oui.ieee.org/oui/oui.txt
```
### Data-model Source
The gateway can make use of the latest uCentral data-model or use the built-in model. These 2 parameters allow you to
choose which method you want. If you select the internal method, the URI is ignored. If for some reason you choose
the on-line data-model from the URI and the URI is not reachable, the system will fall back on the internal model.
```properties
ucentral.datamodel.internal = true
ucentral.datamodel.uri = https://raw.githubusercontent.com/Telecominfraproject/wlan-ucentral-schema/main/ucentral.schema.json
```
### Command Manager
The command manager is responsible for managing command sent and responses received with the APs. Several parameters allow you
to fine tune its behaviour. Unless you have some particular reasons to change tem the defaults are usually just fine.
```properties
command.timeout = 14400
command.retry = 120
command.janitor = 120
command.queue = 30
```
#### command.timeout
How long will the GW wait in seconds before considering a commands has timed out.
#### command.retry
How long between command retries.
#### command.janitor
How long between outstanding RPC clean-ups.
#### command.queue
How long should te gateway wait between running its queue.
### IP to Country Parameters
The controller has the ability to find the location of the IP of each Access Points. This uses an external IP location service. Currently,
the controller supports 3 services. Please note that these services will require to obtain an API key or token, and these may cause you to incur
additional fees. Here is the list of the services supported:
- ip2location: ip2location.com
- ipdata: ipdata.co
- ipinfo: ipinfo.io
```properties
iptocountry.default = US
iptocountry.provider = ipinfo
#iptocountry.provider = ipdata
#iptocountry.provider = ip2location
iptocountry.ipinfo.token =
iptocountry.ipdata.apikey =
iptocountry.ip2location.apikey =
```
#### iptocountry.default
This is the country code to be used if no information can be found at one of the providers or you have not configured any of the providers.
#### iptocountry.provider
You must select onf of the possible services and the fill the appropriate token or api key parameter.
### Provisioning link
This parameter tells the controller how to behave when it receives a request from a device for the first time. In this case, we tell
the controller to look at the provisioning service first, then apply any local configurations.
```properties
autoprovisioning.process = prov,default
```
### Restricted Device Signature Manager
If are using restricted devices, then you can include different keys for each vendor who provided
you with their information. This allows the controller to automatically sign requests to the device. You can have as many vendors
as it is necessary.
```properties
signature.manager.0.key.public = $OWGW_ROOT/certs/signatures/test1-public-key.pem
signature.manager.0.key.private = $OWGW_ROOT/certs/signatures/test1-private-key.pem
signature.manager.0.vendor = test1
signature.manager.1.key.public = $OWGW_ROOT/certs/signatures/test2-public-key.pem
signature.manager.1.key.private = $OWGW_ROOT/certs/signatures/test2-private-key.pem
signature.manager.1.vendor = test2
```
### OWLS Simulator ID
If you plan on using OWLS (OpenWifi Load Simulator), then you will need to put your Simulator ID right here.
This ID must be obtained from TIP.
```properties
simulatorid = 53494dFFEEDD
```
### RTTY Service
The controller comes with the ability to run an RTTY service. The service can either be internal (the prefered choice)
or external. If you decide to use the internal RTTY, the you only need to specify `rtty.internal = true`. If you choose
to use an external RTTY, you must specify the remainder of the parameters.
```properties
rtty.internal = true
rtty.enabled = true
rtty.server = rtty-tip.arilia.com
rtty.port = 5912
rtty.token = 96181c567b4d0d98c50f127230068fa8
rtty.timeout = 60
rtty.viewport = 5913
rtty.assets = $OWGW_ROOT/rtty_ui
```
### RADIUS proxy config
If you are going to use the buil-in RADIUS proxy service, you need to enable this parameter and provide
the ports for you PROXY.
```properties
radius.proxy.enable = false
radius.proxy.accounting.port = 1813
radius.proxy.authentication.port = 1812
radius.proxy.coa.port = 3799
radsec.keepalive = 120
```
### Auto Archiver Parameters
The auto archiver is responsible for removing all stale data. The default is to remove old data after 7 days.
```properties
archiver.enabled = true
archiver.schedule = 03:00
archiver.db.0.name = healthchecks
archiver.db.0.keep = 7
archiver.db.1.name = statistics
archiver.db.1.keep = 7
archiver.db.2.name = devicelogs
archiver.db.2.keep = 7
archiver.db.3.name = commandlist
archiver.db.3.keep = 7
```
## Generic OpenWiFi SDK parameters
### REST API External parameters
These are the parameters required for the configuration of the external facing REST API server
```properties
openwifi.restapi.host.0.backlog = 100
openwifi.restapi.host.0.security = relaxed
openwifi.restapi.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
openwifi.restapi.host.0.address = *
openwifi.restapi.host.0.port = 16004
openwifi.restapi.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
openwifi.restapi.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
openwifi.restapi.host.0.key.password = mypassword
```
#### openwifi.restapi.host.0.backlog
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
#### openwifi.restapi.host.0.rootca
This is the root file of your own certificate CA in `pem` format.
#### openwifi.restapi.host.0.cert
This is your own server certificate in `pem` format..
#### openwifi.restapi.host.0.key
This is the private key associated with your own certificate in `pem` format.
#### openwifi.restapi.host.0.address
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
#### openwifi.restapi.host.0.port
The port on which the REST API server is listening. By default, this is 16002.
#### openwifi.restapi.host.0.security
Leave this as `relaxed` for now for devices.
#### openwifi.restapi.host.0.key.password
If you key file uses a password, please enter it here.
### REST API Intra microservice parameters
The following parameters describe the configuration for the inter-microservice HTTP server. You may use the same certificate/key
you are using for your extenral server or another certificate.
```properties
openwifi.internal.restapi.host.0.backlog = 100
openwifi.internal.restapi.host.0.security = relaxed
openwifi.internal.restapi.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
openwifi.internal.restapi.host.0.address = *
openwifi.internal.restapi.host.0.port = 17004
openwifi.internal.restapi.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
openwifi.internal.restapi.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
openwifi.internal.restapi.host.0.key.password = mypassword
```
#### openwifi.internal.host.0.backlog
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
#### openwifi.internal.host.0.rootca
This is the root file of your own certificate CA in `pem` format.
#### openwifi.internal.host.0.cert
This is your own server certificate in `pem` format..
#### openwifi.internal.host.0.key
This is the private key associated with your own certificate in `pem` format.
#### openwifi.internal.host.0.address
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
#### openwifi.internal.host.0.port
The port on which the REST API server is listening. By default, this is 17002.
#### openwifi.internal.host.0.security
Leave this as `relaxed` for now for devices.
#### openwifi.internal.host.0.key.password
If you key file uses a password, please enter it here.
### Microservice information
These are different Microservie parameters. Following is a brief explanation.
```properties
openwifi.service.key = $OWGW_ROOT/certs/restapi-key.pem
openwifi.service.key.password = mypassword
openwifi.system.data = $OWGW_ROOT/data
openwifi.system.uri.private = https://localhost:17004
openwifi.system.uri.public = https://ucentral.dpaas.arilia.com:16002
openwifi.system.uri.ui = https://ucentral-ui.arilia.com
openwifi.security.restapi.disable = false
openwifi.system.commandchannel = /tmp/app.ucentralfms
openwifi.autoprovisioning = true
```
#### openwifi.service.key
From time to time, the microservice must encrypt information. This is the key it should use. You may use the
same keey as you RESTAPI or your server.
#### openwifi.service.key.password
The password for the `openwifi.service.key`
#### openwifi.system.data
The location of system data. This path must exist.
#### openwifi.system.uri.private
The URI to reach the controller on the internal port.
#### openwifi.system.uri.public
The URI to reach the controller from the outside world.
#### openwifi.system.uri.ui
The URI of the UI to manage this service
#### openwifi.security.restapi.disable
This allows to disable security for internal and external API calls. This should only be used if the controller
sits behind an application load balancer that will actually do TLS. Setting this to `true` disables security.
#### openwifi.system.commandchannel
The UNIX socket command channel used by this service.
#### openwifi.autoprovisioning
Allow unknown devices to be provisioned by the system.
### ALB Support
In order to support an application load balancer health check verification, your need to provide the following parameters.
```properties
alb.enable = true
alb.port = 16102
```
### Kafka
The controller use Kafka, like all the other microservices. You must configure the kafka section in order for the
system to work.
```properties
openwifi.kafka.group.id = gateway
openwifi.kafka.client.id = gateway1
openwifi.kafka.enable = true
openwifi.kafka.brokerlist = my_Kafka.example.com:9092
openwifi.kafka.auto.commit = false
openwifi.kafka.queue.buffering.max.ms = 50
```
### openwifi.kafka.group.id
The group ID is a single word that should identify the type of service tuning. In the case `gateway`
### openwifi.kafka.client.id
The client ID is a single service within that group ID. Each participant must have a unique client ID.
### openwifi.kafka.enable
Kafka should always be enabled.
### openwifi.kafka.brokerlist
The list of servers where your Kafka server is running. Comma separated.
### openwifi.kafka.auto.commit
Auto commit flag in Kafka. Leave as `false`.
### openwifi.kafka.queue.buffering.max.ms
Kafka buffering. Leave as `50`.
### Kafka security
If you intend to use SSL, you should look into Kafka Connect and specify the certificates below.
```properties
penwifi.kafka.ssl.ca.location =
openwifi.kafka.ssl.certificate.location =
openwifi.kafka.ssl.key.location =
openwifi.kafka.ssl.key.password =
```
### DB Type
The controller supports 3 types of Database. SQLite should only be used for sites with less than 100 APs or for testing in the lab.
In order to select which database to use, you must set the `storage.type` value to sqlite, postgresql, or mysql.
```properties
storage.type = sqlite
#storage.type = postgresql
#storage.type = mysql
```
### Storage SQLite parameters
Additional parameters to set for SQLite. The only important one is `storage.type.sqlite.db` which is the database name on disk.
```properties
storage.type.sqlite.db = gateway.db
storage.type.sqlite.idletime = 120
storage.type.sqlite.maxsessions = 128
```
### Storage Postgres
Additional parameters to set if you select Postgres for your database. You must specify `host`, `username`, `password`,
`database`, and `port`.
```properties
storage.type.postgresql.maxsessions = 64
storage.type.postgresql.idletime = 60
storage.type.postgresql.host = localhost
storage.type.postgresql.username = gateway
storage.type.postgresql.password = gateway
storage.type.postgresql.database = gateway
storage.type.postgresql.port = 5432
storage.type.postgresql.connectiontimeout = 60
```
### Storage MySQL/MariaDB
Additional parameters to set if you select mysql for your database. You must specify `host`, `username`, `password`,
`database`, and `port`.
```properties
storage.type.mysql.maxsessions = 64
storage.type.mysql.idletime = 60
storage.type.mysql.host = localhost
storage.type.postgresql.username = gateway
storage.type.postgresql.password = gateway
storage.type.postgresql.database = gateway
storage.type.mysql.port = 3306
storage.type.mysql.connectiontimeout = 60
```
### Logging Parameters
The microservice provides extensive logging. If you would like to keep logging on disk, set the `logging.type = file`. If you only want
console logging, `set logging.type = console`. When selecting file, `logging.path` must exist. `logging.level` sets the
basic logging level for the entire controller. `logging.websocket` disables WebSocket logging.
```properties
logging.type = file
logging.path = $OWGW_ROOT/logs
logging.level = information
logging.asynch = true
logging.websocket = false
```

View File

@@ -1,38 +0,0 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Version of C++
This project is based on the C++17 standard and compiles as-is on most platforms
using either clang or g++. Do not use C++21 or C++23 features for now. Some core
libraries used in this project do not support C++21 or C++23 yet.
## Variable Naming
Naming of pretty much anything uses Pascal naming. Longer explicit names using casing.
Member variable naming adds a `_` at the end of the vars. Try to
keep this standard going. Sometimes you must override a base class function and then of course
you need to follow the base class.
## This is a cmake project
This is a cmake project, and you need to adhere to the cmake rules. If you need
to add a package to the CMakeList, you need to ensure that the package is available
on all required platforms and compiles. Remember that this project runs on Linux, OS X,
and the Raspberry PI.
## Licensed packages
When adding a package, you must also state the licensing for the package. MIT, BSD, Apache licenses
are acceptable. No commercial licenses are allowed.
## clang formatting
Please format your code using the included `.clang-format` file included in the project.
```bash
clang-format -i --style=<project root>/.clang-format myfile.cpp
```
## Pull Requests
All submissions, including submissions by project members, require review. We
accept GitHub pull requests. Please create a branch with the Jira name for addressing the issue you are fixing or the
feature you are implementing.
Create a pull-request from the branch into master.

View File

@@ -1,24 +1,15 @@
ARG DEBIAN_VERSION=bookworm
ARG POCO_VERSION=poco-tip-v4-tag
ARG CPPKAFKA_VERSION=tip-v1
ARG VALIJASON_VERSION=tip-v1.0.2
ARG APP_NAME=owgw
ARG APP_HOME_DIR=/openwifi
FROM alpine:3.15 AS build-base
FROM debian:$DEBIAN_VERSION AS build-base
RUN apt-get update && apt-get install --no-install-recommends -y \
RUN apk add --update --no-cache \
make cmake g++ git \
libpq-dev libmariadb-dev libmariadbclient-dev-compat \
librdkafka-dev libboost-all-dev libssl-dev \
zlib1g-dev nlohmann-json3-dev ca-certificates libfmt-dev
unixodbc-dev postgresql-dev mariadb-dev \
librdkafka-dev boost-dev openssl-dev \
zlib-dev nlohmann-json
FROM build-base AS poco-build
ARG POCO_VERSION
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-poco/git/refs/tags/${POCO_VERSION} version.json
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch ${POCO_VERSION} /poco
ADD https://api.github.com/repos/stephb9959/poco/git/refs/heads/master version.json
RUN git clone https://github.com/stephb9959/poco /poco
WORKDIR /poco
RUN mkdir cmake-build
@@ -29,10 +20,8 @@ RUN cmake --build . --target install
FROM build-base AS cppkafka-build
ARG CPPKAFKA_VERSION
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka
ADD https://api.github.com/repos/stephb9959/cppkafka/git/refs/heads/master version.json
RUN git clone https://github.com/stephb9959/cppkafka /cppkafka
WORKDIR /cppkafka
RUN mkdir cmake-build
@@ -41,84 +30,68 @@ RUN cmake ..
RUN cmake --build . --config Release -j8
RUN cmake --build . --target install
FROM build-base AS valijson-build
FROM build-base AS json-schema-validator-build
ARG VALIJASON_VERSION
ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/heads/master version.json
RUN git clone https://github.com/pboettch/json-schema-validator /json-schema-validator
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-valijson/git/refs/tags/${VALIJASON_VERSION} version.json
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch ${VALIJASON_VERSION} /valijson
WORKDIR /valijson
WORKDIR /json-schema-validator
RUN mkdir cmake-build
WORKDIR cmake-build
RUN cmake ..
RUN cmake --build . --config Release -j8
RUN cmake --build . --target install
RUN make
RUN make install
FROM build-base AS app-build
FROM build-base AS owgw-build
ARG APP_NAME
ADD CMakeLists.txt build /${APP_NAME}/
ADD cmake /${APP_NAME}/cmake
ADD src /${APP_NAME}/src
ADD .git /${APP_NAME}/.git
ADD CMakeLists.txt build /owgw/
ADD cmake /owgw/cmake
ADD src /owgw/src
ADD .git /owgw/.git
COPY --from=poco-build /usr/local/include /usr/local/include
COPY --from=poco-build /usr/local/lib /usr/local/lib
COPY --from=cppkafka-build /usr/local/include /usr/local/include
COPY --from=cppkafka-build /usr/local/lib /usr/local/lib
COPY --from=valijson-build /usr/local/include /usr/local/include
COPY --from=json-schema-validator-build /usr/local/include /usr/local/include
COPY --from=json-schema-validator-build /usr/local/lib /usr/local/lib
WORKDIR /${APP_NAME}
WORKDIR /owgw
RUN mkdir cmake-build
WORKDIR /${APP_NAME}/cmake-build
WORKDIR /owgw/cmake-build
RUN cmake ..
RUN cmake --build . --config Release -j8
FROM debian:$DEBIAN_VERSION
FROM alpine:3.15
ARG APP_NAME
ARG APP_HOME_DIR
ENV OWGW_USER=owgw \
OWGW_ROOT=/owgw-data \
OWGW_CONFIG=/owgw-data
ENV APP_NAME=$APP_NAME \
APP_USER=$APP_NAME \
APP_ROOT=/$APP_NAME-data \
APP_CONFIG=/$APP_NAME-data \
APP_HOME_DIR=$APP_HOME_DIR
RUN addgroup -S "$OWGW_USER" && \
adduser -S -G "$OWGW_USER" "$OWGW_USER"
# This is for legacy
ENV OWGW_USER=$APP_USER \
OWGW_ROOT=$APP_ROOT \
OWGW_CONFIG=$APP_CONFIG
RUN mkdir /openwifi
RUN mkdir -p "$OWGW_ROOT" "$OWGW_CONFIG" && \
chown "$OWGW_USER": "$OWGW_ROOT" "$OWGW_CONFIG"
RUN useradd $APP_USER
RUN mkdir $APP_HOME_DIR
RUN mkdir -p $APP_ROOT $APP_CONFIG && \
chown $APP_USER: $APP_ROOT $APP_CONFIG
RUN apt-get update && apt-get install --no-install-recommends -y \
librdkafka++1 gosu gettext ca-certificates bash jq curl wget \
libmariadb-dev-compat libpq5 unixodbc postgresql-client libfmt9 sqlite3
RUN apk add --update --no-cache librdkafka su-exec gettext ca-certificates bash jq curl \
mariadb-connector-c libpq unixodbc postgresql-client
COPY readiness_check /readiness_check
COPY test_scripts/curl/cli /cli
COPY $APP_NAME.properties.tmpl /
COPY owgw.properties.tmpl /
COPY docker-entrypoint.sh /
COPY wait-for-postgres.sh /
COPY rtty_ui /dist/rtty_ui
RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.crt
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
COPY --from=app-build /$APP_NAME/cmake-build/$APP_NAME $APP_HOME_DIR/$APP_NAME
COPY --from=owgw-build /owgw/cmake-build/owgw /openwifi/owgw
COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib /usr/local/lib/
COPY --from=poco-build /poco/cmake-build/lib /usr/local/lib/
RUN ldconfig
COPY --from=poco-build /poco/cmake-build/lib /usr/local/lib
EXPOSE 15002 16002 16003 17002 16102
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ${APP_HOME_DIR}/${APP_NAME}
CMD ["/openwifi/owgw"]

View File

@@ -4,16 +4,15 @@ This is a fast moving target, so please visit often or set an alert in GitHub.
## Current plans
Currently, most telemetry and reports created in the gateway will be issues as topics in Kafka. You should configure
your Kafka service with the following topics:
- `healthcheck` : These are the `healthcheck` report sent from the AP.
- `state` : This is emitted for every `state` report coming from the AP. This state report contains all the information of state reports.
- `connection` : This is emitted whenever a device connects to the gateway. The report contains all ths information about the connection.
- `state` : This is emitted for every `state` report coming from the AP. This state report contains all the information of state reports.
- `healthcheck` : These are the `healthcheck` report sent from the AP.
- `wifiscan` : Whenever a `wifiscan` report is generated, it will be submitted here.
- `alerts` : Alerts originating from devices (future use).
- `command` : Allow to send commands (future use).
- `service_events` : Inter-service traffic.
- `device_event_queue` : device events
- `device_telemetry` : device telemetry. Telemetry must be started manually or through the device configuration.
- `provisioning_change` : venue, configuration, entity changes from provisioning.
- `security` : This will have application information (future use).
- `command` : Allow to send commands (future use).
- `alerts` : Alerts originating from devices (future use).
## Structure of `kafka` messages
Messages use 2 formats

View File

@@ -1,194 +0,0 @@
# Micro-service backbone responsibilities
## Bus management
Each microservice must get onto kafka and consume/produce messages on the kafka bus. The topic to subscribe to is `service_events`.
## System messages
System messages are what maintains the collection of micro-services working on the system. Each message has the format
```json
{
"event": <event-type>,
"id": 1234567890,
"type": "owrrm",
"publicEndPoint": "https://myhostname.com:16020",
"privateEndPoint": "https://localhost:17020",
"key" : "289479847948794870749",
"version" : "1.0"
}
```
### Responsibilities
Each micro service is responsible to generate its own messages and keep track of messages coming from other
micro services. This is necessary so that any micro service may reach our any other micro service. This provides
discovery for any micro service. All current micro services provided in OpenWiFi perform these functions. If you leverage
the C++ framework, this functionality if performed automatically.
### `event-type`
Each micro service is responsible to generate and consume these events
#### `join` event
When a service start and joins the bus, it should generate an event-type of `join`.
### `leave` event
When a service shuts down, it should generate a `leave` event-type.
### `keep-alive` event
Every 30 seconds, a service should generate a `keep-alive` message.
### `id`
You should generate a random number from some unique factor for the system. This ID is used to identify different services. You should reuse that ID
when you restart.
## The `type`
The `type` in the system message is oen of the following:
```c++
static const std::string uSERVICE_SECURITY{"owsec"};
static const std::string uSERVICE_GATEWAY{"owgw"};
static const std::string uSERVICE_FIRMWARE{ "owfms"};
static const std::string uSERVICE_TOPOLOGY{ "owtopo"};
static const std::string uSERVICE_PROVISIONING{ "owprov"};
static const std::string uSERVICE_OWLS{ "owls"};
static const std::string uSERVICE_SUBCRIBER{ "owsub"};
static const std::string uSERVICE_INSTALLER{ "owinst"};
static const std::string uSERVICE_ANALYTICS{ "owanalytics"};
static const std::string uSERVICE_OWRRM{ "owrrm"};
```
The `type` is what you should use to find the `privateEndPoint` you are looking to communicate with.
### Example
Assume you want to communicate with the gateway to configure a device.
```text
1. Look into my list of current Micro-services for the type=owgw.
2. Use the privateEndPoint associated with that entry
```
## REST API calls on the private interface
For inter-service REST calls, you should never use the `Authorization: Bearer token` method. Instead, the following headers should be included in all API calls
```json
{
"X-API-KEY" : "289479847948794870749",
"X-INTERNAL-NAME" : "https://myhostname.com:16020"
}
```
### `X-API-KEY`
This is the `key` you included in your `system-messages`.
### `X-INTERNAL-NAME`
This is the `publicEndPoint` you included in your `system-messages`.
This method can _only_ be used to any another `privateEndPoint` in the system. You can use the exact same EndPoints provided in the OpenAPI files for any of the services.
## OpenAPI Integration
To appear in the UI consoles, a microservice should be able to handle a get to the `/api/v1/system` endpoint on its `publicEndPoint` interface.
Here is a brief description of what the microservice should answer:
```yaml
/system:
get:
tags:
- System Commands
summary: Retrieve different values from the running service.
operationId: getSystemCommand
parameters:
- in: query
description: Get a value
name: command
schema:
type: string
enum:
- info
required: true
responses:
200:
description: Successful command execution
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/SystemInfoResults'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
```
The relevant data structures are:
```yaml
SystemInfoResults:
type: object
properties:
version:
type: string
uptime:
type: integer
format: integer64
start:
type: integer
format: integer64
os:
type: string
processors:
type: integer
hostname:
type: string
certificates:
type: array
items:
type: object
properties:
filename:
type: string
expires:
type: integer
format: int64
```
and
```yaml
responses:
NotFound:
description: The specified resource was not found.
content:
application/json:
schema:
properties:
ErrorCode:
type: integer
ErrorDetails:
type: string
ErrorDescription:
type: string
Unauthorized:
description: The requested does not have sufficient rights to perform the operation.
content:
application/json:
schema:
properties:
ErrorCode:
type: integer
enum:
- 0 # Success
- 1 # PASSWORD_CHANGE_REQUIRED,
- 2 # INVALID_CREDENTIALS,
- 3 # PASSWORD_ALREADY_USED,
- 4 # USERNAME_PENDING_VERIFICATION,
- 5 # PASSWORD_INVALID,
- 6 # INTERNAL_ERROR,
- 7 # ACCESS_DENIED,
- 8 # INVALID_TOKEN
- 9 # EXPIRED_TOKEN
- 10 # RATE_LIMIT_EXCEEDED
- 11 # BAD_MFA_TRANSACTION
- 12 # MFA_FAILURE
- 13 # SECURITY_SERVICE_UNREACHABLE
ErrorDetails:
type: string
ErrorDescription:
type: string
```

View File

@@ -5,11 +5,6 @@ This document will describe how the API is built and how to use it.
This uses OpenAPI definition 3.0 and can be found [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/owgw.yaml).
All endpoints begin with `/api/v1`.
## OpenAPI docs
You may get static page with OpenAPI docs generated from the definition on [GitHub Page](https://telecominfraproject.github.io/wlan-cloud-ucentralgw).
Also you may use [Swagger UI](https://petstore.swagger.io/#/) with OpenAPI definition file raw link (i.e. [latest version file](https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralgw/master/openapi/owgw.yaml)) to get interactive docs page.
## The flow
In order to use any of the API calls, you must obtain a token (I know - shocking). You do so by calling the end-point
`/oauth2`. Once you obtain that `access-token`, you will need to pass it in the headers under `Authorization: Bearer <place your token here>`.

View File

@@ -30,7 +30,7 @@ In this RPC, here are some common interpretations:
#### Connection event
Device Sends connection notification to the controller after establishing a connection. The controller
my decide to send the AP a newer configuration. The controller will record the device capabilities provided.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "connect" ,
"params" : {
@@ -47,7 +47,7 @@ my decide to send the AP a newer configuration. The controller will record the d
#### State event
The device sends device state information periodically. If the controller detects that it has a newer configuration, it
may decide to send this new configuration to the AP.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "state" ,
"params" : {
@@ -62,7 +62,7 @@ may decide to send this new configuration to the AP.
#### Healthcheck event
Device sends a `healthcheck` periodically. This message contains information about how vital subsystems are operating and
if they need attention.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "healthcheck" ,
"params" : {
@@ -77,7 +77,7 @@ if they need attention.
#### Log event
Device sends a log message whenever necessary. The controller will log this message to the log system for the device.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "log" ,
"params" : {
@@ -89,54 +89,6 @@ Device sends a log message whenever necessary. The controller will log this mess
}
```
#### Events Channel
Device sends unsolicited events to the controller.
```json
{ "jsonrpc" : "2.0" ,
"method" : "event" ,
"params" : {
"serial" : "001122334455" ,
"data" : {
"event" : [
1871263817263,
{
"type" : "the event type",
"payload" : {
"field1" : "value1"
}
}
]
}
}
}
```
The first element of the `event` array is always the `timestamp` of the event. The `payload` is a JSON document contains addition information about the event. This _may not_ be empty.
#### Alarms Channel
Device sends unsolicited alarms to the controller.
```json
{ "jsonrpc" : "2.0" ,
"method" : "alarm" ,
"params" : {
"serial" : <serial number> ,
"data" : <Optiona/may be empty: JSON Document providing additional information related to this event message>
}
}
```
#### Wifiscan Channel
Device sends unsolicited wifiscans to the controller.
```json
{ "jsonrpc" : "2.0" ,
"method" : "wifiscan" ,
"params" : {
"serial" : <serial number> ,
"data" : <Optiona/may be empty: JSON Document providing additional information related to this wifiscan message>
}
}
```
##### `severity`
The `severity` matches the `syslog` levels. Here are the details:
- 0 : LOG_EMERG 0 /* system is unusable */
@@ -149,40 +101,23 @@ The `severity` matches the `syslog` levels. Here are the details:
- 7 : LOG_DEBUG 7 /* debug-level messages */
#### Crash Log event
Device may send a `crash log event` during rebooting after a crash. The event cannot be sent until a connection event has been established.
```json
Device may send a crash log event after rebooting after a crash. The event cannot be sent until a connection event has been sent.
```
{ "jsonrpc" : "2.0" ,
"method" : "crashlog" ,
"params" : {
"serial" : <serial number> ,
"uuid" : <the UUID of the configuration that generated the crash log>,
"loglines" : [ an array of strings representing the logs from the log file ]
"serial" : <serial number> ,
"uuid" : <the UUID of the configuration that generated the crash log>,
"loglines" : [ an array of strings representing the logs from the log file ]
}
}
```
#### Reboot Log event
The device may send a `reboot log event` after a reboot. This maybe a scheduled reboot or caused in some other way.
```json
{ "jsonrpc" : "2.0" ,
"method" : "rebootLog" ,
"params" : {
"serial" : <serial number> ,
"uuid" : <the UUID of the configuration that generated the reboot log>,
"date" : <Unix time when this reboot occurred>,
"type" : <string>,
"info" : [ "info 1", "info 2"]
}
}
```
Here is a possible list of reboot reasons:
#### Config change pending event
Device sends this message to tell the controller that the device
has received a configuration but is still running an older configuration. The controller will not
reply to this message.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "cfgpending" ,
"params" : {
@@ -196,7 +131,7 @@ reply to this message.
#### DeviceUpdate event
Device sends this message to tell the controller it is changing something is its configuration because
of some requirement or some changes.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "deviceupdate" ,
"params" : {
@@ -210,7 +145,7 @@ of some requirement or some changes.
#### Send a keepalive to the controller event
Device sends a keepalive whenever necessary. The device will send this message to tell the controller
which version it is running. The Controller may decide to send the device a newer configuration.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "ping" ,
"params" : {
@@ -222,7 +157,7 @@ which version it is running. The Controller may decide to send the device a newe
#### Recovery Event
Device may decide it has to do into recovery mode. This event should be used.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "recovery" ,
"params" : {
@@ -235,34 +170,6 @@ Device may decide it has to do into recovery mode. This event should be used.
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : 0 or an error number,
"text" : <description of the error or success>
}
},
"id" : <same number>
}
```
#### Device requests a venue broadcast message
Device send this message when it wants to reach out to all other APs in the same venue. The GW will find the
venue where this device belongs and resend the same message to all other devices in the venue.
```json
{ "jsonrpc" : "2.0" ,
"method" : "venue_broadcast" ,
"params" : {
"serial" : <serial number> ,
"timestamp" : <the UTC timestamp when the message was sent>,
"data" : <JSON document to broadcast>
}
}
```
Upon receiving a `venue_broadcast` message, the GW will simply resent the message to all the APs in the venue.
### Controller commands
Most controller commands include a `when` member. This is a UTC clock time asking the AP
@@ -273,7 +180,7 @@ always a numeric parameter.
#### Controller wants the device to apply a given configuration
Controller sends this command when it believes the device should load a new configuration. The device
should send message with `pending change` events until this version has been applied and running.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "configure" ,
"params" : {
@@ -287,7 +194,7 @@ should send message with `pending change` events until this version has been app
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -306,54 +213,8 @@ The device should answer:
},
"id" : <same number>
}
```
#### Controller wants the device to apply a given fixed configuration
Controller sends this command when it requires the device to apply fixed configuration, eg. country code. The device
should respond with message indicating failure or success.
```json
{ "jsonrpc" : "2.0",
"method" : "fixedconfig",
"params" : {
"serial" : <serial number>,
"when" : Optional - <UTC time when to apply this config, 0 means immediate, this is a suggestion>
"country" : "<country-code>"
},
}
```
If AP supports compressed configuration feature by inidcating `compress_cmd=true` in its capabilities, controller
will send a compressed configuration message where configuration payload (i.e. contents of `params`) is compressed
and encoded in base64 format:
```json
{ "jsonrpc" : "2.0",
"method" : "configure",
"params" : {
"compress_64" : "<b64 encoded zlib compressed payload>",
"compress_sz" : "<size of uncompressed data in bytes>"
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0",
"result" : {
"serial": <serial number>,
"status": {
"error": 0 or an error number,
"text": <description of the error or success, eg. "Applied fixed config, rebooting">
},
"uuid": <UUID>
}
}
```
##### The Answer
The device can answer and tell the controller it has rejected certain parts of the config and potentially replaced them with
appropriate values. This could be used to allow a device to replace frequencies for the regions it is located in. The device
@@ -372,7 +233,7 @@ The rejected section is an array containing the following:
#### Controller wants the device to reboot
Controller sends this command when it believes the device should reboot.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "reboot" ,
"params" : {
@@ -384,7 +245,7 @@ Controller sends this command when it believes the device should reboot.
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -401,56 +262,22 @@ The device should answer:
- 1 : the device is busy but will reboot soon. `text` may indicate why.
- 2 : the device will not reboot. `text` contains information as to why.
#### Controller wants to power-cycle PoE port(s)
Controller sends this command to power-cycle 1 or more PoE ports
```json
{ "jsonrpc" : "2.0" ,
"method" : "powercycle" ,
"params" : {
"serial" : <serial number> ,
"ports" : [ { "name" : "Ethernet1", "cycle" : 5000}, { "name" : "Ethernet8", "cycle" : 10000 } ],
"when" : Optional - <UTC time when to reboot, 0 mean immediately, this is a suggestion>
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : 0 or an error number,
"text" : [ "Error 1" , "Error 2" ],
"when" : <time when this will be performed as UTC seconds>,
},
"id" : <same id from request>
}
```
###### Error codes
- 0 : is rebooting at `when` seconds.
- 1 : the device is busy but will reboot soon. `text` may indicate why.
- 2 : the device will not reboot. `text` contains information as to why.
#### Controller wants the device to upgrade its firmware
Controller sends this command when it believes the device should upgrade its firmware.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "upgrade" ,
"params" : {
"serial" : <serial number> ,
"when" : Optional - <UTC time when to upgrade the firmware, 0 mean immediate, this is a suggestion>,
"uri" : <URI to download the firmware>,
"FWsignature" : <string representation of the signature for the FW> (optional)
"uri" : <URI to download the firmware>
},
"id" : <some number>
}
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -463,17 +290,10 @@ The device should answer:
"id" : <same number>
}
```
Here are the error values
```text
0: No error
1: Bad firmware
2: Missing signature
```
#### Controller wants the device to perform a factory reset
Controller sends this command when it believes the device should upgrade its firmware.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "factory" ,
"params" : {
@@ -486,7 +306,7 @@ Controller sends this command when it believes the device should upgrade its fir
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -500,50 +320,9 @@ The device should answer:
}
```
#### Controller issuing RRM commands to the AP
Controller sends this command to perform several RRM commands.
```json
{ "jsonrpc" : "2.0" ,
"method" : "rrm" ,
"params" : {
"serial" : <serial number> ,
"actions": [
{
"type": "roam",
"bss": [ "00:11:22:33:44:55", ... ],
"params" : { action specific data }
}, {
"type": "tx-power",
"bss": [ "00:11:22:33:44:55", ... ],
“params”: { action specific data }
}, {
"type": "beacon-request",
"bss": [ "00:11:22:33:44:55", ... ],
"params": { action specific data }
}
]
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : 0 or an error number,
"text" : <description of the error or success>,
}
},
"id" : <same number>
}
```
#### Controller wants the device to flash its LEDs
Controller sends this command when it wants the device to flash its LEDs.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "leds" ,
"params" : {
@@ -557,13 +336,14 @@ Controller sends this command when it wants the device to flash its LEDs.
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : 0 or an error number,
"text" : <description of the error or success>,
"when" : <time when this will be performed as UTC seconds>,
}
},
"id" : <same number>
@@ -575,9 +355,47 @@ The device should answer:
- 1 : device cannot flash LEDs because it does not have any.
- 2 : device rejects the request. `text` should include information as to why.
#### Controller sends a device specific command
Controller sends this command specific to this device. The command is proprietary and must be agreed upon by the device
and the controller.
```
{ "jsonrpc" : "2.0" ,
"method" : "perform" ,
"params" : {
"serial" : <serial number> ,
"when" : Optional - <UTC time when to perform this command, 0 mean immediate, this is a suggestion>,
"command" : <this is device specific and is TEXT only>,
"payload" : <JSON Document: containing additional information about the command>
},
"id" : <some number>
}
```
The device should answer:
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : 0 or an error number,
"text" : <description of the error or success>,
"when" : <in UTC time in seconds>,
"resultCode" : <0 or an appropriate error code>,
"resultText" : <any text resulting from the command. This is propietary to each command>
}
},
"id" : <same number>
}
```
##### The device answer
The device should answer with teh above message. The `error` value should be interpreted the following way:
- 0 : the command was performed as requested and the reults of the command is available in the `resultCode` and `resultText` parameters.
- 1 : the command will be performed in the future and `when` shows that time. The `resultCode` and `resultText` dod not contain anything relevant.
- 2 : the command cannot be performed as indicated. `resultCode` and `resultText` may contain some indication as to why.
#### Controller wants the device to perform a trace
Controller sends this command when it needs the device to perform a trace (i.e. tcpdump).
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "trace" ,
"params" : {
@@ -594,7 +412,7 @@ Controller sends this command when it needs the device to perform a trace (i.e.
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -620,7 +438,7 @@ uploaded or the timeout occurs, the upload will be rejected.
#### Controller wants the device to perform a WiFi Scan
Controller sends this command when it needs the device to perform a WiFi Scan.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "wifiscan" ,
"params" : {
@@ -628,16 +446,14 @@ Controller sends this command when it needs the device to perform a WiFi Scan.
"bands" : [ "2","5","5l","5u",6" ], <optional this is a list of bands to scan: on or more bands >
"channels" : [ 1,2,3...] , <optional list of discreet channels to scan >
"verbose" : <optional boolean: true or false> (by default false),
"bandwidth" : <optional int: 20,40,80 in MHz>,
"active" : 0 or 1 (to select passive or active scan),
"ies": <optional: array of unsigned int 8 bits: i.e. [1,4,34,58,91]>
"active" : 0 or 1 (to select passive or active scan)
},
"id" : <some number>
}
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -660,7 +476,7 @@ Controller sends this command when it needs the device to provide a message back
supported messages are "state" and "healthcheck". More messages maybe added later. The messages will
be returned the usual way. The RPC response to this message just says that the request has been accepted and the
message will be returned "soon".
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "request" ,
"params" : {
@@ -674,7 +490,7 @@ message will be returned "soon".
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -690,7 +506,7 @@ The device should answer:
#### Controller requesting eventqueue buffers
Controller sends this command when it needs the device to provide the content of ist ring buffers.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "event" ,
"params" : {
@@ -704,7 +520,7 @@ Controller sends this command when it needs the device to provide the content of
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -720,7 +536,7 @@ The device should answer:
#### Controller requesting telemetry stream information
Controller sends this command when it needs the device to telemetry streaming.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "telemetry" ,
"params" : {
@@ -733,7 +549,7 @@ Controller sends this command when it needs the device to telemetry streaming.
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -747,7 +563,7 @@ The device should answer:
```
When the interval is greater than 0, the gateway will start to receive messages
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "telemetry" ,
"params" : {
@@ -762,7 +578,7 @@ The device will stop sending data after 30 minutes or if it receives a `telemetr
#### Controller requesting an `rtty` session
Controller sends this command an administrator requests to start an `rtty` session with the AP.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "remote_access" ,
"params" : {
@@ -780,7 +596,7 @@ Controller sends this command an administrator requests to start an `rtty` sessi
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -796,7 +612,7 @@ The device should answer:
#### Controller wants to ping the device
Controller sends this command when it tries to establish latency to the device.
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "ping" ,
"params" : {
@@ -807,7 +623,7 @@ Controller sends this command when it tries to establish latency to the device.
```
The device should answer:
```json
```
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
@@ -818,209 +634,8 @@ The device should answer:
}
```
#### Controller wants the device to perform a script
Controller sends this command to run a predefined script. Extreme care must be taken.
```json
{ "jsonrpc" : "2.0" ,
"method" : "script" ,
"params" : {
"serial" : <serial number>,
"type" : <one of "shell", "ucode", "bundle">,
"script" : <text blob containing the script, This must be vase64 encoded>,
"timeout" : <max timeout in seconds, default is 30, unused if URI is supplied>,
"uri": "<upload script results using this URI>",
"signature" : "<signature for script>: must be supplied to restricted devices",
"when" : <time when this will be performed as UTC seconds>
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
one of either
"result_64" : <gzipped base64 result of running the command>,
"result_sz" : <size of unzipped content>
or
"result" : <a single text blob of the result>
}
},
"id" : <same number>
}
```
#### Controller wants the device to replace its certificates
Controller sends this command to run a predefined script. Extreme care must be taken.
```json
{ "jsonrpc" : "2.0" ,
"method" : "certupdate" ,
"params" : {
"serial" : <serial number>,
"certificates" : <BASE64 encoded tar file of the cert package from the certificate portal>
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
"txt" : <text describing the error or success>
},
"id" : <same number as request>
}
```
#### Controller wants the device to perform re-enrollment
Controller sends this command to trigger re-enrollment, i.e. update of operational certificate. Extreme care must be taken.
```json
{ "jsonrpc" : "2.0" ,
"method" : "reenroll" ,
"params" : {
"serial" : <serial number>,
"when" : Optional - <UTC time when to apply this config, 0 mean immediate, this is a suggestion>
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
"txt" : <text describing the error or success>
},
"id" : <same number as request>
}
```
#### Controller wants the device to switch to another controller
Controller sends this when the device should change the controller it connects to without looking up a new redirector.
```json
{ "jsonrpc" : "2.0" ,
"method" : "transfer" ,
"params" : {
"serial" : <serial number>,
"server" : <controller hostname>,
"port" : <controller port number (integer)>,
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
"txt" : <text describing the error or success>
},
"id" : <same number as request>
}
```
### RRM AP device commands
The following command is used to send RRM commands to an AP. RRM commands are send to an AP, however the
controller will not or cannot verify if they have been sent or the action was performed.
```json
{ "jsonrpc" : "2.0" ,
"method" : "rrm" ,
"params" : {
"serial" : <serial number>,
"actions" : [ array of actions. Each possible action is defined next]
},
"id" : <some number>
}
```
The device should answer:
```json
{ "jsonrpc" : "2.0" ,
"result" : {
"serial" : <serial number> ,
"status" : {
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
"txt" : <text describing the error or success>
},
"id" : <same number as request>
}
```
#### RRM Roam action
##### Kick
```json
{
"action" : "kick" ,
"addr" : <mac if the client that shall be kicked> ,
"reason": <number>, (default: 5, https://www.cisco.com/assets/sol/sb/WAP371_Emulators/WAP371_Emulator_v1-0-1-5/help/Apx_ReasonCodes2.html)
"ban_time": <number> (seconds, optional)
}
```
##### Channel Switch Announcement
```json
{
"action" : "channel_switch" ,
"bssid" : <mac of the SSID> , (all other SSIDs on the same radio will perform the same action)
"channel" : <number> (HT/HW mode will be retained upon issuing the CSA)
}
```
##### Change TX-Power
```json
{
"action" : "tx_power" ,
"bssid" : <mac of the SSID> , (all other SSIDs on the same radio will perform the same action)
"level" : <number> (DBm inside the positive number space)
}
```
##### Beacon Scan
```json
{
"action" : "beacon_request" ,
"addr" : <mac if the client that shall perform the scan> ,
"ssid": <string>, (the SSID the client shall scan for on all frequencies),
"channel": <number> (the channel that shall be scanned)
}
```
##### BSS Transition
```json
{
"action" : "bss_transition" ,
"addr" : <mac if the client that shall perform the roam> ,
"neighbors": [ <string> ], (an array of BSSIDs the client shall consider as roamin candidates)
}
```
##### Update neighbours
```json
{
"action" : "neighbors" ,
"bssid" : <mac of the SSID> , (the SSID of the specific VAP)
"neighbors": [ [ <BSS>, <ssid>, <neighbor report> ] ]
}
```
### `rtty server`
#### `rtty server`
More information about the [rtty server](https://github.com/zhaojh329/rtty) can be found here.
### Message compression
@@ -1031,48 +646,21 @@ Should other messages get larger, the client may decide to compress the. Only me
#### Identifying a compressed message
A compressed message has a single member to the `params` field. It's only parameter must be called `compress_64`. Any other elements under
params will be dropped. Additional compression schemes may be developed later. The device should also include
a hint to the actual size of the uncompressed data. This would allow listeners to create sufficiently sized
buffers right away instead of guessing. If the device includes `compressed_sz` as the second field in the
params objects. This should be an unsigned int representing the total size of the uncompressed data.
params will be dropped. Additional compression schemes may be developed later.
#### How to compress
The original `params` element should be run through `zlib:compress` and then encoded using base64, and passed as a string. Here is an example
of the completed message. The following should how the `state` event could be compressed:
```json
```
{ "jsonrpc" : "2.0" ,
"method" : "state" ,
"params" : {
"compress_64" : "kqlwhfoihffhwleihfi3uhfkjehfqlkwhfqkhfiu3hffhkjwehfqkwjehfqwiefkjehq.....qwjqkfhqjwk",
"compress_sz" : 212322
"compress_64" : "kqlwhfoihffhwleihfi3uhfkjehfqlkwhfqkhfiu3hffhkjwehfqkwjehfqwiefkjehq.....qwjqkfhqjwk"
}
}
```
### 'Radius Proxying'
The gateway can receive RADIUS messages from the device and forward them. It can also receive messages
on its behalf and send them to the device.
```json
{
"radius" : <type, can be auth, acct, coa> ,
"data" : <base 64 encoded raw RADIUS payload>
}
```
The GW will include a TLV to mark the sender MAC. The RADIUS server must use the same TLV to
identify the destination for its messages.
#### Incoming RADIUS messages configuration
The GW must be configured with the following:
```asm
radius.proxy.enable = true
radius.proxy.accounting.port = 1813
radius.proxy.authentication.port = 1812
radius.proxy.coa.port = 3799
```

570
README.md
View File

@@ -1,36 +1,202 @@
<p align="center">
<img src="images/project/logo.svg" width="200" alt="OpenWiFi Project"/>
</p>
# uCentralGW
# OpenWiFI Gateway (OWGW)
## What is it?
The OpenWiFi Gateway is a service for the TIP OpenWiFi CloudSDK (OWSDK).
OWGW manages Access Points that implement the OpenWiFi uCentral protocol. OWGW, like all other OWSDK microservices, is
defined using an OpenAPI definition and uses the ucentral communication protocol to interact with Access Points. To use
the OWGW, you either need to [build it](#building) or use the [Docker version](#docker).
## What is this?
The uCentralGW is an added service for the TIP controller that allows integration with the
uCentral protocol. It supports a complete OpenAPI definition and uses the ucentral communication protocol. To use the uCentralGW,
you either need to [build it](#building) or use the [Docker version](#docker).
## Building
To build the microservice from source, please follow the instructions in [here](./BUILDING.md)
In order to build the uCentralGW, you will need to install its dependencies, which includes the following:
- cmake
- boost
- POCO 1.10.1 or later
- a C++17 compiler
- libyaml
- openssl
- libpq-dev (PortgreSQL development libraries)
- mysql-client (MySQL client)
- librdkafka
- cppkafka
## Docker
To use the CLoudSDK deployment please follow [here](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy)
The build is done in 2 parts. The first part is to build a local copy of the framework tailored to your environment. This
framework is called [Poco](https://github.com/pocoproject/poco). The version used in this project has a couple of fixes
from the master copy needed for cmake. Please use the version of this [Poco fix](https://github.com/stephb9959/poco). Building
Poco may take several minutes depending on the platform you are building on.
### Ubuntu
These instructions have proven to work on Ubuntu 20.4.
```
sudo apt install git cmake g++ libssl-dev libmariadb-dev
sudo apt install libpq-dev libaprutil1-dev apache2-dev libboost-all-dev
sudo apt install librdkafka-dev // default-libmysqlclient-dev
cd ~
git clone https://github.com/stephb9959/poco
cd poco
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ~
git clone https://github.com/stephb9959/cppkafka
cd cppkafka
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ~
git clone https://github.com/nlohmann/json.git
cd json
mkdir cmake-build
cd cmake-build
cmake ..
make -j
sudo make install
cd ~
git clone https://github.com/pboettch/json-schema-validator.git
cd json-schema-validator
mkdir cmake-build
cd cmake-build
cmake ..
make -j
sudo make install
cd ~
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
cd wlan-cloud-ucentralgw
mkdir cmake-build
cd cmake-build
cmake ..
make -j 8
```
### Fedora
The following instructions have proven to work on Fedora 33
```
sudo yum install cmake g++ openssl-devel unixODBC-devel mysql-devel mysql apr-util-devel boost boost-devel
sudo yum install yaml-cpp-devel lua-devel
sudo dnf install postgresql.x86_64 librdkafka-devel
sudo dnf install postgresql-devel
git clone https://github.com/stephb9959/poco
cd poco
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
git clone https://github.com/stephb9959/cppkafka
cd cppkafka
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ~
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
cd wlan-cloud-ucentralgw
mkdir cmake-build
cd cmake-build
cmake ..
make
```
### OSX Build
The following instructions have proven to work on OSX Big Sur. You need to install [Homebrew](https://brew.sh/). You must also have installed [XCode for OS X](https://www.freecodecamp.org/news/how-to-download-and-install-xcode/).
```
brew install openssl
brew install cmake
brew install libpq
brew install mysql-client
brew install apr
brew install apr-util
brew install boost
brew install yaml-cpp
brew install postgresql
brew install unixodbc
brew install librdkafka
git clone https://github.com/stephb9959/poco
cd poco
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release -j
sudo cmake --build . --target install
git clone https://github.com/stephb9959/cppkafka
cd cppkafka
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ~
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
cd wlan-cloud-ucentralgw
mkdir cmake-build
cd cmake-build
cmake ..
make -j
```
### Raspberry
The build on a rPI takes a while. You can shorten that build time and requirements by disabling all the larger database
support. You can build with only SQLite support by not installing the packages for ODBC, PostgreSQL, and MySQL by
adding -DSMALL_BUILD=1 on the cmake build line.
```
sudo apt install git cmake g++ libssl-dev libaprutil1-dev apache2-dev libboost-all-dev libyaml-cpp-dev
git clone https://github.com/stephb9959/poco
cd poco
mkdir cmake-build
cd cmake-build
cmake ..
cmake --build . --config Release
sudo cmake --build . --target install
cd ~
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
cd wlan-cloud-ucentralgw
mkdir cmake-build
cd cmake-build
cmake -DSMALL_BUILD=1 ..
make
```
### After completing the build
After completing the build, you can remove the Poco source as it is no longer needed.
#### Expected directory layout
From the directory where your cloned source is, you will need to create the `certs`, `logs`, and `uploads` directories.
```bash
```shell
mkdir certs
mkdir certs/cas
mkdir logs
mkdir uploads
```
You should now have the following:
```text
--+-- certs
```
-- cert_scripts
|
+-- certs
| +--- cas
+-- cmake
+-- cmake-build
+-- logs
+-- logs (dir)
+-- src
+-- test_scripts
+-- openapi
@@ -39,16 +205,12 @@ You should now have the following:
```
### Certificates
The OWGW uses a number of certificates to provide security. There are 2 types of certificates required for
a normal deployment:
- A Server Certificate to secure the OWGW<->AP channel
- A REST API Certificate to secure the Northbound API
- Device Certificates
Love'em of hate'em, we gotta use'em. So we tried to make this as easy as possible for you.
#### The `certs` directory
For all deployments, you will need the following certs directory, populated with the proper files.
```text
```asm
certs ---+--- root.pem
+--- issuer.pem
+--- websocket-cert.pem
@@ -63,69 +225,183 @@ certs ---+--- root.pem
```
#### DigiCert files
These are the files you should install on your OWGW and devices. For your OWGW, you will need to provide tge following files in the directory above
These are the files you should install on your gateway and devices. For your gateway, you will need to provide tge following files in the directory above
- `root.pem` is [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem).
- `issuer.pem` is [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/issuer.pem).
- `clientcas.pem` is [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/clientcas.pem).
#### OWGW Server certificate (must be obtained from TIP)
#### Gateway certificates: TIP portion (* must be obtained from TIP)
The gateway requires its own DigiCert certificate. Once obtained, you need to identify the `key` and the `certificate` rename
them `websocket-key.pem` and `websocket-cert.pem`, and copy them in your `certs` directory. These files mus be obtained from TIP.
#### OWGW for certificate: for REST API (from your favourite Certificate Provider)
The gateway requires a key/certificate/ca for the REST interface. These files you need to obtain on your own or generate them. This is beyond the scope of this
document. You, may choose to select LestEncrypt or any other Certificate Authority. Once you have these files, you need to renamed them `restapi-key.pem`, `restapi-cert.pem`, and `restapi-ca.pem`.
This will guarantee proper HTTPS in your browser and RESTAPI.
#### Gateway certificates: for REST API
The gateway requires a key/vertificate/ca for the REST interface. These files you need to obtain on your own of generate them. This is beyond the scope of this
document. Once you have these files, you need to renamed them `restapi-key.pem`, `restapi-cert.pem`, and `restapi-ca.pem`. This will guarantee proper HTTPS
in your browner
### OpenWiFi Device certificates
This may have already done at the factory. If not, you will need to get the following in order to point your devices to use the OWGW:
#### Configuration
The configuration for this service is kept in a properties file. This file is called `owgw.properties` and you can
see the latest version [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/owgw.properties). The file will be loaded from
the directory set by the environment variable `UCENTRALGW_CONFIG`. To use environment variables in the configuration,
you must use `$<varname>`. Only `path names` support the use of environment variables. The sample configuration requires very
little changes if you keep the suggested directory structure. For the sample configuration to work, you need to define 2
environment variables.
```
export OWGW_ROOT=`pwd`
export UCENTRALGW_CONFIG=`pwd`
```
If you current working directory is the root of the project, this will set the variables properly. Otherwise, you can set the variables
to point to wherever is necessary.
##### Important config entries
###### This is the logging directory
```
logging.channels.c2.path = $OWGW_ROOT/logs/sample.log
```
###### This is the type of storage in use
```asm
storage.type = sqlite
```
###### Autoprovisioning settings
```asm
openwifi.autoprovisioning = true
openwifi.devicetypes.0 = AP:linksys_ea8300,edgecore_eap101,linksys_e8450-ubi
openwifi.devicetypes.1 = SWITCH:edgecore_ecs4100-12ph
openwifi.devicetypes.2 = IOT:esp32
```
###### This is the RESTAPI endpoint
```asm
openwifi.restapi.host.0.backlog = 100
openwifi.restapi.host.0.security = relaxed
openwifi.restapi.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
openwifi.restapi.host.0.address = *
openwifi.restapi.host.0.port = 16002
openwifi.restapi.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
openwifi.restapi.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
openwifi.restapi.host.0.key.password = mypassword
```
##### This is the end point for the devices to connect with
This is the crucial section. I bet that 97.4% of all your problems will come from here, and it's boring. So put some good music on,
give the kids the iPad, get a cup of coffee, and pay attention. Every field will be explained.
###### ucentral.websocket.host.0.backlog
This is the number of concurrent devices you are expecting to call all at once. Not the current number of devices. This is how many will connect in the same exact second.
Take the total number of devices you have and divide by 100. That's a good rule of thumb. Never go above 500.
###### ucentral.websocket.host.0.rootca
This is the root file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem)
###### ucentral.websocket.host.0.issuer
This is the issuer file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/issuer.pem)
###### ucentral.websocket.host.0.cert
This is a `pem` file that you will receive from Digicert for the gateway itself. This is the certificate for the gateway.
###### ucentral.websocket.host.0.key
This is a `pem` file that you will receive from Digicert for the gateway itself. The is the private key for the gateway.
###### ucentral.websocket.host.0.clientcas
This is a `pem` file that contains both the issuer and the root CA certificates. You can find it You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/clientcas.pem)
###### ucentral.websocket.host.0.cas
This is a directory where you will copy your own `cert.pem`, the `root.pem`, and the `issuer.pem` files.
###### ucentral.websocket.host.0.address
Leve this a `*` in teh case you want to bind to all interfaces on your gateway host or select the address of a single interface.
###### ucentral.websocket.host.0.port
Leave to 15002 for now.
###### ucentral.websocket.host.0.security
Leave this as strict for now for devices.
###### ucentral.websocket.host.0.key.password
If you key file uses a password, please enter it here.
###### ucentral.websocket.maxreactors
A single reactor can handle between 1000-2000 devices. Never leave this smaller than 5 or larger than 50.
#### Conclusion
You will need to get the `cert.pem` and `key.pem` from Digicert. The rest is here.
```asm
ucentral.websocket.host.0.backlog = 500
ucentral.websocket.host.0.rootca = $OWGW_ROOT/certs/root.pem
ucentral.websocket.host.0.issuer = $OWGW_ROOT/certs/issuer.pem
ucentral.websocket.host.0.cert = $OWGW_ROOT/certs/websocket-cert.pem
ucentral.websocket.host.0.key = $OWGW_ROOT/certs/websocket-key.pem
ucentral.websocket.host.0.clientcas = $OWGW_ROOT/certs/clientcas.pem
ucentral.websocket.host.0.cas = $OWGW_ROOT/certs/cas
ucentral.websocket.host.0.address = *
ucentral.websocket.host.0.port = 15002
ucentral.websocket.host.0.security = strict
ucentral.websocket.host.0.key.password = mypassword
ucentral.websocket.maxreactors = 20
```
###### This is the end point for the devices when uploading files
```asm
openwifi.fileuploader.host.0.backlog = 100
openwifi.fileuploader.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
openwifi.fileuploader.host.0.security = relaxed
openwifi.fileuploader.host.0.address = *
openwifi.fileuploader.host.0.name = 192.168.1.176
openwifi.fileuploader.host.0.port = 16003
openwifi.fileuploader.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
openwifi.fileuploader.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
openwifi.fileuploader.host.0.key.password = mypassword
openwifi.fileuploader.path = $OWGW_ROOT/uploads
openwifi.fileuploader.maxsize = 10000
```
###### host.0.address entries
If you want to limit traffic to a specific interface, you should specify the IP address of that interface instead of
the `*`. Using the `*` means all interfaces will be able to accept connections. You can add multiple interfaces
by changing the `0` to another index. You need to repeat the whole configuration block for each index. Indexes must be sequential
start at `0`.
###### openwifi.fileuploader.host.0.name
This must point to the IP or FQDN of your uCentralGW.
#### Running the gateway
Tu run the gateway, you must run the executable `ucentralgw`. You can use several command line options to run as a daemon or specify the configuration file location.
#### Device configuration
Once you have the gateway configured, you will need to have some devices coming to it. For now, you will need to get
the following in order to use the gateway:
- A DigiCert certificate that you will call `cert.pem`
- A DigiCert key that goes with that certificate. Please call this `key.pem`
- The Digicert root certificate that you will find [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem). You must copy `root.pem`
and rename it `cas.pem` on the device.
and rename it `cas.pem` on the device.
- A Device ID file called `dev-id` or something similar
- Copy all the 4 files to the `/certificates` directory of the AP (you must have firmware created Jun 15th or later).
You will need to upgrade your device to the latest firmware. Once updated, you will need to copy the 4 files mentioned above in
the `/certificates` directory. Please remove all old keys or certificates from the `/etc/ucentral` directory
You will need to upgrade your device to the latest firmware. Once updated, you will need to copy the 4 files mentioned above in
the `/certificates` directory. Please remove all old keys or certificates from the `/etc/ucentral` directory
(anything ending in `.pem`).
### Environment variables
The following environment variables should be set from the root directory of the service. They tell the OWGW process where to find
the configuration and the root directory.
```bash
export OWGW_ROOT=`pwd`
export OWGW_CONFIG=`pwd`
#### Server key entry
The gateway needs to encrypt information from time to time. In order to do so, it must have a crypto key. This key
can be any of the keys you are already using. You must keep that keep secret and always use it. In the configutation,
this is the entry
```asm
openwifi.service.key = $OWGW_ROOT/certs/websocket-key.pem
```
You can run the shell script `set_env.sh` from the microservice root.
### OWGW Service Configuration
The configuration is kept in a file called `owgw.properties`. To understand the content of this file,
please look [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/CONFIGURATION.md)
### Running the OWGW
Tu run the OWGW, you must run the executable `owgw`. You can use several command line options to run as a daemon or
specify the configuration file location.
### systemd: owgw.service
`owgw.service` is a skeleton to allow to run the OWGW in a systemd based operating system (i.e. Debian). You will need
to modify slightly to reflect your environment.
### Default device configuration
By default, the devices will receive a built-in default configuration. This built-in default configuration is probably not what you need.
So there are 2 options in order to specify your own default configuration:
- In the OWGW data directory, you can create a `default_config.json` file that contains your own personal configuration
- Using the OWGW UI, on the left hand pane, there is a `configurations` choice. There you can do the same but apply it with more granularity ot each device type
you may be deploying.
### Command line options
#### Command line options
The current implementation supports the following. If you use the built-in configuration file, you do not need to use any command-line
options. However, you may decide to use the `--daemon` or `umask` options.
```bash
./owgw --help
usage: owgw OPTIONS
A owgw gateway implementation for TIP.
./ucentralgw --help
usage: ucentralgw OPTIONS
A uCentral gateway implementation for TIP.
--daemon Run application as a daemon.
--umask=mask Set the daemon's umask (octal, e.g. 027).
@@ -136,27 +412,119 @@ A owgw gateway implementation for TIP.
--logs=dir specify the log directory and file (i.e. dir/file.log)
```
#### file
This allows you to point to another file without specifying the OWGW_CONFIG variable. The file name must end in `.properties`.
#### daemon
##### file
This allows you to point to another file without specifying the UCENTRALGW_CONFIG variable. The file name must end in `.properties`.
##### daemon
Run this as a UNIX service
#### pidfile
##### pidfile
When running as a daemon, the pid of the running service will be set in the speficied file
#### debug
##### debug
Run the service in debug mode.
#### logs
##### logs
Speficy where logs should be kept. You must include an existing directory and a file name. For example `/var/ucentral/logs/log.0`.
#### umask
##### umask
Seet the umask for the running service.
## Docker
If you would rather launch the docker-compose or helm for the controller, please click [here](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy).
### ALB Support
Support for AWS ALB is provided through the following configuration elements
```asm
alb.enable = true
alb.port = 16102
```
### Docker
So building this thing from scratch is not your thing? I can't blame you. It takes some patience and
in the end, there's still more work. Here comes `docker` to the rescue. You can run a docker version following
these instructions. The following is the content of the `docker_run.sh` script you can find
[here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/docker_run.sh):
```bash
#!/bin/sh
HUBNAME=tip-tip-wlan-cloud-ucentral.jfrog.io
IMAGE_NAME=ucentralgw
DOCKER_NAME=$HUBNAME/$IMAGE_NAME
CONTAINER_NAME=ucentralgw
#stop previously running images
docker container stop $CONTAINER_NAME
docker container rm $CONTAINER_NAME --force
if [[ ! -d logs ]]
then
mkdir logs
fi
if [[ ! -d certs ]]
then
echo "certs directory does not exist. Please create and add the proper certificates."
exit 1
fi
if [[ ! -f owgw.properties ]]
then
echo "Configuration file owgw.properties is missing in the current directory"
exit 2
fi
docker run -d -p 15002:15002 \
-p 16002:16002 \
-p 16003:16003 \
--init \
--volume="$PWD:/ucentral-data" \
-e UCENTRAL_ROOT="/ucentral-data" \
-e UCENTRALGW_CONFIG="/ucentral-data" \
--name="ucentralgw" $DOCKER_NAME
```
Create yourself a directory and copy that script which you can also get from [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/docker_run.sh).
You must have the basic configuration file copied in the directory. This file must be called `owgw.properties`. You can bring your own or
copy it from [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/owgw.properties). Please look at [this](#certificates-with-docker) to have the right
certificates. You need to make sure that the names match the content of the `owgw.properties`
file. Once all this is done, you can simply run `docker_run.sh`.
#### Docker installation directory layout
Here is the layout expected for your Docker installation
```asm
Run-time root
|
----- certs (same as above)
+---- logs (dir)
+---- uploads (dir)
+---- owgw.properties (file)
```
#### `owgw.properties` for Docker
If you use the pre-made configuration file, and you follow the directory layout, the only line you must change
is the following line:
```asm
openwifi.fileuploader.host.0.name = 192.168.1.176
```
This line should reflect the IP of your gateway or its FQDN. You must make sure that this name or IP is accessible
from your devices. This is used during file uploads from the devices.
#### Certificates with Docker
Please refer to the `certs` directory from the sections above.
#### Configuration with Docker
The configuration for this service is kept in a properties file. Currently, this configuration file must be kept in the
current directory of uCentral or one level up. This file is called `owgw.properties` and you can see the latest version
[here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/owgw.properties). The file will be loaded from
the directory set by the environment variable `UCENTRALGW_CONFIG`. To use environment variables in the configuration,
you must use `$<varname>`. The path for the logs for the service must exist prior to starting the
service. The path is defined under `logging.channels.c2.path`. Only `path names` support the use of
environment variables. Here is a sample configuration:
## uCentral communication protocol
The communication protocol between the device and the OGWG is detailed in this [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/PROTOCOL.md).
The communication protocol between the device and the controller is detailed in this [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/PROTOCOL.md).
## OpenAPI
The OWGW supports an OpenAPI REST based interface for management. You can find the [definition here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/owgw.yaml).
The service supports an OpenAPI REST based interface for management. You can find the [definition here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/owgw.yaml).
And here is [how to use it](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/OPENAPI.md)
## Using the API
@@ -166,31 +534,33 @@ or [python](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/ma
More scripts will be added in the future.
## Firewall Considerations
| Port | Description | Configurable |
| :--- | :--- |:------------:|
| 15002 | Default port from the devices to the OWGW | yes |
| 16002 | Default port for REST API Access to the OWGW | yes |
| 5912 | Default port for RTTY connection | yes |
| 5913 | Defailt port for RTTY connection | yes |
- The protocol uses TCP port 15002 between the devices and the gateway. This port must be opened.
- Devices use the TCP port 16003 to upload files. This port is configurable in the `owgw.properties` file. Look for `openwifi.fileuploader.host.0.port`.
- The RESTAPI is accessed through TCP port 16002 by default. This port is configurable in the `owgw.properties` file. Look for the entry `openwifi.restapi.host.0.port`.
## Kafka topics
## Kafka integration
So what about Kafka? Well, the gateway has basic integration with Kafka. It is turned off by default, to turn it on, in the configuration:
```asm
openwifi.kafka.enable = false
openwifi.kafka.brokerlist = 127.0.0.1:9092
openwifi.kafka.commit = false
openwifi.kafka.queue.buffering.max.ms = 50
```
#### `openwifi.kafka.enable`
Kind of obvious but hey, set `true` or `false`. Default is `false`
#### `openwifi.kafka.brokerlist`
This is a comma separator list of the brokers in your `kafka` deployment.
#### Kafka topics
Toe read more about Kafka, follow the [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/KAFKA.md)
## Contributions
We need more contributors. Should you wish to contribute,
please follow the [contributions](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/CONTRIBUTING.md) document.
#### Securing `kafka`
This is beyond the scope of this document. As it stands today, the communication between the gateway and `kafka` is expected to be behind a firewall.
## Pull Requests
Please create a branch with the Jira addressing the issue you are fixing or the feature you are implementing.
Create a pull-request from the branch into master.
## Additional OWSDK Microservices
Here is a list of additional OWSDK microservices
| Name | Description | Link | OpenAPI |
| :--- | :--- | :---: | :---: |
| OWSEC | Security Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralsec) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml) |
| OWGW | Controller Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/openapi/owgw.yaml) |
| OWFMS | Firmware Management Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralfms) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralfms/blob/main/openapi/owfms.yaml) |
| OWPROV | Provisioning Service | [here](https://github.com/Telecominfraproject/wlan-cloud-owprov) | [here](https://github.com/Telecominfraproject/wlan-cloud-owprov/blob/main/openapi/owprov.yaml) |
| OWANALYTICS | Analytics Service | [here](https://github.com/Telecominfraproject/wlan-cloud-analytics) | [here](https://github.com/Telecominfraproject/wlan-cloud-analytics/blob/main/openapi/owanalytics.yaml) |
| OWSUB | Subscriber Service | [here](https://github.com/Telecominfraproject/wlan-cloud-userportal) | [here](https://github.com/Telecominfraproject/wlan-cloud-userportal/blob/main/openapi/userportal.yaml) |
## Contributors
We love ya! We need more of ya! If you want to contribute, make sure you review
the [coding style](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/CODING_STYLE.md) document.
Feel free to ask questions and post issues.

View File

@@ -1,82 +0,0 @@
# Restricted devices
## What is a restricted device?
A restricted device is one that because of regulations or a desire for utmost security, requires signatures to access restricted or blocked
features. The restriction process is burnt in the device at manufacturing or later by running a specific command on the device. Once a device
is restricted, it cannot be unlocked.
## Current restrictions
Restrictions are stored on the AP in a protected partition. They are contained in a file called `restrictions.json`. Here is a sample:
```json
{
"country": [
"US", "CA"
],
"dfs": true,
"rtty": true,
"tty": true,
"developer": true,
"sysupgrade": true,
"commands": true,
"key_info": {
"vendor": "dummy",
"algo": "static"
}
}
```
- country
- List of countries where this device may be used
- dfs
- Disallow DFS Override during wifi-scan. If set to `true`, device will not allow to override DFS channels
- rtty
- Disallow the use of the RTTY command for this device
- tty
- Do not allow the AP to accept `tty` connection
- developer
- Internal use only.
- sysupgrade
- If set to `true`, only signed firmware upgrade command will be allowed.
- commands
- If set to `true`, do not allow commands.
- key_info
- This structure defines how signatures should be generated and verified in a secure system
- vendor
- An identified that must match the vendor name provided in the controller
- algo
- The signature algorithm. Here are the supported algorithms
- `static`
- A test algorithm that always returns and uses a value of `aaaaaaaaaa`. This should never be used in the field.
- `dgst-sha256`
- The default OpenSSL RSA signature generation and verification. The controller will use the following command to generate the signature
```sh
openssl dgst -sha256 -sign private-key.pem -out signature.txt myfile
```
- The AP will be using the following to verify the signature
```sh
openssl dgst -sha256 -verify public-key.pem -signature signature.txt myfile
```
## Creating signatures on the controller
When a device is restricted and a signature is required, the controller can generate the signature
for the specified `vendor`. However, on the controlelr side, you must configure the vendors. In
order to do so we suggest the following.
- Create a directory called `signatures` under your `certs` directory
- Copy the public and private keys for each `vendor` name. We suggest naming them accordingly
- `vendor`-private-key.pem
- `vendor`-public-key.pem
- In the `owgw.properties` file, you need to declare these signatures the following way
```properties
signature.manager.0.key.public = $OWGW_ROOT/certs/signatures/test1-public-key.pem
signature.manager.0.key.private = $OWGW_ROOT/certs/signatures/test1-private-key.pem
signature.manager.0.vendor = test1
signature.manager.1.key.public = $OWGW_ROOT/certs/signatures/test2-public-key.pem
signature.manager.1.key.private = $OWGW_ROOT/certs/signatures/test2-private-key.pem
signature.manager.1.vendor = test2
```
## How do you use the signatures?
There is nothing to do really. Now the controller will use the proper key to create the signatures
when it sends commands to the AP. It will use the algorithm that the device understands too. This is transparent
to the user. The `vendor` name used in the controller configuration must match the `vendor` name provided in the
`restrictions.json` file.

View File

@@ -1,36 +0,0 @@
scripts:
- name: List Antennas
description: A script to list all antennas on a device
type: shell
runtype:
timeout: 30
filename: listantennas.sh
readme: listantennas.md
help: https://authors.com/scripts/index.html
- name: List AP Noise
description: A script to list all noise values on all APs
type: shell
runtype:
deferred: true
filename: listnoise.sh
readme: listnoise.md
help: https://authors.com/scripts/index.html
- name: Reset AP Statistics
description: A script to reset the statistics on a given AP
type: shell
runtype:
timeout: 30
filename: resetstats.sh
readme: resetstats.md
help: https://authors.com/scripts/index.html
- name: Gather kernel stats
description: A script to all the kernel stats for an AP
type: bundle
runtype:
deferred: true
filename: kstats.uci
readme: kstats.md
help: https://authors.com/scripts/index.html

View File

@@ -1 +0,0 @@
#!/bin/sh

View File

@@ -1,2 +0,0 @@
#!/bin/sh

View File

@@ -1,2 +0,0 @@
#!/bin/sh

View File

@@ -1 +0,0 @@
# Repo for scripts

2
build
View File

@@ -1 +1 @@
3
171

View File

@@ -0,0 +1,4 @@
#include <librdkafka/rdkafka.h>
#if RD_KAFKA_VERSION >= 0x00090400
int main() { }
#endif

BIN
cmake-build-debug/ucentralgw Executable file

Binary file not shown.

View File

@@ -0,0 +1,637 @@
<?xml version="1.0" encoding="UTF-8"?>
<CodeBlocks_project_file>
<FileVersion major="1" minor="6"/>
<Project>
<Option title="ucentralgw"/>
<Option makefile_is_custom="1"/>
<Option compiler="gcc"/>
<Option virtualFolders="CMake Files\;CMake Files\..\;CMake Files\..\..\;CMake Files\..\..\..\;CMake Files\..\..\..\..\;CMake Files\..\..\..\..\..\;CMake Files\..\..\..\..\..\..\;CMake Files\..\..\..\..\..\..\usr\;CMake Files\..\..\..\..\..\..\usr\local\;CMake Files\..\..\..\..\..\..\usr\local\lib\;CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\;CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Boost-1.75.0\;CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_headers-1.75.0\;CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_system-1.75.0\;CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\CppKafka\;CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\;CMake Files\cmake\;CMake Files\cmake-build-debug\;"/>
<Build>
<Target title="all">
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option type="4"/>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 all"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="install/local">
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option type="4"/>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 install/local"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="rebuild_cache">
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option type="4"/>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 rebuild_cache"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="edit_cache">
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option type="4"/>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 edit_cache"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="install/strip">
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option type="4"/>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 install/strip"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="install">
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option type="4"/>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 install"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="list_install_components">
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option type="4"/>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 list_install_components"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="ucentralgw">
<Option output="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/ucentralgw" prefix_auto="0" extension_auto="0"/>
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option object_output="./"/>
<Option type="1"/>
<Option compiler="gcc"/>
<Compiler>
<Add option="-DAPP_VERSION=&quot;0.7.0&quot;"/>
<Add option="-DBUILD_NUMBER=&quot;120&quot;"/>
<Add option="-DTIP_GATEWAY_SERVICE=&quot;1&quot;"/>
<Add option="-D_DEBUG"/>
<Add option="-DPOCO_ENABLE_CPP14"/>
<Add option="-DPOCO_ENABLE_CPP11"/>
<Add option="-DPOCO_OS_FAMILY_UNIX"/>
<Add option="-DPOCO_HAVE_IPv6"/>
<Add option="-DPOCO_NO_STAT64"/>
<Add option="-DXML_DTD"/>
<Add option="-DTHREADSAFE"/>
<Add option="-DNO_TCL"/>
<Add option="-DBOOST_ALL_NO_LIB"/>
<Add directory="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src"/>
<Add directory="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/include/kafka"/>
<Add directory="/usr/local/opt/mysql-client/include"/>
<Add directory="/usr/local/include"/>
<Add directory="/usr/local/opt/openssl/include"/>
<Add directory="/usr/local/opt/mysql-client/include/mysql"/>
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1"/>
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.5/include"/>
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"/>
<Add directory="/System/Library/Frameworks"/>
<Add directory="/Library/Frameworks"/>
</Compiler>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 ucentralgw"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
<Target title="ucentralgw/fast">
<Option output="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/ucentralgw" prefix_auto="0" extension_auto="0"/>
<Option working_dir="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug"/>
<Option object_output="./"/>
<Option type="1"/>
<Option compiler="gcc"/>
<Compiler>
<Add option="-DAPP_VERSION=&quot;0.7.0&quot;"/>
<Add option="-DBUILD_NUMBER=&quot;120&quot;"/>
<Add option="-DTIP_GATEWAY_SERVICE=&quot;1&quot;"/>
<Add option="-D_DEBUG"/>
<Add option="-DPOCO_ENABLE_CPP14"/>
<Add option="-DPOCO_ENABLE_CPP11"/>
<Add option="-DPOCO_OS_FAMILY_UNIX"/>
<Add option="-DPOCO_HAVE_IPv6"/>
<Add option="-DPOCO_NO_STAT64"/>
<Add option="-DXML_DTD"/>
<Add option="-DTHREADSAFE"/>
<Add option="-DNO_TCL"/>
<Add option="-DBOOST_ALL_NO_LIB"/>
<Add directory="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src"/>
<Add directory="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/include/kafka"/>
<Add directory="/usr/local/opt/mysql-client/include"/>
<Add directory="/usr/local/include"/>
<Add directory="/usr/local/opt/openssl/include"/>
<Add directory="/usr/local/opt/mysql-client/include/mysql"/>
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1"/>
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.5/include"/>
<Add directory="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include"/>
<Add directory="/System/Library/Frameworks"/>
<Add directory="/Library/Frameworks"/>
</Compiler>
<MakeCommands>
<Build command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 ucentralgw/fast"/>
<CompileFile command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 &quot;$file&quot;"/>
<Clean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
<DistClean command="/usr/local/bin/gmake -j16 -f &quot;/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/Makefile&quot; VERBOSE=1 clean"/>
</MakeCommands>
</Target>
</Build>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/build">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/ALBHealthCheckServer.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/AuthClient.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/AuthClient.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/CentralConfig.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/CentralConfig.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/CommandChannel.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/CommandChannel.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/CommandManager.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/CommandManager.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/Daemon.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/Daemon.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/DeviceRegistry.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/DeviceRegistry.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/FileUploader.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/FileUploader.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/KafkaManager.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/KafkaManager.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/Kafka_topics.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/MicroService.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/MicroService.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/OUIServer.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/OUIServer.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/OpenAPIRequest.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/OpenAPIRequest.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_BlackList.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_BlackList.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_GWobjects.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_GWobjects.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_InternalServer.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_InternalServer.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_RPC.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_RPC.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_SecurityObjects.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_SecurityObjects.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_command.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_command.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_commands.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_commands.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_default_configuration.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_default_configuration.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_default_configurations.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_default_configurations.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_device_commandHandler.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_device_commandHandler.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_device_handler.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_device_handler.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_devices_handler.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_devices_handler.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_file.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_file.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_handler.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_handler.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_ouis.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_ouis.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_protocol.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_server.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_server.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_system_command.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_system_command.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_utils.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/RESTAPI_utils.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/StateProcessor.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/StateProcessor.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/StorageArchiver.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/StorageArchiver.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/StorageService.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/StorageService.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/SubSystemServer.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/SubSystemServer.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/Utils.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/Utils.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/WebSocketServer.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/WebSocketServer.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_blacklist.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_capabilities.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_command.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_defconfig.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_device.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_healthcheck.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_lifetime_stats.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_logs.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_mysql.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_pgql.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_sqlite.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_statistics.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/storage_tables.cpp">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/uCentralProtocol.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/src/uCentralTypes.h">
<Option target="ucentralgw"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/CMakeLists.txt">
<Option virtualFolder="CMake Files\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/BoostDetectToolset-1.75.0.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Boost-1.75.0/BoostConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Boost-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Boost-1.75.0/BoostConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Boost-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_headers-1.75.0/boost_headers-config-version.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_headers-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_headers-1.75.0/boost_headers-config.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_headers-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_system-1.75.0/boost_system-config-version.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_system-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_system-1.75.0/boost_system-config.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_system-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_system-1.75.0/libboost_system-variant-mt-shared.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_system-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_system-1.75.0/libboost_system-variant-mt-static.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_system-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_system-1.75.0/libboost_system-variant-shared.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_system-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/boost_system-1.75.0/libboost_system-variant-static.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\boost_system-1.75.0\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/CppKafka/CppKafkaConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\CppKafka\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/CppKafka/CppKafkaConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\CppKafka\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/CppKafka/CppKafkaTargets-noconfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\CppKafka\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/CppKafka/CppKafkaTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\CppKafka\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoCryptoConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoCryptoConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoCryptoTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoCryptoTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataMySQLConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataMySQLConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataMySQLTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataMySQLTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataPostgreSQLConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataPostgreSQLConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataPostgreSQLTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataPostgreSQLTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataSQLiteConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataSQLiteConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataSQLiteTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataSQLiteTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoDataTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoFoundationConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoFoundationConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoFoundationTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoFoundationTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJSONConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJSONConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJSONTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJSONTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJWTConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJWTConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJWTTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoJWTTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetSSLConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetSSLConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetSSLTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetSSLTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoNetTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoUtilConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoUtilConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoUtilTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoUtilTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoXMLConfig.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoXMLConfigVersion.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoXMLTargets-relwithdebinfo.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/../../../../../../usr/local/lib/cmake/Poco/PocoXMLTargets.cmake">
<Option virtualFolder="CMake Files\..\..\..\..\..\..\usr\local\lib\cmake\Poco\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake/FindMySQL.cmake">
<Option virtualFolder="CMake Files\cmake\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake/FindPostgreSQL.cmake">
<Option virtualFolder="CMake Files\cmake\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake/FindRdKafka.cmake">
<Option virtualFolder="CMake Files\cmake\"/>
</Unit>
<Unit filename="/Users/stephb/Desktop/Dropbox/clion/wlan-cloud-ucentralgw/cmake-build-debug/rdkafka_version_test.cpp">
<Option virtualFolder="CMake Files\cmake-build-debug\"/>
</Unit>
</Project>
</CodeBlocks_project_file>

26
config.yaml Normal file
View File

@@ -0,0 +1,26 @@
tip:
port: 6051
server: ssc.wlan.local
username: support@example.com
password: support
certificates:
keyfile: keyfile.pem
certfile: certfile.pem
password: mypassword
ucentral:
port: 1991
listeners: 100
logger:
size: 10
days: 31
ui:
port: 9771
username: support@example.com
password: support

View File

@@ -1,106 +1,75 @@
#!/bin/bash
#!/bin/sh
set -e
if [ "$SELFSIGNED_CERTS" = 'true' ]; then
update-ca-certificates
fi
if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
WEBSOCKET_HOST_ROOTCA=${WEBSOCKET_HOST_ROOTCA:-"\${APP_ROOT}/certs/root.pem"} \
WEBSOCKET_HOST_ISSUER=${WEBSOCKET_HOST_ISSUER:-"\${APP_ROOT}/certs/issuer.pem"} \
WEBSOCKET_HOST_CERT=${WEBSOCKET_HOST_CERT:-"\${APP_ROOT}/certs/websocket-cert.pem"} \
WEBSOCKET_HOST_KEY=${WEBSOCKET_HOST_KEY:-"\${APP_ROOT}/certs/websocket-key.pem"} \
WEBSOCKET_HOST_CLIENTCAS=${WEBSOCKET_HOST_CLIENTCAS:-"\${APP_ROOT}/certs/clientcas.pem"} \
WEBSOCKET_HOST_CAS=${WEBSOCKET_HOST_CAS:-"\${APP_ROOT}/certs/cas"} \
WEBSOCKET_HOST_PORT=${WEBSOCKET_HOST_PORT:-"15002"} \
WEBSOCKET_HOST_KEY_PASSWORD=${WEBSOCKET_HOST_KEY_PASSWORD:-"mypassword"} \
RESTAPI_HOST_ROOTCA=${RESTAPI_HOST_ROOTCA:-"\${APP_ROOT}/certs/restapi-ca.pem"} \
RESTAPI_HOST_PORT=${RESTAPI_HOST_PORT:-"16002"} \
RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\${APP_ROOT}/certs/restapi-cert.pem"} \
RESTAPI_HOST_KEY=${RESTAPI_HOST_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
RESTAPI_HOST_KEY_PASSWORD=${RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \
INTERNAL_RESTAPI_HOST_ROOTCA=${INTERNAL_RESTAPI_HOST_ROOTCA:-"\${APP_ROOT}/certs/restapi-ca.pem"} \
INTERNAL_RESTAPI_HOST_PORT=${INTERNAL_RESTAPI_HOST_PORT:-"17002"} \
INTERNAL_RESTAPI_HOST_CERT=${INTERNAL_RESTAPI_HOST_CERT:-"\${APP_ROOT}/certs/restapi-cert.pem"} \
INTERNAL_RESTAPI_HOST_KEY=${INTERNAL_RESTAPI_HOST_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
INTERNAL_RESTAPI_HOST_KEY_PASSWORD=${INTERNAL_RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \
FILEUPLOADER_HOST_ROOTCA=${FILEUPLOADER_HOST_ROOTCA:-"\${APP_ROOT}/certs/restapi-ca.pem"} \
FILEUPLOADER_HOST_NAME=${FILEUPLOADER_HOST_NAME:-"localhost"} \
FILEUPLOADER_HOST_PORT=${FILEUPLOADER_HOST_PORT:-"16003"} \
FILEUPLOADER_HOST_CERT=${FILEUPLOADER_HOST_CERT:-"\${APP_ROOT}/certs/restapi-cert.pem"} \
FILEUPLOADER_HOST_KEY=${FILEUPLOADER_HOST_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
FILEUPLOADER_HOST_KEY_PASSWORD=${FILEUPLOADER_HOST_KEY_PASSWORD:-"mypassword"} \
FILEUPLOADER_PATH=${FILEUPLOADER_PATH:-"\${APP_ROOT}/uploads"} \
FILEUPLOADER_URI=${FILEUPLOADER_URI:-"https://localhost:16003"} \
FILEUPLOADER_MAXSIZE=${FILEUPLOADER_MAXSIZE:-"10000"} \
SERVICE_KEY=${SERVICE_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
SERVICE_KEY_PASSWORD=${SERVICE_KEY_PASSWORD:-"mypassword"} \
SYSTEM_DATA=${SYSTEM_DATA:-"\${APP_ROOT}/data"} \
SYSTEM_URI_PRIVATE=${SYSTEM_URI_PRIVATE:-"https://localhost:17002"} \
SYSTEM_URI_PUBLIC=${SYSTEM_URI_PUBLIC:-"https://localhost:16002"} \
SYSTEM_URI_UI=${SYSTEM_URI_UI:-"http://localhost"} \
SECURITY_RESTAPI_DISABLE=${SECURITY_RESTAPI_DISABLE:-"false"} \
SIMULATORID=${SIMULATORID:-""} \
IPTOCOUNTRY_PROVIDER=${IPTOCOUNTRY_PROVIDER:-"ipinfo"} \
IPTOCOUNTRY_IPINFO_TOKEN=${IPTOCOUNTRY_IPINFO_TOKEN:-""} \
IPTOCOUNTRY_IPDATA_APIKEY=${IPTOCOUNTRY_IPDATA_APIKEY:-""} \
AUTOPROVISIONING_PROCESS=${AUTOPROVISIONING_PROCESS:-"prov,default"} \
RTTY_INTERNAL=${RTTY_INTERNAL:-"true"} \
RTTY_ENABLED=${RTTY_ENABLED:-"true"} \
RTTY_SERVER=${RTTY_SERVER:-"localhost"} \
RTTY_PORT=${RTTY_PORT:-"5912"} \
RTTY_TOKEN=${RTTY_TOKEN:-""} \
RTTY_TIMEOUT=${RTTY_TIMEOUT:-"60"} \
RTTY_VIEWPORT=${RTTY_VIEWPORT:-"5913"} \
RTTY_ASSETS=${RTTY_ASSETS:-"\${APP_ROOT}/rtty_ui"} \
RADIUS_PROXY_ENABLE=${RADIUS_PROXY_ENABLE:-"false"} \
RADIUS_PROXY_ACCOUNTING_PORT=${RADIUS_PROXY_ACCOUNTING_PORT:-"1813"} \
RADIUS_PROXY_AUTHENTICATION_PORT=${RADIUS_PROXY_AUTHENTICATION_PORT:-"1812"} \
RADIUS_PROXY_COA_PORT=${RADIUS_PROXY_COA_PORT:-"3799"} \
KAFKA_ENABLE=${KAFKA_ENABLE:-"true"} \
KAFKA_BROKERLIST=${KAFKA_BROKERLIST:-"localhost:9092"} \
KAFKA_SSL_CA_LOCATION=${KAFKA_SSL_CA_LOCATION:-""} \
KAFKA_SSL_CERTIFICATE_LOCATION=${KAFKA_SSL_CERTIFICATE_LOCATION:-""} \
KAFKA_SSL_KEY_LOCATION=${KAFKA_SSL_KEY_LOCATION:-""} \
KAFKA_SSL_KEY_PASSWORD=${KAFKA_SSL_KEY_PASSWORD:-""} \
STORAGE_TYPE=${STORAGE_TYPE:-"sqlite"} \
STORAGE_TYPE_POSTGRESQL_HOST=${STORAGE_TYPE_POSTGRESQL_HOST:-"localhost"} \
STORAGE_TYPE_POSTGRESQL_USERNAME=${STORAGE_TYPE_POSTGRESQL_USERNAME:-"${APP_USER}"} \
STORAGE_TYPE_POSTGRESQL_PASSWORD=${STORAGE_TYPE_POSTGRESQL_PASSWORD:-"${APP_USER}"} \
STORAGE_TYPE_POSTGRESQL_DATABASE=${STORAGE_TYPE_POSTGRESQL_DATABASE:-"${APP_NAME}"} \
STORAGE_TYPE_POSTGRESQL_PORT=${STORAGE_TYPE_POSTGRESQL_PORT:-"5432"} \
STORAGE_TYPE_MYSQL_HOST=${STORAGE_TYPE_MYSQL_HOST:-"localhost"} \
STORAGE_TYPE_MYSQL_USERNAME=${STORAGE_TYPE_MYSQL_USERNAME:-"${APP_USER}"} \
STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"${APP_USER}"} \
STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"${APP_NAME}"} \
STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \
CERTIFICATES_ALLOWMISMATCH=${CERTIFICATES_ALLOWMISMATCH:-"false"} \
IPINFO_DEFAULT_COUNTRY=${IPINFO_DEFAULT_COUNTRY:-"US"} \
DEVICE_SESSION_TIMEOUT=${DEVICE_SESSION_TIMEOUT:-"600"} \
LOGGING_LEVEL=${LOGGING_LEVEL:-"information"} \
envsubst < /"${APP_NAME}".properties.tmpl > "${APP_CONFIG}"/"${APP_NAME}".properties
if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWGW_CONFIG"/owgw.properties ]]; then
WEBSOCKET_HOST_ROOTCA=${WEBSOCKET_HOST_ROOTCA:-"\$OWGW_ROOT/certs/root.pem"} \
WEBSOCKET_HOST_ISSUER=${WEBSOCKET_HOST_ISSUER:-"\$OWGW_ROOT/certs/issuer.pem"} \
WEBSOCKET_HOST_CERT=${WEBSOCKET_HOST_CERT:-"\$OWGW_ROOT/certs/websocket-cert.pem"} \
WEBSOCKET_HOST_KEY=${WEBSOCKET_HOST_KEY:-"\$OWGW_ROOT/certs/websocket-key.pem"} \
WEBSOCKET_HOST_CLIENTCAS=${WEBSOCKET_HOST_CLIENTCAS:-"\$OWGW_ROOT/certs/clientcas.pem"} \
WEBSOCKET_HOST_CAS=${WEBSOCKET_HOST_CAS:-"\$OWGW_ROOT/certs/cas"} \
WEBSOCKET_HOST_PORT=${WEBSOCKET_HOST_PORT:-"15002"} \
WEBSOCKET_HOST_KEY_PASSWORD=${WEBSOCKET_HOST_KEY_PASSWORD:-"mypassword"} \
RESTAPI_HOST_ROOTCA=${RESTAPI_HOST_ROOTCA:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
RESTAPI_HOST_PORT=${RESTAPI_HOST_PORT:-"16002"} \
RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
RESTAPI_HOST_KEY=${RESTAPI_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
RESTAPI_HOST_KEY_PASSWORD=${RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \
INTERNAL_RESTAPI_HOST_ROOTCA=${INTERNAL_RESTAPI_HOST_ROOTCA:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
INTERNAL_RESTAPI_HOST_PORT=${INTERNAL_RESTAPI_HOST_PORT:-"17002"} \
INTERNAL_RESTAPI_HOST_CERT=${INTERNAL_RESTAPI_HOST_CERT:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
INTERNAL_RESTAPI_HOST_KEY=${INTERNAL_RESTAPI_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
INTERNAL_RESTAPI_HOST_KEY_PASSWORD=${INTERNAL_RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \
FILEUPLOADER_HOST_ROOTCA=${FILEUPLOADER_HOST_ROOTCA:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
FILEUPLOADER_HOST_NAME=${FILEUPLOADER_HOST_NAME:-"localhost"} \
FILEUPLOADER_HOST_PORT=${FILEUPLOADER_HOST_PORT:-"16003"} \
FILEUPLOADER_HOST_CERT=${FILEUPLOADER_HOST_CERT:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
FILEUPLOADER_HOST_KEY=${FILEUPLOADER_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
FILEUPLOADER_HOST_KEY_PASSWORD=${FILEUPLOADER_HOST_KEY_PASSWORD:-"mypassword"} \
FILEUPLOADER_PATH=${FILEUPLOADER_PATH:-"\$OWGW_ROOT/uploads"} \
FILEUPLOADER_URI=${FILEUPLOADER_URI:-"https://localhost:16003"} \
SERVICE_KEY=${SERVICE_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
SERVICE_KEY_PASSWORD=${SERVICE_KEY_PASSWORD:-"mypassword"} \
SYSTEM_DATA=${SYSTEM_DATA:-"\$OWGW_ROOT/data"} \
SYSTEM_URI_PRIVATE=${SYSTEM_URI_PRIVATE:-"https://localhost:17002"} \
SYSTEM_URI_PUBLIC=${SYSTEM_URI_PUBLIC:-"https://localhost:16002"} \
SYSTEM_URI_UI=${SYSTEM_URI_UI:-"http://localhost"} \
SIMULATORID=${SIMULATORID:-""} \
IPTOCOUNTRY_PROVIDER=${IPTOCOUNTRY_PROVIDER:-"ipinfo"} \
IPTOCOUNTRY_IPINFO_TOKEN=${IPTOCOUNTRY_IPINFO_TOKEN:-""} \
IPTOCOUNTRY_IPDATA_APIKEY=${IPTOCOUNTRY_IPDATA_APIKEY:-""} \
AUTOPROVISIONING_PROCESS=${AUTOPROVISIONING_PROCESS:-"prov,default"} \
RTTY_ENABLED=${RTTY_ENABLED:-"false"} \
RTTY_SERVER=${RTTY_SERVER:-"localhost"} \
RTTY_PORT=${RTTY_PORT:-"5912"} \
RTTY_TOKEN=${RTTY_TOKEN:-"96181c567b4d0d98c50f127230068fa8"} \
RTTY_TIMEOUT=${RTTY_TIMEOUT:-"60"} \
RTTY_VIEWPORT=${RTTY_VIEWPORT:-"5913"} \
KAFKA_ENABLE=${KAFKA_ENABLE:-"true"} \
KAFKA_BROKERLIST=${KAFKA_BROKERLIST:-"localhost:9092"} \
STORAGE_TYPE=${STORAGE_TYPE:-"sqlite"} \
STORAGE_TYPE_POSTGRESQL_HOST=${STORAGE_TYPE_POSTGRESQL_HOST:-"localhost"} \
STORAGE_TYPE_POSTGRESQL_USERNAME=${STORAGE_TYPE_POSTGRESQL_USERNAME:-"owgw"} \
STORAGE_TYPE_POSTGRESQL_PASSWORD=${STORAGE_TYPE_POSTGRESQL_PASSWORD:-"owgw"} \
STORAGE_TYPE_POSTGRESQL_DATABASE=${STORAGE_TYPE_POSTGRESQL_DATABASE:-"owgw"} \
STORAGE_TYPE_POSTGRESQL_PORT=${STORAGE_TYPE_POSTGRESQL_PORT:-"5432"} \
STORAGE_TYPE_MYSQL_HOST=${STORAGE_TYPE_MYSQL_HOST:-"localhost"} \
STORAGE_TYPE_MYSQL_USERNAME=${STORAGE_TYPE_MYSQL_USERNAME:-"owgw"} \
STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owgw"} \
STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owgw"} \
STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \
envsubst < /owgw.properties.tmpl > $OWGW_CONFIG/owgw.properties
fi
# Check if rtty_ui directory exists
export RTTY_ASSETS=$(grep 'rtty.assets' "${APP_CONFIG}"/"${APP_NAME}".properties | awk -F '=' '{print $2}' | xargs | envsubst)
if [ -z "$RTTY_ASSETS" ]; then
export RTTY_ASSETS="${APP_ROOT}/rtty_ui"
fi
if [[ ! -d "$(dirname $RTTY_ASSETS)" ]]; then
mkdir -p "$(dirname $RTTY_ASSETS)"
fi
if [[ ! -d "$RTTY_ASSETS" ]]; then
cp -r /dist/rtty_ui $RTTY_ASSETS
fi
if [ "$1" = "${APP_HOME_DIR}/${APP_NAME}" -a "$(id -u)" = '0' ]; then
if [ "$1" = '/openwifi/owgw' -a "$(id -u)" = '0' ]; then
if [ "$RUN_CHOWN" = 'true' ]; then
chown -R "$APP_USER": "${APP_ROOT}" "$APP_CONFIG"
chown -R "$OWGW_USER": "$OWGW_ROOT" "$OWGW_CONFIG"
fi
exec gosu "$APP_USER" "$@"
exec su-exec "$OWGW_USER" "$@"
fi
exec "$@"

14
docker_build.sh Executable file
View File

@@ -0,0 +1,14 @@
USERNAME=arilia
HUBNAME=tip-tip-wlan-cloud-ucentral.jfrog.io
IMAGE_NAME=ucentralgw
echo "Removing docker images before build..."
docker rmi -f $(docker images -a -q)
echo "Building $IMAGE_NAME image..."
docker build --no-cache --tag $IMAGE_NAME .
IMAGE_ID=`docker images -q $IMAGE_NAME`
docker login --username=$USERNAME $HUBNAME
docker tag $IMAGE_ID $HUBNAME/$IMAGE_NAME:latest
echo "Updating $HUBNAME with the latest $IMAGE_NAME image..."
docker push $HUBNAME/$IMAGE_NAME
docker logout $HUBNAME

5
docker_clean.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
# Removes all local images. This is dangerous but good when debugging
docker rmi -f $(docker images -a -q)

43
docker_run.sh Normal file
View File

@@ -0,0 +1,43 @@
#!/bin/sh
HUBNAME=tip-tip-wlan-cloud-ucentral.jfrog.io
IMAGE_NAME=ucentralgw
DOCKER_NAME=$HUBNAME/$IMAGE_NAME:master
CONTAINER_NAME=ucentralgw
#stop previously running images
docker container stop $CONTAINER_NAME
docker container rm $CONTAINER_NAME --force
if [[ ! -d logs ]]
then
mkdir logs
fi
if [[ ! -d uploads ]]
then
mkdir uploads
fi
if [[ ! -d certs ]]
then
echo "certs directory does not exist. Please create and add the proper certificates."
exit 1
fi
if [[ ! -f owgw.properties ]]
then
echo "Configuration file ucentral.properties is missing in the current directory"
exit 2
fi
docker run -d -p 15002:15002 \
-p 16001:16001 \
-p 16003:16003 \
--init \
--volume="$PWD:/ucentral-data" \
-e UCENTRALGW_ROOT="/ucentral-data" \
-e UCENTRALGW_CONFIG="/ucentral-data" \
--name="ucentralgw" $DOCKER_NAME

View File

@@ -79,8 +79,7 @@ The following table lists the configurable parameters of the chart and their def
| persistence.size | string | Defines PV size | `'10Gi'` |
| public_env_variables | hash | Defines list of environment variables to be passed to the Gateway | |
| configProperties | hash | Configuration properties that should be passed to the application in `owgw.properties`. May be passed by key in set (i.e. `configProperties."rtty\.token"`) | |
| existingCertsSecret | string | Existing Kubernetes secret containing all required certificates and private keys for microservice operation. If set, certificates from `certs` key are ignored | `""` |
| certs | hash | Defines files (keys and certificates) that should be passed to the Gateway (PEM format is adviced to be used) (see `volumes.owgw` on where it is mounted). If `existingCertsSecret` is set, certificates passed this way will not be used. | |
| certs | hash | Defines files (keys and certificates) that should be passed to the Gateway (PEM format is adviced to be used) (see `volumes.owgw` on where it is mounted) | |
| certsCAs | hash | Defines files with CAs that should be passed to the Gateway (see `volumes.owgw` on where it is mounted) | |

View File

@@ -1,5 +1,4 @@
{{- $root := . -}}
{{- $storageType := index .Values.configProperties "storage.type" -}}
---
apiVersion: apps/v1
kind: Deployment
@@ -49,38 +48,6 @@ spec:
- tcp://{{ index .Values.configProperties "openwifi.kafka.brokerlist" }}
- -timeout
- 600s
{{- if eq $storageType "postgresql" }}
- name: wait-postgres
image: "{{ .Values.images.owgw.repository }}:{{ .Values.images.owgw.tag }}"
imagePullPolicy: {{ .Values.images.owgw.pullPolicy }}
command:
- /wait-for-postgres.sh
- {{ index .Values.configProperties "storage.type.postgresql.host" }}
- echo
- "PostgreSQL is ready"
env:
- name: KUBERNETES_DEPLOYED
value: "{{ now }}"
{{- range $key, $value := .Values.public_env_variables }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.secret_env_variables }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ include "owgw.fullname" $root }}-env
key: {{ $key }}
{{- end }}
volumeMounts:
{{- range .Values.volumes.owgw }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
{{- if .subPath }}
subPath: {{ .subPath }}
{{- end }}
{{- end }}
{{- end }}
containers:
@@ -109,11 +76,6 @@ spec:
containerPort: {{ $portValue.targetPort }}
protocol: {{ $portValue.protocol }}
{{- end }}
{{- range $port, $portValue := .Values.services.radius.ports }}
- name: {{ $port }}
containerPort: {{ $portValue.targetPort }}
protocol: {{ $portValue.protocol }}
{{- end }}
volumeMounts:
{{- range .Values.volumes.owgw }}

View File

@@ -9,7 +9,7 @@ fullnameOverride: ""
images:
owgw:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw
tag: master
tag: v2.5.3
pullPolicy: Always
# regcred:
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io
@@ -40,28 +40,6 @@ services:
servicePort: 16003
targetPort: 16003
protocol: TCP
rttys:
servicePort: 5912
targetPort: 5912
protocol: TCP
rttys-view:
servicePort: 5913
targetPort: 5913
radius:
type: ClusterIP
ports:
acc:
servicePort: 1813
targetPort: 1813
protocol: UDP
auth:
servicePort: 1812
targetPort: 1812
protocol: UDP
coa:
servicePort: 3799
targetPort: 3799
protocol: UDP
checks:
owgw:
@@ -113,7 +91,7 @@ volumes:
mountPath: /owgw-data/certs
volumeDefinition: |
secret:
secretName: {{ if .Values.existingCertsSecret }}{{ .Values.existingCertsSecret }}{{ else }}{{ include "owgw.fullname" . }}-certs{{ end }}
secretName: {{ include "owgw.fullname" . }}-certs
- name: certs-cas
mountPath: /owgw-data/certs/cas
volumeDefinition: |
@@ -139,7 +117,7 @@ resources: {}
# memory: 128Mi
securityContext:
fsGroup: 1000
fsGroup: 101
# Usage of unsafe sysctls requires multiple things:
# - allow these unsafe sysctls on kubelet level (by adding --allowed-unsafe-sysctls flag)
# - enabling addition of PodSecurityContext setting podSecurityPolicy.enabled to "true" below
@@ -231,28 +209,19 @@ configProperties:
openwifi.devicetypes.0: AP:linksys_ea8300,edgecore_eap101,linksys_e8450-ubi
openwifi.devicetypes.1: SWITCH:edgecore_ecs4100-12ph
openwifi.devicetypes.2: IOT:esp32
openwifi.certificates.allowmismatch: "false"
oui.download.uri: https://standards-oui.ieee.org/oui/oui.txt
oui.download.uri: https://linuxnet.ca/ieee/oui.txt
firmware.autoupdate.policy.default: auto
iptocountry.provider: ipinfo
# Callback
openwifi.callback.enable: "false"
openwifi.callback.0.local: localhost:16001
openwifi.callback.0.remote: localhost:15055
openwifi.callback.0.topics: owfws
# rtty
rtty.internal: "true"
rtty.enabled: "true"
rtty.server: localhost
rtty.port: 5912
rtty.timeout: 60
rtty.viewport: 5913
rtty.assets: $OWGW_ROOT/rtty_ui
# RADIUS proxy
radius.proxy.enable: "true"
radius.proxy.accounting.port: 1813
radius.proxy.authentication.port: 1812
radius.proxy.coa.port: 3799
# ALB
alb.enable: "true"
alb.port: 16102
@@ -263,10 +232,6 @@ configProperties:
openwifi.kafka.brokerlist: localhost:9092
openwifi.kafka.auto.commit: false
openwifi.kafka.queue.buffering.max.ms: 50
openwifi.kafka.ssl.ca.location: ""
openwifi.kafka.ssl.certificate.location: ""
openwifi.kafka.ssl.key.location: ""
openwifi.kafka.ssl.key.password: ""
# Storage
storage.type: sqlite # (sqlite|postgresql|mysql|odbc)
## SQLite
@@ -293,7 +258,6 @@ configProperties:
openwifi.system.debug: "true"
openwifi.system.uri.private: https://localhost:17002
openwifi.system.uri.public: https://localhost:16002
openwifi.system.uri.ui: https://localhost
openwifi.system.commandchannel: /tmp/app_owgw
# Logging
logging.type: console
@@ -333,22 +297,166 @@ configProperties:
storage.type.mysql.username: stephb
storage.type.mysql.password: snoopy99
# NOTE: List of required certificates may be found in "certs" key. Alternative way to pass required certificates is to create external secret with all required certificates and set secret name in "existingCertsSecret" key. Details may be found in https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/chart#tldr
existingCertsSecret: ""
certs:
clientcas.pem: ""
issuer.pem: ""
restapi-ca.pem: ""
restapi-cert.pem: ""
restapi-key.pem: ""
root.pem: ""
websocket-cert.pem: ""
websocket-key.pem: ""
clientcas.pem: |
-----BEGIN CERTIFICATE-----
MIIEnDCCA4SgAwIBAgIUVpyCUx1MUeUwxg+7I1BvGFTz7HkwDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjUxMjZaFw0yNjA0MTMyMjM4NDZaMGwx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEpMCcGA1UEAxMgVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IElzc3VpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtKBrq
qd2aKVSk25KfL5xHu8X7/8rJrz3IvyPuVKWhk/N1zabot3suBcGaYNKjnRHxg78R
yKwKzajKYWtiQFqztu24g16LQeAnoUxZnF6a0z3JkkRPsz14A2y8TUhdEe1tx+UU
4VGsk3n+FMmOQHL+79FO57zQC1LwylgfLSltrI6mF3jowVUQvnwzKhUzT87AJ6EO
ndK/q0T/Bgi+aI39zfVOjJjsTJwghvrmYW3iarP1THSKxeib2s02bZKrvvHa5HL4
UI8+LvREpVZl4mzt1z6Nl344Y6f+UeJlYa/Ci0jJqaXJmyVnUbAz+c0i5JfwAVn3
YQzfC4eLnZCmdF8zAgMBAAGjggE3MIIBMzAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBSzG1S44EerPfM4gOQ85f0AYW3R6DAfBgNVHSMEGDAWgBQCRpZgebFT9qny
98WfIUDk6ZEB+jAOBgNVHQ8BAf8EBAMCAYYwgYMGCCsGAQUFBwEBBHcwdTAoBggr
BgEFBQcwAYYcaHR0cDovL29jc3Aub25lLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcw
AoY9aHR0cDovL2NhY2VydHMub25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQ
cm9qZWN0Um9vdENBLmNydDBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLm9u
ZS5kaWdpY2VydC5jb20vVGVsZWNvbUluZnJhUHJvamVjdFJvb3RDQS5jcmwwDQYJ
KoZIhvcNAQELBQADggEBAFbz+K94bHIkBMJqps0dApniUmOn0pO6Q6cGh47UP/kX
IiPIsnYgG+hqYD/qtsiqJhaWi0hixRWn38UmvZxMRk27aSTGE/TWx0JTC3qDGsSe
XkUagumbSfmS0ZyiTwMPeGAjXwyzGorqZWeA95eKfImntMiOf3E7//GK0K7HpCx8
IPCnLZsZD2q/mLyBsduImFIRQJbLAhwIxpcd1qYJk+BlGFL+HtBpEbq6JxW2Xy+v
DpNWc2WIsUTle0rTc9JNJrLX4ChUJmKqf8obKHap3Xh3//qw/jDB9pOAinA33FLJ
EmCnwBvQr9mfNmPBGMYZVU8cPruDQJ57GjmmvdisbJY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIUPVYBpqNbcLYygF6Mx+qxSWwQyFowDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjQyNDRaFw0zMTA0MTMyMjM4NDZaMGkx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEmMCQGA1UEAxMdVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGCibwf5u
AAwZ+1H8U0e3u2V+0d2gSctucoK86XwUmfe1V2a/qlCYZd29r80IuN1IIeB0naIm
KnK/MzXW87clF6tFd1+HzEvmlY/W4KyIXalVCTEzirFSvBEG2oZpM0yC3AefytAO
aOpA00LaM3xTfTqMKIRhJBuLy0I4ANUVG6ixVebbGuc78IodleqiLoWy2Q9QHyEO
t/7hZndJhiVogh0PveRhho45EbsACu7ymDY+JhlIleevqwlE3iQoq0YcmYADHno6
Eq8vcwLpZFxihupUafkd1T3WJYQAJf9coCjBu2qIhNgrcrGD8R9fGswwNRzMRMpX
720+GjcDW3bJAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAJG
lmB5sVP2qfL3xZ8hQOTpkQH6MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
AAOCAQEAVjl9dm4epG9NUYnagT9sg7scVQEPfz3Lt6w1NXJXgD8mAUlK0jXmEyvM
dCPD4514n+8+lM7US8fh+nxc7jO//LwK17Wm9FblgjNFR7+anv0Q99T9fP19DLlF
PSNHL2emogy1bl1lLTAoj8nxg2wVKPDSHBGviQ5LR9fsWUIJDv9Bs5k0qWugWYSj
19S6qnHeskRDB8MqRLhKMG82oDVLerSnhD0P6HjySBHgTTU7/tYS/OZr1jI6MPbG
L+/DtiR5fDVMNdBSGU89UNTi0wHY9+RFuNlIuvZC+x/swF0V9R5mN+ywquTPtDLA
5IOM7ItsRmen6u3qu+JXros54e4juQ==
-----END CERTIFICATE-----
issuer.pem: |
-----BEGIN CERTIFICATE-----
MIIEnDCCA4SgAwIBAgIUVpyCUx1MUeUwxg+7I1BvGFTz7HkwDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjUxMjZaFw0yNjA0MTMyMjM4NDZaMGwx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEpMCcGA1UEAxMgVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IElzc3VpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtKBrq
qd2aKVSk25KfL5xHu8X7/8rJrz3IvyPuVKWhk/N1zabot3suBcGaYNKjnRHxg78R
yKwKzajKYWtiQFqztu24g16LQeAnoUxZnF6a0z3JkkRPsz14A2y8TUhdEe1tx+UU
4VGsk3n+FMmOQHL+79FO57zQC1LwylgfLSltrI6mF3jowVUQvnwzKhUzT87AJ6EO
ndK/q0T/Bgi+aI39zfVOjJjsTJwghvrmYW3iarP1THSKxeib2s02bZKrvvHa5HL4
UI8+LvREpVZl4mzt1z6Nl344Y6f+UeJlYa/Ci0jJqaXJmyVnUbAz+c0i5JfwAVn3
YQzfC4eLnZCmdF8zAgMBAAGjggE3MIIBMzAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBSzG1S44EerPfM4gOQ85f0AYW3R6DAfBgNVHSMEGDAWgBQCRpZgebFT9qny
98WfIUDk6ZEB+jAOBgNVHQ8BAf8EBAMCAYYwgYMGCCsGAQUFBwEBBHcwdTAoBggr
BgEFBQcwAYYcaHR0cDovL29jc3Aub25lLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcw
AoY9aHR0cDovL2NhY2VydHMub25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQ
cm9qZWN0Um9vdENBLmNydDBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLm9u
ZS5kaWdpY2VydC5jb20vVGVsZWNvbUluZnJhUHJvamVjdFJvb3RDQS5jcmwwDQYJ
KoZIhvcNAQELBQADggEBAFbz+K94bHIkBMJqps0dApniUmOn0pO6Q6cGh47UP/kX
IiPIsnYgG+hqYD/qtsiqJhaWi0hixRWn38UmvZxMRk27aSTGE/TWx0JTC3qDGsSe
XkUagumbSfmS0ZyiTwMPeGAjXwyzGorqZWeA95eKfImntMiOf3E7//GK0K7HpCx8
IPCnLZsZD2q/mLyBsduImFIRQJbLAhwIxpcd1qYJk+BlGFL+HtBpEbq6JxW2Xy+v
DpNWc2WIsUTle0rTc9JNJrLX4ChUJmKqf8obKHap3Xh3//qw/jDB9pOAinA33FLJ
EmCnwBvQr9mfNmPBGMYZVU8cPruDQJ57GjmmvdisbJY=
-----END CERTIFICATE-----
# restapi-ca.pem: ""
# restapi-cert.pem: ""
# restapi-key.pem: ""
root.pem: |
-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIUPVYBpqNbcLYygF6Mx+qxSWwQyFowDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjQyNDRaFw0zMTA0MTMyMjM4NDZaMGkx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEmMCQGA1UEAxMdVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGCibwf5u
AAwZ+1H8U0e3u2V+0d2gSctucoK86XwUmfe1V2a/qlCYZd29r80IuN1IIeB0naIm
KnK/MzXW87clF6tFd1+HzEvmlY/W4KyIXalVCTEzirFSvBEG2oZpM0yC3AefytAO
aOpA00LaM3xTfTqMKIRhJBuLy0I4ANUVG6ixVebbGuc78IodleqiLoWy2Q9QHyEO
t/7hZndJhiVogh0PveRhho45EbsACu7ymDY+JhlIleevqwlE3iQoq0YcmYADHno6
Eq8vcwLpZFxihupUafkd1T3WJYQAJf9coCjBu2qIhNgrcrGD8R9fGswwNRzMRMpX
720+GjcDW3bJAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAJG
lmB5sVP2qfL3xZ8hQOTpkQH6MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
AAOCAQEAVjl9dm4epG9NUYnagT9sg7scVQEPfz3Lt6w1NXJXgD8mAUlK0jXmEyvM
dCPD4514n+8+lM7US8fh+nxc7jO//LwK17Wm9FblgjNFR7+anv0Q99T9fP19DLlF
PSNHL2emogy1bl1lLTAoj8nxg2wVKPDSHBGviQ5LR9fsWUIJDv9Bs5k0qWugWYSj
19S6qnHeskRDB8MqRLhKMG82oDVLerSnhD0P6HjySBHgTTU7/tYS/OZr1jI6MPbG
L+/DtiR5fDVMNdBSGU89UNTi0wHY9+RFuNlIuvZC+x/swF0V9R5mN+ywquTPtDLA
5IOM7ItsRmen6u3qu+JXros54e4juQ==
-----END CERTIFICATE-----
# websocket-cert.pem: ""
# websocket-key.pem: ""
certsCAs:
issuer.pem: ""
root.pem: ""
issuer.pem: |
-----BEGIN CERTIFICATE-----
MIIEnDCCA4SgAwIBAgIUVpyCUx1MUeUwxg+7I1BvGFTz7HkwDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjUxMjZaFw0yNjA0MTMyMjM4NDZaMGwx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEpMCcGA1UEAxMgVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IElzc3VpbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtKBrq
qd2aKVSk25KfL5xHu8X7/8rJrz3IvyPuVKWhk/N1zabot3suBcGaYNKjnRHxg78R
yKwKzajKYWtiQFqztu24g16LQeAnoUxZnF6a0z3JkkRPsz14A2y8TUhdEe1tx+UU
4VGsk3n+FMmOQHL+79FO57zQC1LwylgfLSltrI6mF3jowVUQvnwzKhUzT87AJ6EO
ndK/q0T/Bgi+aI39zfVOjJjsTJwghvrmYW3iarP1THSKxeib2s02bZKrvvHa5HL4
UI8+LvREpVZl4mzt1z6Nl344Y6f+UeJlYa/Ci0jJqaXJmyVnUbAz+c0i5JfwAVn3
YQzfC4eLnZCmdF8zAgMBAAGjggE3MIIBMzAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBSzG1S44EerPfM4gOQ85f0AYW3R6DAfBgNVHSMEGDAWgBQCRpZgebFT9qny
98WfIUDk6ZEB+jAOBgNVHQ8BAf8EBAMCAYYwgYMGCCsGAQUFBwEBBHcwdTAoBggr
BgEFBQcwAYYcaHR0cDovL29jc3Aub25lLmRpZ2ljZXJ0LmNvbTBJBggrBgEFBQcw
AoY9aHR0cDovL2NhY2VydHMub25lLmRpZ2ljZXJ0LmNvbS9UZWxlY29tSW5mcmFQ
cm9qZWN0Um9vdENBLmNydDBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vY3JsLm9u
ZS5kaWdpY2VydC5jb20vVGVsZWNvbUluZnJhUHJvamVjdFJvb3RDQS5jcmwwDQYJ
KoZIhvcNAQELBQADggEBAFbz+K94bHIkBMJqps0dApniUmOn0pO6Q6cGh47UP/kX
IiPIsnYgG+hqYD/qtsiqJhaWi0hixRWn38UmvZxMRk27aSTGE/TWx0JTC3qDGsSe
XkUagumbSfmS0ZyiTwMPeGAjXwyzGorqZWeA95eKfImntMiOf3E7//GK0K7HpCx8
IPCnLZsZD2q/mLyBsduImFIRQJbLAhwIxpcd1qYJk+BlGFL+HtBpEbq6JxW2Xy+v
DpNWc2WIsUTle0rTc9JNJrLX4ChUJmKqf8obKHap3Xh3//qw/jDB9pOAinA33FLJ
EmCnwBvQr9mfNmPBGMYZVU8cPruDQJ57GjmmvdisbJY=
-----END CERTIFICATE-----
root.pem: |
-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIUPVYBpqNbcLYygF6Mx+qxSWwQyFowDQYJKoZIhvcNAQEL
BQAwaTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG1RlbGVjb20gSW5mcmEgUHJvamVj
dCwgSW5jLjEMMAoGA1UECxMDVElQMSYwJAYDVQQDEx1UZWxlY29tIEluZnJhIFBy
b2plY3QgUm9vdCBDQTAeFw0yMTA0MTMyMjQyNDRaFw0zMTA0MTMyMjM4NDZaMGkx
CzAJBgNVBAYTAlVTMSQwIgYDVQQKExtUZWxlY29tIEluZnJhIFByb2plY3QsIElu
Yy4xDDAKBgNVBAsTA1RJUDEmMCQGA1UEAxMdVGVsZWNvbSBJbmZyYSBQcm9qZWN0
IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIGCibwf5u
AAwZ+1H8U0e3u2V+0d2gSctucoK86XwUmfe1V2a/qlCYZd29r80IuN1IIeB0naIm
KnK/MzXW87clF6tFd1+HzEvmlY/W4KyIXalVCTEzirFSvBEG2oZpM0yC3AefytAO
aOpA00LaM3xTfTqMKIRhJBuLy0I4ANUVG6ixVebbGuc78IodleqiLoWy2Q9QHyEO
t/7hZndJhiVogh0PveRhho45EbsACu7ymDY+JhlIleevqwlE3iQoq0YcmYADHno6
Eq8vcwLpZFxihupUafkd1T3WJYQAJf9coCjBu2qIhNgrcrGD8R9fGswwNRzMRMpX
720+GjcDW3bJAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFAJG
lmB5sVP2qfL3xZ8hQOTpkQH6MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
AAOCAQEAVjl9dm4epG9NUYnagT9sg7scVQEPfz3Lt6w1NXJXgD8mAUlK0jXmEyvM
dCPD4514n+8+lM7US8fh+nxc7jO//LwK17Wm9FblgjNFR7+anv0Q99T9fP19DLlF
PSNHL2emogy1bl1lLTAoj8nxg2wVKPDSHBGviQ5LR9fsWUIJDv9Bs5k0qWugWYSj
19S6qnHeskRDB8MqRLhKMG82oDVLerSnhD0P6HjySBHgTTU7/tYS/OZr1jI6MPbG
L+/DtiR5fDVMNdBSGU89UNTi0wHY9+RFuNlIuvZC+x/swF0V9R5mN+ywquTPtDLA
5IOM7ItsRmen6u3qu+JXros54e4juQ==
-----END CERTIFICATE-----
# PostgreSQL (https://github.com/bitnami/charts/tree/master/bitnami/postgresql)
postgresql:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -1,165 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 141.5 185.6" style="enable-background:new 0 0 141.5 185.6;" xml:space="preserve">
<style type="text/css">
.st0{fill:#414141;}
.st1{fill:#FFFFFF;}
.st2{fill:#FED206;}
.st3{fill:#EB6F53;}
.st4{fill:#3BA9B6;}
</style>
<g>
<g>
<path class="st0" d="M120.7,183.9H21.5c-10.8,0-19.5-8.7-19.5-19.5V20.5c0-10.8,8.7-19.5,19.5-19.5h99.2
c10.8,0,19.5,8.7,19.5,19.5v143.9C140.2,175.2,131.5,183.9,120.7,183.9z"/>
<g>
<g>
<g>
<path class="st1" d="M46.3,166.2v-3.4h-1.2v-0.6h3.1v0.6H47v3.4H46.3z"/>
</g>
<g>
<path class="st1" d="M49,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H49z"/>
</g>
<g>
<path class="st1" d="M52.6,166.2v-4h0.7v3.4h1.8v0.6H52.6z"/>
</g>
<g>
<path class="st1" d="M55.7,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H55.7z"/>
</g>
<g>
<path class="st1" d="M59.1,164.2c0-1.2,0.9-2.1,2.1-2.1c0.8,0,1.3,0.4,1.6,0.9l-0.6,0.3c-0.2-0.3-0.6-0.6-1-0.6
c-0.8,0-1.4,0.6-1.4,1.4c0,0.8,0.6,1.4,1.4,1.4c0.4,0,0.8-0.3,1-0.6l0.6,0.3c-0.3,0.5-0.8,0.9-1.6,0.9
C60,166.3,59.1,165.5,59.1,164.2z"/>
</g>
<g>
<path class="st1" d="M63.2,164.2c0-1.2,0.8-2.1,2-2.1c1.2,0,2,0.9,2,2.1c0,1.2-0.8,2.1-2,2.1C64,166.3,63.2,165.4,63.2,164.2z
M66.5,164.2c0-0.8-0.5-1.4-1.3-1.4c-0.8,0-1.3,0.6-1.3,1.4c0,0.8,0.5,1.4,1.3,1.4C66,165.7,66.5,165,66.5,164.2z"/>
</g>
<g>
<path class="st1" d="M71.3,166.2v-3.1l-1.2,3.1h-0.3l-1.2-3.1v3.1h-0.7v-4h1l1.1,2.7l1.1-2.7h1v4H71.3z"/>
</g>
<g>
<path class="st1" d="M75.7,166.2v-4h0.7v4H75.7z"/>
</g>
<g>
<path class="st1" d="M80.4,166.2l-2.1-2.8v2.8h-0.7v-4h0.7l2,2.8v-2.8h0.7v4H80.4z"/>
</g>
<g>
<path class="st1" d="M82.3,166.2v-4H85v0.6h-2v1h2v0.6h-2v1.7H82.3z"/>
</g>
<g>
<path class="st1" d="M87.9,166.2l-0.9-1.5h-0.7v1.5h-0.7v-4h1.7c0.8,0,1.3,0.5,1.3,1.2c0,0.7-0.5,1.1-0.9,1.2l1,1.6H87.9z
M88,163.5c0-0.4-0.3-0.6-0.7-0.6h-1v1.3h1C87.7,164.1,88,163.9,88,163.5z"/>
</g>
<g>
<path class="st1" d="M92.4,166.2l-0.3-0.8h-1.8l-0.3,0.8h-0.8l1.6-4h0.9l1.6,4H92.4z M91.2,162.9l-0.7,1.9h1.4L91.2,162.9z"/>
</g>
<g>
<path class="st1" d="M95.8,166.2v-4h1.5c0.8,0,1.2,0.5,1.2,1.2c0,0.6-0.4,1.2-1.2,1.2h-1.2v1.7H95.8z M98.2,163.4
c0-0.5-0.3-0.9-0.9-0.9h-1.1v1.7h1.1C97.8,164.3,98.2,163.9,98.2,163.4z"/>
</g>
<g>
<path class="st1" d="M101.5,166.2l-1.1-1.6h-0.9v1.6h-0.3v-4h1.5c0.7,0,1.2,0.4,1.2,1.2c0,0.7-0.5,1.1-1.1,1.1l1.2,1.7H101.5z
M101.6,163.4c0-0.5-0.4-0.9-0.9-0.9h-1.1v1.7h1.1C101.2,164.3,101.6,163.9,101.6,163.4z"/>
</g>
<g>
<path class="st1" d="M102.8,164.2c0-1.2,0.8-2.1,1.9-2.1c1.2,0,1.9,0.9,1.9,2.1c0,1.2-0.8,2.1-1.9,2.1
C103.6,166.3,102.8,165.4,102.8,164.2z M106.3,164.2c0-1-0.6-1.7-1.6-1.7c-1,0-1.6,0.7-1.6,1.7c0,1,0.6,1.7,1.6,1.7
C105.7,166,106.3,165.2,106.3,164.2z"/>
</g>
<g>
<path class="st1" d="M106.9,165.8l0.2-0.3c0.2,0.2,0.4,0.4,0.8,0.4c0.5,0,0.9-0.4,0.9-0.9v-2.8h0.3v2.8c0,0.8-0.5,1.2-1.2,1.2
C107.5,166.3,107.2,166.1,106.9,165.8z"/>
</g>
<g>
<path class="st1" d="M110.4,166.2v-4h2.5v0.3h-2.2v1.5h2.1v0.3h-2.1v1.6h2.2v0.3H110.4z"/>
</g>
<g>
<path class="st1" d="M113.5,164.2c0-1.2,0.9-2.1,2-2.1c0.6,0,1.1,0.3,1.5,0.7l-0.3,0.2c-0.3-0.3-0.7-0.6-1.2-0.6
c-0.9,0-1.7,0.7-1.7,1.7c0,1,0.7,1.7,1.7,1.7c0.5,0,0.9-0.2,1.2-0.6l0.3,0.2c-0.4,0.4-0.8,0.7-1.5,0.7
C114.4,166.3,113.5,165.5,113.5,164.2z"/>
</g>
<g>
<path class="st1" d="M118.7,166.2v-3.7h-1.3v-0.3h2.9v0.3H119v3.7H118.7z"/>
</g>
</g>
<g>
<polygon class="st1" points="26.3,163.8 31.6,158.5 36.9,163.8 37.7,163.8 31.6,157.6 25.5,163.8 "/>
<polygon class="st1" points="36.9,164.7 31.6,170 26.3,164.7 25.5,164.7 31.6,170.8 37.7,164.7 "/>
<polygon class="st1" points="31,163.8 36.3,158.5 41.6,163.8 42.5,163.8 36.3,157.6 30.2,163.8 "/>
<polygon class="st1" points="41.6,164.7 36.3,170 31,164.7 30.2,164.7 36.3,170.8 42.5,164.7 "/>
</g>
</g>
<g>
<path class="st1" d="M33.2,100.7c-4.6,0-8.3,3.7-8.3,8.3s3.7,8.3,8.3,8.3s8.3-3.7,8.3-8.3S37.8,100.7,33.2,100.7z"/>
</g>
<g>
<g>
<g>
<path class="st2" d="M33.2,35.2c40.7,0,73.8,33.1,73.8,73.8c0,0.7,0,1.4,0,2.1c0,1.7,0.6,3.3,1.7,4.6c1.2,1.2,2.8,1.9,4.5,2
l0.2,0c3.5,0,6.3-2.7,6.4-6.2c0-0.8,0-1.7,0-2.5c0-47.7-38.8-86.6-86.6-86.6c-0.8,0-1.7,0-2.5,0c-1.7,0-3.3,0.8-4.5,2
c-1.2,1.2-1.8,2.9-1.7,4.6c0.1,3.5,3,6.3,6.6,6.2C31.8,35.2,32.5,35.2,33.2,35.2z"/>
</g>
</g>
</g>
<g>
<g>
<g>
<path class="st3" d="M33.2,60.5c26.7,0,48.5,21.7,48.5,48.5c0,0.6,0,1.3,0,2c-0.1,1.7,0.5,3.3,1.7,4.6c1.2,1.3,2.7,2,4.4,2.1
c1.7,0.1,3.3-0.5,4.6-1.7c1.2-1.2,2-2.7,2-4.4c0-0.9,0.1-1.8,0.1-2.6c0-33.8-27.5-61.2-61.2-61.2c-0.8,0-1.6,0-2.6,0.1
c-1.7,0.1-3.3,0.8-4.4,2.1c-1.2,1.3-1.8,2.9-1.7,4.6s0.8,3.3,2.1,4.4c1.3,1.2,2.9,1.8,4.6,1.7C31.9,60.5,32.6,60.5,33.2,60.5z"
/>
</g>
</g>
</g>
<g>
<g>
<g>
<path class="st4" d="M33.2,86.7c12.3,0,22.3,10,22.3,22.3c0,0.5,0,1.1-0.1,1.8c-0.3,3.5,2.3,6.6,5.8,6.9
c3.5,0.3,6.6-2.3,6.9-5.8c0.1-1,0.1-1.9,0.1-2.8c0-19.3-15.7-35.1-35.1-35.1c-0.9,0-1.8,0-2.8,0.1c-1.7,0.1-3.2,0.9-4.3,2.2
c-1.1,1.3-1.6,2.9-1.5,4.6c0.1,1.7,0.9,3.2,2.2,4.3c1.3,1.1,2.9,1.6,4.6,1.5C32.1,86.7,32.7,86.7,33.2,86.7z"/>
</g>
</g>
</g>
</g>
<g>
<path class="st1" d="M35.8,130.4c1.1,0.6,2.1,1.5,2.7,2.6c0.7,1.1,1,2.3,1,3.7s-0.3,2.6-1,3.7c-0.7,1.1-1.6,2-2.7,2.6
c-1.1,0.6-2.4,1-3.8,1s-2.7-0.3-3.8-1c-1.1-0.6-2.1-1.5-2.7-2.6c-0.7-1.1-1-2.3-1-3.7c0-1.3,0.3-2.6,1-3.7c0.7-1.1,1.6-2,2.7-2.6
c1.1-0.6,2.4-0.9,3.8-0.9C33.4,129.5,34.7,129.8,35.8,130.4z M29.9,132.9c-0.7,0.4-1.2,0.9-1.6,1.6s-0.6,1.4-0.6,2.2
c0,0.8,0.2,1.6,0.6,2.3c0.4,0.7,0.9,1.2,1.6,1.6c0.7,0.4,1.4,0.6,2.1,0.6c0.8,0,1.5-0.2,2.1-0.6c0.6-0.4,1.2-0.9,1.5-1.6
c0.4-0.7,0.6-1.4,0.6-2.3c0-0.8-0.2-1.6-0.6-2.2s-0.9-1.2-1.5-1.6c-0.6-0.4-1.4-0.6-2.1-0.6C31.3,132.3,30.6,132.5,29.9,132.9z"/>
<path class="st1" d="M50.6,133.6c0.8,0.5,1.4,1.1,1.8,2c0.4,0.8,0.6,1.8,0.6,2.9c0,1.1-0.2,2-0.6,2.8c-0.4,0.8-1,1.5-1.8,1.9
c-0.8,0.5-1.6,0.7-2.6,0.7c-0.7,0-1.4-0.1-2-0.4s-1.1-0.7-1.5-1.2v5.4h-3.1V133h3.1v1.6c0.4-0.5,0.9-1,1.4-1.2s1.2-0.4,2-0.4
C48.9,132.9,49.8,133.1,50.6,133.6z M49.1,140.5c0.5-0.6,0.7-1.3,0.7-2.2c0-0.9-0.2-1.6-0.7-2.1c-0.5-0.6-1.1-0.8-1.9-0.8
s-1.4,0.3-1.9,0.8c-0.5,0.6-0.8,1.3-0.8,2.1c0,0.9,0.2,1.6,0.8,2.2s1.1,0.8,1.9,0.8S48.6,141,49.1,140.5z"/>
<path class="st1" d="M63.4,134.4c0.9,1,1.4,2.4,1.4,4.2c0,0.3,0,0.6,0,0.7H57c0.2,0.7,0.5,1.2,1,1.6c0.5,0.4,1.1,0.6,1.8,0.6
c0.5,0,1-0.1,1.5-0.3s0.9-0.5,1.3-0.9l1.6,1.6c-0.5,0.6-1.2,1.1-2,1.4c-0.8,0.3-1.6,0.5-2.6,0.5c-1.1,0-2.1-0.2-3-0.7
s-1.5-1.1-2-1.9c-0.5-0.8-0.7-1.8-0.7-2.9c0-1.1,0.2-2.1,0.7-2.9s1.1-1.5,2-1.9c0.8-0.5,1.8-0.7,2.9-0.7
C61.2,132.9,62.5,133.4,63.4,134.4z M61.8,137.5c0-0.7-0.3-1.3-0.7-1.7s-1-0.6-1.7-0.6c-0.7,0-1.2,0.2-1.7,0.6
c-0.4,0.4-0.7,1-0.9,1.7H61.8z"/>
<path class="st1" d="M76.2,134c0.7,0.7,1.1,1.7,1.1,3v6.8h-3.1v-5.9c0-0.7-0.2-1.2-0.6-1.6s-0.9-0.6-1.5-0.6
c-0.8,0-1.4,0.3-1.8,0.8c-0.4,0.5-0.7,1.2-0.7,2v5.3h-3.1V133h3.1v1.9c0.7-1.3,2-2,3.7-2C74.6,132.8,75.5,133.2,76.2,134z"/>
<path class="st1" d="M96,129.7h3.3l-4.7,14h-3.3l-2.9-10.1l-3,10.1h-3.2l-4.7-14h3.4l3,10.7l3-10.7H90l3.1,10.7L96,129.7z"/>
<path class="st1" d="M103.3,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C102.6,128.2,103,128.3,103.3,128.7z M100.6,133h3.1
v10.8h-3.1V133z"/>
<path class="st1" d="M106.5,129.7h10.1l0,2.6h-6.9v3.4h6.3v2.6h-6.3v5.3h-3.2V129.7z"/>
<path class="st1" d="M120.9,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C120.1,128.2,120.5,128.3,120.9,128.7z M118.1,133h3.1
v10.8h-3.1V133z"/>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
Issue: https://telecominfraproject.atlassian.net/browse/WIFI-11388
Problem:
If a configuration was accepted by the GW or Provisioning but is still not valid according to the firmware on teh device,
the device will reject the configuration, however, that configuration is known as the kast good configuration in the GW.
This mens that we will lock the device in a loop where it continuously wants to update the configuration to version X,
and the device will continuously reject it.
Workaround:
Simply send a valid configuration to the GW and this will allow the device you update and stop the cycle.
Fix:
When a new configuration is submitted, store is a "pending". If it is accepted, move it to the current configuration. If
not accepted, simply remove it. One corner case exists. For some configuration updates, the AP will never complete the
update cycle, even if it has updated the configuration. In that case, we can detect the configuration during a connect
later. At that moment, when we look for an upgrade, we must compare with the pending UUID and the current UUID. If it matches the pending,
we know the last update worked. If it does not, we know to revert.

View File

@@ -1,85 +0,0 @@
{
"ethernet": [
{
"select-ports": [
"Ethernet0",
"Ethernet1",
"Ethernet2",
"Ethernet3",
"Ethernet4",
"Ethernet5",
"Ethernet6",
"Ethernet7"
],
"speed": 2500,
"duplex": "full",
"enabled": true,
"poe": {
"admin-mode": true,
"power-limit": 60000
}
},
{
"select-ports": [
"Ethernet8",
"Ethernet9"
],
"speed": 10000,
"duplex": "full",
"media": "sfp-forced-1000sfp"
}
],
"interfaces": [
{
"name": "VLAN1",
"vlan": {
"id": 1
},
"ipv4": {
"addressing": "dynamic"
},
"ethernet": [
{
"select-ports": [
"Ethernet0",
"Ethernet1",
"Ethernet2",
"Ethernet3",
"Ethernet4",
"Ethernet5",
"Ethernet6",
"Ethernet7",
"Ethernet8",
"Ethernet9"
],
"vlan-tag": "un-tagged"
}
]
}
],
"metrics": {
"dhcp-snooping": {
"filters": [
"ack",
"discover",
"offer",
"request",
"solicit",
"reply",
"renew"
]
},
"health": {
"interval": 60
},
"statistics": {
"interval": 120,
"types": []
}
},
"unit": {
"leds-active": true,
"usage-threshold": 95
},
"uuid": 1678263900
}

File diff suppressed because it is too large Load Diff

View File

@@ -65,7 +65,6 @@ openwifi.system.debug = true
openwifi.system.uri.private = https://localhost:17002
openwifi.system.uri.public = https://ucentral.dpaas.arilia.com:16002
openwifi.system.uri.ui = https://ucentral-ui.arilia.com
openwifi.security.restapi.disable = false
openwifi.system.commandchannel = /tmp/app.ucentralgw
#
@@ -75,7 +74,7 @@ openwifi.autoprovisioning = true
openwifi.devicetypes.0 = AP:linksys_ea8300,edgecore_eap101,linksys_e8450-ubi
openwifi.devicetypes.1 = SWITCH:edgecore_ecs4100-12ph
openwifi.devicetypes.2 = IOT:esp32
oui.download.uri = https://standards-oui.ieee.org/oui/oui.txt
oui.download.uri = https://linuxnet.ca/ieee/oui.txt
iptocountry.default = US
iptocountry.provider = ipinfo
@@ -97,12 +96,6 @@ rtty.timeout = 60
rtty.viewport = 5913
rtty.assets = $OWGW_ROOT/rtty_ui
### RADIUS proxy config
radius.proxy.enable = false
radius.proxy.accounting.port = 1813
radius.proxy.authentication.port = 1812
radius.proxy.coa.port = 3799
#############################
# Generic information for all micro services
#############################
@@ -122,11 +115,6 @@ openwifi.kafka.brokerlist = a1.arilia.com:9092
openwifi.kafka.auto.commit = false
openwifi.kafka.queue.buffering.max.ms = 50
openwifi.kafka.ssl.ca.location =
openwifi.kafka.ssl.certificate.location =
openwifi.kafka.ssl.key.location =
openwifi.kafka.ssl.key.password =
#
# This section select which form of persistence you need
# Only one selected at a time. If you select multiple, this service will die if a horrible
@@ -178,4 +166,4 @@ archiver.db.3.keep = 7
########################################################################
logging.type = file
logging.path = $OWGW_ROOT/logs
logging.level = information
logging.level = debug

View File

@@ -52,8 +52,7 @@ openwifi.fileuploader.host.0.cert = ${FILEUPLOADER_HOST_CERT}
openwifi.fileuploader.host.0.key = ${FILEUPLOADER_HOST_KEY}
openwifi.fileuploader.host.0.key.password = ${FILEUPLOADER_HOST_KEY_PASSWORD}
openwifi.fileuploader.path = ${FILEUPLOADER_PATH}
# maxsize in KB
openwifi.fileuploader.maxsize = ${FILEUPLOADER_MAXSIZE}
openwifi.fileuploader.maxsize = 10000
openwifi.fileuploader.uri = ${FILEUPLOADER_URI}
#
@@ -66,7 +65,6 @@ openwifi.system.debug = true
openwifi.system.uri.private = ${SYSTEM_URI_PRIVATE}
openwifi.system.uri.public = ${SYSTEM_URI_PUBLIC}
openwifi.system.uri.ui = ${SYSTEM_URI_UI}
openwifi.security.restapi.disable = ${SECURITY_RESTAPI_DISABLE}
openwifi.system.commandchannel = /tmp/app.ucentralgw
#
@@ -76,9 +74,9 @@ openwifi.autoprovisioning = true
openwifi.devicetypes.0 = AP:linksys_ea8300,edgecore_eap101,linksys_e8450-ubi
openwifi.devicetypes.1 = SWITCH:edgecore_ecs4100-12ph
openwifi.devicetypes.2 = IOT:esp32
openwifi.certificates.allowmismatch = ${CERTIFICATES_ALLOWMISMATCH}
oui.download.uri = https://standards-oui.ieee.org/oui/oui.txt
oui.download.uri = https://linuxnet.ca/ieee/oui.txt
simulatorid = ${SIMULATORID}
iptocountry.default = US
iptocountry.provider = ${IPTOCOUNTRY_PROVIDER}
iptocountry.ipinfo.token = ${IPTOCOUNTRY_IPINFO_TOKEN}
@@ -86,30 +84,15 @@ iptocountry.ipdata.apikey = ${IPTOCOUNTRY_IPDATA_APIKEY}
autoprovisioning.process = ${AUTOPROVISIONING_PROCESS}
openwifi.session.timeout = ${DEVICE_SESSION_TIMEOUT}
#
# rtty
#
rtty.internal = ${RTTY_INTERNAL}
rtty.enabled = ${RTTY_ENABLED}
rtty.server = ${RTTY_SERVER}
rtty.port = ${RTTY_PORT}
rtty.token = ${RTTY_TOKEN}
rtty.timeout = ${RTTY_TIMEOUT}
rtty.viewport = ${RTTY_VIEWPORT}
rtty.assets = ${RTTY_ASSETS}
### RADIUS proxy config
radius.proxy.enable = ${RADIUS_PROXY_ENABLE}
radius.proxy.accounting.port = ${RADIUS_PROXY_ACCOUNTING_PORT}
radius.proxy.authentication.port = ${RADIUS_PROXY_AUTHENTICATION_PORT}
radius.proxy.coa.port = ${RADIUS_PROXY_COA_PORT}
iptocountry.default = ${IPINFO_DEFAULT_COUNTRY}
#iptocountry.provider = ipinfo
#iptocountry.provider = ipdata
#iptocountry.ipinfo.token =
#iptocountry.ipdata.apikey =
#############################
# Generic information for all micro services
@@ -130,11 +113,6 @@ openwifi.kafka.brokerlist = ${KAFKA_BROKERLIST}
openwifi.kafka.auto.commit = false
openwifi.kafka.queue.buffering.max.ms = 50
openwifi.kafka.ssl.ca.location = ${KAFKA_SSL_CA_LOCATION}
openwifi.kafka.ssl.certificate.location = ${KAFKA_SSL_CERTIFICATE_LOCATION}
openwifi.kafka.ssl.key.location = ${KAFKA_SSL_KEY_LOCATION}
openwifi.kafka.ssl.key.password = ${KAFKA_SSL_KEY_PASSWORD}
#
# This section select which form of persistence you need
# Only one selected at a time. If you select multiple, this service will die if a horrible
@@ -146,7 +124,7 @@ storage.type.sqlite.db = devices.db
storage.type.sqlite.idletime = 120
storage.type.sqlite.maxsessions = 128
storage.type.postgresql.maxsessions = 250
storage.type.postgresql.maxsessions = 64
storage.type.postgresql.idletime = 60
storage.type.postgresql.host = ${STORAGE_TYPE_POSTGRESQL_HOST}
storage.type.postgresql.username = ${STORAGE_TYPE_POSTGRESQL_USERNAME}
@@ -183,4 +161,4 @@ archiver.db.3.keep = 7
########################################################################
logging.type = console
logging.path = $OWGW_ROOT/logs
logging.level = ${LOGGING_LEVEL}
logging.level = debug

View File

@@ -1,205 +0,0 @@
{
"interfaces": [
{
"ethernet": [
{
"select-ports": [
"WAN*"
]
}
],
"ipv4": {
"addressing": "dynamic"
},
"ipv6": {
"addressing": "dynamic"
},
"name": "wan",
"role": "upstream",
"services": [
"ssh"
],
"ssids": []
},
{
"ethernet": [
{
"select-ports": [
"LAN*"
]
}
],
"ipv4": {
"addressing": "static",
"dhcp": {
"lease-count": 100,
"lease-first": 10,
"lease-time": "6h"
},
"gateway": "192.168.1.1",
"send-hostname": true,
"subnet": "192.168.1.1/24",
"use-dns": []
},
"ipv6": {
"addressing": "dynamic"
},
"name": "lan",
"role": "downstream",
"services": [
"wifi-steering",
"ssh"
],
"ssids": [
{
"bss-mode": "ap",
"encryption": {
"ieee80211w": "required",
"proto": "wpa"
},
"hidden-ssid": false,
"isolate-clients": false,
"maximum-clients": 64,
"name": "arilia-rad",
"radius": {
"authentication": {
"host": "0.0.0.0",
"port": 1812,
"secret": "radsec"
},
"accounting": {
"host": "0.0.0.0",
"port": 1813,
"secret": "radsec"
}
},
"services": [
"radius-gw-proxy"
],
"wifi-bands": [
"2G",
"5G"
],
"pass-point": {
"venue-name": [
"eng:Example passpoint_venue",
"fra:Exemple de lieu"
],
"domain-name": [
"onboard.almondlabs.net",
"test.com"
],
"asra": false,
"internet": true,
"esr": false,
"uesa": false,
"access-network-type": 0,
"hessid":"11:22:33:44:55:66",
"venue-group": 2,
"venue-type": 8,
"connection-capability":[
"1:0:2",
"6:22:1",
"17:5060:0"
],
"roaming-consortium": [
"F4F5E8F5F4",
"BAA2D00100",
"BAA2D00000"
],
"disable-dgaf": true,
"anqp-domain": 8888,
"ipaddr-type-available": 14,
"nai-realm": [
],
"osen": false,
"anqp-3gpp-cell-net": [
],
"friendly-name": [
"eng:AlmondLabs",
"fra:AlmondLabs"
],
"venue-url": [
"http://www.example.com/info-fra",
"http://www.example.com/info-eng"
],
"auth-type": {
"type": "terms-and-conditions"
}
}
}
]
}
],
"metrics": {
"dhcp-snooping": {
"filters": [
"ack",
"discover",
"offer",
"request",
"solicit",
"reply",
"renew"
]
},
"health": {
"interval": 60
},
"statistics": {
"interval": 60,
"types": [
"ssids",
"lldp",
"clients"
]
},
"wifi-frames": {
"filters": [
"probe",
"auth",
"assoc",
"disassoc",
"deauth",
"local-deauth",
"inactive-deauth",
"key-mismatch",
"beacon-report",
"radar-detected"
]
}
},
"radios": [
{
"band": "2G",
"bandwidth": 10,
"beacon-interval": 100,
"channel": "auto",
"channel-mode": "HT",
"channel-width": 20,
"country": "CA",
"dtim-period": 2,
"maximum-clients": 64,
"tx-power": 0
},
{
"band": "5G",
"bandwidth": 20,
"beacon-interval": 100,
"channel": "auto",
"channel-mode": "HE",
"channel-width": 40,
"country": "CA",
"dtim-period": 2,
"maximum-clients": 64,
"tx-power": 0
}
],
"services": {
"ssh": {
"password-authentication": true,
"port": 22
}
},
"uuid": 1661312631
}

View File

@@ -1,72 +0,0 @@
{
"pools" : [
{
"name" : "master" ,
"description" : "master pool",
"useByDefault" : true,
"authConfig" : {
"strategy" : "weighted",
"monitor" : false,
"monitorMethod" : "none",
"methodParameters" : [],
"servers" : [ {
"name" : "svr1",
"ip" : "10.100.0.1",
"port" : 1812,
"weight" : 10,
"secret" : "my_secret!"
},
{
"name" : "svr2",
"ip" : "10.100.10.1",
"port" : 1812,
"weight" : 20,
"secret" : "my_secret!"
}
]
},
"acctConfig" : {
"strategy" : "random",
"monitor" : false,
"monitorMethod" : "none",
"methodParameters" : [],
"servers" : [ {
"name" : "svr1",
"ip" : "10.100.0.1",
"port" : 1813,
"weight" : 10,
"secret" : "my_secret!"
},
{
"name" : "svr2",
"ip" : "10.100.10.1",
"port" : 1813,
"weight" : 20,
"secret" : "my_secret!"
}
]
},
"coaConfig" : {
"strategy" : "round_robin",
"monitor" : false,
"monitorMethod" : "none",
"methodParameters" : [],
"servers" : [ {
"name" : "svr1",
"ip" : "10.100.0.1",
"port" : 3799,
"weight" : 10,
"secret" : "my_secret!"
},
{
"name" : "svr2",
"ip" : "10.100.10.1",
"port" : 3799,
"weight" : 20,
"secret" : "my_secret!"
}
]
}
}
]
}

View File

@@ -1,33 +0,0 @@
{
"pools" : [
{
"name" : "master" ,
"description" : "master pool",
"useByDefault" : true,
"authConfig" : {
"strategy" : "weighted",
"monitor" : false,
"monitorMethod" : "none",
"methodParameters" : [],
"servers" : [ {
"name" : "orion",
"ip" : "216.239.32.91",
"port" : 2083,
"weight" : 10,
"radsec" : true,
"radsecPort" : 2083,
"allowSelfSigned" : false,
"radsecSecret" : "radsec",
"radsecKey" : "LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUR6RnpXeTZlYXg0QVoxTySG9VUURRZ0FFS3BnWVBHMktPTVd2S0w1Z3NMRXpUc09rREg1M3NHaEQyS3RsRXBDTXVnNDNIZlFnTFVpUgpTR1R2S1l0bDFmbmJaU1lnY0RJdncxdjNYRy9hVDhOY2JBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=",
"radsecCert" : "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNRVENDQWVpZ0F3SUJBZ0lVY3BKS3pVM0Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBb1RDa0oxZEhSdmJuZHZiMlF4SFRBYkJnTlZCQU1URkVKMQpkSFJ2Ym5kdmIyUWdVbUZrYzJWaklFTkJNQjRYRFRJeU1EY3dNekExTWpVeE5Gb1hEVEkzTURVeE9UQTFNalV4Ck5Gb3dkVEVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFvVENrSjFkSFJ2Ym5kdmIyUXhOakEwQmdOVkJBTVQKTFdGeWFXeHBZUzVqWWpFd2FtTnVjemgxYlhCbk9HWnBjRFowTUM1dmNtbHZiaTVoY21WaE1USXdMbU52YlRFWgpNQmNHQ2dtU0pvbVQ4aXhrQVFFVENVZHZiMmRzWlRwVlV6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCQ3FZR0R4dGlqakZyeWkrWUxDeE0wN0RwQXgrZDdCb1E5aXJaUktRakxvT054MzBJQzFJa1Voazd5bUwKWmRYNTIyVW1JSEF5TDhOYjkxeHYyay9EWEd5amdZa3dnWVl3RGdZRFZSMFBBUUgvQkFRREFnZUFNQk1HQTFVZApKUVFNTUFvR0NDc0dBUVVGQndNQ01Bd0dBMVVkRXdFQi93UUNNQUF3T0FZRFZSMFJCREV3TDRJdFlYSnBiR2xoCkxtTmlNVEJxWTI1ek9IVnRjR2M0Wm1sd05uUXdMbTl5YVc5dUxtRnlaV0V4TWpBdVkyOXRNQmNHQTFVZElBUVEKTUE0d0RBWUtLd1lCQkFIdUtnRUJCVEFLQmdncWhrak9QUVFEQWdOSEFEQkVBaUFwTmM1dUNBSkp6KzVyakdqdwpCWGtOdHE3UU83bWU5dUg5bkNsTDZnSVE5Z0lnUHM2VkVKVW5CcEZ0RktXbFF4eWJ1YlBxYnpJNjBPSERHQ0ExCmhXUk1PS1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K",
"radsecCacerts" : [
"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ0akNDQVZ1Z0F3SUJBZ0lCQyOXZaREVkTUJzR0ExVUVBeE1VUW5WMGRHOXVkMjl2WkNCU1lXUnpaV01nUTBFdwpJQmNOTVRrd05qQTJNVFV4TlRNeVdoZ1BNalV4T1RBMk1EY3hOVEUxTXpKYU1FRXhDekFKQmdOVkJBWVRBbFZUCk1STXdFUVlEVlFRS0V3cENkWFIwYjI1M2IyOWtNUjB3R3dZRFZRUURFeFJDZFhSMGIyNTNiMjlrSUZKaFpITmwKWXlCRFFUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJJUW40QVBKaXdvUVFQblR1cFgrZTk1Ugp0ZzVoQVFVbUhCN1E0UkpwSG4welF6TUJpMDdSejkxV05RamFHeERydktLVlQ1OThIM2dxYkI4TTViOHN3aytqClFqQkFNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0RBZ1lJS3dZQkJRVUgKQXdFd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBS0JnZ3Foa2pPUFFRREFnTkpBREJHQWlFQTkrbHUwN2NlaGc1OQpxSi81dWtzN05oL3F2aXFHWCs1WDFwSVVxdENGVlJjQ0lRREZmUGhwZzZJOFE0SUdBbzNuamlHRTdDWC9nMm56CkRzSW5FcG9vRlkxV0xRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo="
],
"radsecRealms" : [],
"ignore" : false
}
]
}
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="icon" href="/favicon.ico">
<title>RTTYs</title>
<link href="/css/app.0e046291.css" rel="preload" as="style">
<link href="/css/chunk-vendors.b221ddbd.css" rel="preload" as="style">
<link href="/css/chunk-vendors.b221ddbd.css" rel="stylesheet">
<link href="/css/app.0e046291.css" rel="stylesheet">
<link href="/js/app.79bf330a.js" rel="preload" as="script">
<link href="/js/chunk-vendors.7fd2577a.js" rel="preload" as="script">
</head>
<body>
<noscript>
<strong>We're sorry but Rttys doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script src="/js/chunk-vendors.7fd2577a.js">
</script><script src="/js/app.79bf330a.js">
</script>
</body>
</html>
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>Rttys</title><link href="/css/app.0e046291.css" rel="preload" as="style"><link href="/css/chunk-vendors.b221ddbd.css" rel="preload" as="style"><link href="/js/app.79bf330a.js" rel="preload" as="script"><link href="/js/chunk-vendors.7fd2577a.js" rel="preload" as="script"><link href="/css/chunk-vendors.b221ddbd.css" rel="stylesheet"><link href="/css/app.0e046291.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but Rttys doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.7fd2577a.js"></script><script src="/js/app.79bf330a.js"></script></body></html>

24
run.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
daemon=ucentralgw
if [[ "$1" == "aws" ]]
then
cp ${daemon}.properties.aws ${daemon}.properties
. ./set_env.sh
cd cmake-build
./${daemon} --daemon
echo "Running AWS version as daemon..."
fi
if [[ "$1" == "priv" ]]
then
cp ${daemon}.properties.priv ${daemon}.properties
. ./set_env.sh
cd cmake-build
./${daemon} --daemon
echo "Running private version as daemon..."
fi

View File

@@ -1,59 +0,0 @@
//
// Created by stephane bourque on 2023-05-23.
//
#include "AP_WS_ConfigAutoUpgrader.h"
#include <framework/utils.h>
#include <RESTObjects/RESTAPI_GWobjects.h>
#include <StorageService.h>
namespace OpenWifi {
int AP_WS_ConfigAutoUpgradeAgent::Start() {
poco_notice(Logger(), "Starting...");
QueueManager_.start(*this);
return 0;
}
void AP_WS_ConfigAutoUpgradeAgent::Stop() {
poco_notice(Logger(), "Stopping...");
Running_ = false;
Queue_.wakeUpAll();
QueueManager_.join();
poco_notice(Logger(), "Stopped...");
}
void AP_WS_ConfigAutoUpgradeAgent::run() {
Utils::SetThreadName("auto:cfgmgr");
Running_ = true;
while (Running_) {
Poco::AutoPtr<Poco::Notification> NextMsg(Queue_.waitDequeueNotification());
try {
auto Entry = dynamic_cast<CheckConfiguration *>(NextMsg.get());
if (Entry != nullptr) {
GWObjects::Device DeviceInfo;
std::string SerialNumber = Utils::IntToSerialNumber(Entry->serial_);
if (StorageService()->GetDevice(SerialNumber, DeviceInfo)) {
if(DeviceInfo.pendingUUID!=0 && Entry->uuid_==DeviceInfo.pendingUUID) {
StorageService()->CompleteDeviceConfigurationChange(SerialNumber);
SetDeviceCacheEntry(Entry->serial_, Utils::Now(), Entry->uuid_, 0);
continue;
}
if(DeviceInfo.UUID==Entry->uuid_) {
SetDeviceCacheEntry(Entry->serial_, Utils::Now(), Entry->uuid_, 0);
continue;
}
}
}
return;
} catch (const Poco::Exception &E) {
Logger().log(E);
} catch (...) {
poco_warning(Logger(), "Exception occurred during run.");
}
}
}
} // namespace OpenWifi

View File

@@ -1,137 +0,0 @@
//
// Created by stephane bourque on 2023-05-23.
//
#pragma once
#include "Poco/Notification.h"
#include "Poco/NotificationQueue.h"
#include "Poco/Timer.h"
#include <framework/SubSystemServer.h>
#include <framework/utils.h>
namespace OpenWifi {
class CheckConfiguration : public Poco::Notification {
public:
explicit CheckConfiguration(std::uint64_t s, std::uint64_t c) :
serial_(s), uuid_(c) {
}
std::uint64_t serial_;
std::uint64_t uuid_;
};
struct ConfigurationCacheEntry {
std::uint64_t last_check_=0;
std::uint64_t current_config_=0;
std::uint64_t pending_config_=0;
};
class AP_WS_ConfigAutoUpgradeAgent : public SubSystemServer, Poco::Runnable {
public:
int Start() final;
void Stop() final;
void run() final;
static auto instance() {
static auto instance = new AP_WS_ConfigAutoUpgradeAgent;
return instance;
}
inline void AddConfiguration(std::uint64_t serial, std::uint64_t config_uuid) {
std::lock_guard Guard(CacheMutex_);
auto hint = Cache_.find(serial);
if(hint==end(Cache_)) {
Cache_[serial] = { Utils::Now(),config_uuid , 0 };
return;
}
if(hint->second.pending_config_==0) {
hint->second.last_check_ = Utils::Now();
hint->second.current_config_ = config_uuid;
return;
}
}
inline void AddConfiguration(std::uint64_t serial, std::uint64_t config_uuid, std::uint64_t pending_config_uuid) {
std::lock_guard Guard(CacheMutex_);
auto hint = Cache_.find(serial);
if(hint==end(Cache_)) {
Cache_[serial] = { Utils::Now(), config_uuid , pending_config_uuid };
return;
}
if(hint->second.pending_config_==0) {
hint->second.last_check_ = Utils::Now();
hint->second.current_config_ = config_uuid;
hint->second.pending_config_ = pending_config_uuid;
return;
}
}
[[nodiscard]] inline ConfigurationCacheEntry GetSerialInfo(std::uint64_t serial) const {
std::lock_guard Guard(CacheMutex_);
auto hint = Cache_.find(serial);
if(hint==end(Cache_)) {
return {0,0,0};
}
return hint->second;
}
inline bool UpdateConfiguration(std::uint64_t serial, std::uint64_t config) {
if(serial==0)
return false;
std::lock_guard Guard(CacheMutex_);
auto hint = Cache_.find(serial);
if(hint!=end(Cache_)) {
if(hint->second.current_config_==config) {
return false;
}
if(config==hint->second.pending_config_) {
Queue_.enqueueNotification(new CheckConfiguration(serial,config));
return true;
}
if(config!=hint->second.current_config_ && hint->second.pending_config_==0) {
Queue_.enqueueNotification(new CheckConfiguration(serial,config));
return true;
}
if((Utils::Now()-hint->second.last_check_)<60*5) {
return false;
}
if(hint->second.pending_config_!=0) {
return false;
}
}
return true;
}
inline void SetDeviceCacheEntry(std::uint64_t serial, std::uint64_t t, std::uint64_t uuid, std::uint64_t pending_uuid) {
std::lock_guard Guard(CacheMutex_);
Cache_[serial] = { t, uuid, pending_uuid };
}
private:
Poco::NotificationQueue Queue_;
Poco::Thread QueueManager_;
std::atomic_bool Running_=false;
mutable std::mutex CacheMutex_;
std::map<std::uint64_t, ConfigurationCacheEntry> Cache_;
AP_WS_ConfigAutoUpgradeAgent() noexcept
: SubSystemServer("AutoConfigUpgrade", "AUTO-CFG-MGR", "auto.config.updater") {
}
};
inline auto AP_WS_ConfigAutoUpgradeAgent() { return AP_WS_ConfigAutoUpgradeAgent::instance(); }
} // namespace OpenWifi

View File

@@ -1,976 +0,0 @@
//
// Created by stephane bourque on 2022-02-03.
//
#include <Poco/Base64Decoder.h>
#include <Poco/Net/Context.h>
#include <Poco/Net/HTTPServerRequestImpl.h>
#include <Poco/Net/HTTPServerResponseImpl.h>
#include <Poco/JSON/JSONException.h>
#include <Poco/Net/NetException.h>
#include <Poco/Net/SSLException.h>
#include <Poco/Net/SecureStreamSocketImpl.h>
#include <Poco/Net/WebSocketImpl.h>
#include <framework/KafkaManager.h>
#include <framework/MicroServiceFuncs.h>
#include <framework/utils.h>
#include <framework/ow_constants.h>
#include <fmt/format.h>
#include <AP_WS_Connection.h>
#include <AP_WS_Server.h>
#include <CentralConfig.h>
#include <CommandManager.h>
#include <StorageService.h>
#include <RADIUSSessionTracker.h>
#include <RADIUS_proxy_server.h>
#include <GWKafkaEvents.h>
#include <UI_GW_WebSocketNotifications.h>
namespace OpenWifi {
void AP_WS_Connection::LogException(const Poco::Exception &E) {
poco_information(Logger_, fmt::format("EXCEPTION({}): {}", CId_, E.displayText()));
}
AP_WS_Connection::AP_WS_Connection(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response,
uint64_t session_id, Poco::Logger &L,
std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> R)
: Logger_(L), IncomingFrame_(0) {
Reactor_ = R.first;
DbSession_ = R.second;
State_.sessionId = session_id;
WS_ = std::make_unique<Poco::Net::WebSocket>(request, response);
auto TS = Poco::Timespan(360, 0);
WS_->setMaxPayloadSize(BufSize);
WS_->setReceiveTimeout(TS);
WS_->setNoDelay(false);
WS_->setKeepAlive(true);
WS_->setBlocking(false);
IncomingFrame_.resize(0);
uuid_ = MicroServiceRandom(std::numeric_limits<std::uint64_t>::max()-1);
AP_WS_Server()->IncrementConnectionCount();
}
void AP_WS_Connection::Start() {
Registered_ = true;
LastContact_ = Utils::Now();
Reactor_->addEventHandler(*WS_,
Poco::NObserver<AP_WS_Connection, Poco::Net::ReadableNotification>(
*this, &AP_WS_Connection::OnSocketReadable));
Reactor_->addEventHandler(*WS_,
Poco::NObserver<AP_WS_Connection, Poco::Net::ShutdownNotification>(
*this, &AP_WS_Connection::OnSocketShutdown));
Reactor_->addEventHandler(*WS_,
Poco::NObserver<AP_WS_Connection, Poco::Net::ErrorNotification>(
*this, &AP_WS_Connection::OnSocketError));
}
AP_WS_Connection::~AP_WS_Connection() {
std::lock_guard G(ConnectionMutex_);
AP_WS_Server()->DecrementConnectionCount();
EndConnection();
poco_debug(Logger_, fmt::format("TERMINATION({}): Session={}, Connection removed.", SerialNumber_,
State_.sessionId));
}
static void NotifyKafkaDisconnect(const std::string &SerialNumber, std::uint64_t uuid) {
try {
Poco::JSON::Object Disconnect;
Poco::JSON::Object Details;
Details.set(uCentralProtocol::SERIALNUMBER, SerialNumber);
Details.set(uCentralProtocol::TIMESTAMP, Utils::Now());
Details.set(uCentralProtocol::UUID,uuid);
Disconnect.set(uCentralProtocol::DISCONNECTION, Details);
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber, Disconnect);
} catch (...) {
}
}
void AP_WS_Connection::EndConnection() {
bool expectedValue=false;
if (Dead_.compare_exchange_strong(expectedValue,true,std::memory_order_release,std::memory_order_relaxed)) {
if(!SerialNumber_.empty() && State_.LastContact!=0) {
StorageService()->SetDeviceLastRecordedContact(SerialNumber_, State_.LastContact);
}
if (Registered_) {
Registered_ = false;
Reactor_->removeEventHandler(
*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ReadableNotification>(
*this, &AP_WS_Connection::OnSocketReadable));
Reactor_->removeEventHandler(
*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ShutdownNotification>(
*this, &AP_WS_Connection::OnSocketShutdown));
Reactor_->removeEventHandler(
*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ErrorNotification>(
*this, &AP_WS_Connection::OnSocketError));
Registered_=false;
}
WS_->close();
if(!SerialNumber_.empty()) {
DeviceDisconnectionCleanup(SerialNumber_, uuid_);
}
AP_WS_Server()->AddCleanupSession(State_.sessionId, SerialNumberInt_);
}
}
bool AP_WS_Connection::ValidatedDevice() {
if(Dead_)
return false;
if (DeviceValidated_)
return true;
try {
auto SockImpl = dynamic_cast<Poco::Net::WebSocketImpl *>(WS_->impl());
auto SS =
dynamic_cast<Poco::Net::SecureStreamSocketImpl *>(SockImpl->streamSocketImpl());
PeerAddress_ = SS->peerAddress().host();
CId_ = Utils::FormatIPv6(SS->peerAddress().toString());
State_.started = Utils::Now();
if (!SS->secure()) {
poco_warning(Logger_, fmt::format("TLS-CONNECTION({}): Session={} Connection is "
"NOT secure. Device is not allowed.",
CId_, State_.sessionId));
return false;
}
poco_trace(Logger_, fmt::format("TLS-CONNECTION({}): Session={} Connection is secure.",
CId_, State_.sessionId));
if (!SS->havePeerCertificate()) {
State_.VerifiedCertificate = GWObjects::NO_CERTIFICATE;
poco_warning(
Logger_,
fmt::format("TLS-CONNECTION({}): Session={} No certificates available..", CId_,
State_.sessionId));
return false;
}
Poco::Crypto::X509Certificate PeerCert(SS->peerCertificate());
if (!AP_WS_Server()->ValidateCertificate(CId_, PeerCert)) {
State_.VerifiedCertificate = GWObjects::NO_CERTIFICATE;
poco_warning(Logger_,
fmt::format("TLS-CONNECTION({}): Session={} Device certificate is not "
"valid. Device is not allowed.",
CId_, State_.sessionId));
return false;
}
CN_ = Poco::trim(Poco::toLower(PeerCert.commonName()));
if(!Utils::ValidSerialNumber(CN_)) {
poco_trace(Logger_,
fmt::format("TLS-CONNECTION({}): Session={} Invalid serial number: CN={}", CId_,
State_.sessionId, CN_));
return false;
}
SerialNumber_ = CN_;
SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_);
State_.VerifiedCertificate = GWObjects::VALID_CERTIFICATE;
poco_trace(Logger_,
fmt::format("TLS-CONNECTION({}): Session={} Valid certificate: CN={}", CId_,
State_.sessionId, CN_));
if (AP_WS_Server::IsSim(CN_) && !AP_WS_Server()->IsSimEnabled()) {
poco_warning(Logger_, fmt::format("TLS-CONNECTION({}): Session={} Sim Device {} is "
"not allowed. Disconnecting.",
CId_, State_.sessionId, CN_));
return false;
}
if(AP_WS_Server::IsSim(SerialNumber_)) {
State_.VerifiedCertificate = GWObjects::SIMULATED;
Simulated_ = true;
}
std::string reason, author;
std::uint64_t created;
if (!CN_.empty() && StorageService()->IsBlackListed(SerialNumberInt_, reason, author, created)) {
DeviceBlacklistedKafkaEvent KE(Utils::SerialNumberToInt(CN_), Utils::Now(), reason, author, created, CId_);
poco_warning(
Logger_,
fmt::format(
"TLS-CONNECTION({}): Session={} Device {} is black listed. Disconnecting.",
CId_, State_.sessionId, CN_));
return false;
}
State_.certificateExpiryDate = PeerCert.expiresOn().timestamp().epochTime();
State_.certificateIssuerName = PeerCert.issuerName();
poco_trace(Logger_,
fmt::format("TLS-CONNECTION({}): Session={} CN={} Completed. (t={})", CId_,
State_.sessionId, CN_, ConcurrentStartingDevices_));
DeviceValidated_ = true;
return true;
} catch (const Poco::Net::CertificateValidationException &E) {
poco_error(
Logger_,
fmt::format(
"CONNECTION({}): Session:{} Poco::CertificateValidationException Certificate "
"Validation failed during connection. Device will have to retry.",
CId_, State_.sessionId));
Logger_.log(E);
} catch (const Poco::Net::WebSocketException &E) {
poco_error(Logger_,
fmt::format("CONNECTION({}): Session:{} Poco::WebSocketException WebSocket "
"error during connection. Device will have to retry.",
CId_, State_.sessionId));
Logger_.log(E);
} catch (const Poco::Net::ConnectionAbortedException &E) {
poco_error(
Logger_,
fmt::format("CONNECTION({}):Session:{} Poco::ConnectionAbortedException "
"Connection was aborted during connection. Device will have to retry.",
CId_, State_.sessionId));
Logger_.log(E);
} catch (const Poco::Net::ConnectionResetException &E) {
poco_error(
Logger_,
fmt::format("CONNECTION({}): Session:{} Poco::ConnectionResetException Connection "
"was reset during connection. Device will have to retry.",
CId_, State_.sessionId));
Logger_.log(E);
} catch (const Poco::Net::InvalidCertificateException &E) {
poco_error(Logger_,
fmt::format("CONNECTION({}): Session:{} Poco::InvalidCertificateException "
"Invalid certificate. Device will have to retry.",
CId_, State_.sessionId));
Logger_.log(E);
} catch (const Poco::Net::SSLException &E) {
poco_error(Logger_,
fmt::format("CONNECTION({}): Session:{} Poco::SSLException SSL Exception "
"during connection. Device will have to retry.",
CId_, State_.sessionId));
Logger_.log(E);
} catch (const Poco::Exception &E) {
poco_error(Logger_, fmt::format("CONNECTION({}): Session:{} Poco::Exception caught "
"during device connection. Device will have to retry.",
CId_, State_.sessionId));
Logger_.log(E);
} catch (...) {
poco_error(
Logger_,
fmt::format("CONNECTION({}): Session:{} Exception caught during device connection. "
"Device will have to retry. Unsecure connect denied.",
CId_, State_.sessionId));
}
EndConnection();
return false;
}
void AP_WS_Connection::DeviceDisconnectionCleanup(const std::string &SerialNumber, std::uint64_t uuid) {
if (KafkaManager()->Enabled()) {
NotifyKafkaDisconnect(SerialNumber, uuid);
}
RADIUSSessionTracker()->DeviceDisconnect(SerialNumber);
GWWebSocketNotifications::SingleDevice_t N;
N.content.serialNumber = SerialNumber;
GWWebSocketNotifications::DeviceDisconnected(N);
}
void AP_WS_Connection::ProcessJSONRPCResult(Poco::JSON::Object::Ptr Doc) {
poco_trace(Logger_, fmt::format("RECEIVED-RPC({}): {}.", CId_,
Doc->get(uCentralProtocol::ID).toString()));
CommandManager()->PostCommandResult(SerialNumber_, Doc);
}
void AP_WS_Connection::ProcessJSONRPCEvent(Poco::JSON::Object::Ptr &Doc) {
auto Method = Doc->get(uCentralProtocol::METHOD).toString();
auto EventType = uCentralProtocol::Events::EventFromString(Method);
if (EventType == uCentralProtocol::Events::ET_UNKNOWN) {
poco_warning(Logger_, fmt::format("ILLEGAL-PROTOCOL({}): Unknown message type '{}'",
CId_, Method));
Errors_++;
return;
}
if (!Doc->isObject(uCentralProtocol::PARAMS)) {
poco_warning(Logger_,
fmt::format("MISSING-PARAMS({}): params must be an object.", CId_));
Errors_++;
return;
}
// expand params if necessary
auto ParamsObj = Doc->get(uCentralProtocol::PARAMS).extract<Poco::JSON::Object::Ptr>();
if (ParamsObj->has(uCentralProtocol::COMPRESS_64)) {
std::string UncompressedData;
try {
auto CompressedData = ParamsObj->get(uCentralProtocol::COMPRESS_64).toString();
uint64_t compress_sz = 0;
if (ParamsObj->has("compress_sz")) {
compress_sz = ParamsObj->get("compress_sz");
}
if (Utils::ExtractBase64CompressedData(CompressedData, UncompressedData,
compress_sz)) {
poco_trace(Logger_,
fmt::format("EVENT({}): Found compressed payload expanded to '{}'.",
CId_, UncompressedData));
Poco::JSON::Parser Parser;
ParamsObj = Parser.parse(UncompressedData).extract<Poco::JSON::Object::Ptr>();
} else {
poco_warning(Logger_,
fmt::format("INVALID-COMPRESSED-DATA({}): Compressed cannot be "
"uncompressed - content must be corrupt..: size={}",
CId_, CompressedData.size()));
Errors_++;
return;
}
} catch (const Poco::Exception &E) {
poco_warning(Logger_, fmt::format("INVALID-COMPRESSED-JSON-DATA({}): Compressed "
"cannot be parsed - JSON must be corrupt..",
CId_));
Logger_.log(E);
return;
}
}
if (!ParamsObj->has(uCentralProtocol::SERIAL)) {
poco_warning(
Logger_,
fmt::format("MISSING-PARAMS({}): Serial number is missing in message.", CId_));
return;
}
auto Serial =
Poco::trim(Poco::toLower(ParamsObj->get(uCentralProtocol::SERIAL).toString()));
if (!Utils::ValidSerialNumber(Serial)) {
Poco::Exception E(
fmt::format(
"ILLEGAL-DEVICE-NAME({}): device name is illegal and not allowed to connect.",
Serial),
EACCES);
E.rethrow();
}
std::string reason, author;
std::uint64_t created;
if (StorageService()->IsBlackListed(SerialNumberInt_, reason, author, created)) {
DeviceBlacklistedKafkaEvent KE(Utils::SerialNumberToInt(CN_), Utils::Now(), reason, author, created, CId_);
Poco::Exception E(
fmt::format("BLACKLIST({}): device is blacklisted and not allowed to connect.",
Serial),
EACCES);
E.rethrow();
}
switch (EventType) {
case uCentralProtocol::Events::ET_CONNECT: {
Process_connect(ParamsObj, Serial);
} break;
case uCentralProtocol::Events::ET_STATE: {
Process_state(ParamsObj);
} break;
case uCentralProtocol::Events::ET_HEALTHCHECK: {
Process_healthcheck(ParamsObj);
} break;
case uCentralProtocol::Events::ET_LOG: {
Process_log(ParamsObj);
} break;
case uCentralProtocol::Events::ET_CRASHLOG: {
Process_crashlog(ParamsObj);
} break;
case uCentralProtocol::Events::ET_PING: {
Process_ping(ParamsObj);
} break;
case uCentralProtocol::Events::ET_CFGPENDING: {
Process_cfgpending(ParamsObj);
} break;
case uCentralProtocol::Events::ET_RECOVERY: {
Process_recovery(ParamsObj);
} break;
case uCentralProtocol::Events::ET_DEVICEUPDATE: {
Process_deviceupdate(ParamsObj, Serial);
} break;
case uCentralProtocol::Events::ET_TELEMETRY: {
Process_telemetry(ParamsObj);
} break;
case uCentralProtocol::Events::ET_VENUEBROADCAST: {
Process_venuebroadcast(ParamsObj);
} break;
case uCentralProtocol::Events::ET_EVENT: {
Process_event(ParamsObj);
} break;
case uCentralProtocol::Events::ET_ALARM: {
Process_alarm(ParamsObj);
} break;
case uCentralProtocol::Events::ET_WIFISCAN: {
Process_wifiscan(ParamsObj);
} break;
case uCentralProtocol::Events::ET_REBOOTLOG: {
Process_rebootLog(ParamsObj);
} break;
// this will never be called but some compilers will complain if we do not have a case for
// every single values of an enum
case uCentralProtocol::Events::ET_UNKNOWN: {
poco_warning(Logger_, fmt::format("ILLEGAL-EVENT({}): Event '{}' unknown. CN={}", CId_,
Method, CN_));
Errors_++;
}
}
}
bool AP_WS_Connection::StartTelemetry(uint64_t RPCID,
const std::vector<std::string> &TelemetryTypes) {
poco_information(Logger_, fmt::format("TELEMETRY({}): Starting.", CId_));
Poco::JSON::Object StartMessage;
StartMessage.set("jsonrpc", "2.0");
StartMessage.set("method", "telemetry");
Poco::JSON::Object Params;
Params.set("serial", SerialNumber_);
Params.set("interval", (uint64_t)TelemetryInterval_);
Poco::JSON::Array Types;
if (TelemetryTypes.empty()) {
Types.add("wifi-frames");
Types.add("dhcp-snooping");
Types.add("state");
} else {
for (const auto &type : TelemetryTypes)
Types.add(type);
}
Params.set(RESTAPI::Protocol::TYPES, Types);
StartMessage.set("id", RPCID);
StartMessage.set("params", Params);
Poco::JSON::Stringifier Stringify;
std::ostringstream OS;
Stringify.condense(StartMessage, OS);
return Send(OS.str());
}
bool AP_WS_Connection::StopTelemetry(uint64_t RPCID) {
poco_information(Logger_, fmt::format("TELEMETRY({}): Stopping.", CId_));
Poco::JSON::Object StopMessage;
StopMessage.set("jsonrpc", "2.0");
StopMessage.set("method", "telemetry");
Poco::JSON::Object Params;
Params.set("serial", SerialNumber_);
Params.set("interval", 0);
StopMessage.set("id", RPCID);
StopMessage.set("params", Params);
Poco::JSON::Stringifier Stringify;
std::ostringstream OS;
Stringify.condense(StopMessage, OS);
TelemetryKafkaPackets_ = TelemetryWebSocketPackets_ = TelemetryInterval_ =
TelemetryKafkaTimer_ = TelemetryWebSocketTimer_ = 0;
return Send(OS.str());
}
void AP_WS_Connection::UpdateCounts() {
State_.kafkaClients = TelemetryKafkaRefCount_;
State_.webSocketClients = TelemetryWebSocketRefCount_;
}
bool AP_WS_Connection::SetWebSocketTelemetryReporting(
std::uint64_t RPCID, std::uint64_t Interval, std::uint64_t LifeTime,
const std::vector<std::string> &TelemetryTypes) {
std::unique_lock Lock(TelemetryMutex_);
TelemetryWebSocketRefCount_++;
TelemetryInterval_ = TelemetryInterval_
? (Interval < (std::uint64_t)TelemetryInterval_ ? Interval : (std::uint64_t )TelemetryInterval_)
: Interval;
auto TelemetryWebSocketTimer = LifeTime + Utils::Now();
TelemetryWebSocketTimer_ = TelemetryWebSocketTimer > (std::uint64_t)TelemetryWebSocketTimer_
? (std::uint64_t)TelemetryWebSocketTimer
: (std::uint64_t)TelemetryWebSocketTimer_;
UpdateCounts();
if (!TelemetryReporting_) {
TelemetryReporting_ = true;
return StartTelemetry(RPCID, TelemetryTypes);
}
return true;
}
bool
AP_WS_Connection::SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t Interval,
uint64_t LifeTime,
const std::vector<std::string> &TelemetryTypes) {
std::unique_lock Lock(TelemetryMutex_);
TelemetryKafkaRefCount_++;
TelemetryInterval_ = TelemetryInterval_
? (Interval < (std::uint64_t)TelemetryInterval_ ? (std::uint64_t)Interval : (std::uint64_t)TelemetryInterval_)
: Interval;
auto TelemetryKafkaTimer = LifeTime + Utils::Now();
TelemetryKafkaTimer_ =
TelemetryKafkaTimer > (std::uint64_t)TelemetryKafkaTimer_ ? (std::uint64_t)TelemetryKafkaTimer : (std::uint64_t)TelemetryKafkaTimer_;
UpdateCounts();
if (!TelemetryReporting_) {
TelemetryReporting_ = true;
return StartTelemetry(RPCID, TelemetryTypes);
}
return true;
}
bool AP_WS_Connection::StopWebSocketTelemetry(uint64_t RPCID) {
std::unique_lock Lock(TelemetryMutex_);
if (TelemetryWebSocketRefCount_)
TelemetryWebSocketRefCount_--;
UpdateCounts();
if (TelemetryWebSocketRefCount_ == 0 && TelemetryKafkaRefCount_ == 0) {
TelemetryReporting_ = false;
StopTelemetry(RPCID);
}
return true;
}
bool AP_WS_Connection::StopKafkaTelemetry(uint64_t RPCID) {
std::unique_lock Lock(TelemetryMutex_);
if (TelemetryKafkaRefCount_)
TelemetryKafkaRefCount_--;
UpdateCounts();
if (TelemetryWebSocketRefCount_ == 0 && TelemetryKafkaRefCount_ == 0) {
TelemetryReporting_ = false;
StopTelemetry(RPCID);
}
return true;
}
void AP_WS_Connection::OnSocketShutdown(
[[maybe_unused]] const Poco::AutoPtr<Poco::Net::ShutdownNotification> &pNf) {
poco_trace(Logger_, fmt::format("SOCKET-SHUTDOWN({}): Closing.", CId_));
// std::lock_guard G(ConnectionMutex_);
return EndConnection();
}
void AP_WS_Connection::OnSocketError(
[[maybe_unused]] const Poco::AutoPtr<Poco::Net::ErrorNotification> &pNf) {
poco_trace(Logger_, fmt::format("SOCKET-ERROR({}): Closing.", CId_));
// std::lock_guard G(ConnectionMutex_);
return EndConnection();
}
void AP_WS_Connection::OnSocketReadable(
[[maybe_unused]] const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf) {
if (Dead_) // we are dead, so we do not process anything.
return;
std::lock_guard G(ConnectionMutex_);
State_.LastContact = LastContact_ = Utils::Now();
if (AP_WS_Server()->Running() && (DeviceValidated_ || ValidatedDevice())) {
try {
return ProcessIncomingFrame();
} catch (const Poco::Exception &E) {
Logger_.log(E);
} catch (const std::exception &E) {
std::string W = E.what();
poco_information(
Logger_, fmt::format("std::exception caught: {}. Connection terminated with {}",
W, CId_));
} catch (...) {
poco_information(
Logger_, fmt::format("Unknown exception for {}. Connection terminated.", CId_));
}
}
EndConnection();
}
void AP_WS_Connection::ProcessWSFinalPayload() {
auto IncomingSize = IncomingFrame_.size();
if (IncomingSize == 0) {
poco_debug(Logger_,
fmt::format("ProcessWSFrame({}): Final Acc. Frame received but empty",
CId_));
return;
}
IncomingFrame_.append(0);
poco_trace(Logger_,
fmt::format("ProcessWSFrame({}): Final Acc. Frame received (len={}, Msg={}",
CId_, IncomingSize, IncomingFrame_.begin()));
Poco::JSON::Parser parser;
auto ParsedMessage = parser.parse(IncomingFrame_.begin());
auto IncomingJSON = ParsedMessage.extract<Poco::JSON::Object::Ptr>();
if (IncomingJSON->has(uCentralProtocol::JSONRPC)) {
if (IncomingJSON->has(uCentralProtocol::METHOD) &&
IncomingJSON->has(uCentralProtocol::PARAMS)) {
ProcessJSONRPCEvent(IncomingJSON);
} else if (IncomingJSON->has(uCentralProtocol::RESULT) &&
IncomingJSON->has(uCentralProtocol::ID)) {
poco_trace(Logger_, fmt::format("RPC-RESULT({}): payload: {}", CId_,
IncomingFrame_.begin()));
ProcessJSONRPCResult(IncomingJSON);
} else {
poco_warning(
Logger_,
fmt::format("INVALID-PAYLOAD({}): Payload is not JSON-RPC 2.0: {}",
CId_, IncomingFrame_.begin()));
}
} else if (IncomingJSON->has(uCentralProtocol::RADIUS)) {
ProcessIncomingRadiusData(IncomingJSON);
} else {
std::ostringstream iS;
IncomingJSON->stringify(iS);
poco_warning(
Logger_,
fmt::format("FRAME({}): illegal transaction header, missing 'jsonrpc': {}",
CId_, iS.str()));
Errors_++;
}
IncomingFrame_.clear();
IncomingFrame_.resize(0);
}
void AP_WS_Connection::ProcessIncomingFrame() {
Poco::Buffer<char> CurrentFrame(0);
bool KillConnection = false;
int flags = 0;
int IncomingSize = 0;
try {
IncomingSize = WS_->receiveFrame(CurrentFrame, flags);
int Op;
Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK;
if (IncomingSize < 0 && flags == 0) {
poco_trace(Logger_,
fmt::format("EMPTY({}): Non-blocking try-again empty frame (len={}, flags={})",
CId_, IncomingSize, flags));
} else if (IncomingSize == 0 && flags == 0) {
poco_information(Logger_,
fmt::format("DISCONNECT({}): device has disconnected. Session={}",
CId_, State_.sessionId));
return EndConnection();
}
if (IncomingSize > 0) {
State_.RX += IncomingSize;
AP_WS_Server()->AddRX(IncomingSize);
IncomingFrame_.append(CurrentFrame);
}
State_.MessageCount++;
State_.LastContact = Utils::Now();
poco_trace(Logger_,
fmt::format("FRAME({}): Frame rx (op={} len={}, flags={}, acc.len={})",
CId_, Op, IncomingSize, flags, IncomingFrame_.size()));
switch (Op) {
case Poco::Net::WebSocket::FRAME_OP_PING: {
poco_trace(Logger_, fmt::format("PING({}): received. PONG sent back.", CId_));
WS_->sendFrame("", 0,
(int)Poco::Net::WebSocket::FRAME_OP_PONG |
(int)Poco::Net::WebSocket::FRAME_FLAG_FIN);
if (KafkaManager()->Enabled()) {
Poco::JSON::Object PingObject;
Poco::JSON::Object PingDetails;
PingDetails.set(uCentralProtocol::FIRMWARE, State_.Firmware);
PingDetails.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
PingDetails.set(uCentralProtocol::COMPATIBLE, Compatible_);
PingDetails.set(uCentralProtocol::CONNECTIONIP, CId_);
PingDetails.set(uCentralProtocol::TIMESTAMP, Utils::Now());
PingDetails.set(uCentralProtocol::UUID, uuid_);
PingDetails.set("locale", State_.locale);
PingObject.set(uCentralProtocol::PING, PingDetails);
poco_trace(Logger_,fmt::format("Sending PING for {}", SerialNumber_));
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_,
PingObject);
}
return;
} break;
case Poco::Net::WebSocket::FRAME_OP_PONG: {
poco_trace(Logger_, fmt::format("PONG({}): received and ignored.", CId_));
return;
} break;
case Poco::Net::WebSocket::FRAME_OP_CONT: {
poco_trace(Logger_, fmt::format("CONTINUATION({}): registered.", CId_));
} break;
case Poco::Net::WebSocket::FRAME_OP_BINARY: {
poco_trace(Logger_, fmt::format("BINARY({}): Invalid frame type.", CId_));
KillConnection=true;
return;
} break;
case Poco::Net::WebSocket::FRAME_OP_TEXT: {
poco_trace(Logger_,
fmt::format("TEXT({}): Frame received (len={}, flags={}). Msg={}",
CId_, IncomingSize, flags,
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin()));
} break;
case Poco::Net::WebSocket::FRAME_OP_CLOSE: {
poco_information(Logger_,
fmt::format("CLOSE({}): Device is closing its connection.", CId_));
KillConnection=true;
} break;
default: {
poco_warning(Logger_, fmt::format("UNKNOWN({}): unknown WS Frame operation: {}",
CId_, std::to_string(Op)));
Errors_++;
return;
}
}
// Check for final frame and process accumulated payload
if (!KillConnection && (flags & Poco::Net::WebSocket::FRAME_FLAG_FIN) != 0) {
ProcessWSFinalPayload();
}
} catch (const Poco::Net::ConnectionResetException &E) {
poco_warning(Logger_,
fmt::format("ConnectionResetException({}): Text:{} Payload:{} Session:{}",
CId_, E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const Poco::JSON::JSONException &E) {
poco_warning(Logger_,
fmt::format("JSONException({}): Text:{} Payload:{} Session:{}", CId_,
E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const Poco::Net::WebSocketException &E) {
poco_warning(Logger_,
fmt::format("WebSocketException({}): Text:{} Payload:{} Session:{}", CId_,
E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const Poco::Net::SSLConnectionUnexpectedlyClosedException &E) {
poco_warning(
Logger_,
fmt::format(
"SSLConnectionUnexpectedlyClosedException({}): Text:{} Payload:{} Session:{}",
CId_, E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const Poco::Net::SSLException &E) {
poco_warning(Logger_,
fmt::format("SSLException({}): Text:{} Payload:{} Session:{}", CId_,
E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const Poco::Net::NetException &E) {
poco_warning(Logger_,
fmt::format("NetException({}): Text:{} Payload:{} Session:{}", CId_,
E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const Poco::IOException &E) {
poco_warning(Logger_,
fmt::format("IOException({}): Text:{} Payload:{} Session:{}", CId_,
E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const Poco::Exception &E) {
poco_warning(Logger_,
fmt::format("Exception({}): Text:{} Payload:{} Session:{}", CId_,
E.displayText(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (const std::exception &E) {
poco_warning(Logger_,
fmt::format("std::exception({}): Text:{} Payload:{} Session:{}", CId_,
E.what(),
CurrentFrame.begin() == nullptr ? "" : CurrentFrame.begin(),
State_.sessionId));
KillConnection=true;
} catch (...) {
poco_error(Logger_, fmt::format("UnknownException({}): Device must be disconnected. "
"Unknown exception. Session:{}",
CId_, State_.sessionId));
KillConnection=true;
}
if (!KillConnection && Errors_ < 10)
return;
poco_warning(Logger_,
fmt::format("DISCONNECTING({}): ConnectionException: {} Errors: {}",
CId_, KillConnection, Errors_ ));
EndConnection();
}
bool AP_WS_Connection::Send(const std::string &Payload) {
try {
size_t BytesSent = WS_->sendFrame(Payload.c_str(), (int)Payload.size());
/*
* There is a possibility to actually try and send data but the device is no longer
* listening. This code attempts to wait 5 seconds to see if the device is actually
* still listening. if the data is not acked under 5 seconds, then we consider that the
* data never made it or the device is disconnected somehow.
*/
#if defined(__APPLE__)
tcp_connection_info info;
int timeout = 4000;
auto expireAt = std::chrono::system_clock::now() + std::chrono::milliseconds(timeout);
do {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
socklen_t opt_len = sizeof(info);
getsockopt(WS_->impl()->sockfd(), SOL_SOCKET, TCP_CONNECTION_INFO, (void *)&info,
&opt_len);
} while (!info.tcpi_tfo_syn_data_acked && expireAt > std::chrono::system_clock::now());
if (!info.tcpi_tfo_syn_data_acked)
return false;
#else
tcp_info info;
int timeout = 4000;
auto expireAt = std::chrono::system_clock::now() + std::chrono::milliseconds(timeout);
do {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
socklen_t opt_len = sizeof(info);
getsockopt(WS_->impl()->sockfd(), SOL_TCP, TCP_INFO, (void *)&info, &opt_len);
} while (info.tcpi_unacked > 0 && expireAt > std::chrono::system_clock::now());
if (info.tcpi_unacked > 0) {
return false;
}
#endif
State_.TX += BytesSent;
AP_WS_Server()->AddTX(BytesSent);
return BytesSent == Payload.size();
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
std::string Base64Encode(const unsigned char *buffer, std::size_t size) {
return Utils::base64encode(buffer, size);
}
std::string Base64Decode(const std::string &F) {
std::istringstream ifs(F);
Poco::Base64Decoder b64in(ifs);
std::ostringstream ofs;
Poco::StreamCopier::copyStream(b64in, ofs);
return ofs.str();
}
bool AP_WS_Connection::SendRadiusAuthenticationData(const unsigned char *buffer,
std::size_t size) {
Poco::JSON::Object Answer;
Answer.set(uCentralProtocol::RADIUS, uCentralProtocol::RADIUSAUTH);
Answer.set(uCentralProtocol::RADIUSDATA, Base64Encode(buffer, size));
std::ostringstream Payload;
Answer.stringify(Payload);
return Send(Payload.str());
}
bool AP_WS_Connection::SendRadiusAccountingData(const unsigned char *buffer, std::size_t size) {
Poco::JSON::Object Answer;
Answer.set(uCentralProtocol::RADIUS, uCentralProtocol::RADIUSACCT);
Answer.set(uCentralProtocol::RADIUSDATA, Base64Encode(buffer, size));
std::ostringstream Payload;
Answer.stringify(Payload);
return Send(Payload.str());
}
bool AP_WS_Connection::SendRadiusCoAData(const unsigned char *buffer, std::size_t size) {
Poco::JSON::Object Answer;
Answer.set(uCentralProtocol::RADIUS, uCentralProtocol::RADIUSCOA);
Answer.set(uCentralProtocol::RADIUSDATA, Base64Encode(buffer, size));
std::ostringstream Payload;
Answer.stringify(Payload);
return Send(Payload.str());
}
void AP_WS_Connection::ProcessIncomingRadiusData(const Poco::JSON::Object::Ptr &Doc) {
if (Doc->has(uCentralProtocol::RADIUSDATA)) {
auto Type = Doc->get(uCentralProtocol::RADIUS).toString();
if (Type == uCentralProtocol::RADIUSACCT) {
auto Data = Doc->get(uCentralProtocol::RADIUSDATA).toString();
auto DecodedData = Base64Decode(Data);
RADIUS_proxy_server()->SendAccountingData(SerialNumber_, DecodedData.c_str(),
DecodedData.size());
} else if (Type == uCentralProtocol::RADIUSAUTH) {
auto Data = Doc->get(uCentralProtocol::RADIUSDATA).toString();
auto DecodedData = Base64Decode(Data);
RADIUS_proxy_server()->SendAuthenticationData(SerialNumber_, DecodedData.c_str(),
DecodedData.size());
} else if (Type == uCentralProtocol::RADIUSCOA) {
auto Data = Doc->get(uCentralProtocol::RADIUSDATA).toString();
auto DecodedData = Base64Decode(Data);
RADIUS_proxy_server()->SendCoAData(SerialNumber_, DecodedData.c_str(),
DecodedData.size());
}
}
}
void AP_WS_Connection::SetLastStats(const std::string &LastStats) {
RawLastStats_ = LastStats;
try {
Poco::JSON::Parser P;
auto Stats = P.parse(LastStats).extract<Poco::JSON::Object::Ptr>();
State_.hasGPS = Stats->isObject("gps");
auto Unit = Stats->getObject("unit");
auto Memory = Unit->getObject("memory");
std::uint64_t TotalMemory = Memory->get("total");
std::uint64_t FreeMemory = Memory->get("free");
if (TotalMemory > 0) {
State_.memoryUsed =
(100.0 * ((double)TotalMemory - (double)FreeMemory)) / (double)TotalMemory;
}
if (Unit->isArray("load")) {
Poco::JSON::Array::Ptr Load = Unit->getArray("load");
if (Load->size() > 1) {
State_.load = Load->get(1);
}
}
if (Unit->isArray("temperature")) {
Poco::JSON::Array::Ptr Temperature = Unit->getArray("temperature");
if (Temperature->size() > 1) {
State_.temperature = Temperature->get(0);
}
}
} catch (const Poco::Exception &E) {
poco_error(Logger_, "Failed to parse last stats: " + E.displayText());
}
}
} // namespace OpenWifi

View File

@@ -1,180 +0,0 @@
//
// Created by stephane bourque on 2022-02-03.
//
#pragma once
#include <mutex>
#include <string>
#include "Poco/JSON/Object.h"
#include <Poco/JSON/Parser.h>
#include "Poco/Logger.h"
#include "Poco/Net/SocketNotification.h"
#include "Poco/Net/SocketReactor.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/WebSocket.h"
#include <Poco/Data/Session.h>
#include "RESTObjects/RESTAPI_GWobjects.h"
#include <AP_WS_Reactor_Pool.h>
namespace OpenWifi {
class AP_WS_Connection {
static constexpr int BufSize = 512000;
public:
explicit AP_WS_Connection(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response, uint64_t connection_id,
Poco::Logger &L, std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> R);
~AP_WS_Connection();
void EndConnection();
void ProcessJSONRPCEvent(Poco::JSON::Object::Ptr &Doc);
void ProcessJSONRPCResult(Poco::JSON::Object::Ptr Doc);
void ProcessWSFinalPayload();
void ProcessIncomingFrame();
void ProcessIncomingRadiusData(const Poco::JSON::Object::Ptr &Doc);
[[nodiscard]] bool Send(const std::string &Payload);
[[nodiscard]] inline bool MustBeSecureRTTY() const { return RTTYMustBeSecure_; }
bool SendRadiusAuthenticationData(const unsigned char *buffer, std::size_t size);
bool SendRadiusAccountingData(const unsigned char *buffer, std::size_t size);
bool SendRadiusCoAData(const unsigned char *buffer, std::size_t size);
void OnSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf);
void OnSocketShutdown(const Poco::AutoPtr<Poco::Net::ShutdownNotification> &pNf);
void OnSocketError(const Poco::AutoPtr<Poco::Net::ErrorNotification> &pNf);
bool LookForUpgrade(Poco::Data::Session &Session, uint64_t UUID, uint64_t &UpgradedUUID);
void LogException(const Poco::Exception &E);
inline Poco::Logger &Logger() { return Logger_; }
bool SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t interval,
uint64_t TelemetryWebSocketTimer,
const std::vector<std::string> &TelemetryTypes);
bool SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t interval,
uint64_t TelemetryKafkaTimer,
const std::vector<std::string> &TelemetryTypes);
bool StopWebSocketTelemetry(uint64_t RPCID);
bool StopKafkaTelemetry(uint64_t RPCID);
inline void GetLastStats(std::string &LastStats) {
if(!Dead_) {
std::lock_guard G(ConnectionMutex_);
LastStats = RawLastStats_;
}
}
inline void GetLastHealthCheck(GWObjects::HealthCheck &H) {
if(!Dead_) {
std::lock_guard G(ConnectionMutex_);
H = RawLastHealthcheck_;
}
}
inline void GetState(GWObjects::ConnectionState &State) {
if(!Dead_) {
std::lock_guard G(ConnectionMutex_);
State = State_;
}
}
inline GWObjects::DeviceRestrictions GetRestrictions() {
std::lock_guard G(ConnectionMutex_);
return Restrictions_;
}
[[nodiscard]] inline bool HasGPS() const { return hasGPS_; }
[[nodiscard]] bool ValidatedDevice();
inline bool GetTelemetryParameters(bool &Reporting, uint64_t &Interval,
uint64_t &WebSocketTimer, uint64_t &KafkaTimer,
uint64_t &WebSocketCount, uint64_t &KafkaCount,
uint64_t &WebSocketPackets,
uint64_t &KafkaPackets) const {
Reporting = TelemetryReporting_;
WebSocketTimer = TelemetryWebSocketTimer_;
KafkaTimer = TelemetryKafkaTimer_;
WebSocketCount = TelemetryWebSocketRefCount_;
KafkaCount = TelemetryKafkaRefCount_;
Interval = TelemetryInterval_;
WebSocketPackets = TelemetryWebSocketPackets_;
KafkaPackets = TelemetryKafkaPackets_;
return true;
}
friend class AP_WS_Server;
void Start();
private:
mutable std::recursive_mutex ConnectionMutex_;
std::mutex TelemetryMutex_;
Poco::Logger &Logger_;
std::shared_ptr<Poco::Net::SocketReactor> Reactor_;
std::shared_ptr<LockedDbSession> DbSession_;
std::unique_ptr<Poco::Net::WebSocket> WS_;
std::string SerialNumber_;
uint64_t SerialNumberInt_ = 0;
std::string Compatible_;
std::atomic_bool Registered_ = false;
std::string CId_;
std::string CN_;
uint64_t Errors_ = 0;
Poco::Net::IPAddress PeerAddress_;
volatile bool TelemetryReporting_ = false;
std::atomic_uint64_t TelemetryWebSocketRefCount_ = 0;
std::atomic_uint64_t TelemetryKafkaRefCount_ = 0;
std::atomic_uint64_t TelemetryWebSocketTimer_ = 0;
std::atomic_uint64_t TelemetryKafkaTimer_ = 0;
std::atomic_uint64_t TelemetryInterval_ = 0;
std::atomic_uint64_t TelemetryWebSocketPackets_ = 0;
std::atomic_uint64_t TelemetryKafkaPackets_ = 0;
GWObjects::ConnectionState State_;
Utils::CompressedString RawLastStats_;
GWObjects::HealthCheck RawLastHealthcheck_;
std::chrono::time_point<std::chrono::high_resolution_clock> ConnectionStart_ =
std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> ConnectionCompletionTime_{0.0};
std::atomic<bool> Dead_ = false;
std::atomic_bool DeviceValidated_ = false;
OpenWifi::GWObjects::DeviceRestrictions Restrictions_;
bool RTTYMustBeSecure_ = false;
bool hasGPS_=false;
std::double_t memory_used_=0.0, cpu_load_ = 0.0, temperature_ = 0.0;
std::uint64_t uuid_=0;
bool Simulated_=false;
std::atomic_uint64_t LastContact_=0;
Poco::Buffer<char> IncomingFrame_;
static inline std::atomic_uint64_t ConcurrentStartingDevices_ = 0;
bool StartTelemetry(uint64_t RPCID, const std::vector<std::string> &TelemetryTypes);
bool StopTelemetry(uint64_t RPCID);
void UpdateCounts();
static void DeviceDisconnectionCleanup(const std::string &SerialNumber, std::uint64_t uuid);
void SetLastStats(const std::string &LastStats);
void Process_connect(Poco::JSON::Object::Ptr ParamsObj, const std::string &Serial);
void Process_state(Poco::JSON::Object::Ptr ParamsObj);
void Process_healthcheck(Poco::JSON::Object::Ptr ParamsObj);
void Process_log(Poco::JSON::Object::Ptr ParamsObj);
void Process_crashlog(Poco::JSON::Object::Ptr ParamsObj);
void Process_ping(Poco::JSON::Object::Ptr ParamsObj);
void Process_cfgpending(Poco::JSON::Object::Ptr ParamsObj);
void Process_recovery(Poco::JSON::Object::Ptr ParamsObj);
void Process_deviceupdate(Poco::JSON::Object::Ptr ParamsObj, std::string &Serial);
void Process_telemetry(Poco::JSON::Object::Ptr ParamsObj);
void Process_venuebroadcast(Poco::JSON::Object::Ptr ParamsObj);
void Process_event(Poco::JSON::Object::Ptr ParamsObj);
void Process_wifiscan(Poco::JSON::Object::Ptr ParamsObj);
void Process_alarm(Poco::JSON::Object::Ptr ParamsObj);
void Process_rebootLog(Poco::JSON::Object::Ptr ParamsObj);
inline void SetLastHealthCheck(const GWObjects::HealthCheck &H) {
RawLastHealthcheck_ = H;
}
};
} // namespace OpenWifi

View File

@@ -1,111 +0,0 @@
#include <AP_WS_Connection.h>
#include "ConfigurationCache.h"
#include "UI_GW_WebSocketNotifications.h"
#include "CommandManager.h"
namespace OpenWifi {
bool AP_WS_Connection::LookForUpgrade(Poco::Data::Session &Session, const uint64_t UUID, uint64_t &UpgradedUUID) {
// A UUID of zero means ignore updates for that connection.
if (UUID == 0)
return false;
uint64_t GoodConfig = GetCurrentConfigurationID(SerialNumberInt_);
if (GoodConfig && (GoodConfig == UUID || GoodConfig == State_.PendingUUID)) {
UpgradedUUID = UUID;
State_.PendingUUID = 0;
return false;
}
GWObjects::Device D;
if (!StorageService()->GetDevice(Session,SerialNumber_, D)) {
return false;
}
if(State_.PendingUUID!=0 && UUID==State_.PendingUUID) {
// so we sent an upgrade to a device, and now it is completing now...
UpgradedUUID = UUID;
StorageService()->CompleteDeviceConfigurationChange(Session, SerialNumber_);
State_.PendingUUID = 0;
return true;
}
// dont upgrade a switch if it does not have a real config. Config will always be more than 20 characters
if (D.DeviceType==Platforms::SWITCH && D.Configuration.size()<20) {
return false;
}
Config::Config Cfg(D.Configuration);
// if this is a broken device (UUID==0) just fix it
auto StoredConfigurationUUID = Cfg.UUID();
if(D.UUID==0) {
D.UUID = StoredConfigurationUUID;
}
if (D.UUID == UUID) {
D.UUID = UpgradedUUID = UUID;
State_.PendingUUID = D.pendingUUID = 0;
D.pendingConfiguration.clear();
D.pendingConfigurationCmd.clear();
StorageService()->UpdateDevice(Session, D);
SetCurrentConfigurationID(SerialNumberInt_, UUID);
// std::cout << __LINE__ << ": " << SerialNumber_ << " GoodConfig: " << GoodConfig << " UUID:" << UUID << " Pending:" << State_.PendingUUID << std::endl;
return false;
}
if (UUID > D.UUID) {
// so we have a problem, the device has a newer config than we have. So we need to
// make sure our config is newer.
D.UUID = UUID + 2;
UpgradedUUID = D.UUID;
// std::cout << __LINE__ << ": " << SerialNumber_ << " GoodConfig: " << GoodConfig << " UUID:" << UUID << " Pending:" << State_.PendingUUID << std::endl;
}
Cfg.SetUUID(D.UUID);
D.Configuration = Cfg.get();
D.pendingUUID = State_.PendingUUID = UpgradedUUID = D.UUID;
StorageService()->UpdateDevice(Session, D);
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroServiceCreateUUID();
Cmd.SubmittedBy = uCentralProtocol::SUBMITTED_BY_SYSTEM;
Cmd.Status = uCentralProtocol::PENDING;
Cmd.Command = uCentralProtocol::CONFIGURE;
Poco::JSON::Parser P;
auto ParsedConfig = P.parse(D.Configuration).extract<Poco::JSON::Object::Ptr>();
Poco::JSON::Object Params;
Params.set(uCentralProtocol::SERIAL, SerialNumber_);
Params.set(uCentralProtocol::UUID, D.UUID);
Params.set(uCentralProtocol::WHEN, 0);
Params.set(uCentralProtocol::CONFIG, ParsedConfig);
std::ostringstream O;
Poco::JSON::Stringifier::stringify(Params, O);
Cmd.Details = O.str();
poco_information(Logger_,
fmt::format("CFG-UPGRADE({}): Current ID: {}, newer configuration {}.",
CId_, UUID, D.UUID));
bool Sent;
StorageService()->AddCommand(SerialNumber_, Cmd,
Storage::CommandExecutionType::COMMAND_EXECUTED);
CommandManager()->PostCommand(
CommandManager()->Next_RPC_ID(), APCommands::to_apcommand(Cmd.Command.c_str()),
SerialNumber_, Cmd.Command, Params, Cmd.UUID, Sent, false, false);
GWWebSocketNotifications::SingleDeviceConfigurationChange_t Notification;
Notification.content.serialNumber = D.SerialNumber;
Notification.content.oldUUID = UUID;
Notification.content.newUUID = UpgradedUUID;
GWWebSocketNotifications::DeviceConfigurationChange(Notification);
// std::cout << __LINE__ << ": " << SerialNumber_ << " GoodConfig: " << GoodConfig << " UUID:" << UUID <<
// " Pending:" << State_.PendingUUID << " Upgraded:" << UpgradedUUID << std::endl;
return true;
}
}

View File

@@ -1,28 +0,0 @@
//
// Created by stephane bourque on 2023-01-22.
//
#include "AP_WS_Connection.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/KafkaManager.h"
#include "framework/ow_constants.h"
namespace OpenWifi {
void AP_WS_Connection::Process_alarm(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
poco_trace(Logger_, fmt::format("Alarm data received for {}", SerialNumber_));
if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
if (KafkaManager()->Enabled()) {
KafkaManager()->PostMessage(KafkaTopics::ALERTS, SerialNumber_, *ParamsObj);
}
}
}
} // namespace OpenWifi

View File

@@ -1,28 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "fmt/format.h"
#include "framework/ow_constants.h"
namespace OpenWifi {
void AP_WS_Connection::Process_cfgpending(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::ACTIVE)) {
[[maybe_unused]] uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
[[maybe_unused]] uint64_t Active = ParamsObj->get(uCentralProtocol::ACTIVE);
poco_trace(Logger_,
fmt::format("CFG-PENDING({}): Active: {} Target: {}", CId_, Active, UUID));
} else {
poco_warning(Logger_, fmt::format("CFG-PENDING({}): Missing some parameters", CId_));
}
}
} // namespace OpenWifi

View File

@@ -1,307 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "AP_WS_Server.h"
#include "CentralConfig.h"
#include "Daemon.h"
#include "FindCountry.h"
#include "StorageService.h"
#include "CommandManager.h"
#include "framework/KafkaManager.h"
#include "framework/utils.h"
#include "firmware_revision_cache.h"
#include "UI_GW_WebSocketNotifications.h"
#include <GWKafkaEvents.h>
namespace OpenWifi {
[[maybe_unused]] static void SendKafkaFirmwareUpdate(const std::string &SerialNumber,
const std::string &OldFirmware,
const std::string &NewFirmware) {
if (KafkaManager()->Enabled()) {
Poco::JSON::Object EventDetails;
EventDetails.set("oldFirmware", OldFirmware);
EventDetails.set("newFirmware", NewFirmware);
Poco::JSON::Object Event;
Event.set("type", "device.firmware_change");
Event.set("timestamp", Utils::Now());
Event.set("payload", EventDetails);
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
}
}
[[maybe_unused]] static void SendKafkaDeviceNotProvisioned( const std::string &SerialNumber,
const std::string &Firmware,
const std::string &DeviceType,
const std::string &IP) {
if (KafkaManager()->Enabled()) {
Poco::JSON::Object EventDetails;
EventDetails.set("firmware", Firmware);
EventDetails.set("deviceType", DeviceType);
EventDetails.set("IP", IP);
Poco::JSON::Object Event;
Event.set("type", "device.not_provisioned");
Event.set("timestamp", Utils::Now());
Event.set("payload", EventDetails);
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
}
}
void AP_WS_Connection::Process_connect(Poco::JSON::Object::Ptr ParamsObj,
const std::string &Serial) {
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::FIRMWARE) &&
ParamsObj->has(uCentralProtocol::CAPABILITIES)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
auto Firmware = ParamsObj->get(uCentralProtocol::FIRMWARE).toString();
auto Capabilities = ParamsObj->getObject(uCentralProtocol::CAPABILITIES);
std::string DevicePassword;
if(ParamsObj->has("password")) {
DevicePassword = ParamsObj->get("password").toString();
}
SerialNumber_ = Serial;
SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_);
CommandManager()->ClearQueue(SerialNumberInt_);
AP_WS_Server()->StartSession(State_.sessionId, SerialNumberInt_);
Config::Capabilities Caps(Capabilities);
Compatible_ = Caps.Compatible();
State_.UUID = UUID;
State_.Firmware = Firmware;
State_.PendingUUID = 0;
State_.Address = Utils::FormatIPv6(WS_->peerAddress().toString());
CId_ = SerialNumber_ + "@" + CId_;
auto Platform = Poco::toLower(Caps.Platform());
if(ParamsObj->has("reason")) {
State_.connectReason = ParamsObj->get("reason").toString();
}
auto IP = PeerAddress_.toString();
if (IP.substr(0, 7) == "::ffff:") {
IP = IP.substr(7);
}
bool RestrictedDevice = false;
if (Capabilities->has("restrictions")) {
RestrictedDevice = true;
Poco::JSON::Object::Ptr RestrictionObject = Capabilities->getObject("restrictions");
Restrictions_.from_json(RestrictionObject);
}
if (Capabilities->has("developer") && !Capabilities->isNull("developer")) {
Restrictions_.developer = Capabilities->getValue<bool>("developer");
}
if (Capabilities->has("secure-rtty")) {
RTTYMustBeSecure_ = Capabilities->getValue<bool>("secure-rtty");
}
State_.locale = FindCountryFromIP()->Get(IP);
GWObjects::Device DeviceInfo;
std::lock_guard DbSessionLock(DbSession_->Mutex());
auto DeviceExists = StorageService()->GetDevice(DbSession_->Session(), SerialNumber_, DeviceInfo);
if (Daemon()->AutoProvisioning() && !DeviceExists) {
// check the firmware version. if this is too old, we cannot let that device connect yet, we must
// force a firmware upgrade
GWObjects::DefaultFirmware MinimumFirmware;
if(FirmwareRevisionCache()->DeviceMustUpgrade(Compatible_, Firmware, MinimumFirmware)) {
Poco::JSON::Object UpgradeCommand, Params;
UpgradeCommand.set(uCentralProtocol::JSONRPC,uCentralProtocol::JSONRPC_VERSION);
UpgradeCommand.set(uCentralProtocol::METHOD,uCentralProtocol::UPGRADE);
Params.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
Params.set(uCentralProtocol::WHEN, 0);
Params.set(uCentralProtocol::URI, MinimumFirmware.uri);
Params.set(uCentralProtocol::KEEP_REDIRECTOR,1);
UpgradeCommand.set(uCentralProtocol::PARAMS, Params);
UpgradeCommand.set(uCentralProtocol::ID, 1);
std::ostringstream Command;
UpgradeCommand.stringify(Command);
if(Send(Command.str())) {
poco_information(
Logger(),
fmt::format(
"Forcing device {} to upgrade to {} before connection is allowed.",
SerialNumber_, MinimumFirmware.revision));
} else {
poco_error(
Logger(),
fmt::format(
"Could not force device {} to upgrade to {} before connection is allowed.",
SerialNumber_, MinimumFirmware.revision));
}
return;
} else {
StorageService()->CreateDefaultDevice( DbSession_->Session(),
SerialNumber_, Caps, Firmware, PeerAddress_,
State_.VerifiedCertificate == GWObjects::SIMULATED);
}
} else if (!Daemon()->AutoProvisioning() && !DeviceExists) {
SendKafkaDeviceNotProvisioned(SerialNumber_, Firmware, Compatible_, CId_);
poco_warning(Logger(),fmt::format("Device {} is a {} from {} and cannot be provisioned.",SerialNumber_,Compatible_, CId_));
return EndConnection();
} else if (DeviceExists) {
StorageService()->UpdateDeviceCapabilities(DbSession_->Session(), SerialNumber_, Caps);
int Updated{0};
if (!Firmware.empty()) {
if (Firmware != DeviceInfo.Firmware) {
DeviceFirmwareChangeKafkaEvent KEvent(SerialNumberInt_, Utils::Now(),
DeviceInfo.Firmware, Firmware);
DeviceInfo.Firmware = Firmware;
DeviceInfo.LastFWUpdate = Utils::Now();
++Updated;
GWWebSocketNotifications::SingleDeviceFirmwareChange_t Notification;
Notification.content.serialNumber = SerialNumber_;
Notification.content.newFirmware = Firmware;
GWWebSocketNotifications::DeviceFirmwareUpdated(Notification);
} else if (DeviceInfo.LastFWUpdate == 0) {
DeviceInfo.LastFWUpdate = Utils::Now();
++Updated;
}
}
if(ParamsObj->has("reason")) {
State_.connectReason = ParamsObj->get("reason").toString();
DeviceInfo.connectReason = State_.connectReason;
++Updated;
}
if(DeviceInfo.DevicePassword!=DevicePassword) {
DeviceInfo.DevicePassword = DevicePassword.empty() ? "openwifi" : DevicePassword ;
++Updated;
}
if (DeviceInfo.lastRecordedContact==0) {
DeviceInfo.lastRecordedContact = Utils::Now();
++Updated;
}
if (DeviceInfo.simulated && (State_.VerifiedCertificate!=GWObjects::SIMULATED)) {
DeviceInfo.simulated = false;
++Updated;
}
if (!DeviceInfo.simulated && (State_.VerifiedCertificate==GWObjects::SIMULATED)) {
DeviceInfo.simulated = true;
++Updated;
}
if (DeviceInfo.locale != State_.locale) {
DeviceInfo.locale = State_.locale;
++Updated;
}
if (Compatible_ != DeviceInfo.Compatible) {
DeviceInfo.Compatible = Compatible_;
++Updated;
}
if (Platform != DeviceInfo.DeviceType) {
DeviceInfo.DeviceType = Platform;
++Updated;
}
if (RestrictedDevice != DeviceInfo.restrictedDevice) {
DeviceInfo.restrictedDevice = RestrictedDevice;
++Updated;
}
if (Restrictions_ != DeviceInfo.restrictionDetails) {
DeviceInfo.restrictionDetails = Restrictions_;
++Updated;
}
if(DeviceInfo.certificateExpiryDate!=State_.certificateExpiryDate) {
DeviceInfo.certificateExpiryDate = State_.certificateExpiryDate;
++Updated;
}
if (Updated) {
StorageService()->UpdateDevice(DbSession_->Session(), DeviceInfo);
}
}
if(!Simulated_) {
uint64_t UpgradedUUID = 0;
if (LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID)) {
State_.UUID = UpgradedUUID;
}
}
State_.Compatible = Compatible_;
State_.Connected = true;
ConnectionCompletionTime_ =
std::chrono::high_resolution_clock::now() - ConnectionStart_;
State_.connectionCompletionTime = ConnectionCompletionTime_.count();
if (State_.VerifiedCertificate == GWObjects::VALID_CERTIFICATE) {
if ((Utils::SerialNumberMatch(CN_, SerialNumber_,
(int)AP_WS_Server()->MismatchDepth())) ||
AP_WS_Server()->IsSimSerialNumber(CN_)) {
State_.VerifiedCertificate = GWObjects::VERIFIED;
poco_information(Logger_,
fmt::format("CONNECT({}): Fully validated and authenticated "
"device. Session={} ConnectionCompletion Time={}",
CId_, State_.sessionId,
State_.connectionCompletionTime));
} else {
State_.VerifiedCertificate = GWObjects::MISMATCH_SERIAL;
if (AP_WS_Server()->AllowSerialNumberMismatch()) {
poco_information(
Logger_,
fmt::format("CONNECT({}): Serial number mismatch allowed. CN={} "
"Serial={} Session={} ConnectionCompletion Time={}",
CId_, CN_, SerialNumber_, State_.sessionId,
State_.connectionCompletionTime));
} else {
poco_information(
Logger_, fmt::format("CONNECT({}): Serial number mismatch disallowed. "
"Device rejected. CN={} Serial={} Session={}",
CId_, CN_, SerialNumber_, State_.sessionId));
return EndConnection();
}
}
} else {
poco_information(Logger_,
fmt::format("CONNECT({}): Simulator device. "
"Session={} ConnectionCompletion Time={}",
CId_, State_.sessionId,
State_.connectionCompletionTime));
}
GWWebSocketNotifications::SingleDevice_t Notification;
Notification.content.serialNumber = SerialNumber_;
GWWebSocketNotifications::DeviceConnected(Notification);
if (KafkaManager()->Enabled()) {
ParamsObj->set(uCentralProtocol::CONNECTIONIP, CId_);
ParamsObj->set("locale", State_.locale);
ParamsObj->set(uCentralProtocol::TIMESTAMP, Utils::Now());
ParamsObj->set(uCentralProtocol::UUID, uuid_);
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, *ParamsObj);
}
} else {
poco_warning(
Logger_,
fmt::format("INVALID-PROTOCOL({}): Missing one of uuid, firmware, or capabilities",
CId_));
Errors_++;
}
}
} // namespace OpenWifi

View File

@@ -1,39 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/ow_constants.h"
#include <GWKafkaEvents.h>
namespace OpenWifi {
void AP_WS_Connection::Process_crashlog(Poco::JSON::Object::Ptr ParamsObj) {
if (ParamsObj->has(uCentralProtocol::UUID)
&& ParamsObj->has(uCentralProtocol::LOGLINES)) {
poco_trace(Logger_, fmt::format("CRASH-LOG({}): new entry.", CId_));
auto LogLines = ParamsObj->get(uCentralProtocol::LOGLINES);
std::string LogText;
if (LogLines.isArray()) {
auto LogLinesArray = LogLines.extract<Poco::JSON::Array::Ptr>();
for (const auto &i : *LogLinesArray)
LogText += i.toString() + "\r\n";
}
GWObjects::DeviceLog DeviceLog{.SerialNumber = SerialNumber_,
.Log = LogText,
.Data = "",
.Severity = GWObjects::DeviceLog::LOG_EMERG,
.Recorded = Utils::Now(),
.LogType = 1,
.UUID = ParamsObj->get(uCentralProtocol::UUID)};
StorageService()->AddLog(*DbSession_, DeviceLog);
DeviceLogKafkaEvent E(DeviceLog);
} else {
poco_warning(Logger_, fmt::format("LOG({}): Missing parameters.", CId_));
return;
}
}
} // namespace OpenWifi

View File

@@ -1,31 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "StorageService.h"
#include "fmt/format.h"
namespace OpenWifi {
void AP_WS_Connection::Process_deviceupdate(Poco::JSON::Object::Ptr ParamsObj,
std::string &Serial) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
if (ParamsObj->has("currentPassword")) {
auto Password = ParamsObj->get("currentPassword").toString();
StorageService()->SetDevicePassword(*DbSession_,Serial, Password);
poco_trace(
Logger_,
fmt::format("DEVICE-UPDATE({}): Device is updating its login password.", Serial));
}
}
} // namespace OpenWifi

View File

@@ -1,51 +0,0 @@
//
// Created by stephane bourque on 2023-01-22.
//
#include "AP_WS_Connection.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/KafkaManager.h"
#include "framework/ow_constants.h"
namespace OpenWifi {
void AP_WS_Connection::Process_event(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
poco_trace(Logger_, fmt::format("Event data received for {}", SerialNumber_));
try {
if (ParamsObj->has(uCentralProtocol::SERIAL) &&
ParamsObj->has(uCentralProtocol::DATA)) {
if (KafkaManager()->Enabled()) {
auto Data = ParamsObj->getObject(uCentralProtocol::DATA);
auto Event = Data->getArray("event");
auto EventTimeStamp = Event->getElement<std::uint64_t>(0);
auto EventDetails = Event->getObject(1);
auto EventType = EventDetails->get("type").extract<std::string>();
auto EventPayload = EventDetails->getObject("payload");
Poco::JSON::Object FullEvent;
FullEvent.set("type", EventType);
FullEvent.set("timestamp", EventTimeStamp);
FullEvent.set("payload", EventPayload);
if(strncmp(EventType.c_str(),"rrm.",4) == 0 ) {
KafkaManager()->PostMessage(KafkaTopics::RRM, SerialNumber_,
FullEvent);
} else {
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber_,
FullEvent);
}
}
}
} catch (const Poco::Exception &E) {
Logger_.log(E);
} catch (...) {
}
}
} // namespace OpenWifi

View File

@@ -1,69 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "AP_WS_Server.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/KafkaManager.h"
#include "framework/utils.h"
namespace OpenWifi {
void AP_WS_Connection::Process_healthcheck(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
if (ParamsObj->has(uCentralProtocol::UUID) &&
ParamsObj->has(uCentralProtocol::SANITY) &&
ParamsObj->has(uCentralProtocol::DATA)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
auto Sanity = ParamsObj->get(uCentralProtocol::SANITY);
State_.sanity = Sanity;
auto CheckData = ParamsObj->get(uCentralProtocol::DATA).toString();
if (CheckData.empty())
CheckData = uCentralProtocol::EMPTY_JSON_DOC;
std::string request_uuid;
if (ParamsObj->has(uCentralProtocol::REQUEST_UUID))
request_uuid = ParamsObj->get(uCentralProtocol::REQUEST_UUID).toString();
if (request_uuid.empty()) {
poco_trace(Logger_, fmt::format("HEALTHCHECK({}): UUID={} Updating.", CId_, UUID));
} else {
poco_trace(Logger_, fmt::format("HEALTHCHECK({}): UUID={} Updating for CMD={}.",
CId_, UUID, request_uuid));
}
GWObjects::HealthCheck Check;
Check.SerialNumber = SerialNumber_;
Check.Recorded = Utils::Now();
Check.UUID = UUID;
Check.Data = CheckData;
Check.Sanity = Sanity;
StorageService()->AddHealthCheckData(*DbSession_, Check);
if (!request_uuid.empty()) {
StorageService()->SetCommandResult(request_uuid, CheckData);
}
SetLastHealthCheck(Check);
if (KafkaManager()->Enabled() && !AP_WS_Server()->KafkaDisableHealthChecks()) {
KafkaManager()->PostMessage(KafkaTopics::HEALTHCHECK, SerialNumber_, *ParamsObj);
}
} else {
poco_warning(Logger_, fmt::format("HEALTHCHECK({}): Missing parameter", CId_));
return;
}
}
} // namespace OpenWifi

View File

@@ -1,46 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/ow_constants.h"
#include <GWKafkaEvents.h>
namespace OpenWifi {
void AP_WS_Connection::Process_log(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
if (ParamsObj->has(uCentralProtocol::LOG) && ParamsObj->has(uCentralProtocol::SEVERITY)) {
poco_trace(Logger_, fmt::format("LOG({}): new entry.", CId_));
auto Log = ParamsObj->get(uCentralProtocol::LOG).toString();
auto Severity = ParamsObj->get(uCentralProtocol::SEVERITY);
std::string DataStr = uCentralProtocol::EMPTY_JSON_DOC;
if (ParamsObj->has(uCentralProtocol::DATA)) {
auto DataObj = ParamsObj->get(uCentralProtocol::DATA);
if (DataObj.isStruct())
DataStr = DataObj.toString();
}
GWObjects::DeviceLog DeviceLog{.SerialNumber = SerialNumber_,
.Log = Log,
.Data = DataStr,
.Severity = Severity,
.Recorded = (uint64_t)time(nullptr),
.LogType = 0,
.UUID = State_.UUID};
StorageService()->AddLog(*DbSession_, DeviceLog);
DeviceLogKafkaEvent E(DeviceLog);
} else {
poco_warning(Logger_, fmt::format("LOG({}): Missing parameters.", CId_));
return;
}
}
} // namespace OpenWifi

View File

@@ -1,18 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "fmt/format.h"
#include "framework/ow_constants.h"
namespace OpenWifi {
void AP_WS_Connection::Process_ping(Poco::JSON::Object::Ptr ParamsObj) {
if (ParamsObj->has(uCentralProtocol::UUID)) {
[[maybe_unused]] uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
poco_trace(Logger_, fmt::format("PING({}): Current config is {}", CId_, UUID));
} else {
poco_warning(Logger_, fmt::format("PING({}): Missing parameter.", CId_));
}
}
} // namespace OpenWifi

View File

@@ -1,44 +0,0 @@
//
// Created by stephane bourque on 2023-05-16.
//
#include "AP_WS_Connection.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/ow_constants.h"
#include <GWKafkaEvents.h>
namespace OpenWifi {
void StripNulls(std::string &S) {
for(std::size_t i=0;i<S.size();++i) {
if(S[i]==0)
S[i]=' ';
}
}
void AP_WS_Connection::Process_rebootLog(Poco::JSON::Object::Ptr ParamsObj) {
if (ParamsObj->has(uCentralProtocol::UUID)
&& ParamsObj->isArray(uCentralProtocol::INFO)
&& ParamsObj->has(uCentralProtocol::TYPE)
&& ParamsObj->has(uCentralProtocol::DATE) ) {
poco_warning(Logger_, fmt::format("REBOOT-LOG({}): new entry.", CId_));
auto InfoLines = ParamsObj->getArray(uCentralProtocol::INFO);
std::ostringstream os;
InfoLines->stringify(os);
GWObjects::DeviceLog DeviceLog{.SerialNumber = SerialNumber_,
.Log = ParamsObj->get(uCentralProtocol::TYPE).toString(),
.Data = "{ \"info\" : " + os.str() + "}",
.Severity = GWObjects::DeviceLog::LOG_INFO,
.Recorded = ParamsObj->get(uCentralProtocol::DATE),
.LogType = 2,
.UUID = ParamsObj->get(uCentralProtocol::UUID)};
StorageService()->AddLog(*DbSession_, DeviceLog);
DeviceLogKafkaEvent E(DeviceLog);
} else {
poco_warning(Logger_, fmt::format("REBOOT-LOG({}): Missing parameters.", CId_));
}
}
} // namespace OpenWifi

View File

@@ -1,74 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "CommandManager.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/MicroServiceFuncs.h"
#include "framework/ow_constants.h"
namespace OpenWifi {
void AP_WS_Connection::Process_recovery(Poco::JSON::Object::Ptr ParamsObj) {
if (ParamsObj->has(uCentralProtocol::SERIAL) &&
ParamsObj->has(uCentralProtocol::FIRMWARE) && ParamsObj->has(uCentralProtocol::UUID) &&
ParamsObj->has(uCentralProtocol::REBOOT) &&
ParamsObj->has(uCentralProtocol::LOGLINES)) {
auto LogLines = ParamsObj->get(uCentralProtocol::LOGLINES);
std::string LogText;
LogText = "Firmware: " + ParamsObj->get(uCentralProtocol::FIRMWARE).toString() + "\r\n";
if (LogLines.isArray()) {
auto LogLinesArray = LogLines.extract<Poco::JSON::Array::Ptr>();
for (const auto &i : *LogLinesArray)
LogText += i.toString() + "\r\n";
}
GWObjects::DeviceLog DeviceLog{.SerialNumber = SerialNumber_,
.Log = LogText,
.Data = "",
.Severity = GWObjects::DeviceLog::LOG_EMERG,
.Recorded = (uint64_t)time(nullptr),
.LogType = 1,
.UUID = 0};
StorageService()->AddLog(*DbSession_, DeviceLog);
if (ParamsObj->get(uCentralProtocol::REBOOT).toString() == "true") {
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroServiceCreateUUID();
Cmd.SubmittedBy = uCentralProtocol::SUBMITTED_BY_SYSTEM;
Cmd.Status = uCentralProtocol::PENDING;
Cmd.Command = uCentralProtocol::REBOOT;
Poco::JSON::Object Params;
Params.set(uCentralProtocol::SERIAL, SerialNumber_);
Params.set(uCentralProtocol::WHEN, 0);
std::ostringstream O;
Poco::JSON::Stringifier::stringify(Params, O);
Cmd.Details = O.str();
bool Sent;
CommandManager()->PostCommand(CommandManager()->Next_RPC_ID(),
APCommands::Commands::reboot, SerialNumber_,
Cmd.Command, Params, Cmd.UUID, Sent, false, false);
StorageService()->AddCommand(SerialNumber_, Cmd,
Storage::CommandExecutionType::COMMAND_EXECUTED);
poco_information(
Logger_,
fmt::format("RECOVERY({}): Recovery mode received, need for a reboot.", CId_));
} else {
poco_information(
Logger_,
fmt::format("RECOVERY({}): Recovery mode received, no need for a reboot.",
CId_));
}
} else {
poco_warning(Logger_, fmt::format("RECOVERY({}): Recovery missing one of serialnumber, "
"firmware, uuid, loglines, reboot",
CId_));
}
}
} // namespace OpenWifi

View File

@@ -1,77 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "AP_WS_Server.h"
#include "StateUtils.h"
#include "StorageService.h"
#include "UI_GW_WebSocketNotifications.h"
#include "framework/KafkaManager.h"
#include "framework/utils.h"
#include "fmt/format.h"
namespace OpenWifi {
void AP_WS_Connection::Process_state(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::STATE)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
auto StateStr = ParamsObj->get(uCentralProtocol::STATE).toString();
auto StateObj = ParamsObj->getObject(uCentralProtocol::STATE);
std::string request_uuid;
if (ParamsObj->has(uCentralProtocol::REQUEST_UUID))
request_uuid = ParamsObj->get(uCentralProtocol::REQUEST_UUID).toString();
if (request_uuid.empty()) {
poco_trace(Logger_, fmt::format("STATE({}): UUID={} Updating.", CId_, UUID));
} else {
poco_trace(Logger_, fmt::format("STATE({}): UUID={} Updating for CMD={}.", CId_,
UUID, request_uuid));
}
std::lock_guard Guard(DbSession_->Mutex());
if(!Simulated_) {
uint64_t UpgradedUUID;
LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID);
State_.UUID = UpgradedUUID;
}
SetLastStats(StateStr);
GWObjects::Statistics Stats{
.SerialNumber = SerialNumber_, .UUID = UUID, .Data = StateStr};
Stats.Recorded = Utils::Now();
StorageService()->AddStatisticsData(DbSession_->Session(),Stats);
if (!request_uuid.empty()) {
StorageService()->SetCommandResult(request_uuid, StateStr);
}
StateUtils::ComputeAssociations(StateObj, State_.Associations_2G,
State_.Associations_5G, State_.Associations_6G, State_.uptime);
if (KafkaManager()->Enabled() && !AP_WS_Server()->KafkaDisableState()) {
KafkaManager()->PostMessage(KafkaTopics::STATE, SerialNumber_, *ParamsObj);
}
GWWebSocketNotifications::SingleDevice_t N;
N.content.serialNumber = SerialNumber_;
GWWebSocketNotifications::DeviceStatistics(N);
} else {
poco_warning(
Logger_,
fmt::format("STATE({}): Invalid request. Missing serial, uuid, or state", CId_));
}
}
} // namespace OpenWifi

View File

@@ -1,67 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "CommandManager.h"
#include "TelemetryStream.h"
#include "fmt/format.h"
#include "framework/KafkaManager.h"
#include "framework/utils.h"
namespace OpenWifi {
void AP_WS_Connection::Process_telemetry(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
poco_trace(Logger_, fmt::format("Telemetry data received for {}", SerialNumber_));
if (TelemetryReporting_ || ParamsObj->has("adhoc")) {
if (ParamsObj->has("data")) {
auto Payload = ParamsObj->get("data").extract<Poco::JSON::Object::Ptr>();
Payload->set("timestamp", Utils::Now());
std::ostringstream SS;
Payload->stringify(SS);
auto now = Utils::Now();
auto KafkaPayload = SS.str();
if (ParamsObj->has("adhoc")) {
KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
KafkaPayload);
return;
}
if (TelemetryWebSocketRefCount_) {
if (now < TelemetryWebSocketTimer_) {
TelemetryWebSocketPackets_++;
State_.websocketPackets = TelemetryWebSocketPackets_;
TelemetryStream()->NotifyEndPoint(SerialNumberInt_, KafkaPayload);
} else {
StopWebSocketTelemetry(CommandManager()->Next_RPC_ID());
}
}
if (TelemetryKafkaRefCount_) {
if (KafkaManager()->Enabled() && now < TelemetryKafkaTimer_) {
TelemetryKafkaPackets_++;
State_.kafkaPackets = TelemetryKafkaPackets_;
KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
KafkaPayload);
} else {
StopKafkaTelemetry(CommandManager()->Next_RPC_ID());
}
}
} else {
poco_debug(Logger_,
fmt::format("TELEMETRY({}): Invalid telemetry packet.", SerialNumber_));
}
} else {
// if we are ignoring telemetry, then close it down on the device.
poco_debug(Logger_,
fmt::format("TELEMETRY({}): Stopping runaway telemetry.", SerialNumber_));
StopTelemetry(CommandManager()->Next_RPC_ID());
}
}
} // namespace OpenWifi

View File

@@ -1,16 +0,0 @@
//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "VenueBroadcaster.h"
namespace OpenWifi {
void AP_WS_Connection::Process_venuebroadcast(Poco::JSON::Object::Ptr ParamsObj) {
if (ParamsObj->has("data") && ParamsObj->has("serial") && ParamsObj->has("timestamp")) {
VenueBroadcaster()->Broadcast(ParamsObj->get("serial").toString(),
ParamsObj->getObject("data"),
ParamsObj->get("timestamp"));
}
}
} // namespace OpenWifi

View File

@@ -1,28 +0,0 @@
//
// Created by stephane bourque on 2023-01-22.
//
#include "AP_WS_Connection.h"
#include "StorageService.h"
#include "fmt/format.h"
#include "framework/KafkaManager.h"
#include "framework/ow_constants.h"
namespace OpenWifi {
void AP_WS_Connection::Process_wifiscan(Poco::JSON::Object::Ptr ParamsObj) {
if (!State_.Connected) {
poco_warning(Logger_,
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
CId_, CN_));
Errors_++;
return;
}
poco_trace(Logger_, fmt::format("Wifiscan data received for {}", SerialNumber_));
if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
if (KafkaManager()->Enabled()) {
KafkaManager()->PostMessage(KafkaTopics::WIFISCAN, SerialNumber_, *ParamsObj);
}
}
}
} // namespace OpenWifi

View File

@@ -1,77 +0,0 @@
//
// Created by stephane bourque on 2022-02-03.
//
#pragma once
#include <mutex>
#include <string>
#include <framework/utils.h>
#include <Poco/Environment.h>
#include <Poco/Net/SocketAcceptor.h>
#include <Poco/Data/SessionPool.h>
#include <StorageService.h>
namespace OpenWifi {
class AP_WS_ReactorThreadPool {
public:
explicit AP_WS_ReactorThreadPool(Poco::Logger &Logger) : Logger_(Logger) {
NumberOfThreads_ = Poco::Environment::processorCount() * 4;
if (NumberOfThreads_ == 0)
NumberOfThreads_ = 8;
NumberOfThreads_ = std::min(NumberOfThreads_, (std::uint64_t) 128);
}
~AP_WS_ReactorThreadPool() { Stop(); }
void Start() {
Reactors_.reserve(NumberOfThreads_);
DbSessions_.reserve(NumberOfThreads_);
Threads_.reserve(NumberOfThreads_);
Logger_.information(fmt::format("WebSocket Processor: starting {} threads.", NumberOfThreads_));
for (uint64_t i = 0; i < NumberOfThreads_; ++i) {
auto NewReactor = std::make_shared<Poco::Net::SocketReactor>();
auto NewThread = std::make_unique<Poco::Thread>();
NewThread->start(*NewReactor);
std::string ThreadName{"ap:react:" + std::to_string(i)};
Utils::SetThreadName(*NewThread, ThreadName.c_str());
Reactors_.emplace_back(std::move(NewReactor));
Threads_.emplace_back(std::move(NewThread));
DbSessions_.emplace_back(std::make_shared<LockedDbSession>());
}
Logger_.information(fmt::format("WebSocket Processor: {} threads started.", NumberOfThreads_));
}
void Stop() {
for (auto &i : Reactors_)
i->stop();
for (auto &i : Threads_) {
i->join();
}
Reactors_.clear();
Threads_.clear();
DbSessions_.clear();
}
auto NextReactor() {
std::lock_guard Lock(Mutex_);
NextReactor_++;
NextReactor_ %= NumberOfThreads_;
return std::make_pair(Reactors_[NextReactor_], DbSessions_[NextReactor_]);
}
private:
std::mutex Mutex_;
uint64_t NumberOfThreads_;
uint64_t NextReactor_ = 0;
std::vector<std::shared_ptr<Poco::Net::SocketReactor>> Reactors_;
std::vector<std::unique_ptr<Poco::Thread>> Threads_;
std::vector<std::shared_ptr<LockedDbSession>> DbSessions_;
Poco::Logger &Logger_;
};
} // namespace OpenWifi

View File

@@ -1,797 +0,0 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <Poco/Net/Context.h>
#include <Poco/Net/HTTPHeaderStream.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <AP_WS_Connection.h>
#include <AP_WS_Server.h>
#include <ConfigurationCache.h>
#include <TelemetryStream.h>
#include <fmt/format.h>
#include <framework/MicroServiceFuncs.h>
#include <framework/utils.h>
#include <framework/KafkaManager.h>
#include <UI_GW_WebSocketNotifications.h>
namespace OpenWifi {
class AP_WS_RequestHandler : public Poco::Net::HTTPRequestHandler {
public:
explicit AP_WS_RequestHandler(Poco::Logger &L, std::uint64_t session_id) : Logger_(L),
session_id_(session_id) {
};
void handleRequest( Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response) override {
try {
auto NewConnection = std::make_shared<AP_WS_Connection>(request, response, session_id_, Logger_,
AP_WS_Server()->NextReactor());
AP_WS_Server()->AddConnection(NewConnection);
NewConnection->Start();
} catch (...) {
poco_warning(Logger_, "Exception during WS creation");
}
};
private:
Poco::Logger &Logger_;
std::uint64_t session_id_;
};
class AP_WS_RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
inline explicit AP_WS_RequestHandlerFactory(Poco::Logger &L) : Logger_(L) {}
inline Poco::Net::HTTPRequestHandler *
createRequestHandler(const Poco::Net::HTTPServerRequest &request) override {
if (request.find("Upgrade") != request.end() &&
Poco::icompare(request["Upgrade"], "websocket") == 0) {
Utils::SetThreadName("ws:conn-init");
session_id_++;
return new AP_WS_RequestHandler(Logger_, session_id_);
} else {
return nullptr;
}
}
private:
Poco::Logger &Logger_;
inline static std::atomic_uint64_t session_id_ = 0;
};
bool AP_WS_Server::ValidateCertificate(const std::string &ConnectionId,
const Poco::Crypto::X509Certificate &Certificate) {
if (IsCertOk()) {
// validate certificate agains trusted chain
for (const auto &cert : ClientCasCerts_) {
if (Certificate.issuedBy(cert)) {
return true;
}
}
poco_warning(
Logger(),
fmt::format(
"CERTIFICATE({}): issuer mismatch. Certificate not issued by any trusted CA",
ConnectionId)
);
}
return false;
}
int AP_WS_Server::Start() {
AllowSerialNumberMismatch_ =
MicroServiceConfigGetBool("openwifi.certificates.allowmismatch", true);
MismatchDepth_ = MicroServiceConfigGetInt("openwifi.certificates.mismatchdepth", 2);
SessionTimeOut_ = MicroServiceConfigGetInt("openwifi.session.timeout", 10*60);
Reactor_pool_ = std::make_unique<AP_WS_ReactorThreadPool>(Logger());
Reactor_pool_->Start();
for (const auto &Svr : ConfigServersList_) {
poco_notice(Logger(),
fmt::format("Starting: {}:{} Keyfile:{} CertFile: {}", Svr.Address(),
Svr.Port(), Svr.KeyFile(), Svr.CertFile()));
Svr.LogCert(Logger());
if (!Svr.RootCA().empty())
Svr.LogCas(Logger());
if (!IsCertOk()) {
IssuerCert_ = std::make_unique<Poco::Crypto::X509Certificate>(Svr.IssuerCertFile());
poco_information(
Logger(), fmt::format("Certificate Issuer Name:{}", IssuerCert_->issuerName()));
}
Poco::Net::Context::Params P;
P.verificationMode = Poco::Net::Context::VERIFY_ONCE;
P.verificationDepth = 9;
P.loadDefaultCAs = Svr.RootCA().empty();
P.cipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
P.caLocation = Svr.Cas();
auto Context = Poco::AutoPtr<Poco::Net::Context>(
new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P));
Poco::Crypto::X509Certificate Cert(Svr.CertFile());
Poco::Crypto::X509Certificate Root(Svr.RootCA());
Context->useCertificate(Cert);
Context->addChainCertificate(Root);
Context->addCertificateAuthority(Root);
Poco::Crypto::X509Certificate Issuing(Svr.IssuerCertFile());
Context->addChainCertificate(Issuing);
Context->addCertificateAuthority(Issuing);
// add certificates from clientcas to trust chain
ClientCasCerts_ = Poco::Net::X509Certificate::readPEM(Svr.ClientCas());
for (const auto &cert : ClientCasCerts_) {
Context->addChainCertificate(cert);
Context->addCertificateAuthority(cert);
}
Poco::Crypto::RSAKey Key("", Svr.KeyFile(), Svr.KeyFilePassword());
Context->usePrivateKey(Key);
Context->setSessionCacheSize(0);
Context->setSessionTimeout(120);
Context->flushSessionCache();
Context->enableSessionCache(true);
Context->enableExtendedCertificateVerification(false);
Context->disableProtocols(Poco::Net::Context::PROTO_TLSV1 |
Poco::Net::Context::PROTO_TLSV1_1);
auto WebServerHttpParams = new Poco::Net::HTTPServerParams;
WebServerHttpParams->setMaxThreads(50);
WebServerHttpParams->setMaxQueued(200);
WebServerHttpParams->setKeepAlive(true);
WebServerHttpParams->setName("ws:ap_dispatch");
if (Svr.Address() == "*") {
Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard(
Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6
: Poco::Net::AddressFamily::IPv4));
Poco::Net::SocketAddress SockAddr(Addr, Svr.Port());
auto NewWebServer = std::make_unique<Poco::Net::HTTPServer>(
new AP_WS_RequestHandlerFactory(Logger()), DeviceConnectionPool_,
Poco::Net::SecureServerSocket(SockAddr, Svr.Backlog(), Context),
WebServerHttpParams);
WebServers_.push_back(std::move(NewWebServer));
} else {
Poco::Net::IPAddress Addr(Svr.Address());
Poco::Net::SocketAddress SockAddr(Addr, Svr.Port());
auto NewWebServer = std::make_unique<Poco::Net::HTTPServer>(
new AP_WS_RequestHandlerFactory(Logger()), DeviceConnectionPool_,
Poco::Net::SecureServerSocket(SockAddr, Svr.Backlog(), Context),
WebServerHttpParams);
WebServers_.push_back(std::move(NewWebServer));
}
KafkaDisableState_ = MicroServiceConfigGetBool("openwifi.kafka.disablestate", false);
KafkaDisableHealthChecks_ = MicroServiceConfigGetBool("openwifi.kafka.disablehealthchecks", false);
}
for (auto &server : WebServers_) {
server->start();
}
ReactorThread_.start(Reactor_);
auto ProvString = MicroServiceConfigGetString("autoprovisioning.process", "default");
if (ProvString != "default") {
auto Tokens = Poco::StringTokenizer(ProvString, ",");
for (const auto &i : Tokens) {
if (i == "prov")
LookAtProvisioning_ = true;
else
UseDefaultConfig_ = true;
}
} else {
UseDefaultConfig_ = true;
}
SimulatorId_ = Poco::toLower(MicroServiceConfigGetString("simulatorid", ""));
SimulatorEnabled_ = !SimulatorId_.empty();
Utils::SetThreadName(ReactorThread_, "dev:react:head");
Running_ = true;
GarbageCollector_.setName("ws:garbage");
GarbageCollector_.start(*this);
std::thread CleanupThread([this](){ CleanupSessions(); });
CleanupThread.detach();
return 0;
}
bool AP_WS_Server::Disconnect(uint64_t SerialNumber) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == SerialNumbers_[hashIndex].end() || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
SerialNumbers_[hashIndex].erase(DeviceHint);
}
{
auto H = SessionHash::Hash(Connection->State_.sessionId);
std::lock_guard SessionLock(SessionMutex_[H]);
Sessions_[H].erase(Connection->State_.sessionId);
}
return true;
}
void AP_WS_Server::CleanupSessions() {
while(Running_) {
std::this_thread::sleep_for(std::chrono::seconds(10));
while(Running_ && !CleanupSessions_.empty()) {
std::pair<uint64_t, uint64_t> Session;
{
std::lock_guard G(CleanupMutex_);
Session = CleanupSessions_.front();
CleanupSessions_.pop_front();
}
poco_trace(this->Logger(),fmt::format("Cleaning up session: {} for device: {}", Session.first, Utils::IntToSerialNumber(Session.second)));
EndSession(Session.first, Session.second);
}
}
}
void AP_WS_Server::run() {
uint64_t last_log = Utils::Now(),
last_zombie_run = 0,
last_garbage_run = 0;
Poco::Logger &LocalLogger = Poco::Logger::create(
"WS-Session-Janitor", Poco::Logger::root().getChannel(), Poco::Logger::root().getLevel());
while(Running_) {
if(!Poco::Thread::trySleep(30000)) {
break;
}
LocalLogger.information(fmt::format("Garbage collecting starting run." ));
uint64_t total_connected_time = 0, now = Utils::Now();
if(now-last_zombie_run > 60) {
try {
poco_information(LocalLogger,
fmt::format("Garbage collecting zombies... (step 1)"));
NumberOfConnectingDevices_ = 0;
AverageDeviceConnectionTime_ = 0;
int waits = 0;
for (int hashIndex = 0; hashIndex < MACHash::HashMax(); hashIndex++) {
last_zombie_run = now;
waits = 0;
while (true) {
if (SerialNumbersMutex_[hashIndex].try_lock()) {
waits = 0;
auto hint = SerialNumbers_[hashIndex].begin();
while (hint != end(SerialNumbers_[hashIndex])) {
if (hint->second == nullptr) {
poco_information(
LocalLogger,
fmt::format("Dead device found in hash index {}", hashIndex));
hint = SerialNumbers_[hashIndex].erase(hint);
} else {
auto Device = hint->second;
auto RightNow = Utils::Now();
if (Device->Dead_) {
AddCleanupSession(Device->State_.sessionId, Device->SerialNumberInt_);
++hint;
// hint = SerialNumbers_[hashIndex].erase(hint);
} else if (RightNow > Device->LastContact_ &&
(RightNow - Device->LastContact_) > SessionTimeOut_) {
poco_information(
LocalLogger,
fmt::format(
"{}: Session seems idle. Controller disconnecting device.",
Device->SerialNumber_));
// hint = SerialNumbers_[hashIndex].erase(hint);
AddCleanupSession(Device->State_.sessionId, Device->SerialNumberInt_);
++hint;
} else {
if (Device->State_.Connected) {
total_connected_time +=
(RightNow - Device->State_.started);
}
++hint;
}
}
}
SerialNumbersMutex_[hashIndex].unlock();
break;
} else if (waits < 5) {
waits++;
Poco::Thread::trySleep(10);
} else {
break;
}
}
}
poco_information(LocalLogger, fmt::format("Garbage collecting zombies... (step 2)"));
LeftOverSessions_ = 0;
for (int i = 0; i < SessionHash::HashMax(); i++) {
waits = 0;
while (true) {
if (SessionMutex_[i].try_lock()) {
waits = 0;
auto hint = Sessions_[i].begin();
auto RightNow = Utils::Now();
while (hint != end(Sessions_[i])) {
if (hint->second == nullptr) {
hint = Sessions_[i].erase(hint);
} else if (hint->second->Dead_) {
// hint = Sessions_[i].erase(hint);
AddCleanupSession(hint->second->State_.sessionId, hint->second->SerialNumberInt_);
++hint;
} else if (RightNow > hint->second->LastContact_ &&
(RightNow - hint->second->LastContact_) >
SessionTimeOut_) {
poco_information(
LocalLogger,
fmt::format("{}: Session seems idle. Controller disconnecting device.",
hint->second->SerialNumber_));
AddCleanupSession(hint->second->State_.sessionId, hint->second->SerialNumberInt_);
++hint;
// hint = Sessions_[i].erase(hint);
} else {
++LeftOverSessions_;
++hint;
}
}
SessionMutex_[i].unlock();
break;
} else if (waits < 5) {
Poco::Thread::trySleep(10);
waits++;
} else {
break;
}
}
}
AverageDeviceConnectionTime_ = NumberOfConnectedDevices_ > 0
? total_connected_time / NumberOfConnectedDevices_
: 0;
poco_information(LocalLogger, fmt::format("Garbage collecting zombies done..."));
} catch (const Poco::Exception &E) {
poco_error(LocalLogger, fmt::format("Poco::Exception: Garbage collecting zombies failed: {}", E.displayText()));
} catch (const std::exception &E) {
poco_error(LocalLogger, fmt::format("std::exception: Garbage collecting zombies failed: {}", E.what()));
} catch (...) {
poco_error(LocalLogger, fmt::format("exception:Garbage collecting zombies failed: {}", "unknown"));
}
}
if(NumberOfConnectedDevices_) {
if (last_garbage_run > 0) {
AverageDeviceConnectionTime_ += (now - last_garbage_run);
}
}
try {
if ((now - last_log) > 60) {
last_log = now;
poco_information(
LocalLogger,
fmt::format("Active AP connections: {} Connecting: {} Average connection time: {} seconds. Left Over Sessions: {}",
NumberOfConnectedDevices_, NumberOfConnectingDevices_,
AverageDeviceConnectionTime_, LeftOverSessions_));
}
GWWebSocketNotifications::NumberOfConnection_t Notification;
Notification.content.numberOfConnectingDevices = NumberOfConnectingDevices_;
Notification.content.numberOfDevices = NumberOfConnectedDevices_;
Notification.content.averageConnectedTime = AverageDeviceConnectionTime_;
GetTotalDataStatistics(Notification.content.tx, Notification.content.rx);
GWWebSocketNotifications::NumberOfConnections(Notification);
Poco::JSON::Object KafkaNotification;
Notification.to_json(KafkaNotification);
Poco::JSON::Object FullEvent;
FullEvent.set("type", "load-update");
FullEvent.set("timestamp", now);
FullEvent.set("payload", KafkaNotification);
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, "system", FullEvent);
LocalLogger.information(fmt::format("Garbage collection finished run."));
last_garbage_run = now;
} catch (const Poco::Exception &E) {
LocalLogger.error(fmt::format("Poco::Exception: Garbage collecting failed: {}", E.displayText()));
} catch (const std::exception &E) {
LocalLogger.error(fmt::format("std::exception: Garbage collecting failed: {}", E.what()));
} catch (...) {
LocalLogger.error(fmt::format("exception:Garbage collecting failed: {}", "unknown"));
}
}
LocalLogger.information(fmt::format("Garbage collector done for the day." ));
}
void AP_WS_Server::Stop() {
poco_information(Logger(), "Stopping...");
Running_ = false;
GarbageCollector_.wakeUp();
GarbageCollector_.join();
for (auto &server : WebServers_) {
server->stopAll();
}
Reactor_pool_->Stop();
Reactor_.stop();
ReactorThread_.join();
poco_information(Logger(), "Stopped...");
}
bool AP_WS_Server::GetHealthDevices(std::uint64_t lowLimit, std::uint64_t highLimit, std::vector<std::string> & SerialNumbers) {
SerialNumbers.clear();
for(int i=0;i<SessionHash::HashMax();i++) {
std::lock_guard Lock(SessionMutex_[i]);
for (const auto &connection : Sessions_[i]) {
if (connection.second->RawLastHealthcheck_.Sanity >= lowLimit &&
connection.second->RawLastHealthcheck_.Sanity <= highLimit) {
SerialNumbers.push_back(connection.second->SerialNumber_);
}
}
}
return true;
}
bool AP_WS_Server::GetStatistics(uint64_t SerialNumber, std::string &Statistics) const {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == SerialNumbers_[hashIndex].end() || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
Connection->GetLastStats(Statistics);
return true;
}
bool AP_WS_Server::GetState(uint64_t SerialNumber, GWObjects::ConnectionState &State) const {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == SerialNumbers_[hashIndex].end() ||
DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
Connection->GetState(State);
return true;
}
bool AP_WS_Server::GetHealthcheck(uint64_t SerialNumber,
GWObjects::HealthCheck &CheckData) const {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto Device = SerialNumbers_[hashIndex].find(SerialNumber);
if (Device == SerialNumbers_[hashIndex].end() || Device->second == nullptr) {
return false;
}
Connection = Device->second;
}
Connection->GetLastHealthCheck(CheckData);
return true;
}
void AP_WS_Server::StartSession(uint64_t session_id, uint64_t SerialNumber) {
auto sessionHash = SessionHash::Hash(session_id);
std::shared_ptr<AP_WS_Connection> Connection;
{
std::lock_guard SessionLock(SessionMutex_[sessionHash]);
auto SessionHint = Sessions_[sessionHash].find(session_id);
if (SessionHint == end(Sessions_[sessionHash])) {
return;
}
Connection = SessionHint->second;
Sessions_[sessionHash].erase(SessionHint);
}
auto deviceHash = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[deviceHash]);
SerialNumbers_[deviceHash][SerialNumber] = Connection;
}
bool AP_WS_Server::EndSession(uint64_t session_id, uint64_t SerialNumber) {
{
poco_trace(Logger(), fmt::format("Ending session 1: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
auto sessionHash = SessionHash::Hash(session_id);
std::lock_guard SessionLock(SessionMutex_[sessionHash]);
Sessions_[sessionHash].erase(session_id);
poco_trace(Logger(), fmt::format("Ended session 1: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
}
{
auto hashIndex = MACHash::Hash(SerialNumber);
poco_trace(Logger(), fmt::format("Ending session 2.0: {} for device: {} hi:{}", session_id, Utils::IntToSerialNumber(SerialNumber), hashIndex));
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
poco_trace(Logger(), fmt::format("Ending session 2.1: {} for device: {} hi:{}", session_id, Utils::IntToSerialNumber(SerialNumber), hashIndex));
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
poco_trace(Logger(), fmt::format("Ending session 2.2: {} for device: {} hi:{}", session_id, Utils::IntToSerialNumber(SerialNumber), hashIndex));
if (DeviceHint == SerialNumbers_[hashIndex].end()
|| DeviceHint->second == nullptr
|| DeviceHint->second->State_.sessionId != session_id) {
poco_trace(Logger(), fmt::format("Did not end session 2: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
return false;
}
SerialNumbers_[hashIndex].erase(DeviceHint);
poco_trace(Logger(), fmt::format("Ended session 2: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
}
return true;
}
bool AP_WS_Server::Connected(uint64_t SerialNumber,
GWObjects::DeviceRestrictions &Restrictions) const {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
if(Connection->Dead_) {
return false;
}
Restrictions = Connection->GetRestrictions();
return Connection->State_.Connected;
}
bool AP_WS_Server::Connected(uint64_t SerialNumber) const {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
if(Connection->Dead_) {
return false;
}
return Connection->State_.Connected;
}
bool AP_WS_Server::SendFrame(uint64_t SerialNumber, const std::string &Payload) const {
auto hashIndex = MACHash::Hash(SerialNumber);
std::shared_ptr<AP_WS_Connection> Connection;
{
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
if(Connection->Dead_) {
return false;
}
try {
return Connection->Send(Payload);
} catch (...) {
poco_debug(Logger(), fmt::format(": SendFrame: Could not send data to device '{}'",
Utils::IntToSerialNumber(SerialNumber)));
}
return false;
}
void AP_WS_Server::StopWebSocketTelemetry(uint64_t RPCID, uint64_t SerialNumber) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto Device = SerialNumbers_[hashIndex].find(SerialNumber);
if (Device == end(SerialNumbers_[hashIndex]) || Device->second == nullptr) {
return;
}
Connection = Device->second;
}
Connection->StopWebSocketTelemetry(RPCID);
}
void
AP_WS_Server::SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber,
uint64_t Interval, uint64_t Lifetime,
const std::vector<std::string> &TelemetryTypes) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return;
}
Connection = DeviceHint->second;
}
Connection->SetWebSocketTelemetryReporting(RPCID, Interval, Lifetime, TelemetryTypes);
}
void AP_WS_Server::SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber,
uint64_t Interval, uint64_t Lifetime,
const std::vector<std::string> &TelemetryTypes) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return;
}
Connection = DeviceHint->second;
}
Connection->SetKafkaTelemetryReporting(RPCID, Interval, Lifetime, TelemetryTypes);
}
void AP_WS_Server::StopKafkaTelemetry(uint64_t RPCID, uint64_t SerialNumber) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return;
}
Connection = DeviceHint->second;
}
Connection->StopKafkaTelemetry(RPCID);
}
void AP_WS_Server::GetTelemetryParameters(
uint64_t SerialNumber, bool &TelemetryRunning, uint64_t &TelemetryInterval,
uint64_t &TelemetryWebSocketTimer, uint64_t &TelemetryKafkaTimer,
uint64_t &TelemetryWebSocketCount, uint64_t &TelemetryKafkaCount,
uint64_t &TelemetryWebSocketPackets, uint64_t &TelemetryKafkaPackets) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(SerialNumber);
std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return;
}
Connection = DeviceHint->second;
}
Connection->GetTelemetryParameters(TelemetryRunning, TelemetryInterval,
TelemetryWebSocketTimer, TelemetryKafkaTimer,
TelemetryWebSocketCount, TelemetryKafkaCount,
TelemetryWebSocketPackets, TelemetryKafkaPackets);
}
bool AP_WS_Server::SendRadiusAccountingData(const std::string &SerialNumber,
const unsigned char *buffer, std::size_t size) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto IntSerialNumber = Utils::SerialNumberToInt(SerialNumber);
auto hashIndex = MACHash::Hash(IntSerialNumber);
std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(IntSerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
if(Connection->Dead_) {
return false;
}
try {
return Connection->SendRadiusAccountingData(buffer, size);
} catch (...) {
poco_debug(
Logger(),
fmt::format(": SendRadiusAuthenticationData: Could not send data to device '{}'",
SerialNumber));
}
return false;
}
bool AP_WS_Server::SendRadiusAuthenticationData(const std::string &SerialNumber,
const unsigned char *buffer, std::size_t size) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto IntSerialNumber = Utils::SerialNumberToInt(SerialNumber);
auto hashIndex = MACHash::Hash(IntSerialNumber);
std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(IntSerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
if(Connection->Dead_) {
return false;
}
try {
return Connection->SendRadiusAuthenticationData(buffer, size);
} catch (...) {
poco_debug(
Logger(),
fmt::format(": SendRadiusAuthenticationData: Could not send data to device '{}'",
SerialNumber));
}
return false;
}
bool AP_WS_Server::SendRadiusCoAData(const std::string &SerialNumber,
const unsigned char *buffer, std::size_t size) {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto IntSerialNumber = Utils::SerialNumberToInt(SerialNumber);
auto hashIndex = MACHash::Hash(IntSerialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(IntSerialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
return false;
}
Connection = DeviceHint->second;
}
if(Connection->Dead_) {
return false;
}
try {
return Connection->SendRadiusCoAData(buffer, size);
} catch (...) {
poco_debug(Logger(),
fmt::format(": SendRadiusCoAData: Could not send data to device '{}'",
SerialNumber));
}
return false;
}
} // namespace OpenWifi

View File

@@ -1,264 +0,0 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#pragma once
#include <array>
#include <ctime>
#include <mutex>
#include <thread>
#include "Poco/AutoPtr.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/ParallelSocketAcceptor.h"
#include "Poco/Net/SocketAcceptor.h"
#include "Poco/Net/SocketReactor.h"
#include "Poco/Timer.h"
#include "AP_WS_Connection.h"
#include "AP_WS_Reactor_Pool.h"
#include "framework/SubSystemServer.h"
#include "framework/utils.h"
namespace OpenWifi {
constexpr uint MACHashMax = 256;
constexpr uint MACHashMask = MACHashMax-1;
class MACHash {
public:
[[nodiscard]] static inline uint16_t Hash(std::uint64_t value) {
uint8_t hash = 0, i=6;
while(i) {
hash ^= (value & MACHashMask) + 1;
value >>= 8;
--i;
}
return hash;
}
[[nodiscard]] static inline uint16_t Hash(const std::string & value) {
return Hash(Utils::MACToInt(value));
}
[[nodiscard]] static inline uint16_t HashMax() {
return MACHashMax;
}
};
constexpr uint SessionHashMax = 256;
constexpr uint SessionHashMask = SessionHashMax-1;
class SessionHash {
public:
[[nodiscard]] static inline uint16_t Hash(std::uint64_t value) {
return (value & SessionHashMask);
}
[[nodiscard]] static inline uint16_t HashMax() {
return SessionHashMax;
}
};
class AP_WS_Server : public SubSystemServer, public Poco::Runnable {
public:
static auto instance() {
static auto instance_ = new AP_WS_Server;
return instance_;
}
int Start() override;
void Stop() override;
bool IsCertOk() { return IssuerCert_ != nullptr; }
bool ValidateCertificate(const std::string &ConnectionId,
const Poco::Crypto::X509Certificate &Certificate);
inline bool IsSimSerialNumber(const std::string &SerialNumber) const {
return IsSim(SerialNumber) &&
SerialNumber == SimulatorId_;
}
inline static bool IsSim(const std::string &SerialNumber) {
return SerialNumber.substr(0, 6) == "53494d";
}
void run() override; // Garbage collector thread.
[[nodiscard]] inline bool IsSimEnabled() const { return SimulatorEnabled_; }
[[nodiscard]] inline bool AllowSerialNumberMismatch() const { return AllowSerialNumberMismatch_; }
[[nodiscard]] inline uint64_t MismatchDepth() const { return MismatchDepth_; }
[[nodiscard]] inline bool UseProvisioning() const { return LookAtProvisioning_; }
[[nodiscard]] inline bool UseDefaults() const { return UseDefaultConfig_; }
[[nodiscard]] inline bool Running() const { return Running_; }
[[nodiscard]] inline std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> NextReactor() {
return Reactor_pool_->NextReactor();
}
inline void AddConnection(std::shared_ptr<AP_WS_Connection> Connection) {
std::uint64_t sessionHash = SessionHash::Hash(Connection->State_.sessionId);
std::lock_guard SessionLock(SessionMutex_[sessionHash]);
if(Sessions_[sessionHash].find(Connection->State_.sessionId)==end(Sessions_[sessionHash])) {
Sessions_[sessionHash][Connection->State_.sessionId] = std::move(Connection);
}
}
[[nodiscard]] inline bool DeviceRequiresSecureRTTY(uint64_t serialNumber) const {
std::shared_ptr<AP_WS_Connection> Connection;
{
auto hashIndex = MACHash::Hash(serialNumber);
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
auto DeviceHint = SerialNumbers_[hashIndex].find(serialNumber);
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr)
return false;
Connection = DeviceHint->second;
}
return Connection->RTTYMustBeSecure_;
}
inline bool GetStatistics(const std::string &SerialNumber, std::string &Statistics) const {
return GetStatistics(Utils::SerialNumberToInt(SerialNumber), Statistics);
}
[[nodiscard]] bool GetStatistics(uint64_t SerialNumber, std::string &Statistics) const;
inline bool GetState(const std::string &SerialNumber,
GWObjects::ConnectionState &State) const {
return GetState(Utils::SerialNumberToInt(SerialNumber), State);
}
bool GetState(uint64_t SerialNumber, GWObjects::ConnectionState &State) const;
inline bool GetHealthcheck(const std::string &SerialNumber,
GWObjects::HealthCheck &CheckData) const {
return GetHealthcheck(Utils::SerialNumberToInt(SerialNumber), CheckData);
}
bool GetHealthcheck(uint64_t SerialNumber, GWObjects::HealthCheck &CheckData) const;
bool Connected(uint64_t SerialNumber, GWObjects::DeviceRestrictions &Restrictions) const;
bool Connected(uint64_t SerialNumber) const;
bool Disconnect(uint64_t SerialNumber);
bool SendFrame(uint64_t SerialNumber, const std::string &Payload) const;
bool SendRadiusAuthenticationData(const std::string &SerialNumber,
const unsigned char *buffer, std::size_t size);
bool SendRadiusAccountingData(const std::string &SerialNumber, const unsigned char *buffer,
std::size_t size);
bool SendRadiusCoAData(const std::string &SerialNumber, const unsigned char *buffer,
std::size_t size);
void StartSession(uint64_t session_id, uint64_t SerialNumber);
bool EndSession(uint64_t session_id, uint64_t SerialNumber);
void SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber,
uint64_t Interval, uint64_t Lifetime,
const std::vector<std::string> &TelemetryTypes);
void StopWebSocketTelemetry(uint64_t RPCID, uint64_t SerialNumber);
void SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber, uint64_t Interval,
uint64_t Lifetime,
const std::vector<std::string> &TelemetryTypes);
void StopKafkaTelemetry(uint64_t RPCID, uint64_t SerialNumber);
void GetTelemetryParameters(uint64_t SerialNumber, bool &TelemetryRunning,
uint64_t &TelemetryInterval, uint64_t &TelemetryWebSocketTimer,
uint64_t &TelemetryKafkaTimer,
uint64_t &TelemetryWebSocketCount,
uint64_t &TelemetryKafkaCount,
uint64_t &TelemetryWebSocketPackets,
uint64_t &TelemetryKafkaPackets);
bool GetHealthDevices(std::uint64_t lowLimit, std::uint64_t highLimit, std::vector<std::string> & SerialNumbers);
// bool ExtendedAttributes(const std::string &serialNumber, bool & hasGPS, std::uint64_t &Sanity,
// std::double_t &MemoryUsed, std::double_t &Load, std::double_t &Temperature);
inline void AverageDeviceStatistics(uint64_t &Connections, uint64_t &AverageConnectionTime,
uint64_t &NumberOfConnectingDevices) const {
Connections = NumberOfConnectedDevices_;
AverageConnectionTime = AverageDeviceConnectionTime_;
NumberOfConnectingDevices = NumberOfConnectingDevices_;
}
inline bool SendFrame(const std::string &SerialNumber, const std::string &Payload) const {
return SendFrame(Utils::SerialNumberToInt(SerialNumber), Payload);
}
inline void AddRX(std::uint64_t bytes) {
RX_ += bytes;
}
inline void AddTX(std::uint64_t bytes) {
TX_ += bytes;
}
inline void GetTotalDataStatistics(std::uint64_t &TX, std::uint64_t &RX) const {
TX = TX_;
RX = RX_;
}
bool KafkaDisableState() const { return KafkaDisableState_; }
bool KafkaDisableHealthChecks() const { return KafkaDisableHealthChecks_; }
inline void IncrementConnectionCount() {
++NumberOfConnectedDevices_;
}
inline void DecrementConnectionCount() {
--NumberOfConnectedDevices_;
}
inline void AddCleanupSession(uint64_t session_id, uint64_t SerialNumber) {
std::lock_guard G(CleanupMutex_);
CleanupSessions_.emplace_back(session_id, SerialNumber);
}
void CleanupSessions();
private:
std::array<std::mutex,SessionHashMax> SessionMutex_;
std::array<std::map<std::uint64_t, std::shared_ptr<AP_WS_Connection>>,SessionHashMax> Sessions_;
using SerialNumberMap = std::map<uint64_t /* serial number */,
std::shared_ptr<AP_WS_Connection>>;
std::array<SerialNumberMap,MACHashMax> SerialNumbers_;
mutable std::array<std::mutex,MACHashMax> SerialNumbersMutex_;
std::unique_ptr<Poco::Crypto::X509Certificate> IssuerCert_;
std::vector<Poco::Crypto::X509Certificate> ClientCasCerts_;
std::list<std::unique_ptr<Poco::Net::HTTPServer>> WebServers_;
Poco::ThreadPool DeviceConnectionPool_{"ws:dev-pool", 4, 256};
Poco::Net::SocketReactor Reactor_;
Poco::Thread ReactorThread_;
std::string SimulatorId_;
bool LookAtProvisioning_ = false;
bool UseDefaultConfig_ = true;
bool SimulatorEnabled_ = false;
bool AllowSerialNumberMismatch_ = true;
Poco::Thread CleanupThread_;
std::mutex CleanupMutex_;
std::deque<std::pair<uint64_t, uint64_t>> CleanupSessions_;
std::unique_ptr<AP_WS_ReactorThreadPool> Reactor_pool_;
std::atomic_bool Running_ = false;
std::uint64_t MismatchDepth_ = 2;
std::atomic_uint64_t NumberOfConnectedDevices_ = 0;
std::atomic_uint64_t AverageDeviceConnectionTime_ = 0;
std::uint64_t NumberOfConnectingDevices_ = 0;
std::uint64_t SessionTimeOut_ = 10*60;
std::uint64_t LeftOverSessions_ = 0;
std::atomic_uint64_t TX_=0,RX_=0;
std::atomic_bool KafkaDisableState_=false,
KafkaDisableHealthChecks_=false;
Poco::Thread GarbageCollector_;
AP_WS_Server() noexcept
: SubSystemServer("WebSocketServer", "WS-SVR", "ucentral.websocket") {}
};
inline auto AP_WS_Server() { return AP_WS_Server::instance(); }
} // namespace OpenWifi

View File

@@ -4,105 +4,95 @@
#pragma once
#include <fstream>
#include <map>
#include <mutex>
#include <string>
#include "framework/MicroServiceFuncs.h"
#include "framework/ow_constants.h"
#include "CentralConfig.h"
#include "framework/MicroService.h"
#include "nlohmann/json.hpp"
namespace OpenWifi {
const std::string PlatformCacheFileName{"/plat_cache.json"};
const std::string CapabilitiesCacheFileName{"/caps_cache.json"};
typedef std::map<std::string, nlohmann::json> CapabilitiesCache_t;
typedef std::map<std::string,nlohmann::json> CapabilitiesCache_t;
class CapabilitiesCache {
public:
static auto instance() {
static auto instance = new CapabilitiesCache;
return instance;
}
inline void Add(const Config::Capabilities &Caps) {
if (Caps.Compatible().empty() || Caps.Platform().empty())
inline void Add(const std::string & DeviceType, const std::string & Platform, const std::string & FullCapabilities) {
if(DeviceType.empty() || Platform.empty())
return;
std::lock_guard G(Mutex_);
if (!PlatformsLoaded_)
std::lock_guard G(Mutex_);
if(!PlatformsLoaded_)
LoadPlatforms();
auto P = Poco::toLower(Caps.Platform());
auto Hint = Platforms_.find(Caps.Compatible());
if (Hint == Platforms_.end()) {
Platforms_.insert(std::make_pair(Caps.Compatible(), P));
auto P = Poco::toUpper(Platform);
auto Hint = Platforms_.find(DeviceType);
if(Hint==Platforms_.end()) {
Platforms_.insert(std::make_pair(DeviceType,P));
SavePlatforms();
} else if (Hint->second != P) {
} else if(Hint->second != P) {
Hint->second = P;
SavePlatforms();
}
if (!CapabilitiesLoaded_)
if(!CapabilitiesLoaded_)
LoadCapabilities();
auto CapHint = Capabilities_.find(Caps.Compatible());
if (CapHint == Capabilities_.end()) {
auto C = nlohmann::json::parse(Caps.AsString());
C.erase("restrictions");
Capabilities_[Caps.Compatible()] = nlohmann::json::parse(Caps.AsString());
auto CapHint = Capabilities_.find(DeviceType);
if(CapHint==Capabilities_.end()) {
Capabilities_[DeviceType] = nlohmann::json::parse(FullCapabilities);
SaveCapabilities();
} else {
CapHint->second = nlohmann::json::parse(Caps.AsString());
CapHint->second = nlohmann::json::parse(FullCapabilities);
SaveCapabilities();
}
}
inline std::string GetPlatform(const std::string &DeviceType) {
std::lock_guard G(Mutex_);
inline std::string GetPlatform(const std::string & DeviceType) {
std::lock_guard G(Mutex_);
if (!PlatformsLoaded_) {
if(!PlatformsLoaded_) {
LoadPlatforms();
}
auto Hint = Platforms_.find(DeviceType);
if (Hint == Platforms_.end())
return Platforms::AP;
if(Hint==Platforms_.end())
return "AP";
return Hint->second;
}
inline nlohmann::json GetCapabilities(const std::string &DeviceType) {
std::lock_guard G(Mutex_);
inline nlohmann::json GetCapabilities(const std::string & DeviceType) {
std::lock_guard G(Mutex_);
if (!CapabilitiesLoaded_) {
if(!CapabilitiesLoaded_) {
LoadCapabilities();
}
auto Hint = Capabilities_.find(DeviceType);
if (Hint == Capabilities_.end())
if(Hint==Capabilities_.end())
return nlohmann::json{};
return Hint->second;
}
inline const CapabilitiesCache_t &AllCapabilities() {
std::lock_guard G(Mutex_);
if (!CapabilitiesLoaded_) {
inline const CapabilitiesCache_t & AllCapabilities() {
std::lock_guard G(Mutex_);
if(!CapabilitiesLoaded_) {
LoadCapabilities();
}
return Capabilities_;
}
private:
std::recursive_mutex Mutex_;
std::atomic_bool PlatformsLoaded_ = false;
std::atomic_bool CapabilitiesLoaded_ = false;
std::map<std::string, std::string> Platforms_;
CapabilitiesCache_t Capabilities_;
std::string PlatformCacheFileName_{MicroServiceDataDirectory() + PlatformCacheFileName};
std::string CapabilitiesCacheFileName_{MicroServiceDataDirectory() +
CapabilitiesCacheFileName};
std::recursive_mutex Mutex_;
std::atomic_bool PlatformsLoaded_=false;
std::atomic_bool CapabilitiesLoaded_=false;
std::map<std::string,std::string> Platforms_;
CapabilitiesCache_t Capabilities_;
std::string PlatformCacheFileName_{ MicroService::instance().DataDir()+PlatformCacheFileName };
std::string CapabilitiesCacheFileName_{ MicroService::instance().DataDir()+CapabilitiesCacheFileName };
inline void LoadPlatforms() {
try {
@@ -110,10 +100,11 @@ namespace OpenWifi {
nlohmann::json cache;
i >> cache;
for (const auto &[Type, Platform] : cache.items()) {
Platforms_[Type] = Poco::toLower(Platform.get<std::string>());
for(const auto &[Type,Platform]:cache.items()) {
Platforms_[Type] = Platform;
}
} catch (...) {
} catch(...) {
}
PlatformsLoaded_ = true;
}
@@ -124,35 +115,33 @@ namespace OpenWifi {
nlohmann::json cache(Platforms_);
i << cache;
} catch (...) {
}
}
inline void LoadCapabilities() {
try {
std::ifstream i(CapabilitiesCacheFileName_,
std::ios_base::binary | std::ios_base::in);
std::ifstream i(CapabilitiesCacheFileName_, std::ios_base::binary|std::ios_base::in);
nlohmann::json cache;
i >> cache;
for (const auto &[Type, Caps] : cache.items()) {
for(const auto &[Type,Caps]:cache.items()) {
Capabilities_[Type] = Caps;
}
} catch (...) {
} catch(...) {
}
CapabilitiesLoaded_ = true;
}
inline void SaveCapabilities() {
try {
std::ofstream i(CapabilitiesCacheFileName_,
std::ios_base::trunc | std::ios_base::out | std::ios_base::binary);
std::ofstream i(CapabilitiesCacheFileName_, std::ios_base::trunc | std::ios_base::out | std::ios_base::binary );
nlohmann::json cache(Capabilities_);
i << cache;
} catch (...) {
}
}
};
inline auto CapabilitiesCache() { return CapabilitiesCache::instance(); };
} // namespace OpenWifi
}

View File

@@ -7,154 +7,122 @@
//
#include <fstream>
#include "Poco/File.h"
#include "Poco/JSON/Object.h"
#include "Poco/JSON/Parser.h"
#include "Poco/File.h"
#include "CentralConfig.h"
#include "framework/MicroService.h"
#include "Daemon.h"
namespace OpenWifi::Config {
const static std::string BasicConfig{
R"lit(
{
"interfaces": [
{
"ethernet": [
{
"select-ports": [
"WAN*"
]
}
],
"ipv4": {
"addressing": "dynamic"
},
"name": "WAN",
"role": "upstream",
"services": [
"ssh",
"lldp",
"dhcp-snooping"
],
"ssids": [
{
"bss-mode": "ap",
"encryption": {
"ieee80211w": "optional",
"key": "OpenWifi",
"proto": "psk2"
},
"name": "OpenWifi",
"services": [
"wifi-frames"
],
"wifi-bands": [
"2G","5G"
]
}
]
},
{
"ethernet": [
{
"select-ports": [
"LAN*"
]
}
],
"ipv4": {
"addressing": "static",
"dhcp": {
"lease-count": 100,
"lease-first": 10,
"lease-time": "6h"
},
"subnet": "192.168.1.1/24"
},
"name": "LAN",
"role": "downstream",
"services": [
"ssh",
"lldp",
"dhcp-snooping"
]
}
],
"metrics": {
"dhcp-snooping": {
"filters": [
"ack",
"discover",
"offer",
"request",
"solicit",
"reply",
"renew"
]
},
"health": {
"interval": 120
},
"statistics": {
"interval": 60,
"types": [
"ssids",
"lldp",
"clients"
]
},
"wifi-frames": {
"filters": [
"probe",
"auth",
"assoc",
"disassoc",
"deauth",
"local-deauth",
"inactive-deauth",
"key-mismatch",
"beacon-report",
"radar-detected"
]
}
},
"radios": [
{
"band": "2G",
"channel": "auto",
"channel-mode": "HE",
"country": "CA"
},
{
"allow-dfs": true,
"band": "5G",
"channel": "auto",
"channel-mode": "HE",
"country": "CA"
}
],
"services": {
"lldp": {
"describe": "TIP OpenWiFi",
"location": "QA"
},
"ssh": {
"port": 22
}
},
"uuid": 2
}
)lit"};
const static std::string BasicConfig {
R"lit({
"uuid": 1,
"radios": [
{
"band": "5G",
"country": "CA",
"channel-mode": "HE",
"channel-width": 80,
"channel": 32
}
],
"interfaces": [
{
"name": "WAN",
"role": "upstream",
"services": [ "lldp" ],
"ethernet": [
{
"select-ports": [
"WAN*"
]
}
],
"ipv4": {
"addressing": "dynamic"
},
"ssids": [
{
"name": "OpenWifi",
"wifi-bands": [
"5G"
],
"bss-mode": "ap",
"encryption": {
"proto": "psk2",
"key": "OpenWifi",
"ieee80211w": "optional"
}
}
]
},
{
"name": "LAN",
"role": "downstream",
"services": [ "ssh", "lldp" ],
"ethernet": [
{
"select-ports": [
"LAN*"
]
}
],
"ipv4": {
"addressing": "static",
"subnet": "192.168.1.1/24",
"dhcp": {
"lease-first": 10,
"lease-count": 100,
"lease-time": "6h"
}
},
"ssids": [
{
"name": "OpenWifi",
"wifi-bands": [
"5G"
],
"bss-mode": "ap",
"encryption": {
"proto": "psk2",
"key": "OpenWifi",
"ieee80211w": "optional"
}
}
]
}
],
"metrics": {
"statistics": {
"interval": 120,
"types": [ "ssids", "lldp", "clients" ]
},
"health": {
"interval": 120
}
},
"services": {
"lldp": {
"describe": "uCentral",
"location": "universe"
},
"ssh": {
"port": 22
}
}
})lit"};
void Config::SetBasicConfigFile() {
try {
Poco::File DefaultConfigFileName{MicroService::instance().DataDir() +
"/default_config.json"};
Poco::File DefaultConfigFileName{MicroService::instance().DataDir() + "/default_config.json"};
DefaultConfiguration_ = BasicConfig;
std::ofstream OS(DefaultConfigFileName.path(), std::ios::binary | std::ios::trunc);
std::istringstream IS(DefaultConfiguration_);
std::ofstream OS(DefaultConfigFileName.path(), std::ios::binary | std::ios::trunc );
std::istringstream IS(DefaultConfiguration_);
Poco::StreamCopier::copyStream(IS, OS);
} catch (...) {
DefaultConfiguration_ = BasicConfig;
@@ -162,23 +130,22 @@ namespace OpenWifi::Config {
}
Config::Config() {
if (DefaultConfiguration_.empty())
if(DefaultConfiguration_.empty())
Init();
Config_ = DefaultConfiguration_;
}
void Config::Init() {
if (DefaultConfiguration_.empty()) {
if(DefaultConfiguration_.empty()) {
// open the file
try {
Poco::File DefaultConfigFileName{MicroService::instance().DataDir() +
"/default_config.json"};
Poco::File DefaultConfigFileName{MicroService::instance().DataDir()+"/default_config.json"};
if (!DefaultConfigFileName.exists()) {
SetBasicConfigFile();
} else {
std::ifstream F(DefaultConfigFileName.path(), std::ios::binary | std::ios::in);
std::ifstream F(DefaultConfigFileName.path(),std::ios::binary | std::ios::in);
std::ostringstream C;
Poco::StreamCopier::copyStream(F, C);
Poco::StreamCopier::copyStream(F,C);
DefaultConfiguration_ = C.str();
}
} catch (...) {
@@ -187,104 +154,107 @@ namespace OpenWifi::Config {
}
}
bool Config::SetUUID(uint64_t UUID) {
try {
Poco::JSON::Parser Parser;
auto Object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
bool Config::SetUUID(uint64_t UUID) {
try {
Poco::JSON::Parser Parser;
auto Object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
Object->set("uuid", UUID);
std::ostringstream NewConfig;
Poco::JSON::Stringifier Stringifier;
Poco::JSON::Stringifier Stringifier;
Stringifier.condense(Object, NewConfig);
Config_ = NewConfig.str();
return true;
} catch (const Poco::Exception &E) {
std::cout << __func__ << ": new Configuration failed with " << E.displayText()
<< std::endl;
}
return false;
}
}
catch(const Poco::Exception &E)
{
std::cout << __func__ << ": new Configuration failed with " << E.displayText() << std::endl;
}
return false;
}
std::uint64_t Config::UUID() {
try {
Poco::JSON::Parser Parser;
auto object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
if (object->has("uuid"))
return object->get("uuid");
} catch (...) {
}
return 0;
}
bool Config::Valid() {
try {
Poco::JSON::Parser Parser;
auto object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
if (object->has("uuid"))
return true;
bool Config::Valid() {
try {
Poco::JSON::Parser Parser;
auto object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
if(object->has("uuid"))
return true;
return false;
}
catch (...)
{
return false;
} catch (...) {
return false;
}
}
}
}
Poco::JSON::Object::Ptr Config::to_json() {
Poco::JSON::Parser Parser;
return Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
}
Poco::JSON::Parser Parser;
return Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
}
std::string Config::Default() {
if (DefaultConfiguration_.empty())
if(DefaultConfiguration_.empty())
Init();
return DefaultConfiguration_;
}
/* std::string Capabilities::Default() {
return std::string(R"lit({"model":{"id":"linksys,ea8300","name":"Linksys EA8300
(Dallas)"},
"network":{"lan":{"ifname":"eth0","protocol":"static"},"wan":{"ifname":"eth1","protocol":"dhcp"}},
"switch":{"switch0":{"enable":true,"reset":true,"ports":[{"num":0,"device":"eth0","need_tag":false,
"want_untag":true},{"num":1,"role":"lan"},{"num":2,"role":"lan"},{"num":3,"role":"lan"},{"num":4,"role":"lan"}],
"roles":[{"role":"lan","ports":"1 2 3 4 0","device":"eth0"}]}},
"wifi":{"soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0":{"band":["5u"],"ht_capa":6639,
"vht_capa":865696178,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,
"channels":[100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165]},
"platform/soc/a000000.wifi":{"band":["2"],"ht_capa":6639,"vht_capa":865687986,
"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,"channels":[1,2,3,4,5,6,7,8,9,10,11]},
"platform/soc/a800000.wifi":{"band":["5l"],"ht_capa":6639,"vht_capa":865687986,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],
"tx_ant":3,"rx_ant":3,"channels":[36,40,44,48,52,56,60,64]}}})lit");
}
*/
std::string Capabilities::Default() {
return std::string(R"lit({"model":{"id":"linksys,ea8300","name":"Linksys EA8300 (Dallas)"},
"network":{"lan":{"ifname":"eth0","protocol":"static"},"wan":{"ifname":"eth1","protocol":"dhcp"}},
"switch":{"switch0":{"enable":true,"reset":true,"ports":[{"num":0,"device":"eth0","need_tag":false,
"want_untag":true},{"num":1,"role":"lan"},{"num":2,"role":"lan"},{"num":3,"role":"lan"},{"num":4,"role":"lan"}],
"roles":[{"role":"lan","ports":"1 2 3 4 0","device":"eth0"}]}},
"wifi":{"soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0":{"band":["5u"],"ht_capa":6639,
"vht_capa":865696178,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,
"channels":[100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165]},
"platform/soc/a000000.wifi":{"band":["2"],"ht_capa":6639,"vht_capa":865687986,
"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,"channels":[1,2,3,4,5,6,7,8,9,10,11]},
"platform/soc/a800000.wifi":{"band":["5l"],"ht_capa":6639,"vht_capa":865687986,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],
"tx_ant":3,"rx_ant":3,"channels":[36,40,44,48,52,56,60,64]}}})lit");
}
Capabilities::Capabilities(const Poco::JSON::Object::Ptr &Caps) {
try {
void Capabilities::Parse() {
if(Capabilities_.empty())
Capabilities_=Default();
if (Caps->has("compatible"))
Compatible_ = Caps->get("compatible").toString();
try {
Poco::JSON::Parser parser;
if (Caps->has("model"))
Model_ = Caps->get("model").toString();
auto Result = parser.parse(Capabilities_);
auto Objects = Result.extract<Poco::JSON::Object::Ptr>();
if (Caps->has("platform"))
Platform_ = Poco::toLower(Caps->get("platform").toString());
if(Objects->has("compatible"))
Compatible_ = Objects->get("compatible").toString();
if(Compatible_.empty()) {
Compatible_ = Model_;
}
if(Objects->has("model"))
Model_ = Objects->get("model").toString();
std::ostringstream OS;
Caps->stringify(OS);
AsString_ = OS.str();
} catch (const Poco::Exception &E) {
Daemon()->logger().log(E);
}
if(Objects->has("platform"))
Platform_ = Objects->get("platform").toString();
Parsed_ = true ;
}
catch ( const Poco::Exception & E )
{
Daemon()->logger().log(E);
}
}
const std::string & Capabilities::Compatible() {
if(!Parsed_)
Parse();
return Compatible_;
}
const std::string &Capabilities::Compatible() const { return Compatible_; }
const std::string & Capabilities::Model() {
if(!Parsed_)
Parse();
return Model_;
}
const std::string &Capabilities::Model() const { return Model_; }
const std::string & Capabilities::Platform() {
if(!Parsed_)
Parse();
return Platform_;
}
const std::string &Capabilities::Platform() const { return Platform_; }
const std::string &Capabilities::AsString() const { return AsString_; }
} // namespace OpenWifi::Config
} // namespace

View File

@@ -8,56 +8,59 @@
#pragma once
#include "Poco/JSON/Object.h"
#include <string>
#include "Poco/JSON/Object.h"
namespace OpenWifi::Config {
class Config {
public:
explicit Config(const std::string &Config) : Config_(Config) {}
public:
explicit Config(const std::string &Config)
:Config_(Config) {
}
Config();
bool SetUUID(uint64_t UUID);
[[nodiscard]] bool Valid();
Config();
bool SetUUID(uint64_t UUID);
[[nodiscard]] bool Valid();
[[nodiscard]] std::string get() { return Config_; };
[[nodiscard]] std::string Default();
[[nodiscard]] Poco::JSON::Object::Ptr to_json();
[[nodiscard]] std::uint64_t UUID();
private:
void Init();
void SetBasicConfigFile();
inline static std::string DefaultConfiguration_ = "";
std::string Config_;
};
private:
void Init();
void SetBasicConfigFile();
inline static std::string DefaultConfiguration_ = "";
std::string Config_;
};
class Capabilities {
public:
explicit Capabilities(const Poco::JSON::Object::Ptr &Caps);
class Capabilities {
public:
explicit Capabilities(std::string Caps)
: Capabilities_(std::move(Caps))
{
/* Capabilities()
{
Capabilities_ = Default();
}
}
static std::string Default();
Capabilities()
{
Capabilities_ = Default();
}
[[nodiscard]] const std::string & Get() const { return Capabilities_; };
*/
static std::string Default();
[[nodiscard]] const std::string & Get() const { return Capabilities_; };
[[nodiscard]] const std::string & Compatible();
[[nodiscard]] const std::string & Model();
[[nodiscard]] const std::string & Platform();
[[nodiscard]] const std::string &Compatible() const;
[[nodiscard]] const std::string &Model() const;
[[nodiscard]] const std::string &Platform() const;
[[nodiscard]] const std::string &AsString() const;
private:
std::string Compatible_;
std::string Model_;
private:
std::string Capabilities_;
bool Parsed_=false;
std::string Compatible_;
std::string Model_;
std::string Platform_;
std::string AsString_;
void Parse();
};
};
} // namespace
} // namespace OpenWifi::Config

View File

@@ -10,458 +10,174 @@
#include "Poco/JSON/Parser.h"
#include "AP_WS_Server.h"
#include "CommandManager.h"
#include "DeviceRegistry.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "StorageService.h"
#include "framework/MicroServiceFuncs.h"
#include "framework/ow_constants.h"
#include "framework/utils.h"
using namespace std::chrono_literals;
#include "framework/MicroService.h"
#include "framework/uCentral_Protocol.h"
namespace OpenWifi {
void CommandManager::run() {
Utils::SetThreadName("cmd:mgr");
Running_ = true;
while(Running_)
{
Poco::Thread::trySleep(30000);
if(!Running_)
break;
Poco::AutoPtr<Poco::Notification> NextMsg(ResponseQueue_.waitDequeueNotification());
while (NextMsg && Running_) {
auto Resp = dynamic_cast<RPCResponseNotification *>(NextMsg.get());
try {
if (Resp != nullptr) {
Poco::JSON::Object::Ptr Payload = Resp->Payload_;
std::string SerialNumberStr = Utils::IntToSerialNumber(Resp->SerialNumber_);
if (!Payload->has(uCentralProtocol::ID)) {
poco_error(Logger(),
fmt::format("({}): Invalid RPC response.", SerialNumberStr));
} else {
uint64_t ID = Payload->get(uCentralProtocol::ID);
if (ID > 1) {
poco_debug(Logger(), fmt::format("({}): Processing {} response.",
SerialNumberStr, ID));
std::lock_guard Lock(LocalMutex_);
auto RPC = OutStandingRequests_.find(ID);
if (RPC == OutStandingRequests_.end()) {
poco_debug(Logger(), fmt::format("({}): RPC {} cannot be found.",
SerialNumberStr, ID));
} else if (RPC->second.SerialNumber != Resp->SerialNumber_) {
poco_debug(
Logger(),
fmt::format("({}): RPC {} serial number mismatch {}!={}.",
SerialNumberStr, ID, RPC->second.SerialNumber,
Resp->SerialNumber_));
} else {
std::shared_ptr<promise_type_t> TmpRpcEntry;
std::chrono::duration<double, std::milli> rpc_execution_time =
std::chrono::high_resolution_clock::now() -
RPC->second.submitted;
poco_debug(Logger(),
fmt::format("({}): Received RPC answer {}. Command={}",
SerialNumberStr, ID,
APCommands::to_string(RPC->second.Command)));
if (RPC->second.Command == APCommands::Commands::script) {
CompleteScriptCommand(RPC->second, Payload, rpc_execution_time);
} else if (RPC->second.Command == APCommands::Commands::telemetry) {
CompleteTelemetryCommand(RPC->second, Payload,
rpc_execution_time);
} else if (RPC->second.Command == APCommands::Commands::configure && RPC->second.rpc_entry==nullptr) {
CompleteConfigureCommand(RPC->second, Payload,
rpc_execution_time);
} else {
StorageService()->CommandCompleted(RPC->second.UUID, Payload,
rpc_execution_time, true);
if (RPC->second.rpc_entry) {
TmpRpcEntry = RPC->second.rpc_entry;
}
RPC->second.State = 0;
OutStandingRequests_.erase(ID);
if (TmpRpcEntry != nullptr)
TmpRpcEntry->set_value(Payload);
}
}
std::vector<GWObjects::CommandDetails> Commands;
if(StorageService()->GetReadyToExecuteCommands(0,200,Commands))
{
for(auto & Cmd: Commands)
{
if(!Running_)
break;
try {
Poco::JSON::Parser P;
bool Sent;
Logger().information(Poco::format("Parsing: %s", Cmd.UUID));
auto Params = P.parse(Cmd.Details).extract<Poco::JSON::Object::Ptr>();
Logger().information(Poco::format("Parsed: %s", Cmd.UUID));
auto Result = PostCommandDisk( Cmd.SerialNumber,
Cmd.Command,
*Params,
Cmd.UUID,
Sent);
if(Sent) {
StorageService()->SetCommandExecuted(Cmd.UUID);
Logger().information(Poco::format("%s: Sent command '%s-%s'", Cmd.SerialNumber, Cmd.Command, Cmd.UUID));
} else {
Logger().information(Poco::format("%s: Could not send command '%s-%s'", Cmd.SerialNumber, Cmd.Command, Cmd.UUID));
}
} catch (const Poco::Exception &E) {
Logger().information(Poco::format("%s: Failed command '%s-%s'", Cmd.SerialNumber, Cmd.Command, Cmd.UUID));
Logger().log(E);
StorageService()->SetCommandExecuted(Cmd.UUID);
} catch (...) {
Logger().information(Poco::format("%s: Exception - hard fail - Failed command '%s-%s'", Cmd.SerialNumber, Cmd.Command, Cmd.UUID));
StorageService()->SetCommandExecuted(Cmd.UUID);
}
}
} catch (const Poco::Exception &E) {
Logger().log(E);
} catch (...) {
poco_warning(Logger(), "Exception occurred during run.");
}
NextMsg = ResponseQueue_.waitDequeueNotification();
}
poco_information(Logger(), "RPC Command processor stopping.");
}
}
}
}
}
bool CommandManager::CompleteTelemetryCommand(
CommandInfo &Command, [[maybe_unused]] const Poco::JSON::Object::Ptr &Payload,
std::chrono::duration<double, std::milli> rpc_execution_time) {
std::shared_ptr<promise_type_t> TmpRpcEntry;
int CommandManager::Start() {
Logger().notice("Starting...");
ManagerThread.start(*this);
JanitorCallback_ = std::make_unique<Poco::TimerCallback<CommandManager>>(*this,&CommandManager::onTimer);
Timer_.setStartInterval( 10000 );
Timer_.setPeriodicInterval(5 * 60 * 1000); // 1 hours
Timer_.start(*JanitorCallback_);
return 0;
}
StorageService()->CommandCompleted(Command.UUID, Payload, rpc_execution_time, true);
if (Command.rpc_entry) {
TmpRpcEntry = Command.rpc_entry;
}
Command.State = 0;
OutStandingRequests_.erase(Command.Id);
if (TmpRpcEntry != nullptr)
TmpRpcEntry->set_value(Payload);
return true;
}
bool CommandManager::CompleteConfigureCommand(
CommandInfo &Command, [[maybe_unused]] const Poco::JSON::Object::Ptr &Payload,
std::chrono::duration<double, std::milli> rpc_execution_time) {
std::shared_ptr<promise_type_t> TmpRpcEntry;
if (Command.rpc_entry) {
TmpRpcEntry = Command.rpc_entry;
}
StorageService()->CommandCompleted(Command.UUID, Payload, rpc_execution_time, true);
if (Payload->has("result")) {
auto Result = Payload->getObject("result");
if (Result->has("status") && Result->has("serial")) {
auto Status = Result->getObject("status");
auto SerialNumber = Result->get("serial").toString();
std::uint64_t Error = Status->get("error");
if (Error == 2) {
StorageService()->RollbackDeviceConfigurationChange(SerialNumber);
} else {
StorageService()->CompleteDeviceConfigurationChange(SerialNumber);
}
}
} else {
}
Command.State = 0;
if (Command.rpc_entry) {
TmpRpcEntry = Command.rpc_entry;
}
OutStandingRequests_.erase(Command.Id);
if (TmpRpcEntry != nullptr)
TmpRpcEntry->set_value(Payload);
return true;
}
bool CommandManager::CompleteScriptCommand(
CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
std::chrono::duration<double, std::milli> rpc_execution_time) {
bool Reply = true;
std::shared_ptr<promise_type_t> TmpRpcEntry;
if (Command.rpc_entry) {
TmpRpcEntry = Command.rpc_entry;
}
if (Command.State == 2) {
// look at the payload to see if we should continue or not...
if (Payload->has("result")) {
auto Result = Payload->getObject("result");
if (Result->has("status")) {
auto Status = Result->getObject("status");
std::uint64_t Error = Status->get("error");
if (Error == 0) {
StorageService()->CommandCompleted(Command.UUID, Payload,
rpc_execution_time, true);
Command.State = 1;
} else {
StorageService()->CommandCompleted(Command.UUID, Payload,
rpc_execution_time, true);
std::string ErrorTxt = Status->get("result");
StorageService()->CancelWaitFile(Command.UUID, ErrorTxt);
Command.State = 0;
}
} else {
}
} else {
Command.State = 0;
}
} else if (Command.State == 1) {
StorageService()->CommandCompleted(Command.UUID, Payload, rpc_execution_time, true);
if (Command.Deferred) {
Reply = false;
}
Command.State = 0;
}
if (Command.State == 0) {
OutStandingRequests_.erase(Command.Id);
}
if (Reply && TmpRpcEntry != nullptr)
TmpRpcEntry->set_value(Payload);
return true;
}
int CommandManager::Start() {
poco_notice(Logger(), "Starting...");
commandTimeOut_ = MicroServiceConfigGetInt("command.timeout", 4 * 60 * 60);
commandRetry_ = MicroServiceConfigGetInt("command.retry", 120);
janitorInterval_ = MicroServiceConfigGetInt("command.janitor", 2 * 60); // 1 hour
queueInterval_ = MicroServiceConfigGetInt("command.queue", 30);
ManagerThread.start(*this);
JanitorCallback_ = std::make_unique<Poco::TimerCallback<CommandManager>>(
*this, &CommandManager::onJanitorTimer);
JanitorTimer_.setStartInterval(10000);
JanitorTimer_.setPeriodicInterval(janitorInterval_ * 1000); // 1 hours
JanitorTimer_.start(*JanitorCallback_, MicroServiceTimerPool());
CommandRunnerCallback_ = std::make_unique<Poco::TimerCallback<CommandManager>>(
*this, &CommandManager::onCommandRunnerTimer);
CommandRunnerTimer_.setStartInterval(10000);
CommandRunnerTimer_.setPeriodicInterval(queueInterval_ * 1000); // 1 hours
CommandRunnerTimer_.start(*CommandRunnerCallback_, MicroServiceTimerPool());
return 0;
}
void CommandManager::Stop() {
poco_notice(Logger(), "Stopping...");
void CommandManager::Stop() {
Logger().notice("Stopping...");
Running_ = false;
JanitorTimer_.stop();
CommandRunnerTimer_.stop();
ResponseQueue_.wakeUpAll();
Timer_.stop();
ManagerThread.wakeUp();
ManagerThread.join();
poco_notice(Logger(), "Stopped...");
}
ManagerThread.join();
}
void CommandManager::WakeUp() {
poco_notice(Logger(), "Waking up...");
ManagerThread.wakeUp();
}
void CommandManager::WakeUp() {
Logger().notice("Waking up...");
ManagerThread.wakeUp();
}
void CommandManager::onJanitorTimer([[maybe_unused]] Poco::Timer &timer) {
std::lock_guard Lock(LocalMutex_);
Utils::SetThreadName("cmd:janitor");
Poco::Logger &MyLogger = Poco::Logger::get("CMD-MGR-JANITOR");
std::string TimeOutError("No response.");
auto now = std::chrono::high_resolution_clock::now();
for (auto request = OutStandingRequests_.begin(); request != OutStandingRequests_.end();) {
std::chrono::duration<double, std::milli> delta = now - request->second.submitted;
if (delta > 10min) {
MyLogger.debug(fmt::format("{}: Command={} for {} Timed out.", request->second.UUID,
APCommands::to_string(request->second.Command),
Utils::IntToSerialNumber(request->second.SerialNumber)));
if ((request->second.Command == APCommands::Commands::script &&
request->second.Deferred) ||
(request->second.Command == APCommands::Commands::trace)) {
StorageService()->CancelWaitFile(request->second.UUID, TimeOutError);
}
StorageService()->SetCommandTimedOut(request->second.UUID);
request = OutStandingRequests_.erase(request);
void CommandManager::onTimer(Poco::Timer & timer) {
std::lock_guard G(Mutex_);
Logger().information("Removing expired commands: start");
auto Now = std::chrono::high_resolution_clock::now();
for(auto i=OutStandingRequests_.begin();i!=OutStandingRequests_.end();) {
std::chrono::duration<double, std::milli> delta = Now - i->second->submitted;
if(delta > 120000ms) {
i = OutStandingRequests_.erase(i);
} else {
++request;
++i;
}
}
poco_information(MyLogger,
fmt::format("Outstanding-requests {}", OutStandingRequests_.size()));
Logger().information("Removing expired commands: done");
}
bool CommandManager::IsCommandRunning(const std::string &C) {
std::lock_guard Lock(LocalMutex_);
return std::any_of(
OutStandingRequests_.begin(), OutStandingRequests_.end(),
[C](const std::pair<std::uint64_t, CommandInfo> &r) { return r.second.UUID == C; });
}
std::shared_ptr<CommandManager::promise_type_t> CommandManager::PostCommand( const std::string &SerialNumber,
const std::string &Method,
const Poco::JSON::Object &Params,
const std::string &UUID,
bool oneway_rpc,
bool disk_only,
bool & Sent) {
void CommandManager::onCommandRunnerTimer([[maybe_unused]] Poco::Timer &timer) {
Utils::SetThreadName("cmd:schdlr");
Poco::Logger &MyLogger = Poco::Logger::get("CMD-MGR-SCHEDULER");
Sent=false;
if(!DeviceRegistry()->Connected(SerialNumber)) {
return nullptr;
}
poco_trace(MyLogger, "Scheduler starting.");
std::stringstream ToSend;
auto Object = std::make_shared<RpcObject>();
try {
CommandTagIndex Idx;
{
std::lock_guard M(Mutex_);
if (oneway_rpc)
Idx.Id = 1;
else
Idx.Id = ++Id_;
Idx.SerialNumber = SerialNumber;
StorageService()->RemovedExpiredCommands();
StorageService()->RemoveTimedOutCommands();
Poco::JSON::Object CompleteRPC;
CompleteRPC.set(uCentralProtocol::JSONRPC, uCentralProtocol::JSONRPC_VERSION);
CompleteRPC.set(uCentralProtocol::ID, Idx.Id);
CompleteRPC.set(uCentralProtocol::METHOD, Method);
CompleteRPC.set(uCentralProtocol::PARAMS, Params);
Poco::JSON::Stringifier::stringify(CompleteRPC, ToSend);
Logger().information(
Poco::format("(%s): Sending command '%s', ID: %lu", SerialNumber, Method, Idx.Id));
std::uint64_t offset = 0;
bool Done = false;
while (!Done) {
std::vector<GWObjects::CommandDetails> Commands;
if (StorageService()->GetReadyToExecuteCommands(offset, 200, Commands)) {
if(Commands.empty()) {
Done=true;
continue;
}
poco_trace(MyLogger, fmt::format("Scheduler about to process {} commands.",
Commands.size()));
for (auto &Cmd : Commands) {
if (!Running_) {
poco_warning(MyLogger,
"Scheduler quitting because service is stopping.");
break;
}
poco_trace(MyLogger,
fmt::format("{}: Serial={} Command={} Starting processing.",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
try {
// Skip an already running command
if (IsCommandRunning(Cmd.UUID)) {
continue;
}
auto now = Utils::Now();
// 2 hour timeout for commands
if ((now - Cmd.Submitted) > commandTimeOut_) {
poco_information(
MyLogger, fmt::format("{}: Serial={} Command={} has expired.",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
StorageService()->SetCommandTimedOut(Cmd.UUID);
continue;
}
auto SerialNumberInt = Utils::SerialNumberToInt(Cmd.SerialNumber);
if (!AP_WS_Server()->Connected(SerialNumberInt)) {
poco_trace(
MyLogger,
fmt::format("{}: Serial={} Command={} Device is not connected.",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
StorageService()->SetCommandLastTry(Cmd.UUID);
continue;
}
std::string ExecutingUUID;
APCommands::Commands ExecutingCommand = APCommands::Commands::unknown;
if (CommandRunningForDevice(SerialNumberInt, ExecutingUUID,
ExecutingCommand)) {
poco_trace(
MyLogger,
fmt::format("{}: Serial={} Command={} Device is already busy "
"with command {} (Command={}).",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command,
ExecutingUUID,
APCommands::to_string(ExecutingCommand)));
continue;
}
Poco::JSON::Parser P;
bool Sent;
poco_information(
MyLogger,
fmt::format("{}: Serial={} Command={} Preparing execution.",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
auto Params = P.parse(Cmd.Details).extract<Poco::JSON::Object::Ptr>();
auto Result = PostCommandDisk(
Next_RPC_ID(), APCommands::to_apcommand(Cmd.Command.c_str()),
Cmd.SerialNumber, Cmd.Command, *Params, Cmd.UUID, Sent);
if (Sent) {
StorageService()->SetCommandExecuted(Cmd.UUID);
poco_debug(MyLogger,
fmt::format("{}: Serial={} Command={} Sent.", Cmd.UUID,
Cmd.SerialNumber, Cmd.Command));
} else {
poco_debug(
MyLogger,
fmt::format("{}: Serial={} Command={} Re-queued command.",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
StorageService()->SetCommandLastTry(Cmd.UUID);
}
} catch (const Poco::Exception &E) {
poco_debug(
MyLogger,
fmt::format(
"{}: Serial={} Command={} Failed. Command marked as completed.",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
MyLogger.log(E);
StorageService()->SetCommandExecuted(Cmd.UUID);
} catch (...) {
poco_debug(MyLogger,
fmt::format("{}: Serial={} Command={} Hard failure. "
"Command marked as completed.",
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
StorageService()->SetCommandExecuted(Cmd.UUID);
}
}
offset += Commands.size();
} else {
Done=true;
continue;
}
Object->submitted = std::chrono::high_resolution_clock::now();
Object->uuid = UUID;
if(disk_only) {
Object->rpc_entry = nullptr;
} else {
Object->rpc_entry = std::make_shared<CommandManager::promise_type_t>();
}
}
catch (Poco::Exception &E) {
MyLogger.log(E);
}
catch (...) {
poco_warning(MyLogger, "Exception during command processing.");
}
poco_trace(MyLogger, "Scheduler done.");
}
std::shared_ptr<CommandManager::promise_type_t> CommandManager::PostCommand(
uint64_t RPC_ID, APCommands::Commands Command, const std::string &SerialNumber,
const std::string &CommandStr, const Poco::JSON::Object &Params, const std::string &UUID,
bool oneway_rpc, [[maybe_unused]] bool disk_only, bool &Sent, bool rpc, bool Deferred) {
auto SerialNumberInt = Utils::SerialNumberToInt(SerialNumber);
Sent = false;
std::stringstream ToSend;
CommandInfo CInfo;
CInfo.Id = oneway_rpc ? 1 : RPC_ID;
CInfo.SerialNumber = SerialNumberInt;
CInfo.Command = Command;
CInfo.Deferred = Deferred;
CInfo.UUID = UUID;
if (Command == APCommands::Commands::script && Deferred) {
CInfo.State = 2;
} else {
CInfo.State = 1;
OutStandingRequests_[Idx] = Object;
}
Poco::JSON::Object CompleteRPC;
CompleteRPC.set(uCentralProtocol::JSONRPC, uCentralProtocol::JSONRPC_VERSION);
CompleteRPC.set(uCentralProtocol::ID, RPC_ID);
CompleteRPC.set(uCentralProtocol::METHOD, CommandStr);
CompleteRPC.set(uCentralProtocol::PARAMS, Params);
Poco::JSON::Stringifier::stringify(CompleteRPC, ToSend);
CInfo.rpc_entry = rpc ? std::make_shared<CommandManager::promise_type_t>() : nullptr;
poco_debug(Logger(), fmt::format("{}: Sending command {} to {}. ID: {}", UUID, CommandStr,
SerialNumber, RPC_ID));
// Do not change the order. It is possible that an RPC completes before it is entered in
// the map. So we insert it first, even if we may need to remove it later upon failure.
if (!oneway_rpc) {
std::lock_guard M(Mutex_);
OutStandingRequests_[RPC_ID] = CInfo;
if(DeviceRegistry()->SendFrame(SerialNumber, ToSend.str())) {
Sent=true;
return Object->rpc_entry;
}
if (AP_WS_Server()->SendFrame(SerialNumber, ToSend.str())) {
poco_debug(Logger(), fmt::format("{}: Sent command. ID: {}", UUID, RPC_ID));
Sent = true;
return CInfo.rpc_entry;
} else if (!oneway_rpc) {
std::lock_guard M(Mutex_);
OutStandingRequests_.erase(RPC_ID);
}
poco_warning(Logger(), fmt::format("{}: Failed to send command. ID: {}", UUID, RPC_ID));
return nullptr;
}
bool CommandManager::FireAndForget(const std::string &SerialNumber, const std::string &Method, const Poco::JSON::Object &Params) {
Poco::JSON::Object CompleteRPC;
CompleteRPC.set(uCentralProtocol::JSONRPC, uCentralProtocol::JSONRPC_VERSION);
CompleteRPC.set(uCentralProtocol::ID, 0);
CompleteRPC.set(uCentralProtocol::METHOD, Method);
CompleteRPC.set(uCentralProtocol::PARAMS, Params);
std::stringstream ToSend;
CompleteRPC.stringify(ToSend);
poco_debug(Logger(), fmt::format("{}: Fire and forget command {}.", SerialNumber, Method));
return AP_WS_Server()->SendFrame(SerialNumber, ToSend.str())>0;
void CommandManager::PostCommandResult(const std::string &SerialNumber, Poco::JSON::Object::Ptr Obj) {
if(!Obj->has(uCentralProtocol::ID)){
Logger().error(Poco::format("(%s): Invalid RPC response.",SerialNumber));
return;
}
uint64_t ID = Obj->get(uCentralProtocol::ID);
if(ID<2) {
Logger().error(Poco::format("(%s): Ignoring RPC response.",SerialNumber));
return;
}
std::lock_guard G(Mutex_);
auto Idx = CommandTagIndex{.Id = ID, .SerialNumber = SerialNumber};
auto RPC = OutStandingRequests_.find(Idx);
if (RPC == OutStandingRequests_.end()) {
Logger().warning(Poco::format("(%s): Outdated RPC %lu", SerialNumber, ID));
return;
}
std::chrono::duration<double, std::milli> rpc_execution_time = std::chrono::high_resolution_clock::now() - RPC->second->submitted;
StorageService()->CommandCompleted(RPC->second->uuid, Obj, rpc_execution_time, true);
if(RPC->second->rpc_entry) {
RPC->second->rpc_entry->set_value(Obj);
}
Logger().information(Poco::format("(%s): Received RPC answer %lu", SerialNumber, ID));
}
} // namespace OpenWifi
} // namespace

View File

@@ -9,194 +9,142 @@
#pragma once
#include <chrono>
#include <functional>
#include <future>
#include <map>
#include <mutex>
#include <utility>
#include <functional>
#include "Poco/JSON/Object.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Notification.h"
#include "Poco/NotificationQueue.h"
#include "Poco/Timer.h"
#include "fmt/format.h"
#include "framework/SubSystemServer.h"
#include "RESTObjects/RESTAPI_GWobjects.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "framework/MicroService.h"
namespace OpenWifi {
class RPCResponseNotification : public Poco::Notification {
public:
RPCResponseNotification(std::uint64_t ser, Poco::JSON::Object::Ptr pl)
: SerialNumber_(ser), Payload_(std::move(pl)) {}
std::uint64_t SerialNumber_;
Poco::JSON::Object::Ptr Payload_;
struct CommandTagIndex {
uint64_t Id=0;
std::string SerialNumber;
};
class CommandManager : public SubSystemServer, Poco::Runnable {
public:
using objtype_t = Poco::JSON::Object::Ptr;
using promise_type_t = std::promise<objtype_t>;
struct CommandInfo {
std::uint64_t Id = 0;
std::uint64_t SerialNumber = 0;
APCommands::Commands Command;
std::string UUID;
std::uint64_t State = 1;
std::chrono::time_point<std::chrono::high_resolution_clock> submitted =
std::chrono::high_resolution_clock::now();
std::shared_ptr<promise_type_t> rpc_entry;
bool Deferred = false;
};
struct RPCResponse {
std::uint64_t serialNumber;
Poco::JSON::Object::Ptr payload;
explicit RPCResponse(std::uint64_t ser, Poco::JSON::Object::Ptr pl)
: serialNumber(ser), payload(std::move(pl)) {}
};
int Start() override;
void Stop() override;
void WakeUp();
inline void PostCommandResult(const std::string &SerialNumber,
Poco::JSON::Object::Ptr Obj) {
ResponseQueue_.enqueueNotification(new RPCResponseNotification(
Utils::SerialNumberToInt(SerialNumber), std::move(Obj)));
}
std::shared_ptr<promise_type_t> PostCommandOneWayDisk(uint64_t RPC_ID,
APCommands::Commands Command,
const std::string &SerialNumber,
const std::string &Method,
const Poco::JSON::Object &Params,
const std::string &UUID, bool &Sent) {
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, true, true,
Sent, false);
}
std::shared_ptr<promise_type_t>
PostCommandDisk(uint64_t RPC_ID, APCommands::Commands Command,
const std::string &SerialNumber, const std::string &Method,
const Poco::JSON::Object &Params, const std::string &UUID, bool &Sent) {
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, false, true,
Sent, false);
}
std::shared_ptr<promise_type_t>
PostCommand(uint64_t RPC_ID, APCommands::Commands Command, const std::string &SerialNumber,
const std::string &Method, const Poco::JSON::Object &Params,
const std::string &UUID, bool &Sent, bool rpc, bool Deferred) {
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, false, false,
Sent, rpc, Deferred);
}
std::shared_ptr<promise_type_t>
PostCommandOneWay(uint64_t RPC_ID, APCommands::Commands Command,
const std::string &SerialNumber, const std::string &Method,
const Poco::JSON::Object &Params, const std::string &UUID, bool &Sent) {
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, true, false,
Sent, false);
}
bool IsCommandRunning(const std::string &C);
void run() override;
static auto instance() {
static auto instance_ = new CommandManager;
return instance_;
}
inline bool Running() const { return Running_; }
void onJanitorTimer(Poco::Timer &timer);
void onCommandRunnerTimer(Poco::Timer &timer);
inline uint64_t Next_RPC_ID() { return ++Id_; }
void RemovePendingCommand(std::uint64_t Id) {
std::unique_lock Lock(LocalMutex_);
OutStandingRequests_.erase(Id);
}
inline bool CommandRunningForDevice(std::uint64_t SerialNumber, std::string &uuid,
APCommands::Commands &command) {
std::lock_guard Lock(LocalMutex_);
for (const auto &[Request, Command] : OutStandingRequests_) {
if (Command.SerialNumber == SerialNumber) {
uuid = Command.UUID;
command = Command.Command;
return true;
}
}
inline bool operator <(const CommandTagIndex& lhs, const CommandTagIndex& rhs) {
if(lhs.Id<rhs.Id)
return true;
if(lhs.Id>rhs.Id)
return false;
}
return lhs.SerialNumber<rhs.SerialNumber;
}
inline void ClearQueue(std::uint64_t SerialNumber) {
std::lock_guard Lock(LocalMutex_);
for (auto Request = OutStandingRequests_.begin();
Request != OutStandingRequests_.end();) {
if (Request->second.SerialNumber == SerialNumber)
Request = OutStandingRequests_.erase(Request);
else
++Request;
inline bool operator ==(const CommandTagIndex& lhs, const CommandTagIndex& rhs) {
if(lhs.Id == rhs.Id && lhs.SerialNumber == rhs.SerialNumber)
return true;
return false;
}
class CommandManager : public SubSystemServer, Poco::Runnable {
public:
typedef Poco::JSON::Object::Ptr objtype_t;
typedef std::promise<objtype_t> promise_type_t;
struct RpcObject {
std::string uuid;
std::chrono::time_point<std::chrono::high_resolution_clock> submitted = std::chrono::high_resolution_clock::now();
std::shared_ptr<promise_type_t> rpc_entry;
};
int Start() override;
void Stop() override;
void WakeUp();
void PostCommandResult(const std::string &SerialNumber, Poco::JSON::Object::Ptr Obj);
std::shared_ptr<promise_type_t> PostCommandOneWayDisk(
const std::string &SerialNumber,
const std::string &Method,
const Poco::JSON::Object &Params,
const std::string &UUID,
bool & Sent) {
return PostCommand(SerialNumber,
Method,
Params,
UUID,
true, true, Sent );
}
}
inline void RemoveCommand(const std::string &UUID) {
std::lock_guard Lock(LocalMutex_);
for (const auto &[Id, Cmd] : OutStandingRequests_) {
if (Cmd.UUID == UUID) {
OutStandingRequests_.erase(Id);
return;
}
std::shared_ptr<promise_type_t> PostCommandDisk(
const std::string &SerialNumber,
const std::string &Method,
const Poco::JSON::Object &Params,
const std::string &UUID,
bool & Sent) {
return PostCommand(SerialNumber,
Method,
Params,
UUID,
false, true, Sent );
}
}
inline auto CommandTimeout() const { return commandTimeOut_; }
inline auto CommandRetry() const { return commandRetry_; }
std::shared_ptr<promise_type_t> PostCommand(
const std::string &SerialNumber,
const std::string &Method,
const Poco::JSON::Object &Params,
const std::string &UUID,
bool & Sent) {
return PostCommand(SerialNumber,
Method,
Params,
UUID,
false,
false, Sent );
}
bool FireAndForget(const std::string &SerialNumber, const std::string &Method,
const Poco::JSON::Object &Params);
private:
mutable std::mutex LocalMutex_;
std::atomic_bool Running_ = false;
Poco::Thread ManagerThread;
std::atomic_uint64_t Id_ = 3; // do not start @1. We ignore ID=1 & 0 is illegal..
std::map<std::uint64_t, CommandInfo> OutStandingRequests_;
Poco::Timer JanitorTimer_;
std::unique_ptr<Poco::TimerCallback<CommandManager>> JanitorCallback_;
Poco::Timer CommandRunnerTimer_;
std::unique_ptr<Poco::TimerCallback<CommandManager>> CommandRunnerCallback_;
Poco::NotificationQueue ResponseQueue_;
std::uint64_t commandTimeOut_ = 0;
std::uint64_t commandRetry_ = 0;
std::uint64_t janitorInterval_ = 0;
std::uint64_t queueInterval_ = 0;
std::shared_ptr<promise_type_t> PostCommandOneWay(
const std::string &SerialNumber,
const std::string &Method,
const Poco::JSON::Object &Params,
const std::string &UUID,
bool & Sent) {
return PostCommand(SerialNumber,
Method,
Params,
UUID,
true,
false, Sent );
}
std::shared_ptr<promise_type_t>
PostCommand(uint64_t RPCID, APCommands::Commands Command, const std::string &SerialNumber,
const std::string &Method, const Poco::JSON::Object &Params,
const std::string &UUID, bool oneway_rpc, bool disk_only, bool &Sent,
bool rpc_call, bool Deferred = false);
void run() override;
bool CompleteScriptCommand(CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
std::chrono::duration<double, std::milli> rpc_execution_time);
bool CompleteTelemetryCommand(CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
std::chrono::duration<double, std::milli> rpc_execution_time);
bool CompleteConfigureCommand(CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
std::chrono::duration<double, std::milli> rpc_execution_time);
static auto instance() {
static auto instance_ = new CommandManager;
return instance_;
}
CommandManager() noexcept
: SubSystemServer("CommandManager", "CMD-MGR", "command.manager") {}
inline bool Running() const { return Running_; }
void onTimer(Poco::Timer & timer);
private:
std::atomic_bool Running_ = false;
Poco::Thread ManagerThread;
uint64_t Id_=3; // do not start @1. We ignore ID=1 & 0 is illegal..
std::map<CommandTagIndex,std::shared_ptr<RpcObject>> OutStandingRequests_;
Poco::Timer Timer_;
std::unique_ptr<Poco::TimerCallback<CommandManager>> JanitorCallback_;
std::shared_ptr<promise_type_t> PostCommand(
const std::string &SerialNumber,
const std::string &Method,
const Poco::JSON::Object &Params,
const std::string &UUID,
bool oneway_rpc,
bool disk_only,
bool & Sent);
CommandManager() noexcept:
SubSystemServer("CommandManager", "CMD-MGR", "command.manager") {
}
};
inline auto CommandManager() { return CommandManager::instance(); }
} // namespace OpenWifi
} // namespace

View File

@@ -5,44 +5,46 @@
#pragma once
#include <map>
#include <mutex>
#include <string>
#include <mutex>
#include "framework/MicroService.h"
namespace OpenWifi {
class ConfigurationCache {
public:
static auto instance() {
static auto instance = new ConfigurationCache;
static ConfigurationCache & instance() {
static ConfigurationCache instance;
return instance;
}
inline uint64_t GetCurrentConfig(std::uint64_t SerialNumber) {
inline uint64_t CurrentConfig(uint64_t SerialNumber) {
std::lock_guard G(Mutex_);
const auto Hint = Cache_.find(SerialNumber);
if (Hint == end(Cache_))
if(Hint==end(Cache_))
return 0;
return Hint->second;
}
inline void SetCurrentConfig(std::uint64_t SerialNumber, uint64_t Id) {
std::lock_guard G(Mutex_);
Cache_[SerialNumber] = Id;
inline void Add(uint64_t SerialNumber, uint64_t Id) {
std::lock_guard G(Mutex_);
Cache_[SerialNumber]=Id;
}
private:
std::mutex Mutex_;
std::map<uint64_t, uint64_t> Cache_;
std::recursive_mutex Mutex_;
std::map<uint64_t,uint64_t> Cache_;
};
inline auto GetCurrentConfigurationID(std::uint64_t SerialNumber) {
return ConfigurationCache::instance()->GetCurrentConfig(SerialNumber);
inline uint64_t GetCurrentConfigurationID(uint64_t SerialNumber) {
return ConfigurationCache::instance().CurrentConfig(SerialNumber);
}
inline void SetCurrentConfigurationID(const std::string &SerialNumber, std::uint64_t ID) {
return ConfigurationCache::instance()->SetCurrentConfig(Utils::SerialNumberToInt(SerialNumber), ID);
inline void SetCurrentConfigurationID(const std::string & SerialNumber, uint64_t ID) {
return ConfigurationCache::instance().Add(Utils::SerialNumberToInt(SerialNumber), ID);
}
inline void SetCurrentConfigurationID(uint64_t SerialNumber, std::uint64_t ID) {
return ConfigurationCache::instance()->SetCurrentConfig(SerialNumber, ID);
inline void SetCurrentConfigurationID(uint64_t SerialNumber, uint64_t ID) {
return ConfigurationCache::instance().Add(SerialNumber, ID);
}
} // namespace OpenWifi
}

View File

@@ -6,107 +6,114 @@
// Arilia Wireless Inc.
//
#include "Poco/Environment.h"
#include "Poco/Net/SSLManager.h"
#include <boost/algorithm/string.hpp>
#include "Poco/Util/Application.h"
#include "Poco/Util/Option.h"
#include "Poco/Environment.h"
#include <framework/ConfigurationValidator.h>
#include <framework/UI_WebSocketClientServer.h>
#include <framework/default_device_types.h>
#include "AP_WS_Server.h"
#include "CentralConfig.h"
#include "CommandManager.h"
#include "Daemon.h"
#include "DeviceRegistry.h"
#include "FileUploader.h"
#include "FindCountry.h"
#include "OUIServer.h"
#include "RADIUSSessionTracker.h"
#include "RADIUS_proxy_server.h"
#include "RegulatoryInfo.h"
#include "ScriptManager.h"
#include "SerialNumberCache.h"
#include "SignatureMgr.h"
#include "StorageArchiver.h"
#include "StorageService.h"
#include "TelemetryStream.h"
#include "GenericScheduler.h"
#include "UI_GW_WebSocketNotifications.h"
#include "VenueBroadcaster.h"
#include "AP_WS_ConfigAutoUpgrader.h"
#include "WS_Server.h"
#include "framework/ConfigurationValidator.h"
#include "framework/MicroService.h"
#include "FindCountry.h"
#include "rttys/RTTYS_server.h"
#include "firmware_revision_cache.h"
namespace OpenWifi {
class Daemon *Daemon::instance() {
static Daemon instance(
vDAEMON_PROPERTIES_FILENAME, vDAEMON_ROOT_ENV_VAR, vDAEMON_CONFIG_ENV_VAR,
vDAEMON_APP_NAME, vDAEMON_BUS_TIMER,
SubSystemVec{GenericScheduler(), StorageService(), SerialNumberCache(), ConfigurationValidator(),
UI_WebSocketClientServer(), OUIServer(), FindCountryFromIP(),
CommandManager(), FileUploader(), StorageArchiver(), TelemetryStream(),
RTTYS_server(), RADIUS_proxy_server(), VenueBroadcaster(), ScriptManager(),
SignatureManager(), AP_WS_Server(),
RegulatoryInfo(),
RADIUSSessionTracker(),
AP_WS_ConfigAutoUpgradeAgent(),
FirmwareRevisionCache()
});
return &instance;
static Daemon instance(vDAEMON_PROPERTIES_FILENAME,
vDAEMON_ROOT_ENV_VAR,
vDAEMON_CONFIG_ENV_VAR,
vDAEMON_APP_NAME,
vDAEMON_BUS_TIMER,
SubSystemVec{
StorageService(),
SerialNumberCache(),
ConfigurationValidator(),
OUIServer(),
FindCountryFromIP(),
DeviceRegistry(),
CommandManager(),
FileUploader(),
StorageArchiver(),
TelemetryStream(),
RTTYS_server(),
WebSocketServer()
});
return &instance;
}
static std::string ALBHealthCallback() {
uint64_t Connections, AverageConnectionTime, NumberOfConnectingDevices;
AP_WS_Server()->AverageDeviceStatistics(Connections, AverageConnectionTime,
NumberOfConnectingDevices);
std::ostringstream os;
os << "Connections: " << Connections << std::endl <<
"ConnectingDevices: " << NumberOfConnectingDevices << std::endl <<
"ConnectionTime: " << AverageConnectionTime << std::endl;
return os.str();
static const std::vector<std::pair<std::string,std::string>> DefaultDeviceTypes{
{"cig_wf160d","AP"},
{"cig_wf188","AP"},
{"cig_wf188n","AP"},
{"cig_wf194c","AP"},
{"cig_wf194c4","AP"},
{"edgecore_eap101","AP"},
{"edgecore_eap102","AP"},
{"edgecore_ecs4100-12ph","AP"},
{"edgecore_ecw5211","AP"},
{"edgecore_ecw5410","AP"},
{"edgecore_oap100","AP"},
{"edgecore_spw2ac1200","SWITCH"},
{"edgecore_spw2ac1200-lan-poe","SWITCH"},
{"edgecore_ssw2ac2600","SWITCH"},
{"hfcl_ion4","AP"},
{"indio_um-305ac","AP"},
{"linksys_e8450-ubi","AP"},
{"linksys_ea6350","AP"},
{"linksys_ea6350-v4","AP"},
{"linksys_ea8300","AP"},
{"mikrotik_nand","AP"},
{"tp-link_ec420-g1","AP"},
{"tplink_cpe210_v3","AP"},
{"tplink_cpe510_v3","AP"},
{"tplink_eap225_outdoor_v1","AP"},
{"tplink_ec420","AP"},
{"tplink_ex227","AP"},
{"tplink_ex228","AP"},
{"tplink_ex447","AP"},
{"wallys_dr40x9","AP"}
};
void Daemon::initialize() {
AutoProvisioning_ = config().getBool("openwifi.autoprovisioning",false);
DeviceTypes_ = DefaultDeviceTypes;
}
void MicroServicePostInitialization() {
Daemon()->initialize();
}
void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) {
AutoProvisioning_ = config().getBool("openwifi.autoprovisioning", false);
DeviceTypes_ = DefaultDeviceTypeList;
WebSocketProcessor_ = std::make_unique<GwWebSocketClient>(logger());
MicroServiceALBCallback(ALBHealthCallback);
}
[[nodiscard]] std::string Daemon::IdentifyDevice(const std::string &Id) const {
for (const auto &[DeviceType, Type] : DeviceTypes_) {
if (Id == DeviceType)
return Type;
}
return Platforms::AP;
}
void DaemonPostInitialization(Poco::Util::Application &self) {
Daemon()->PostInitialization(self);
GWWebSocketNotifications::Register();
}
} // namespace OpenWifi
[[nodiscard]] std::string Daemon::IdentifyDevice(const std::string & Id ) const {
for(const auto &[DeviceType,Type]:DeviceTypes_)
{
if(Id == DeviceType)
return Type;
}
return "AP";
}
}
int main(int argc, char **argv) {
int ExitCode;
try {
Poco::Net::SSLManager::instance().initializeServer(nullptr, nullptr, nullptr);
auto App = OpenWifi::Daemon::instance();
ExitCode = App->run(argc, argv);
Poco::Net::SSLManager::instance().shutdown();
} catch (Poco::Exception &exc) {
ExitCode = Poco::Util::Application::EXIT_SOFTWARE;
std::cout << exc.displayText() << std::endl;
} catch (std::exception &exc) {
ExitCode = Poco::Util::Application::EXIT_TEMPFAIL;
std::cout << exc.what() << std::endl;
} catch (...) {
ExitCode = Poco::Util::Application::EXIT_TEMPFAIL;
std::cout << "Exception on closure" << std::endl;
}
std::cout << "Exitcode: " << ExitCode << std::endl;
return ExitCode;
auto App = OpenWifi::Daemon::instance();
auto ExitCode = App->run(argc, argv);
return ExitCode;
} catch (Poco::Exception &exc) {
std::cerr << exc.displayText() << std::endl;
return Poco::Util::Application::EXIT_SOFTWARE;
}
}
// end of namespace

View File

@@ -9,47 +9,56 @@
#pragma once
#include <array>
#include <cstdlib>
#include <iostream>
#include <set>
#include <cstdlib>
#include <vector>
#include <set>
#include "framework/MicroService.h"
#include "framework/MicroServiceNames.h"
#include "Poco/Util/Application.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/UUIDGenerator.h"
#include "Poco/ErrorHandler.h"
#include "Poco/Crypto/RSAKey.h"
#include "Poco/Crypto/CipherFactory.h"
#include "Poco/Crypto/Cipher.h"
#include "Dashboard.h"
#include "GwWebSocketClient.h"
#include "framework/MicroService.h"
#include "framework/OpenWifiTypes.h"
namespace OpenWifi {
[[maybe_unused]] static const char *vDAEMON_PROPERTIES_FILENAME = "owgw.properties";
[[maybe_unused]] static const char *vDAEMON_ROOT_ENV_VAR = "OWGW_ROOT";
[[maybe_unused]] static const char *vDAEMON_CONFIG_ENV_VAR = "OWGW_CONFIG";
[[maybe_unused]] static const char *vDAEMON_APP_NAME = uSERVICE_GATEWAY.c_str();
[[maybe_unused]] static const uint64_t vDAEMON_BUS_TIMER = 10000;
static const char * vDAEMON_PROPERTIES_FILENAME = "owgw.properties";
static const char * vDAEMON_ROOT_ENV_VAR = "OWGW_ROOT";
static const char * vDAEMON_CONFIG_ENV_VAR = "OWGW_CONFIG";
static const char * vDAEMON_APP_NAME = uSERVICE_GATEWAY.c_str();
static const uint64_t vDAEMON_BUS_TIMER = 10000;
class Daemon : public MicroService {
public:
explicit Daemon(const std::string &PropFile, const std::string &RootEnv,
const std::string &ConfigEnv, const std::string &AppName, uint64_t BusTimer,
const SubSystemVec &SubSystems)
: MicroService(PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems){};
class Daemon : public MicroService {
public:
explicit Daemon(const std::string & PropFile,
const std::string & RootEnv,
const std::string & ConfigEnv,
const std::string & AppName,
uint64_t BusTimer,
const SubSystemVec & SubSystems) :
MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {};
bool AutoProvisioning() const { return AutoProvisioning_; }
[[nodiscard]] std::string IdentifyDevice(const std::string &Compatible) const;
static Daemon *instance();
inline DeviceDashboard &GetDashboard() { return DB_; }
Poco::Logger &Log() { return Poco::Logger::get(AppName()); }
void PostInitialization(Poco::Util::Application &self);
bool AutoProvisioning() const { return AutoProvisioning_ ; }
[[nodiscard]] std::string IdentifyDevice(const std::string & Compatible) const;
void initialize();
static Daemon *instance();
inline DeviceDashboard & GetDashboard() { return DB_; }
Poco::Logger & Log() { return Poco::Logger::get(AppName()); }
private:
bool AutoProvisioning_ = false;
std::vector<std::pair<std::string,std::string>> DeviceTypes_;
DeviceDashboard DB_;
private:
bool AutoProvisioning_ = false;
std::vector<std::pair<std::string, std::string>> DeviceTypes_;
DeviceDashboard DB_;
std::unique_ptr<GwWebSocketClient> WebSocketProcessor_;
};
};
inline Daemon * Daemon() { return Daemon::instance(); }
}
inline Daemon *Daemon() { return Daemon::instance(); }
void DaemonPostInitialization(Poco::Util::Application &self);
} // namespace OpenWifi

View File

@@ -3,46 +3,18 @@
//
#include "Dashboard.h"
#include "DeviceRegistry.h"
#include "StorageService.h"
#include "framework/utils.h"
namespace OpenWifi {
void DeviceDashboard::Create() {
uint64_t Now = std::time(nullptr);
bool DeviceDashboard::Get(GWObjects::Dashboard &D, Poco::Logger &Logger) {
uint64_t Now = Utils::Now();
if (!ValidDashboard_ || LastRun_ == 0 || (Now - LastRun_) > 120) {
Generate(D, Logger);
} else {
std::lock_guard G(DataMutex_);
D = DB_;
}
return ValidDashboard_;
};
void DeviceDashboard::Generate(GWObjects::Dashboard &D, Poco::Logger &Logger) {
if (GeneratingDashboard_.load()) {
while (GeneratingDashboard_.load()) {
Poco::Thread::trySleep(100);
}
std::lock_guard G(DataMutex_);
D = DB_;
} else {
GeneratingDashboard_ = true;
ValidDashboard_ = false;
try {
poco_information(Logger, "DASHBOARD: Generating a new dashboard.");
GWObjects::Dashboard NewData;
StorageService()->AnalyzeCommands(NewData.commands);
StorageService()->AnalyzeDevices(NewData);
LastRun_ = Utils::Now();
NewData.snapshot = LastRun_;
D = NewData;
std::lock_guard G(DataMutex_);
DB_ = NewData;
ValidDashboard_ = true;
} catch (...) {
}
GeneratingDashboard_ = false;
if(LastRun_==0 || (Now-LastRun_)>120) {
DB_.reset();
StorageService()->AnalyzeCommands(DB_.commands);
StorageService()->AnalyzeDevices(DB_);
LastRun_ = Now;
}
}
} // namespace OpenWifi
}

View File

@@ -4,24 +4,19 @@
#pragma once
#include <mutex>
#include "Poco/Logger.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "framework/OpenWifiTypes.h"
namespace OpenWifi {
class DeviceDashboard {
public:
bool Get(GWObjects::Dashboard &D, Poco::Logger &Logger);
DeviceDashboard() { DB_.reset(); }
void Create();
[[nodiscard]] const GWObjects::Dashboard & Report() const { return DB_;}
private:
std::mutex DataMutex_;
volatile std::atomic_bool GeneratingDashboard_ = false;
volatile bool ValidDashboard_ = false;
GWObjects::Dashboard DB_;
uint64_t LastRun_ = 0;
void Generate(GWObjects::Dashboard &D, Poco::Logger &Logger);
GWObjects::Dashboard DB_;
uint64_t LastRun_=0;
inline void Reset() { DB_.reset(); }
};
} // namespace OpenWifi
}

150
src/DeviceRegistry.cpp Normal file
View File

@@ -0,0 +1,150 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include "Poco/JSON/Object.h"
#include "Poco/JSON/Parser.h"
#include "DeviceRegistry.h"
#include "WS_Server.h"
#include "OUIServer.h"
namespace OpenWifi {
int DeviceRegistry::Start() {
std::lock_guard Guard(Mutex_);
Logger().notice("Starting ");
return 0;
}
void DeviceRegistry::Stop() {
std::lock_guard Guard(Mutex_);
Logger().notice("Stopping ");
}
bool DeviceRegistry::GetStatistics(uint64_t SerialNumber, std::string & Statistics) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device == Devices_.end())
return false;
Statistics = Device->second->LastStats;
return true;
}
void DeviceRegistry::SetStatistics(uint64_t SerialNumber, const std::string &Statistics) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device != Devices_.end())
{
Device->second->Conn_.LastContact = time(nullptr);
Device->second->LastStats = Statistics;
}
}
bool DeviceRegistry::GetState(uint64_t SerialNumber, GWObjects::ConnectionState & State) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device == Devices_.end())
return false;
State = Device->second->Conn_;
return true;
}
void DeviceRegistry::SetState(uint64_t SerialNumber, const GWObjects::ConnectionState & State) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device != Devices_.end())
{
Device->second->Conn_.LastContact = time(nullptr);
Device->second->Conn_ = State;
}
}
bool DeviceRegistry::GetHealthcheck(uint64_t SerialNumber, GWObjects::HealthCheck & CheckData) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device != Devices_.end()) {
CheckData = Device->second->LastHealthcheck;
return true;
}
return false;
}
void DeviceRegistry::SetHealthcheck(uint64_t SerialNumber, const GWObjects::HealthCheck & CheckData) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device != Devices_.end())
{
Device->second->LastHealthcheck = CheckData;
}
}
std::shared_ptr<DeviceRegistry::ConnectionEntry> DeviceRegistry::Register(uint64_t SerialNumber, WSConnection *Ptr, uint64_t & ConnectionId )
{
std::lock_guard Guard(Mutex_);
const auto & E = Devices_[SerialNumber] = std::make_shared<ConnectionEntry>();
E->WSConn_ = Ptr;
E->Conn_.LastContact = std::time(nullptr);
E->Conn_.Connected = true ;
E->Conn_.UUID = 0 ;
E->Conn_.MessageCount = 0 ;
E->Conn_.Address = "";
E->Conn_.TX = 0 ;
E->Conn_.RX = 0;
E->Conn_.VerifiedCertificate = GWObjects::CertificateValidation::NO_CERTIFICATE;
ConnectionId = E->ConnectionId = ++Id_;
return E;
}
bool DeviceRegistry::Connected(uint64_t SerialNumber) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device == Devices_.end())
return false;
return Device->second->Conn_.Connected;
}
void DeviceRegistry::UnRegister(uint64_t SerialNumber, uint64_t ConnectionId) {
std::lock_guard Guard(Mutex_);
auto It = Devices_.find(SerialNumber);
if(It!=Devices_.end()) {
if(It->second->ConnectionId == ConnectionId)
Devices_.erase(SerialNumber);
}
}
bool DeviceRegistry::SendFrame(uint64_t SerialNumber, const std::string & Payload) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device!=Devices_.end() && Device->second->WSConn_!= nullptr) {
try {
return Device->second->WSConn_->Send(Payload);
} catch (...) {
Logger().debug(Poco::format("Could not send data to device '%s'", SerialNumber));
Device->second->Conn_.Address = "";
Device->second->WSConn_ = nullptr;
Device->second->Conn_.Connected = false;
Device->second->Conn_.VerifiedCertificate = GWObjects::NO_CERTIFICATE;
}
}
return false;
}
void DeviceRegistry::SetPendingUUID(uint64_t SerialNumber, uint64_t PendingUUID) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device!=Devices_.end()) {
Device->second->Conn_.PendingUUID = PendingUUID;
}
}
} // namespace

118
src/DeviceRegistry.h Normal file
View File

@@ -0,0 +1,118 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#pragma once
#include "Poco/JSON/Object.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "framework/MicroService.h"
// class uCentral::WebSocket::WSConnection;
namespace OpenWifi {
class WSConnection;
class DeviceRegistry : public SubSystemServer {
public:
struct ConnectionEntry {
WSConnection * WSConn_ = nullptr;
GWObjects::ConnectionState Conn_;
std::string LastStats;
GWObjects::HealthCheck LastHealthcheck;
uint64_t ConnectionId=0;
};
static auto instance() {
static auto instance_ = new DeviceRegistry;
return instance_;
}
int Start() override;
void Stop() override;
inline bool GetStatistics(const std::string &SerialNumber, std::string & Statistics) {
return GetStatistics(Utils::SerialNumberToInt(SerialNumber),Statistics);
}
bool GetStatistics(uint64_t SerialNumber, std::string & Statistics);
inline void SetStatistics(const std::string &SerialNumber, const std::string &Statistics) {
return SetStatistics(Utils::SerialNumberToInt(SerialNumber),Statistics);
}
void SetStatistics(uint64_t SerialNumber, const std::string &stats);
inline bool GetState(const std::string & SerialNumber, GWObjects::ConnectionState & State) {
return GetState(Utils::SerialNumberToInt(SerialNumber), State);
}
bool GetState(uint64_t SerialNumber, GWObjects::ConnectionState & State);
inline void SetState(const std::string & SerialNumber, const GWObjects::ConnectionState & State) {
return SetState(Utils::SerialNumberToInt(SerialNumber), State);
}
void SetState(uint64_t SerialNumber, const GWObjects::ConnectionState & State);
inline bool GetHealthcheck(const std::string &SerialNumber, GWObjects::HealthCheck & CheckData) {
return GetHealthcheck(Utils::SerialNumberToInt(SerialNumber), CheckData);
}
bool GetHealthcheck(uint64_t SerialNumber, GWObjects::HealthCheck & CheckData);
inline void SetHealthcheck(const std::string &SerialNumber, const GWObjects::HealthCheck &H) {
return SetHealthcheck(Utils::SerialNumberToInt(SerialNumber),H);
}
void SetHealthcheck(uint64_t SerialNumber, const GWObjects::HealthCheck &H);
std::shared_ptr<ConnectionEntry> Register(uint64_t SerialNumber, WSConnection *Conn, uint64_t & ConnectionId);
inline void UnRegister(const std::string & SerialNumber, uint64_t ConnectionId) {
return UnRegister(Utils::SerialNumberToInt(SerialNumber),ConnectionId);
}
void UnRegister(uint64_t SerialNumber, uint64_t ConnectionId);
inline bool Connected(const std::string & SerialNumber) {
return Connected(Utils::SerialNumberToInt(SerialNumber));
}
bool Connected(uint64_t SerialNumber);
inline bool SendFrame(const std::string & SerialNumber, const std::string & Payload) {
return SendFrame(Utils::SerialNumberToInt(SerialNumber), Payload);
}
bool SendFrame(uint64_t SerialNumber, const std::string & Payload);
inline void SetPendingUUID(const std::string & SerialNumber, uint64_t PendingUUID) {
return SetPendingUUID(Utils::SerialNumberToInt(SerialNumber), PendingUUID);
}
void SetPendingUUID(uint64_t SerialNumber, uint64_t PendingUUID);
[[nodiscard]] inline std::shared_ptr<ConnectionEntry> GetDeviceConnection(const std::string & SerialNumber) {
return GetDeviceConnection(Utils::SerialNumberToInt(SerialNumber));
}
[[nodiscard]] inline std::shared_ptr<ConnectionEntry> GetDeviceConnection(uint64_t SerialNumber) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device!=Devices_.end() && Device->second->WSConn_!= nullptr) {
return Device->second;
}
return nullptr;
}
private:
inline static std::atomic_uint64_t Id_=1;
std::map<uint64_t ,std::shared_ptr<ConnectionEntry>> Devices_;
DeviceRegistry() noexcept:
SubSystemServer("DeviceRegistry", "DevStatus", "devicestatus") {
}
};
inline auto DeviceRegistry() { return DeviceRegistry::instance(); }
} // namespace

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