mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralgw.git
synced 2025-12-24 22:37:04 +00:00
Compare commits
326 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e727ac1de | ||
|
|
4c8017c170 | ||
|
|
bc8cf59626 | ||
|
|
c48d129f4a | ||
|
|
673f506ff0 | ||
|
|
ac078ec1b8 | ||
|
|
e623774aa6 | ||
|
|
2007998ae1 | ||
|
|
8050228b40 | ||
|
|
a73ea9b260 | ||
|
|
334728ca4e | ||
|
|
33e010bf78 | ||
|
|
657c96f8cd | ||
|
|
4c91742d8f | ||
|
|
5cd421a4b9 | ||
|
|
b5a0c96927 | ||
|
|
e5a22a1af2 | ||
|
|
4f1fa18cf6 | ||
|
|
bc04d0b774 | ||
|
|
598a15100d | ||
|
|
3227883074 | ||
|
|
4a4c771e52 | ||
|
|
2f45a6955d | ||
|
|
78015d2ecf | ||
|
|
9235495e2c | ||
|
|
84e4a201e7 | ||
|
|
89522132d0 | ||
|
|
780cf3e18f | ||
|
|
6ba3c6d713 | ||
|
|
eae89452ff | ||
|
|
33884c8b81 | ||
|
|
1b33470d69 | ||
|
|
625ace2d50 | ||
|
|
09b50cde9b | ||
|
|
1c6022c227 | ||
|
|
960640cc05 | ||
|
|
ebc4fd6f4c | ||
|
|
2542635aa8 | ||
|
|
8150daa6d5 | ||
|
|
06af6eb5a1 | ||
|
|
fcc765c981 | ||
|
|
98db7f19d1 | ||
|
|
904d034d5f | ||
|
|
52fa1ac922 | ||
|
|
7790cde143 | ||
|
|
1d294c86a2 | ||
|
|
45ba1d94ba | ||
|
|
bdcb1aebce | ||
|
|
cb276b3246 | ||
|
|
eeac5844f6 | ||
|
|
396462c5a2 | ||
|
|
b83b15d2a3 | ||
|
|
1a6fb2a277 | ||
|
|
c2012bcc00 | ||
|
|
15f938fc07 | ||
|
|
8718bb882d | ||
|
|
bc38160b71 | ||
|
|
99c037b1ed | ||
|
|
c66ab909d4 | ||
|
|
cf811a5767 | ||
|
|
d7b5d7fda3 | ||
|
|
797165f10d | ||
|
|
32f094562a | ||
|
|
bc8f714362 | ||
|
|
ec0f2ae59e | ||
|
|
8f34181ae5 | ||
|
|
92f14f517a | ||
|
|
caf1ec9381 | ||
|
|
6b49c684c5 | ||
|
|
5c85694200 | ||
|
|
5425366f0c | ||
|
|
6ea604c64f | ||
|
|
816885273d | ||
|
|
da6a49cda6 | ||
|
|
0b02e0d8fd | ||
|
|
f03a5ca216 | ||
|
|
cc1aac5520 | ||
|
|
fb265ff767 | ||
|
|
746458d6ac | ||
|
|
63d2bcde53 | ||
|
|
68827af75b | ||
|
|
ba525dfc19 | ||
|
|
174c0fa430 | ||
|
|
e50392d837 | ||
|
|
d28d70e5b9 | ||
|
|
240950787c | ||
|
|
e6da06e22c | ||
|
|
7edbdb719c | ||
|
|
992616c01b | ||
|
|
a17529d0c4 | ||
|
|
c1d4082401 | ||
|
|
fabea09f3d | ||
|
|
bb577a3fc1 | ||
|
|
1d5007ab6a | ||
|
|
624f995966 | ||
|
|
e78ed60974 | ||
|
|
df769135b6 | ||
|
|
8a77ba8c76 | ||
|
|
d0931d95d5 | ||
|
|
a4c6d26a6c | ||
|
|
6b0594a393 | ||
|
|
9844b8a6b8 | ||
|
|
3d1887c5a8 | ||
|
|
ed845b6a0d | ||
|
|
7d58000caa | ||
|
|
0f3c847c60 | ||
|
|
d5bc014618 | ||
|
|
104da1278b | ||
|
|
6933602706 | ||
|
|
b7df12e7b0 | ||
|
|
029bc8e3df | ||
|
|
ed66a2cc2b | ||
|
|
25383df599 | ||
|
|
25fb62403d | ||
|
|
c928c1492b | ||
|
|
958b07b0df | ||
|
|
642c2bb914 | ||
|
|
0486907e33 | ||
|
|
6b92d0619c | ||
|
|
7093999c19 | ||
|
|
ef2e4069d0 | ||
|
|
3fdb6a6177 | ||
|
|
3b9d3ee422 | ||
|
|
a4ea9744cc | ||
|
|
1aa3af7cec | ||
|
|
10cced8d5e | ||
|
|
de85d4e517 | ||
|
|
a06a6a2b27 | ||
|
|
1a4af66a8a | ||
|
|
6db4feeedd | ||
|
|
dfdfd06ab1 | ||
|
|
842995f7f6 | ||
|
|
ba01cfa02b | ||
|
|
329dfbbcde | ||
|
|
ac1b5df59a | ||
|
|
8f2dd3ec6e | ||
|
|
8edf3af709 | ||
|
|
4392b37c0b | ||
|
|
286607fc5d | ||
|
|
4f15fb055d | ||
|
|
3864082269 | ||
|
|
076ad66754 | ||
|
|
cd5d8365fb | ||
|
|
1b4ba18d70 | ||
|
|
80502c7414 | ||
|
|
dea0e371fd | ||
|
|
df9c11b640 | ||
|
|
fef2e00fef | ||
|
|
9f27ed2d64 | ||
|
|
a9ebb18609 | ||
|
|
7b2d557d90 | ||
|
|
fe50bdc8c5 | ||
|
|
3ae4676df2 | ||
|
|
ff78235bf7 | ||
|
|
68d11560ee | ||
|
|
b468952e04 | ||
|
|
aec6e6f71d | ||
|
|
1020473e54 | ||
|
|
3433a13b62 | ||
|
|
b9ecb8632a | ||
|
|
be97cca949 | ||
|
|
0c46a0a957 | ||
|
|
3e0d091f4d | ||
|
|
1387c67219 | ||
|
|
fd02712159 | ||
|
|
c75689800e | ||
|
|
b094709d4c | ||
|
|
f1a6f42b8a | ||
|
|
b8673c2dcc | ||
|
|
13e8ceb7e3 | ||
|
|
6c112c21a7 | ||
|
|
1ca76459fe | ||
|
|
52ebfd30b9 | ||
|
|
44822e5a63 | ||
|
|
2561fb5cd4 | ||
|
|
96f25fd949 | ||
|
|
ccbc6307e6 | ||
|
|
39e625bb34 | ||
|
|
8fd4e57fe8 | ||
|
|
192b2d50b4 | ||
|
|
6dd67a8218 | ||
|
|
3edfb11c33 | ||
|
|
fd607bf963 | ||
|
|
c68cd6aad8 | ||
|
|
f4674e93c7 | ||
|
|
21a4eb2a71 | ||
|
|
ed336bec0c | ||
|
|
b5f8063ab7 | ||
|
|
69dc1b8056 | ||
|
|
a933376bf6 | ||
|
|
21a4245204 | ||
|
|
bcfbc0d2c0 | ||
|
|
f453205a5d | ||
|
|
a8d3ed0dba | ||
|
|
42ab4717d5 | ||
|
|
a36796ab4e | ||
|
|
cd9a4f9902 | ||
|
|
806f39bc88 | ||
|
|
6afeb470d9 | ||
|
|
991d60e019 | ||
|
|
5cebcdd675 | ||
|
|
fdb09654c2 | ||
|
|
a657ce2f52 | ||
|
|
7b17af5575 | ||
|
|
cc2b9e536f | ||
|
|
1a8a0b2ebc | ||
|
|
c808d0146c | ||
|
|
0bb107b48d | ||
|
|
a4f2b8fd5e | ||
|
|
4fd9b4f540 | ||
|
|
d51980e0e5 | ||
|
|
5ece25ebac | ||
|
|
d578174b23 | ||
|
|
08689107da | ||
|
|
0d680934f8 | ||
|
|
907d739943 | ||
|
|
e5665769ef | ||
|
|
8abbc73546 | ||
|
|
0c86ffdfff | ||
|
|
c0f59756ae | ||
|
|
41dd567630 | ||
|
|
6d87fafbc0 | ||
|
|
dddc4f34ac | ||
|
|
9a99bcd2c2 | ||
|
|
e849a3eba0 | ||
|
|
decb2bf8c2 | ||
|
|
0ae5dc5cf9 | ||
|
|
9160497b28 | ||
|
|
9a4e19c651 | ||
|
|
62bfb10ca3 | ||
|
|
9e3d80f1ea | ||
|
|
fc3b516c76 | ||
|
|
ac183fcde6 | ||
|
|
7ebc977601 | ||
|
|
ec94cdb2df | ||
|
|
9c61451f0f | ||
|
|
d08afb6d75 | ||
|
|
087ea5372b | ||
|
|
b882796c90 | ||
|
|
91c6a8fba1 | ||
|
|
2e69ca7444 | ||
|
|
58c08c3ff7 | ||
|
|
737f1146a1 | ||
|
|
0f85f2453b | ||
|
|
a8c4cf1940 | ||
|
|
cf320e63ee | ||
|
|
95a11e8f96 | ||
|
|
bf611d36cc | ||
|
|
b543e03660 | ||
|
|
c1e101366e | ||
|
|
47f21ae17e | ||
|
|
c1dd0151f9 | ||
|
|
80efb654d5 | ||
|
|
2ef9a9220f | ||
|
|
c27edec0f7 | ||
|
|
c8a65d6137 | ||
|
|
f408a44898 | ||
|
|
4f289427ca | ||
|
|
2b89cc187b | ||
|
|
59dc2ad032 | ||
|
|
0d6e2c0e33 | ||
|
|
37a02b699d | ||
|
|
9772e95238 | ||
|
|
f4d86120c4 | ||
|
|
384d11d998 | ||
|
|
5f8ea6a474 | ||
|
|
9769a9fa24 | ||
|
|
4d16155aec | ||
|
|
00ca2a6d92 | ||
|
|
ecddb5ba63 | ||
|
|
6fe5298602 | ||
|
|
1c3c7ec842 | ||
|
|
e547ee40b0 | ||
|
|
81806df5cb | ||
|
|
3b9c574149 | ||
|
|
4159ba4dca | ||
|
|
a1eb8ab3d0 | ||
|
|
2fb316b79e | ||
|
|
ad3319496d | ||
|
|
38a7d35c4b | ||
|
|
3c6e24e50f | ||
|
|
b127f38081 | ||
|
|
82cdc4cc03 | ||
|
|
2566237c91 | ||
|
|
562c952d52 | ||
|
|
23d160cba1 | ||
|
|
8cbe0b7c4f | ||
|
|
d804dad1f0 | ||
|
|
72595922eb | ||
|
|
f1c330eac7 | ||
|
|
54d94eef84 | ||
|
|
397799278c | ||
|
|
d8d0eed1fb | ||
|
|
d1eb584430 | ||
|
|
c76e10299d | ||
|
|
2949231f67 | ||
|
|
d6731e4e5b | ||
|
|
d92b064561 | ||
|
|
ec4a7f64de | ||
|
|
14877301e4 | ||
|
|
d92cba15bb | ||
|
|
33d92c9240 | ||
|
|
737ed24d91 | ||
|
|
670f497af4 | ||
|
|
11ba1cc6e4 | ||
|
|
c69c4754cf | ||
|
|
8478d761db | ||
|
|
500a4dc130 | ||
|
|
c420d9c572 | ||
|
|
c94d90911f | ||
|
|
04fd524b52 | ||
|
|
b49744779e | ||
|
|
60de792147 | ||
|
|
e6d6da53c7 | ||
|
|
cd62cb8b84 | ||
|
|
85eb8546c3 | ||
|
|
bc5a8bbe5b | ||
|
|
d92a93a872 | ||
|
|
0def242b40 | ||
|
|
eeb5e2d3be | ||
|
|
e76132f464 | ||
|
|
ac41327bf8 | ||
|
|
0084b5d21c | ||
|
|
69b4de1b74 | ||
|
|
0c3e8d4630 | ||
|
|
af739bc32b |
2
.idea/ucentral.iml
generated
2
.idea/ucentral.iml
generated
@@ -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 (venv)" />
|
||||
<configuration sdkName="Python 3.9 (wlan-cloud-ucentralgw)" />
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
||||
<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>
|
||||
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(owgw VERSION 2.9.0)
|
||||
project(owgw VERSION 2.11.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
@@ -49,7 +49,7 @@ if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}")
|
||||
endif()
|
||||
|
||||
add_definitions(-DTIP_GATEWAY_SERVICE="1" -DPOCO_LOG_DEBUG="1")
|
||||
add_definitions(-DTIP_GATEWAY_SERVICE="1" -DPOCO_LOG_DEBUG="1" -DBOOST_NO_CXX98_FUNCTION_BASE=1)
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
@@ -144,9 +144,14 @@ 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/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
|
||||
@@ -195,7 +200,18 @@ add_executable( owgw
|
||||
src/AP_WS_Process_telemetry.cpp
|
||||
src/AP_WS_Process_venuebroadcast.cpp
|
||||
src/RADSEC_server.h
|
||||
src/UI_GW_WebSocketNotifications.cpp src/UI_GW_WebSocketNotifications.h src/framework/RESTAPI_SystemConfiguration.h src/ScriptManager.cpp src/ScriptManager.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/storage/storage_scripts.cpp src/storage/storage_scripts.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/RESTAPI/RESTAPI_regulatory.cpp src/RESTAPI/RESTAPI_regulatory.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)
|
||||
|
||||
if(NOT SMALL_BUILD)
|
||||
|
||||
|
||||
43
Dockerfile
43
Dockerfile
@@ -2,6 +2,8 @@ ARG DEBIAN_VERSION=11.5-slim
|
||||
ARG POCO_VERSION=poco-tip-v2
|
||||
ARG CPPKAFKA_VERSION=tip-v1
|
||||
ARG VALIJASON_VERSION=tip-v1
|
||||
ARG APP_NAME=owgw
|
||||
ARG APP_HOME_DIR=/openwifi
|
||||
|
||||
FROM debian:$DEBIAN_VERSION AS build-base
|
||||
|
||||
@@ -53,12 +55,14 @@ RUN cmake ..
|
||||
RUN cmake --build . --config Release -j8
|
||||
RUN cmake --build . --target install
|
||||
|
||||
FROM build-base AS owgw-build
|
||||
FROM build-base AS app-build
|
||||
|
||||
ADD CMakeLists.txt build /owgw/
|
||||
ADD cmake /owgw/cmake
|
||||
ADD src /owgw/src
|
||||
ADD .git /owgw/.git
|
||||
ARG APP_NAME
|
||||
|
||||
ADD CMakeLists.txt build /${APP_NAME}/
|
||||
ADD cmake /${APP_NAME}/cmake
|
||||
ADD src /${APP_NAME}/src
|
||||
ADD .git /${APP_NAME}/.git
|
||||
|
||||
COPY --from=poco-build /usr/local/include /usr/local/include
|
||||
COPY --from=poco-build /usr/local/lib /usr/local/lib
|
||||
@@ -66,23 +70,28 @@ 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
|
||||
|
||||
WORKDIR /owgw
|
||||
WORKDIR /${APP_NAME}
|
||||
RUN mkdir cmake-build
|
||||
WORKDIR /owgw/cmake-build
|
||||
WORKDIR /${APP_NAME}/cmake-build
|
||||
RUN cmake ..
|
||||
RUN cmake --build . --config Release -j8
|
||||
|
||||
FROM debian:$DEBIAN_VERSION
|
||||
|
||||
ENV OWGW_USER=owgw \
|
||||
OWGW_ROOT=/owgw-data \
|
||||
OWGW_CONFIG=/owgw-data
|
||||
ARG APP_NAME
|
||||
ARG APP_HOME_DIR
|
||||
|
||||
RUN useradd "$OWGW_USER"
|
||||
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 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 \
|
||||
@@ -91,14 +100,14 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
COPY readiness_check /readiness_check
|
||||
COPY test_scripts/curl/cli /cli
|
||||
|
||||
COPY owgw.properties.tmpl /
|
||||
COPY $APP_NAME.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
|
||||
|
||||
COPY --from=owgw-build /owgw/cmake-build/owgw /openwifi/owgw
|
||||
COPY --from=app-build /$APP_NAME/cmake-build/$APP_NAME $APP_HOME_DIR/$APP_NAME
|
||||
COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib /usr/local/lib/
|
||||
COPY --from=poco-build /poco/cmake-build/lib /usr/local/lib/
|
||||
|
||||
@@ -107,4 +116,4 @@ RUN ldconfig
|
||||
EXPOSE 15002 16002 16003 17002 16102
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["/openwifi/owgw"]
|
||||
CMD ${APP_HOME_DIR}/${APP_NAME}
|
||||
|
||||
25
PROTOCOL.md
25
PROTOCOL.md
@@ -149,18 +149,35 @@ 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 after rebooting after a crash. The event cannot be sent until a connection event has been sent.
|
||||
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
|
||||
{ "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
|
||||
|
||||
@@ -6,35 +6,35 @@ if [ "$SELFSIGNED_CERTS" = 'true' ]; then
|
||||
fi
|
||||
|
||||
if [[ "$TEMPLATE_CONFIG" = 'true' ]]; 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_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:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
|
||||
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:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
|
||||
RESTAPI_HOST_KEY=${RESTAPI_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
|
||||
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:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
|
||||
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:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
|
||||
INTERNAL_RESTAPI_HOST_KEY=${INTERNAL_RESTAPI_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
|
||||
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:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
|
||||
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:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
|
||||
FILEUPLOADER_HOST_KEY=${FILEUPLOADER_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
|
||||
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:-"\$OWGW_ROOT/uploads"} \
|
||||
FILEUPLOADER_PATH=${FILEUPLOADER_PATH:-"\${APP_ROOT}/uploads"} \
|
||||
FILEUPLOADER_URI=${FILEUPLOADER_URI:-"https://localhost:16003"} \
|
||||
SERVICE_KEY=${SERVICE_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
|
||||
SERVICE_KEY=${SERVICE_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
|
||||
SERVICE_KEY_PASSWORD=${SERVICE_KEY_PASSWORD:-"mypassword"} \
|
||||
SYSTEM_DATA=${SYSTEM_DATA:-"\$OWGW_ROOT/data"} \
|
||||
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"} \
|
||||
@@ -51,7 +51,7 @@ if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
|
||||
RTTY_TOKEN=${RTTY_TOKEN:-""} \
|
||||
RTTY_TIMEOUT=${RTTY_TIMEOUT:-"60"} \
|
||||
RTTY_VIEWPORT=${RTTY_VIEWPORT:-"5913"} \
|
||||
RTTY_ASSETS=${RTTY_ASSETS:-"\$OWGW_ROOT/rtty_ui"} \
|
||||
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"} \
|
||||
@@ -64,39 +64,41 @@ if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
|
||||
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:-"owgw"} \
|
||||
STORAGE_TYPE_POSTGRESQL_PASSWORD=${STORAGE_TYPE_POSTGRESQL_PASSWORD:-"owgw"} \
|
||||
STORAGE_TYPE_POSTGRESQL_DATABASE=${STORAGE_TYPE_POSTGRESQL_DATABASE:-"owgw"} \
|
||||
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:-"owgw"} \
|
||||
STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owgw"} \
|
||||
STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owgw"} \
|
||||
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"} \
|
||||
envsubst < /owgw.properties.tmpl > $OWGW_CONFIG/owgw.properties
|
||||
IPINFO_DEFAULT_COUNTRY=${IPINFO_DEFAULT_COUNTRY:-"US"} \
|
||||
DEVICE_SESSION_TIMEOUT=${DEVICE_SESSION_TIMEOUT:-"600"} \
|
||||
envsubst < /"${APP_NAME}".properties.tmpl > "${APP_CONFIG}"/"${APP_NAME}".properties
|
||||
fi
|
||||
|
||||
# Check if rtty_ui directory exists
|
||||
export RTTY_ASSETS=$(grep 'rtty.assets' $OWGW_CONFIG/owgw.properties | awk -F '=' '{print $2}' | xargs | envsubst)
|
||||
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="$OWGW_ROOT/rtty_ui"
|
||||
export RTTY_ASSETS="${APP_ROOT}/rtty_ui"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$(dirname $RTTY_ASSETS)" ]]; then
|
||||
mkdir -p $(dirname $RTTY_ASSETS)
|
||||
mkdir -p "$(dirname $RTTY_ASSETS)"
|
||||
fi
|
||||
|
||||
if [[ ! -d "$RTTY_ASSETS" ]]; then
|
||||
cp -r /dist/rtty_ui $RTTY_ASSETS
|
||||
fi
|
||||
|
||||
if [ "$1" = '/openwifi/owgw' -a "$(id -u)" = '0' ]; then
|
||||
if [ "$1" = "${APP_HOME_DIR}/${APP_NAME}" -a "$(id -u)" = '0' ]; then
|
||||
if [ "$RUN_CHOWN" = 'true' ]; then
|
||||
chown -R "$OWGW_USER": "$OWGW_ROOT" "$OWGW_CONFIG"
|
||||
chown -R "$APP_USER": "${APP_ROOT}" "$APP_CONFIG"
|
||||
fi
|
||||
exec gosu "$OWGW_USER" "$@"
|
||||
exec gosu "$APP_USER" "$@"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -9,7 +9,7 @@ fullnameOverride: ""
|
||||
images:
|
||||
owgw:
|
||||
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw
|
||||
tag: v2.9.0
|
||||
tag: v2.11.0
|
||||
pullPolicy: Always
|
||||
# regcred:
|
||||
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||
|
||||
22213
issues/OWGW Logs.txt
Normal file
22213
issues/OWGW Logs.txt
Normal file
File diff suppressed because it is too large
Load Diff
2158
issues/OWLS Logs.rtf
Normal file
2158
issues/OWLS Logs.rtf
Normal file
File diff suppressed because it is too large
Load Diff
2154
issues/OWLS Logs.txt
Normal file
2154
issues/OWLS Logs.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ openapi: 3.0.1
|
||||
info:
|
||||
title: uCentral gateway API
|
||||
description: A process to manage configuration for devices.
|
||||
version: 2.5.0
|
||||
version: 2.11.0
|
||||
license:
|
||||
name: BSD3
|
||||
url: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
|
||||
@@ -152,6 +152,11 @@ components:
|
||||
format: uuid
|
||||
restrictionDetails:
|
||||
$ref: '#/components/schemas/DeviceRestrictions'
|
||||
simulated:
|
||||
type: boolean
|
||||
lastRecordedContact:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
DeviceWithStatus:
|
||||
type: object
|
||||
@@ -260,6 +265,22 @@ components:
|
||||
format: uuid
|
||||
restrictionDetails:
|
||||
$ref: '#/components/schemas/DeviceRestrictions'
|
||||
hasGPS:
|
||||
type: boolean
|
||||
sanity:
|
||||
type: integer
|
||||
format: int64
|
||||
memoryUsed:
|
||||
type: number
|
||||
format: float
|
||||
load:
|
||||
type: number
|
||||
format: float
|
||||
temperature:
|
||||
type: number
|
||||
format: float
|
||||
connectReason:
|
||||
type: string
|
||||
|
||||
DeviceList:
|
||||
type: object
|
||||
@@ -329,6 +350,9 @@ components:
|
||||
associations_5G:
|
||||
type: integer
|
||||
format: int64
|
||||
associations_6G:
|
||||
type: integer
|
||||
format: int64
|
||||
verifiedCertificate:
|
||||
type: string
|
||||
enum:
|
||||
@@ -336,6 +360,7 @@ components:
|
||||
- VALID_CERTIFICATE,
|
||||
- MISMATCH_SERIAL,
|
||||
- VERIFIED
|
||||
- SIMULATED
|
||||
|
||||
DeviceCapabilities:
|
||||
type: object
|
||||
@@ -420,7 +445,7 @@ components:
|
||||
type: string
|
||||
|
||||
CommandSubmitSuccess:
|
||||
description: The command was submitted succesfully.
|
||||
description: The command was submitted successfully.
|
||||
properties:
|
||||
serialNumber:
|
||||
type: string
|
||||
@@ -458,6 +483,10 @@ components:
|
||||
logType:
|
||||
type: integer
|
||||
format: int64
|
||||
example:
|
||||
- 0 normal logs
|
||||
- 1 crash logs
|
||||
- 2 reboot logs
|
||||
UUID:
|
||||
type: integer
|
||||
format: int64
|
||||
@@ -524,6 +553,35 @@ components:
|
||||
items:
|
||||
$ref : '#/components/schemas/DefaultConfiguration'
|
||||
|
||||
DefaultFirmware:
|
||||
type: object
|
||||
properties:
|
||||
deviceType:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
uri:
|
||||
type: string
|
||||
revision:
|
||||
type: string
|
||||
imageCreationDate:
|
||||
type: integer
|
||||
format: int64
|
||||
created:
|
||||
type: integer
|
||||
format: int64
|
||||
lastModified:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
DefaultFirmwareList:
|
||||
type: object
|
||||
properties:
|
||||
firmwares:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/DefaultFirmware'
|
||||
|
||||
UpgradeRequest:
|
||||
type: object
|
||||
properties:
|
||||
@@ -913,12 +971,6 @@ components:
|
||||
- $ref: '#/components/schemas/StringList'
|
||||
- $ref: '#/components/schemas/TagValuePairList'
|
||||
|
||||
SystemCommandResults:
|
||||
type: object
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/StringList'
|
||||
- $ref: '#/components/schemas/TagValuePairList'
|
||||
|
||||
NoteInfo:
|
||||
type: object
|
||||
properties:
|
||||
@@ -958,6 +1010,33 @@ components:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
SystemResources:
|
||||
type: object
|
||||
properties:
|
||||
numberOfFileDescriptors:
|
||||
type: integer
|
||||
format: int64
|
||||
currRealMem:
|
||||
type: integer
|
||||
format: int64
|
||||
peakRealMem:
|
||||
type: integer
|
||||
format: int64
|
||||
currVirtMem:
|
||||
type: integer
|
||||
format: int64
|
||||
peakVirtMem:
|
||||
type: integer
|
||||
format: int64
|
||||
|
||||
SystemCommandResults:
|
||||
type: object
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/SystemResources'
|
||||
- $ref: '#/components/schemas/SystemInfoResults'
|
||||
- $ref: '#/components/schemas/StringList'
|
||||
- $ref: '#/components/schemas/TagValuePairList'
|
||||
|
||||
SystemCommandSetLogLevel:
|
||||
type: object
|
||||
properties:
|
||||
@@ -1260,6 +1339,29 @@ components:
|
||||
$ref: '#/components/schemas/RadiusProxyServerConfig'
|
||||
coaConfig:
|
||||
$ref: '#/components/schemas/RadiusProxyServerConfig'
|
||||
radsecPoolType:
|
||||
type: string
|
||||
enum:
|
||||
- generic
|
||||
- orion
|
||||
- globalreach
|
||||
default:
|
||||
generic
|
||||
poolProxyIp:
|
||||
type: string
|
||||
description: This is the fake IP for the entire pool
|
||||
example:
|
||||
- These addresses must match the addresses in the AP configuration and must start with 0.0
|
||||
- 0.0.0.1
|
||||
- 0.0.1.1
|
||||
radsecPoolKeepAlive:
|
||||
type: integer
|
||||
description: The keep alive value in seconds. Usually 30s or less.
|
||||
format: int64
|
||||
default: 25
|
||||
enabled:
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
RadiusProxyPoolList:
|
||||
type: object
|
||||
@@ -1269,6 +1371,70 @@ components:
|
||||
items:
|
||||
$ref: '#/components/schemas/RadiusProxyPool'
|
||||
|
||||
RadiusSession:
|
||||
type: object
|
||||
properties:
|
||||
started:
|
||||
type: integer
|
||||
format: int64
|
||||
lastTransaction:
|
||||
type: integer
|
||||
format: int64
|
||||
inputPackets:
|
||||
type: integer
|
||||
format: int64
|
||||
outputPackets:
|
||||
type: integer
|
||||
format: int64
|
||||
inputOctets:
|
||||
type: integer
|
||||
format: int64
|
||||
outputOctets:
|
||||
type: integer
|
||||
format: int64
|
||||
inputGigaWords:
|
||||
type: integer
|
||||
format: int64
|
||||
outputGigaWords:
|
||||
type: integer
|
||||
format: int64
|
||||
sessionTime:
|
||||
type: integer
|
||||
format: int64
|
||||
destination:
|
||||
type: string
|
||||
userName:
|
||||
type: string
|
||||
accountingSessionId:
|
||||
type: string
|
||||
accountingMultiSessionId:
|
||||
type: string
|
||||
callingStationId:
|
||||
type: string
|
||||
|
||||
RadiusSessionList:
|
||||
type: object
|
||||
properties:
|
||||
sessions:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/RadiusSession'
|
||||
|
||||
RadiusCoADMParameters:
|
||||
type: object
|
||||
properties:
|
||||
accountingSessionId:
|
||||
type: string
|
||||
accountingMultiSessionId:
|
||||
type: string
|
||||
callingStationId:
|
||||
type: string
|
||||
chargeableUserIdentity:
|
||||
type: string
|
||||
userName:
|
||||
type: string
|
||||
|
||||
|
||||
paths:
|
||||
/devices:
|
||||
get:
|
||||
@@ -1377,6 +1543,54 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- Devices
|
||||
summary: Delete a list of devices matching a criteria
|
||||
description: Delete a list of devices matching a criteria
|
||||
operationId: deleteDeviceList
|
||||
parameters:
|
||||
- in: query
|
||||
description: Supply a list of devices comma separated
|
||||
name: select
|
||||
schema:
|
||||
type: string
|
||||
example: serial1,serial2,serial3
|
||||
required: false
|
||||
- in: query
|
||||
description: Only simulated devices
|
||||
name: simulatedOnly
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
- in: query
|
||||
description: MAC address must match this pattern. Mutually exclusive with oldestContact
|
||||
name: macPattern
|
||||
schema:
|
||||
type: string
|
||||
example:
|
||||
- "aabbcc*"
|
||||
- "*aabbcc*"
|
||||
- "*cccddee"
|
||||
required: false
|
||||
- in: query
|
||||
description: lastRecordedContact older than this value. Mutually exclusive with macPattern
|
||||
name: oldestContact
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
|
||||
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/commands:
|
||||
get:
|
||||
tags:
|
||||
@@ -1633,6 +1847,123 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/default_firmwares:
|
||||
get:
|
||||
tags:
|
||||
- Firmware
|
||||
summary: Retrieve the lists of all default firmwares.
|
||||
description: Retrieve the lists of all default firmwares.
|
||||
operationId: getDefaultFirmwares
|
||||
|
||||
responses:
|
||||
200:
|
||||
description: List of default firmwares
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DefaultFirmwareList'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/default_firmware/{deviceType}:
|
||||
get:
|
||||
tags:
|
||||
- Firmware
|
||||
summary: Retrieve a default firmware.
|
||||
description: Retrieve a default firmware.
|
||||
operationId: getDefaultFirmware
|
||||
parameters:
|
||||
- in: path
|
||||
name: deviceType
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: Default firmware included
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DefaultFirmware'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
post:
|
||||
tags:
|
||||
- Firmware
|
||||
summary: Create a default firmware.
|
||||
description: Create a default firmware.
|
||||
operationId: createDefaultFirmware
|
||||
parameters:
|
||||
- in: path
|
||||
name: deviceType
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
requestBody:
|
||||
description: Information used to create the new firmware entry
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DefaultFirmware'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
delete:
|
||||
tags:
|
||||
- Firmware
|
||||
summary: Delete a default default firmware
|
||||
description: Delete a default default firmware
|
||||
operationId: deleteDefaultFirmware
|
||||
parameters:
|
||||
- in: path
|
||||
name: deviceType
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
204:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
put:
|
||||
tags:
|
||||
- Firmware
|
||||
summary: Update a default firmware
|
||||
description: Update a default firmware
|
||||
operationId: updateDefaultFirmware
|
||||
parameters:
|
||||
- in: path
|
||||
name: deviceType
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
requestBody:
|
||||
description: Firmware details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/DefaultFirmware'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/device/{serialNumber}:
|
||||
get:
|
||||
tags:
|
||||
@@ -1775,7 +2106,7 @@ paths:
|
||||
format: int64
|
||||
- in: query
|
||||
name: logType
|
||||
description: 0=any kind of logs (default) 0=normal logs only 1=crash logs only
|
||||
description: 0=any kind of logs (default) 0=normal logs, 1=crash logs, 2=reboot logs only
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
@@ -2113,32 +2444,6 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/device/{serialNumber}/command:
|
||||
post:
|
||||
tags:
|
||||
- Commands
|
||||
summary: Post a command to a device
|
||||
operationId: executeCommand
|
||||
parameters:
|
||||
- in: path
|
||||
name: serialNumber
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
requestBody:
|
||||
description: Command details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CommandDetails'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/CommandInfo'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/device/{serialNumber}/configure:
|
||||
post:
|
||||
tags:
|
||||
@@ -2723,9 +3028,6 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
|
||||
|
||||
|
||||
/blacklist:
|
||||
get:
|
||||
tags:
|
||||
@@ -2918,6 +3220,83 @@ paths:
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
|
||||
/radiusSessions/{serialNumber}:
|
||||
get:
|
||||
tags:
|
||||
- Radius Sessions
|
||||
summary: Retrieve the RADIUS sessions for a given AP
|
||||
operationId: getAPRadiusSessions
|
||||
parameters:
|
||||
- in: path
|
||||
name: serialNumber
|
||||
schema:
|
||||
type: string
|
||||
example: for searches or listing only serial number, set the serialNumber to 0
|
||||
required: true
|
||||
- in: query
|
||||
name: serialNumberOnly
|
||||
schema:
|
||||
type: boolean
|
||||
required: false
|
||||
- in: query
|
||||
name: userName
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
- in: query
|
||||
name: mac
|
||||
schema:
|
||||
type: string
|
||||
required: false
|
||||
example: aa:bb:cc:dd:ee:ff
|
||||
responses:
|
||||
200:
|
||||
description: AP List
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/RadiusSessionList'
|
||||
- $ref: '#/components/schemas/SerialNumberList'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
put:
|
||||
tags:
|
||||
- Radius Sessions
|
||||
summary: Retrieve the RADIUS sessions for a given AP
|
||||
operationId: putAPRadiusSessions
|
||||
parameters:
|
||||
- in: path
|
||||
name: serialNumber
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
- in: query
|
||||
name: operation
|
||||
schema:
|
||||
type: string
|
||||
enum:
|
||||
- coadm
|
||||
- disconnectUser
|
||||
requestBody:
|
||||
description: operationParameters
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/RadiusCoADMParameters'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
|
||||
|
||||
/deviceDashboard:
|
||||
get:
|
||||
tags:
|
||||
@@ -3014,16 +3393,12 @@ paths:
|
||||
type: string
|
||||
enum:
|
||||
- info
|
||||
- extraConfiguration
|
||||
- resources
|
||||
required: true
|
||||
|
||||
responses:
|
||||
200:
|
||||
description: Successful command execution
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/SystemInfoResults'
|
||||
$ref: '#/components/schemas/SystemCommandResults'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
|
||||
@@ -85,6 +85,7 @@ iptocountry.ipdata.apikey = ${IPTOCOUNTRY_IPDATA_APIKEY}
|
||||
|
||||
autoprovisioning.process = ${AUTOPROVISIONING_PROCESS}
|
||||
|
||||
openwifi.session.timeout = ${DEVICE_SESSION_TIMEOUT}
|
||||
#
|
||||
# rtty
|
||||
#
|
||||
@@ -103,6 +104,12 @@ 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
|
||||
#############################
|
||||
|
||||
59
src/AP_WS_ConfigAutoUpgrader.cpp
Normal file
59
src/AP_WS_ConfigAutoUpgrader.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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_ConfigAutoUpgrader::Start() {
|
||||
poco_notice(Logger(), "Starting...");
|
||||
QueueManager_.start(*this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AP_WS_ConfigAutoUpgrader::Stop() {
|
||||
poco_notice(Logger(), "Stopping...");
|
||||
Running_ = false;
|
||||
Queue_.wakeUpAll();
|
||||
QueueManager_.join();
|
||||
poco_notice(Logger(), "Stopped...");
|
||||
}
|
||||
|
||||
void AP_WS_ConfigAutoUpgrader::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
|
||||
137
src/AP_WS_ConfigAutoUpgrader.h
Normal file
137
src/AP_WS_ConfigAutoUpgrader.h
Normal file
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// 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_ConfigAutoUpgrader : public SubSystemServer, Poco::Runnable {
|
||||
public:
|
||||
int Start() final;
|
||||
void Stop() final;
|
||||
void run() final;
|
||||
|
||||
static auto instance() {
|
||||
static auto instance = new AP_WS_ConfigAutoUpgrader;
|
||||
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_ConfigAutoUpgrader() noexcept
|
||||
: SubSystemServer("AutoConfigUpgrade", "AUTO-CFG-MGR", "auto.config.updater") {
|
||||
}
|
||||
};
|
||||
|
||||
inline auto AP_WS_ConfigAutoUpgrader() { return AP_WS_ConfigAutoUpgrader::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
#include "RADIUSSessionTracker.h"
|
||||
#include "RADIUS_proxy_server.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -72,6 +73,7 @@ namespace OpenWifi {
|
||||
*this, &AP_WS_Connection::OnSocketError));
|
||||
Registered_ = true;
|
||||
Valid_ = true;
|
||||
uuid_ = MicroServiceRandom(std::numeric_limits<std::uint64_t>::max()-1);
|
||||
}
|
||||
|
||||
bool AP_WS_Connection::ValidatedDevice() {
|
||||
@@ -126,7 +128,7 @@ namespace OpenWifi {
|
||||
|
||||
CN_ = Poco::trim(Poco::toLower(PeerCert.commonName()));
|
||||
State_.VerifiedCertificate = GWObjects::VALID_CERTIFICATE;
|
||||
poco_information(Logger_,
|
||||
poco_trace(Logger_,
|
||||
fmt::format("TLS-CONNECTION({}): Session={} Valid certificate: CN={}", CId_,
|
||||
State_.sessionId, CN_));
|
||||
|
||||
@@ -138,10 +140,14 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(AP_WS_Server::IsSim(CN_)) {
|
||||
State_.VerifiedCertificate = GWObjects::SIMULATED;
|
||||
}
|
||||
|
||||
std::string reason, author;
|
||||
std::uint64_t created;
|
||||
if (!CN_.empty() && StorageService()->IsBlackListed(CN_, reason, author, created)) {
|
||||
DeviceBlacklistedKafkaEvent KE(CN_, Utils::Now(), reason, author, created, CId_);
|
||||
DeviceBlacklistedKafkaEvent KE(Utils::SerialNumberToInt(CN_), Utils::Now(), reason, author, created, CId_);
|
||||
poco_warning(
|
||||
Logger_,
|
||||
fmt::format(
|
||||
@@ -217,17 +223,15 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void NotifyKafkaDisconnect(const std::string &SerialNumber) {
|
||||
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);
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
std::ostringstream OS;
|
||||
Stringify.condense(Disconnect, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber, Disconnect);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
@@ -237,10 +241,21 @@ namespace OpenWifi {
|
||||
EndConnection();
|
||||
}
|
||||
|
||||
void AP_WS_Connection::EndConnection() {
|
||||
Valid_ = false;
|
||||
void DeviceDisconnectionCleanup(const std::string &SerialNumber, std::uint64_t uuid) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
NotifyKafkaDisconnect(SerialNumber, uuid);
|
||||
}
|
||||
RADIUSSessionTracker()->DeviceDisconnect(SerialNumber);
|
||||
}
|
||||
|
||||
void AP_WS_Connection::EndConnection(bool DeleteSession) {
|
||||
Valid_ = false;
|
||||
if (!Dead_.test_and_set()) {
|
||||
|
||||
if(!SerialNumber_.empty() && State_.LastContact!=0) {
|
||||
StorageService()->SetDeviceLastRecordedContact(SerialNumber_, State_.LastContact);
|
||||
}
|
||||
|
||||
if (Registered_) {
|
||||
Registered_ = false;
|
||||
Reactor_.removeEventHandler(
|
||||
@@ -255,14 +270,16 @@ namespace OpenWifi {
|
||||
}
|
||||
WS_->close();
|
||||
|
||||
if (KafkaManager()->Enabled() && !SerialNumber_.empty()) {
|
||||
std::string s(SerialNumber_);
|
||||
std::thread t([s]() { NotifyKafkaDisconnect(s); });
|
||||
t.detach();
|
||||
if(!SerialNumber_.empty()) {
|
||||
std::thread Cleanup(DeviceDisconnectionCleanup,SerialNumber_, uuid_);
|
||||
Cleanup.detach();
|
||||
}
|
||||
|
||||
auto SessionDeleted = AP_WS_Server()->EndSession(State_.sessionId, SerialNumberInt_);
|
||||
if (SessionDeleted) {
|
||||
bool SessionDeleted = false;
|
||||
if(DeleteSession)
|
||||
SessionDeleted = AP_WS_Server()->EndSession(State_.sessionId, SerialNumberInt_);
|
||||
|
||||
if (SessionDeleted || !DeleteSession) {
|
||||
GWWebSocketNotifications::SingleDevice_t N;
|
||||
N.content.serialNumber = SerialNumber_;
|
||||
GWWebSocketNotifications::DeviceDisconnected(N);
|
||||
@@ -430,7 +447,7 @@ namespace OpenWifi {
|
||||
std::string reason, author;
|
||||
std::uint64_t created;
|
||||
if (StorageService()->IsBlackListed(Serial, reason, author, created)) {
|
||||
DeviceBlacklistedKafkaEvent KE(CN_, Utils::Now(), reason, author, created, CId_);
|
||||
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),
|
||||
@@ -495,6 +512,10 @@ namespace OpenWifi {
|
||||
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: {
|
||||
@@ -698,12 +719,11 @@ namespace OpenWifi {
|
||||
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::JSON::Stringifier Stringify;
|
||||
std::ostringstream OS;
|
||||
Stringify.condense(PingObject, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, OS.str());
|
||||
poco_trace(Logger_,fmt::format("Sending PING for {}", SerialNumber_));
|
||||
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_,PingObject);
|
||||
}
|
||||
return;
|
||||
} break;
|
||||
@@ -932,22 +952,23 @@ namespace OpenWifi {
|
||||
|
||||
void AP_WS_Connection::ProcessIncomingRadiusData(const Poco::JSON::Object::Ptr &Doc) {
|
||||
if (Doc->has(uCentralProtocol::RADIUSDATA)) {
|
||||
std::string secret;
|
||||
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());
|
||||
DecodedData.size(),secret);
|
||||
} 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());
|
||||
DecodedData.size(), secret);
|
||||
} 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());
|
||||
DecodedData.size(), secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#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"
|
||||
@@ -27,7 +28,7 @@ namespace OpenWifi {
|
||||
Poco::Logger &L, Poco::Net::SocketReactor &R);
|
||||
~AP_WS_Connection();
|
||||
|
||||
void EndConnection();
|
||||
void EndConnection(bool DeleteSession=true);
|
||||
void ProcessJSONRPCEvent(Poco::JSON::Object::Ptr &Doc);
|
||||
void ProcessJSONRPCResult(Poco::JSON::Object::Ptr Doc);
|
||||
void ProcessIncomingFrame();
|
||||
@@ -57,7 +58,7 @@ namespace OpenWifi {
|
||||
bool StopWebSocketTelemetry(uint64_t RPCID);
|
||||
bool StopKafkaTelemetry(uint64_t RPCID);
|
||||
|
||||
inline void GetLastStats(std::string &LastStats) const {
|
||||
inline void GetLastStats(std::string &LastStats) {
|
||||
std::shared_lock G(ConnectionMutex_);
|
||||
LastStats = RawLastStats_;
|
||||
}
|
||||
@@ -65,6 +66,33 @@ namespace OpenWifi {
|
||||
inline void SetLastStats(const std::string &LastStats) {
|
||||
std::unique_lock G(ConnectionMutex_);
|
||||
RawLastStats_ = LastStats;
|
||||
try {
|
||||
Poco::JSON::Parser P;
|
||||
auto Stats = P.parse(LastStats).extract<Poco::JSON::Object::Ptr>();
|
||||
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) {
|
||||
memory_used_ =
|
||||
(100.0 * ((double)TotalMemory - (double)FreeMemory)) / (double)TotalMemory;
|
||||
}
|
||||
if(Unit->isArray("load")) {
|
||||
Poco::JSON::Array::Ptr Load = Unit->getArray("load");
|
||||
if(Load->size()>1) {
|
||||
cpu_load_ = Load->get(1);
|
||||
}
|
||||
}
|
||||
if(Unit->isArray("temperature")) {
|
||||
Poco::JSON::Array::Ptr Temperature = Unit->getArray("temperature");
|
||||
if(Temperature->size()>1) {
|
||||
temperature_ = Temperature->get(0);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void SetLastHealthCheck(const GWObjects::HealthCheck &H) {
|
||||
@@ -82,6 +110,8 @@ namespace OpenWifi {
|
||||
State = State_;
|
||||
}
|
||||
|
||||
inline bool HasGPS() { return hasGPS; }
|
||||
|
||||
inline void GetRestrictions(GWObjects::DeviceRestrictions &R) const {
|
||||
std::shared_lock G(ConnectionMutex_);
|
||||
R = Restrictions_;
|
||||
@@ -101,6 +131,7 @@ namespace OpenWifi {
|
||||
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);
|
||||
|
||||
bool ValidatedDevice();
|
||||
|
||||
@@ -168,6 +199,9 @@ namespace OpenWifi {
|
||||
bool StartTelemetry(uint64_t RPCID, const std::vector<std::string> &TelemetryTypes);
|
||||
bool StopTelemetry(uint64_t RPCID);
|
||||
void UpdateCounts();
|
||||
bool hasGPS=false;
|
||||
std::double_t memory_used_=0.0, cpu_load_ = 0.0, temperature_ = 0.0;
|
||||
std::uint64_t uuid_=0;
|
||||
};
|
||||
|
||||
} // namespace OpenWifi
|
||||
@@ -21,11 +21,7 @@ namespace OpenWifi {
|
||||
|
||||
if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
auto Data = ParamsObj->get(uCentralProtocol::DATA);
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
std::ostringstream OS;
|
||||
Stringify.condense(ParamsObj, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::ALERTS, SerialNumber_, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::ALERTS, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
#include "firmware_revision_cache.h"
|
||||
|
||||
#include "UI_GW_WebSocketNotifications.h"
|
||||
#include <GWKafkaEvents.h>
|
||||
|
||||
@@ -30,9 +32,7 @@ namespace OpenWifi {
|
||||
Event.set("type", "device.firmware_change");
|
||||
Event.set("timestamp", Utils::Now());
|
||||
Event.set("payload", EventDetails);
|
||||
std::ostringstream OS;
|
||||
Event.stringify(OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,7 @@ namespace OpenWifi {
|
||||
Event.set("type", "device.not_provisioned");
|
||||
Event.set("timestamp", Utils::Now());
|
||||
Event.set("payload", EventDetails);
|
||||
std::ostringstream OS;
|
||||
Event.stringify(OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +61,11 @@ namespace OpenWifi {
|
||||
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_);
|
||||
|
||||
@@ -81,6 +84,10 @@ namespace OpenWifi {
|
||||
State_.Address = Utils::FormatIPv6(WS_->peerAddress().toString());
|
||||
CId_ = SerialNumber_ + "@" + CId_;
|
||||
|
||||
if(ParamsObj->has("reason")) {
|
||||
State_.connectReason = ParamsObj->get("reason").toString();
|
||||
}
|
||||
|
||||
auto IP = PeerAddress_.toString();
|
||||
if (IP.substr(0, 7) == "::ffff:") {
|
||||
IP = IP.substr(7);
|
||||
@@ -105,7 +112,55 @@ namespace OpenWifi {
|
||||
GWObjects::Device DeviceInfo;
|
||||
auto DeviceExists = StorageService()->GetDevice(SerialNumber_, DeviceInfo);
|
||||
if (Daemon()->AutoProvisioning() && !DeviceExists) {
|
||||
StorageService()->CreateDefaultDevice(SerialNumber_, Caps, Firmware, PeerAddress_);
|
||||
// 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)) {
|
||||
/*
|
||||
|
||||
{ "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)
|
||||
},
|
||||
"id" : <some number>
|
||||
}
|
||||
|
||||
*/
|
||||
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(
|
||||
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_));
|
||||
@@ -115,7 +170,7 @@ namespace OpenWifi {
|
||||
int Updated{0};
|
||||
if (!Firmware.empty()) {
|
||||
if (Firmware != DeviceInfo.Firmware) {
|
||||
DeviceFirmwareChangeKafkaEvent KEvent(SerialNumber_, Utils::Now(),
|
||||
DeviceFirmwareChangeKafkaEvent KEvent(SerialNumberInt_, Utils::Now(),
|
||||
DeviceInfo.Firmware, Firmware);
|
||||
DeviceInfo.Firmware = Firmware;
|
||||
DeviceInfo.LastFWUpdate = Utils::Now();
|
||||
@@ -131,6 +186,26 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -193,6 +268,12 @@ namespace OpenWifi {
|
||||
return EndConnection();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
poco_information(Logger_,
|
||||
fmt::format("CONNECT({}): Simulator device. "
|
||||
"Session={} ConnectionCompletion Time={}",
|
||||
CId_, State_.sessionId,
|
||||
State_.connectionCompletionTime));
|
||||
}
|
||||
|
||||
GWWebSocketNotifications::SingleDevice_t Notification;
|
||||
@@ -200,14 +281,11 @@ namespace OpenWifi {
|
||||
GWWebSocketNotifications::DeviceConnected(Notification);
|
||||
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
|
||||
ParamsObj->set(uCentralProtocol::CONNECTIONIP, CId_);
|
||||
ParamsObj->set("locale", State_.locale);
|
||||
ParamsObj->set(uCentralProtocol::TIMESTAMP, Utils::Now());
|
||||
std::ostringstream OS;
|
||||
Stringify.condense(ParamsObj, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, OS.str());
|
||||
ParamsObj->set(uCentralProtocol::UUID, uuid_);
|
||||
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
} else {
|
||||
poco_warning(
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
|
||||
#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)) {
|
||||
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;
|
||||
@@ -24,11 +26,11 @@ namespace OpenWifi {
|
||||
.Log = LogText,
|
||||
.Data = "",
|
||||
.Severity = GWObjects::DeviceLog::LOG_EMERG,
|
||||
.Recorded = (uint64_t)time(nullptr),
|
||||
.Recorded = Utils::Now(),
|
||||
.LogType = 1,
|
||||
.UUID = 0};
|
||||
.UUID = ParamsObj->get(uCentralProtocol::UUID)};
|
||||
StorageService()->AddLog(DeviceLog);
|
||||
|
||||
DeviceLogKafkaEvent E(DeviceLog);
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("LOG({}): Missing parameters.", CId_));
|
||||
return;
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace OpenWifi {
|
||||
StorageService()->SetDevicePassword(Serial, Password);
|
||||
poco_trace(
|
||||
Logger_,
|
||||
fmt::format("DEVICEUPDATE({}): Device is updating its login password.", Serial));
|
||||
fmt::format("DEVICE-UPDATE({}): Device is updating its login password.", Serial));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,11 +34,8 @@ namespace OpenWifi {
|
||||
FullEvent.set("type", EventType);
|
||||
FullEvent.set("timestamp", EventTimeStamp);
|
||||
FullEvent.set("payload", EventPayload);
|
||||
|
||||
std::ostringstream OS;
|
||||
FullEvent.stringify(OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber_,
|
||||
OS.str());
|
||||
FullEvent);
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
|
||||
@@ -19,8 +19,10 @@ namespace OpenWifi {
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::SANITY) &&
|
||||
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);
|
||||
auto CheckData = ParamsObj->get(uCentralProtocol::DATA).toString();
|
||||
@@ -58,11 +60,7 @@ namespace OpenWifi {
|
||||
|
||||
SetLastHealthCheck(Check);
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
std::ostringstream OS;
|
||||
ParamsObj->set("timestamp", Utils::Now());
|
||||
Stringify.condense(ParamsObj, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::HEALTHCHECK, SerialNumber_, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::HEALTHCHECK, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("HEALTHCHECK({}): Missing parameter", CId_));
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#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) {
|
||||
@@ -36,6 +37,7 @@ namespace OpenWifi {
|
||||
.LogType = 0,
|
||||
.UUID = State_.UUID};
|
||||
StorageService()->AddLog(DeviceLog);
|
||||
DeviceLogKafkaEvent E(DeviceLog);
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("LOG({}): Missing parameters.", CId_));
|
||||
return;
|
||||
|
||||
44
src/AP_WS_Process_rebootLog.cpp
Normal file
44
src/AP_WS_Process_rebootLog.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// 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(DeviceLog);
|
||||
DeviceLogKafkaEvent E(DeviceLog);
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("REBOOT-LOG({}): Missing parameters.", CId_));
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -56,10 +56,7 @@ namespace OpenWifi {
|
||||
State_.Associations_5G, State_.Associations_6G);
|
||||
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
std::ostringstream OS;
|
||||
Stringify.condense(ParamsObj, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::STATE, SerialNumber_, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::STATE, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
|
||||
GWWebSocketNotifications::SingleDevice_t N;
|
||||
|
||||
@@ -27,9 +27,10 @@ namespace OpenWifi {
|
||||
std::ostringstream SS;
|
||||
Payload->stringify(SS);
|
||||
auto now = Utils::Now();
|
||||
auto KafkaPayload = SS.str();
|
||||
if (ParamsObj->has("adhoc")) {
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
|
||||
SS.str());
|
||||
KafkaPayload);
|
||||
return;
|
||||
}
|
||||
if (TelemetryWebSocketRefCount_) {
|
||||
@@ -38,7 +39,7 @@ namespace OpenWifi {
|
||||
// std::endl;
|
||||
TelemetryWebSocketPackets_++;
|
||||
State_.websocketPackets = TelemetryWebSocketPackets_;
|
||||
TelemetryStream()->NotifyEndPoint(SerialNumberInt_, SS.str());
|
||||
TelemetryStream()->NotifyEndPoint(SerialNumberInt_, KafkaPayload);
|
||||
} else {
|
||||
StopWebSocketTelemetry(CommandManager()->Next_RPC_ID());
|
||||
}
|
||||
@@ -49,7 +50,7 @@ namespace OpenWifi {
|
||||
TelemetryKafkaPackets_++;
|
||||
State_.kafkaPackets = TelemetryKafkaPackets_;
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
|
||||
SS.str());
|
||||
KafkaPayload);
|
||||
} else {
|
||||
StopKafkaTelemetry(CommandManager()->Next_RPC_ID());
|
||||
}
|
||||
|
||||
@@ -21,11 +21,7 @@ namespace OpenWifi {
|
||||
|
||||
if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
auto Data = ParamsObj->get(uCentralProtocol::DATA);
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
std::ostringstream OS;
|
||||
Stringify.condense(ParamsObj, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::WIFISCAN, SerialNumber_, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::WIFISCAN, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "fmt/format.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/utils.h"
|
||||
#include <framework/KafkaManager.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -54,6 +55,8 @@ namespace OpenWifi {
|
||||
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>();
|
||||
Reactor_pool_->Start();
|
||||
|
||||
@@ -161,7 +164,7 @@ namespace OpenWifi {
|
||||
GarbageCollectorCallback_ = std::make_unique<Poco::TimerCallback<AP_WS_Server>>(
|
||||
*this, &AP_WS_Server::onGarbageCollecting);
|
||||
Timer_.setStartInterval(10 * 1000);
|
||||
Timer_.setPeriodicInterval(5 * 1000); // every minute
|
||||
Timer_.setPeriodicInterval(10 * 1000); // every minute
|
||||
Timer_.start(*GarbageCollectorCallback_, MicroServiceTimerPool());
|
||||
|
||||
Running_ = true;
|
||||
@@ -169,41 +172,50 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void AP_WS_Server::onGarbageCollecting([[maybe_unused]] Poco::Timer &timer) {
|
||||
std::lock_guard Lock(WSServerMutex_);
|
||||
if (!Garbage_.empty()) {
|
||||
Garbage_.clear();
|
||||
}
|
||||
|
||||
static uint64_t last_log = Utils::Now();
|
||||
|
||||
NumberOfConnectedDevices_ = 0;
|
||||
NumberOfConnectingDevices_ = 0;
|
||||
AverageDeviceConnectionTime_ = 0;
|
||||
uint64_t total_connected_time = 0;
|
||||
|
||||
auto now = Utils::Now();
|
||||
for (const auto &connection : SerialNumbers_) {
|
||||
if (connection.second.second == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (connection.second.second->State_.Connected) {
|
||||
NumberOfConnectedDevices_++;
|
||||
total_connected_time += (now - connection.second.second->State_.started);
|
||||
} else {
|
||||
NumberOfConnectingDevices_++;
|
||||
}
|
||||
}
|
||||
|
||||
AverageDeviceConnectionTime_ =
|
||||
(NumberOfConnectedDevices_ != 0) ? total_connected_time / NumberOfConnectedDevices_ : 0;
|
||||
if ((now - last_log) > 120) {
|
||||
last_log = now;
|
||||
poco_information(
|
||||
Logger(),
|
||||
fmt::format(
|
||||
"Active AP connections: {} Connecting: {} Average connection time: {} seconds",
|
||||
NumberOfConnectedDevices_, NumberOfConnectingDevices_,
|
||||
AverageDeviceConnectionTime_));
|
||||
{
|
||||
std::lock_guard Lock(WSServerMutex_);
|
||||
if (!Garbage_.empty()) {
|
||||
Garbage_.clear();
|
||||
}
|
||||
|
||||
NumberOfConnectedDevices_ = 0;
|
||||
NumberOfConnectingDevices_ = 0;
|
||||
AverageDeviceConnectionTime_ = 0;
|
||||
uint64_t total_connected_time = 0;
|
||||
|
||||
auto hint = SerialNumbers_.begin();
|
||||
while (hint != end(SerialNumbers_)) {
|
||||
if (hint->second.second == nullptr) {
|
||||
hint = SerialNumbers_.erase(hint);
|
||||
} else if ((now - hint->second.second->State_.LastContact) > SessionTimeOut_) {
|
||||
hint->second.second->EndConnection(false);
|
||||
poco_information(Logger(),fmt::format("{}: Session seems idle. Controller disconnecting device.", hint->second.second->SerialNumber_));
|
||||
Sessions_.erase(hint->second.second->State_.sessionId);
|
||||
Garbage_.push_back(hint->second.second);
|
||||
hint = SerialNumbers_.erase(hint);
|
||||
} else if (hint->second.second->State_.Connected) {
|
||||
NumberOfConnectedDevices_++;
|
||||
total_connected_time += (now - hint->second.second->State_.started);
|
||||
hint++;
|
||||
} else {
|
||||
NumberOfConnectingDevices_++;
|
||||
hint++;
|
||||
}
|
||||
}
|
||||
|
||||
AverageDeviceConnectionTime_ = NumberOfConnectedDevices_ > 0
|
||||
? total_connected_time / NumberOfConnectedDevices_
|
||||
: 0;
|
||||
if ((now - last_log) > 120) {
|
||||
last_log = now;
|
||||
poco_information(Logger(),
|
||||
fmt::format("Active AP connections: {} Connecting: {} Average connection time: {} seconds",
|
||||
NumberOfConnectedDevices_, NumberOfConnectingDevices_,
|
||||
AverageDeviceConnectionTime_));
|
||||
}
|
||||
}
|
||||
|
||||
GWWebSocketNotifications::NumberOfConnection_t Notification;
|
||||
@@ -212,6 +224,16 @@ namespace OpenWifi {
|
||||
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);
|
||||
}
|
||||
|
||||
void AP_WS_Server::Stop() {
|
||||
@@ -309,6 +331,32 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
Sessions_.erase(Session);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AP_WS_Server::EndSessionUnSafe(uint64_t session_id, uint64_t serial_number) {
|
||||
|
||||
auto Session = Sessions_.find(session_id);
|
||||
if (Session == end(Sessions_))
|
||||
return false;
|
||||
|
||||
Garbage_.push_back(Session->second);
|
||||
|
||||
auto Device = SerialNumbers_.find(serial_number);
|
||||
if (Device == end(SerialNumbers_)) {
|
||||
Sessions_.erase(Session);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Device->second.first == session_id) {
|
||||
Sessions_.erase(Session);
|
||||
SerialNumbers_.erase(Device);
|
||||
return true;
|
||||
}
|
||||
|
||||
Sessions_.erase(Session);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace OpenWifi {
|
||||
|
||||
void SetSessionDetails(uint64_t connection_id, uint64_t SerialNumber);
|
||||
bool EndSession(uint64_t connection_id, uint64_t serial_number);
|
||||
|
||||
bool EndSessionUnSafe(uint64_t session_id, uint64_t serial_number);
|
||||
void SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber,
|
||||
uint64_t Interval, uint64_t Lifetime,
|
||||
const std::vector<std::string> &TelemetryTypes);
|
||||
@@ -212,6 +212,27 @@ namespace OpenWifi {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ExtendedAttributes(const std::string &serialNumber,
|
||||
bool & hasGPS,
|
||||
std::uint64_t &Sanity,
|
||||
std::double_t &MemoryUsed,
|
||||
std::double_t &Load,
|
||||
std::double_t &Temperature
|
||||
) {
|
||||
std::lock_guard G(WSServerMutex_);
|
||||
auto session_hint = SerialNumbers_.find(Utils::SerialNumberToInt(serialNumber));
|
||||
if(session_hint==end(SerialNumbers_)) {
|
||||
return false;
|
||||
}
|
||||
hasGPS = session_hint->second.second->hasGPS;
|
||||
Sanity = session_hint->second.second->RawLastHealthcheck_.Sanity;
|
||||
MemoryUsed = session_hint->second.second->memory_used_;
|
||||
Load = session_hint->second.second->cpu_load_;
|
||||
Temperature = session_hint->second.second->temperature_;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::recursive_mutex WSServerMutex_;
|
||||
std::unique_ptr<Poco::Crypto::X509Certificate> IssuerCert_;
|
||||
@@ -230,10 +251,10 @@ namespace OpenWifi {
|
||||
std::atomic_bool AllowSerialNumberMismatch_ = true;
|
||||
std::atomic_uint64_t MismatchDepth_ = 2;
|
||||
|
||||
std::atomic_uint64_t NumberOfConnectedDevices_ = 0;
|
||||
std::atomic_uint64_t AverageDeviceConnectionTime_ = 0;
|
||||
std::atomic_uint64_t NumberOfConnectingDevices_ = 0;
|
||||
|
||||
std::uint64_t NumberOfConnectedDevices_ = 0;
|
||||
std::uint64_t AverageDeviceConnectionTime_ = 0;
|
||||
std::uint64_t NumberOfConnectingDevices_ = 0;
|
||||
std::uint64_t SessionTimeOut_ = 10*60;
|
||||
mutable std::mutex StatsMutex_;
|
||||
std::atomic_uint64_t TX_=0,RX_=0;
|
||||
|
||||
|
||||
@@ -302,97 +302,116 @@ namespace OpenWifi {
|
||||
StorageService()->RemovedExpiredCommands();
|
||||
StorageService()->RemoveTimedOutCommands();
|
||||
|
||||
std::vector<GWObjects::CommandDetails> Commands;
|
||||
if (StorageService()->GetReadyToExecuteCommands(0, 200, Commands)) {
|
||||
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;
|
||||
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("{}: Serial={} Command={} Starting processing.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
try {
|
||||
|
||||
// Skip an already running command
|
||||
if (IsCommandRunning(Cmd.UUID)) {
|
||||
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 {
|
||||
|
||||
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;
|
||||
}
|
||||
// Skip an already running command
|
||||
if (IsCommandRunning(Cmd.UUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto SerialNumberInt = Utils::SerialNumberToInt(Cmd.SerialNumber);
|
||||
if (!AP_WS_Server()->Connected(SerialNumberInt)) {
|
||||
poco_trace(
|
||||
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={} Device is not connected.",
|
||||
fmt::format("{}: Serial={} Command={} Preparing execution.",
|
||||
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(
|
||||
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={} 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) {
|
||||
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={} Sent.", Cmd.UUID,
|
||||
Cmd.SerialNumber, Cmd.Command));
|
||||
} else {
|
||||
poco_debug(MyLogger,
|
||||
fmt::format("{}: Serial={} Command={} Re-queued command.",
|
||||
fmt::format("{}: Serial={} Command={} Hard failure. "
|
||||
"Command marked as completed.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
StorageService()->SetCommandLastTry(Cmd.UUID);
|
||||
StorageService()->SetCommandExecuted(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;
|
||||
}
|
||||
}
|
||||
} catch (Poco::Exception &E) {
|
||||
}
|
||||
catch (Poco::Exception &E) {
|
||||
MyLogger.log(E);
|
||||
} catch (...) {
|
||||
}
|
||||
catch (...) {
|
||||
poco_warning(MyLogger, "Exception during command processing.");
|
||||
}
|
||||
poco_trace(MyLogger, "Scheduler done.");
|
||||
|
||||
@@ -11,12 +11,17 @@
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/Util/Option.h"
|
||||
|
||||
#include <framework/ConfigurationValidator.h>
|
||||
#include <framework/UI_WebSocketClientServer.h>
|
||||
#include <framework/default_device_types.h>
|
||||
|
||||
#include "AP_WS_Server.h"
|
||||
#include "CommandManager.h"
|
||||
#include "Daemon.h"
|
||||
#include "FileUploader.h"
|
||||
#include "FindCountry.h"
|
||||
#include "OUIServer.h"
|
||||
#include "RADIUSSessionTracker.h"
|
||||
#include "RADIUS_proxy_server.h"
|
||||
#include "RegulatoryInfo.h"
|
||||
#include "ScriptManager.h"
|
||||
@@ -25,64 +30,47 @@
|
||||
#include "StorageArchiver.h"
|
||||
#include "StorageService.h"
|
||||
#include "TelemetryStream.h"
|
||||
#include "GenericScheduler.h"
|
||||
#include "UI_GW_WebSocketNotifications.h"
|
||||
#include "VenueBroadcaster.h"
|
||||
#include "framework/ConfigurationValidator.h"
|
||||
#include "framework/UI_WebSocketClientServer.h"
|
||||
#include "AP_WS_ConfigAutoUpgrader.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{StorageService(), SerialNumberCache(), ConfigurationValidator(),
|
||||
UI_WebSocketClientServer(), OUIServer(), FindCountryFromIP(),
|
||||
CommandManager(), FileUploader(), StorageArchiver(), TelemetryStream(),
|
||||
RTTYS_server(), RADIUS_proxy_server(), VenueBroadcaster(), ScriptManager(),
|
||||
SignatureManager(), AP_WS_Server(),
|
||||
RegulatoryInfo()
|
||||
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_ConfigAutoUpgrader(),
|
||||
FirmwareRevisionCache()
|
||||
});
|
||||
return &instance;
|
||||
}
|
||||
|
||||
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"}};
|
||||
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();
|
||||
}
|
||||
|
||||
void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) {
|
||||
AutoProvisioning_ = config().getBool("openwifi.autoprovisioning", false);
|
||||
DeviceTypes_ = DefaultDeviceTypes;
|
||||
|
||||
DeviceTypes_ = DefaultDeviceTypeList;
|
||||
WebSocketProcessor_ = std::make_unique<GwWebSocketClient>(logger());
|
||||
MicroServiceALBCallback(ALBHealthCallback);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string Daemon::IdentifyDevice(const std::string &Id) const {
|
||||
|
||||
@@ -12,9 +12,7 @@ namespace OpenWifi {
|
||||
Event.set("type", type_);
|
||||
Event.set("timestamp", timestamp_);
|
||||
Event.set("payload", payload_);
|
||||
std::ostringstream OS;
|
||||
Event.stringify(OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, serialNumber_, OS.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, Utils::IntToSerialNumber(serialNumber_), Event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,23 +4,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Poco/JSON/Object.h>
|
||||
#include <framework/KafkaManager.h>
|
||||
#include <string>
|
||||
|
||||
#include <Poco/JSON/Object.h>
|
||||
#include <RESTObjects/RESTAPI_GWobjects.h>
|
||||
#include <framework/KafkaManager.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class GWKafkaEvents {
|
||||
public:
|
||||
GWKafkaEvents(const std::string &serialNumber, const std::string &type,
|
||||
GWKafkaEvents(std::uint64_t serialNumber, const std::string &type,
|
||||
std::uint64_t timestamp)
|
||||
: serialNumber_(serialNumber), type_(type), timestamp_(timestamp) {}
|
||||
|
||||
inline void SetPayload(Poco::JSON::Object::Ptr payload) { payload_ = std::move(payload); }
|
||||
: serialNumber_(serialNumber), type_(type), timestamp_(timestamp) {
|
||||
payload_ = Poco::SharedPtr<Poco::JSON::Object>(new Poco::JSON::Object);
|
||||
}
|
||||
void Send();
|
||||
[[nodiscard]] inline std::uint64_t Serial() const { return serialNumber_;};
|
||||
|
||||
private:
|
||||
std::string serialNumber_;
|
||||
protected:
|
||||
std::uint64_t serialNumber_;
|
||||
std::string type_;
|
||||
std::uint64_t timestamp_ = 0;
|
||||
Poco::JSON::Object::Ptr payload_;
|
||||
@@ -28,17 +31,15 @@ namespace OpenWifi {
|
||||
|
||||
class DeviceFirmwareChangeKafkaEvent : public GWKafkaEvents {
|
||||
public:
|
||||
DeviceFirmwareChangeKafkaEvent(const std::string &serialNumber, std::uint64_t timestamp,
|
||||
DeviceFirmwareChangeKafkaEvent(std::uint64_t serialNumber, std::uint64_t timestamp,
|
||||
const std::string &oldFirmware,
|
||||
const std::string &newFirmware)
|
||||
: GWKafkaEvents(serialNumber, "unit.firmware_change", timestamp),
|
||||
oldFirmware_(oldFirmware), newFirmware_(newFirmware) {}
|
||||
|
||||
~DeviceFirmwareChangeKafkaEvent() {
|
||||
Poco::JSON::Object::Ptr payload = new Poco::JSON::Object;
|
||||
payload->set("oldFirmware", oldFirmware_);
|
||||
payload->set("newFirmware", newFirmware_);
|
||||
SetPayload(payload);
|
||||
payload_->set("oldFirmware", oldFirmware_);
|
||||
payload_->set("newFirmware", newFirmware_);
|
||||
Send();
|
||||
}
|
||||
|
||||
@@ -48,15 +49,13 @@ namespace OpenWifi {
|
||||
|
||||
class DeviceConfigurationChangeKafkaEvent : public GWKafkaEvents {
|
||||
public:
|
||||
DeviceConfigurationChangeKafkaEvent(const std::string &serialNumber,
|
||||
DeviceConfigurationChangeKafkaEvent(std::uint64_t serialNumber,
|
||||
std::uint64_t timestamp, const std::string config)
|
||||
: GWKafkaEvents(serialNumber, "unit.configuration_change", timestamp), config_(config) {
|
||||
}
|
||||
|
||||
~DeviceConfigurationChangeKafkaEvent() {
|
||||
Poco::JSON::Object::Ptr payload = new Poco::JSON::Object;
|
||||
payload->set("configuration", config_);
|
||||
SetPayload(payload);
|
||||
payload_->set("configuration", config_);
|
||||
Send();
|
||||
}
|
||||
|
||||
@@ -66,7 +65,7 @@ namespace OpenWifi {
|
||||
|
||||
class DeviceBlacklistedKafkaEvent : public GWKafkaEvents {
|
||||
public:
|
||||
explicit DeviceBlacklistedKafkaEvent(const std::string &serialNumber,
|
||||
explicit DeviceBlacklistedKafkaEvent(std::uint64_t serialNumber,
|
||||
std::uint64_t timestamp, const std::string &reason,
|
||||
const std::string &author, std::uint64_t created,
|
||||
std::string &IP)
|
||||
@@ -74,12 +73,10 @@ namespace OpenWifi {
|
||||
author_(author), created_(created), IP_(IP) {}
|
||||
|
||||
~DeviceBlacklistedKafkaEvent() {
|
||||
Poco::JSON::Object::Ptr payload = new Poco::JSON::Object;
|
||||
payload->set("reason", reason_);
|
||||
payload->set("author", author_);
|
||||
payload->set("created", created_);
|
||||
payload->set("ipaddress", IP_);
|
||||
SetPayload(payload);
|
||||
payload_->set("reason", reason_);
|
||||
payload_->set("author", author_);
|
||||
payload_->set("created", created_);
|
||||
payload_->set("ipaddress", IP_);
|
||||
Send();
|
||||
}
|
||||
|
||||
@@ -89,4 +86,21 @@ namespace OpenWifi {
|
||||
std::string IP_;
|
||||
};
|
||||
|
||||
class DeviceLogKafkaEvent : public GWKafkaEvents {
|
||||
public:
|
||||
explicit DeviceLogKafkaEvent( const GWObjects::DeviceLog &L)
|
||||
: GWKafkaEvents(Utils::MACToInt(L.SerialNumber), "device_log", L.Recorded),
|
||||
DL_(L)
|
||||
{
|
||||
}
|
||||
|
||||
~DeviceLogKafkaEvent() {
|
||||
DL_.to_json(*payload_);
|
||||
Send();
|
||||
}
|
||||
|
||||
private:
|
||||
GWObjects::DeviceLog DL_;
|
||||
};
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
19
src/GenericScheduler.cpp
Normal file
19
src/GenericScheduler.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-19.
|
||||
//
|
||||
|
||||
#include "GenericScheduler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
int GenericScheduler::Start() {
|
||||
poco_information(Logger(),"Starting...");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GenericScheduler::Stop() {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
poco_information(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
37
src/GenericScheduler.h
Normal file
37
src/GenericScheduler.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-19.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <framework/SubSystemServer.h>
|
||||
#include <libs/Scheduler.h>
|
||||
#include <Poco/Environment.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class GenericScheduler : public SubSystemServer {
|
||||
public:
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new GenericScheduler;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
|
||||
auto & Scheduler() { return Scheduler_; }
|
||||
|
||||
private:
|
||||
GenericScheduler() noexcept
|
||||
: SubSystemServer("Scheduler", "SCHEDULER", "scheduler"),
|
||||
Scheduler_(Poco::Environment::processorCount()*2) {
|
||||
|
||||
}
|
||||
Bosma::Scheduler Scheduler_;
|
||||
};
|
||||
|
||||
inline auto GenericScheduler() { return GenericScheduler::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
452
src/RADIUSSessionTracker.cpp
Normal file
452
src/RADIUSSessionTracker.cpp
Normal file
@@ -0,0 +1,452 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-03-19.
|
||||
//
|
||||
|
||||
#include "RADIUSSessionTracker.h"
|
||||
#include <fmt/format.h>
|
||||
#include <framework/utils.h>
|
||||
|
||||
#include "RADIUS_proxy_server.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
int RADIUSSessionTracker::Start() {
|
||||
poco_information(Logger(),"Starting...");
|
||||
QueueManager_.start(*this);
|
||||
GarbageCollectionCallback_ = std::make_unique<Poco::TimerCallback<RADIUSSessionTracker>>(
|
||||
*this, &RADIUSSessionTracker::GarbageCollection);
|
||||
GarbageCollectionTimer_.setStartInterval(10000);
|
||||
GarbageCollectionTimer_.setPeriodicInterval(2*60*1000); // every 2 minutes
|
||||
GarbageCollectionTimer_.start(*GarbageCollectionCallback_, MicroServiceTimerPool());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RADIUSSessionTracker::Stop() {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
Running_ = false;
|
||||
GarbageCollectionTimer_.stop();
|
||||
SessionMessageQueue_.wakeUpAll();
|
||||
QueueManager_.join();
|
||||
poco_information(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
void RADIUSSessionTracker::GarbageCollection([[maybe_unused]] Poco::Timer &timer) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
auto Now = Utils::Now();
|
||||
std::uint64_t active_sessions=0, active_devices=0;
|
||||
for(auto device_it = AccountingSessions_.begin(); device_it != end(AccountingSessions_); ) {
|
||||
auto & serialNumber = device_it->first;
|
||||
auto & session_list = device_it->second;
|
||||
for(auto session_it=session_list.begin();session_it!=end(session_list);) {
|
||||
auto & session_name = session_it->first;
|
||||
auto & session = session_it->second;
|
||||
if((Now-session->lastTransaction)>SessionTimeout_) {
|
||||
poco_debug(Logger(),fmt::format("{}: Session {} timeout for {}", serialNumber, session_name, session->userName));
|
||||
session_it = session_list.erase(session_it);
|
||||
} else {
|
||||
++active_sessions;
|
||||
++session_it;
|
||||
}
|
||||
}
|
||||
if(session_list.empty()) {
|
||||
device_it = AccountingSessions_.erase(device_it);
|
||||
} else {
|
||||
++active_devices;
|
||||
++device_it;
|
||||
}
|
||||
}
|
||||
poco_information(Logger(),fmt::format("{} active sessions on {} devices",active_sessions, active_devices));
|
||||
}
|
||||
|
||||
void RADIUSSessionTracker::run() {
|
||||
Utils::SetThreadName("rad:sessmgr");
|
||||
Running_ = true;
|
||||
|
||||
Poco::AutoPtr<Poco::Notification> NextSession(SessionMessageQueue_.waitDequeueNotification());
|
||||
while (NextSession && Running_) {
|
||||
auto Session = dynamic_cast<SessionNotification *>(NextSession.get());
|
||||
|
||||
try {
|
||||
if (Session != nullptr) {
|
||||
switch(Session->Type_) {
|
||||
case SessionNotification::NotificationType::accounting_session_message: {
|
||||
ProcessAccountingSession(*Session);
|
||||
} break;
|
||||
case SessionNotification::NotificationType::authentication_session_message: {
|
||||
ProcessAuthenticationSession(*Session);
|
||||
} break;
|
||||
case SessionNotification::NotificationType::ap_disconnect: {
|
||||
DisconnectSession(Session->SerialNumber_);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(), "Exception occurred during run.");
|
||||
}
|
||||
NextSession = SessionMessageQueue_.waitDequeueNotification();
|
||||
}
|
||||
poco_information(Logger(), "RADIUS session manager stopping.");
|
||||
}
|
||||
|
||||
void RADIUSSessionTracker::ProcessAuthenticationSession([[maybe_unused]] OpenWifi::SessionNotification &Notification) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
std::string CallingStationId, CalledStationId, AccountingSessionId, AccountingMultiSessionId, UserName, ChargeableUserIdentity, Interface, nasId;
|
||||
for (const auto &attribute : Notification.Packet_.Attrs_) {
|
||||
switch (attribute.type) {
|
||||
case RADIUS::Attributes::AUTH_USERNAME: {
|
||||
UserName.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::CALLING_STATION_ID: {
|
||||
CallingStationId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::CALLED_STATION_ID: {
|
||||
CalledStationId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_SESSION_ID: {
|
||||
AccountingSessionId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_MULTI_SESSION_ID: {
|
||||
AccountingMultiSessionId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::CHARGEABLE_USER_IDENTITY:{
|
||||
ChargeableUserIdentity.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::NAS_IDENTIFIER:{
|
||||
nasId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::PROXY_STATE: {
|
||||
std::string Tmp;
|
||||
Tmp.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
auto ProxyParts = Poco::StringTokenizer(Tmp,":");
|
||||
if(ProxyParts.count()==4)
|
||||
Interface=ProxyParts[3];
|
||||
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
auto ap_hint = AccountingSessions_.find(Notification.SerialNumber_);
|
||||
if(ap_hint==end(AccountingSessions_)) {
|
||||
SessionMap M;
|
||||
AccountingSessions_[Notification.SerialNumber_ ] = M;
|
||||
ap_hint = AccountingSessions_.find(Notification.SerialNumber_);
|
||||
}
|
||||
|
||||
auto Index = AccountingSessionId +AccountingMultiSessionId;
|
||||
auto session_hint = ap_hint->second.find(Index);
|
||||
if(session_hint==end(ap_hint->second)) {
|
||||
auto NewSession = std::make_shared<GWObjects::RADIUSSession>();
|
||||
NewSession->serialNumber = Notification.SerialNumber_;
|
||||
NewSession->started = NewSession->lastTransaction = Utils::Now();
|
||||
NewSession->userName = UserName;
|
||||
NewSession->callingStationId = CallingStationId;
|
||||
NewSession->calledStationId = CalledStationId;
|
||||
NewSession->accountingSessionId = AccountingSessionId;
|
||||
NewSession->accountingMultiSessionId = AccountingMultiSessionId;
|
||||
NewSession->chargeableUserIdentity = ChargeableUserIdentity;
|
||||
NewSession->interface = Interface;
|
||||
NewSession->nasId = nasId;
|
||||
NewSession->secret = Notification.Secret_;
|
||||
ap_hint->second[Index] = NewSession;
|
||||
} else {
|
||||
session_hint->second->lastTransaction = Utils::Now();
|
||||
}
|
||||
|
||||
/*
|
||||
if(ap_hint!=AccountingSessions_.end()) {
|
||||
std::cout << "Auth table:" << std::endl;
|
||||
for(const auto &session:ap_hint->second) {
|
||||
std::cout << Notification.SerialNumber_ << ": Index: " << session.first << ": ID: " << session.second->accountingSessionId << " MID:" << session.second->accountingMultiSessionId << std::endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
std::uint32_t GetUiInt32(const std::uint8_t *buf) {
|
||||
return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + (buf[3] << 0);
|
||||
}
|
||||
|
||||
/*
|
||||
std::string RADIUSSessionTracker::ComputeSessionIndex(OpenWifi::RADIUSSessionPtr S) {
|
||||
return "";
|
||||
}
|
||||
*/
|
||||
|
||||
void
|
||||
RADIUSSessionTracker::ProcessAccountingSession(OpenWifi::SessionNotification &Notification) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
std::string CallingStationId, CalledStationId, AccountingSessionId, AccountingMultiSessionId, UserName, ChargeableUserIdentity, Interface;
|
||||
std::uint8_t AccountingPacketType = 0;
|
||||
std::uint32_t InputOctets=0, OutputOctets=0, InputPackets=0, OutputPackets=0, InputGigaWords=0, OutputGigaWords=0,
|
||||
SessionTime = 0;
|
||||
for (const auto &attribute : Notification.Packet_.Attrs_) {
|
||||
switch (attribute.type) {
|
||||
case RADIUS::Attributes::AUTH_USERNAME: {
|
||||
UserName.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::CALLING_STATION_ID: {
|
||||
CallingStationId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::CALLED_STATION_ID: {
|
||||
CalledStationId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_SESSION_ID: {
|
||||
AccountingSessionId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_MULTI_SESSION_ID: {
|
||||
AccountingMultiSessionId.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::CHARGEABLE_USER_IDENTITY:{
|
||||
ChargeableUserIdentity.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_STATUS_TYPE: {
|
||||
AccountingPacketType = Notification.Packet_.P_.attributes[attribute.pos + 3];
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_INPUT_OCTETS: {
|
||||
InputOctets = GetUiInt32(&Notification.Packet_.P_.attributes[attribute.pos]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_INPUT_PACKETS: {
|
||||
InputPackets = GetUiInt32(&Notification.Packet_.P_.attributes[attribute.pos]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_INPUT_GIGAWORDS: {
|
||||
InputGigaWords = GetUiInt32(&Notification.Packet_.P_.attributes[attribute.pos]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_OUTPUT_OCTETS: {
|
||||
OutputOctets = GetUiInt32(&Notification.Packet_.P_.attributes[attribute.pos]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_OUTPUT_PACKETS: {
|
||||
OutputPackets= GetUiInt32(&Notification.Packet_.P_.attributes[attribute.pos]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_OUTPUT_GIGAWORDS: {
|
||||
OutputGigaWords = GetUiInt32(&Notification.Packet_.P_.attributes[attribute.pos]);
|
||||
} break;
|
||||
case RADIUS::Attributes::ACCT_SESSION_TIME: {
|
||||
SessionTime = GetUiInt32(&Notification.Packet_.P_.attributes[attribute.pos]);
|
||||
} break;
|
||||
case RADIUS::Attributes::PROXY_STATE: {
|
||||
std::string Tmp;
|
||||
Tmp.assign(
|
||||
&Notification.Packet_.P_.attributes[attribute.pos],
|
||||
&Notification.Packet_.P_.attributes[attribute.pos + attribute.len]);
|
||||
auto ProxyParts = Poco::StringTokenizer(Tmp,":");
|
||||
if(ProxyParts.count()==4)
|
||||
Interface=ProxyParts[3];
|
||||
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
auto ap_hint = AccountingSessions_.find(Notification.SerialNumber_);
|
||||
if(ap_hint==end(AccountingSessions_)) {
|
||||
SessionMap M;
|
||||
AccountingSessions_[Notification.SerialNumber_ ] = M;
|
||||
ap_hint = AccountingSessions_.find(Notification.SerialNumber_);
|
||||
}
|
||||
|
||||
auto Index = AccountingSessionId + AccountingMultiSessionId;
|
||||
auto session_hint = ap_hint->second.find(Index);
|
||||
if(session_hint==end(ap_hint->second)) {
|
||||
// find the calling_station_id
|
||||
// if we are getting a stop for something we do not know, nothing to do...
|
||||
if( AccountingPacketType!=OpenWifi::RADIUS::AccountingPacketTypes::ACCT_STATUS_TYPE_START &&
|
||||
AccountingPacketType!=OpenWifi::RADIUS::AccountingPacketTypes::ACCT_STATUS_TYPE_INTERIM_UPDATE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// std::cout << "ACT -> " << Notification.SerialNumber_ << ": AccountingSessionId: " << AccountingSessionId << " AccountingMultiSessionId: " << AccountingMultiSessionId << std::endl;
|
||||
|
||||
auto NewSession = std::make_shared<GWObjects::RADIUSSession>();
|
||||
NewSession->serialNumber = Notification.SerialNumber_;
|
||||
NewSession->destination = Notification.Destination_;
|
||||
NewSession->started = NewSession->lastTransaction = Utils::Now();
|
||||
NewSession->userName = UserName;
|
||||
NewSession->callingStationId = CallingStationId;
|
||||
NewSession->calledStationId = CalledStationId;
|
||||
NewSession->accountingSessionId = AccountingSessionId;
|
||||
NewSession->accountingMultiSessionId = AccountingMultiSessionId;
|
||||
NewSession->accountingPacket = Notification.Packet_;
|
||||
NewSession->destination = Notification.Destination_;
|
||||
NewSession->inputOctets = InputOctets;
|
||||
NewSession->inputPackets = InputPackets;
|
||||
NewSession->inputGigaWords = InputGigaWords;
|
||||
NewSession->outputOctets = OutputOctets;
|
||||
NewSession->outputOctets = OutputPackets;
|
||||
NewSession->outputGigaWords = OutputGigaWords;
|
||||
NewSession->sessionTime = SessionTime;
|
||||
NewSession->chargeableUserIdentity = ChargeableUserIdentity;
|
||||
NewSession->interface = Interface;
|
||||
NewSession->secret = Notification.Secret_;
|
||||
|
||||
poco_debug(Logger(),fmt::format("{}: Creating session", CallingStationId));
|
||||
ap_hint->second[Index] = NewSession;
|
||||
|
||||
} else {
|
||||
|
||||
// If we receive a stop, just remove that session
|
||||
if(AccountingPacketType==OpenWifi::RADIUS::AccountingPacketTypes::ACCT_STATUS_TYPE_STOP) {
|
||||
poco_debug(Logger(),fmt::format("{}: Deleting session", CallingStationId));
|
||||
ap_hint->second.erase(Index);
|
||||
} else {
|
||||
poco_debug(Logger(),fmt::format("{}: Updating session", CallingStationId));
|
||||
session_hint->second->accountingPacket = Notification.Packet_;
|
||||
session_hint->second->destination = Notification.Destination_;
|
||||
session_hint->second->lastTransaction = Utils::Now();
|
||||
session_hint->second->inputOctets = InputOctets;
|
||||
session_hint->second->inputPackets = InputPackets;
|
||||
session_hint->second->inputGigaWords = InputGigaWords;
|
||||
session_hint->second->outputOctets = OutputOctets;
|
||||
session_hint->second->outputOctets = OutputPackets;
|
||||
session_hint->second->outputGigaWords = OutputGigaWords;
|
||||
session_hint->second->sessionTime = SessionTime;
|
||||
}
|
||||
}
|
||||
|
||||
/* if(ap_hint!=AccountingSessions_.end()) {
|
||||
std::cout << "Acct table:" << std::endl;
|
||||
for(const auto &session:ap_hint->second) {
|
||||
std::cout << Notification.SerialNumber_ << ": Index: " << session.first << ": ID: " << session.second->accountingSessionId << " MID:" << session.second->accountingMultiSessionId << std::endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void store_packet(const std::string &serialNumber, const char *buffer, std::size_t size, int i) {
|
||||
static std::uint64_t pkt=0;
|
||||
|
||||
std::string filename = MicroServiceDataDirectory() + "/radius." + serialNumber + ".stop." +
|
||||
std::to_string(pkt++) + "." + std::to_string(i) + ".bin";
|
||||
|
||||
std::ofstream ofs(filename,std::ios_base::binary | std::ios_base::trunc | std::ios_base::out);
|
||||
ofs.write(buffer,size);
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
bool RADIUSSessionTracker::SendCoADM(const RADIUSSessionPtr &session) {
|
||||
|
||||
RADIUS::RadiusPacket P;
|
||||
|
||||
P.PacketType(RADIUS::Disconnect_Request);
|
||||
P.Identifier(std::rand() & 0x00ff);
|
||||
P.AppendAttribute(RADIUS::Attributes::AUTH_USERNAME, session->userName);
|
||||
P.AppendAttribute(RADIUS::Attributes::NAS_IP, (std::uint32_t)(0x7f000001));
|
||||
if(!session->calledStationId.empty())
|
||||
P.AppendAttribute(RADIUS::Attributes::CALLED_STATION_ID, session->calledStationId);
|
||||
if(!session->callingStationId.empty())
|
||||
P.AppendAttribute(RADIUS::Attributes::CALLING_STATION_ID, session->callingStationId);
|
||||
if(!session->nasId.empty())
|
||||
P.AppendAttribute(RADIUS::Attributes::NAS_IDENTIFIER, session->nasId);
|
||||
if(!session->accountingSessionId.empty())
|
||||
P.AppendAttribute(RADIUS::Attributes::ACCT_SESSION_ID, session->accountingSessionId);
|
||||
if(!session->accountingMultiSessionId.empty())
|
||||
P.AppendAttribute(RADIUS::Attributes::ACCT_MULTI_SESSION_ID, session->accountingMultiSessionId);
|
||||
auto ProxyState = session->serialNumber + ":" + "0.0.0.0" + ":" + "3799" + ":" + session->interface;
|
||||
// std::cout << "Proxy state: " << ProxyState << " Secret: " << session->secret << std::endl;
|
||||
P.AppendAttribute(RADIUS::Attributes::PROXY_STATE, ProxyState);
|
||||
P.RecomputeAuthenticator(session->secret);
|
||||
P.Log(std::cout);
|
||||
AP_WS_Server()->SendRadiusCoAData(session->serialNumber, P.Buffer(), P.Size_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RADIUSSessionTracker::SendCoADM(const std::string &serialNumber, const std::string &sessionId) {
|
||||
poco_information(Logger(),fmt::format("{}: SendCoADM for {}.", serialNumber, sessionId));
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
auto ap_hint = AccountingSessions_.find(serialNumber);
|
||||
if(ap_hint==end(AccountingSessions_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto session_hint = ap_hint->second.find(sessionId);
|
||||
if(session_hint!=ap_hint->second.end()) {
|
||||
SendCoADM(session_hint->second);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RADIUSSessionTracker::DisconnectUser(const std::string &UserName) {
|
||||
poco_information(Logger(),fmt::format("Disconnect user {}.", UserName));
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
for(const auto &AP:AccountingSessions_) {
|
||||
for(const auto &Session:AP.second) {
|
||||
if(Session.second->userName==UserName) {
|
||||
SendCoADM(Session.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RADIUSSessionTracker::DisconnectSession(const std::string &SerialNumber) {
|
||||
poco_information(Logger(),fmt::format("{}: Disconnecting.", SerialNumber));
|
||||
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
auto hint = AccountingSessions_.find(SerialNumber);
|
||||
if(hint==end(AccountingSessions_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to go through all sessions and send an accounting stop
|
||||
for(const auto &session:hint->second) {
|
||||
poco_debug(Logger(), fmt::format("Stopping accounting for {}:{}", SerialNumber, session.first ));
|
||||
|
||||
RADIUS::RadiusPacket P(session.second->accountingPacket);
|
||||
|
||||
P.P_.identifier++;
|
||||
P.ReplaceAttribute(RADIUS::Attributes::ACCT_STATUS_TYPE, (std::uint32_t) RADIUS::AccountingPacketTypes::ACCT_STATUS_TYPE_STOP);
|
||||
P.ReplaceOrAdd(RADIUS::Attributes::EVENT_TIMESTAMP, (std::uint32_t) std::time(nullptr));
|
||||
P.AppendAttribute(RADIUS::Attributes::ACCT_TERMINATE_CAUSE, (std::uint32_t) RADIUS::AccountingTerminationReasons::ACCT_TERMINATE_LOST_CARRIER);
|
||||
RADIUS_proxy_server()->RouteAndSendAccountingPacket(session.second->destination, SerialNumber, P, true, session.second->secret);
|
||||
}
|
||||
|
||||
AccountingSessions_.erase(hint);
|
||||
}
|
||||
|
||||
|
||||
} // namespace OpenWifi
|
||||
208
src/RADIUSSessionTracker.h
Normal file
208
src/RADIUSSessionTracker.h
Normal file
@@ -0,0 +1,208 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-03-19.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <framework/SubSystemServer.h>
|
||||
#include <Poco/Runnable.h>
|
||||
#include <Poco/Notification.h>
|
||||
#include <Poco/NotificationQueue.h>
|
||||
#include <Poco/JSON/Object.h>
|
||||
#include <Poco/Timer.h>
|
||||
|
||||
#include "RADIUS_helpers.h"
|
||||
|
||||
#include <RESTObjects/RESTAPI_GWobjects.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class SessionNotification : public Poco::Notification {
|
||||
public:
|
||||
|
||||
enum class NotificationType {
|
||||
accounting_session_message,
|
||||
authentication_session_message,
|
||||
ap_disconnect
|
||||
};
|
||||
|
||||
explicit SessionNotification(NotificationType T, const std::string &Destination, const std::string &SerialNumber, const RADIUS::RadiusPacket &P, const std::string &secret)
|
||||
: Type_(T), Destination_(Destination), SerialNumber_(SerialNumber), Packet_(P), Secret_(secret) {
|
||||
}
|
||||
|
||||
explicit SessionNotification(const std::string &SerialNumber)
|
||||
: Type_(NotificationType::ap_disconnect), SerialNumber_(SerialNumber) {
|
||||
|
||||
}
|
||||
|
||||
NotificationType Type_;
|
||||
std::string Destination_;
|
||||
std::string SerialNumber_;
|
||||
RADIUS::RadiusPacket Packet_;
|
||||
std::string Secret_;
|
||||
};
|
||||
|
||||
class TrackerFutureCompletion {
|
||||
public:
|
||||
virtual bool Completed(const RADIUS::RadiusPacket &P) = 0;
|
||||
virtual bool StillValid() = 0;
|
||||
private:
|
||||
};
|
||||
|
||||
class CoADisconnectResponse : public TrackerFutureCompletion {
|
||||
public:
|
||||
CoADisconnectResponse(const std::string &serialNumber, std::uint8_t id, const std::vector<std::uint8_t> &types, const std::string &callingStationId):
|
||||
SerialNumber_(serialNumber),
|
||||
Id_(id),
|
||||
PacketTypes_(types),
|
||||
CallingStationId_(callingStationId) {
|
||||
Created_ = Utils::Now();
|
||||
}
|
||||
|
||||
bool Completed(const RADIUS::RadiusPacket &P) final {
|
||||
if(P.Identifier()==Id_) {
|
||||
if(P.P_.code == RADIUS::Disconnect_ACK) {
|
||||
|
||||
} else if (P.P_.code == RADIUS::Disconnect_NAK) {
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StillValid() final {
|
||||
return (Utils::Now()-Created_) < 20;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string SerialNumber_;
|
||||
std::uint8_t Id_;
|
||||
std::vector<std::uint8_t> PacketTypes_;
|
||||
std::uint64_t Created_;
|
||||
std::string CallingStationId_;
|
||||
};
|
||||
|
||||
using RADIUSSessionPtr = std::shared_ptr<GWObjects::RADIUSSession>;
|
||||
|
||||
class RADIUSSessionTracker : public SubSystemServer, Poco::Runnable {
|
||||
public:
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new RADIUSSessionTracker;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
void run() final;
|
||||
|
||||
inline void AddAccountingSession(const std::string &Destination, const std::string &SerialNumber,
|
||||
const RADIUS::RadiusPacket &P, const std::string &secret) {
|
||||
SessionMessageQueue_.enqueueNotification(new SessionNotification(SessionNotification::NotificationType::accounting_session_message, Destination, SerialNumber, P, secret));
|
||||
}
|
||||
|
||||
inline void AddAuthenticationSession(const std::string &Destination, const std::string &SerialNumber,
|
||||
const RADIUS::RadiusPacket &P, const std::string &secret) {
|
||||
std::lock_guard G(Mutex_);
|
||||
auto ap_hint = AccountingSessions_.find(SerialNumber);
|
||||
if(AccountingSessions_.find(SerialNumber)!=end(AccountingSessions_)) {
|
||||
// if we have already added the info, do not need to add it again
|
||||
auto CallingStationId = P.ExtractCallingStationID();
|
||||
auto AccountingSessionId = P.ExtractAccountingSessionID();
|
||||
if(ap_hint->second.find(CallingStationId+AccountingSessionId)!=end(ap_hint->second)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
SessionMessageQueue_.enqueueNotification(new SessionNotification(SessionNotification::NotificationType::authentication_session_message, Destination, SerialNumber, P, secret));
|
||||
}
|
||||
|
||||
inline void DeviceDisconnect(const std::string &serialNumber) {
|
||||
SessionMessageQueue_.enqueueNotification(new SessionNotification(serialNumber));
|
||||
}
|
||||
|
||||
inline void GetAPList(std::vector<std::string> &SerialNumbers) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
for(const auto &[serialNumber,_]:AccountingSessions_) {
|
||||
SerialNumbers.emplace_back(serialNumber);
|
||||
}
|
||||
}
|
||||
|
||||
inline void GetAPSessions(const std::string &SerialNumber, GWObjects::RADIUSSessionList & list) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
auto ap_hint = AccountingSessions_.find(SerialNumber);
|
||||
if(ap_hint!=end(AccountingSessions_)) {
|
||||
for(const auto &[index,session]:ap_hint->second) {
|
||||
list.sessions.emplace_back(*session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void GetUserNameAPSessions(const std::string &userName, GWObjects::RADIUSSessionList & list) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
for(const auto &[_,sessions]:AccountingSessions_) {
|
||||
for(const auto &[_,session]:sessions) {
|
||||
if(Utils::match(userName.c_str(),session->userName.c_str())) {
|
||||
list.sessions.emplace_back(*session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void GetMACAPSessions(const std::string &mac, GWObjects::RADIUSSessionList & list) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
for(const auto &[_,sessions]:AccountingSessions_) {
|
||||
for(const auto &[_,session]:sessions) {
|
||||
if(Utils::match(mac.c_str(),session->callingStationId.c_str())) {
|
||||
list.sessions.emplace_back(*session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SendCoADM(const std::string &serialNumber, const std::string &sessionId);
|
||||
bool SendCoADM(const RADIUSSessionPtr &session);
|
||||
bool DisconnectUser(const std::string &UserName);
|
||||
|
||||
inline std::uint32_t HasSessions(const std::string & serialNumber) {
|
||||
std::lock_guard G(Mutex_);
|
||||
auto ap_hint = AccountingSessions_.find(serialNumber);
|
||||
if(ap_hint==end(AccountingSessions_)) {
|
||||
return 0;
|
||||
}
|
||||
return ap_hint->second.size();
|
||||
}
|
||||
|
||||
void GarbageCollection(Poco::Timer &timer);
|
||||
|
||||
private:
|
||||
std::atomic_bool Running_=false;
|
||||
Poco::NotificationQueue SessionMessageQueue_;
|
||||
Poco::Thread QueueManager_;
|
||||
|
||||
using SessionMap = std::map<std::string,RADIUSSessionPtr>; // calling-station-id + accounting-session-id
|
||||
std::map<std::string,SessionMap> AccountingSessions_; // serial-number -> session< accounting-session -> session>
|
||||
|
||||
Poco::Timer GarbageCollectionTimer_;
|
||||
std::unique_ptr<Poco::TimerCallback<RADIUSSessionTracker>> GarbageCollectionCallback_;
|
||||
|
||||
std::uint64_t SessionTimeout_=10*60;
|
||||
|
||||
|
||||
void ProcessAccountingSession(SessionNotification &Notification);
|
||||
void ProcessAuthenticationSession(SessionNotification &Notification);
|
||||
void DisconnectSession(const std::string &SerialNumber);
|
||||
|
||||
RADIUSSessionTracker() noexcept
|
||||
: SubSystemServer("RADIUSSessionTracker", "RADIUS-SESSION", "radius.session") {}
|
||||
|
||||
std::string ComputeSessionIndex(RADIUSSessionPtr S);
|
||||
};
|
||||
|
||||
inline auto RADIUSSessionTracker() { return RADIUSSessionTracker::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
#include "RADIUS_helpers.h"
|
||||
#include "RADIUS_proxy_server.h"
|
||||
|
||||
#include "RADIUSSessionTracker.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -158,11 +159,15 @@ namespace OpenWifi {
|
||||
void RADIUS_proxy_server::StartRADSECServers() {
|
||||
std::lock_guard G(Mutex_);
|
||||
for (const auto &pool : PoolList_.pools) {
|
||||
for (const auto &entry : pool.authConfig.servers) {
|
||||
if (entry.radsec) {
|
||||
RADSECservers_[Poco::Net::SocketAddress(entry.ip, 0)] =
|
||||
std::make_unique<RADSEC_server>(*RadiusReactor_, entry);
|
||||
if(pool.enabled) {
|
||||
for (const auto &entry : pool.authConfig.servers) {
|
||||
if (entry.radsec) {
|
||||
RADSECservers_[Poco::Net::SocketAddress(entry.ip, 0)] =
|
||||
std::make_unique<RADSEC_server>(*RadiusReactor_, entry, pool);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
poco_information(Logger(),fmt::format("Pool {} is not enabled.", pool.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,17 +190,13 @@ namespace OpenWifi {
|
||||
P.Evaluate(ReceiveSize);
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if (SerialNumber.empty()) {
|
||||
poco_warning(Logger(), "Accounting: missing serial number.");
|
||||
poco_warning(Logger(), "Accounting: missing serial number. Dropping request.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
|
||||
poco_debug(
|
||||
Logger(),
|
||||
fmt::format(
|
||||
"Accounting Packet received for {}, CalledStationID: {}, CallingStationID:{}",
|
||||
SerialNumber, CalledStationID, CallingStationID));
|
||||
"Accounting Packet Response received for {}", SerialNumber ));
|
||||
AP_WS_Server()->SendRadiusAccountingData(SerialNumber, P.Buffer(), P.Size());
|
||||
}
|
||||
|
||||
@@ -210,9 +211,13 @@ namespace OpenWifi {
|
||||
return;
|
||||
}
|
||||
P.Evaluate(ReceiveSize);
|
||||
|
||||
if(Logger().trace()) {
|
||||
P.Log(std::cout);
|
||||
}
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if (SerialNumber.empty()) {
|
||||
poco_warning(Logger(), "Authentication: missing serial number.");
|
||||
poco_warning(Logger(), "Authentication: missing serial number. Dropping request.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
@@ -236,10 +241,11 @@ namespace OpenWifi {
|
||||
poco_warning(Logger(), "CoA/DM: bad packet received.");
|
||||
return;
|
||||
}
|
||||
|
||||
P.Evaluate(ReceiveSize);
|
||||
auto SerialNumber = P.ExtractSerialNumberTIP();
|
||||
if (SerialNumber.empty()) {
|
||||
poco_warning(Logger(), "CoA/DM: missing serial number.");
|
||||
poco_warning(Logger(), "CoA/DM: missing serial number. Dropping request.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
@@ -252,28 +258,23 @@ namespace OpenWifi {
|
||||
AP_WS_Server()->SendRadiusCoAData(SerialNumber, P.Buffer(), P.Size());
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::SendAccountingData(const std::string &serialNumber,
|
||||
const char *buffer, std::size_t size) {
|
||||
|
||||
if (!Continue())
|
||||
return;
|
||||
|
||||
try {
|
||||
RADIUS::RadiusPacket P((unsigned char *)buffer, size);
|
||||
auto Destination = P.ExtractProxyStateDestination();
|
||||
void RADIUS_proxy_server::RouteAndSendAccountingPacket(const std::string &Destination, const std::string &serialNumber, RADIUS::RadiusPacket &P, bool RecomputeAuthenticator, std::string & secret) {
|
||||
try{
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
Poco::Net::SocketAddress Dst(Destination);
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
bool UseRADSEC = false;
|
||||
auto FinalDestination = Route(radius_type::acct, Dst, P, UseRADSEC);
|
||||
auto FinalDestination = Route(radius_type::acct, Dst, P, UseRADSEC, secret);
|
||||
if (UseRADSEC) {
|
||||
Poco::Net::SocketAddress RSP(FinalDestination.host(), 0);
|
||||
auto DestinationServer = RADSECservers_.find(RSP);
|
||||
if (DestinationServer != end(RADSECservers_)) {
|
||||
DestinationServer->second->SendData(serialNumber, (const unsigned char *)buffer,
|
||||
size);
|
||||
if(RecomputeAuthenticator) {
|
||||
P.RecomputeAuthenticator("radsec");
|
||||
}
|
||||
DestinationServer->second->SendData(serialNumber, P.Buffer(), P.Size());
|
||||
}
|
||||
} else {
|
||||
if ((Dst.family() == Poco::Net::SocketAddress::IPv4 &&
|
||||
@@ -287,10 +288,15 @@ namespace OpenWifi {
|
||||
serialNumber));
|
||||
return;
|
||||
}
|
||||
|
||||
if(RecomputeAuthenticator) {
|
||||
P.RecomputeAuthenticator(secret);
|
||||
}
|
||||
|
||||
auto AllSent =
|
||||
SendData(Dst.family() == Poco::Net::SocketAddress::IPv4 ? *AccountingSocketV4_
|
||||
: *AccountingSocketV6_,
|
||||
(const unsigned char *)buffer, size, FinalDestination);
|
||||
: *AccountingSocketV6_
|
||||
, P.Buffer(), P.Size(), FinalDestination);
|
||||
if (!AllSent)
|
||||
poco_error(Logger(),
|
||||
fmt::format("{}: Could not send Accounting packet packet to {}.",
|
||||
@@ -309,13 +315,43 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void store_packet(const std::string &serialNumber, const char *buffer, std::size_t size) {
|
||||
static std::uint64_t pkt=0;
|
||||
|
||||
std::string filename = MicroServiceDataDirectory() + "/radius." + serialNumber + "." + std::to_string(pkt++) + ".bin";
|
||||
|
||||
std::ofstream ofs(filename,std::ios_base::binary | std::ios_base::trunc | std::ios_base::out);
|
||||
ofs.write(buffer,size);
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::SendAccountingData(const std::string &serialNumber,
|
||||
const char *buffer, std::size_t size, std::string & secret) {
|
||||
|
||||
if (!Continue())
|
||||
return;
|
||||
|
||||
try {
|
||||
RADIUS::RadiusPacket P((unsigned char *)buffer, size);
|
||||
auto Destination = P.ExtractProxyStateDestination();
|
||||
RouteAndSendAccountingPacket(Destination, serialNumber, P, false, secret);
|
||||
RADIUSSessionTracker()->AddAccountingSession(Destination, serialNumber, P, secret);
|
||||
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(),
|
||||
fmt::format("Bad RADIUS ACCT Packet from {}. Dropped.", serialNumber));
|
||||
}
|
||||
}
|
||||
|
||||
bool RADIUS_proxy_server::SendData(Poco::Net::DatagramSocket &Sock, const unsigned char *buf,
|
||||
std::size_t size, const Poco::Net::SocketAddress &S) {
|
||||
return Sock.sendTo(buf, size, S) == (int)size;
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::SendAuthenticationData(const std::string &serialNumber,
|
||||
const char *buffer, std::size_t size) {
|
||||
const char *buffer, std::size_t size, std::string & secret) {
|
||||
|
||||
if (!Continue())
|
||||
return;
|
||||
@@ -329,7 +365,9 @@ namespace OpenWifi {
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
bool UseRADSEC = false;
|
||||
auto FinalDestination = Route(radius_type::auth, Dst, P, UseRADSEC);
|
||||
auto FinalDestination = Route(radius_type::auth, Dst, P, UseRADSEC, secret);
|
||||
RADIUSSessionTracker()->AddAuthenticationSession(Destination, serialNumber, P, secret);
|
||||
|
||||
if (UseRADSEC) {
|
||||
Poco::Net::SocketAddress RSP(FinalDestination.host(), 0);
|
||||
auto DestinationServer = RADSECservers_.find(RSP);
|
||||
@@ -372,7 +410,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::SendCoAData(const std::string &serialNumber, const char *buffer,
|
||||
std::size_t size) {
|
||||
std::size_t size, std::string & secret) {
|
||||
|
||||
if (!Continue())
|
||||
return;
|
||||
@@ -385,10 +423,19 @@ namespace OpenWifi {
|
||||
Destination = "0.0.0.0:0";
|
||||
}
|
||||
|
||||
if(Logger().trace()) {
|
||||
P.Log(std::cout);
|
||||
}
|
||||
|
||||
if(Destination.empty()) {
|
||||
poco_warning(Logger(),fmt::format("{}: CoA packet does not have a valid destination.", serialNumber));
|
||||
return;
|
||||
}
|
||||
|
||||
Poco::Net::SocketAddress Dst(Destination);
|
||||
std::lock_guard G(Mutex_);
|
||||
bool UseRADSEC = false;
|
||||
auto FinalDestination = Route(radius_type::coa, Dst, P, UseRADSEC);
|
||||
auto FinalDestination = Route(radius_type::coa, Dst, P, UseRADSEC, secret);
|
||||
if (UseRADSEC) {
|
||||
Poco::Net::SocketAddress RSP(FinalDestination.host(), 0);
|
||||
auto DestinationServer = RADSECservers_.find(RSP);
|
||||
@@ -409,9 +456,10 @@ namespace OpenWifi {
|
||||
auto AllSent = SendData(
|
||||
Dst.family() == Poco::Net::SocketAddress::IPv4 ? *CoASocketV4_ : *CoASocketV6_,
|
||||
(const unsigned char *)buffer, size, FinalDestination);
|
||||
if (!AllSent)
|
||||
if (!AllSent) {
|
||||
poco_error(Logger(), fmt::format("{}: Could not send CoA packet packet to {}.",
|
||||
serialNumber, Destination));
|
||||
}
|
||||
else
|
||||
poco_debug(Logger(), fmt::format("{}: Sending CoA Packet to {}", serialNumber,
|
||||
FinalDestination.toString()));
|
||||
@@ -426,7 +474,8 @@ namespace OpenWifi {
|
||||
|
||||
void RADIUS_proxy_server::ParseServerList(const GWObjects::RadiusProxyServerConfig &Config,
|
||||
std::vector<Destination> &V4,
|
||||
std::vector<Destination> &V6, bool setAsDefault) {
|
||||
std::vector<Destination> &V6, bool setAsDefault,
|
||||
const std::string &poolProxyIp) {
|
||||
uint64_t TotalV4 = 0, TotalV6 = 0;
|
||||
|
||||
for (const auto &server : Config.servers) {
|
||||
@@ -449,7 +498,9 @@ namespace OpenWifi {
|
||||
.methodParameters = Config.methodParameters,
|
||||
.useAsDefault = setAsDefault,
|
||||
.useRADSEC = server.radsec,
|
||||
.realms = server.radsecRealms};
|
||||
.realms = server.radsecRealms,
|
||||
.secret = server.secret,
|
||||
.poolProxyIp = poolProxyIp};
|
||||
|
||||
if (setAsDefault && D.useRADSEC)
|
||||
DefaultIsRADSEC_ = true;
|
||||
@@ -498,11 +549,11 @@ namespace OpenWifi {
|
||||
for (const auto &pool : RPC.pools) {
|
||||
RadiusPool NewPool;
|
||||
ParseServerList(pool.authConfig, NewPool.AuthV4, NewPool.AuthV6,
|
||||
pool.useByDefault);
|
||||
pool.useByDefault, pool.poolProxyIp);
|
||||
ParseServerList(pool.acctConfig, NewPool.AcctV4, NewPool.AcctV6,
|
||||
pool.useByDefault);
|
||||
pool.useByDefault, pool.poolProxyIp);
|
||||
ParseServerList(pool.coaConfig, NewPool.CoaV4, NewPool.CoaV6,
|
||||
pool.useByDefault);
|
||||
pool.useByDefault, pool.poolProxyIp);
|
||||
Pools_.push_back(NewPool);
|
||||
}
|
||||
} else {
|
||||
@@ -530,7 +581,9 @@ namespace OpenWifi {
|
||||
Poco::Net::SocketAddress
|
||||
RADIUS_proxy_server::DefaultRoute(radius_type rtype,
|
||||
const Poco::Net::SocketAddress &RequestedAddress,
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC) {
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC,
|
||||
std::string &Secret) {
|
||||
|
||||
bool IsV4 = RequestedAddress.family() == Poco::Net::SocketAddress::IPv4;
|
||||
|
||||
// find the realm...
|
||||
@@ -545,7 +598,6 @@ namespace OpenWifi {
|
||||
if (!server.realms.empty()) {
|
||||
for (const auto &realm : server.realms) {
|
||||
if (RealmMatch(UserRealm, realm)) {
|
||||
std::cout << "Realm match..." << std::endl;
|
||||
UseRADSEC = true;
|
||||
return server.Addr;
|
||||
}
|
||||
@@ -565,18 +617,18 @@ namespace OpenWifi {
|
||||
case radius_type::auth: {
|
||||
return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].AuthV4
|
||||
: Pools_[DefaultPoolIndex_].AuthV6,
|
||||
RequestedAddress);
|
||||
RequestedAddress, Secret);
|
||||
}
|
||||
case radius_type::coa: {
|
||||
return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].CoaV4
|
||||
: Pools_[DefaultPoolIndex_].CoaV6,
|
||||
RequestedAddress, Secret);
|
||||
}
|
||||
case radius_type::acct:
|
||||
default: {
|
||||
return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].AcctV4
|
||||
: Pools_[DefaultPoolIndex_].AcctV6,
|
||||
RequestedAddress);
|
||||
}
|
||||
case radius_type::coa: {
|
||||
return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].CoaV4
|
||||
: Pools_[DefaultPoolIndex_].CoaV6,
|
||||
RequestedAddress);
|
||||
RequestedAddress, Secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,8 +636,8 @@ namespace OpenWifi {
|
||||
Poco::Net::SocketAddress
|
||||
RADIUS_proxy_server::Route([[maybe_unused]] radius_type rtype,
|
||||
const Poco::Net::SocketAddress &RequestedAddress,
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC) {
|
||||
std::lock_guard G(Mutex_);
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC,
|
||||
std::string &Secret) {
|
||||
|
||||
if (Pools_.empty()) {
|
||||
UseRADSEC = false;
|
||||
@@ -594,39 +646,47 @@ namespace OpenWifi {
|
||||
|
||||
bool IsV4 = RequestedAddress.family() == Poco::Net::SocketAddress::IPv4;
|
||||
bool useDefault;
|
||||
|
||||
useDefault = IsV4 ? RequestedAddress.host() ==
|
||||
Poco::Net::IPAddress::wildcard(Poco::Net::IPAddress::IPv4)
|
||||
: RequestedAddress.host() ==
|
||||
Poco::Net::IPAddress::wildcard(Poco::Net::IPAddress::IPv6);
|
||||
|
||||
if (useDefault) {
|
||||
return DefaultRoute(rtype, RequestedAddress, P, UseRADSEC);
|
||||
return DefaultRoute(rtype, RequestedAddress, P, UseRADSEC, Secret);
|
||||
}
|
||||
|
||||
auto isAddressInPool = [&](const std::vector<Destination> &D, bool &UseRADSEC) -> bool {
|
||||
for (const auto &entry : D)
|
||||
for (const auto &entry : D) {
|
||||
if (!entry.poolProxyIp.empty() &&
|
||||
entry.poolProxyIp == RequestedAddress.host().toString()) {
|
||||
UseRADSEC = entry.useRADSEC;
|
||||
return true;
|
||||
}
|
||||
if (entry.Addr.host() == RequestedAddress.host()) {
|
||||
UseRADSEC = entry.useRADSEC;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
for (auto &i : Pools_) {
|
||||
for (auto &pool : Pools_) {
|
||||
// try and match the pool's address to the destination
|
||||
switch (rtype) {
|
||||
case radius_type::coa: {
|
||||
if (isAddressInPool((IsV4 ? i.CoaV4 : i.CoaV6), UseRADSEC)) {
|
||||
return ChooseAddress(IsV4 ? i.CoaV4 : i.CoaV6, RequestedAddress);
|
||||
if (isAddressInPool((IsV4 ? pool.CoaV4 : pool.CoaV6), UseRADSEC)) {
|
||||
return ChooseAddress(IsV4 ? pool.CoaV4 : pool.CoaV6, RequestedAddress, Secret);
|
||||
}
|
||||
} break;
|
||||
case radius_type::auth: {
|
||||
if (isAddressInPool((IsV4 ? i.AuthV4 : i.AuthV6), UseRADSEC)) {
|
||||
return ChooseAddress(IsV4 ? i.AuthV4 : i.AuthV6, RequestedAddress);
|
||||
if (isAddressInPool((IsV4 ? pool.AuthV4 : pool.AuthV6), UseRADSEC)) {
|
||||
return ChooseAddress(IsV4 ? pool.AuthV4 : pool.AuthV6, RequestedAddress, Secret);
|
||||
}
|
||||
} break;
|
||||
case radius_type::acct: {
|
||||
if (isAddressInPool((IsV4 ? i.AcctV4 : i.AcctV6), UseRADSEC)) {
|
||||
return ChooseAddress(IsV4 ? i.AcctV4 : i.AcctV6, RequestedAddress);
|
||||
if (isAddressInPool((IsV4 ? pool.AcctV4 : pool.AcctV6), UseRADSEC)) {
|
||||
return ChooseAddress(IsV4 ? pool.AcctV4 : pool.AcctV6, RequestedAddress, Secret);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@@ -638,10 +698,13 @@ namespace OpenWifi {
|
||||
|
||||
Poco::Net::SocketAddress
|
||||
RADIUS_proxy_server::ChooseAddress(std::vector<Destination> &Pool,
|
||||
const Poco::Net::SocketAddress &OriginalAddress) {
|
||||
const Poco::Net::SocketAddress &OriginalAddress,
|
||||
std::string &Secret) {
|
||||
|
||||
if (Pool.size() == 1) {
|
||||
return Pool[0].Addr;
|
||||
Secret = Pool[0].secret;
|
||||
auto A = Pool[0].Addr;
|
||||
return A;
|
||||
}
|
||||
|
||||
if (Pool[0].strategy == "weighted") {
|
||||
@@ -657,6 +720,7 @@ namespace OpenWifi {
|
||||
index = pos;
|
||||
cur_state = i.state;
|
||||
found = true;
|
||||
Secret = i.secret ;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
@@ -664,9 +728,9 @@ namespace OpenWifi {
|
||||
if (!found) {
|
||||
return OriginalAddress;
|
||||
}
|
||||
|
||||
Pool[index].state += Pool[index].step;
|
||||
return Pool[index].Addr;
|
||||
|
||||
} else if (Pool[0].strategy == "round_robin") {
|
||||
bool found = false;
|
||||
uint64_t cur_state = std::numeric_limits<uint64_t>::max();
|
||||
@@ -679,20 +743,23 @@ namespace OpenWifi {
|
||||
if (i.state < cur_state) {
|
||||
index = pos;
|
||||
cur_state = i.state;
|
||||
Secret = i.secret;
|
||||
found = true;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return OriginalAddress;
|
||||
// return OriginalAddress;
|
||||
}
|
||||
|
||||
Pool[index].state += 1;
|
||||
return Pool[index].Addr;
|
||||
} else if (Pool[0].strategy == "random") {
|
||||
if (Pool.size() > 1) {
|
||||
return Pool[std::rand() % Pool.size()].Addr;
|
||||
auto index = std::rand() % Pool.size();
|
||||
Secret = Pool[index].secret;
|
||||
return Pool[index].Addr;
|
||||
} else {
|
||||
return OriginalAddress;
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ namespace OpenWifi {
|
||||
void OnCoASocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf);
|
||||
|
||||
void SendAccountingData(const std::string &serialNumber, const char *buffer,
|
||||
std::size_t size);
|
||||
std::size_t size, std::string & secret);
|
||||
void SendAuthenticationData(const std::string &serialNumber, const char *buffer,
|
||||
std::size_t size);
|
||||
void SendCoAData(const std::string &serialNumber, const char *buffer, std::size_t size);
|
||||
std::size_t size, std::string & secret);
|
||||
void SendCoAData(const std::string &serialNumber, const char *buffer, std::size_t size, std::string & secret);
|
||||
|
||||
void RouteAndSendAccountingPacket(const std::string &Destination, const std::string &serialNumber, RADIUS::RadiusPacket &P, bool reComputeAuthenticator, std::string & secret);
|
||||
|
||||
void SetConfig(const GWObjects::RadiusProxyPoolList &C);
|
||||
void DeleteConfig();
|
||||
@@ -59,6 +61,8 @@ namespace OpenWifi {
|
||||
bool useAsDefault = false;
|
||||
bool useRADSEC = false;
|
||||
std::vector<std::string> realms;
|
||||
std::string secret;
|
||||
std::string poolProxyIp;
|
||||
};
|
||||
|
||||
inline bool Continue() const { return Running_ && Enabled_ && !Pools_.empty(); }
|
||||
@@ -102,16 +106,18 @@ namespace OpenWifi {
|
||||
void ParseConfig();
|
||||
void ResetConfig();
|
||||
Poco::Net::SocketAddress Route(radius_type rtype, const Poco::Net::SocketAddress &A,
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC);
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC, std::string &secret);
|
||||
void ParseServerList(const GWObjects::RadiusProxyServerConfig &Config,
|
||||
std::vector<Destination> &V4, std::vector<Destination> &V6,
|
||||
bool setAsDefault);
|
||||
std::vector<Destination> &V4,
|
||||
std::vector<Destination> &V6, bool setAsDefault,
|
||||
const std::string &poolProxyIp);
|
||||
static Poco::Net::SocketAddress
|
||||
ChooseAddress(std::vector<Destination> &Pool,
|
||||
const Poco::Net::SocketAddress &OriginalAddress);
|
||||
const Poco::Net::SocketAddress &OriginalAddress, std::string &Secret);
|
||||
Poco::Net::SocketAddress DefaultRoute([[maybe_unused]] radius_type rtype,
|
||||
const Poco::Net::SocketAddress &RequestedAddress,
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC);
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC,
|
||||
std::string &Secret);
|
||||
};
|
||||
|
||||
inline auto RADIUS_proxy_server() { return RADIUS_proxy_server::instance(); }
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "RESTObjects/RESTAPI_GWobjects.h"
|
||||
|
||||
#include "Poco/Crypto/X509Certificate.h"
|
||||
#include "Poco/Crypto/RSAKey.h"
|
||||
#include "Poco/Net/Context.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
#include "Poco/Net/SecureStreamSocket.h"
|
||||
@@ -27,10 +28,12 @@ namespace OpenWifi {
|
||||
|
||||
class RADSEC_server : public Poco::Runnable {
|
||||
public:
|
||||
RADSEC_server(Poco::Net::SocketReactor &R, GWObjects::RadiusProxyServerEntry E)
|
||||
RADSEC_server(Poco::Net::SocketReactor &R, GWObjects::RadiusProxyServerEntry E, const GWObjects::RadiusProxyPool &P)
|
||||
: Reactor_(R), Server_(std::move(E)),
|
||||
Logger_(Poco::Logger::get(
|
||||
fmt::format("RADSEC: {}@{}:{}", Server_.name, Server_.ip, Server_.port))) {
|
||||
KeepAlive_ = P.radsecKeepAlive;
|
||||
Type_ = P.radsecPoolType;
|
||||
Start();
|
||||
}
|
||||
|
||||
@@ -49,22 +52,27 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
inline void run() final {
|
||||
Poco::Thread::trySleep(3000);
|
||||
std::uint64_t LastStatus = 0;
|
||||
auto RadSecKeepAlive = MicroServiceConfigGetInt("radsec.keepalive", 120);
|
||||
Poco::Thread::trySleep(5000);
|
||||
std::uint64_t CurrentDelay = 10, maxDelay=300, LastTry=0, LastKeepAlive=0;
|
||||
while (TryAgain_) {
|
||||
if (!Connected_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
LastStatus = Utils::Now();
|
||||
Connect();
|
||||
} else if ((Utils::Now() - LastStatus) > RadSecKeepAlive) {
|
||||
if(!LastTry || (Utils::Now()-LastTry)>CurrentDelay) {
|
||||
LastTry = Utils::Now();
|
||||
if (!Connect()) {
|
||||
CurrentDelay *= 2;
|
||||
if(CurrentDelay>maxDelay) CurrentDelay=10;
|
||||
} else {
|
||||
CurrentDelay = 10;
|
||||
}
|
||||
}
|
||||
} else if ((Utils::Now() - LastKeepAlive) > KeepAlive_) {
|
||||
RADIUS::RadiusOutputPacket P(Server_.radsecSecret);
|
||||
P.MakeStatusMessage();
|
||||
poco_information(Logger_, "Keep-Alive message.");
|
||||
poco_trace(Logger_, fmt::format("{}: Keep-Alive message.", Server_.name));
|
||||
Socket_->sendBytes(P.Data(), P.Len());
|
||||
LastStatus = Utils::Now();
|
||||
LastKeepAlive = Utils::Now();
|
||||
}
|
||||
Poco::Thread::trySleep(!Connected_ ? 3000 : 10000);
|
||||
Poco::Thread::trySleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +83,11 @@ namespace OpenWifi {
|
||||
RADIUS::RadiusPacket P(buffer, length);
|
||||
int sent_bytes;
|
||||
if (P.VerifyMessageAuthenticator(Server_.radsecSecret)) {
|
||||
poco_debug(Logger_, fmt::format("{}: {} Sending {} bytes", serial_number,
|
||||
poco_trace(Logger_, fmt::format("{}: {} Sending {} bytes", serial_number,
|
||||
P.PacketType(), length));
|
||||
sent_bytes = Socket_->sendBytes(buffer, length);
|
||||
} else {
|
||||
poco_debug(Logger_, fmt::format("{}: {} Sending {} bytes", serial_number,
|
||||
poco_trace(Logger_, fmt::format("{}: {} Sending {} bytes", serial_number,
|
||||
P.PacketType(), length));
|
||||
P.ComputeMessageAuthenticator(Server_.radsecSecret);
|
||||
sent_bytes = Socket_->sendBytes(P.Buffer(), length);
|
||||
@@ -105,35 +113,35 @@ namespace OpenWifi {
|
||||
if (P.IsAuthentication()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if (!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
poco_trace(Logger_,
|
||||
fmt::format("{}: {} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusAuthenticationData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "AUTH packet dropped.");
|
||||
poco_trace(Logger_, "AUTH packet dropped.");
|
||||
}
|
||||
} else if (P.IsAccounting()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if (!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
poco_trace(Logger_,
|
||||
fmt::format("{}: {} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusAccountingData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "ACCT packet dropped.");
|
||||
poco_trace(Logger_, "ACCT packet dropped.");
|
||||
}
|
||||
} else if (P.IsAuthority()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberTIP();
|
||||
if (!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
poco_trace(Logger_,
|
||||
fmt::format("{}: {} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusCoAData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "CoA/DM packet dropped.");
|
||||
poco_trace(Logger_, "CoA/DM packet dropped.");
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger_,
|
||||
@@ -165,7 +173,126 @@ namespace OpenWifi {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
inline bool Connect() {
|
||||
static inline bool IsExpired(const Poco::Crypto::X509Certificate &C) {
|
||||
return C.expiresOn().timestamp().epochTime() < (std::time_t)Utils::Now();
|
||||
}
|
||||
|
||||
inline bool Connect_GlobalReach() {
|
||||
if (TryAgain_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
|
||||
Poco::TemporaryFile CertFile_(MicroServiceDataDirectory());
|
||||
Poco::TemporaryFile KeyFile_(MicroServiceDataDirectory());
|
||||
Poco::TemporaryFile OpenRoamingRootCertFile_(MicroServiceDataDirectory());
|
||||
Poco::TemporaryFile Intermediate0(MicroServiceDataDirectory());
|
||||
Poco::TemporaryFile Intermediate1(MicroServiceDataDirectory());
|
||||
Poco::TemporaryFile Combined(MicroServiceDataDirectory());
|
||||
std::vector<std::unique_ptr<Poco::TemporaryFile>> CaCertFiles_;
|
||||
|
||||
DecodeFile(KeyFile_.path(), Server_.radsecKey);
|
||||
DecodeFile(CertFile_.path(), Server_.radsecCert);
|
||||
DecodeFile(Intermediate0.path(), Server_.radsecCacerts[0]);
|
||||
DecodeFile(Intermediate1.path(), Server_.radsecCacerts[1]);
|
||||
|
||||
for (auto &cert : Server_.radsecCacerts) {
|
||||
CaCertFiles_.emplace_back(
|
||||
std::make_unique<Poco::TemporaryFile>(MicroServiceDataDirectory()));
|
||||
DecodeFile(CaCertFiles_[CaCertFiles_.size() - 1]->path(), cert);
|
||||
}
|
||||
|
||||
std::string OpenRoamingRootCert{"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIClDCCAhugAwIBAgIUF1f+h+uJNHyr+ZqTpwew8LYRAW0wCgYIKoZIzj0EAwMw\n"
|
||||
"gYkxCzAJBgNVBAYTAkdCMQ8wDQYDVQQIEwZMb25kb24xDzANBgNVBAcTBkxvbmRv\n"
|
||||
"bjEsMCoGA1UEChMjR2xvYmFsUmVhY2ggVGVjaG5vbG9neSBFTUVBIExpbWl0ZWQx\n"
|
||||
"KjAoBgNVBAMTIUdsb2JhbFJlYWNoIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0y\n"
|
||||
"MzA3MTQwOTMyMDBaFw00MzA3MDkwOTMyMDBaMIGJMQswCQYDVQQGEwJHQjEPMA0G\n"
|
||||
"A1UECBMGTG9uZG9uMQ8wDQYDVQQHEwZMb25kb24xLDAqBgNVBAoTI0dsb2JhbFJl\n"
|
||||
"YWNoIFRlY2hub2xvZ3kgRU1FQSBMaW1pdGVkMSowKAYDVQQDEyFHbG9iYWxSZWFj\n"
|
||||
"aCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARy\n"
|
||||
"f02umFNy5W/TtM5nfMaLhRF61vLxhT8iNQHR1mXiRmNdME3ArForBcAm2eolHPcJ\n"
|
||||
"RH9DcXs59d2zzoPEaBjXADTCjUts3F7G6fjqvfki2e/txx/xfUopQO8G54XcFWqj\n"
|
||||
"QjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRS\n"
|
||||
"tNe7MgAFwTaMZKUtS1/8pVoBqjAKBggqhkjOPQQDAwNnADBkAjA7VKHTybtSMBcN\n"
|
||||
"717jGYvkWlcj4c9/LzPtkHO053wGsPigaq+1SjY7tDhS/g9oUQACMA6UqH2e8cfn\n"
|
||||
"cZqmBNVNN3DBjIb4anug7F+FnYOQF36ua6MLBeGn3aKxvu1aO+hjPg==\n"
|
||||
"-----END CERTIFICATE-----\n"};
|
||||
|
||||
std::ofstream ofs{OpenRoamingRootCertFile_.path().c_str(),std::ios_base::trunc|std::ios_base::out|std::ios_base::binary};
|
||||
ofs << OpenRoamingRootCert;
|
||||
ofs.close();
|
||||
|
||||
Poco::Net::Context::Ptr SecureContext =
|
||||
Poco::AutoPtr<Poco::Net::Context>(new Poco::Net::Context(
|
||||
Poco::Net::Context::TLS_CLIENT_USE, ""));
|
||||
|
||||
if (Server_.allowSelfSigned) {
|
||||
SecureContext->setSecurityLevel(Poco::Net::Context::SECURITY_LEVEL_NONE);
|
||||
SecureContext->enableExtendedCertificateVerification(false);
|
||||
}
|
||||
|
||||
SecureContext->usePrivateKey(Poco::Crypto::RSAKey("",KeyFile_.path(),""));
|
||||
Poco::Crypto::X509Certificate Cert(CertFile_.path());
|
||||
if(!IsExpired(Cert)) {
|
||||
SecureContext->useCertificate(Poco::Crypto::X509Certificate(CertFile_.path()));
|
||||
} else {
|
||||
poco_error(Logger_, fmt::format("Certificate for {} has expired. We cannot connect to this server.", Server_.name));
|
||||
return false;
|
||||
}
|
||||
SecureContext->addCertificateAuthority(Poco::Crypto::X509Certificate(OpenRoamingRootCertFile_.path()));
|
||||
SecureContext->addChainCertificate(Poco::Crypto::X509Certificate(Intermediate0.path()));
|
||||
SecureContext->addChainCertificate(Poco::Crypto::X509Certificate(Intermediate1.path()));
|
||||
SecureContext->enableExtendedCertificateVerification(false);
|
||||
|
||||
Socket_ = std::make_unique<Poco::Net::SecureStreamSocket>(SecureContext);
|
||||
|
||||
Poco::Net::SocketAddress Destination(Server_.ip, Server_.port);
|
||||
|
||||
try {
|
||||
poco_information(Logger_, "Attempting to connect");
|
||||
Socket_->connect(Destination, Poco::Timespan(20, 0));
|
||||
Socket_->completeHandshake();
|
||||
|
||||
if (!Server_.allowSelfSigned) {
|
||||
Socket_->verifyPeerCertificate();
|
||||
}
|
||||
|
||||
if (Socket_->havePeerCertificate()) {
|
||||
Peer_Cert_ = std::make_unique<Poco::Crypto::X509Certificate>(
|
||||
Socket_->peerCertificate());
|
||||
}
|
||||
|
||||
Socket_->setBlocking(false);
|
||||
Socket_->setNoDelay(true);
|
||||
Socket_->setKeepAlive(true);
|
||||
Socket_->setReceiveTimeout(Poco::Timespan(1 * 60 * 60, 0));
|
||||
|
||||
Reactor_.addEventHandler(
|
||||
*Socket_, Poco::NObserver<RADSEC_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADSEC_server::onData));
|
||||
Reactor_.addEventHandler(
|
||||
*Socket_, Poco::NObserver<RADSEC_server, Poco::Net::ErrorNotification>(
|
||||
*this, &RADSEC_server::onError));
|
||||
Reactor_.addEventHandler(
|
||||
*Socket_, Poco::NObserver<RADSEC_server, Poco::Net::ShutdownNotification>(
|
||||
*this, &RADSEC_server::onShutdown));
|
||||
|
||||
Connected_ = true;
|
||||
poco_information(Logger_, fmt::format("Connected. CN={}", CommonName()));
|
||||
return true;
|
||||
} catch (const Poco::Net::NetException &E) {
|
||||
poco_warning(Logger_, "NetException: Could not connect.");
|
||||
Logger_.log(E);
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger_, "Exception: Could not connect.");
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger_, "Could not connect.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Connect_Orion() {
|
||||
if (TryAgain_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
|
||||
@@ -176,6 +303,12 @@ namespace OpenWifi {
|
||||
DecodeFile(CertFile_.path(), Server_.radsecCert);
|
||||
DecodeFile(KeyFile_.path(), Server_.radsecKey);
|
||||
|
||||
Poco::Crypto::X509Certificate Cert(CertFile_.path());
|
||||
if(IsExpired(Cert)) {
|
||||
poco_error(Logger_, fmt::format("Certificate for {} has expired. We cannot connect to this server.", Server_.name));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto &cert : Server_.radsecCacerts) {
|
||||
CaCertFiles_.emplace_back(
|
||||
std::make_unique<Poco::TemporaryFile>(MicroServiceDataDirectory()));
|
||||
@@ -244,6 +377,19 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Connect_Generic() {
|
||||
if (TryAgain_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Connect() {
|
||||
if(Type_=="orion") return Connect_Orion();
|
||||
if(Type_=="globalreach") return Connect_GlobalReach();
|
||||
return Connect_Generic();
|
||||
}
|
||||
|
||||
inline void Disconnect() {
|
||||
if (Connected_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
@@ -300,5 +446,7 @@ namespace OpenWifi {
|
||||
std::unique_ptr<Poco::Crypto::X509Certificate> Peer_Cert_;
|
||||
volatile bool Connected_ = false;
|
||||
volatile bool TryAgain_ = true;
|
||||
std::uint64_t KeepAlive_;
|
||||
std::string Type_;
|
||||
};
|
||||
} // namespace OpenWifi
|
||||
@@ -171,7 +171,7 @@ namespace OpenWifi::RESTAPI_RPC {
|
||||
// we need to post a kafka event for this.
|
||||
if (Params.has(uCentralProtocol::CONFIG)) {
|
||||
DeviceConfigurationChangeKafkaEvent KEvent(
|
||||
Cmd.SerialNumber, Utils::Now(),
|
||||
Utils::SerialNumberToInt(Cmd.SerialNumber), Utils::Now(),
|
||||
Params.get(uCentralProtocol::CONFIG).toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
#include "Poco/Array.h"
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
|
||||
#include "RESTAPI_default_configurations.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
117
src/RESTAPI/RESTAPI_default_firmware.cpp
Normal file
117
src/RESTAPI/RESTAPI_default_firmware.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// License type: BSD 3-Clause License
|
||||
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
|
||||
//
|
||||
// Created by stephane bourque on 2023-07-11.
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#include "RESTAPI/RESTAPI_default_firmware.h"
|
||||
|
||||
#include "RESTObjects/RESTAPI_GWobjects.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/orm.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void RESTAPI_default_firmware::DoGet() {
|
||||
std::string deviceType = ORM::Escape(GetBinding(RESTAPI::Protocol::DEVICETYPE, ""));
|
||||
GWObjects::DefaultFirmware Firmware;
|
||||
if (StorageService()->GetDefaultFirmware(deviceType, Firmware)) {
|
||||
return Object(Firmware);
|
||||
}
|
||||
NotFound();
|
||||
}
|
||||
|
||||
void RESTAPI_default_firmware::DoDelete() {
|
||||
std::string deviceType = ORM::Escape(GetBinding(RESTAPI::Protocol::DEVICETYPE, ""));
|
||||
if (deviceType.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
if (StorageService()->DeleteDefaultFirmware(deviceType)) {
|
||||
return OK();
|
||||
}
|
||||
BadRequest(RESTAPI::Errors::CouldNotBeDeleted);
|
||||
}
|
||||
|
||||
void RESTAPI_default_firmware::DoPost() {
|
||||
std::string deviceType = GetBinding(RESTAPI::Protocol::DEVICETYPE, "");
|
||||
|
||||
if (deviceType.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
if (StorageService()->DefaultFirmwareAlreadyExists(deviceType)) {
|
||||
return BadRequest(RESTAPI::Errors::DefFirmwareNameExists);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
GWObjects::DefaultFirmware Firmware;
|
||||
if (!Firmware.from_json(Obj)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
if(Firmware.uri.empty() || Firmware.revision.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
try {
|
||||
Poco::URI FirmwareURI(Firmware.uri);
|
||||
} catch (...) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidURI);
|
||||
}
|
||||
|
||||
Firmware.Created = Firmware.LastModified = Utils::Now();
|
||||
if (StorageService()->CreateDefaultFirmware(Firmware)) {
|
||||
GWObjects::DefaultFirmware ModifiedFirmware;
|
||||
StorageService()->GetDefaultFirmware(deviceType, ModifiedFirmware);
|
||||
return Object(ModifiedFirmware);
|
||||
}
|
||||
BadRequest(RESTAPI::Errors::RecordNotCreated);
|
||||
}
|
||||
|
||||
void RESTAPI_default_firmware::DoPut() {
|
||||
std::string deviceType = GetBinding(RESTAPI::Protocol::DEVICETYPE, "");
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
GWObjects::DefaultFirmware NewFirmware;
|
||||
if (!NewFirmware.from_json(Obj)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
GWObjects::DefaultFirmware Existing;
|
||||
if (!StorageService()->GetDefaultFirmware(deviceType, Existing)) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
Existing.LastModified = Utils::Now();
|
||||
AssignIfPresent(Obj, "description", Existing.Description);
|
||||
AssignIfPresent(Obj, "imageCreationDate", Existing.imageCreationDate);
|
||||
AssignIfPresent(Obj, "revision", Existing.revision);
|
||||
|
||||
|
||||
if (Obj->has("uri")) {
|
||||
if(NewFirmware.uri.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
try {
|
||||
Poco::URI FirmwareURI(NewFirmware.uri);
|
||||
} catch (...) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidURI);
|
||||
}
|
||||
Existing.uri = NewFirmware.uri;
|
||||
}
|
||||
|
||||
|
||||
if (StorageService()->UpdateDefaultFirmware(Existing)) {
|
||||
GWObjects::DefaultFirmware ModifiedFirmware;
|
||||
|
||||
StorageService()->GetDefaultFirmware(deviceType, ModifiedFirmware);
|
||||
return Object(ModifiedFirmware);
|
||||
}
|
||||
|
||||
BadRequest(RESTAPI::Errors::RecordNotUpdated);
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
30
src/RESTAPI/RESTAPI_default_firmware.h
Normal file
30
src/RESTAPI/RESTAPI_default_firmware.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-07-11.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_default_firmware : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_default_firmware(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
|
||||
RESTAPI_GenericServerAccounting &Server,
|
||||
uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_POST,
|
||||
Poco::Net::HTTPRequest::HTTP_PUT,
|
||||
Poco::Net::HTTPRequest::HTTP_DELETE,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server, TransactionId, Internal) {}
|
||||
static auto PathName() {
|
||||
return std::list<std::string>{"/api/v1/default_firmware/{deviceType}"};
|
||||
}
|
||||
void DoGet() final;
|
||||
void DoDelete() final;
|
||||
void DoPost() final;
|
||||
void DoPut() final;
|
||||
};
|
||||
} // namespace OpenWifi
|
||||
25
src/RESTAPI/RESTAPI_default_firmwares.cpp
Normal file
25
src/RESTAPI/RESTAPI_default_firmwares.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-07-11.
|
||||
//
|
||||
|
||||
#include "RESTAPI_default_firmwares.h"
|
||||
|
||||
#include "Poco/Array.h"
|
||||
|
||||
#include "RESTAPI_default_firmwares.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void RESTAPI_default_firmwares::DoGet() {
|
||||
|
||||
if (QB_.CountOnly) {
|
||||
auto Count = StorageService()->GetDefaultFirmwaresCount();
|
||||
return ReturnCountOnly(Count);
|
||||
}
|
||||
|
||||
std::vector<GWObjects::DefaultFirmware> Firmwares;
|
||||
StorageService()->GetDefaultFirmwares(QB_.Offset, QB_.Limit, Firmwares);
|
||||
return Object(RESTAPI::Protocol::FIRMWARES, Firmwares);
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
25
src/RESTAPI/RESTAPI_default_firmwares.h
Normal file
25
src/RESTAPI/RESTAPI_default_firmwares.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-07-11.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_default_firmwares : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_default_firmwares(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
|
||||
RESTAPI_GenericServerAccounting &Server,
|
||||
uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server, TransactionId, Internal){};
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/default_firmwares"}; }
|
||||
void DoGet() final;
|
||||
void DoDelete() final{};
|
||||
void DoPost() final{};
|
||||
void DoPut() final{};
|
||||
};
|
||||
} // namespace OpenWifi
|
||||
@@ -309,7 +309,7 @@ namespace OpenWifi {
|
||||
|
||||
if (AP_WS_Server()->GetState(SerialNumber_, State)) {
|
||||
Poco::JSON::Object RetObject;
|
||||
State.to_json(RetObject);
|
||||
State.to_json(SerialNumber_, RetObject);
|
||||
return ReturnObject(RetObject);
|
||||
} else {
|
||||
Poco::JSON::Object RetObject;
|
||||
@@ -406,6 +406,7 @@ namespace OpenWifi {
|
||||
[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
|
||||
poco_debug(Logger_, fmt::format("PING({},{}): TID={} user={} serial={}", CMD_UUID, CMD_RPC,
|
||||
TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
if (Obj->has(RESTAPI::Protocol::SERIALNUMBER)) {
|
||||
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
|
||||
@@ -459,6 +460,14 @@ namespace OpenWifi {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
bool RESTAPI_device_commandHandler::IsDeviceSimulated(std::string &Serial) {
|
||||
GWObjects::Device Device;
|
||||
if(StorageService()->GetDevice(Serial,Device)) {
|
||||
return Device.simulated;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RESTAPI_device_commandHandler::CallCanceled(const char *Cmd, const std::string &UUID,
|
||||
uint64_t RPC,
|
||||
const OpenWifi::RESTAPI::Errors::msg &Err) {
|
||||
@@ -474,6 +483,10 @@ namespace OpenWifi {
|
||||
poco_debug(Logger_, fmt::format("SCRIPT({},{}): TID={} user={} serial={}", CMD_UUID,
|
||||
CMD_RPC, TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
if(IsDeviceSimulated(SerialNumber_)) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
GWObjects::ScriptRequest SCR;
|
||||
if (!SCR.from_json(Obj)) {
|
||||
@@ -899,6 +912,10 @@ namespace OpenWifi {
|
||||
poco_debug(Logger_, fmt::format("TRACE({},{}): TID={} user={} serial={}", CMD_UUID, CMD_RPC,
|
||||
TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
if(IsDeviceSimulated(SerialNumber_)) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
|
||||
if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) &&
|
||||
@@ -956,6 +973,12 @@ namespace OpenWifi {
|
||||
[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
|
||||
poco_debug(Logger_, fmt::format("WIFISCAN({},{}): TID={} user={} serial={}", CMD_UUID,
|
||||
CMD_RPC, TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
|
||||
if(IsDeviceSimulated(SerialNumber_)) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
|
||||
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
|
||||
@@ -1011,6 +1034,10 @@ namespace OpenWifi {
|
||||
poco_debug(Logger_, fmt::format("EVENT-QUEUE({},{}): TID={} user={} serial={}", CMD_UUID,
|
||||
CMD_RPC, TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
if(IsDeviceSimulated(SerialNumber_)) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) && Obj->isArray(RESTAPI::Protocol::TYPES)) {
|
||||
|
||||
@@ -1055,6 +1082,10 @@ namespace OpenWifi {
|
||||
poco_debug(Logger_, fmt::format("FORCE-REQUEST({},{}): TID={} user={} serial={}", CMD_UUID,
|
||||
CMD_RPC, TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
if(IsDeviceSimulated(SerialNumber_)) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) && Obj->has(uCentralProtocol::MESSAGE)) {
|
||||
|
||||
@@ -1095,15 +1126,16 @@ namespace OpenWifi {
|
||||
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
#define DBGLINE \
|
||||
{ std::cout << __LINE__ << std::endl; }
|
||||
|
||||
void RESTAPI_device_commandHandler::Rtty(
|
||||
const std::string &CMD_UUID, uint64_t CMD_RPC, std::chrono::milliseconds timeout,
|
||||
[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
|
||||
poco_debug(Logger_, fmt::format("RTTY({},{}): TID={} user={} serial={}", CMD_UUID, CMD_RPC,
|
||||
TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
if(IsDeviceSimulated(SerialNumber_)) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
if (!Restrictions.developer && Restrictions.rtty) {
|
||||
return BadRequest(RESTAPI::Errors::DeviceIsRestricted);
|
||||
}
|
||||
@@ -1112,6 +1144,11 @@ namespace OpenWifi {
|
||||
GWObjects::Device Device;
|
||||
|
||||
if (StorageService()->GetDevice(SerialNumber_, Device)) {
|
||||
|
||||
if(Device.simulated) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
static std::uint64_t rtty_sid = 0;
|
||||
rtty_sid += std::rand();
|
||||
GWObjects::RttySessionDetails Rtty{
|
||||
@@ -1185,6 +1222,10 @@ namespace OpenWifi {
|
||||
poco_debug(Logger_, fmt::format("TELEMETRY({},{}): TID={} user={} serial={}", CMD_UUID,
|
||||
CMD_RPC, TransactionId_, Requester(), SerialNumber_));
|
||||
|
||||
if(IsDeviceSimulated(SerialNumber_)) {
|
||||
return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
|
||||
if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) && Obj->has(RESTAPI::Protocol::INTERVAL) &&
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace OpenWifi {
|
||||
void GetChecks();
|
||||
void DeleteChecks();
|
||||
|
||||
bool IsDeviceSimulated(std::string &Serial);
|
||||
|
||||
void Configure(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
|
||||
const GWObjects::DeviceRestrictions &R);
|
||||
void Upgrade(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
|
||||
|
||||
@@ -41,6 +41,10 @@ namespace OpenWifi {
|
||||
void RESTAPI_device_handler::DoDelete() {
|
||||
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
|
||||
|
||||
if(!RESTAPI_utils::IsRootOrAdmin(UserInfo_.userinfo)) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if (!Utils::NormalizeMac(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace OpenWifi {
|
||||
Device.to_json(DeviceInfo);
|
||||
Answer.set("deviceInfo", DeviceInfo);
|
||||
Poco::JSON::Object CSInfo;
|
||||
CS.to_json(CSInfo);
|
||||
CS.to_json(Device.SerialNumber, CSInfo);
|
||||
Answer.set("connectionInfo", CSInfo);
|
||||
Poco::JSON::Object HCInfo;
|
||||
HC.to_json(HCInfo);
|
||||
|
||||
@@ -158,4 +158,54 @@ namespace OpenWifi {
|
||||
}
|
||||
ReturnObject(RetObj);
|
||||
}
|
||||
|
||||
static bool ValidMacPatternOnlyChars(const std::string &s) {
|
||||
return std::for_each(s.begin(),s.end(),[](const char c) {
|
||||
if(c=='%') return true;
|
||||
if(c>='0' && c<='9') return true;
|
||||
if(c>='a' && c<='f') return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void RESTAPI_devices_handler::DoDelete() {
|
||||
|
||||
if(!RESTAPI_utils::IsRootOrAdmin(UserInfo_.userinfo)) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if(!QB_.Select.empty() && !Utils::ValidSerialNumbers(QB_.Select)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
if(!QB_.Select.empty()) {
|
||||
for(auto &serialNumber:QB_.Select) {
|
||||
StorageService()->DeleteDevice(serialNumber);
|
||||
}
|
||||
return OK();
|
||||
}
|
||||
|
||||
auto SimulatedOnly = GetBoolParameter("simulatedOnly",false);
|
||||
auto oldestContact = GetParameter("oldestContact",0);
|
||||
if(oldestContact!=0) {
|
||||
StorageService()->DeleteDevices(oldestContact,SimulatedOnly);
|
||||
return OK();
|
||||
}
|
||||
|
||||
auto macPattern = GetParameter("macPattern","");
|
||||
if(macPattern.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
// rules out wrong values.
|
||||
Poco::toLowerInPlace(macPattern);
|
||||
Poco::replaceInPlace(macPattern,"*","%");
|
||||
if(!ValidMacPatternOnlyChars(macPattern)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
StorageService()->DeleteDevices(macPattern, SimulatedOnly);
|
||||
return OK();
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
@@ -18,11 +18,12 @@ namespace OpenWifi {
|
||||
bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_DELETE,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server, TransactionId, Internal){};
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/devices"}; };
|
||||
void DoGet() final;
|
||||
void DoDelete() final{};
|
||||
void DoDelete() final;
|
||||
void DoPost() final{};
|
||||
void DoPut() final{};
|
||||
};
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
static bool ValidRadiusPoolServerType(const std::string &T) {
|
||||
static std::set<std::string> Types{ "generic", "orion", "globalreach"};
|
||||
return Types.find(T)!=Types.end();
|
||||
}
|
||||
|
||||
void RESTAPI_radiusProxyConfig_handler::DoGet() {
|
||||
Logger_.information(fmt::format("GET-RADIUS-PROXY-CONFIG: TID={} user={} thr_id={}",
|
||||
TransactionId_, Requester(),
|
||||
@@ -44,10 +49,22 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
// Logically validate the config.
|
||||
for (const auto &pool : C.pools) {
|
||||
for (auto &pool : C.pools) {
|
||||
if (pool.name.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::PoolNameInvalid);
|
||||
}
|
||||
if (pool.radsecPoolType.empty()) {
|
||||
pool.radsecPoolType = "generic";
|
||||
}
|
||||
|
||||
if(!ValidRadiusPoolServerType(pool.radsecPoolType)) {
|
||||
return BadRequest(RESTAPI::Errors::NotAValidRadiusPoolType);
|
||||
}
|
||||
|
||||
if(pool.radsecKeepAlive==0) {
|
||||
pool.radsecKeepAlive=25;
|
||||
}
|
||||
|
||||
for (const auto &config : {pool.acctConfig, pool.authConfig, pool.coaConfig}) {
|
||||
if (config.servers.empty())
|
||||
continue;
|
||||
|
||||
141
src/RESTAPI/RESTAPI_radiussessions_handler.cpp
Normal file
141
src/RESTAPI/RESTAPI_radiussessions_handler.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-02.
|
||||
//
|
||||
|
||||
#include "RESTAPI_radiussessions_handler.h"
|
||||
#include <RESTObjects/RESTAPI_GWobjects.h>
|
||||
#include <RADIUSSessionTracker.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
bool MayBeAMAC(const std::string &mac) {
|
||||
return std::all_of(mac.begin(),mac.end(),[](char c)->bool {
|
||||
if ((c>='0' && c<='9') ||
|
||||
(c>='a' && c<='f') ||
|
||||
(c>='A' && c<='F') ||
|
||||
(c==':') ||
|
||||
(c=='-') ||
|
||||
(c=='*')) return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
std::string InsertDelimiters(const std::string &mac, int first=1, char delimiter=':') {
|
||||
std::string res;
|
||||
std::size_t index=0;
|
||||
for(auto c:mac) {
|
||||
res += c;
|
||||
index++;
|
||||
if(index<mac.size()) {
|
||||
if (!first)
|
||||
res += delimiter;
|
||||
}
|
||||
first = 1-first;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string StripDelimiters(const std::string &V) {
|
||||
std::string Res;
|
||||
std::for_each(V.begin(),V.end(),[&](char c){ if(c!=':' && c!='-') { Res += c; }});
|
||||
return Res;
|
||||
}
|
||||
|
||||
static std::string ConvertToMac(const std::string & V) {
|
||||
auto res = V;
|
||||
Poco::toUpperInPlace(res);
|
||||
res = StripDelimiters(res);
|
||||
if(res.size()==12) {
|
||||
res = InsertDelimiters(res);
|
||||
} else {
|
||||
if(res.find_first_of('*')==std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
if(res[0]=='*') {
|
||||
res = InsertDelimiters(res, 1 - (res.size() % 2) );
|
||||
} else {
|
||||
res = InsertDelimiters(res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void RESTAPI_radiussessions_handler::DoGet() {
|
||||
|
||||
if(GetBoolParameter("serialNumberOnly")) {
|
||||
std::vector<std::string> L;
|
||||
RADIUSSessionTracker()->GetAPList(L);
|
||||
return ReturnObject("serialNumbers",L);
|
||||
}
|
||||
|
||||
auto mac = GetParameter("mac","");
|
||||
auto userName = GetParameter("userName","");
|
||||
if(!userName.empty()) {
|
||||
GWObjects::RADIUSSessionList L;
|
||||
Poco::toLowerInPlace(userName);
|
||||
RADIUSSessionTracker()->GetUserNameAPSessions(userName,L);
|
||||
if(L.sessions.empty() && MayBeAMAC(userName)) {
|
||||
mac = ConvertToMac(userName);
|
||||
} else {
|
||||
return ReturnObject("sessions", L.sessions);
|
||||
}
|
||||
}
|
||||
|
||||
if(!mac.empty()) {
|
||||
Poco::toUpperInPlace(mac);
|
||||
Poco::replaceInPlace(mac,":","-");
|
||||
GWObjects::RADIUSSessionList L;
|
||||
RADIUSSessionTracker()->GetMACAPSessions(mac,L);
|
||||
return ReturnObject("sessions",L.sessions);
|
||||
}
|
||||
|
||||
auto SerialNumber = GetBinding("serialNumber","");
|
||||
if(SerialNumber.empty() || !Utils::ValidSerialNumber(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
GWObjects::RADIUSSessionList L;
|
||||
RADIUSSessionTracker()->GetAPSessions(SerialNumber,L);
|
||||
return ReturnObject("sessions",L.sessions);
|
||||
}
|
||||
|
||||
void RESTAPI_radiussessions_handler::DoPut() {
|
||||
auto SerialNumber = GetBinding("serialNumber","");
|
||||
if(SerialNumber.empty() || !Utils::ValidSerialNumber(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
GWObjects::RadiusCoADMParameters Parameters;
|
||||
if(!Parameters.from_json(ParsedBody_)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
auto Command = GetParameter("operation","");
|
||||
|
||||
if(Command=="coadm") {
|
||||
if(Parameters.callingStationId.empty() || Parameters.accountingSessionId.empty() || Parameters.accountingMultiSessionId.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
auto Index = Parameters.accountingSessionId + Parameters.accountingMultiSessionId;
|
||||
poco_information(Logger(), fmt::format("Disconnecting session {},{}", Parameters.accountingSessionId, Parameters.accountingMultiSessionId ));
|
||||
if(RADIUSSessionTracker()->SendCoADM(SerialNumber, Index)) {
|
||||
return OK();
|
||||
}
|
||||
return BadRequest(RESTAPI::Errors::CouldNotPerformCommand);
|
||||
}
|
||||
|
||||
if(Command=="disconnectUser" && !Parameters.userName.empty()) {
|
||||
if(Parameters.userName.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
poco_information(Logger(), fmt::format("Disconnecting sessions for user: {}", Parameters.userName ));
|
||||
if(RADIUSSessionTracker()->DisconnectUser(Parameters.userName)) {
|
||||
return OK();
|
||||
}
|
||||
return BadRequest(RESTAPI::Errors::CouldNotPerformCommand);
|
||||
}
|
||||
|
||||
return BadRequest(RESTAPI::Errors::InvalidCommand);
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
28
src/RESTAPI/RESTAPI_radiussessions_handler.h
Normal file
28
src/RESTAPI/RESTAPI_radiussessions_handler.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-02.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_radiussessions_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_radiussessions_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
|
||||
RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
|
||||
bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_PUT,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server, TransactionId, Internal){};
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/radiusSessions/{serialNumber}"}; };
|
||||
void DoGet() final;
|
||||
void DoDelete() final{};
|
||||
void DoPost() final{};
|
||||
void DoPut();
|
||||
};
|
||||
|
||||
} // namespace OpenWifi
|
||||
@@ -21,6 +21,9 @@
|
||||
#include "RESTAPI/RESTAPI_script_handler.h"
|
||||
#include "RESTAPI/RESTAPI_scripts_handler.h"
|
||||
#include "RESTAPI/RESTAPI_telemetryWebSocket.h"
|
||||
#include "RESTAPI/RESTAPI_radiussessions_handler.h"
|
||||
#include "RESTAPI/RESTAPI_default_firmware.h"
|
||||
#include "RESTAPI/RESTAPI_default_firmwares.h"
|
||||
|
||||
#include "framework/RESTAPI_SystemCommand.h"
|
||||
#include "framework/RESTAPI_SystemConfiguration.h"
|
||||
@@ -39,8 +42,9 @@ namespace OpenWifi {
|
||||
RESTAPI_system_configuration, RESTAPI_deviceDashboardHandler, RESTAPI_webSocketServer,
|
||||
RESTAPI_blacklist, RESTAPI_blacklist_list, RESTAPI_iptocountry_handler,
|
||||
RESTAPI_radiusProxyConfig_handler, RESTAPI_scripts_handler, RESTAPI_script_handler,
|
||||
RESTAPI_capabilities_handler, RESTAPI_telemetryWebSocket,
|
||||
RESTAPI_regulatory>(Path, Bindings, L, S,
|
||||
RESTAPI_capabilities_handler, RESTAPI_telemetryWebSocket, RESTAPI_radiussessions_handler,
|
||||
RESTAPI_regulatory, RESTAPI_default_firmwares,
|
||||
RESTAPI_default_firmware>(Path, Bindings, L, S,
|
||||
TransactionId);
|
||||
}
|
||||
|
||||
@@ -53,7 +57,8 @@ namespace OpenWifi {
|
||||
RESTAPI_default_configurations, RESTAPI_default_configuration, RESTAPI_command,
|
||||
RESTAPI_commands, RESTAPI_ouis, RESTAPI_file, RESTAPI_blacklist,
|
||||
RESTAPI_iptocountry_handler, RESTAPI_radiusProxyConfig_handler, RESTAPI_scripts_handler,
|
||||
RESTAPI_script_handler, RESTAPI_blacklist_list,
|
||||
RESTAPI_regulatory>(Path, Bindings, L, S, TransactionId);
|
||||
RESTAPI_script_handler, RESTAPI_blacklist_list, RESTAPI_radiussessions_handler,
|
||||
RESTAPI_regulatory, RESTAPI_default_firmwares,
|
||||
RESTAPI_default_firmware>(Path, Bindings, L, S, TransactionId);
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -13,6 +13,7 @@
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
#include "AP_WS_Server.h"
|
||||
#include "CapabilitiesCache.h"
|
||||
#include "RADIUSSessionTracker.h"
|
||||
#endif
|
||||
|
||||
#include "RESTAPI_GWobjects.h"
|
||||
@@ -29,6 +30,7 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj, "serialNumber", SerialNumber);
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
field_to_json(Obj, "deviceType", CapabilitiesCache::instance()->GetPlatform(Compatible));
|
||||
field_to_json(Obj, "hasRADIUSSessions", RADIUSSessionTracker()->HasSessions(SerialNumber));
|
||||
#endif
|
||||
field_to_json(Obj, "macAddress", MACAddress);
|
||||
field_to_json(Obj, "manufacturer", Manufacturer);
|
||||
@@ -55,6 +57,8 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj, "pendingConfigurationCmd", pendingConfigurationCmd);
|
||||
field_to_json(Obj, "restrictionDetails", restrictionDetails);
|
||||
field_to_json(Obj, "pendingUUID", pendingUUID);
|
||||
field_to_json(Obj, "simulated", simulated);
|
||||
field_to_json(Obj, "lastRecordedContact", lastRecordedContact);
|
||||
}
|
||||
|
||||
void Device::to_json_with_status(Poco::JSON::Object &Obj) const {
|
||||
@@ -64,7 +68,7 @@ namespace OpenWifi::GWObjects {
|
||||
ConnectionState ConState;
|
||||
|
||||
if (AP_WS_Server()->GetState(SerialNumber, ConState)) {
|
||||
ConState.to_json(Obj);
|
||||
ConState.to_json(SerialNumber,Obj);
|
||||
} else {
|
||||
field_to_json(Obj, "ipAddress", "");
|
||||
field_to_json(Obj, "txBytes", (uint64_t)0);
|
||||
@@ -76,6 +80,13 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj, "associations_2G", (uint64_t)0);
|
||||
field_to_json(Obj, "associations_5G", (uint64_t)0);
|
||||
field_to_json(Obj, "associations_6G", (uint64_t)0);
|
||||
field_to_json(Obj, "hasRADIUSSessions", false);
|
||||
field_to_json(Obj, "hasGPS", ConState.hasGPS);
|
||||
field_to_json(Obj, "sanity", ConState.sanity);
|
||||
field_to_json(Obj, "memoryUsed", ConState.memoryUsed);
|
||||
field_to_json(Obj, "sanity", ConState.sanity);
|
||||
field_to_json(Obj, "load", ConState.load);
|
||||
field_to_json(Obj, "temperature", ConState.temperature);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -109,6 +120,8 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj, "pendingConfigurationCmd", pendingConfigurationCmd);
|
||||
field_from_json(Obj, "restrictionDetails", restrictionDetails);
|
||||
field_from_json(Obj, "pendingUUID", pendingUUID);
|
||||
field_from_json(Obj, "simulated", simulated);
|
||||
field_from_json(Obj, "lastRecordedContact", lastRecordedContact);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
@@ -158,6 +171,31 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj, "lastModified", LastModified);
|
||||
}
|
||||
|
||||
void DefaultFirmware::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "deviceType", deviceType);
|
||||
field_to_json(Obj, "description", Description);
|
||||
field_to_json(Obj, "uri", uri);
|
||||
field_to_json(Obj, "revision", revision);
|
||||
field_to_json(Obj, "imageCreationDate", imageCreationDate);
|
||||
field_to_json(Obj, "created", Created);
|
||||
field_to_json(Obj, "lastModified", LastModified);
|
||||
}
|
||||
|
||||
bool DefaultFirmware::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "deviceType", deviceType);
|
||||
field_from_json(Obj, "description", Description);
|
||||
field_from_json(Obj, "uri", uri);
|
||||
field_from_json(Obj, "revision", revision);
|
||||
field_from_json(Obj, "imageCreationDate", imageCreationDate);
|
||||
field_from_json(Obj, "created", Created);
|
||||
field_from_json(Obj, "lastModified", LastModified);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
EmbedDocument("details", Obj, Details);
|
||||
EmbedDocument("results", Obj, Results);
|
||||
@@ -211,7 +249,7 @@ namespace OpenWifi::GWObjects {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConnectionState::to_json(Poco::JSON::Object &Obj) const {
|
||||
void ConnectionState::to_json([[maybe_unused]] const std::string &SerialNumber, Poco::JSON::Object &Obj) {
|
||||
field_to_json(Obj, "ipAddress", Address);
|
||||
field_to_json(Obj, "txBytes", TX);
|
||||
field_to_json(Obj, "rxBytes", RX);
|
||||
@@ -233,6 +271,22 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj, "connectionCompletionTime", connectionCompletionTime);
|
||||
field_to_json(Obj, "totalConnectionTime", Utils::Now() - started);
|
||||
field_to_json(Obj, "certificateExpiryDate", certificateExpiryDate);
|
||||
field_to_json(Obj, "connectReason", connectReason);
|
||||
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
hasRADIUSSessions = RADIUSSessionTracker()->HasSessions(SerialNumber);
|
||||
AP_WS_Server()->ExtendedAttributes(SerialNumber, hasGPS, sanity,
|
||||
memoryUsed,
|
||||
load,
|
||||
temperature);
|
||||
#endif
|
||||
field_to_json(Obj, "hasRADIUSSessions", hasRADIUSSessions );
|
||||
field_to_json(Obj, "hasGPS", hasGPS);
|
||||
field_to_json(Obj, "sanity", sanity);
|
||||
field_to_json(Obj, "memoryUsed", memoryUsed);
|
||||
field_to_json(Obj, "sanity", sanity);
|
||||
field_to_json(Obj, "load", load);
|
||||
field_to_json(Obj, "temperature", temperature);
|
||||
|
||||
switch (VerifiedCertificate) {
|
||||
case NO_CERTIFICATE:
|
||||
@@ -247,6 +301,9 @@ namespace OpenWifi::GWObjects {
|
||||
case VERIFIED:
|
||||
field_to_json(Obj, "verifiedCertificate", "VERIFIED");
|
||||
break;
|
||||
case SIMULATED:
|
||||
field_to_json(Obj, "verifiedCertificate", "SIMULATED");
|
||||
break;
|
||||
default:
|
||||
field_to_json(Obj, "verifiedCertificate", "NO_CERTIFICATE");
|
||||
break;
|
||||
@@ -373,6 +430,10 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj, "acctConfig", acctConfig);
|
||||
field_to_json(Obj, "coaConfig", coaConfig);
|
||||
field_to_json(Obj, "useByDefault", useByDefault);
|
||||
field_to_json(Obj, "radsecKeepAlive", radsecKeepAlive);
|
||||
field_to_json(Obj, "poolProxyIp", poolProxyIp);
|
||||
field_to_json(Obj, "radsecPoolType", radsecPoolType);
|
||||
field_to_json(Obj, "enabled", enabled);
|
||||
}
|
||||
|
||||
bool RadiusProxyPool::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
@@ -383,6 +444,10 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj, "acctConfig", acctConfig);
|
||||
field_from_json(Obj, "coaConfig", coaConfig);
|
||||
field_from_json(Obj, "useByDefault", useByDefault);
|
||||
field_from_json(Obj, "radsecKeepAlive", radsecKeepAlive);
|
||||
field_from_json(Obj, "poolProxyIp", poolProxyIp);
|
||||
field_from_json(Obj, "radsecPoolType", radsecPoolType);
|
||||
field_from_json(Obj, "enabled", enabled);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
@@ -580,4 +645,53 @@ namespace OpenWifi::GWObjects {
|
||||
(T.commands != commands) || (T.developer != developer) || (T.ssh != ssh) ||
|
||||
(T.key_info != key_info) || (T.country != country));
|
||||
}
|
||||
|
||||
void RADIUSSession::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "started", started);
|
||||
field_to_json(Obj, "lastTransaction", lastTransaction);
|
||||
field_to_json(Obj, "destination", destination);
|
||||
field_to_json(Obj, "serialNumber", serialNumber);
|
||||
field_to_json(Obj, "userName", userName);
|
||||
field_to_json(Obj, "accountingSessionId", accountingSessionId);
|
||||
field_to_json(Obj, "accountingMultiSessionId", accountingMultiSessionId);
|
||||
field_to_json(Obj, "inputPackets", inputPackets);
|
||||
field_to_json(Obj, "outputPackets", outputPackets);
|
||||
field_to_json(Obj, "inputOctets", inputOctets);
|
||||
field_to_json(Obj, "outputOctets", outputOctets);
|
||||
field_to_json(Obj, "inputGigaWords", inputGigaWords);
|
||||
field_to_json(Obj, "outputGigaWords", outputGigaWords);
|
||||
field_to_json(Obj, "sessionTime", sessionTime);
|
||||
field_to_json(Obj, "callingStationId", callingStationId);
|
||||
field_to_json(Obj, "chargeableUserIdentity", chargeableUserIdentity);
|
||||
field_to_json(Obj, "interface", interface);
|
||||
field_to_json(Obj, "secret", secret);
|
||||
field_to_json(Obj, "nasId", nasId);
|
||||
field_to_json(Obj, "calledStationId", calledStationId);
|
||||
}
|
||||
|
||||
void RADIUSSessionList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "sessions", sessions);
|
||||
}
|
||||
|
||||
void RadiusCoADMParameters::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "accountingSessionId", accountingSessionId);
|
||||
field_to_json(Obj, "accountingMultiSessionId", accountingMultiSessionId);
|
||||
field_to_json(Obj, "callingStationId", callingStationId);
|
||||
field_to_json(Obj, "chargeableUserIdentity", chargeableUserIdentity);
|
||||
field_to_json(Obj, "userName", userName);
|
||||
}
|
||||
|
||||
bool RadiusCoADMParameters::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "accountingSessionId", accountingSessionId);
|
||||
field_from_json(Obj, "accountingMultiSessionId", accountingMultiSessionId);
|
||||
field_from_json(Obj, "callingStationId", callingStationId);
|
||||
field_from_json(Obj, "chargeableUserIdentity", chargeableUserIdentity);
|
||||
field_from_json(Obj, "userName", userName);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace OpenWifi::GWObjects
|
||||
|
||||
@@ -11,9 +11,13 @@
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "RESTAPI_SecurityObjects.h"
|
||||
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
#include <RADIUS_helpers.h>
|
||||
#endif
|
||||
|
||||
namespace OpenWifi::GWObjects {
|
||||
|
||||
enum CertificateValidation { NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED };
|
||||
enum CertificateValidation { NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED, SIMULATED };
|
||||
|
||||
struct ConnectionState {
|
||||
uint64_t MessageCount = 0;
|
||||
@@ -38,8 +42,15 @@ namespace OpenWifi::GWObjects {
|
||||
uint64_t sessionId = 0;
|
||||
double connectionCompletionTime = 0.0;
|
||||
std::uint64_t certificateExpiryDate = 0;
|
||||
std::uint64_t hasRADIUSSessions = 0;
|
||||
bool hasGPS = false;
|
||||
std::uint64_t sanity=0;
|
||||
std::double_t memoryUsed=0.0;
|
||||
std::double_t load=0.0;
|
||||
std::double_t temperature=0.0;
|
||||
std::string connectReason;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
void to_json(const std::string &SerialNumber, Poco::JSON::Object &Obj) ;
|
||||
};
|
||||
|
||||
struct DeviceRestrictionsKeyInfo {
|
||||
@@ -97,6 +108,8 @@ namespace OpenWifi::GWObjects {
|
||||
std::string pendingConfigurationCmd;
|
||||
DeviceRestrictions restrictionDetails;
|
||||
std::uint64_t pendingUUID = 0;
|
||||
bool simulated=false;
|
||||
std::uint64_t lastRecordedContact=0;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
void to_json_with_status(Poco::JSON::Object &Obj) const;
|
||||
@@ -169,6 +182,26 @@ namespace OpenWifi::GWObjects {
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct DefaultFirmware {
|
||||
std::string deviceType;
|
||||
std::string Description;
|
||||
std::string uri;
|
||||
std::string revision;
|
||||
uint64_t imageCreationDate;
|
||||
uint64_t Created;
|
||||
uint64_t LastModified;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct DefaultFirmwareList {
|
||||
std::vector<DefaultFirmware> firmwares;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct CommandDetails {
|
||||
std::string UUID;
|
||||
std::string SerialNumber;
|
||||
@@ -327,6 +360,10 @@ namespace OpenWifi::GWObjects {
|
||||
RadiusProxyServerConfig acctConfig;
|
||||
RadiusProxyServerConfig coaConfig;
|
||||
bool useByDefault = false;
|
||||
std::string radsecPoolType;
|
||||
std::string poolProxyIp;
|
||||
std::uint64_t radsecKeepAlive=25;
|
||||
bool enabled=true;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
@@ -370,5 +407,49 @@ namespace OpenWifi::GWObjects {
|
||||
|
||||
using RegulatoryInfoCountryMap = std::map<std::string,RegulatoryCountryInfo>;
|
||||
|
||||
struct RADIUSSession {
|
||||
std::uint64_t started=0,
|
||||
lastTransaction=0;
|
||||
std::string serialNumber,
|
||||
destination,
|
||||
userName,
|
||||
accountingSessionId,
|
||||
accountingMultiSessionId,
|
||||
callingStationId,
|
||||
chargeableUserIdentity,
|
||||
secret,
|
||||
interface,
|
||||
nasId;
|
||||
std::uint64_t inputPackets = 0,
|
||||
outputPackets = 0,
|
||||
inputOctets = 0,
|
||||
outputOctets = 0,
|
||||
inputGigaWords = 0,
|
||||
outputGigaWords = 0;
|
||||
std::uint32_t sessionTime = 0;
|
||||
std::string calledStationId;
|
||||
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
RADIUS::RadiusPacket accountingPacket;
|
||||
#endif
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct RADIUSSessionList {
|
||||
std::vector<RADIUSSession> sessions;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct RadiusCoADMParameters {
|
||||
std::string accountingSessionId,
|
||||
accountingMultiSessionId,
|
||||
callingStationId,
|
||||
chargeableUserIdentity,
|
||||
userName;
|
||||
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
} // namespace OpenWifi::GWObjects
|
||||
|
||||
@@ -119,7 +119,8 @@ namespace OpenWifi {
|
||||
|
||||
bool CreateDevice(GWObjects::Device &);
|
||||
bool CreateDefaultDevice(std::string &SerialNumber, const Config::Capabilities &Caps,
|
||||
std::string &Firmware, const Poco::Net::IPAddress &IPAddress);
|
||||
std::string &Firmware, const Poco::Net::IPAddress &IPAddress,
|
||||
bool simulated);
|
||||
|
||||
bool GetDevice(std::string &SerialNumber, GWObjects::Device &);
|
||||
bool GetDevices(uint64_t From, uint64_t HowMany, std::vector<GWObjects::Device> &Devices,
|
||||
@@ -127,6 +128,9 @@ namespace OpenWifi {
|
||||
// bool GetDevices(uint64_t From, uint64_t HowMany, const std::string & Select,
|
||||
// std::vector<GWObjects::Device> &Devices, const std::string & orderBy="");
|
||||
bool DeleteDevice(std::string &SerialNumber);
|
||||
bool DeleteDevices(std::string &SerialPattern, bool SimulatedOnly);
|
||||
bool DeleteDevices(std::uint64_t OlderContact, bool SimulatedOnly);
|
||||
|
||||
bool UpdateDevice(GWObjects::Device &);
|
||||
bool DeviceExists(std::string &SerialNumber);
|
||||
bool SetConnectInfo(std::string &SerialNumber, std::string &Firmware);
|
||||
@@ -171,6 +175,19 @@ namespace OpenWifi {
|
||||
uint64_t GetDefaultConfigurationsCount();
|
||||
bool DefaultConfigurationAlreadyExists(std::string &Name);
|
||||
|
||||
bool UpdateDefaultFirmware(GWObjects::DefaultFirmware &DefFirmware);
|
||||
bool CreateDefaultFirmware(GWObjects::DefaultFirmware &DefConfig);
|
||||
bool DeleteDefaultFirmware(std::string &name);
|
||||
bool GetDefaultFirmware(std::string &name, GWObjects::DefaultFirmware &DefConfig);
|
||||
bool GetDefaultFirmwares(uint64_t From, uint64_t HowMany,
|
||||
std::vector<GWObjects::DefaultFirmware> &Devices);
|
||||
bool FindDefaultFirmwareForModel(const std::string &Model,
|
||||
GWObjects::DefaultFirmware &DefConfig);
|
||||
uint64_t GetDefaultFirmwaresCount();
|
||||
bool DefaultFirmwareAlreadyExists(std::string &Name);
|
||||
|
||||
|
||||
|
||||
bool AddCommand(std::string &SerialNumber, GWObjects::CommandDetails &Command,
|
||||
CommandExecutionType Type);
|
||||
bool GetCommands(std::string &SerialNumber, uint64_t FromDate, uint64_t ToDate,
|
||||
@@ -226,6 +243,8 @@ namespace OpenWifi {
|
||||
bool RemoveCommandListRecordsOlderThan(uint64_t Date);
|
||||
bool RemoveUploadedFilesRecordsOlderThan(uint64_t Date);
|
||||
|
||||
bool SetDeviceLastRecordedContact(std::string & SeialNumber, std::uint64_t lastRecordedContact);
|
||||
|
||||
int Create_Tables();
|
||||
int Create_Statistics();
|
||||
int Create_Devices();
|
||||
@@ -236,6 +255,7 @@ namespace OpenWifi {
|
||||
int Create_CommandList();
|
||||
int Create_BlackList();
|
||||
int Create_FileUploads();
|
||||
int Create_DefaultFirmwares();
|
||||
|
||||
bool AnalyzeCommands(Types::CountedMap &R);
|
||||
bool AnalyzeDevices(GWObjects::Dashboard &D);
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace OpenWifi::GWWebSocketNotifications {
|
||||
|
||||
struct SingleDevice {
|
||||
std::string serialNumber;
|
||||
inline void to_json(Poco::JSON::Object &Obj) const;
|
||||
inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct SingleDeviceConfigurationChange {
|
||||
@@ -20,15 +20,15 @@ namespace OpenWifi::GWWebSocketNotifications {
|
||||
uint64_t oldUUID;
|
||||
uint64_t newUUID;
|
||||
|
||||
inline void to_json(Poco::JSON::Object &Obj) const;
|
||||
inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct SingleDeviceFirmwareChange {
|
||||
std::string serialNumber;
|
||||
std::string newFirmware;
|
||||
inline void to_json(Poco::JSON::Object &Obj) const;
|
||||
inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct NumberOfConnection {
|
||||
@@ -38,8 +38,8 @@ namespace OpenWifi::GWWebSocketNotifications {
|
||||
std::uint64_t rx = 0;
|
||||
std::uint64_t tx = 0;
|
||||
|
||||
inline void to_json(Poco::JSON::Object &Obj) const;
|
||||
inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
void Register();
|
||||
|
||||
66
src/firmware_revision_cache.h
Normal file
66
src/firmware_revision_cache.h
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-07-12.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "Poco/ExpireLRUCache.h"
|
||||
#include "sdks/sdk_fms.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class FirmwareRevisionCache : public SubSystemServer {
|
||||
public:
|
||||
static auto instance() {
|
||||
static auto instance_ = new FirmwareRevisionCache;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
inline int Start() override {
|
||||
poco_notice(Logger(), "Starting...");
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void Stop() override {
|
||||
poco_notice(Logger(), "Stopping...");
|
||||
poco_notice(Logger(), "Stopped...");
|
||||
}
|
||||
|
||||
inline bool DeviceMustUpgrade([[maybe_unused]] std::string &deviceType,
|
||||
[[maybe_unused]] const std::string &firmware_string,
|
||||
[[maybe_unused]] GWObjects::DefaultFirmware &Firmware) {
|
||||
return false;
|
||||
if(StorageService()->GetDefaultFirmware(deviceType,Firmware)) {
|
||||
|
||||
std::string key{ deviceType + Firmware.revision };
|
||||
|
||||
if(!Cache_.has(key)) {
|
||||
FMSObjects::FirmwareAgeDetails FAD;
|
||||
if(SDK::FMS::GetFirmwareAge(deviceType,Firmware.revision,FAD,Logger())) {
|
||||
Cache_.add(key,FAD);
|
||||
} else {
|
||||
// if we cannot establish the age of the currently running firmware,
|
||||
// then we assume it is too old.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
auto FAD = Cache_.get(key);
|
||||
if(FAD->imageDate < Firmware.imageCreationDate) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
Poco::ExpireLRUCache<std::string, FMSObjects::FirmwareAgeDetails> Cache_{
|
||||
512, 1200000};
|
||||
|
||||
FirmwareRevisionCache() noexcept
|
||||
: SubSystemServer("FirmwareRevisionCache", "FWCACHE-SVR", "firmwarecache") {
|
||||
}
|
||||
};
|
||||
|
||||
inline auto FirmwareRevisionCache() { return FirmwareRevisionCache::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
@@ -26,7 +26,7 @@ namespace OpenWifi {
|
||||
Response.set("Connection", "keep-alive");
|
||||
Response.setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
std::ostream &Answer = Response.send();
|
||||
Answer << "process Alive and kicking!";
|
||||
Answer << ALBHealthCheckServer()->CallbackText();
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace OpenWifi {
|
||||
inline static std::atomic_uint64_t req_id_ = 1;
|
||||
};
|
||||
|
||||
typedef std::string ALBHealthMessageCallback();
|
||||
|
||||
class ALBHealthCheckServer : public SubSystemServer {
|
||||
public:
|
||||
ALBHealthCheckServer();
|
||||
@@ -48,10 +50,22 @@ namespace OpenWifi {
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
inline void RegisterExtendedHealthMessage(ALBHealthMessageCallback *F) {
|
||||
Callback_=F;
|
||||
};
|
||||
|
||||
inline std::string CallbackText() {
|
||||
if(Callback_== nullptr) {
|
||||
return "process Alive and kicking!";
|
||||
} else {
|
||||
return Callback_();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Poco::Net::HTTPServer> Server_;
|
||||
std::unique_ptr<Poco::Net::ServerSocket> Socket_;
|
||||
ALBHealthMessageCallback *Callback_= nullptr;
|
||||
int Port_ = 0;
|
||||
mutable std::atomic_bool Running_ = false;
|
||||
};
|
||||
|
||||
@@ -34,9 +34,17 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"strict": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"uuid": {
|
||||
"type": "integer"
|
||||
},
|
||||
"public_ip_lookup": {
|
||||
"type": "string",
|
||||
"format": "uc-fqdn"
|
||||
},
|
||||
"unit": {
|
||||
"$ref": "#/$defs/unit"
|
||||
},
|
||||
@@ -110,6 +118,20 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"random-password": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"beacon-advertisement": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"device-name": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"device-serial": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"network-id": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -218,6 +240,52 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
}
|
||||
}
|
||||
},
|
||||
"interface.ssid.encryption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"proto": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"owe",
|
||||
"owe-transition",
|
||||
"psk",
|
||||
"psk2",
|
||||
"psk-mixed",
|
||||
"psk2-radius",
|
||||
"wpa",
|
||||
"wpa2",
|
||||
"wpa-mixed",
|
||||
"sae",
|
||||
"sae-mixed",
|
||||
"wpa3",
|
||||
"wpa3-192",
|
||||
"wpa3-mixed"
|
||||
],
|
||||
"examples": [
|
||||
"psk2"
|
||||
]
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"maxLength": 63,
|
||||
"minLength": 8
|
||||
},
|
||||
"ieee80211w": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"disabled",
|
||||
"optional",
|
||||
"required"
|
||||
],
|
||||
"default": "disabled"
|
||||
},
|
||||
"key-caching": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -634,26 +702,6 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"type": "string",
|
||||
"format": "uc-timeout",
|
||||
"default": "6h"
|
||||
},
|
||||
"relay-server": {
|
||||
"type": "string",
|
||||
"format": "ipv4",
|
||||
"example": "192.168.2.1"
|
||||
},
|
||||
"circuit-id-format": {
|
||||
"type": "string",
|
||||
"example": [
|
||||
"\\{Interface\\}:\\{VLAN-Id\\}:\\{SSID\\}:\\{Model\\}:\\{Name\\}:\\{AP-MAC\\}:\\{Location\\}",
|
||||
"\\{AP-MAC\\};\\{SSID\\};\\{Crypto\\}",
|
||||
"\\{Name\\} \\{ESSID\\}"
|
||||
]
|
||||
},
|
||||
"remote-id-format": {
|
||||
"type": "string",
|
||||
"example": [
|
||||
"\\{Client-MAC-hex\\} \\{SSID\\}",
|
||||
"\\{AP-MAC-hex\\} \\{SSID\\}"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -732,7 +780,8 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"dynamic",
|
||||
"static"
|
||||
"static",
|
||||
"none"
|
||||
],
|
||||
"examples": [
|
||||
"static"
|
||||
@@ -1022,52 +1071,6 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface.ssid.encryption": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"proto": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"owe",
|
||||
"owe-transition",
|
||||
"psk",
|
||||
"psk2",
|
||||
"psk-mixed",
|
||||
"psk2-radius",
|
||||
"wpa",
|
||||
"wpa2",
|
||||
"wpa-mixed",
|
||||
"sae",
|
||||
"sae-mixed",
|
||||
"wpa3",
|
||||
"wpa3-192",
|
||||
"wpa3-mixed"
|
||||
],
|
||||
"examples": [
|
||||
"psk2"
|
||||
]
|
||||
},
|
||||
"key": {
|
||||
"type": "string",
|
||||
"maxLength": 63,
|
||||
"minLength": 8
|
||||
},
|
||||
"ieee80211w": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"disabled",
|
||||
"optional",
|
||||
"required"
|
||||
],
|
||||
"default": "disabled"
|
||||
},
|
||||
"key-caching": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"interface.ssid.multi-psk": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1232,6 +1235,32 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"secondary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"examples": [
|
||||
1812
|
||||
]
|
||||
},
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"request-attribute": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -1309,6 +1338,25 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"value": "Example Operator"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"maximum": 255,
|
||||
"minimum": 1
|
||||
},
|
||||
"hex-value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"examples": [
|
||||
{
|
||||
"id": 32,
|
||||
"value": "0a0b0c0d"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1658,6 +1706,236 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.click": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "click-to-continue"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.radius": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "radius"
|
||||
},
|
||||
"auth-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"auth-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"auth-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"acct-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"acct-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-interval": {
|
||||
"type": "integer",
|
||||
"default": 600
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.credentials": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "credentials"
|
||||
},
|
||||
"credentials": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.uam": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "uam"
|
||||
},
|
||||
"uam-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 3990
|
||||
},
|
||||
"uam-secret": {
|
||||
"type": "string"
|
||||
},
|
||||
"uam-server": {
|
||||
"type": "string"
|
||||
},
|
||||
"nasid": {
|
||||
"type": "string"
|
||||
},
|
||||
"nasmac": {
|
||||
"type": "string"
|
||||
},
|
||||
"auth-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"auth-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"auth-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"acct-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"acct-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-interval": {
|
||||
"type": "integer",
|
||||
"default": 600
|
||||
},
|
||||
"ssid": {
|
||||
"type": "string"
|
||||
},
|
||||
"mac-format": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"aabbccddeeff",
|
||||
"aa-bb-cc-dd-ee-ff",
|
||||
"aa:bb:cc:dd:ee:ff",
|
||||
"AABBCCDDEEFF",
|
||||
"AA:BB:CC:DD:EE:FF",
|
||||
"AA-BB-CC-DD-EE-FF"
|
||||
]
|
||||
},
|
||||
"final-redirect-url": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"uam"
|
||||
]
|
||||
},
|
||||
"mac-auth": {
|
||||
"type": "boolean",
|
||||
"default": "default"
|
||||
},
|
||||
"radius-gw-proxy": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive": {
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.click"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.radius"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.credentials"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.uam"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"walled-garden-fqdn": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"walled-garden-ipaddr": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uc-ip"
|
||||
}
|
||||
},
|
||||
"web-root": {
|
||||
"type": "string",
|
||||
"format": "uc-base64"
|
||||
},
|
||||
"idle-timeout": {
|
||||
"type": "integer",
|
||||
"default": 600
|
||||
},
|
||||
"session-timeout": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface.ssid": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1710,6 +1988,10 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"isolate-clients": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"strict-forwarding": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"power-save": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -1757,6 +2039,11 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"decription": "This option allows embedding custom vendor specific IEs inside the beacons of a BSS in AP mode.",
|
||||
"type": "string"
|
||||
},
|
||||
"tip-information-element": {
|
||||
"decription": "The device will broadcast the TIP vendor IE inside its beacons if this option is enabled.",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"fils-discovery-interval": {
|
||||
"type": "integer",
|
||||
"default": 20,
|
||||
@@ -1778,7 +2065,14 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"$ref": "#/$defs/interface.ssid.rate-limit"
|
||||
},
|
||||
"roaming": {
|
||||
"$ref": "#/$defs/interface.ssid.roaming"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/$defs/interface.ssid.roaming"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
"radius": {
|
||||
"$ref": "#/$defs/interface.ssid.radius"
|
||||
@@ -1795,6 +2089,9 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"access-control-list": {
|
||||
"$ref": "#/$defs/interface.ssid.acl"
|
||||
},
|
||||
"captive": {
|
||||
"$ref": "#/$defs/service.captive"
|
||||
},
|
||||
"hostapd-bss-raw": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -1962,6 +2259,17 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
]
|
||||
}
|
||||
},
|
||||
"vlan-awareness": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"first": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vlan": {
|
||||
"$ref": "#/$defs/interface.vlan"
|
||||
},
|
||||
@@ -2084,6 +2392,10 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"examples": [
|
||||
"01234567890123456789012345678901"
|
||||
]
|
||||
},
|
||||
"mutual-tls": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2155,6 +2467,24 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"radius",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"port-filter": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
{
|
||||
"LAN1": null
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"server-certificate": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -2166,6 +2496,77 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
"items": {
|
||||
"$ref": "#/$defs/interface.ssid.radius.local-user"
|
||||
}
|
||||
},
|
||||
"radius": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"nas-identifier": {
|
||||
"type": "string"
|
||||
},
|
||||
"auth-server-addr": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"auth-server-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"examples": [
|
||||
1812
|
||||
]
|
||||
},
|
||||
"auth-server-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-server-addr": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"acct-server-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"examples": [
|
||||
1813
|
||||
]
|
||||
},
|
||||
"acct-server-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"coa-server-addr": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"coa-server-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"examples": [
|
||||
1814
|
||||
]
|
||||
},
|
||||
"coa-server-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2489,6 +2890,12 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"classifier": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -2693,236 +3100,6 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.click": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "click-to-continue"
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.radius": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "radius"
|
||||
},
|
||||
"auth-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"auth-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"auth-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"acct-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"acct-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-interval": {
|
||||
"type": "integer",
|
||||
"default": 600
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.credentials": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "credentials"
|
||||
},
|
||||
"credentials": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive.uam": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"auth-mode": {
|
||||
"type": "string",
|
||||
"const": "uam"
|
||||
},
|
||||
"uam-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 3990
|
||||
},
|
||||
"uam-secret": {
|
||||
"type": "string"
|
||||
},
|
||||
"uam-server": {
|
||||
"type": "string"
|
||||
},
|
||||
"nasid": {
|
||||
"type": "string"
|
||||
},
|
||||
"nasmac": {
|
||||
"type": "string"
|
||||
},
|
||||
"auth-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"auth-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"auth-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-server": {
|
||||
"type": "string",
|
||||
"format": "uc-host",
|
||||
"examples": [
|
||||
"192.168.1.10"
|
||||
]
|
||||
},
|
||||
"acct-port": {
|
||||
"type": "integer",
|
||||
"maximum": 65535,
|
||||
"minimum": 1024,
|
||||
"default": 1812
|
||||
},
|
||||
"acct-secret": {
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"secret"
|
||||
]
|
||||
},
|
||||
"acct-interval": {
|
||||
"type": "integer",
|
||||
"default": 600
|
||||
},
|
||||
"ssid": {
|
||||
"type": "string"
|
||||
},
|
||||
"mac-format": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"aabbccddeeff",
|
||||
"aa-bb-cc-dd-ee-ff",
|
||||
"aa:bb:cc:dd:ee:ff",
|
||||
"AABBCCDDEEFF",
|
||||
"AA:BB:CC:DD:EE:FF",
|
||||
"AA-BB-CC-DD-EE-FF"
|
||||
]
|
||||
},
|
||||
"final-redirect-url": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"default",
|
||||
"uam"
|
||||
]
|
||||
},
|
||||
"mac-auth": {
|
||||
"type": "boolean",
|
||||
"default": "default"
|
||||
},
|
||||
"radius-gw-proxy": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.captive": {
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.click"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.radius"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.credentials"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/service.captive.uam"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"walled-garden-fqdn": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"walled-garden-ipaddr": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uc-ip"
|
||||
}
|
||||
},
|
||||
"web-root": {
|
||||
"type": "string",
|
||||
"format": "uc-base64"
|
||||
},
|
||||
"idle-timeout": {
|
||||
"type": "integer",
|
||||
"default": 600
|
||||
},
|
||||
"session-timeout": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"service.gps": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -2941,6 +3118,50 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
}
|
||||
}
|
||||
},
|
||||
"service.dhcp-relay": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"select-ports": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"vlans": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vlan": {
|
||||
"type": "number"
|
||||
},
|
||||
"relay-server": {
|
||||
"type": "string",
|
||||
"format": "uc-ip"
|
||||
},
|
||||
"circuit-id-format": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"vlan-id",
|
||||
"ap-mac",
|
||||
"ssid"
|
||||
],
|
||||
"default": "vlan-id"
|
||||
},
|
||||
"remote-id-format": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"vlan-id",
|
||||
"ap-mac",
|
||||
"ssid"
|
||||
],
|
||||
"default": "ap-mac"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3000,6 +3221,9 @@ static std::string DefaultUCentralSchema = R"foo(
|
||||
},
|
||||
"gps": {
|
||||
"$ref": "#/$defs/service.gps"
|
||||
},
|
||||
"dhcp-relay": {
|
||||
"$ref": "#/$defs/service.dhcp-relay"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -14,18 +14,18 @@ namespace OpenWifi {
|
||||
void EventBusManager::run() {
|
||||
Running_ = true;
|
||||
Utils::SetThreadName("fmwk:EventMgr");
|
||||
auto Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN);
|
||||
auto Msg = (MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN));
|
||||
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroServicePrivateEndPoint(), Msg,
|
||||
false);
|
||||
while (Running_) {
|
||||
Poco::Thread::trySleep((unsigned long)MicroServiceDaemonBusTimer());
|
||||
if (!Running_)
|
||||
break;
|
||||
Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE);
|
||||
Msg = (MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE));
|
||||
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroServicePrivateEndPoint(),
|
||||
Msg, false);
|
||||
}
|
||||
Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE);
|
||||
Msg = (MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE));
|
||||
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroServicePrivateEndPoint(), Msg,
|
||||
false);
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "cppkafka/utils/consumer_dispatcher.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -99,9 +100,12 @@ namespace OpenWifi {
|
||||
try {
|
||||
auto Msg = dynamic_cast<KafkaMessage *>(Note.get());
|
||||
if (Msg != nullptr) {
|
||||
Producer.produce(cppkafka::MessageBuilder(Msg->Topic())
|
||||
.key(Msg->Key())
|
||||
.payload(Msg->Payload()));
|
||||
auto NewMessage = cppkafka::MessageBuilder(Msg->Topic());
|
||||
NewMessage.key(Msg->Key());
|
||||
NewMessage.partition(0);
|
||||
NewMessage.payload(Msg->Payload());
|
||||
Producer.produce(NewMessage);
|
||||
Producer.flush();
|
||||
}
|
||||
} catch (const cppkafka::HandleException &E) {
|
||||
poco_warning(Logger_,
|
||||
@@ -156,43 +160,49 @@ namespace OpenWifi {
|
||||
}
|
||||
});
|
||||
|
||||
bool AutoCommit = MicroServiceConfigGetBool("openwifi.kafka.auto.commit", false);
|
||||
auto BatchSize = MicroServiceConfigGetInt("openwifi.kafka.consumer.batchsize", 20);
|
||||
// bool AutoCommit = MicroServiceConfigGetBool("openwifi.kafka.auto.commit", false);
|
||||
// auto BatchSize = MicroServiceConfigGetInt("openwifi.kafka.consumer.batchsize", 100);
|
||||
|
||||
Types::StringVec Topics;
|
||||
KafkaManager()->Topics(Topics);
|
||||
std::for_each(Topics_.begin(),Topics_.end(),
|
||||
[&](const std::string & T) { Topics.emplace_back(T); });
|
||||
Consumer.subscribe(Topics);
|
||||
|
||||
Running_ = true;
|
||||
while (Running_) {
|
||||
try {
|
||||
std::vector<cppkafka::Message> MsgVec =
|
||||
Consumer.poll_batch(BatchSize, std::chrono::milliseconds(100));
|
||||
for (auto const &Msg : MsgVec) {
|
||||
if (!Msg)
|
||||
continue;
|
||||
if (Msg.get_error()) {
|
||||
if (!Msg.is_eof()) {
|
||||
poco_error(Logger_,
|
||||
fmt::format("Error: {}", Msg.get_error().to_string()));
|
||||
std::vector<cppkafka::Message> MsgVec;
|
||||
|
||||
Dispatcher_ = std::make_unique<cppkafka::ConsumerDispatcher>(Consumer);
|
||||
|
||||
Dispatcher_->run(
|
||||
// Callback executed whenever a new message is consumed
|
||||
[&](cppkafka::Message msg) {
|
||||
// Print the key (if any)
|
||||
std::lock_guard G(ConsumerMutex_);
|
||||
auto It = Notifiers_.find(msg.get_topic());
|
||||
if (It != Notifiers_.end()) {
|
||||
const auto &FL = It->second;
|
||||
for (const auto &[CallbackFunc, _] : FL) {
|
||||
try {
|
||||
CallbackFunc(msg.get_key(), msg.get_payload());
|
||||
} catch(const Poco::Exception &E) {
|
||||
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
if (!AutoCommit)
|
||||
Consumer.async_commit(Msg);
|
||||
continue;
|
||||
}
|
||||
KafkaManager()->Dispatch(Msg.get_topic(), Msg.get_key(), Msg.get_payload());
|
||||
if (!AutoCommit)
|
||||
Consumer.async_commit(Msg);
|
||||
}
|
||||
} catch (const cppkafka::HandleException &E) {
|
||||
poco_warning(Logger_,
|
||||
fmt::format("Caught a Kafka exception (consumer): {}", E.what()));
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_error(Logger_, "std::exception");
|
||||
Consumer.commit(msg);
|
||||
},
|
||||
// Whenever there's an error (other than the EOF soft error)
|
||||
[&Logger_](cppkafka::Error error) {
|
||||
poco_warning(Logger_,fmt::format("Error: {}", error.to_string()));
|
||||
},
|
||||
// Whenever EOF is reached on a partition, print this
|
||||
[&Logger_](cppkafka::ConsumerDispatcher::EndOfFile, const cppkafka::TopicPartition& topic_partition) {
|
||||
poco_debug(Logger_,fmt::format("Partition {} EOF", topic_partition.get_partition()));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Consumer.unsubscribe();
|
||||
poco_information(Logger_, "Stopped...");
|
||||
}
|
||||
@@ -212,7 +222,7 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaProducer::Produce(const std::string &Topic, const std::string &Key,
|
||||
void KafkaProducer::Produce(const char *Topic, const std::string &Key,
|
||||
const std::string &Payload) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Queue_.enqueueNotification(new KafkaMessage(Topic, Key, Payload));
|
||||
@@ -220,7 +230,6 @@ namespace OpenWifi {
|
||||
|
||||
void KafkaConsumer::Start() {
|
||||
if (!Running_) {
|
||||
Running_ = true;
|
||||
Worker_.start(*this);
|
||||
}
|
||||
}
|
||||
@@ -228,29 +237,16 @@ namespace OpenWifi {
|
||||
void KafkaConsumer::Stop() {
|
||||
if (Running_) {
|
||||
Running_ = false;
|
||||
Worker_.wakeUp();
|
||||
if(Dispatcher_) {
|
||||
Dispatcher_->stop();
|
||||
}
|
||||
Worker_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Start() {
|
||||
if (!Running_) {
|
||||
Running_ = true;
|
||||
Worker_.start(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Stop() {
|
||||
if (Running_) {
|
||||
Running_ = false;
|
||||
Queue_.wakeUpAll();
|
||||
Worker_.join();
|
||||
}
|
||||
}
|
||||
|
||||
auto KafkaDispatcher::RegisterTopicWatcher(const std::string &Topic,
|
||||
std::uint64_t KafkaConsumer::RegisterTopicWatcher(const std::string &Topic,
|
||||
Types::TopicNotifyFunction &F) {
|
||||
std::lock_guard G(Mutex_);
|
||||
std::lock_guard G(ConsumerMutex_);
|
||||
auto It = Notifiers_.find(Topic);
|
||||
if (It == Notifiers_.end()) {
|
||||
Types::TopicNotifyFunctionList L;
|
||||
@@ -259,11 +255,12 @@ namespace OpenWifi {
|
||||
} else {
|
||||
It->second.emplace(It->second.end(), std::make_pair(F, FunctionId_));
|
||||
}
|
||||
Topics_.insert(Topic);
|
||||
return FunctionId_++;
|
||||
}
|
||||
|
||||
void KafkaDispatcher::UnregisterTopicWatcher(const std::string &Topic, int Id) {
|
||||
std::lock_guard G(Mutex_);
|
||||
void KafkaConsumer::UnregisterTopicWatcher(const std::string &Topic, int Id) {
|
||||
std::lock_guard G(ConsumerMutex_);
|
||||
auto It = Notifiers_.find(Topic);
|
||||
if (It != Notifiers_.end()) {
|
||||
Types::TopicNotifyFunctionList &L = It->second;
|
||||
@@ -275,56 +272,17 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Dispatch(const std::string &Topic, const std::string &Key,
|
||||
const std::string &Payload) {
|
||||
std::lock_guard G(Mutex_);
|
||||
auto It = Notifiers_.find(Topic);
|
||||
if (It != Notifiers_.end()) {
|
||||
Queue_.enqueueNotification(new KafkaMessage(Topic, Key, Payload));
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::run() {
|
||||
Poco::Logger &Logger_ =
|
||||
Poco::Logger::create("KAFKA-DISPATCHER", KafkaManager()->Logger().getChannel());
|
||||
poco_information(Logger_, "Starting...");
|
||||
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
|
||||
Utils::SetThreadName("kafka:dispatch");
|
||||
while (Note && Running_) {
|
||||
auto Msg = dynamic_cast<KafkaMessage *>(Note.get());
|
||||
if (Msg != nullptr) {
|
||||
auto It = Notifiers_.find(Msg->Topic());
|
||||
if (It != Notifiers_.end()) {
|
||||
const auto &FL = It->second;
|
||||
for (const auto &[CallbackFunc, _] : FL) {
|
||||
CallbackFunc(Msg->Key(), Msg->Payload());
|
||||
}
|
||||
}
|
||||
}
|
||||
Note = Queue_.waitDequeueNotification();
|
||||
}
|
||||
poco_information(Logger_, "Stopped...");
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Topics(std::vector<std::string> &T) {
|
||||
T.clear();
|
||||
for (const auto &[TopicName, _] : Notifiers_)
|
||||
T.push_back(TopicName);
|
||||
}
|
||||
|
||||
int KafkaManager::Start() {
|
||||
if (!KafkaEnabled_)
|
||||
return 0;
|
||||
ConsumerThr_.Start();
|
||||
ProducerThr_.Start();
|
||||
Dispatcher_.Start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KafkaManager::Stop() {
|
||||
if (KafkaEnabled_) {
|
||||
poco_information(Logger(), "Stopping...");
|
||||
Dispatcher_.Stop();
|
||||
ProducerThr_.Stop();
|
||||
ConsumerThr_.Stop();
|
||||
poco_information(Logger(), "Stopped...");
|
||||
@@ -332,39 +290,29 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaManager::PostMessage(const std::string &topic, const std::string &key,
|
||||
const std::string &PayLoad, bool WrapMessage) {
|
||||
void KafkaManager::PostMessage(const char *topic, const std::string &key,
|
||||
const std::string & PayLoad, bool WrapMessage) {
|
||||
if (KafkaEnabled_) {
|
||||
ProducerThr_.Produce(topic, key, WrapMessage ? WrapSystemId(PayLoad) : PayLoad);
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaManager::Dispatch(const std::string &Topic, const std::string &Key,
|
||||
const std::string &Payload) {
|
||||
Dispatcher_.Dispatch(Topic, Key, Payload);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string KafkaManager::WrapSystemId(const std::string &PayLoad) {
|
||||
return SystemInfoWrapper_ + PayLoad + "}";
|
||||
}
|
||||
|
||||
uint64_t KafkaManager::RegisterTopicWatcher(const std::string &Topic,
|
||||
Types::TopicNotifyFunction &F) {
|
||||
void KafkaManager::PostMessage(const char *topic, const std::string &key,
|
||||
const Poco::JSON::Object &Object, bool WrapMessage) {
|
||||
if (KafkaEnabled_) {
|
||||
return Dispatcher_.RegisterTopicWatcher(Topic, F);
|
||||
} else {
|
||||
return 0;
|
||||
std::ostringstream ObjectStr;
|
||||
Object.stringify(ObjectStr);
|
||||
ProducerThr_.Produce(topic, key, WrapMessage ? WrapSystemId(ObjectStr.str()) : ObjectStr.str());
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, uint64_t Id) {
|
||||
if (KafkaEnabled_) {
|
||||
Dispatcher_.UnregisterTopicWatcher(Topic, Id);
|
||||
}
|
||||
[[nodiscard]] std::string KafkaManager::WrapSystemId(const std::string & PayLoad) {
|
||||
return fmt::format( R"lit({{ "system" : {{ "id" : {},
|
||||
"host" : "{}" }},
|
||||
"payload" : {} }})lit", MicroServiceID(),
|
||||
MicroServicePrivateEndPoint(), PayLoad ) ;
|
||||
}
|
||||
|
||||
void KafkaManager::Topics(std::vector<std::string> &T) { Dispatcher_.Topics(T); }
|
||||
|
||||
void KafkaManager::PartitionAssignment(const cppkafka::TopicPartitionList &partitions) {
|
||||
poco_information(
|
||||
Logger(), fmt::format("Partition assigned: {}...", partitions.front().get_partition()));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "Poco/Notification.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "framework/KafkaTopics.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
@@ -18,15 +18,15 @@ namespace OpenWifi {
|
||||
|
||||
class KafkaMessage : public Poco::Notification {
|
||||
public:
|
||||
KafkaMessage(const std::string &Topic, const std::string &Key, const std::string &Payload)
|
||||
KafkaMessage(const char * Topic, const std::string &Key, const std::string &Payload)
|
||||
: Topic_(Topic), Key_(Key), Payload_(Payload) {}
|
||||
|
||||
inline const std::string &Topic() { return Topic_; }
|
||||
inline const char * Topic() { return Topic_; }
|
||||
inline const std::string &Key() { return Key_; }
|
||||
inline const std::string &Payload() { return Payload_; }
|
||||
|
||||
private:
|
||||
std::string Topic_;
|
||||
const char *Topic_;
|
||||
std::string Key_;
|
||||
std::string Payload_;
|
||||
};
|
||||
@@ -36,10 +36,10 @@ namespace OpenWifi {
|
||||
void run() override;
|
||||
void Start();
|
||||
void Stop();
|
||||
void Produce(const std::string &Topic, const std::string &Key, const std::string &Payload);
|
||||
void Produce(const char *Topic, const std::string &Key, const std::string & Payload);
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
std::mutex Mutex_;
|
||||
Poco::Thread Worker_;
|
||||
mutable std::atomic_bool Running_ = false;
|
||||
Poco::NotificationQueue Queue_;
|
||||
@@ -47,33 +47,22 @@ namespace OpenWifi {
|
||||
|
||||
class KafkaConsumer : public Poco::Runnable {
|
||||
public:
|
||||
void run() override;
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Poco::Thread Worker_;
|
||||
std::mutex ConsumerMutex_;
|
||||
Types::NotifyTable Notifiers_;
|
||||
Poco::Thread Worker_;
|
||||
mutable std::atomic_bool Running_ = false;
|
||||
};
|
||||
uint64_t FunctionId_ = 1;
|
||||
std::unique_ptr<cppkafka::ConsumerDispatcher> Dispatcher_;
|
||||
std::set<std::string> Topics_;
|
||||
|
||||
class KafkaDispatcher : public Poco::Runnable {
|
||||
public:
|
||||
void Start();
|
||||
void Stop();
|
||||
auto RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F);
|
||||
void run() override;
|
||||
friend class KafkaManager;
|
||||
std::uint64_t RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F);
|
||||
void UnregisterTopicWatcher(const std::string &Topic, int Id);
|
||||
void Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload);
|
||||
void run() override;
|
||||
void Topics(std::vector<std::string> &T);
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Types::NotifyTable Notifiers_;
|
||||
Poco::Thread Worker_;
|
||||
mutable std::atomic_bool Running_ = false;
|
||||
uint64_t FunctionId_ = 1;
|
||||
Poco::NotificationQueue Queue_;
|
||||
};
|
||||
|
||||
class KafkaManager : public SubSystemServer {
|
||||
@@ -91,21 +80,25 @@ namespace OpenWifi {
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
|
||||
void PostMessage(const std::string &topic, const std::string &key,
|
||||
void PostMessage(const char *topic, const std::string &key,
|
||||
const std::string &PayLoad, bool WrapMessage = true);
|
||||
void Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload);
|
||||
[[nodiscard]] std::string WrapSystemId(const std::string &PayLoad);
|
||||
void PostMessage(const char *topic, const std::string &key,
|
||||
const Poco::JSON::Object &Object, bool WrapMessage = true);
|
||||
|
||||
[[nodiscard]] std::string WrapSystemId(const std::string & PayLoad);
|
||||
[[nodiscard]] inline bool Enabled() const { return KafkaEnabled_; }
|
||||
uint64_t RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F);
|
||||
void UnregisterTopicWatcher(const std::string &Topic, uint64_t Id);
|
||||
void Topics(std::vector<std::string> &T);
|
||||
inline std::uint64_t RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) {
|
||||
return ConsumerThr_.RegisterTopicWatcher(Topic,F);
|
||||
}
|
||||
inline void UnregisterTopicWatcher(const std::string &Topic, uint64_t Id) {
|
||||
return ConsumerThr_.UnregisterTopicWatcher(Topic,Id);
|
||||
}
|
||||
|
||||
private:
|
||||
bool KafkaEnabled_ = false;
|
||||
std::string SystemInfoWrapper_;
|
||||
KafkaProducer ProducerThr_;
|
||||
KafkaConsumer ConsumerThr_;
|
||||
KafkaDispatcher Dispatcher_;
|
||||
|
||||
void PartitionAssignment(const cppkafka::TopicPartitionList &partitions);
|
||||
void PartitionRevocation(const cppkafka::TopicPartitionList &partitions);
|
||||
|
||||
@@ -10,32 +10,32 @@
|
||||
|
||||
#include <string>
|
||||
namespace OpenWifi::KafkaTopics {
|
||||
static const std::string HEALTHCHECK{"healthcheck"};
|
||||
static const std::string STATE{"state"};
|
||||
static const std::string CONNECTION{"connection"};
|
||||
static const std::string WIFISCAN{"wifiscan"};
|
||||
static const std::string ALERTS{"alerts"};
|
||||
static const std::string COMMAND{"command"};
|
||||
static const std::string SERVICE_EVENTS{"service_events"};
|
||||
static const std::string DEVICE_EVENT_QUEUE{"device_event_queue"};
|
||||
static const std::string DEVICE_TELEMETRY{"device_telemetry"};
|
||||
static const std::string PROVISIONING_CHANGE{"provisioning_change"};
|
||||
inline const char * HEALTHCHECK = "healthcheck";
|
||||
inline const char * STATE = "state";
|
||||
inline const char * CONNECTION = "connection";
|
||||
inline const char * WIFISCAN = "wifiscan";
|
||||
inline const char * ALERTS = "alerts";
|
||||
inline const char * COMMAND = "command";
|
||||
inline const char * SERVICE_EVENTS = "service_events";
|
||||
inline const char * DEVICE_EVENT_QUEUE = "device_event_queue";
|
||||
inline const char * DEVICE_TELEMETRY = "device_telemetry";
|
||||
inline const char * PROVISIONING_CHANGE = "provisioning_change";
|
||||
|
||||
namespace ServiceEvents {
|
||||
static const std::string EVENT_JOIN{"join"};
|
||||
static const std::string EVENT_LEAVE{"leave"};
|
||||
static const std::string EVENT_KEEP_ALIVE{"keep-alive"};
|
||||
static const std::string EVENT_REMOVE_TOKEN{"remove-token"};
|
||||
inline const char * EVENT_JOIN = "join";
|
||||
inline const char * EVENT_LEAVE = "leave";
|
||||
inline const char * EVENT_KEEP_ALIVE = "keep-alive";
|
||||
inline const char * EVENT_REMOVE_TOKEN = "remove-token";
|
||||
|
||||
namespace Fields {
|
||||
static const std::string EVENT{"event"};
|
||||
static const std::string ID{"id"};
|
||||
static const std::string TYPE{"type"};
|
||||
static const std::string PUBLIC{"publicEndPoint"};
|
||||
static const std::string PRIVATE{"privateEndPoint"};
|
||||
static const std::string KEY{"key"};
|
||||
static const std::string VRSN{"version"};
|
||||
static const std::string TOKEN{"token"};
|
||||
inline const char * EVENT = "event";
|
||||
inline const char * ID = "id";
|
||||
inline const char * TYPE = "type";
|
||||
inline const char * PUBLIC = "publicEndPoint";
|
||||
inline const char * PRIVATE = "privateEndPoint";
|
||||
inline const char * KEY = "key";
|
||||
inline const char * VRSN = "version";
|
||||
inline const char * TOKEN = "token";
|
||||
} // namespace Fields
|
||||
} // namespace ServiceEvents
|
||||
} // namespace OpenWifi::KafkaTopics
|
||||
|
||||
@@ -129,6 +129,8 @@ namespace OpenWifi {
|
||||
}
|
||||
} else {
|
||||
poco_error(logger(), "Bad bus message.");
|
||||
std::ostringstream os;
|
||||
Object->stringify(std::cout);
|
||||
}
|
||||
|
||||
auto i = Services_.begin();
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/MicroService.h"
|
||||
|
||||
#include "framework/ALBserver.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
const std::string &MicroServiceDataDirectory() { return MicroService::instance().DataDir(); }
|
||||
|
||||
@@ -47,11 +49,11 @@ namespace OpenWifi {
|
||||
|
||||
void MicroServiceReload(const std::string &Type) { MicroService::instance().Reload(Type); }
|
||||
|
||||
const Types::StringVec MicroServiceGetLogLevelNames() {
|
||||
Types::StringVec MicroServiceGetLogLevelNames() {
|
||||
return MicroService::instance().GetLogLevelNames();
|
||||
}
|
||||
|
||||
const Types::StringVec MicroServiceGetSubSystems() {
|
||||
Types::StringVec MicroServiceGetSubSystems() {
|
||||
return MicroService::instance().GetSubSystems();
|
||||
}
|
||||
|
||||
@@ -79,7 +81,7 @@ namespace OpenWifi {
|
||||
|
||||
std::string MicroServiceGetUIURI() { return MicroService::instance().GetUIURI(); }
|
||||
|
||||
const SubSystemVec MicroServiceGetFullSubSystems() {
|
||||
SubSystemVec MicroServiceGetFullSubSystems() {
|
||||
return MicroService::instance().GetFullSubSystems();
|
||||
}
|
||||
|
||||
@@ -87,7 +89,7 @@ namespace OpenWifi {
|
||||
|
||||
std::uint64_t MicroServiceDaemonBusTimer() { return MicroService::instance().DaemonBusTimer(); }
|
||||
|
||||
std::string MicroServiceMakeSystemEventMessage(const std::string &Type) {
|
||||
std::string MicroServiceMakeSystemEventMessage(const char *Type) {
|
||||
return MicroService::instance().MakeSystemEventMessage(Type);
|
||||
}
|
||||
|
||||
@@ -123,4 +125,8 @@ namespace OpenWifi {
|
||||
return MicroService::instance().AllowExternalMicroServices();
|
||||
}
|
||||
|
||||
void MicroServiceALBCallback( std::string Callback()) {
|
||||
return ALBHealthCheckServer()->RegisterExtendedHealthMessage(Callback);
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -31,8 +31,8 @@ namespace OpenWifi {
|
||||
void MicroServiceLoadConfigurationFile();
|
||||
void MicroServiceReload();
|
||||
void MicroServiceReload(const std::string &Type);
|
||||
const Types::StringVec MicroServiceGetLogLevelNames();
|
||||
const Types::StringVec MicroServiceGetSubSystems();
|
||||
Types::StringVec MicroServiceGetLogLevelNames();
|
||||
Types::StringVec MicroServiceGetSubSystems();
|
||||
Types::StringPairVec MicroServiceGetLogLevels();
|
||||
bool MicroServiceSetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level);
|
||||
void MicroServiceGetExtraConfiguration(Poco::JSON::Object &Answer);
|
||||
@@ -40,10 +40,10 @@ namespace OpenWifi {
|
||||
std::uint64_t MicroServiceUptimeTotalSeconds();
|
||||
std::uint64_t MicroServiceStartTimeEpochTime();
|
||||
std::string MicroServiceGetUIURI();
|
||||
const SubSystemVec MicroServiceGetFullSubSystems();
|
||||
SubSystemVec MicroServiceGetFullSubSystems();
|
||||
std::string MicroServiceCreateUUID();
|
||||
std::uint64_t MicroServiceDaemonBusTimer();
|
||||
std::string MicroServiceMakeSystemEventMessage(const std::string &Type);
|
||||
std::string MicroServiceMakeSystemEventMessage(const char *Type);
|
||||
Poco::ThreadPool &MicroServiceTimerPool();
|
||||
std::string MicroServiceConfigPath(const std::string &Key, const std::string &DefaultValue);
|
||||
std::string MicroServiceWWWAssetsDir();
|
||||
@@ -53,4 +53,5 @@ namespace OpenWifi {
|
||||
std::string MicroServiceGetPublicAPIEndPoint();
|
||||
void MicroServiceDeleteOverrideConfiguration();
|
||||
bool AllowExternalMicroServices();
|
||||
void MicroServiceALBCallback( std::string Callback());
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -28,6 +28,9 @@ namespace OpenWifi::Types {
|
||||
typedef std::string UUID_t;
|
||||
typedef std::vector<UUID_t> UUIDvec_t;
|
||||
typedef std::map<std::string, std::map<uint32_t, uint64_t>> Counted3DMapSII;
|
||||
typedef std::vector<int64_t> IntList;
|
||||
typedef std::vector<uint64_t> UIntList;
|
||||
typedef std::vector<double> DoubleList;
|
||||
|
||||
struct MicroServiceMeta {
|
||||
uint64_t Id = 0;
|
||||
|
||||
@@ -574,7 +574,37 @@ namespace OpenWifi {
|
||||
Poco::JSON::Stringifier::stringify(Object, Answer);
|
||||
}
|
||||
|
||||
inline void ReturnRawJSON(const std::string &json_doc) {
|
||||
inline void ReturnObject(const std::vector<std::string> &Strings) {
|
||||
Poco::JSON::Array Arr;
|
||||
for(const auto &String:Strings) {
|
||||
Arr.add(String);
|
||||
}
|
||||
std::ostringstream os;
|
||||
Arr.stringify(os);
|
||||
return ReturnRawJSON(os.str());
|
||||
}
|
||||
|
||||
template<class T> void ReturnObject(const std::vector<T> &Objects) {
|
||||
Poco::JSON::Array Arr;
|
||||
for(const auto &Object:Objects) {
|
||||
Poco::JSON::Object O;
|
||||
Object.to_json(O);
|
||||
Arr.add(O);
|
||||
}
|
||||
std::ostringstream os;
|
||||
Arr.stringify(os);
|
||||
return ReturnRawJSON(os.str());
|
||||
}
|
||||
|
||||
template<class T> void ReturnObject(const T &Object) {
|
||||
Poco::JSON::Object O;
|
||||
Object.to_json(O);
|
||||
std::ostringstream os;
|
||||
O.stringify(os);
|
||||
return ReturnRawJSON(os.str());
|
||||
}
|
||||
|
||||
inline void ReturnRawJSON(const std::string &json_doc) {
|
||||
PrepareResponse();
|
||||
if (Request != nullptr) {
|
||||
// can we compress ???
|
||||
|
||||
@@ -24,50 +24,63 @@ namespace OpenWifi {
|
||||
Server, TransactionId, Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/system"}; }
|
||||
|
||||
inline void DoGet() {
|
||||
inline void DoGet() final {
|
||||
std::string Arg;
|
||||
if (HasParameter("command", Arg) && Arg == "info") {
|
||||
Poco::JSON::Object Answer;
|
||||
Answer.set(RESTAPI::Protocol::VERSION, MicroServiceVersion());
|
||||
Answer.set(RESTAPI::Protocol::UPTIME, MicroServiceUptimeTotalSeconds());
|
||||
Answer.set(RESTAPI::Protocol::START, MicroServiceStartTimeEpochTime());
|
||||
Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName());
|
||||
Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount());
|
||||
Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName());
|
||||
Answer.set(RESTAPI::Protocol::UI, MicroServiceGetUIURI());
|
||||
if (HasParameter("command", Arg)) {
|
||||
if (Arg == "info") {
|
||||
Poco::JSON::Object Answer;
|
||||
Answer.set(RESTAPI::Protocol::VERSION, MicroServiceVersion());
|
||||
Answer.set(RESTAPI::Protocol::UPTIME, MicroServiceUptimeTotalSeconds());
|
||||
Answer.set(RESTAPI::Protocol::START, MicroServiceStartTimeEpochTime());
|
||||
Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName());
|
||||
Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount());
|
||||
Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName());
|
||||
Answer.set(RESTAPI::Protocol::UI, MicroServiceGetUIURI());
|
||||
|
||||
Poco::JSON::Array Certificates;
|
||||
auto SubSystems = MicroServiceGetFullSubSystems();
|
||||
std::set<std::string> CertNames;
|
||||
Poco::JSON::Array Certificates;
|
||||
auto SubSystems = MicroServiceGetFullSubSystems();
|
||||
std::set<std::string> CertNames;
|
||||
|
||||
for (const auto &i : SubSystems) {
|
||||
auto Hosts = i->HostSize();
|
||||
for (uint64_t j = 0; j < Hosts; ++j) {
|
||||
auto CertFileName = i->Host(j).CertFile();
|
||||
if (!CertFileName.empty()) {
|
||||
Poco::File F1(CertFileName);
|
||||
if (F1.exists()) {
|
||||
auto InsertResult = CertNames.insert(CertFileName);
|
||||
if (InsertResult.second) {
|
||||
Poco::JSON::Object Inner;
|
||||
Poco::Path F(CertFileName);
|
||||
Inner.set("filename", F.getFileName());
|
||||
Poco::Crypto::X509Certificate C(CertFileName);
|
||||
auto ExpiresOn = C.expiresOn();
|
||||
Inner.set("expiresOn", ExpiresOn.timestamp().epochTime());
|
||||
Certificates.add(Inner);
|
||||
for (const auto &i : SubSystems) {
|
||||
auto Hosts = i->HostSize();
|
||||
for (uint64_t j = 0; j < Hosts; ++j) {
|
||||
auto CertFileName = i->Host(j).CertFile();
|
||||
if (!CertFileName.empty()) {
|
||||
Poco::File F1(CertFileName);
|
||||
if (F1.exists()) {
|
||||
auto InsertResult = CertNames.insert(CertFileName);
|
||||
if (InsertResult.second) {
|
||||
Poco::JSON::Object Inner;
|
||||
Poco::Path F(CertFileName);
|
||||
Inner.set("filename", F.getFileName());
|
||||
Poco::Crypto::X509Certificate C(CertFileName);
|
||||
auto ExpiresOn = C.expiresOn();
|
||||
Inner.set("expiresOn", ExpiresOn.timestamp().epochTime());
|
||||
Certificates.add(Inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Answer.set("certificates", Certificates);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
if (Arg == "extraConfiguration") {
|
||||
Poco::JSON::Object Answer;
|
||||
MicroServiceGetExtraConfiguration(Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
if (Arg == "resources") {
|
||||
Poco::JSON::Object Answer;
|
||||
Answer.set("numberOfFileDescriptors", Utils::get_open_fds());
|
||||
std::uint64_t currRealMem, peakRealMem, currVirtMem, peakVirtMem;
|
||||
Utils::getMemory(currRealMem, peakRealMem, currVirtMem, peakVirtMem);
|
||||
Answer.set("currRealMem", currRealMem);
|
||||
Answer.set("peakRealMem", peakRealMem);
|
||||
Answer.set("currVirtMem", currVirtMem);
|
||||
Answer.set("peakVirtMem", peakVirtMem);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
Answer.set("certificates", Certificates);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
if (GetBoolParameter("extraConfiguration")) {
|
||||
Poco::JSON::Object Answer;
|
||||
MicroServiceGetExtraConfiguration(Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
BadRequest(RESTAPI::Errors::InvalidCommand);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,15 @@
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
#include <RESTObjects/RESTAPI_SecurityObjects.h>
|
||||
|
||||
namespace OpenWifi::RESTAPI_utils {
|
||||
|
||||
inline bool IsRootOrAdmin(const SecurityObjects::UserInfo &UI) {
|
||||
return UI.userRole==SecurityObjects::ROOT ||
|
||||
UI.userRole==SecurityObjects::ADMIN;
|
||||
}
|
||||
|
||||
inline void EmbedDocument(const std::string &ObjName, Poco::JSON::Object &Obj,
|
||||
const std::string &ObjStr) {
|
||||
std::string D = ObjStr.empty() ? "{}" : ObjStr;
|
||||
@@ -95,6 +102,20 @@ namespace OpenWifi::RESTAPI_utils {
|
||||
Obj.set(Field, A);
|
||||
}
|
||||
|
||||
inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::DoubleList &V) {
|
||||
Poco::JSON::Array A;
|
||||
for (const auto &i : V)
|
||||
A.add(i);
|
||||
Obj.set(Field, A);
|
||||
}
|
||||
|
||||
inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::IntList &V) {
|
||||
Poco::JSON::Array A;
|
||||
for (const auto &i : V)
|
||||
A.add(i);
|
||||
Obj.set(Field, A);
|
||||
}
|
||||
|
||||
inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::TagList &V) {
|
||||
Poco::JSON::Array A;
|
||||
for (const auto &i : V)
|
||||
@@ -277,6 +298,28 @@ namespace OpenWifi::RESTAPI_utils {
|
||||
}
|
||||
}
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field,
|
||||
Types::DoubleList &Value) {
|
||||
if (Obj->isArray(Field) && !Obj->isNull(Field)) {
|
||||
Value.clear();
|
||||
Poco::JSON::Array::Ptr A = Obj->getArray(Field);
|
||||
for (const auto &i : *A) {
|
||||
Value.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field,
|
||||
Types::IntList &Value) {
|
||||
if (Obj->isArray(Field) && !Obj->isNull(Field)) {
|
||||
Value.clear();
|
||||
Poco::JSON::Array::Ptr A = Obj->getArray(Field);
|
||||
for (const auto &i : *A) {
|
||||
Value.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field,
|
||||
std::vector<T> &Value) {
|
||||
|
||||
@@ -22,9 +22,8 @@ namespace OpenWifi {
|
||||
|
||||
class StorageClass : public SubSystemServer {
|
||||
public:
|
||||
StorageClass() noexcept : SubSystemServer("StorageClass", "STORAGE-SVR", "storage") {}
|
||||
|
||||
int Start() override {
|
||||
inline int Start() override {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
Logger().notice("Starting.");
|
||||
@@ -40,17 +39,22 @@ namespace OpenWifi {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Stop() override { Pool_->shutdown(); }
|
||||
inline void Stop() override { Pool_->shutdown(); }
|
||||
|
||||
DBType Type() const { return dbType_; };
|
||||
|
||||
StorageClass() noexcept : SubSystemServer("StorageClass", "STORAGE-SVR", "storage") {
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
inline int Setup_SQLite();
|
||||
inline int Setup_MySQL();
|
||||
inline int Setup_PostgreSQL();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Poco::Data::SessionPool> Pool_;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Poco::Data::SessionPool> Pool_;
|
||||
Poco::Data::SQLite::Connector SQLiteConn_;
|
||||
Poco::Data::PostgreSQL::Connector PostgresConn_;
|
||||
Poco::Data::MySQL::Connector MySQLConn_;
|
||||
@@ -81,7 +85,7 @@ namespace OpenWifi {
|
||||
// Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 8,
|
||||
// (int)NumSessions,
|
||||
// (int)IdleTime));
|
||||
Pool_ = std::make_unique<Poco::Data::SessionPool>(SQLiteConn_.name(), DBName, 8,
|
||||
Pool_ = std::make_shared<Poco::Data::SessionPool>(SQLiteConn_.name(), DBName, 8,
|
||||
(int)NumSessions, (int)IdleTime);
|
||||
return 0;
|
||||
}
|
||||
@@ -102,7 +106,7 @@ namespace OpenWifi {
|
||||
";compress=true;auto-reconnect=true";
|
||||
|
||||
Poco::Data::MySQL::Connector::registerConnector();
|
||||
Pool_ = std::make_unique<Poco::Data::SessionPool>(MySQLConn_.name(), ConnectionStr, 8,
|
||||
Pool_ = std::make_shared<Poco::Data::SessionPool>(MySQLConn_.name(), ConnectionStr, 8,
|
||||
NumSessions, IdleTime);
|
||||
|
||||
return 0;
|
||||
@@ -126,7 +130,7 @@ namespace OpenWifi {
|
||||
" connect_timeout=" + ConnectionTimeout;
|
||||
|
||||
Poco::Data::PostgreSQL::Connector::registerConnector();
|
||||
Pool_ = std::make_unique<Poco::Data::SessionPool>(PostgresConn_.name(), ConnectionStr, 8,
|
||||
Pool_ = std::make_shared<Poco::Data::SessionPool>(PostgresConn_.name(), ConnectionStr, 8,
|
||||
NumSessions, IdleTime);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace OpenWifi {
|
||||
P.cipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
|
||||
P.dhUse2048Bits = true;
|
||||
P.caLocation = cas_;
|
||||
// P.securityLevel =
|
||||
|
||||
auto Context = Poco::AutoPtr<Poco::Net::Context>(
|
||||
new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P));
|
||||
@@ -53,7 +54,6 @@ namespace OpenWifi {
|
||||
|
||||
Context->useCertificate(Cert);
|
||||
Context->addChainCertificate(Root);
|
||||
|
||||
Context->addCertificateAuthority(Root);
|
||||
|
||||
if (level_ == Poco::Net::Context::VERIFY_STRICT) {
|
||||
@@ -76,18 +76,18 @@ namespace OpenWifi {
|
||||
L.fatal(fmt::format("Wrong Certificate({}) for Key({})", cert_file_, key_file_));
|
||||
}
|
||||
|
||||
SSL_CTX_set_verify(SSLCtx, SSL_VERIFY_PEER, nullptr);
|
||||
SSL_CTX_set_verify(SSLCtx, level_==Poco::Net::Context::VERIFY_NONE ? SSL_VERIFY_NONE : SSL_VERIFY_PEER, nullptr);
|
||||
|
||||
if (level_ == Poco::Net::Context::VERIFY_STRICT) {
|
||||
SSL_CTX_set_client_CA_list(SSLCtx, SSL_load_client_CA_file(client_cas_.c_str()));
|
||||
SSL_CTX_enable_ct(SSLCtx, SSL_CT_VALIDATION_STRICT);
|
||||
}
|
||||
SSL_CTX_enable_ct(SSLCtx, SSL_CT_VALIDATION_STRICT);
|
||||
SSL_CTX_dane_enable(SSLCtx);
|
||||
|
||||
Context->enableSessionCache();
|
||||
Context->setSessionCacheSize(0);
|
||||
Context->setSessionTimeout(60);
|
||||
Context->enableExtendedCertificateVerification(true);
|
||||
Context->enableExtendedCertificateVerification( level_!= Poco::Net::Context::VERIFY_NONE );
|
||||
Context->disableStatelessSessionResumption();
|
||||
}
|
||||
|
||||
|
||||
63
src/framework/default_device_types.h
Normal file
63
src/framework/default_device_types.h
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-19.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace OpenWifi {
|
||||
inline const std::vector<std::pair<std::string, std::string>> DefaultDeviceTypeList{
|
||||
{"actiontec_web7200", "AP"},
|
||||
{"cig_wf186w", "AP"},
|
||||
{"cig_wf188n", "AP"},
|
||||
{"cig_wf194c4", "AP"},
|
||||
{"cig_wf196", "AP"},
|
||||
{"cig_wf196-ca", "AP"},
|
||||
{"cig_wf196-ca-ath12", "AP"},
|
||||
{"cig_wf196-us", "AP"},
|
||||
{"cig_wf610d", "AP"},
|
||||
{"cig_wf660a", "AP"},
|
||||
{"cig_wf808", "AP"},
|
||||
{"cybertan_eww622-a1", "AP"},
|
||||
{"edgecore_eap101", "AP"},
|
||||
{"edgecore_eap101-ath12", "AP"},
|
||||
{"edgecore_eap102", "AP"},
|
||||
{"edgecore_eap104", "AP"},
|
||||
{"edgecore_eap104-ath12", "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"},
|
||||
{"hfcl_ion4x", "AP"},
|
||||
{"hfcl_ion4x_2", "AP"},
|
||||
{"hfcl_ion4xe", "AP"},
|
||||
{"hfcl_ion4xi", "AP"},
|
||||
{"indio_um-305ac", "AP"},
|
||||
{"indio_um-305ax", "AP"},
|
||||
{"indio_um-310ax-v1", "AP"},
|
||||
{"indio_um-325ac", "AP"},
|
||||
{"indio_um-510ac-v3", "AP"},
|
||||
{"indio_um-510axm-v1", "AP"},
|
||||
{"indio_um-510axp-v1", "AP"},
|
||||
{"indio_um-550ac", "AP"},
|
||||
{"linksys_e8450-ubi", "AP"},
|
||||
{"linksys_ea6350-v4", "AP"},
|
||||
{"linksys_ea8300", "AP"},
|
||||
{"liteon_wpx8324", "AP"},
|
||||
{"meshpp_s618_cp01", "AP"},
|
||||
{"meshpp_s618_cp03", "AP"},
|
||||
{"udaya_a5-id2", "AP"},
|
||||
{"wallys_dr40x9", "AP"},
|
||||
{"wallys_dr6018", "AP"},
|
||||
{"wallys_dr6018_v4", "AP"},
|
||||
{"x64_vm", "AP"},
|
||||
{"yuncore_ax840", "AP"},
|
||||
{"yuncore_fap640", "AP"},
|
||||
{"yuncore_fap650", "AP"}};
|
||||
}
|
||||
@@ -40,6 +40,7 @@ namespace OpenWifi {
|
||||
};
|
||||
}
|
||||
|
||||
#define DBGLINE std::cout << __LINE__ << ":" << __FILE__ << ", " << __func__ << std::endl;
|
||||
namespace OpenWifi::RESTAPI::Errors {
|
||||
struct msg {
|
||||
uint64_t err_num;
|
||||
@@ -397,6 +398,35 @@ namespace OpenWifi::RESTAPI::Errors {
|
||||
static const struct msg FirmwareBDInProgress {
|
||||
1170, "Firmware DB update already in progress."
|
||||
};
|
||||
static const struct msg SimulatedDeviceNotSupported {
|
||||
1171, "Command not supported on simulated device."
|
||||
};
|
||||
|
||||
static const struct msg VenuesNameAlreadyExists {
|
||||
1172, "The venue name already exists."
|
||||
};
|
||||
|
||||
static const struct msg InvalidGlobalReachAccount {
|
||||
1173, "Invalid Global Reach account information."
|
||||
};
|
||||
static const struct msg CannotCreateCSR {
|
||||
1174, "Cannot create a CSR certificate."
|
||||
};
|
||||
|
||||
static const struct msg DefFirmwareNameExists { 1175, "Firmware name already exists." };
|
||||
|
||||
static const struct msg NotAValidECKey { 1176, "Not a valid Signing Key." };
|
||||
|
||||
static const struct msg NotAValidRadiusPoolType { 1177, "Not a valid RADIUS pool type." };
|
||||
|
||||
static const struct msg SimulationDoesNotExist {
|
||||
7000, "Simulation Instance ID does not exist."
|
||||
};
|
||||
|
||||
static const struct msg SimulationIsAlreadyRunning {
|
||||
7001, "There is an instance of this simulation already running.."
|
||||
};
|
||||
|
||||
|
||||
} // namespace OpenWifi::RESTAPI::Errors
|
||||
|
||||
@@ -554,6 +584,7 @@ namespace OpenWifi::uCentralProtocol {
|
||||
static const char *HEALTHCHECK = "healthcheck";
|
||||
static const char *LOG = "log";
|
||||
static const char *CRASHLOG = "crashlog";
|
||||
static const char *REBOOTLOG = "rebootLog";
|
||||
static const char *PING = "ping";
|
||||
static const char *CFGPENDING = "cfgpending";
|
||||
static const char *RECOVERY = "recovery";
|
||||
@@ -612,6 +643,8 @@ namespace OpenWifi::uCentralProtocol {
|
||||
static const char *DEVICEUPDATE = "deviceupdate";
|
||||
static const char *FWSIGNATURE = "FWsignature";
|
||||
static const char *SIGNATURE = "signature";
|
||||
static const char *INFO = "info";
|
||||
static const char *DATE = "date";
|
||||
|
||||
static const char *SERIALNUMBER = "serialNumber";
|
||||
static const char *COMPATIBLE = "compatible";
|
||||
@@ -642,6 +675,7 @@ namespace OpenWifi::uCentralProtocol::Events {
|
||||
static const char *HEALTHCHECK = "healthcheck";
|
||||
static const char *LOG = "log";
|
||||
static const char *CRASHLOG = "crashlog";
|
||||
static const char *REBOOTLOG = "rebootLog";
|
||||
static const char *PING = "ping";
|
||||
static const char *CFGPENDING = "cfgpending";
|
||||
static const char *RECOVERY = "recovery";
|
||||
@@ -665,7 +699,8 @@ namespace OpenWifi::uCentralProtocol::Events {
|
||||
ET_VENUEBROADCAST,
|
||||
ET_EVENT,
|
||||
ET_WIFISCAN,
|
||||
ET_ALARM
|
||||
ET_ALARM,
|
||||
ET_REBOOTLOG
|
||||
};
|
||||
|
||||
inline EVENT_MSG EventFromString(const std::string &Method) {
|
||||
@@ -696,8 +731,10 @@ namespace OpenWifi::uCentralProtocol::Events {
|
||||
else if (strcmp(WIFISCAN, Method.c_str()) == 0)
|
||||
return ET_WIFISCAN;
|
||||
else if (strcmp(ALARM, Method.c_str()) == 0)
|
||||
return ET_WIFISCAN;
|
||||
return ET_ALARM;
|
||||
return ET_ALARM;
|
||||
else if (strcmp(REBOOTLOG, Method.c_str()) == 0)
|
||||
return ET_REBOOTLOG;
|
||||
return ET_UNKNOWN;
|
||||
};
|
||||
} // namespace OpenWifi::uCentralProtocol::Events
|
||||
|
||||
|
||||
@@ -3,10 +3,17 @@
|
||||
//
|
||||
|
||||
#include "Poco/Path.h"
|
||||
|
||||
#include "Poco/TemporaryFile.h"
|
||||
#include "Poco/Crypto/ECKey.h"
|
||||
#include "framework/AppServiceRegistry.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
namespace OpenWifi::Utils {
|
||||
|
||||
bool NormalizeMac(std::string &Mac) {
|
||||
@@ -27,6 +34,10 @@ namespace OpenWifi::Utils {
|
||||
std::all_of(Serial.begin(), Serial.end(), [](auto i) { return std::isxdigit(i); }));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ValidSerialNumbers(const std::vector<std::string> &numbers) {
|
||||
return std::all_of(numbers.begin(),numbers.end(),[](auto &number) {return ValidSerialNumber(number);});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ValidUUID(const std::string &UUID) {
|
||||
if (UUID.size() > 36)
|
||||
return false;
|
||||
@@ -128,6 +139,15 @@ namespace OpenWifi::Utils {
|
||||
return std::regex_match(Hostname, HostNameRegex);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ValidNumber(const std::string &number, bool isSigned)
|
||||
{
|
||||
static std::regex IntRegex("^-?[0-9]\\d*(\\.\\d+)?$");
|
||||
if(!isSigned) {
|
||||
IntRegex = "^[0-9]\\d*(\\.\\d+)?$";
|
||||
}
|
||||
return std::regex_match(number, IntRegex);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string ToHex(const std::vector<unsigned char> &B) {
|
||||
std::string R;
|
||||
R.reserve(B.size() * 2);
|
||||
@@ -595,4 +615,251 @@ namespace OpenWifi::Utils {
|
||||
return DT.timestamp().epochTime();
|
||||
}
|
||||
|
||||
static std::string FileToString(const std::string &Filename) {
|
||||
std::ifstream ifs(Filename.c_str(),std::ios_base::in|std::ios_base::binary);
|
||||
std::ostringstream os;
|
||||
Poco::StreamCopier::copyStream(ifs,os);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
bool CreateX509CSR(const CSRCreationParameters & Parameters, CSRCreationResults & Results) {
|
||||
int ret = 0;
|
||||
RSA *r = nullptr;
|
||||
BIGNUM *bne = nullptr;
|
||||
|
||||
int nVersion = 0;
|
||||
unsigned long e = RSA_F4;
|
||||
|
||||
X509_REQ *x509_req = nullptr;
|
||||
X509_NAME *x509_name = nullptr;
|
||||
EVP_PKEY *pKey = nullptr;
|
||||
// RSA *tem = nullptr;
|
||||
// BIO *bio_err = nullptr;
|
||||
|
||||
const char *szCountry = Parameters.Country.c_str();
|
||||
const char *szProvince = Parameters.Province.c_str();
|
||||
const char *szCity = Parameters.City.c_str();
|
||||
const char *szOrganization = Parameters.Organization.c_str();
|
||||
const char *szCommon = Parameters.CommonName.c_str();
|
||||
|
||||
Poco::TemporaryFile CsrPath, PubKey, PrivateKey;
|
||||
std::string Result;
|
||||
std::ifstream ifs;
|
||||
std::ostringstream ss;
|
||||
BIO *bp_public = nullptr,
|
||||
*bp_private = nullptr,
|
||||
*bp_csr = nullptr;
|
||||
|
||||
// 1. generate rsa key
|
||||
bne = BN_new();
|
||||
ret = BN_set_word(bne,e);
|
||||
if(ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
r = RSA_new();
|
||||
ret = RSA_generate_key_ex(r, Parameters.bits, bne, nullptr);
|
||||
if(ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
bp_public = BIO_new_file(PubKey.path().c_str(), "w+");
|
||||
ret = PEM_write_bio_RSAPublicKey(bp_public, r);
|
||||
if(ret != 1) {
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
bp_private = BIO_new_file(PrivateKey.path().c_str(), "w+");
|
||||
ret = PEM_write_bio_RSAPrivateKey(bp_private, r, NULL, NULL, 0, NULL, NULL);
|
||||
if(ret != 1) {
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// 2. set version of x509 req
|
||||
x509_req = X509_REQ_new();
|
||||
ret = X509_REQ_set_version(x509_req, nVersion);
|
||||
if (ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// 3. set subject of x509 req
|
||||
x509_name = X509_REQ_get_subject_name(x509_req);
|
||||
|
||||
ret = X509_NAME_add_entry_by_txt(x509_name,"C", MBSTRING_ASC, (const unsigned char*)szCountry, -1, -1, 0);
|
||||
if (ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
ret = X509_NAME_add_entry_by_txt(x509_name,"ST", MBSTRING_ASC, (const unsigned char*)szProvince, -1, -1, 0);
|
||||
if (ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
ret = X509_NAME_add_entry_by_txt(x509_name,"L", MBSTRING_ASC, (const unsigned char*)szCity, -1, -1, 0);
|
||||
if (ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
ret = X509_NAME_add_entry_by_txt(x509_name,"O", MBSTRING_ASC, (const unsigned char*)szOrganization, -1, -1, 0);
|
||||
if (ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
ret = X509_NAME_add_entry_by_txt(x509_name,"CN", MBSTRING_ASC, (const unsigned char*)szCommon, -1, -1, 0);
|
||||
if (ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// 4. set public key of x509 req
|
||||
pKey = EVP_PKEY_new();
|
||||
EVP_PKEY_assign_RSA(pKey, r);
|
||||
r = nullptr; // will be free rsa when EVP_PKEY_free(pKey)
|
||||
|
||||
ret = X509_REQ_set_pubkey(x509_req, pKey);
|
||||
if (ret != 1){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
// 5. set sign key of x509 req
|
||||
ret = X509_REQ_sign(x509_req, pKey, EVP_sha1()); // return x509_req->signature->length
|
||||
if (ret <= 0){
|
||||
goto free_all;
|
||||
}
|
||||
|
||||
bp_csr = BIO_new_file(CsrPath.path().c_str(),"w");
|
||||
ret = PEM_write_bio_X509_REQ(bp_csr, x509_req);
|
||||
|
||||
// 6. free
|
||||
free_all:
|
||||
X509_REQ_free(x509_req);
|
||||
BIO_free_all(bp_csr);
|
||||
BIO_free_all(bp_public);
|
||||
BIO_free_all(bp_private);
|
||||
|
||||
EVP_PKEY_free(pKey);
|
||||
BN_free(bne);
|
||||
if(ret==1) {
|
||||
Results.CSR = FileToString(CsrPath.path());
|
||||
Results.PrivateKey = FileToString(PrivateKey.path());
|
||||
Results.PublicKey = FileToString(PubKey.path());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool VerifyECKey(const std::string &key) {
|
||||
try {
|
||||
Poco::TemporaryFile F;
|
||||
|
||||
std::ofstream of(F.path().c_str(), std::ios_base::trunc | std::ios_base::out | std::ios_base::binary);
|
||||
of << key;
|
||||
of.close();
|
||||
|
||||
auto Key = Poco::SharedPtr<Poco::Crypto::ECKey>(
|
||||
new Poco::Crypto::ECKey("", F.path(),""));
|
||||
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VerifyRSAKey([[
|
||||
maybe_unused]] const std::string &key) {
|
||||
try {
|
||||
Poco::TemporaryFile F;
|
||||
|
||||
std::ofstream of(F.path().c_str(), std::ios_base::trunc | std::ios_base::out | std::ios_base::binary);
|
||||
of << key;
|
||||
of.close();
|
||||
|
||||
auto Key = Poco::SharedPtr<Poco::Crypto::RSAKey>(
|
||||
new Poco::Crypto::RSAKey("", F.path(),""));
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidX509Certificate([[
|
||||
maybe_unused]] const std::string &Cert) {
|
||||
try {
|
||||
Poco::TemporaryFile F;
|
||||
std::ofstream of(F.path().c_str(), std::ios_base::trunc | std::ios_base::out | std::ios_base::binary);
|
||||
of << Cert;
|
||||
of.close();
|
||||
|
||||
auto Key = Poco::SharedPtr<Poco::Crypto::X509Certificate>(
|
||||
new Poco::Crypto::X509Certificate(F.path()));
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidX509Certificate([[
|
||||
maybe_unused]] const std::vector<std::string> &Certs) {
|
||||
auto F = [](const std::string &C) -> bool { return ValidX509Certificate(C); };
|
||||
return std::all_of(Certs.begin(),Certs.end(), F);
|
||||
}
|
||||
|
||||
std::string generateStrongPassword(int minLength, int maxLength, int numDigits, int minLowercase, int minSpecial, int minUppercase) {
|
||||
// Define character sets for each category
|
||||
const std::string lowercaseChars = "abcdefghijklmnopqrstuvwxyz";
|
||||
const std::string uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const std::string digitChars = "0123456789";
|
||||
const std::string specialChars = "!@#$%^&*()_+[]{}|;:,.<>?";
|
||||
|
||||
// Check if parameters are valid
|
||||
if (minLength < 1 || minLength > maxLength || minLowercase + minUppercase + numDigits + minSpecial > maxLength) {
|
||||
return "Invalid parameters";
|
||||
}
|
||||
|
||||
// Initialize random seed
|
||||
std::random_device rd;
|
||||
std::mt19937 g(rd());
|
||||
|
||||
// Initialize the password string
|
||||
std::string password;
|
||||
|
||||
// Generate the required number of each character type
|
||||
for (int i = 0; i < minLowercase; ++i) {
|
||||
password += lowercaseChars[g() % lowercaseChars.length()];
|
||||
}
|
||||
for (int i = 0; i < minUppercase; ++i) {
|
||||
password += uppercaseChars[g() % uppercaseChars.length()];
|
||||
}
|
||||
for (int i = 0; i < numDigits; ++i) {
|
||||
password += digitChars[g() % digitChars.length()];
|
||||
}
|
||||
for (int i = 0; i < minSpecial; ++i) {
|
||||
password += specialChars[g() % specialChars.length()];
|
||||
}
|
||||
|
||||
// Calculate how many more characters are needed
|
||||
int remainingLength = maxLength - (int)password.length();
|
||||
|
||||
// Generate random characters to fill the remaining length
|
||||
for (int i = 0; i < remainingLength; ++i) {
|
||||
int category = g() % 4; // Randomly select a category
|
||||
if (category == 0) {
|
||||
password += lowercaseChars[g() % lowercaseChars.length()];
|
||||
} else if (category == 1) {
|
||||
password += uppercaseChars[g() % uppercaseChars.length()];
|
||||
} else if (category == 2) {
|
||||
password += digitChars[g() % digitChars.length()];
|
||||
} else {
|
||||
password += specialChars[g() % specialChars.length()];
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle the password to randomize the character order
|
||||
std::shuffle(password.begin(), password.end(),g);
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
} // namespace OpenWifi::Utils
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "Poco/Base64Decoder.h"
|
||||
#include "Poco/Base64Encoder.h"
|
||||
#include "Poco/File.h"
|
||||
@@ -68,8 +70,10 @@ namespace OpenWifi::Utils {
|
||||
};
|
||||
|
||||
[[nodiscard]] bool ValidSerialNumber(const std::string &Serial);
|
||||
[[nodiscard]] bool ValidSerialNumbers(const std::vector<std::string> &Serial);
|
||||
[[nodiscard]] bool ValidUUID(const std::string &UUID);
|
||||
[[nodiscard]] bool ValidHostname(const std::string &hostname);
|
||||
[[nodiscard]] bool ValidNumber(const std::string &number, bool isSigned);
|
||||
|
||||
template <typename... Args> std::string ComputeHash(Args &&...args) {
|
||||
Poco::SHA2Engine E;
|
||||
@@ -146,4 +150,118 @@ namespace OpenWifi::Utils {
|
||||
|
||||
bool ExtractBase64CompressedData(const std::string &CompressedData,
|
||||
std::string &UnCompressedData, uint64_t compress_sz);
|
||||
|
||||
inline bool match(const char* first, const char* second)
|
||||
{
|
||||
// If we reach at the end of both strings, we are done
|
||||
if (*first == '\0' && *second == '\0')
|
||||
return true;
|
||||
|
||||
// Make sure to eliminate consecutive '*'
|
||||
if (*first == '*') {
|
||||
while (*(first + 1) == '*')
|
||||
first++;
|
||||
}
|
||||
|
||||
// Make sure that the characters after '*' are present
|
||||
// in second string. This function assumes that the
|
||||
// first string will not contain two consecutive '*'
|
||||
if (*first == '*' && *(first + 1) != '\0'
|
||||
&& *second == '\0')
|
||||
return false;
|
||||
|
||||
// If the first string contains '?', or current
|
||||
// characters of both strings match
|
||||
if (*first == '?' || *first == *second)
|
||||
return match(first + 1, second + 1);
|
||||
|
||||
// If there is *, then there are two possibilities
|
||||
// a) We consider current character of second string
|
||||
// b) We ignore current character of second string.
|
||||
if (*first == '*')
|
||||
return match(first + 1, second)
|
||||
|| match(first, second + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline std::uint64_t GetValue(FILE *file) {
|
||||
unsigned long v=0;
|
||||
char factor[32];
|
||||
if(fscanf(file, " %lu %31s", &v, factor)==2) {
|
||||
switch (factor[0]) {
|
||||
case 'k':
|
||||
return v * 1000;
|
||||
case 'M':
|
||||
return v * 1000000;
|
||||
case 'G':
|
||||
return v * 1000000000;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
inline bool getMemory(
|
||||
std::uint64_t &currRealMem, std::uint64_t &peakRealMem,
|
||||
std::uint64_t &currVirtMem, std::uint64_t &peakVirtMem) {
|
||||
|
||||
// stores each word in status file
|
||||
char buffer[1024] = "";
|
||||
|
||||
currRealMem = peakRealMem = currVirtMem = peakVirtMem = 0;
|
||||
|
||||
// linux file contains this-process info
|
||||
FILE * file = std::fopen("/proc/self/status", "r");
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the entire file, recording mems in kB
|
||||
while (fscanf(file, " %1023s", buffer) == 1) {
|
||||
|
||||
if (strcmp(buffer, "VmRSS:") == 0) {
|
||||
currRealMem= GetValue(file);
|
||||
} else if (strcmp(buffer, "VmHWM:") == 0) {
|
||||
peakRealMem= GetValue(file);
|
||||
} else if (strcmp(buffer, "VmSize:") == 0) {
|
||||
currVirtMem= GetValue(file);
|
||||
} else if (strcmp(buffer, "VmPeak:") == 0) {
|
||||
peakVirtMem= GetValue(file);
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int get_open_fds() {
|
||||
DIR *dp = opendir("/proc/self/fd");
|
||||
struct dirent *de;
|
||||
int count = -3; // '.', '..', dp
|
||||
|
||||
if (dp == nullptr)
|
||||
return -1;
|
||||
while ((de = readdir(dp)) != nullptr)
|
||||
count++;
|
||||
(void)closedir(dp);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
struct CSRCreationParameters {
|
||||
std::string Country, Province, City,
|
||||
Organization, CommonName;
|
||||
int bits=2048;
|
||||
};
|
||||
|
||||
struct CSRCreationResults {
|
||||
std::string CSR, PublicKey, PrivateKey;
|
||||
};
|
||||
|
||||
bool CreateX509CSR(const CSRCreationParameters & Parameters, CSRCreationResults & Results);
|
||||
std::string generateStrongPassword(int minLength, int maxLength, int numDigits, int minLowercase, int minSpecial, int minUppercase);
|
||||
bool VerifyECKey(const std::string &key);
|
||||
bool VerifyRSAKey(const std::string &key);
|
||||
bool ValidX509Certificate(const std::string &Cert);
|
||||
bool ValidX509Certificate(const std::vector<std::string> &Certs);
|
||||
|
||||
} // namespace OpenWifi::Utils
|
||||
|
||||
127
src/libs/Cron.h
Normal file
127
src/libs/Cron.h
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-12.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
namespace Bosma {
|
||||
using Clock = std::chrono::system_clock;
|
||||
|
||||
inline void add(std::tm &tm, Clock::duration time) {
|
||||
auto tp = Clock::from_time_t(std::mktime(&tm));
|
||||
auto tp_adjusted = tp + time;
|
||||
auto tm_adjusted = Clock::to_time_t(tp_adjusted);
|
||||
tm = *std::localtime(&tm_adjusted);
|
||||
}
|
||||
|
||||
class BadCronExpression : public std::exception {
|
||||
public:
|
||||
explicit BadCronExpression(std::string msg) : msg_(std::move(msg)) {}
|
||||
|
||||
const char *what() const noexcept override { return (msg_.c_str()); }
|
||||
|
||||
private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
inline void
|
||||
verify_and_set(const std::string &token, const std::string &expression, int &field, const int lower_bound,
|
||||
const int upper_bound, const bool adjust = false) {
|
||||
if (token == "*")
|
||||
field = -1;
|
||||
else {
|
||||
try {
|
||||
field = std::stoi(token);
|
||||
} catch (const std::invalid_argument &) {
|
||||
throw BadCronExpression("malformed cron string (`" + token + "` not an integer or *): " + expression);
|
||||
} catch (const std::out_of_range &) {
|
||||
throw BadCronExpression("malformed cron string (`" + token + "` not convertable to int): " + expression);
|
||||
}
|
||||
if (field < lower_bound || field > upper_bound) {
|
||||
std::ostringstream oss;
|
||||
oss << "malformed cron string ('" << token << "' must be <= " << upper_bound << " and >= " << lower_bound
|
||||
<< "): " << expression;
|
||||
throw BadCronExpression(oss.str());
|
||||
}
|
||||
if (adjust)
|
||||
field--;
|
||||
}
|
||||
}
|
||||
|
||||
class Cron {
|
||||
public:
|
||||
explicit Cron(const std::string &expression) {
|
||||
std::istringstream iss(expression);
|
||||
std::vector<std::string> tokens{std::istream_iterator<std::string>{iss},
|
||||
std::istream_iterator<std::string>{}};
|
||||
|
||||
if (tokens.size() != 5) throw BadCronExpression("malformed cron string (must be 5 fields): " + expression);
|
||||
|
||||
verify_and_set(tokens[0], expression, minute, 0, 59);
|
||||
verify_and_set(tokens[1], expression, hour, 0, 23);
|
||||
verify_and_set(tokens[2], expression, day, 1, 31);
|
||||
verify_and_set(tokens[3], expression, month, 1, 12, true);
|
||||
verify_and_set(tokens[4], expression, day_of_week, 0, 6);
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/a/322058/1284550
|
||||
Clock::time_point cron_to_next(const Clock::time_point from = Clock::now()) const {
|
||||
// get current time as a tm object
|
||||
auto now = Clock::to_time_t(from);
|
||||
std::tm next(*std::localtime(&now));
|
||||
// it will always at least run the next minute
|
||||
next.tm_sec = 0;
|
||||
add(next, std::chrono::minutes(1));
|
||||
while (true) {
|
||||
if (month != -1 && next.tm_mon != month) {
|
||||
// add a month
|
||||
// if this will bring us over a year, increment the year instead and reset the month
|
||||
if (next.tm_mon + 1 > 11) {
|
||||
next.tm_mon = 0;
|
||||
next.tm_year++;
|
||||
} else
|
||||
next.tm_mon++;
|
||||
|
||||
next.tm_mday = 1;
|
||||
next.tm_hour = 0;
|
||||
next.tm_min = 0;
|
||||
continue;
|
||||
}
|
||||
if (day != -1 && next.tm_mday != day) {
|
||||
add(next, std::chrono::hours(24));
|
||||
next.tm_hour = 0;
|
||||
next.tm_min = 0;
|
||||
continue;
|
||||
}
|
||||
if (day_of_week != -1 && next.tm_wday != day_of_week) {
|
||||
add(next, std::chrono::hours(24));
|
||||
next.tm_hour = 0;
|
||||
next.tm_min = 0;
|
||||
continue;
|
||||
}
|
||||
if (hour != -1 && next.tm_hour != hour) {
|
||||
add(next, std::chrono::hours(1));
|
||||
next.tm_min = 0;
|
||||
continue;
|
||||
}
|
||||
if (minute != -1 && next.tm_min != minute) {
|
||||
add(next, std::chrono::minutes(1));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// telling mktime to figure out dst
|
||||
next.tm_isdst = -1;
|
||||
return Clock::from_time_t(std::mktime(&next));
|
||||
}
|
||||
|
||||
int minute, hour, day, month, day_of_week;
|
||||
};
|
||||
}
|
||||
67
src/libs/InterruptableSleep.h
Normal file
67
src/libs/InterruptableSleep.h
Normal file
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-12.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
|
||||
namespace Bosma {
|
||||
class InterruptableSleep {
|
||||
|
||||
using Clock = std::chrono::system_clock;
|
||||
|
||||
// InterruptableSleep offers a sleep that can be interrupted by any thread.
|
||||
// It can be interrupted multiple times
|
||||
// and be interrupted before any sleep is called (the sleep will immediately complete)
|
||||
// Has same interface as condition_variables and futures, except with sleep instead of wait.
|
||||
// For a given object, sleep can be called on multiple threads safely, but is not recommended as behaviour is undefined.
|
||||
|
||||
public:
|
||||
InterruptableSleep() : interrupted(false) {
|
||||
}
|
||||
|
||||
InterruptableSleep(const InterruptableSleep &) = delete;
|
||||
|
||||
InterruptableSleep(InterruptableSleep &&) noexcept = delete;
|
||||
|
||||
~InterruptableSleep() noexcept = default;
|
||||
|
||||
InterruptableSleep &operator=(const InterruptableSleep &) noexcept = delete;
|
||||
|
||||
InterruptableSleep &operator=(InterruptableSleep &&) noexcept = delete;
|
||||
|
||||
void sleep_for(Clock::duration duration) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
cv.wait_for(ul, duration, [this] { return interrupted; });
|
||||
interrupted = false;
|
||||
}
|
||||
|
||||
void sleep_until(Clock::time_point time) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
cv.wait_until(ul, time, [this] { return interrupted; });
|
||||
interrupted = false;
|
||||
}
|
||||
|
||||
void sleep() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
cv.wait(ul, [this] { return interrupted; });
|
||||
interrupted = false;
|
||||
}
|
||||
|
||||
void interrupt() {
|
||||
std::lock_guard<std::mutex> lg(m);
|
||||
interrupted = true;
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
bool interrupted;
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
}
|
||||
237
src/libs/Scheduler.h
Normal file
237
src/libs/Scheduler.h
Normal file
@@ -0,0 +1,237 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-12.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
|
||||
#include "ctpl_stl.h"
|
||||
|
||||
#include "InterruptableSleep.h"
|
||||
#include "Cron.h"
|
||||
|
||||
namespace Bosma {
|
||||
using Clock = std::chrono::system_clock;
|
||||
|
||||
class Task {
|
||||
public:
|
||||
explicit Task(std::function<void()> &&f, bool recur = false, bool interval = false) :
|
||||
f(std::move(f)), recur(recur), interval(interval) {}
|
||||
|
||||
virtual Clock::time_point get_new_time() const = 0;
|
||||
virtual ~Task() = default;
|
||||
|
||||
std::function<void()> f;
|
||||
|
||||
bool recur;
|
||||
bool interval;
|
||||
};
|
||||
|
||||
class InTask : public Task {
|
||||
public:
|
||||
explicit InTask(std::function<void()> &&f) : Task(std::move(f)) {}
|
||||
|
||||
// dummy time_point because it's not used
|
||||
[[nodiscard]] Clock::time_point get_new_time() const override { return Clock::time_point(Clock::duration(0)); }
|
||||
};
|
||||
|
||||
class EveryTask : public Task {
|
||||
public:
|
||||
EveryTask(Clock::duration time, std::function<void()> &&f, bool interval = false) :
|
||||
Task(std::move(f), true, interval), time(time) {}
|
||||
|
||||
[[nodiscard]] Clock::time_point get_new_time() const override {
|
||||
return Clock::now() + time;
|
||||
};
|
||||
Clock::duration time;
|
||||
};
|
||||
|
||||
class CronTask : public Task {
|
||||
public:
|
||||
CronTask(const std::string &expression, std::function<void()> &&f) : Task(std::move(f), true),
|
||||
cron(expression) {}
|
||||
|
||||
[[nodiscard]] Clock::time_point get_new_time() const override {
|
||||
return cron.cron_to_next();
|
||||
};
|
||||
Cron cron;
|
||||
};
|
||||
|
||||
inline bool try_parse(std::tm &tm, const std::string &expression, const std::string &format) {
|
||||
std::stringstream ss(expression);
|
||||
return !(ss >> std::get_time(&tm, format.c_str())).fail();
|
||||
}
|
||||
|
||||
class Scheduler {
|
||||
public:
|
||||
explicit Scheduler(unsigned int max_n_tasks = 4) : done(false), threads(max_n_tasks + 1) {
|
||||
threads.push([this](int) {
|
||||
while (!done) {
|
||||
Clock::time_point sleep_until_time;
|
||||
if(find_sleep_time(sleep_until_time)) {
|
||||
sleeper.sleep_until(sleep_until_time);
|
||||
} else {
|
||||
sleeper.sleep();
|
||||
}
|
||||
manage_tasks();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Scheduler(const Scheduler &) = delete;
|
||||
|
||||
Scheduler(Scheduler &&) noexcept = delete;
|
||||
|
||||
Scheduler &operator=(const Scheduler &) = delete;
|
||||
|
||||
Scheduler &operator=(Scheduler &&) noexcept = delete;
|
||||
|
||||
~Scheduler() {
|
||||
done = true;
|
||||
sleeper.interrupt();
|
||||
}
|
||||
|
||||
template<typename _Callable, typename... _Args>
|
||||
void in(const Clock::time_point time, _Callable &&f, _Args &&... args) {
|
||||
std::shared_ptr<Task> t = std::make_shared<InTask>(
|
||||
std::bind(std::forward<_Callable>(f), std::forward<_Args>(args)...));
|
||||
add_task(time, std::move(t));
|
||||
}
|
||||
|
||||
template<typename _Callable, typename... _Args>
|
||||
void in(const Clock::duration time, _Callable &&f, _Args &&... args) {
|
||||
in(Clock::now() + time, std::forward<_Callable>(f), std::forward<_Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename _Callable, typename... _Args>
|
||||
void at(const std::string &time, _Callable &&f, _Args &&... args) {
|
||||
// get current time as a tm object
|
||||
auto time_now = Clock::to_time_t(Clock::now());
|
||||
std::tm tm = *std::localtime(&time_now);
|
||||
|
||||
// our final time as a time_point
|
||||
Clock::time_point tp;
|
||||
|
||||
if (try_parse(tm, time, "%H:%M:%S")) {
|
||||
// convert tm back to time_t, then to a time_point and assign to final
|
||||
tp = Clock::from_time_t(std::mktime(&tm));
|
||||
|
||||
// if we've already passed this time, the user will mean next day, so add a day.
|
||||
if (Clock::now() >= tp)
|
||||
tp += std::chrono::hours(24);
|
||||
} else if (try_parse(tm, time, "%Y-%m-%d %H:%M:%S")) {
|
||||
tp = Clock::from_time_t(std::mktime(&tm));
|
||||
} else if (try_parse(tm, time, "%Y/%m/%d %H:%M:%S")) {
|
||||
tp = Clock::from_time_t(std::mktime(&tm));
|
||||
} else {
|
||||
// could not parse time
|
||||
throw std::runtime_error("Cannot parse time string: " + time);
|
||||
}
|
||||
|
||||
in(tp, std::forward<_Callable>(f), std::forward<_Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename _Callable, typename... _Args>
|
||||
void every(const Clock::duration time, _Callable &&f, _Args &&... args) {
|
||||
std::shared_ptr<Task> t = std::make_shared<EveryTask>(time, std::bind(std::forward<_Callable>(f),
|
||||
std::forward<_Args>(args)...));
|
||||
auto next_time = t->get_new_time();
|
||||
add_task(next_time, std::move(t));
|
||||
}
|
||||
|
||||
// expression format:
|
||||
// from https://en.wikipedia.org/wiki/Cron#Overview
|
||||
// ┌───────────── minute (0 - 59)
|
||||
// │ ┌───────────── hour (0 - 23)
|
||||
// │ │ ┌───────────── day of month (1 - 31)
|
||||
// │ │ │ ┌───────────── month (1 - 12)
|
||||
// │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday)
|
||||
// │ │ │ │ │
|
||||
// │ │ │ │ │
|
||||
// * * * * *
|
||||
template<typename _Callable, typename... _Args>
|
||||
void cron(const std::string &expression, _Callable &&f, _Args &&... args) {
|
||||
std::shared_ptr<Task> t = std::make_shared<CronTask>(expression, std::bind(std::forward<_Callable>(f),
|
||||
std::forward<_Args>(args)...));
|
||||
auto next_time = t->get_new_time();
|
||||
add_task(next_time, std::move(t));
|
||||
}
|
||||
|
||||
template<typename _Callable, typename... _Args>
|
||||
void interval(const Clock::duration time, _Callable &&f, _Args &&... args) {
|
||||
std::shared_ptr<Task> t = std::make_shared<EveryTask>(time, std::bind(std::forward<_Callable>(f),
|
||||
std::forward<_Args>(args)...), true);
|
||||
add_task(Clock::now(), std::move(t));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> done;
|
||||
|
||||
Bosma::InterruptableSleep sleeper;
|
||||
|
||||
std::multimap<Clock::time_point, std::shared_ptr<Task>> tasks;
|
||||
std::mutex lock;
|
||||
ctpl::thread_pool threads;
|
||||
|
||||
void add_task(const Clock::time_point time, std::shared_ptr<Task> t) {
|
||||
std::lock_guard<std::mutex> l(lock);
|
||||
tasks.emplace(time, std::move(t));
|
||||
sleeper.interrupt();
|
||||
}
|
||||
|
||||
bool find_sleep_time(Clock::time_point &sleep_value) {
|
||||
std::lock_guard<std::mutex> l(lock);
|
||||
if(tasks.empty()) {
|
||||
return false;
|
||||
}
|
||||
sleep_value = (*tasks.begin()).first;
|
||||
return true;
|
||||
}
|
||||
|
||||
void manage_tasks() {
|
||||
std::lock_guard<std::mutex> l(lock);
|
||||
|
||||
auto end_of_tasks_to_run = tasks.upper_bound(Clock::now());
|
||||
|
||||
// if there are any tasks to be run and removed
|
||||
if (end_of_tasks_to_run != tasks.begin()) {
|
||||
// keep track of tasks that will be re-added
|
||||
decltype(tasks) recurred_tasks;
|
||||
|
||||
// for all tasks that have been triggered
|
||||
for (auto i = tasks.begin(); i != end_of_tasks_to_run; ++i) {
|
||||
|
||||
auto &task = (*i).second;
|
||||
|
||||
if (task->interval) {
|
||||
// if it's an interval task, only add the task back after f() is completed
|
||||
threads.push([this, task](int) {
|
||||
task->f();
|
||||
// no risk of race-condition,
|
||||
// add_task() will wait for manage_tasks() to release lock
|
||||
add_task(task->get_new_time(), task);
|
||||
});
|
||||
} else {
|
||||
threads.push([task](int) {
|
||||
task->f();
|
||||
});
|
||||
// calculate time of next run and add the new task to the tasks to be recurred
|
||||
if (task->recur)
|
||||
recurred_tasks.emplace(task->get_new_time(), std::move(task));
|
||||
}
|
||||
}
|
||||
|
||||
// remove the completed tasks
|
||||
tasks.erase(tasks.begin(), end_of_tasks_to_run);
|
||||
|
||||
// re-add the tasks that are recurring
|
||||
for (auto &task : recurred_tasks)
|
||||
tasks.emplace(task.first, std::move(task.second));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
253
src/libs/ctpl_stl.h
Normal file
253
src/libs/ctpl_stl.h
Normal file
@@ -0,0 +1,253 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-04-12.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
/*********************************************************
|
||||
*
|
||||
* Copyright (C) 2014 by Vitaliy Vitsentiy
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*********************************************************/
|
||||
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
|
||||
|
||||
// thread pool to run user's functors with signature
|
||||
// ret func(int id, other_params)
|
||||
// where id is the index of the thread that runs the functor
|
||||
// ret is some return type
|
||||
|
||||
|
||||
namespace ctpl {
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
class Queue {
|
||||
public:
|
||||
bool push(T const & value) {
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
this->q.push(value);
|
||||
return true;
|
||||
}
|
||||
// deletes the retrieved element, do not use for non integral types
|
||||
bool pop(T & v) {
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
if (this->q.empty())
|
||||
return false;
|
||||
v = this->q.front();
|
||||
this->q.pop();
|
||||
return true;
|
||||
}
|
||||
bool empty() {
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
return this->q.empty();
|
||||
}
|
||||
private:
|
||||
std::queue<T> q;
|
||||
std::mutex mutex;
|
||||
};
|
||||
}
|
||||
|
||||
class thread_pool {
|
||||
|
||||
public:
|
||||
|
||||
thread_pool() { this->init(); }
|
||||
thread_pool(int nThreads) { this->init(); this->resize(nThreads); }
|
||||
|
||||
// the destructor waits for all the functions in the queue to be finished
|
||||
~thread_pool() {
|
||||
this->stop(true);
|
||||
}
|
||||
|
||||
// get the number of running threads in the pool
|
||||
int size() { return static_cast<int>(this->threads.size()); }
|
||||
|
||||
// number of idle threads
|
||||
int n_idle() { return this->nWaiting; }
|
||||
std::thread & get_thread(int i) { return *this->threads[i]; }
|
||||
|
||||
// change the number of threads in the pool
|
||||
// should be called from one thread, otherwise be careful to not interleave, also with this->stop()
|
||||
// nThreads must be >= 0
|
||||
void resize(int nThreads) {
|
||||
if (!this->isStop && !this->isDone) {
|
||||
int oldNThreads = static_cast<int>(this->threads.size());
|
||||
if (oldNThreads <= nThreads) { // if the number of threads is increased
|
||||
this->threads.resize(nThreads);
|
||||
this->flags.resize(nThreads);
|
||||
|
||||
for (int i = oldNThreads; i < nThreads; ++i) {
|
||||
this->flags[i] = std::make_shared<std::atomic<bool>>(false);
|
||||
this->set_thread(i);
|
||||
}
|
||||
}
|
||||
else { // the number of threads is decreased
|
||||
for (int i = oldNThreads - 1; i >= nThreads; --i) {
|
||||
*this->flags[i] = true; // this thread will finish
|
||||
this->threads[i]->detach();
|
||||
}
|
||||
{
|
||||
// stop the detached threads that were waiting
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
this->cv.notify_all();
|
||||
}
|
||||
this->threads.resize(nThreads); // safe to delete because the threads are detached
|
||||
this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// empty the queue
|
||||
void clear_queue() {
|
||||
std::function<void(int id)> * _f;
|
||||
while (this->q.pop(_f))
|
||||
delete _f; // empty the queue
|
||||
}
|
||||
|
||||
// pops a functional wrapper to the original function
|
||||
std::function<void(int)> pop() {
|
||||
std::function<void(int id)> * _f = nullptr;
|
||||
this->q.pop(_f);
|
||||
std::unique_ptr<std::function<void(int id)>> func(_f); // at return, delete the function even if an exception occurred
|
||||
std::function<void(int)> f;
|
||||
if (_f)
|
||||
f = *_f;
|
||||
return f;
|
||||
}
|
||||
|
||||
// wait for all computing threads to finish and stop all threads
|
||||
// may be called asynchronously to not pause the calling thread while waiting
|
||||
// if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions
|
||||
void stop(bool isWait = false) {
|
||||
if (!isWait) {
|
||||
if (this->isStop)
|
||||
return;
|
||||
this->isStop = true;
|
||||
for (int i = 0, n = this->size(); i < n; ++i) {
|
||||
*this->flags[i] = true; // command the threads to stop
|
||||
}
|
||||
this->clear_queue(); // empty the queue
|
||||
}
|
||||
else {
|
||||
if (this->isDone || this->isStop)
|
||||
return;
|
||||
this->isDone = true; // give the waiting threads a command to finish
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
this->cv.notify_all(); // stop all waiting threads
|
||||
}
|
||||
for (int i = 0; i < static_cast<int>(this->threads.size()); ++i) { // wait for the computing threads to finish
|
||||
if (this->threads[i]->joinable())
|
||||
this->threads[i]->join();
|
||||
}
|
||||
// if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads
|
||||
// therefore delete them here
|
||||
this->clear_queue();
|
||||
this->threads.clear();
|
||||
this->flags.clear();
|
||||
}
|
||||
|
||||
template<typename F, typename... Rest>
|
||||
auto push(F && f, Rest&&... rest) ->std::future<decltype(f(0, rest...))> {
|
||||
auto pck = std::make_shared<std::packaged_task<decltype(f(0, rest...))(int)>>(
|
||||
std::bind(std::forward<F>(f), std::placeholders::_1, std::forward<Rest>(rest)...)
|
||||
);
|
||||
auto _f = new std::function<void(int id)>([pck](int id) {
|
||||
(*pck)(id);
|
||||
});
|
||||
this->q.push(_f);
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
this->cv.notify_one();
|
||||
return pck->get_future();
|
||||
}
|
||||
|
||||
// run the user's function that excepts argument int - id of the running thread. returned value is templatized
|
||||
// operator returns std::future, where the user can get the result and rethrow the catched exceptins
|
||||
template<typename F>
|
||||
auto push(F && f) ->std::future<decltype(f(0))> {
|
||||
auto pck = std::make_shared<std::packaged_task<decltype(f(0))(int)>>(std::forward<F>(f));
|
||||
auto _f = new std::function<void(int id)>([pck](int id) {
|
||||
(*pck)(id);
|
||||
});
|
||||
this->q.push(_f);
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
this->cv.notify_one();
|
||||
return pck->get_future();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// deleted
|
||||
thread_pool(const thread_pool &);// = delete;
|
||||
thread_pool(thread_pool &&);// = delete;
|
||||
thread_pool & operator=(const thread_pool &);// = delete;
|
||||
thread_pool & operator=(thread_pool &&);// = delete;
|
||||
|
||||
void set_thread(int i) {
|
||||
std::shared_ptr<std::atomic<bool>> flag(this->flags[i]); // a copy of the shared ptr to the flag
|
||||
auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() {
|
||||
std::atomic<bool> & _flag = *flag;
|
||||
std::function<void(int id)> * _f;
|
||||
bool isPop = this->q.pop(_f);
|
||||
while (true) {
|
||||
while (isPop) { // if there is anything in the queue
|
||||
std::unique_ptr<std::function<void(int id)>> func(_f); // at return, delete the function even if an exception occurred
|
||||
(*_f)(i);
|
||||
if (_flag)
|
||||
return; // the thread is wanted to stop, return even if the queue is not empty yet
|
||||
else
|
||||
isPop = this->q.pop(_f);
|
||||
}
|
||||
// the queue is empty here, wait for the next command
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
++this->nWaiting;
|
||||
this->cv.wait(lock, [this, &_f, &isPop, &_flag](){ isPop = this->q.pop(_f); return isPop || this->isDone || _flag; });
|
||||
--this->nWaiting;
|
||||
if (!isPop)
|
||||
return; // if the queue is empty and this->isDone == true or *flag then return
|
||||
}
|
||||
};
|
||||
this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique()
|
||||
}
|
||||
|
||||
void init() { this->nWaiting = 0; this->isStop = false; this->isDone = false; }
|
||||
|
||||
std::vector<std::unique_ptr<std::thread>> threads;
|
||||
std::vector<std::shared_ptr<std::atomic<bool>>> flags;
|
||||
detail::Queue<std::function<void(int id)> *> q;
|
||||
std::atomic<bool> isDone;
|
||||
std::atomic<bool> isStop;
|
||||
std::atomic<int> nWaiting; // how many threads are waiting
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -21,9 +21,18 @@
|
||||
#include "Poco/Net/SocketAcceptor.h"
|
||||
#include <algorithm>
|
||||
|
||||
/*
|
||||
|
||||
#define DBGLINE \
|
||||
{ std::cout << __LINE__ << std::endl; }
|
||||
2023-09-25 14:57:48.963 RADSEC: radsec.openro.am@3.33.129.120:2084: [Error][thr:7] SSL connection unexpectedly closed
|
||||
2023-09-25 14:57:48.964 RADSEC: radsec.openro.am@3.33.129.120:2084: [Information][thr:7] Disconnecting.
|
||||
2023-09-25 14:57:50.965 RADSEC: radsec.openro.am@3.33.129.120:2084: [Information][thr:40] Attempting to connect
|
||||
2023-09-25 14:57:51.675 RTTY-SVR: [Error][thr:6] Frame readable shutdown.
|
||||
2023-09-25 14:57:51.675 RTTY-SVR: [Debug][thr:6] Closing connection onClientSocketReadable:646
|
||||
2023-09-25 14:57:51.717 RADSEC: radsec.openro.am@3.33.129.120:2084: [Information][thr:40] Connected. CN=radsec.openro.am
|
||||
2023-09-25 14:57:51.717 RADSEC: radsec.openro.am@3.33.129.120:2084: [Error][thr:7] SSL connection unexpectedly closed
|
||||
2023-09-25 14:57:51.717 RADSEC: radsec.openro.am@3.33.129.120:2084: [Information][thr:7] Disconnecting.
|
||||
|
||||
*/
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -222,7 +231,6 @@ namespace OpenWifi {
|
||||
}
|
||||
NewSocket.close();
|
||||
} catch (const Poco::Exception &E) {
|
||||
std::cout << "Exception onDeviceAccept: " << E.what() << std::endl;
|
||||
Logger().log(E);
|
||||
}
|
||||
}
|
||||
@@ -246,7 +254,7 @@ namespace OpenWifi {
|
||||
void RTTYS_server::AddNewSocket(Poco::Net::StreamSocket &Socket, std::unique_ptr<Poco::Crypto::X509Certificate> P, bool valid, const std::string &cid, const std::string &cn) {
|
||||
Socket.setNoDelay(true);
|
||||
Socket.setKeepAlive(true);
|
||||
Socket.setBlocking(false);
|
||||
Socket.setBlocking(true);
|
||||
Socket.setReceiveBufferSize(RTTY_RECEIVE_BUFFER);
|
||||
Socket.setSendBufferSize(RTTY_RECEIVE_BUFFER);
|
||||
Poco::Timespan TS2(300, 100);
|
||||
@@ -265,7 +273,6 @@ namespace OpenWifi {
|
||||
Sockets_[fd] = std::make_unique<SecureSocketPair>(Socket, std::move(P), valid, cid, cn);
|
||||
}
|
||||
|
||||
|
||||
void RTTYS_server::RemoveSocket(const Poco::Net::Socket &Socket) {
|
||||
auto hint = Sockets_.find(Socket.impl()->sockfd());
|
||||
if(hint!=end(Sockets_)) {
|
||||
@@ -296,16 +303,8 @@ namespace OpenWifi {
|
||||
*this, &RTTYS_server::onClientSocketError));
|
||||
}
|
||||
|
||||
int RTTYS_server::SendBytes(int fd, const unsigned char *buffer, std::size_t len) {
|
||||
auto hint = Sockets_.find(fd);
|
||||
if(hint==end(Sockets_)) {
|
||||
poco_error(Logger(),fmt::format("Cannot find this socket: {}",fd));
|
||||
return -1;
|
||||
}
|
||||
return hint->second->socket.impl()->sendBytes(buffer,len);
|
||||
}
|
||||
|
||||
int RTTYS_server::SendBytes(const Poco::Net::Socket &Socket, const unsigned char *buffer, std::size_t len) {
|
||||
int RTTYS_server::SendBytes(const std::shared_ptr<RTTYS_EndPoint> & Conn, const Poco::Net::Socket &Socket, const unsigned char *buffer, std::size_t len) {
|
||||
Conn->tx += len;
|
||||
return Socket.impl()->sendBytes(buffer,len);
|
||||
}
|
||||
|
||||
@@ -381,7 +380,7 @@ namespace OpenWifi {
|
||||
OutBuf[5] = 'K';
|
||||
OutBuf[6] = 0;
|
||||
|
||||
if (SendBytes(Socket,OutBuf, 7) != 7) {
|
||||
if (SendBytes(ConnectionEp,Socket,OutBuf, 7) != 7) {
|
||||
poco_error(
|
||||
Logger(),
|
||||
fmt::format("{}: Description:{} Could not send data to complete registration",
|
||||
@@ -419,15 +418,24 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void RTTYS_server::EmptyBuffer(int fd, const std::uint8_t *buffer, std::size_t len) {
|
||||
auto EndPoint = Connected_.find(fd);
|
||||
if (EndPoint!=end(Connected_) && EndPoint->second->WSSocket_!= nullptr && EndPoint->second->WSSocket_->impl() != nullptr) {
|
||||
SendToClient(*EndPoint->second->WSSocket_, buffer,
|
||||
len);
|
||||
EndPoint->second->rx += len;
|
||||
// std::cout << "Total: " << EndPoint->second->rx << " bytes now: " << len << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void RTTYS_server::onConnectedDeviceSocketReadable(
|
||||
const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf) {
|
||||
|
||||
std::shared_ptr<RTTYS_EndPoint> ConnectionPtr;
|
||||
std::lock_guard Lock(ServerMutex_);
|
||||
int fd = pNf->socket().impl()->sockfd();
|
||||
|
||||
try {
|
||||
|
||||
int fd = pNf->socket().impl()->sockfd();
|
||||
auto hint = Sockets_.find(fd);
|
||||
if(hint==end(Sockets_)) {
|
||||
poco_error(Logger(),fmt::format("{}: unknown socket",fd));
|
||||
@@ -437,19 +445,17 @@ namespace OpenWifi {
|
||||
Poco::FIFOBuffer &buffer = *hint->second->buffer;
|
||||
|
||||
int received_bytes=0;
|
||||
// int line=0;
|
||||
std::uint8_t agg_buffer[RTTY_RECEIVE_BUFFER];
|
||||
std::size_t agg_buf_pos=0;
|
||||
|
||||
try {
|
||||
// std::cout << "Available: " << buffer.available() << " ";
|
||||
received_bytes = hint->second->socket.receiveBytes(*hint->second->buffer);
|
||||
Poco::Timespan TS(5,0);
|
||||
received_bytes = hint->second->socket.receiveBytes(buffer);
|
||||
if(received_bytes==0) {
|
||||
poco_warning(Logger(), "Device Closing connection - 0 bytes received.");
|
||||
EndConnection( pNf->socket(), __func__, __LINE__ );
|
||||
return;
|
||||
}
|
||||
// for(std::size_t i=0;i< std::min(buffer.used(),(std::size_t) 16) ;++i) {
|
||||
// std::cout << (int) buffer[i] << " ";
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
} catch (const Poco::TimeoutException &E) {
|
||||
poco_warning(Logger(), "Receive timeout");
|
||||
EndConnection( pNf->socket(), __func__, __LINE__ );
|
||||
@@ -465,12 +471,10 @@ namespace OpenWifi {
|
||||
while (!buffer.isEmpty() && good) {
|
||||
|
||||
if(buffer.used() < RTTY_HDR_SIZE) {
|
||||
poco_debug(Logger(),fmt::format("Not enough data in the pipe for header",buffer.used()));
|
||||
// std::cout << "Not enough in header: " << buffer.used() << std::endl;
|
||||
// for(std::size_t i=0;i< std::min(buffer.used(),(std::size_t) 16) ;++i) {
|
||||
// std::cout << (int) buffer[i] << " ";
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
if(agg_buf_pos>0) {
|
||||
EmptyBuffer(fd, agg_buffer, agg_buf_pos);
|
||||
}
|
||||
// poco_debug(Logger(),fmt::format("Not enough data in the pipe for header",buffer.used()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -481,24 +485,15 @@ namespace OpenWifi {
|
||||
std::uint16_t msg_len = (header[1] << 8) + header[2];
|
||||
|
||||
if(buffer.used()<(RTTY_HDR_SIZE+msg_len)) {
|
||||
poco_debug(Logger(),fmt::format("Not enough data in the pipe for command data",buffer.used()));
|
||||
// std::cout << "Not enough in header: " << buffer.used() << " msg length: " << msg_len << std::endl;
|
||||
// for(std::size_t i=0;i< std::min(buffer.used(),(std::size_t) 16) ;++i) {
|
||||
// std::cout << (int) buffer[i] << " ";
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
if(agg_buf_pos>0) {
|
||||
EmptyBuffer(fd, agg_buffer, agg_buf_pos);
|
||||
}
|
||||
// poco_debug(Logger(),fmt::format("Not enough data in the pipe for command data",buffer.used()));
|
||||
return;
|
||||
}
|
||||
|
||||
// std::cout << line++ << " Available: " << buffer.available() << " Cmd: " << (int) LastCommand << " Received: " << received_bytes
|
||||
// << " MsgLen: " << msg_len << " Data in buffer: " << buffer.used() << std::endl;
|
||||
|
||||
buffer.drain(RTTY_HDR_SIZE);
|
||||
|
||||
// if((line & 0x0000003f)==0) {
|
||||
// do_msgTypeHeartbeat(pNf->socket(),buffer,msg_len);
|
||||
// }
|
||||
|
||||
switch (LastCommand) {
|
||||
case RTTYS_EndPoint::msgTypeRegister: {
|
||||
good = do_msgTypeRegister(pNf->socket(), buffer, msg_len);
|
||||
@@ -510,7 +505,7 @@ namespace OpenWifi {
|
||||
good = do_msgTypeLogout(pNf->socket(), buffer, msg_len);
|
||||
} break;
|
||||
case RTTYS_EndPoint::msgTypeTermData: {
|
||||
good = do_msgTypeTermData(pNf->socket(), buffer, msg_len);
|
||||
good = do_msgTypeTermData(pNf->socket(), buffer, msg_len, agg_buffer, agg_buf_pos);
|
||||
} break;
|
||||
case RTTYS_EndPoint::msgTypeWinsize: {
|
||||
good = do_msgTypeWinsize(pNf->socket(), buffer, msg_len);
|
||||
@@ -542,7 +537,9 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
// std::cout << "Empty: " << buffer.isEmpty() << std::endl;
|
||||
if(agg_buf_pos>0) {
|
||||
EmptyBuffer(fd, agg_buffer, agg_buf_pos);
|
||||
}
|
||||
|
||||
if (!good) {
|
||||
EndConnection(pNf->socket(), __func__, __LINE__);
|
||||
@@ -588,7 +585,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
int flags;
|
||||
unsigned char FrameBuffer[1024];
|
||||
unsigned char FrameBuffer[64000];
|
||||
|
||||
auto ReceivedBytes = Connection->WSSocket_->receiveFrame(FrameBuffer, sizeof(FrameBuffer), flags);
|
||||
auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK;
|
||||
@@ -824,9 +821,8 @@ namespace OpenWifi {
|
||||
} else {
|
||||
EndPoints_.erase(hint->second->Id_);
|
||||
}
|
||||
} else {
|
||||
std::cout << "Cannot find the associated WS..." << std::endl;
|
||||
}
|
||||
|
||||
poco_debug(Logger(),fmt::format("Closing connection at {}:{}", func, Line));
|
||||
}
|
||||
|
||||
@@ -870,7 +866,7 @@ namespace OpenWifi {
|
||||
Conn->small_buf_[3] = Conn->sid_;
|
||||
memcpy(&Conn->small_buf_[RTTY_HDR_SIZE + 1], &buf[1], len - 1);
|
||||
try {
|
||||
auto Sent = SendBytes(Conn->DeviceSocket_, Conn->small_buf_,
|
||||
auto Sent = SendBytes(Conn,Conn->DeviceSocket_, Conn->small_buf_,
|
||||
RTTY_HDR_SIZE + 1 + len - 1);
|
||||
return (Sent == (int)(RTTY_HDR_SIZE + 1 + len - 1));
|
||||
} catch (const Poco::Exception &E) {
|
||||
@@ -888,7 +884,7 @@ namespace OpenWifi {
|
||||
Msg.get()[3] = Conn->sid_;
|
||||
memcpy((Msg.get() + RTTY_HDR_SIZE + 1), &buf[1], len - 1);
|
||||
try {
|
||||
auto Sent = SendBytes(Conn->DeviceSocket_,Msg.get(),
|
||||
auto Sent = SendBytes(Conn,Conn->DeviceSocket_,Msg.get(),
|
||||
RTTY_HDR_SIZE + 1 + len - 1);
|
||||
return (Sent == (int)(RTTY_HDR_SIZE + 1 + len - 1));
|
||||
} catch (const Poco::Exception &E) {
|
||||
@@ -913,7 +909,7 @@ namespace OpenWifi {
|
||||
outBuf[RTTY_HDR_SIZE + 2 + 1] = rows >> 8;
|
||||
outBuf[RTTY_HDR_SIZE + 3 + 1] = rows & 0x00ff;
|
||||
try {
|
||||
auto Sent = SendBytes(Conn->DeviceSocket_, outBuf, RTTY_HDR_SIZE + 4 + 1);
|
||||
auto Sent = SendBytes(Conn,Conn->DeviceSocket_, outBuf, RTTY_HDR_SIZE + 4 + 1);
|
||||
return (Sent == (int)(RTTY_HDR_SIZE + 4 + 1));
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
@@ -931,7 +927,7 @@ namespace OpenWifi {
|
||||
outBuf[2] = 0;
|
||||
try {
|
||||
poco_debug(Logger(), fmt::format("TID:{} Starting login on device.",Conn->TID_));
|
||||
auto Sent = SendBytes(Socket,outBuf,RTTY_HDR_SIZE);
|
||||
auto Sent = SendBytes(Conn,Socket,outBuf,RTTY_HDR_SIZE);
|
||||
Conn->completed_ = true;
|
||||
return Sent == RTTY_HDR_SIZE;
|
||||
} catch (const Poco::Exception &E) {
|
||||
@@ -950,7 +946,7 @@ namespace OpenWifi {
|
||||
outBuf[3] = Conn->sid_;
|
||||
poco_debug(Logger(), fmt::format("{}: Logout", Conn->TID_));
|
||||
try {
|
||||
auto Sent = SendBytes(Socket, outBuf, RTTY_HDR_SIZE + 1);
|
||||
auto Sent = SendBytes(Conn,Socket, outBuf, RTTY_HDR_SIZE + 1);
|
||||
return Sent == (int)(RTTY_HDR_SIZE + 1);
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
@@ -1034,17 +1030,18 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RTTYS_server::do_msgTypeTermData(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len) {
|
||||
bool RTTYS_server::do_msgTypeTermData(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len, std::uint8_t *buf, std::size_t &pos) {
|
||||
auto EndPoint = Connected_.find(Socket.impl()->sockfd());
|
||||
if (EndPoint!=end(Connected_) && EndPoint->second->WSSocket_!= nullptr && EndPoint->second->WSSocket_->impl() != nullptr) {
|
||||
try {
|
||||
buffer.drain(1);
|
||||
msg_len--;
|
||||
auto good = SendToClient(*EndPoint->second->WSSocket_, (unsigned char*) buffer.begin(), (int) msg_len );
|
||||
buffer.drain(msg_len);
|
||||
return good;
|
||||
buffer.read((char*)&(buf[pos]),msg_len);
|
||||
pos+=msg_len;
|
||||
// auto good = SendToClient(*EndPoint->second->WSSocket_, (unsigned char*) buffer.begin(), (int) msg_len );
|
||||
// buffer.drain(msg_len);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
std::cout << "Failed to send WS stuff" << std::endl;
|
||||
Logger().log(E);
|
||||
} catch (const std::exception &E) {
|
||||
LogStdException(E, "Cannot send data to UI Client");
|
||||
@@ -1071,8 +1068,11 @@ namespace OpenWifi {
|
||||
MsgBuf[0] = RTTYS_EndPoint::msgTypeHeartbeat;
|
||||
MsgBuf[1] = 0;
|
||||
MsgBuf[2] = 0;
|
||||
auto Sent = SendBytes(Socket,MsgBuf, RTTY_HDR_SIZE);
|
||||
return Sent == RTTY_HDR_SIZE;
|
||||
auto hint = Connected_.find(Socket.impl()->sockfd());
|
||||
if(hint!=end(Connected_)) {
|
||||
auto Sent = SendBytes(hint->second,Socket, MsgBuf, RTTY_HDR_SIZE);
|
||||
return Sent == RTTY_HDR_SIZE;
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (const std::exception &E) {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Poco/Net/WebSocket.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
#include "Poco/Timer.h"
|
||||
#include <Poco/FIFOBuffer.h>
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "framework/utils.h"
|
||||
@@ -25,7 +26,7 @@ namespace OpenWifi {
|
||||
constexpr uint RTTY_DEVICE_TOKEN_LENGTH = 32;
|
||||
constexpr std::size_t RTTY_SESSION_ID_LENGTH = 32;
|
||||
constexpr std::size_t RTTY_HDR_SIZE = 3;
|
||||
constexpr std::size_t RTTY_RECEIVE_BUFFER = 64000;
|
||||
constexpr std::size_t RTTY_RECEIVE_BUFFER = 1024 << 10;
|
||||
|
||||
class RTTYS_server;
|
||||
|
||||
@@ -114,6 +115,7 @@ namespace OpenWifi {
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> Created_{0s},
|
||||
DeviceDisconnected_{0s}, ClientDisconnected_{0s}, DeviceConnected_{0s},
|
||||
ClientConnected_{0s};
|
||||
std::uint64_t rx=0,tx=0;
|
||||
};
|
||||
|
||||
class RTTYS_server : public SubSystemServer {
|
||||
@@ -199,8 +201,8 @@ namespace OpenWifi {
|
||||
void SendData(std::shared_ptr<RTTYS_EndPoint> &Connection, const u_char *Buf, size_t len);
|
||||
void SendData(std::shared_ptr<RTTYS_EndPoint> &Connection, const std::string &s);
|
||||
|
||||
int SendBytes(int fd, const unsigned char *buffer, std::size_t len);
|
||||
int SendBytes(const Poco::Net::Socket &Socket, const unsigned char *buffer, std::size_t len);
|
||||
// int SendBytes(int fd, const unsigned char *buffer, std::size_t len);
|
||||
int SendBytes(const std::shared_ptr<RTTYS_EndPoint> & Conn,const Poco::Net::Socket &Socket, const unsigned char *buffer, std::size_t len);
|
||||
|
||||
std::shared_ptr<RTTYS_EndPoint> FindRegisteredEndPoint(const std::string &Id,
|
||||
const std::string &Token);
|
||||
@@ -211,7 +213,7 @@ namespace OpenWifi {
|
||||
|
||||
bool do_msgTypeRegister(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
bool do_msgTypeLogin(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
bool do_msgTypeTermData(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
bool do_msgTypeTermData(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len, std::uint8_t *buf, std::size_t &pos);
|
||||
bool do_msgTypeLogout(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
bool do_msgTypeWinsize(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
bool do_msgTypeCmd(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
@@ -221,6 +223,7 @@ namespace OpenWifi {
|
||||
bool do_msgTypeAck(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
bool do_msgTypeMax(const Poco::Net::Socket &Socket, Poco::FIFOBuffer &buffer, std::size_t msg_len);
|
||||
|
||||
void EmptyBuffer(int fd, const std::uint8_t *buffer, std::size_t len);
|
||||
bool WindowSize(std::shared_ptr<RTTYS_EndPoint> Conn, int cols, int rows);
|
||||
bool KeyStrokes(std::shared_ptr<RTTYS_EndPoint> Conn, const u_char *buf, size_t len);
|
||||
|
||||
|
||||
38
src/sdks/sdk_fms.h
Normal file
38
src/sdks/sdk_fms.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-07-12.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "RESTObjects/RESTAPI_FMSObjects.h"
|
||||
#include "framework/MicroServiceNames.h"
|
||||
#include "framework/OpenAPIRequests.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/Logger.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi::SDK::FMS {
|
||||
inline bool GetFirmwareAge( const std::string &deviceType, const std::string &revision, FMSObjects::FirmwareAgeDetails Age, Poco::Logger &Logger) {
|
||||
OpenAPIRequestGet GetFirmwareAgeAPI(
|
||||
uSERVICE_FIRMWARE, "/api/v1/firmwareAge" ,
|
||||
{
|
||||
{ RESTAPI::Protocol::DEVICETYPE, deviceType },
|
||||
{ RESTAPI::Protocol::REVISION, revision }
|
||||
}, 30000);
|
||||
|
||||
auto CallResponse = Poco::makeShared<Poco::JSON::Object>();
|
||||
if (!GetFirmwareAgeAPI.Do(CallResponse, "")) {
|
||||
Logger.error(fmt::format("{}: Cannot find revision.", revision));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Age.from_json(CallResponse)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace OpenWifi::SDK::Prov
|
||||
248
src/storage/storage_def_firmware.cpp
Normal file
248
src/storage/storage_def_firmware.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
//
|
||||
// License type: BSD 3-Clause License
|
||||
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
|
||||
//
|
||||
// Created by stephane bourque on 2023-07-11.
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#include "CentralConfig.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
const static std::string DB_DefFirmware_SelectFields_ForCreation{
|
||||
"deviceType VARCHAR(128) PRIMARY KEY, "
|
||||
"uri TEXT, "
|
||||
"revision TEXT, "
|
||||
"Description TEXT, "
|
||||
"Created BIGINT , "
|
||||
"imageCreationDate BIGINT , "
|
||||
"LastModified BIGINT)" };
|
||||
|
||||
const static std::string DB_DefFirmware_SelectFields{
|
||||
"deviceType, "
|
||||
"uri, "
|
||||
"revision, "
|
||||
"Description, "
|
||||
"Created, "
|
||||
"imageCreationDate, "
|
||||
"LastModified "};
|
||||
|
||||
const static std::string DB_DefFirmware_InsertValues{"?,?,?,?,?,?,?"};
|
||||
|
||||
typedef Poco::Tuple<std::string,
|
||||
std::string,
|
||||
std::string,
|
||||
std::string,
|
||||
uint64_t,
|
||||
uint64_t,
|
||||
uint64_t>
|
||||
DefFirmwareRecordTuple;
|
||||
typedef std::vector<DefFirmwareRecordTuple> DefFirmwareRecordList;
|
||||
|
||||
void Convert(const DefFirmwareRecordTuple &R, GWObjects::DefaultFirmware &T) {
|
||||
T.deviceType = R.get<0>();
|
||||
T.uri = R.get<1>();
|
||||
T.revision = R.get<2>();
|
||||
T.Description = R.get<3>();
|
||||
T.Created = R.get<4>();
|
||||
T.imageCreationDate = R.get<5>();
|
||||
T.LastModified = R.get<6>();
|
||||
}
|
||||
|
||||
void Convert(const GWObjects::DefaultFirmware &R, DefFirmwareRecordTuple &T) {
|
||||
T.set<0>(R.deviceType);
|
||||
T.set<1>(R.uri);
|
||||
T.set<2>(R.revision);
|
||||
T.set<3>(R.Description);
|
||||
T.set<4>(R.Created);
|
||||
T.set<5>(R.imageCreationDate);
|
||||
T.set<6>(R.LastModified);
|
||||
}
|
||||
|
||||
bool Storage::CreateDefaultFirmware(GWObjects::DefaultFirmware &DefFirmware) {
|
||||
try {
|
||||
|
||||
std::string TmpName;
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Select(Sess);
|
||||
|
||||
Poco::toLowerInPlace(DefFirmware.deviceType);
|
||||
|
||||
std::string St{"SELECT DeviceType FROM DefaultFirmwares WHERE deviceType=?"};
|
||||
Select << ConvertParams(St),
|
||||
Poco::Data::Keywords::into(TmpName),
|
||||
Poco::Data::Keywords::use(DefFirmware.deviceType);
|
||||
Select.execute();
|
||||
|
||||
if (!TmpName.empty())
|
||||
return false;
|
||||
|
||||
Poco::Data::Statement Insert(Sess);
|
||||
|
||||
std::string St2{"INSERT INTO DefaultFirmwares ( " + DB_DefFirmware_SelectFields +
|
||||
" ) "
|
||||
"VALUES(" +
|
||||
DB_DefFirmware_InsertValues + ")"};
|
||||
|
||||
DefFirmwareRecordTuple R;
|
||||
Convert(DefFirmware, R);
|
||||
Insert << ConvertParams(St2),
|
||||
Poco::Data::Keywords::use(R);
|
||||
Insert.execute();
|
||||
return true;
|
||||
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger(), fmt::format("{}: Failed with: {}", std::string(__func__),
|
||||
E.displayText()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::DeleteDefaultFirmware(std::string &deviceType) {
|
||||
try {
|
||||
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Delete(Sess);
|
||||
Poco::toLowerInPlace(deviceType);
|
||||
|
||||
std::string St{"DELETE FROM DefaultFirmwares WHERE deviceType=?"};
|
||||
|
||||
Delete << ConvertParams(St), Poco::Data::Keywords::use(deviceType);
|
||||
Delete.execute();
|
||||
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger(), fmt::format("{}: Failed with: {}", std::string(__func__),
|
||||
E.displayText()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::UpdateDefaultFirmware(GWObjects::DefaultFirmware &DefFirmware) {
|
||||
try {
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
|
||||
uint64_t Now = time(nullptr);
|
||||
Poco::Data::Statement Update(Sess);
|
||||
DefFirmware.LastModified = Now;
|
||||
Poco::toLowerInPlace(DefFirmware.deviceType);
|
||||
|
||||
|
||||
std::string St{"UPDATE DefaultFirmwares SET deviceType=?, uri=?, revision=?, "
|
||||
"Description=?, Created=? , imageCreationDate=?, LastModified=? WHERE deviceType=?"};
|
||||
|
||||
DefFirmwareRecordTuple R;
|
||||
Convert(DefFirmware, R);
|
||||
|
||||
Update << ConvertParams(St),
|
||||
Poco::Data::Keywords::use(R),
|
||||
Poco::Data::Keywords::use(DefFirmware.deviceType);
|
||||
Update.execute();
|
||||
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger(), fmt::format("{}: Failed with: {}", std::string(__func__),
|
||||
E.displayText()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::GetDefaultFirmware(std::string &deviceType,
|
||||
GWObjects::DefaultFirmware &DefFirmware) {
|
||||
try {
|
||||
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Select(Sess);
|
||||
Poco::toLowerInPlace(deviceType);
|
||||
|
||||
std::string St{"SELECT " + DB_DefFirmware_SelectFields +
|
||||
" FROM DefaultFirmwares WHERE deviceType=?"};
|
||||
|
||||
DefFirmwareRecordTuple R;
|
||||
Select << ConvertParams(St),
|
||||
Poco::Data::Keywords::into(R),
|
||||
Poco::Data::Keywords::use(deviceType);
|
||||
Select.execute();
|
||||
|
||||
if (Select.rowsExtracted() == 1) {
|
||||
Convert(R, DefFirmware);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger(), fmt::format("{}: Failed with: {}", std::string(__func__),
|
||||
E.displayText()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::DefaultFirmwareAlreadyExists(std::string &deviceType) {
|
||||
try {
|
||||
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Select(Sess);
|
||||
Poco::toLowerInPlace(deviceType);
|
||||
|
||||
std::string St{"SELECT " + DB_DefFirmware_SelectFields +
|
||||
" FROM DefaultFirmwares WHERE deviceType=?"};
|
||||
|
||||
DefFirmwareRecordTuple R;
|
||||
Select << ConvertParams(St), Poco::Data::Keywords::into(R),
|
||||
Poco::Data::Keywords::use(deviceType);
|
||||
Select.execute();
|
||||
|
||||
return Select.rowsExtracted() == 1;
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger(), fmt::format("{}: Failed with: {}", std::string(__func__),
|
||||
E.displayText()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Storage::GetDefaultFirmwares(uint64_t From, uint64_t HowMany,
|
||||
std::vector<GWObjects::DefaultFirmware> &Firmwares) {
|
||||
try {
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Select(Sess);
|
||||
|
||||
DefFirmwareRecordList Records;
|
||||
Select << "SELECT " + DB_DefFirmware_SelectFields +
|
||||
" FROM DefaultFirmwares ORDER BY deviceType ASC " + ComputeRange(From, HowMany),
|
||||
Poco::Data::Keywords::into(Records);
|
||||
Select.execute();
|
||||
Firmwares.clear();
|
||||
for (const auto &i : Records) {
|
||||
GWObjects::DefaultFirmware R;
|
||||
Convert(i, R);
|
||||
Firmwares.push_back(R);
|
||||
}
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger(), fmt::format("{}: Failed with: {}", std::string(__func__),
|
||||
E.displayText()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Storage::GetDefaultFirmwaresCount() {
|
||||
uint64_t Count = 0;
|
||||
try {
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Select(Sess);
|
||||
Select << "SELECT Count(*) from DefaultFirmwares", Poco::Data::Keywords::into(Count);
|
||||
Select.execute();
|
||||
return Count;
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_warning(Logger(), fmt::format("{}: Failed with: {}", std::string(__func__),
|
||||
E.displayText()));
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
@@ -51,7 +51,10 @@ namespace OpenWifi {
|
||||
"pendingConfiguration, "
|
||||
"pendingConfigurationCmd, "
|
||||
"restrictionDetails, "
|
||||
"pendingUUID"};
|
||||
"pendingUUID, "
|
||||
"simulated,"
|
||||
"lastRecordedContact"
|
||||
};
|
||||
|
||||
const static std::string DB_DeviceUpdateFields{"SerialNumber=?,"
|
||||
"DeviceType=?, "
|
||||
@@ -79,16 +82,18 @@ namespace OpenWifi {
|
||||
"pendingConfiguration=?, "
|
||||
"pendingConfigurationCmd=?, "
|
||||
"restrictionDetails=?, "
|
||||
"pendingUUID=?"};
|
||||
"pendingUUID=?, "
|
||||
"simulated=?,"
|
||||
"lastRecordedContact=? "};
|
||||
|
||||
const static std::string DB_DeviceInsertValues{
|
||||
" VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) "};
|
||||
" VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) "};
|
||||
|
||||
typedef Poco::Tuple<std::string, std::string, std::string, std::string, std::string,
|
||||
std::string, std::string, std::string, std::string, std::string,
|
||||
std::string, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, std::string,
|
||||
std::string, std::string, std::string, uint64_t, std::string, bool,
|
||||
std::string, std::string, std::string, std::uint64_t>
|
||||
std::string, std::string, std::string, std::uint64_t, bool, std::uint64_t>
|
||||
DeviceRecordTuple;
|
||||
typedef std::vector<DeviceRecordTuple> DeviceRecordList;
|
||||
|
||||
@@ -121,6 +126,8 @@ namespace OpenWifi {
|
||||
D.restrictionDetails =
|
||||
RESTAPI_utils::to_object<OpenWifi::GWObjects::DeviceRestrictions>(R.get<25>());
|
||||
D.pendingUUID = R.get<26>();
|
||||
D.simulated = R.get<27>();
|
||||
D.lastRecordedContact = R.get<28>();
|
||||
}
|
||||
|
||||
void ConvertDeviceRecord(const GWObjects::Device &D, DeviceRecordTuple &R) {
|
||||
@@ -151,6 +158,8 @@ namespace OpenWifi {
|
||||
R.set<24>(D.pendingConfigurationCmd);
|
||||
R.set<25>(RESTAPI_utils::to_string(D.restrictionDetails));
|
||||
R.set<26>(D.pendingUUID);
|
||||
R.set<27>(D.simulated);
|
||||
R.set<28>(D.lastRecordedContact);
|
||||
}
|
||||
|
||||
bool Storage::GetDeviceCount(uint64_t &Count) {
|
||||
@@ -347,6 +356,23 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::SetDeviceLastRecordedContact(std::string &SerialNumber, std::uint64_t lastRecordedContact) {
|
||||
try {
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Update(Sess);
|
||||
std::string St{"UPDATE Devices SET lastRecordedContact=? WHERE SerialNumber=?"};
|
||||
|
||||
Update << ConvertParams(St), Poco::Data::Keywords::use(lastRecordedContact),
|
||||
Poco::Data::Keywords::use(SerialNumber);
|
||||
Update.execute();
|
||||
return true;
|
||||
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::CreateDevice(GWObjects::Device &DeviceDetails) {
|
||||
std::string SerialNumber;
|
||||
try {
|
||||
@@ -425,7 +451,8 @@ namespace OpenWifi {
|
||||
|
||||
bool Storage::CreateDefaultDevice(std::string &SerialNumber, const Config::Capabilities &Caps,
|
||||
std::string &Firmware,
|
||||
const Poco::Net::IPAddress &IPAddress) {
|
||||
const Poco::Net::IPAddress &IPAddress,
|
||||
bool simulated) {
|
||||
|
||||
GWObjects::Device D;
|
||||
poco_information(Logger(), fmt::format("AUTO-CREATION({})", SerialNumber));
|
||||
@@ -468,6 +495,7 @@ namespace OpenWifi {
|
||||
D.MACAddress = Utils::SerialToMAC(SerialNumber);
|
||||
D.Manufacturer = Caps.Model();
|
||||
D.Firmware = Firmware;
|
||||
D.simulated = simulated;
|
||||
D.Notes = SecurityObjects::NoteInfoVec{
|
||||
SecurityObjects::NoteInfo{(uint64_t)Utils::Now(), "", "Auto-provisioned."}};
|
||||
|
||||
@@ -540,17 +568,17 @@ namespace OpenWifi {
|
||||
|
||||
bool Storage::DeleteDevice(std::string &SerialNumber) {
|
||||
try {
|
||||
std::vector<std::string> DBList{"Devices", "Statistics", "CommandList",
|
||||
std::vector<std::string> TableNames{"Devices", "Statistics", "CommandList",
|
||||
"HealthChecks", "Capabilities", "DeviceLogs"};
|
||||
|
||||
for (const auto &i : DBList) {
|
||||
for (const auto &tableName : TableNames) {
|
||||
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement Delete(Sess);
|
||||
|
||||
std::string St{"DELETE FROM " + i + " WHERE SerialNumber=?"};
|
||||
std::string St = fmt::format("DELETE FROM {} WHERE SerialNumber='{}'", tableName, SerialNumber);
|
||||
try {
|
||||
Delete << ConvertParams(St), Poco::Data::Keywords::use(SerialNumber);
|
||||
Delete << St;
|
||||
Delete.execute();
|
||||
} catch (...) {
|
||||
}
|
||||
@@ -562,9 +590,7 @@ namespace OpenWifi {
|
||||
Poco::JSON::Object Message;
|
||||
Message.set("command", "device_deleted");
|
||||
Message.set("timestamp", Utils::Now());
|
||||
std::ostringstream StrPayload;
|
||||
Message.stringify(StrPayload);
|
||||
KafkaManager()->PostMessage(KafkaTopics::COMMAND, SerialNumber, StrPayload.str());
|
||||
KafkaManager()->PostMessage(KafkaTopics::COMMAND, SerialNumber, Message);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -574,6 +600,58 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void DeleteDeviceList(std::vector<std::string> &SerialNumbers, Poco::Logger &Logger) {
|
||||
for (auto &serialNumber:SerialNumbers) {
|
||||
poco_information(Logger,fmt::format("BATCH-DEVICE_DELETE: deleting {}", serialNumber));
|
||||
StorageService()->DeleteDevice(serialNumber);
|
||||
}
|
||||
}
|
||||
|
||||
bool Storage::DeleteDevices(std::string &SerialPattern, bool SimulatedOnly) {
|
||||
try {
|
||||
std::vector<std::string> SerialNumbers;
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement GetSerialNumbers(Sess);
|
||||
|
||||
std::string SelectStatement = SimulatedOnly ?
|
||||
fmt::format("SELECT SerialNumber FROM Devices WHERE simulated and SerialNumber LIKE '{}' limit 10000",SerialPattern) :
|
||||
fmt::format("SELECT SerialNumber FROM Devices WHERE SerialNumber LIKE '{}' limit 10000", SerialPattern);
|
||||
|
||||
GetSerialNumbers << SelectStatement,
|
||||
Poco::Data::Keywords::into(SerialNumbers);
|
||||
GetSerialNumbers.execute();
|
||||
|
||||
poco_information(Logger(),fmt::format("BATCH-DEVICE_DELETE: Found {} devices that match the criteria {} to delete.", SerialNumbers.size(), SerialPattern));
|
||||
DeleteDeviceList(SerialNumbers, Logger());
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::DeleteDevices(std::uint64_t OlderContact, bool SimulatedOnly) {
|
||||
try {
|
||||
std::vector<std::string> SerialNumbers;
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
Poco::Data::Statement GetSerialNumbers(Sess);
|
||||
|
||||
std::string SelectStatement = SimulatedOnly ?
|
||||
fmt::format("SELECT SerialNumber FROM Devices WHERE simulated and lastRecordedContact!=0 and lastRecordedContact<{} limit 10000",OlderContact) :
|
||||
fmt::format("SELECT SerialNumber FROM Devices lastRecordedContact>0 and lastRecordedContact<{} limit 10000",OlderContact);
|
||||
GetSerialNumbers << SelectStatement,
|
||||
Poco::Data::Keywords::into(SerialNumbers);
|
||||
GetSerialNumbers.execute();
|
||||
|
||||
poco_information(Logger(),fmt::format("BATCH-DEVICE_DELETE: Found {} devices that match with lastRecordedContact older than {} to delete.", SerialNumbers.size(), OlderContact));
|
||||
DeleteDeviceList(SerialNumbers, Logger());
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Storage::GetDevice(std::string &SerialNumber, GWObjects::Device &DeviceDetails) {
|
||||
try {
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
@@ -746,6 +824,8 @@ namespace OpenWifi {
|
||||
return "serial mismatch";
|
||||
case GWObjects::VERIFIED:
|
||||
return "verified";
|
||||
case GWObjects::SIMULATED:
|
||||
return "simulated";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace OpenWifi {
|
||||
Create_CommandList();
|
||||
Create_BlackList();
|
||||
Create_FileUploads();
|
||||
Create_DefaultFirmwares();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -87,7 +88,9 @@ namespace OpenWifi {
|
||||
"pendingConfiguration TEXT, "
|
||||
"pendingConfigurationCmd VARCHAR(64), "
|
||||
"restrictionDetails TEXT, "
|
||||
"pendingUUID BIGINT "
|
||||
"pendingUUID BIGINT, "
|
||||
"simulated BOOLEAN,"
|
||||
"lastRecordedContact BIGINT"
|
||||
",INDEX DeviceOwner (Owner ASC),"
|
||||
"INDEX LocationIndex (Location ASC))",
|
||||
Poco::Data::Keywords::now;
|
||||
@@ -119,7 +122,9 @@ namespace OpenWifi {
|
||||
"pendingConfiguration TEXT,"
|
||||
"pendingConfigurationCmd VARCHAR(64), "
|
||||
"restrictionDetails TEXT,"
|
||||
"pendingUUID BIGINT "
|
||||
"pendingUUID BIGINT, "
|
||||
"simulated BOOLEAN, "
|
||||
"lastRecordedContact BIGINT"
|
||||
")",
|
||||
Poco::Data::Keywords::now;
|
||||
Sess << "CREATE INDEX IF NOT EXISTS DeviceOwner ON Devices (Owner ASC)",
|
||||
@@ -138,7 +143,10 @@ namespace OpenWifi {
|
||||
"alter table devices add column pendingConfiguration TEXT",
|
||||
"alter table devices add column pendingConfigurationCmd VARCHAR(64)",
|
||||
"alter table devices add column restrictionDetails TEXT",
|
||||
"alter table devices add column pendingUUID bigint"};
|
||||
"alter table devices add column pendingUUID bigint",
|
||||
"alter table devices add column lastRecordedContact bigint",
|
||||
"alter table devices add column simulated boolean"
|
||||
};
|
||||
|
||||
for (const auto &i : Script) {
|
||||
try {
|
||||
@@ -268,6 +276,28 @@ namespace OpenWifi {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int Storage::Create_DefaultFirmwares() {
|
||||
try {
|
||||
Poco::Data::Session Sess = Pool_->get();
|
||||
|
||||
if (dbType_ == pgsql || dbType_ == sqlite || dbType_ == mysql) {
|
||||
Sess << "CREATE TABLE IF NOT EXISTS DefaultFirmwares ("
|
||||
"deviceType VARCHAR(128) PRIMARY KEY, "
|
||||
"uri TEXT, "
|
||||
"revision TEXT, "
|
||||
"Description TEXT, "
|
||||
"Created BIGINT , "
|
||||
"imageCreationDate BIGINT , "
|
||||
"LastModified BIGINT)",
|
||||
Poco::Data::Keywords::now;
|
||||
}
|
||||
return 0;
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// mysql = float
|
||||
// sqlite, postgresql = real
|
||||
|
||||
|
||||
@@ -175,6 +175,22 @@ listdevices() {
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
deletesimdevices() {
|
||||
curl ${FLAGS} -X DELETE "https://${OWGW}/api/v1/devices?simulatedOnly=true&macPattern=$1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
deletebulkdevices() {
|
||||
curl ${FLAGS} -X DELETE "https://${OWGW}/api/v1/devices?macPattern=$1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
listdevicesk() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/devices" \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -283,6 +299,14 @@ systeminfo() {
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
systemresources() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/system?command=resources" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
reloadsubsystem() {
|
||||
payload="{ \"command\" : \"reload\", \"subsystems\" : [ \"$1\" ] }"
|
||||
curl ${FLAGS} -X POST "https://${OWGW}/api/v1/system" \
|
||||
@@ -842,6 +866,64 @@ regulatory_reload() {
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
radiussessions() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/radiusSessions/$1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
radiussearch() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/radiusSessions/0?userName=$1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
radiussearchmac() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/radiusSessions/0?mac=$1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
radiusaps() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/radiusSessions/0?serialNumberOnly=true" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
radiuscoadm() {
|
||||
payload="$(printf '{ "accountingSessionId": "%s", "accountingMultiSessionId": "%s" , "callingStationId": "%s" }' "$2" "$3" "$4" )"
|
||||
curl ${FLAGS} -X PUT "https://${OWGW}/api/v1/radiusSessions/$1?operation=coadm" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" \
|
||||
-d "$payload" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
listdefaultfirmwares() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/default_firmwares" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
getdefaultfirmware() {
|
||||
curl ${FLAGS} -X GET "https://${OWGW}/api/v1/default_firmware/$1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-H "Authorization: Bearer ${token}" > ${result_file}
|
||||
jq < ${result_file}
|
||||
}
|
||||
|
||||
check_response() {
|
||||
|
||||
if [ -s "$1" ]; then
|
||||
@@ -1092,6 +1174,7 @@ case "$1" in
|
||||
"getsubsystemnames") login; getsubsystemnames; logout ;;
|
||||
"reloadsubsystem") login; reloadsubsystem "$2"; logout ;;
|
||||
"systeminfo") login; systeminfo ; logout;;
|
||||
"systemresources") login; systemresources ; logout;;
|
||||
"ouilookup") login; ouilookup "$2"; logout;;
|
||||
"telemetry") login; telemetry "$2"; logout;;
|
||||
"telemetry_to_kafka") login; telemetry_to_kafka "$2" "$3"; logout;;
|
||||
@@ -1118,7 +1201,16 @@ case "$1" in
|
||||
"regulatory") login; regulatory "$2"; logout;;
|
||||
"regulatory_reload") login; regulatory_reload; logout;;
|
||||
"gethealthrange") login; gethealthrange "$2" "$3"; logout;;
|
||||
"radiussessions") login; radiussessions $2; logout;;
|
||||
"radiusaps") login; radiusaps ; logout;;
|
||||
"radiuscoadm") login; radiuscoadm "$2" "$3" "$4" "$5"; logout;;
|
||||
"radiussearch") login; radiussearch "$2"; logout;;
|
||||
"radiussearchmac") login; radiussearchmac "$2"; logout;;
|
||||
"deletesimdevices") login; deletesimdevices "$2"; logout;;
|
||||
"deletebulkdevices") login; deletebulkdevices "$2"; logout;;
|
||||
"listdefaultfirmwares") login; listdefaultfirmwares; logout;;
|
||||
"getdefaultfirmware") login; getdefaultfirmware "$2"; logout;;
|
||||
"testtoken") testtoken;;
|
||||
*) help ;;
|
||||
*) help ;;
|
||||
esac
|
||||
|
||||
|
||||
Reference in New Issue
Block a user