mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralgw.git
synced 2025-11-01 19:28:01 +00:00
Compare commits
1127 Commits
v2.8.0-RC3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
691d3399f8 | ||
|
|
6542726ac3 | ||
|
|
4bc753f8b5 | ||
|
|
e3a513fa6a | ||
|
|
b151239a5c | ||
|
|
862dab38d1 | ||
|
|
ee2e9a19c0 | ||
|
|
8b28fa0435 | ||
|
|
aeef70a121 | ||
|
|
42c421ec12 | ||
|
|
98bfb4b24d | ||
|
|
6b64089f55 | ||
|
|
1f6e42b57c | ||
|
|
f857d61377 | ||
|
|
66d580d047 | ||
|
|
567c671c22 | ||
|
|
9e3735ced8 | ||
|
|
18b169e517 | ||
|
|
cab0d8aee6 | ||
|
|
680b6a16e3 | ||
|
|
218694872f | ||
|
|
6ae1eeb2ea | ||
|
|
f537e701a0 | ||
|
|
d4dfb7b620 | ||
|
|
446cbf270f | ||
|
|
06ffee27b1 | ||
|
|
fa3c325bfa | ||
|
|
182a442582 | ||
|
|
3b7a24ea30 | ||
|
|
1c5909613f | ||
|
|
8e5e51a52a | ||
|
|
b4699e9178 | ||
|
|
438309714f | ||
|
|
a9130eeb75 | ||
|
|
33068fca9e | ||
|
|
d329151f6c | ||
|
|
ec846006bb | ||
|
|
bd48079a8d | ||
|
|
242261de0a | ||
|
|
31a4edead5 | ||
|
|
f7b697f219 | ||
|
|
e020da75fc | ||
|
|
89702f56e0 | ||
|
|
0ac97442c0 | ||
|
|
e38b4c8a13 | ||
|
|
9c5bbee834 | ||
|
|
a5d1eebe6d | ||
|
|
ee14f064c8 | ||
|
|
dbf52c1f23 | ||
|
|
9dc6a6bf97 | ||
|
|
1c0556f8bf | ||
|
|
d298139525 | ||
|
|
a37c961f5b | ||
|
|
75bcbd748c | ||
|
|
b6eba2a96d | ||
|
|
17082803d4 | ||
|
|
26b9a96506 | ||
|
|
5ce8dae9ec | ||
|
|
7da135c1e5 | ||
|
|
50ee4ba5cb | ||
|
|
3a8109d7ad | ||
|
|
56232966ec | ||
|
|
1ecf98d712 | ||
|
|
f5b60ced61 | ||
|
|
e4d141bb8e | ||
|
|
25b4288050 | ||
|
|
82430c2d5d | ||
|
|
7b68ec0536 | ||
|
|
839f4fec44 | ||
|
|
c4178209bb | ||
|
|
79ab67db50 | ||
|
|
00bc77feea | ||
|
|
4f00d77d2b | ||
|
|
c679d4ac40 | ||
|
|
4a150a9fcb | ||
|
|
83eb603f0a | ||
|
|
38bc0f0d69 | ||
|
|
e7362c2020 | ||
|
|
9c9987e190 | ||
|
|
4ac7b6ba0b | ||
|
|
f9ee19af91 | ||
|
|
cd2ab8660f | ||
|
|
b9f00f6603 | ||
|
|
596cfd49e1 | ||
|
|
b3deba5606 | ||
|
|
a97d49a06b | ||
|
|
b1be0604d6 | ||
|
|
b29f7f7dc4 | ||
|
|
132b31b06b | ||
|
|
3114ff8a32 | ||
|
|
9c5aeda5dd | ||
|
|
783ec99930 | ||
|
|
0c661b8b93 | ||
|
|
9d7f4da504 | ||
|
|
a3b6e7c315 | ||
|
|
451680cd5a | ||
|
|
7be48c3cfc | ||
|
|
b59d1cb4da | ||
|
|
c3a709c2b9 | ||
|
|
5d89107827 | ||
|
|
3c15c6dc4f | ||
|
|
7b33a692b2 | ||
|
|
b118dcbcec | ||
|
|
02a0eef44a | ||
|
|
c7ed7fb264 | ||
|
|
1d88bb50d9 | ||
|
|
3b613ea159 | ||
|
|
d00d409fca | ||
|
|
8382818e2d | ||
|
|
ed4670d239 | ||
|
|
cca3619e91 | ||
|
|
9a834c29a2 | ||
|
|
2b06a0bcf6 | ||
|
|
03dabed878 | ||
|
|
e133a9c3ab | ||
|
|
23b33fab20 | ||
|
|
909b4c889e | ||
|
|
a04c5336d2 | ||
|
|
4df1bf985d | ||
|
|
26a89f3eb5 | ||
|
|
b055711993 | ||
|
|
fcdb7423ef | ||
|
|
f286d5fb48 | ||
|
|
0d70601c64 | ||
|
|
a493defc99 | ||
|
|
fb3e1288ae | ||
|
|
ce52e05104 | ||
|
|
920b922121 | ||
|
|
3732cfd07e | ||
|
|
9e772b8c91 | ||
|
|
92252d09dc | ||
|
|
28636c3e1e | ||
|
|
8e44f1e85c | ||
|
|
b77d40fbf8 | ||
|
|
574172b8bf | ||
|
|
794b31591d | ||
|
|
66aee07105 | ||
|
|
86685f17d6 | ||
|
|
08f683c15a | ||
|
|
7fea477f55 | ||
|
|
f22b3e3995 | ||
|
|
3a1011a662 | ||
|
|
6595b37ae4 | ||
|
|
05b6a9474b | ||
|
|
1de94be447 | ||
|
|
aa8486c71b | ||
|
|
e75d3cfdbb | ||
|
|
1a0a6d4a70 | ||
|
|
071922c555 | ||
|
|
4e4b69e672 | ||
|
|
3d8f7c1162 | ||
|
|
2bf60dbb3f | ||
|
|
44bc27e9d4 | ||
|
|
f328a72b85 | ||
|
|
4cbceb9366 | ||
|
|
921267c00a | ||
|
|
d63cbce602 | ||
|
|
094bba4747 | ||
|
|
5bee5b1372 | ||
|
|
45357ad567 | ||
|
|
8e984a8f0e | ||
|
|
4e7babc25f | ||
|
|
1acabd4986 | ||
|
|
d839646dd8 | ||
|
|
31b52f9bd2 | ||
|
|
97bc19b949 | ||
|
|
c252e6c5c7 | ||
|
|
cef012c333 | ||
|
|
6513980525 | ||
|
|
adc055f3e8 | ||
|
|
6d991e5a48 | ||
|
|
ec5031ca83 | ||
|
|
19e4e92d92 | ||
|
|
37feb6a44c | ||
|
|
2aaab1207b | ||
|
|
7896b071da | ||
|
|
e073576692 | ||
|
|
65ad9ff96e | ||
|
|
08e7900889 | ||
|
|
8554481186 | ||
|
|
2b246fe1ee | ||
|
|
bd37534223 | ||
|
|
aa862d3fcf | ||
|
|
c6c6eaa4a5 | ||
|
|
ffe86a3994 | ||
|
|
52123f7dcc | ||
|
|
e430c522ba | ||
|
|
93c236aa79 | ||
|
|
c802e35c12 | ||
|
|
a59d49e096 | ||
|
|
5756d59519 | ||
|
|
35aa6fb99d | ||
|
|
fa6d0aa714 | ||
|
|
91147f3fbb | ||
|
|
15f3eaa02e | ||
|
|
6305e92399 | ||
|
|
3714fd5f05 | ||
|
|
19497b88ce | ||
|
|
6a35dc93bf | ||
|
|
8004aa6676 | ||
|
|
2c654d3471 | ||
|
|
df67141a98 | ||
|
|
071330d7f8 | ||
|
|
90f23dca73 | ||
|
|
500688edb7 | ||
|
|
222b98d019 | ||
|
|
915a2c936d | ||
|
|
e1bf6a9ab7 | ||
|
|
58fc925a2f | ||
|
|
3dda87b41f | ||
|
|
a5fe59086a | ||
|
|
c66b936c1e | ||
|
|
0e763cf034 | ||
|
|
eb441d71aa | ||
|
|
082153a229 | ||
|
|
25a03ca801 | ||
|
|
0d549137de | ||
|
|
40e814cab8 | ||
|
|
e40d53f3e5 | ||
|
|
607507ce9a | ||
|
|
b3ec5b1e81 | ||
|
|
c52d3c92f6 | ||
|
|
d1216a8ac4 | ||
|
|
c27f0390f8 | ||
|
|
052d379e2b | ||
|
|
4eda1b813f | ||
|
|
5364adf509 | ||
|
|
3b5580a525 | ||
|
|
644918fa14 | ||
|
|
8cdd398a6e | ||
|
|
9f7f4683df | ||
|
|
1c6e35fa8b | ||
|
|
af17823df0 | ||
|
|
0cff2163bb | ||
|
|
0a5fe39bde | ||
|
|
c42feca957 | ||
|
|
d3c5b17733 | ||
|
|
30bc8e8283 | ||
|
|
336c94a25a | ||
|
|
84f42e5a7d | ||
|
|
17752fdefe | ||
|
|
ef300b0349 | ||
|
|
9c4ecb6165 | ||
|
|
efd099b6fa | ||
|
|
15805dcaf6 | ||
|
|
e5ed1750cc | ||
|
|
87d74568f2 | ||
|
|
0cca3caa9b | ||
|
|
1d22e1153f | ||
|
|
f3ccc49647 | ||
|
|
46fb410108 | ||
|
|
fc680a6bc4 | ||
|
|
246f9c8aad | ||
|
|
3f06f00a88 | ||
|
|
0288d905b7 | ||
|
|
135b63c021 | ||
|
|
12e07fa65f | ||
|
|
567c2d1514 | ||
|
|
b9bd768ca6 | ||
|
|
f2dec010ee | ||
|
|
329d8d4441 | ||
|
|
d4dbbeb54b | ||
|
|
b1c6884d49 | ||
|
|
3ffa35bb14 | ||
|
|
bd06722948 | ||
|
|
8c70ec5280 | ||
|
|
6c13d845f1 | ||
|
|
77cc0b250b | ||
|
|
a3c424d8fe | ||
|
|
3f834a967b | ||
|
|
4120aefcd4 | ||
|
|
909ee66ef0 | ||
|
|
c3ad34d84b | ||
|
|
fbb9f40529 | ||
|
|
5f6300bb17 | ||
|
|
0bcfb26579 | ||
|
|
5b199bc4f5 | ||
|
|
4c6fb85542 | ||
|
|
63993789ca | ||
|
|
cc9d4c5f68 | ||
|
|
50fa1de62c | ||
|
|
14ef6608d6 | ||
|
|
8e48d30d6a | ||
|
|
46c6321674 | ||
|
|
77f67fe545 | ||
|
|
ceda1fc8f6 | ||
|
|
5b1a4fb9c0 | ||
|
|
2ddc0d3117 | ||
|
|
2cfc6c30e9 | ||
|
|
0318b475f5 | ||
|
|
c5c2dc1a1e | ||
|
|
7b899adb88 | ||
|
|
aa472ed79e | ||
|
|
1778912264 | ||
|
|
142bc4f271 | ||
|
|
a01d006d4e | ||
|
|
135a195081 | ||
|
|
ef7cb883fb | ||
|
|
2e361a41d7 | ||
|
|
84281ec58e | ||
|
|
3560871f44 | ||
|
|
b072f1e2ab | ||
|
|
72173ed4b5 | ||
|
|
ff53d4ba2e | ||
|
|
81721b4a61 | ||
|
|
3619be1832 | ||
|
|
a8fc823b94 | ||
|
|
a8a33013be | ||
|
|
9560f908a6 | ||
|
|
8400b8cfcb | ||
|
|
82897f5b76 | ||
|
|
4b472fd112 | ||
|
|
ecb6312f0f | ||
|
|
0aba846277 | ||
|
|
5be884ef98 | ||
|
|
10c890a196 | ||
|
|
0974abd510 | ||
|
|
8dbbfc3298 | ||
|
|
c981ae14ee | ||
|
|
97547068d7 | ||
|
|
16cc443786 | ||
|
|
7d8e15bf66 | ||
|
|
3f60c5abc6 | ||
|
|
d85fb32af9 | ||
|
|
030991f13c | ||
|
|
142541180f | ||
|
|
9bd48bf029 | ||
|
|
8581048528 | ||
|
|
6cfb0ae975 | ||
|
|
45561de44b | ||
|
|
2fccfd756e | ||
|
|
df42837a76 | ||
|
|
4bc1ac1aef | ||
|
|
77ee9d48d0 | ||
|
|
7154cca4b9 | ||
|
|
77732bdb95 | ||
|
|
7a8a05d77d | ||
|
|
aa2c28355b | ||
|
|
b6cb5d003b | ||
|
|
c93acdf54a | ||
|
|
0c5e0d649e | ||
|
|
1757440cfe | ||
|
|
c3ccfe455e | ||
|
|
66ec7745bb | ||
|
|
531c51dd64 | ||
|
|
227ec2dc96 | ||
|
|
50c5c76484 | ||
|
|
7a567e400b | ||
|
|
148eabdbc2 | ||
|
|
94ae20ce65 | ||
|
|
fd80f02f0b | ||
|
|
943cdd5010 | ||
|
|
36a5f4a5da | ||
|
|
e0e8cc1295 | ||
|
|
d7bad290e9 | ||
|
|
06766d2ed9 | ||
|
|
913a3e4ce1 | ||
|
|
4e713e4471 | ||
|
|
cb10ea6a7f | ||
|
|
f5095b9f79 | ||
|
|
8a69089513 | ||
|
|
ba6c657587 | ||
|
|
177deb8cd2 | ||
|
|
1dfc478d0e | ||
|
|
f077fb7fad | ||
|
|
0dc1be1f41 | ||
|
|
e1a0864b68 | ||
|
|
222c796eee | ||
|
|
15fe0df04a | ||
|
|
ad1a3c694c | ||
|
|
04be75f037 | ||
|
|
8cd26ce8cb | ||
|
|
525f53aaa9 | ||
|
|
ab95733067 | ||
|
|
35f4e26ca4 | ||
|
|
14f63cb324 | ||
|
|
0962c8383a | ||
|
|
891965a321 | ||
|
|
4d2adc3c3a | ||
|
|
24eb4079d7 | ||
|
|
53010fca84 | ||
|
|
6f3079ab0a | ||
|
|
c73a7c6c09 | ||
|
|
bb60fef3d6 | ||
|
|
b52766d23a | ||
|
|
6f9a9471c3 | ||
|
|
992c169ac7 | ||
|
|
ba8a932b36 | ||
|
|
13dc97d35b | ||
|
|
0ef01e5547 | ||
|
|
138b236832 | ||
|
|
a0d4606c22 | ||
|
|
a5200e46b7 | ||
|
|
2d3866a987 | ||
|
|
0251b40287 | ||
|
|
1e2f215902 | ||
|
|
8184027534 | ||
|
|
a43841b867 | ||
|
|
c391ea2f04 | ||
|
|
111faaa80d | ||
|
|
687cbcd0a4 | ||
|
|
c48665d4c5 | ||
|
|
c873681adc | ||
|
|
cb64f8a809 | ||
|
|
0d8be1fd46 | ||
|
|
e496f71e7d | ||
|
|
de7c8b687a | ||
|
|
61e346e0bf | ||
|
|
67bf9ca8c6 | ||
|
|
14b7fc8a0e | ||
|
|
546d6f9ee0 | ||
|
|
fe74fdecf9 | ||
|
|
c53a67edd5 | ||
|
|
5207f1e1a0 | ||
|
|
53aa2f05d2 | ||
|
|
2405b9fe95 | ||
|
|
bc14bf28bf | ||
|
|
878e705db5 | ||
|
|
be62022344 | ||
|
|
432434a377 | ||
|
|
60860ad9de | ||
|
|
29d6f2dda1 | ||
|
|
330b4176f2 | ||
|
|
75b76bb380 | ||
|
|
c48d129f4a | ||
|
|
673f506ff0 | ||
|
|
ac078ec1b8 | ||
|
|
2007998ae1 | ||
|
|
8050228b40 | ||
|
|
a73ea9b260 | ||
|
|
334728ca4e | ||
|
|
33e010bf78 | ||
|
|
657c96f8cd | ||
|
|
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 | ||
|
|
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 | ||
|
|
f143471723 | ||
|
|
957e6acf03 | ||
|
|
a6bc509c3d | ||
|
|
5fd145c7d6 | ||
|
|
f9b431e566 | ||
|
|
a35acfeb88 | ||
|
|
e028b2dd41 | ||
|
|
69e539ffbe | ||
|
|
4a8506f4fe | ||
|
|
2621e2fdde | ||
|
|
8425632453 | ||
|
|
bce03ee024 | ||
|
|
b6f76b70ab | ||
|
|
30e62dd203 | ||
|
|
ae9179eb0e | ||
|
|
0d59e8b0c2 | ||
|
|
545f787a4e | ||
|
|
64199bbb62 | ||
|
|
1ac4625947 | ||
|
|
b45c16f2c0 | ||
|
|
86a33ed818 | ||
|
|
65c4a96f41 | ||
|
|
b7908816a9 | ||
|
|
dc55a3a180 | ||
|
|
4b953d968b | ||
|
|
81324da9e8 | ||
|
|
54d94eef84 | ||
|
|
397799278c | ||
|
|
4b32a9cae0 | ||
|
|
6f6fc32ed4 | ||
|
|
429e0345d0 | ||
|
|
67c3f39ae0 | ||
|
|
50911d8a54 | ||
|
|
d8d0eed1fb | ||
|
|
d1eb584430 | ||
|
|
c76e10299d | ||
|
|
2949231f67 | ||
|
|
d6731e4e5b | ||
|
|
d92b064561 | ||
|
|
ec4a7f64de | ||
|
|
14877301e4 | ||
|
|
d92cba15bb | ||
|
|
33d92c9240 | ||
|
|
737ed24d91 | ||
|
|
670f497af4 | ||
|
|
11ba1cc6e4 | ||
|
|
e43ff02c3d | ||
|
|
c69c4754cf | ||
|
|
8478d761db | ||
|
|
500a4dc130 | ||
|
|
3e2a32544a | ||
|
|
83e0f88827 | ||
|
|
c420d9c572 | ||
|
|
c94d90911f | ||
|
|
04fd524b52 | ||
|
|
b49744779e | ||
|
|
60de792147 | ||
|
|
e6d6da53c7 | ||
|
|
cd62cb8b84 | ||
|
|
3439eeb7b7 | ||
|
|
85eb8546c3 | ||
|
|
bc5a8bbe5b | ||
|
|
6e413643de | ||
|
|
bd64ceb82d | ||
|
|
d92a93a872 | ||
|
|
0def242b40 | ||
|
|
eeb5e2d3be | ||
|
|
e76132f464 | ||
|
|
ac41327bf8 | ||
|
|
0084b5d21c | ||
|
|
69b4de1b74 | ||
|
|
0c3e8d4630 | ||
|
|
af739bc32b | ||
|
|
b535ca2b35 | ||
|
|
42a3f7f9c3 | ||
|
|
a244a7ea09 | ||
|
|
c03a3c336d | ||
|
|
59d648517f | ||
|
|
e24e62a556 | ||
|
|
a61ab8f0fb | ||
|
|
6841322a99 | ||
|
|
3b7dab4c6a | ||
|
|
229a403167 | ||
|
|
8513601a77 | ||
|
|
a13da08652 | ||
|
|
5b19f4fdeb | ||
|
|
ba9fab427b | ||
|
|
72ffcbbb45 | ||
|
|
e028768252 | ||
|
|
a7d07f8c60 | ||
|
|
9a16b51c28 | ||
|
|
fd1edc0a10 | ||
|
|
8deaeb26a7 | ||
|
|
518d9232ca | ||
|
|
a3e16eb1e2 | ||
|
|
1d630e4770 | ||
|
|
e843cf4575 | ||
|
|
3cd4ef228c | ||
|
|
7240a9aa51 | ||
|
|
213323d713 | ||
|
|
64cd687635 | ||
|
|
3db5c9ac48 | ||
|
|
acd0e46768 | ||
|
|
b52c19cde9 | ||
|
|
18c3902a7f | ||
|
|
bfb6f3b662 | ||
|
|
12d13df907 | ||
|
|
d25c7cbdb9 | ||
|
|
3e40b9312a | ||
|
|
e74623ba75 | ||
|
|
fe4f977848 | ||
|
|
5884db2e53 | ||
|
|
9a0b23e683 | ||
|
|
5309c8fd08 | ||
|
|
1f8912aa2e | ||
|
|
873c348d50 | ||
|
|
47d08002bf | ||
|
|
47c2f21884 | ||
|
|
da6aec9cf9 | ||
|
|
703788a1c6 | ||
|
|
fc0de059bb | ||
|
|
448594e724 | ||
|
|
ffbd3d8246 | ||
|
|
128e4e99e7 | ||
|
|
6983b0c2dd | ||
|
|
87281b450b | ||
|
|
3482057a68 | ||
|
|
0c960010f7 | ||
|
|
6bda58b24e | ||
|
|
0c086a5e56 | ||
|
|
53f738f39c | ||
|
|
9be08e1921 | ||
|
|
afccb5d0f2 | ||
|
|
9769787337 | ||
|
|
7d3a4dbfc7 | ||
|
|
0b6b1c7e9d | ||
|
|
f3ea96ca23 | ||
|
|
e314b33d05 | ||
|
|
68eff53911 | ||
|
|
86b4c2a64e | ||
|
|
dd6f845fe8 | ||
|
|
8f183d4dfa | ||
|
|
23fe484670 | ||
|
|
aba5e50ddb | ||
|
|
fc6e8802a3 | ||
|
|
5414bb4757 | ||
|
|
1f8b119b67 | ||
|
|
54cfa15b29 | ||
|
|
9b3dfafdcd | ||
|
|
ec03dbae5e | ||
|
|
e360ab3995 | ||
|
|
b843c6e0db | ||
|
|
b68e7c5fb4 | ||
|
|
e184e3230c | ||
|
|
532a1d135e | ||
|
|
f940d10ca4 | ||
|
|
11eb93b280 | ||
|
|
68f34b0cc4 | ||
|
|
d5c69dd634 | ||
|
|
b512a83a5c | ||
|
|
7c5f81dbde | ||
|
|
370f3ac523 | ||
|
|
4c75b784dd | ||
|
|
efa481f624 | ||
|
|
14c686b92f | ||
|
|
c503f9fb03 | ||
|
|
bdfaad90c5 | ||
|
|
e2fafe6e8c | ||
|
|
01902d9df3 | ||
|
|
95d953b3ca | ||
|
|
572ed17ac8 | ||
|
|
c148825631 | ||
|
|
3f92eb3025 | ||
|
|
01f6986d6e | ||
|
|
317236505d | ||
|
|
3836e9aa69 | ||
|
|
9436adc9ed | ||
|
|
c826435268 | ||
|
|
c748d162a7 | ||
|
|
3ad15178e0 | ||
|
|
a01aabba40 | ||
|
|
a196e03c8e | ||
|
|
00698ce31c | ||
|
|
e93d8904dc | ||
|
|
1a10f84e15 | ||
|
|
cb1b290ec2 | ||
|
|
2cb4129d03 | ||
|
|
942bf438f4 | ||
|
|
89bb47b913 | ||
|
|
c35f8e1664 | ||
|
|
4c8e1fe1f5 | ||
|
|
1c2e349cb7 | ||
|
|
41f551018b | ||
|
|
b71b8e8c34 | ||
|
|
ea91aec1fc | ||
|
|
381b62b65b | ||
|
|
4def172286 | ||
|
|
be30a63c68 | ||
|
|
d03fc90227 | ||
|
|
904def81f1 | ||
|
|
dd0fe0834a | ||
|
|
89f516c3c8 | ||
|
|
ff0028a7fd | ||
|
|
4a41cda137 | ||
|
|
208fd40ea1 | ||
|
|
ef78269c86 | ||
|
|
2469101020 | ||
|
|
ee6dbc0c7d | ||
|
|
7e2d4368be | ||
|
|
99c4509793 | ||
|
|
d102674c61 | ||
|
|
d5085938fa | ||
|
|
0dc43670c1 | ||
|
|
b16e4026dd | ||
|
|
dedfea75ef | ||
|
|
2044a8f8b2 | ||
|
|
d4cb886293 | ||
|
|
eb62730693 | ||
|
|
57531f147b | ||
|
|
ea9868404a | ||
|
|
1a2769c5bf | ||
|
|
2708542410 | ||
|
|
008eed2b67 | ||
|
|
97d45125ba | ||
|
|
f6ddfc7725 | ||
|
|
c59d84ff59 | ||
|
|
de37559249 | ||
|
|
498a79f418 | ||
|
|
179737df72 | ||
|
|
c448e2bdb9 | ||
|
|
d45f7044d9 | ||
|
|
7e556af27e | ||
|
|
45d9cd1bd6 | ||
|
|
567b14da00 | ||
|
|
e526ac1daa | ||
|
|
6be14ff514 | ||
|
|
7b1daefd53 | ||
|
|
cc61983763 | ||
|
|
b2444e454c | ||
|
|
d6e94f5df4 | ||
|
|
b6480f071a | ||
|
|
c5404455f3 | ||
|
|
651457304b | ||
|
|
953826c516 | ||
|
|
c658db6cfd | ||
|
|
699a7d25ff | ||
|
|
ddf209ebc5 | ||
|
|
9f632ad794 | ||
|
|
13eae2f5c4 | ||
|
|
56cf0da5cd | ||
|
|
301b76292e | ||
|
|
04747ca43d | ||
|
|
dbd976cd2f | ||
|
|
0166fec840 | ||
|
|
313776c724 | ||
|
|
87ea75809a | ||
|
|
8405a42cb4 | ||
|
|
6e3edff660 | ||
|
|
76eb301ace | ||
|
|
f65f927544 | ||
|
|
de5ea6c4bd | ||
|
|
770183f0ad | ||
|
|
cc5c7b5e26 | ||
|
|
f43118abee | ||
|
|
1e57f66e17 | ||
|
|
4f781690dd | ||
|
|
61dbd740b9 | ||
|
|
407a14b018 | ||
|
|
2df967d8be | ||
|
|
b336ee4e23 | ||
|
|
0cc023c4b0 | ||
|
|
e323edd373 | ||
|
|
bd08d60298 | ||
|
|
0138a37f3a | ||
|
|
d00a94e673 | ||
|
|
5b1f7406fb | ||
|
|
462b91a6d3 | ||
|
|
b861f8b48c | ||
|
|
1c9664febe | ||
|
|
48e4e2a120 | ||
|
|
bf82bf6cb5 | ||
|
|
86131a642d | ||
|
|
bd2034fa5b | ||
|
|
26ebf360e6 | ||
|
|
917da04b6d | ||
|
|
85b8acd1b7 | ||
|
|
699b28b9af | ||
|
|
6739b42162 | ||
|
|
657f62228d | ||
|
|
cd547c168d | ||
|
|
d85ff96a77 | ||
|
|
31e5617e82 | ||
|
|
80377ccc93 | ||
|
|
bd88b31a0c | ||
|
|
01d20b8162 | ||
|
|
ecc7c813fd | ||
|
|
c0ca83596d | ||
|
|
62e4941ad4 | ||
|
|
96a2e9133f | ||
|
|
6b6f6a9055 | ||
|
|
9907e92f66 | ||
|
|
14f3e23216 | ||
|
|
7deeb14b7e | ||
|
|
fd0212f474 | ||
|
|
4a708b7afe | ||
|
|
b7ef3f61be | ||
|
|
8e1f7cadca | ||
|
|
71c40d6122 | ||
|
|
6544bc27fe | ||
|
|
62ac98157e | ||
|
|
929ede64dd | ||
|
|
9f5d52fc93 | ||
|
|
2616674785 | ||
|
|
52c1ec1150 | ||
|
|
d3bf54ad85 | ||
|
|
2bc928849e | ||
|
|
8088b9054a | ||
|
|
d30170f6cb | ||
|
|
1cb2d78863 | ||
|
|
54318b8dfd | ||
|
|
2fc8cabb7d | ||
|
|
8427a29843 | ||
|
|
3bb48bbc64 | ||
|
|
83fdb1c38a | ||
|
|
5d6ff4cc47 | ||
|
|
14235886f5 | ||
|
|
2968bcd9a0 | ||
|
|
1df3fd1185 | ||
|
|
dd9a498813 | ||
|
|
81401186f5 | ||
|
|
2bb0a652e8 | ||
|
|
6cc5c9e6b0 | ||
|
|
c0e3303e49 | ||
|
|
c7d8da1b69 | ||
|
|
184debe26f | ||
|
|
f1e6a9b708 | ||
|
|
017d4ec40b | ||
|
|
b41d31423c | ||
|
|
3a9ca2ff04 | ||
|
|
ddb037c70c | ||
|
|
703cae55e8 | ||
|
|
4ee2a523f1 | ||
|
|
587ed9fa9c | ||
|
|
7214b0fdd5 | ||
|
|
41ba33da43 | ||
|
|
9a268c3f4e | ||
|
|
0393f2eeb2 | ||
|
|
4f02ce7880 | ||
|
|
75b24e6035 | ||
|
|
1739e74dad | ||
|
|
33fd644931 | ||
|
|
cfdcc9bea0 | ||
|
|
f986e4356c | ||
|
|
6f2db198ec | ||
|
|
d8ad6eedd8 | ||
|
|
b188d2bc95 | ||
|
|
61a32163a1 | ||
|
|
7b1b97d430 | ||
|
|
e42fd2c3ee | ||
|
|
486bc21908 | ||
|
|
4bb997b5ee | ||
|
|
76f7861cd9 | ||
|
|
36b558ab88 | ||
|
|
551a749c5d | ||
|
|
6609d3f86a | ||
|
|
5c59766538 | ||
|
|
9bc97fab98 | ||
|
|
e46d551baa | ||
|
|
2b38ddc764 | ||
|
|
08772d3816 | ||
|
|
1d7135bb51 | ||
|
|
6bc6e588b0 | ||
|
|
199e827566 | ||
|
|
23af7e44ee | ||
|
|
b640093f63 | ||
|
|
c3d7dd14c9 | ||
|
|
1150ade401 | ||
|
|
8b7737141f | ||
|
|
6f4354a82f | ||
|
|
1042c00188 | ||
|
|
9739985141 | ||
|
|
3ddfb2f411 | ||
|
|
4a66cb7a24 | ||
|
|
f2012e396c | ||
|
|
2f14700f10 | ||
|
|
49e226e4c1 | ||
|
|
8263eb0e5e | ||
|
|
c1da544a9d | ||
|
|
e74df56e0c | ||
|
|
111b50b437 | ||
|
|
0bf22d3406 | ||
|
|
61bb2db3d1 | ||
|
|
9fff261779 | ||
|
|
b627ee7516 | ||
|
|
a2ba11277e | ||
|
|
d1a0f7954b | ||
|
|
74e6264b47 | ||
|
|
ced1522c61 | ||
|
|
426845b192 | ||
|
|
0be833e73c | ||
|
|
64b1c9171c | ||
|
|
ee33bc365e | ||
|
|
bc02666ff0 | ||
|
|
9c4a7a7a27 | ||
|
|
3021e1b20d | ||
|
|
99018fba32 | ||
|
|
84e14c9f80 | ||
|
|
2a375ed98d | ||
|
|
0e9c8ce944 | ||
|
|
2312513a27 | ||
|
|
2d495b070f | ||
|
|
1f1d40e166 | ||
|
|
90e2b2e71f | ||
|
|
08b3d5e316 | ||
|
|
1ac475d48f | ||
|
|
6f874baa54 | ||
|
|
a9ffe80d27 | ||
|
|
83b4f3341e | ||
|
|
801704f065 |
183
.clang-format
183
.clang-format
@@ -1,7 +1,178 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: true
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequires: false
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Right
|
||||
PPIndentWidth: -1
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 4
|
||||
UseCRLF: false
|
||||
UseTab: Always
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
||||
BasedOnStyle: LLVM
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
UseTab: Always
|
||||
ColumnLimit: 100
|
||||
Language: Cpp
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -21,7 +21,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKER_REGISTRY_URL: tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||
DOCKER_REGISTRY_USERNAME: ucentral
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -11,7 +11,7 @@ defaults:
|
||||
|
||||
jobs:
|
||||
helm-package:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
HELM_REPO_URL: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||
HELM_REPO_USERNAME: ucentral
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -21,10 +21,12 @@ _deps
|
||||
/docker-compose/.env
|
||||
/docker-compose/.env_*
|
||||
/cmake-build/
|
||||
/uploads/
|
||||
test_scripts/curl/token.json
|
||||
.vscode/c_cpp_properties.json
|
||||
test_scripts/curl/result.json
|
||||
*.swp
|
||||
helm/charts/*
|
||||
!helm/charts/.gitkeep
|
||||
/portal-test/
|
||||
/src/ow_version.h
|
||||
.vscode/*
|
||||
|
||||
2
.idea/.gitignore
generated
vendored
2
.idea/.gitignore
generated
vendored
@@ -6,3 +6,5 @@
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# GitHub Copilot persisted chat sessions
|
||||
/copilot/chatSessions
|
||||
|
||||
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.9 (wlan-cloud-ucentralgw)" />
|
||||
</component>
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
<component name="CidrRootsConfiguration">
|
||||
<excludeRoots>
|
||||
|
||||
2
.idea/ucentral.iml
generated
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>
|
||||
196
BUILDING.md
Normal file
196
BUILDING.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Building from source
|
||||
In order to build OWGW, you will need to install its dependencies, which includes the following:
|
||||
- cmake
|
||||
- boost
|
||||
- POCO 1.10.1 or later
|
||||
- a C++17 compiler
|
||||
- openssl
|
||||
- libpq-dev (PortgreSQL development libraries)
|
||||
- mysql-client (MySQL client)
|
||||
- librdkafka
|
||||
- cppkafka
|
||||
|
||||
The build is done in 2 parts. The first part is to build a local copy of the framework tailored to your environment. This
|
||||
framework is called [Poco](https://github.com/pocoproject/poco). The version used in this project has a couple of fixes
|
||||
from the master copy needed for cmake. Please use the version of this [Poco fix](https://github.com/Telecominfraproject/wlan-cloud-lib-poco). Building
|
||||
Poco may take several minutes depending on the platform you are building on.
|
||||
|
||||
## Ubuntu
|
||||
These instructions have proven to work on Ubuntu 20.4.
|
||||
```bash
|
||||
sudo apt install git cmake g++ libssl-dev libmariadb-dev \
|
||||
libpq-dev libaprutil1-dev apache2-dev libboost-all-dev \
|
||||
librdkafka-dev // default-libmysqlclient-dev \
|
||||
nlohmann-json-dev
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
|
||||
cd poco
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
|
||||
cd cppkafka
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
|
||||
cd valijson
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/fmtlib/fmt --branch 9.0.0 /fmtlib
|
||||
cd fmtlib
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
cd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make -j 8
|
||||
cd ../..
|
||||
```
|
||||
|
||||
## Fedora
|
||||
The following instructions have proven to work on Fedora 33
|
||||
```bash
|
||||
sudo yum install cmake g++ openssl-devel mysql-devel mysql apr-util-devel boost boost-devel \
|
||||
yaml-cpp-devel lua-devel
|
||||
sudo dnf install postgresql.x86_64 librdkafka-devel
|
||||
sudo dnf install postgresql-devel json-devel
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
|
||||
cd poco
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
|
||||
cd cppkafka
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
|
||||
cd valijson
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
cd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make
|
||||
cd ../..
|
||||
```
|
||||
|
||||
## macOS Build
|
||||
The following instructions have proven to work on macOS Big Sur. You need to install [Homebrew](https://brew.sh/). You must also have installed [XCode for OS X](https://www.freecodecamp.org/news/how-to-download-and-install-xcode/).
|
||||
```bash
|
||||
brew install openssl \
|
||||
cmake \
|
||||
libpq \
|
||||
mysql-client \
|
||||
apr \
|
||||
apr-util \
|
||||
boost \
|
||||
yaml-cpp \
|
||||
postgresql \
|
||||
librdkafka \
|
||||
nlohmann-json \
|
||||
fmt
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
|
||||
pushd poco
|
||||
mkdir cmake-build
|
||||
push cmake-build
|
||||
cmake -DOPENSSL_ROOT_DIR=</path/to/openssl> -DENABLE_NETSSL=1 -DENABLE_JWT=1 -DENABLE_CRYPTO=1 ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
popd
|
||||
popd
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
|
||||
pushd cppkafka
|
||||
mkdir cmake-build
|
||||
pushd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
popd
|
||||
popd
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
|
||||
pushd valijson
|
||||
mkdir cmake-build
|
||||
pushd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
popd
|
||||
popd
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
pushd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
pushd cmake-build
|
||||
cmake ..
|
||||
make -j
|
||||
popd
|
||||
popd
|
||||
```
|
||||
|
||||
## Raspberry
|
||||
The build on a rPI takes a while. You can shorten that build time and requirements by disabling all the larger database
|
||||
support. You can build with only SQLite support by not installing the packages for PostgreSQL, and MySQL by
|
||||
adding -DSMALL_BUILD=1 on the cmake build line.
|
||||
|
||||
```bash
|
||||
sudo apt install git cmake g++ libssl-dev libaprutil1-dev apache2-dev \
|
||||
libboost-all-dev libyaml-cpp-dev
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
|
||||
cd poco
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
cd ../..
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
cd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake -DSMALL_BUILD=1 ..
|
||||
make
|
||||
cd ../..
|
||||
```
|
||||
77
CLI.md
77
CLI.md
@@ -7,139 +7,139 @@ cli help
|
||||
|
||||
## The commands
|
||||
|
||||
### getdevice <serial>
|
||||
### getdevice `serial`
|
||||
Get the device JSON document.
|
||||
|
||||
### deletedevice <serial>
|
||||
### deletedevice `serial`
|
||||
Delete the device.
|
||||
|
||||
### createdevice <serial> <cfg> <MAC>
|
||||
### createdevice `serial` `cfg` `MAC`
|
||||
Create a device using the default configuration.
|
||||
- `serial`: device serial number
|
||||
- `cfg`: JSON config file name
|
||||
- `MAC`: string MAC Address
|
||||
|
||||
### getdevicestatus <serial>
|
||||
### getdevicestatus `serial`
|
||||
Get the device status JSON document.
|
||||
- `serial`: device serial number
|
||||
|
||||
### getstats <serial>
|
||||
### getstats `serial`
|
||||
Get statistics for the device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### gethealthchecks <serial>
|
||||
### gethealthchecks `serial`
|
||||
Get healthchecks for a device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### newesthealthchecks <serial>
|
||||
### newesthealthchecks `serial`
|
||||
Get newest healthchecks for a device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### lasthealthcheck <serial>
|
||||
### lasthealthcheck `serial`
|
||||
Get the last healthcheck
|
||||
- `serial`: device serial number
|
||||
|
||||
### getcapababilities <serial>
|
||||
### getcapababilities `serial`
|
||||
Get the device capabilities JSON document.
|
||||
- `serial`: device serial number
|
||||
|
||||
### deletecapababilities <serial>
|
||||
### deletecapababilities `serial`
|
||||
Delete the device capabilities JSON.
|
||||
- `serial`: device serial number
|
||||
|
||||
### reboot <serial>
|
||||
### reboot `serial`
|
||||
Reboot the device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### upgrade <serial> <URI>
|
||||
### upgrade `serial` `URI`
|
||||
Do firmware upgrade for a device.
|
||||
- `serial`: device serial number
|
||||
- `URI`: complete URI where the upgrade file exists. No validation is performed.
|
||||
|
||||
### leds <serial> <pattern> <duration>
|
||||
### leds `serial` `pattern` `duration`
|
||||
Activate LEDs a device.
|
||||
- `serial`: device serial number
|
||||
- `pattern`: on/off/blink
|
||||
- `duration`: number in seconds
|
||||
|
||||
### configure <serial> <cfg>
|
||||
### configure `serial` `cfg`
|
||||
Change configuration for a device.
|
||||
- `serial`: device serial number
|
||||
- `cfg`: JSON config file name
|
||||
|
||||
### factory <serial> <keep_redirector>
|
||||
### factory `serial` `keep_redirector`
|
||||
Do factory reset for device.
|
||||
- `serial`: device serial number
|
||||
- `keep_redirector`: true=keep redirector, false=reset redirector
|
||||
|
||||
### request <serial> <message>
|
||||
### request `serial` `message`
|
||||
Force a message from the device.
|
||||
- `serial`: device serial number
|
||||
- `message`: state or healthcheck
|
||||
|
||||
### wifiscan <serial> <verbose>
|
||||
### wifiscan `serial` `verbose`
|
||||
Do wifiscan for a device.
|
||||
- `serial`: device serial number
|
||||
- `verbose`: verbose=true/false
|
||||
|
||||
### telemetry <serial>
|
||||
### telemetry `serial`
|
||||
Start `telemetry` stream for a device.
|
||||
|
||||
### trace <serial> <duration> <network>
|
||||
### trace `serial` `duration` `network`
|
||||
Launch a remote trace for a device.
|
||||
- `serial`: device serial number
|
||||
- `duration`: number in seconds
|
||||
- `network`: which network to perform trace on: lan or wan
|
||||
|
||||
### getcommand <command-uuid>
|
||||
### getcommand `command-uuid`
|
||||
Get the command JSON document.
|
||||
- `command-uuid`: command UUID
|
||||
|
||||
### deletecommand <command-uuid>
|
||||
### deletecommand `command-uuid`
|
||||
Delete the command.
|
||||
- `command-uuid`: command UUID
|
||||
|
||||
### newestcommands <serial>
|
||||
### newestcommands `serial`
|
||||
Get the newest commands for a device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### listdevices
|
||||
List devices.
|
||||
|
||||
### listcommands <serial>
|
||||
### listcommands `serial`
|
||||
List commands for a specific device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### deletecommands <serial>
|
||||
### deletecommands `serial`
|
||||
Delete commands for a device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### getlogs <serial>
|
||||
### getlogs `serial`
|
||||
Get logs for the device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### newestlogs <serial>
|
||||
### newestlogs `serial`
|
||||
Get the latest logs for the device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### deletelogs <serial>
|
||||
### deletelogs `serial`
|
||||
Delete logs for the device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### eventqueue <serial>
|
||||
### eventqueue `serial`
|
||||
Request event queue for the device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### listdefaultconfigs
|
||||
List default configurations.
|
||||
|
||||
### createdefaultconfig <name> <ids> <cfg>
|
||||
### createdefaultconfig `name` `ids` `cfg`
|
||||
Create a default configuration
|
||||
- `name`: unique name, no spaces
|
||||
- `ids`: comma separated list of models
|
||||
- `cfg`: JSON config file name
|
||||
|
||||
### addblacklistdevice <serial> <reason>
|
||||
### addblacklistdevice `serial` `reason`
|
||||
Add a device to the black list
|
||||
- `serial`: serial number of the device to add
|
||||
- `reason`: reason for blacklisting
|
||||
@@ -147,7 +147,7 @@ Add a device to the black list
|
||||
### getblacklist
|
||||
List all blacklisted devices
|
||||
|
||||
### deleteblacklistdevice <serial>
|
||||
### deleteblacklistdevice `serial`
|
||||
Add a device to the black list.
|
||||
- `serial`: device serial number
|
||||
|
||||
@@ -157,7 +157,7 @@ Get the number of devices in the DB.
|
||||
### deviceserialnumbers
|
||||
Get only the serial numbers.
|
||||
|
||||
### selectdevices <serial_list>
|
||||
### selectdevices `serial_list`
|
||||
Get a list of devices based on a list.
|
||||
- `serial_list`: serial numbers (must be comma separated).
|
||||
|
||||
@@ -179,27 +179,30 @@ Get the list of subsystems.
|
||||
### systeminfo
|
||||
Get basic system information.
|
||||
|
||||
### reloadsubsystem <subsystem name>
|
||||
### reloadsubsystem `subsystem name`
|
||||
Reload the configuration for a subsystem.### getfile <uuid>
|
||||
Get the file associated with trace command <uuid>.
|
||||
- `uuid`: UUID of file to retrieve
|
||||
|
||||
### rtty <serial>
|
||||
### rtty `serial number`
|
||||
Get the details for an rtty session.
|
||||
- `serial`: device serial number
|
||||
|
||||
### lifetimestats <serial>
|
||||
### lifetimestats `serial number`
|
||||
Get the lifetime stats counters for a device
|
||||
- `serial`: device serial number
|
||||
|
||||
### laststats <serial>
|
||||
### laststats `serial number`
|
||||
Get the last statistics for a device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### neweststats <serial>
|
||||
### neweststats `serial number`
|
||||
Get the newest statistics for a device.
|
||||
- `serial`: device serial number
|
||||
|
||||
### deviceping `serial number`
|
||||
This will return you the end-to-end latency from command-line to return value.
|
||||
|
||||
## Notes
|
||||
To pass additional flags to the CURL command, create an environment variable called FLAGS and git ve the values you
|
||||
want. For example, for force all call to use IPv6, set FLAGS=\"-6\", for verbose mode and IPv6, set FLAGS=\"-6 -v\"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(owgw VERSION 2.8.0)
|
||||
project(owgw VERSION 4.1.1)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
if(UNIX AND APPLE)
|
||||
set(OPENSSL_ROOT_DIR /usr/local/opt/openssl)
|
||||
@@ -49,13 +50,13 @@ 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)
|
||||
find_package(fmt REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(nlohmann_json_schema_validator REQUIRED)
|
||||
# find_package(valijson REQUIRED)
|
||||
|
||||
if(SMALL_BUILD)
|
||||
find_package(Poco REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite)
|
||||
@@ -144,9 +145,15 @@ 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
|
||||
@@ -170,7 +177,7 @@ add_executable( owgw
|
||||
src/SDKcalls.cpp
|
||||
src/SDKcalls.h
|
||||
src/StateUtils.cpp src/StateUtils.h
|
||||
src/AP_WS_ReactorPool.h
|
||||
src/AP_WS_Reactor_Pool.h
|
||||
src/AP_WS_Connection.h
|
||||
src/AP_WS_Connection.cpp
|
||||
src/TelemetryClient.h src/TelemetryClient.cpp
|
||||
@@ -194,8 +201,20 @@ add_executable( owgw
|
||||
src/AP_WS_Process_deviceupdate.cpp
|
||||
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.cpp src/SignatureMgr.h)
|
||||
src/RADIUS_Destination.h
|
||||
src/UI_GW_WebSocketNotifications.cpp src/UI_GW_WebSocketNotifications.h
|
||||
src/framework/RESTAPI_SystemConfiguration.h
|
||||
src/ScriptManager.cpp src/ScriptManager.h
|
||||
src/SignatureMgr.h
|
||||
src/AP_WS_Process_event.cpp
|
||||
src/AP_WS_Process_wifiscan.cpp
|
||||
src/AP_WS_Process_alarm.cpp
|
||||
src/GWKafkaEvents.cpp src/GWKafkaEvents.h
|
||||
src/RegulatoryInfo.cpp src/RegulatoryInfo.h
|
||||
src/RADIUSSessionTracker.cpp src/RADIUSSessionTracker.h
|
||||
src/libs/Scheduler.h src/libs/InterruptableSleep.h src/libs/ctpl_stl.h src/libs/Cron.h
|
||||
src/GenericScheduler.cpp src/GenericScheduler.h src/framework/default_device_types.h src/AP_WS_Process_rebootLog.cpp src/AP_WS_ConfigAutoUpgrader.cpp src/AP_WS_ConfigAutoUpgrader.h src/RESTAPI/RESTAPI_default_firmwares.cpp src/RESTAPI/RESTAPI_default_firmwares.h src/RESTAPI/RESTAPI_default_firmware.cpp src/RESTAPI/RESTAPI_default_firmware.h src/storage/storage_def_firmware.cpp src/firmware_revision_cache.h src/sdks/sdk_fms.h
|
||||
src/AP_WS_LookForUpgrade.cpp)
|
||||
|
||||
if(NOT SMALL_BUILD)
|
||||
|
||||
@@ -207,15 +226,17 @@ INSTALL(TARGETS owgw
|
||||
|
||||
target_link_libraries(owgw PUBLIC
|
||||
${Poco_LIBRARIES}
|
||||
${ZLIB_LIBRARIES})
|
||||
${ZLIB_LIBRARIES}
|
||||
)
|
||||
|
||||
if(NOT SMALL_BUILD)
|
||||
target_link_libraries(owgw PUBLIC
|
||||
${MySQL_LIBRARIES} ${ZLIB_LIBRARIES}
|
||||
CppKafka::cppkafka
|
||||
nlohmann_json_schema_validator
|
||||
fmt::fmt
|
||||
)
|
||||
${MySQL_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
CppKafka::cppkafka
|
||||
fmt::fmt
|
||||
resolv
|
||||
)
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(owgw PUBLIC PocoJSON)
|
||||
endif()
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
# Coding Style
|
||||
I just want ot make sure we all follow the same rules when contributing
|
||||
code back into this tree.
|
||||
|
||||
## Version of C++
|
||||
This project is based on the C++17 standard. If compiles as-is on most platforms
|
||||
using either clang or g++. Do not use C++21 features for now. I would love to do some
|
||||
of the new stuff but let's wait for these features to be available on
|
||||
all compilers first. `coroutine` has to wait.
|
||||
|
||||
## Naming
|
||||
Naming of pretty much anything uses Pascal naming. I know... You might not be a big fan or have
|
||||
fallen to the JS gods and use camelNaming. Well, let's all make an effort to keep
|
||||
this coherent. Member variable naming adds a `_` at the end of the vars. Try to
|
||||
keep this standard going. Sometimes you must override a base class function and then of course
|
||||
you need to follow the base class. Let's be real...
|
||||
|
||||
## File Sizes
|
||||
Do you best to keep your file sizes < 300 lines. It just makes the code more readable
|
||||
and shortens compile times.
|
||||
|
||||
## This is a cmake project
|
||||
This is a cmake project and you need to adhere to the cmake rules. If you need
|
||||
to add a package to the CMakeList, you need to ensure that the package is available
|
||||
on all required platforms and compiles. Remember that this project runs on Linux, OS X,
|
||||
and the Raspberry PI.
|
||||
|
||||
## Licensed packages
|
||||
When adding a package, you must also state the licensing for the package. MIT, BSD, Apache licenses
|
||||
are acceptable. No commercial licenses are allowed.
|
||||
|
||||
|
||||
415
CONFIGURATION.md
Normal file
415
CONFIGURATION.md
Normal file
@@ -0,0 +1,415 @@
|
||||
# Controller Configuration Parameters
|
||||
|
||||
## OWGW Specific Parameters
|
||||
### Websocket parameters
|
||||
This is the crucial section. I bet that 97.4% of all your problems will come from here, and it's boring. So put some good music on,
|
||||
give the kids the iPad, get a cup of coffee, and pay attention. Every field will be explained.
|
||||
|
||||
```properties
|
||||
ucentral.websocket.host.0.backlog = 500
|
||||
ucentral.websocket.host.0.rootca = $OWGW_ROOT/certs/root.pem
|
||||
ucentral.websocket.host.0.issuer = $OWGW_ROOT/certs/issuer.pem
|
||||
ucentral.websocket.host.0.cert = $OWGW_ROOT/certs/websocket-cert.pem
|
||||
ucentral.websocket.host.0.key = $OWGW_ROOT/certs/websocket-key.pem
|
||||
ucentral.websocket.host.0.clientcas = $OWGW_ROOT/certs/clientcas.pem
|
||||
ucentral.websocket.host.0.cas = $OWGW_ROOT/certs/cas
|
||||
ucentral.websocket.host.0.address = *
|
||||
ucentral.websocket.host.0.port = 15002
|
||||
ucentral.websocket.host.0.security = strict
|
||||
ucentral.websocket.host.0.key.password = mypassword
|
||||
ucentral.websocket.maxreactors = 20
|
||||
```
|
||||
|
||||
#### ucentral.websocket.host.0.backlog
|
||||
This is the number of concurrent devices you are expecting to call all at once. Not the current number of devices. This is how many will connect in the same exact second.
|
||||
Take the total number of devices you have and divide by 100. That's a good rule of thumb. Never go above 500.
|
||||
#### ucentral.websocket.host.0.rootca
|
||||
This is the root file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem)
|
||||
#### ucentral.websocket.host.0.issuer
|
||||
This is the issuer file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/issuer.pem)
|
||||
#### ucentral.websocket.host.0.cert
|
||||
This is a `pem` file that you will receive from Digicert for the gateway itself. This is the certificate for the gateway.
|
||||
#### ucentral.websocket.host.0.key
|
||||
This is a `pem` file that you will receive from Digicert for the gateway itself. The is the private key for the gateway.
|
||||
#### ucentral.websocket.host.0.clientcas
|
||||
This is a `pem` file that contains both the issuer and the root CA certificates. You can find it You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/clientcas.pem)
|
||||
#### ucentral.websocket.host.0.cas
|
||||
This is a directory where you will copy your own `cert.pem`, the `root.pem`, and the `issuer.pem` files.
|
||||
#### ucentral.websocket.host.0.address
|
||||
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
|
||||
#### ucentral.websocket.host.0.port
|
||||
Leave to 15002 for now.
|
||||
#### ucentral.websocket.host.0.security
|
||||
Leave this as strict for now for devices.
|
||||
#### ucentral.websocket.host.0.key.password
|
||||
If you key file uses a password, please enter it here.
|
||||
#### ucentral.websocket.maxreactors
|
||||
A single reactor can handle between 1000-2000 devices. Never leave this smaller than 5 or larger than 50.
|
||||
|
||||
### File uploader parameters
|
||||
Certain commands may require the Access Point to upload a file into the Controller. For this reason, there is a special embedded HTTP
|
||||
server to receive these files.
|
||||
|
||||
```properties
|
||||
openwifi.fileuploader.host.0.backlog = 100
|
||||
openwifi.fileuploader.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
|
||||
openwifi.fileuploader.host.0.security = relaxed
|
||||
openwifi.fileuploader.host.0.address = *
|
||||
openwifi.fileuploader.host.0.name = ucentral.dpaas.arilia.com
|
||||
openwifi.fileuploader.host.0.port = 16003
|
||||
openwifi.fileuploader.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
|
||||
openwifi.fileuploader.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
|
||||
openwifi.fileuploader.host.0.key.password = mypassword
|
||||
openwifi.fileuploader.path = $OWGW_ROOT/uploads
|
||||
openwifi.fileuploader.maxsize = 10000
|
||||
openwifi.fileuploader.uri = https://ucentral.dpaas.arilia.com:16003
|
||||
```
|
||||
|
||||
#### openwifi.fileuploader.host.0.backlog
|
||||
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
|
||||
#### openwifi.fileuploader.host.0.rootca
|
||||
This is the root file of your own certificate CA in `pem` format.
|
||||
#### openwifi.fileuploader.host.0.cert
|
||||
This is your own server certificate in `pem` format..
|
||||
#### openwifi.fileuploader.host.0.key
|
||||
This is the private key associated with your own certificate in `pem` format.
|
||||
#### openwifi.intfileuploaderernal.host.0.address
|
||||
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
|
||||
#### openwifi.fileuploader.host.0.port
|
||||
The port on which the REST API server is listening. By default, this is 16003.
|
||||
#### openwifi.fileuploader.host.0.security
|
||||
Leave this as `relaxed` for now for devices.
|
||||
#### openwifi.fileuploader.host.0.key.password
|
||||
If you key file uses a password, please enter it here.
|
||||
#### openwifi.fileuploader.path
|
||||
This is the location where the files will be stored temporarily before processing. This `path` must exist.
|
||||
#### openwifi.fileuploader.maxsize
|
||||
This is the maximum uploaded file size. The default maximum size if 10MB. This size is in KB.
|
||||
#### openwifi.fileuploader.uri
|
||||
This is the URI that will be passed to the AP. You must make sure that the AP can resolve this URI.
|
||||
|
||||
### OUI Service
|
||||
The controller has a built-in OUI resolver for MAC addresses. The GW will periodically load this file to obtain the latest.
|
||||
This is ths URI for this file.
|
||||
```properties
|
||||
oui.download.uri = https://standards-oui.ieee.org/oui/oui.txt
|
||||
```
|
||||
|
||||
### Data-model Source
|
||||
The gateway can make use of the latest uCentral data-model or use the built-in model. These 2 parameters allow you to
|
||||
choose which method you want. If you select the internal method, the URI is ignored. If for some reason you choose
|
||||
the on-line data-model from the URI and the URI is not reachable, the system will fall back on the internal model.
|
||||
```properties
|
||||
ucentral.datamodel.internal = true
|
||||
ucentral.datamodel.uri = https://raw.githubusercontent.com/Telecominfraproject/wlan-ucentral-schema/main/ucentral.schema.json
|
||||
```
|
||||
|
||||
### Command Manager
|
||||
The command manager is responsible for managing command sent and responses received with the APs. Several parameters allow you
|
||||
to fine tune its behaviour. Unless you have some particular reasons to change tem the defaults are usually just fine.
|
||||
```properties
|
||||
command.timeout = 14400
|
||||
command.retry = 120
|
||||
command.janitor = 120
|
||||
command.queue = 30
|
||||
```
|
||||
#### command.timeout
|
||||
How long will the GW wait in seconds before considering a commands has timed out.
|
||||
|
||||
#### command.retry
|
||||
How long between command retries.
|
||||
|
||||
#### command.janitor
|
||||
How long between outstanding RPC clean-ups.
|
||||
|
||||
#### command.queue
|
||||
How long should te gateway wait between running its queue.
|
||||
|
||||
### IP to Country Parameters
|
||||
The controller has the ability to find the location of the IP of each Access Points. This uses an external IP location service. Currently,
|
||||
the controller supports 3 services. Please note that these services will require to obtain an API key or token, and these may cause you to incur
|
||||
additional fees. Here is the list of the services supported:
|
||||
- ip2location: ip2location.com
|
||||
- ipdata: ipdata.co
|
||||
- ipinfo: ipinfo.io
|
||||
|
||||
```properties
|
||||
iptocountry.default = US
|
||||
iptocountry.provider = ipinfo
|
||||
#iptocountry.provider = ipdata
|
||||
#iptocountry.provider = ip2location
|
||||
iptocountry.ipinfo.token =
|
||||
iptocountry.ipdata.apikey =
|
||||
iptocountry.ip2location.apikey =
|
||||
```
|
||||
|
||||
#### iptocountry.default
|
||||
This is the country code to be used if no information can be found at one of the providers or you have not configured any of the providers.
|
||||
|
||||
#### iptocountry.provider
|
||||
You must select onf of the possible services and the fill the appropriate token or api key parameter.
|
||||
|
||||
### Provisioning link
|
||||
This parameter tells the controller how to behave when it receives a request from a device for the first time. In this case, we tell
|
||||
the controller to look at the provisioning service first, then apply any local configurations.
|
||||
```properties
|
||||
autoprovisioning.process = prov,default
|
||||
```
|
||||
|
||||
### Restricted Device Signature Manager
|
||||
If are using restricted devices, then you can include different keys for each vendor who provided
|
||||
you with their information. This allows the controller to automatically sign requests to the device. You can have as many vendors
|
||||
as it is necessary.
|
||||
|
||||
```properties
|
||||
signature.manager.0.key.public = $OWGW_ROOT/certs/signatures/test1-public-key.pem
|
||||
signature.manager.0.key.private = $OWGW_ROOT/certs/signatures/test1-private-key.pem
|
||||
signature.manager.0.vendor = test1
|
||||
signature.manager.1.key.public = $OWGW_ROOT/certs/signatures/test2-public-key.pem
|
||||
signature.manager.1.key.private = $OWGW_ROOT/certs/signatures/test2-private-key.pem
|
||||
signature.manager.1.vendor = test2
|
||||
```
|
||||
|
||||
### OWLS Simulator ID
|
||||
If you plan on using OWLS (OpenWifi Load Simulator), then you will need to put your Simulator ID right here.
|
||||
This ID must be obtained from TIP.
|
||||
```properties
|
||||
simulatorid = 53494dFFEEDD
|
||||
```
|
||||
|
||||
### RTTY Service
|
||||
The controller comes with the ability to run an RTTY service. The service can either be internal (the prefered choice)
|
||||
or external. If you decide to use the internal RTTY, the you only need to specify `rtty.internal = true`. If you choose
|
||||
to use an external RTTY, you must specify the remainder of the parameters.
|
||||
|
||||
```properties
|
||||
rtty.internal = true
|
||||
rtty.enabled = true
|
||||
rtty.server = rtty-tip.arilia.com
|
||||
rtty.port = 5912
|
||||
rtty.token = 96181c567b4d0d98c50f127230068fa8
|
||||
rtty.timeout = 60
|
||||
rtty.viewport = 5913
|
||||
rtty.assets = $OWGW_ROOT/rtty_ui
|
||||
```
|
||||
|
||||
### RADIUS proxy config
|
||||
If you are going to use the buil-in RADIUS proxy service, you need to enable this parameter and provide
|
||||
the ports for you PROXY.
|
||||
```properties
|
||||
radius.proxy.enable = false
|
||||
radius.proxy.accounting.port = 1813
|
||||
radius.proxy.authentication.port = 1812
|
||||
radius.proxy.coa.port = 3799
|
||||
radsec.keepalive = 120
|
||||
```
|
||||
|
||||
### Auto Archiver Parameters
|
||||
The auto archiver is responsible for removing all stale data. The default is to remove old data after 7 days.
|
||||
```properties
|
||||
archiver.enabled = true
|
||||
archiver.schedule = 03:00
|
||||
archiver.db.0.name = healthchecks
|
||||
archiver.db.0.keep = 7
|
||||
archiver.db.1.name = statistics
|
||||
archiver.db.1.keep = 7
|
||||
archiver.db.2.name = devicelogs
|
||||
archiver.db.2.keep = 7
|
||||
archiver.db.3.name = commandlist
|
||||
archiver.db.3.keep = 7
|
||||
```
|
||||
|
||||
## Generic OpenWiFi SDK parameters
|
||||
### REST API External parameters
|
||||
These are the parameters required for the configuration of the external facing REST API server
|
||||
```properties
|
||||
openwifi.restapi.host.0.backlog = 100
|
||||
openwifi.restapi.host.0.security = relaxed
|
||||
openwifi.restapi.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
|
||||
openwifi.restapi.host.0.address = *
|
||||
openwifi.restapi.host.0.port = 16004
|
||||
openwifi.restapi.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
|
||||
openwifi.restapi.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
|
||||
openwifi.restapi.host.0.key.password = mypassword
|
||||
```
|
||||
|
||||
#### openwifi.restapi.host.0.backlog
|
||||
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
|
||||
#### openwifi.restapi.host.0.rootca
|
||||
This is the root file of your own certificate CA in `pem` format.
|
||||
#### openwifi.restapi.host.0.cert
|
||||
This is your own server certificate in `pem` format..
|
||||
#### openwifi.restapi.host.0.key
|
||||
This is the private key associated with your own certificate in `pem` format.
|
||||
#### openwifi.restapi.host.0.address
|
||||
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
|
||||
#### openwifi.restapi.host.0.port
|
||||
The port on which the REST API server is listening. By default, this is 16002.
|
||||
#### openwifi.restapi.host.0.security
|
||||
Leave this as `relaxed` for now for devices.
|
||||
#### openwifi.restapi.host.0.key.password
|
||||
If you key file uses a password, please enter it here.
|
||||
|
||||
### REST API Intra microservice parameters
|
||||
The following parameters describe the configuration for the inter-microservice HTTP server. You may use the same certificate/key
|
||||
you are using for your extenral server or another certificate.
|
||||
```properties
|
||||
openwifi.internal.restapi.host.0.backlog = 100
|
||||
openwifi.internal.restapi.host.0.security = relaxed
|
||||
openwifi.internal.restapi.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
|
||||
openwifi.internal.restapi.host.0.address = *
|
||||
openwifi.internal.restapi.host.0.port = 17004
|
||||
openwifi.internal.restapi.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
|
||||
openwifi.internal.restapi.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
|
||||
openwifi.internal.restapi.host.0.key.password = mypassword
|
||||
```
|
||||
|
||||
#### openwifi.internal.host.0.backlog
|
||||
This is the number of concurrent REST API calls that maybe be kept in the backlog for processing. That's a good rule of thumb. Never go above 500.
|
||||
#### openwifi.internal.host.0.rootca
|
||||
This is the root file of your own certificate CA in `pem` format.
|
||||
#### openwifi.internal.host.0.cert
|
||||
This is your own server certificate in `pem` format..
|
||||
#### openwifi.internal.host.0.key
|
||||
This is the private key associated with your own certificate in `pem` format.
|
||||
#### openwifi.internal.host.0.address
|
||||
Leve this a `*` in the case you want to bind to all interfaces on your gateway host or select the address of a single interface.
|
||||
#### openwifi.internal.host.0.port
|
||||
The port on which the REST API server is listening. By default, this is 17002.
|
||||
#### openwifi.internal.host.0.security
|
||||
Leave this as `relaxed` for now for devices.
|
||||
#### openwifi.internal.host.0.key.password
|
||||
If you key file uses a password, please enter it here.
|
||||
|
||||
### Microservice information
|
||||
These are different Microservie parameters. Following is a brief explanation.
|
||||
```properties
|
||||
openwifi.service.key = $OWGW_ROOT/certs/restapi-key.pem
|
||||
openwifi.service.key.password = mypassword
|
||||
openwifi.system.data = $OWGW_ROOT/data
|
||||
openwifi.system.uri.private = https://localhost:17004
|
||||
openwifi.system.uri.public = https://ucentral.dpaas.arilia.com:16002
|
||||
openwifi.system.uri.ui = https://ucentral-ui.arilia.com
|
||||
openwifi.security.restapi.disable = false
|
||||
openwifi.system.commandchannel = /tmp/app.ucentralfms
|
||||
openwifi.autoprovisioning = true
|
||||
```
|
||||
#### openwifi.service.key
|
||||
From time to time, the microservice must encrypt information. This is the key it should use. You may use the
|
||||
same keey as you RESTAPI or your server.
|
||||
#### openwifi.service.key.password
|
||||
The password for the `openwifi.service.key`
|
||||
#### openwifi.system.data
|
||||
The location of system data. This path must exist.
|
||||
#### openwifi.system.uri.private
|
||||
The URI to reach the controller on the internal port.
|
||||
#### openwifi.system.uri.public
|
||||
The URI to reach the controller from the outside world.
|
||||
#### openwifi.system.uri.ui
|
||||
The URI of the UI to manage this service
|
||||
#### openwifi.security.restapi.disable
|
||||
This allows to disable security for internal and external API calls. This should only be used if the controller
|
||||
sits behind an application load balancer that will actually do TLS. Setting this to `true` disables security.
|
||||
#### openwifi.system.commandchannel
|
||||
The UNIX socket command channel used by this service.
|
||||
#### openwifi.autoprovisioning
|
||||
Allow unknown devices to be provisioned by the system.
|
||||
|
||||
### ALB Support
|
||||
In order to support an application load balancer health check verification, your need to provide the following parameters.
|
||||
```properties
|
||||
alb.enable = true
|
||||
alb.port = 16102
|
||||
```
|
||||
|
||||
### Kafka
|
||||
The controller use Kafka, like all the other microservices. You must configure the kafka section in order for the
|
||||
system to work.
|
||||
```properties
|
||||
openwifi.kafka.group.id = gateway
|
||||
openwifi.kafka.client.id = gateway1
|
||||
openwifi.kafka.enable = true
|
||||
openwifi.kafka.brokerlist = my_Kafka.example.com:9092
|
||||
openwifi.kafka.auto.commit = false
|
||||
openwifi.kafka.queue.buffering.max.ms = 50
|
||||
```
|
||||
|
||||
### openwifi.kafka.group.id
|
||||
The group ID is a single word that should identify the type of service tuning. In the case `gateway`
|
||||
### openwifi.kafka.client.id
|
||||
The client ID is a single service within that group ID. Each participant must have a unique client ID.
|
||||
### openwifi.kafka.enable
|
||||
Kafka should always be enabled.
|
||||
### openwifi.kafka.brokerlist
|
||||
The list of servers where your Kafka server is running. Comma separated.
|
||||
### openwifi.kafka.auto.commit
|
||||
Auto commit flag in Kafka. Leave as `false`.
|
||||
### openwifi.kafka.queue.buffering.max.ms
|
||||
Kafka buffering. Leave as `50`.
|
||||
### Kafka security
|
||||
If you intend to use SSL, you should look into Kafka Connect and specify the certificates below.
|
||||
```properties
|
||||
penwifi.kafka.ssl.ca.location =
|
||||
openwifi.kafka.ssl.certificate.location =
|
||||
openwifi.kafka.ssl.key.location =
|
||||
openwifi.kafka.ssl.key.password =
|
||||
```
|
||||
|
||||
### DB Type
|
||||
The controller supports 3 types of Database. SQLite should only be used for sites with less than 100 APs or for testing in the lab.
|
||||
In order to select which database to use, you must set the `storage.type` value to sqlite, postgresql, or mysql.
|
||||
|
||||
```properties
|
||||
storage.type = sqlite
|
||||
#storage.type = postgresql
|
||||
#storage.type = mysql
|
||||
```
|
||||
|
||||
### Storage SQLite parameters
|
||||
Additional parameters to set for SQLite. The only important one is `storage.type.sqlite.db` which is the database name on disk.
|
||||
```properties
|
||||
storage.type.sqlite.db = gateway.db
|
||||
storage.type.sqlite.idletime = 120
|
||||
storage.type.sqlite.maxsessions = 128
|
||||
```
|
||||
|
||||
### Storage Postgres
|
||||
Additional parameters to set if you select Postgres for your database. You must specify `host`, `username`, `password`,
|
||||
`database`, and `port`.
|
||||
```properties
|
||||
storage.type.postgresql.maxsessions = 64
|
||||
storage.type.postgresql.idletime = 60
|
||||
storage.type.postgresql.host = localhost
|
||||
storage.type.postgresql.username = gateway
|
||||
storage.type.postgresql.password = gateway
|
||||
storage.type.postgresql.database = gateway
|
||||
storage.type.postgresql.port = 5432
|
||||
storage.type.postgresql.connectiontimeout = 60
|
||||
```
|
||||
|
||||
### Storage MySQL/MariaDB
|
||||
Additional parameters to set if you select mysql for your database. You must specify `host`, `username`, `password`,
|
||||
`database`, and `port`.
|
||||
```properties
|
||||
storage.type.mysql.maxsessions = 64
|
||||
storage.type.mysql.idletime = 60
|
||||
storage.type.mysql.host = localhost
|
||||
storage.type.postgresql.username = gateway
|
||||
storage.type.postgresql.password = gateway
|
||||
storage.type.postgresql.database = gateway
|
||||
storage.type.mysql.port = 3306
|
||||
storage.type.mysql.connectiontimeout = 60
|
||||
```
|
||||
|
||||
### Logging Parameters
|
||||
The microservice provides extensive logging. If you would like to keep logging on disk, set the `logging.type = file`. If you only want
|
||||
console logging, `set logging.type = console`. When selecting file, `logging.path` must exist. `logging.level` sets the
|
||||
basic logging level for the entire controller. `logging.websocket` disables WebSocket logging.
|
||||
|
||||
```properties
|
||||
logging.type = file
|
||||
logging.path = $OWGW_ROOT/logs
|
||||
logging.level = information
|
||||
logging.asynch = true
|
||||
logging.websocket = false
|
||||
```
|
||||
38
CONTRIBUTING.md
Normal file
38
CONTRIBUTING.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# How to Contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Version of C++
|
||||
This project is based on the C++17 standard and compiles as-is on most platforms
|
||||
using either clang or g++. Do not use C++21 or C++23 features for now. Some core
|
||||
libraries used in this project do not support C++21 or C++23 yet.
|
||||
|
||||
## Variable Naming
|
||||
Naming of pretty much anything uses Pascal naming. Longer explicit names using casing.
|
||||
Member variable naming adds a `_` at the end of the vars. Try to
|
||||
keep this standard going. Sometimes you must override a base class function and then of course
|
||||
you need to follow the base class.
|
||||
|
||||
## This is a cmake project
|
||||
This is a cmake project, and you need to adhere to the cmake rules. If you need
|
||||
to add a package to the CMakeList, you need to ensure that the package is available
|
||||
on all required platforms and compiles. Remember that this project runs on Linux, OS X,
|
||||
and the Raspberry PI.
|
||||
|
||||
## Licensed packages
|
||||
When adding a package, you must also state the licensing for the package. MIT, BSD, Apache licenses
|
||||
are acceptable. No commercial licenses are allowed.
|
||||
|
||||
## clang formatting
|
||||
Please format your code using the included `.clang-format` file included in the project.
|
||||
|
||||
```bash
|
||||
clang-format -i --style=<project root>/.clang-format myfile.cpp
|
||||
```
|
||||
|
||||
## Pull Requests
|
||||
All submissions, including submissions by project members, require review. We
|
||||
accept GitHub pull requests. Please create a branch with the Jira name for addressing the issue you are fixing or the
|
||||
feature you are implementing.
|
||||
Create a pull-request from the branch into master.
|
||||
81
Dockerfile
81
Dockerfile
@@ -1,7 +1,9 @@
|
||||
ARG DEBIAN_VERSION=11.5-slim
|
||||
ARG POCO_VERSION=poco-tip-v2
|
||||
ARG DEBIAN_VERSION=bookworm
|
||||
ARG POCO_VERSION=poco-tip-v4-tag
|
||||
ARG CPPKAFKA_VERSION=tip-v1
|
||||
ARG JSON_VALIDATOR_VERSION=2.1.0
|
||||
ARG VALIJASON_VERSION=tip-v1.0.2
|
||||
ARG APP_NAME=owgw
|
||||
ARG APP_HOME_DIR=/openwifi
|
||||
|
||||
FROM debian:$DEBIAN_VERSION AS build-base
|
||||
|
||||
@@ -15,8 +17,8 @@ FROM build-base AS poco-build
|
||||
|
||||
ARG POCO_VERSION
|
||||
|
||||
ADD https://api.github.com/repos/AriliaWireless/poco/git/refs/tags/${POCO_VERSION} version.json
|
||||
RUN git clone https://github.com/AriliaWireless/poco --branch ${POCO_VERSION} /poco
|
||||
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-poco/git/refs/tags/${POCO_VERSION} version.json
|
||||
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch ${POCO_VERSION} /poco
|
||||
|
||||
WORKDIR /poco
|
||||
RUN mkdir cmake-build
|
||||
@@ -29,8 +31,8 @@ FROM build-base AS cppkafka-build
|
||||
|
||||
ARG CPPKAFKA_VERSION
|
||||
|
||||
ADD https://api.github.com/repos/AriliaWireless/cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json
|
||||
RUN git clone https://github.com/AriliaWireless/cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka
|
||||
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json
|
||||
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka
|
||||
|
||||
WORKDIR /cppkafka
|
||||
RUN mkdir cmake-build
|
||||
@@ -39,67 +41,78 @@ RUN cmake ..
|
||||
RUN cmake --build . --config Release -j8
|
||||
RUN cmake --build . --target install
|
||||
|
||||
FROM build-base AS json-schema-validator-build
|
||||
FROM build-base AS valijson-build
|
||||
|
||||
ARG JSON_VALIDATOR_VERSION
|
||||
ARG VALIJASON_VERSION
|
||||
|
||||
ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/tags/${JSON_VALIDATOR_VERSION} version.json
|
||||
RUN git clone https://github.com/pboettch/json-schema-validator --branch ${JSON_VALIDATOR_VERSION} /json-schema-validator
|
||||
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-valijson/git/refs/tags/${VALIJASON_VERSION} version.json
|
||||
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch ${VALIJASON_VERSION} /valijson
|
||||
|
||||
WORKDIR /json-schema-validator
|
||||
WORKDIR /valijson
|
||||
RUN mkdir cmake-build
|
||||
WORKDIR cmake-build
|
||||
RUN cmake ..
|
||||
RUN make
|
||||
RUN make install
|
||||
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
|
||||
COPY --from=cppkafka-build /usr/local/include /usr/local/include
|
||||
COPY --from=cppkafka-build /usr/local/lib /usr/local/lib
|
||||
COPY --from=json-schema-validator-build /usr/local/include /usr/local/include
|
||||
COPY --from=json-schema-validator-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"
|
||||
# This is for legacy
|
||||
ENV OWGW_USER=$APP_USER \
|
||||
OWGW_ROOT=$APP_ROOT \
|
||||
OWGW_CONFIG=$APP_CONFIG
|
||||
|
||||
RUN useradd $APP_USER
|
||||
|
||||
RUN mkdir $APP_HOME_DIR
|
||||
RUN mkdir -p $APP_ROOT $APP_CONFIG && \
|
||||
chown $APP_USER: $APP_ROOT $APP_CONFIG
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
librdkafka++1 gosu gettext ca-certificates bash jq curl wget \
|
||||
libmariadb-dev-compat libpq5 unixodbc postgresql-client libfmt7 sqlite3
|
||||
libmariadb-dev-compat libpq5 unixodbc postgresql-client libfmt9 sqlite3
|
||||
|
||||
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/
|
||||
|
||||
@@ -108,4 +121,4 @@ RUN ldconfig
|
||||
EXPOSE 15002 16002 16003 17002 16102
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["/openwifi/owgw"]
|
||||
CMD ${APP_HOME_DIR}/${APP_NAME}
|
||||
|
||||
13
KAFKA.md
13
KAFKA.md
@@ -4,15 +4,16 @@ This is a fast moving target, so please visit often or set an alert in GitHub.
|
||||
## Current plans
|
||||
Currently, most telemetry and reports created in the gateway will be issues as topics in Kafka. You should configure
|
||||
your Kafka service with the following topics:
|
||||
|
||||
- `connection` : This is emitted whenever a device connects to the gateway. The report contains all ths information about the connection.
|
||||
- `state` : This is emitted for every `state` report coming from the AP. This state report contains all the information of state reports.
|
||||
- `healthcheck` : These are the `healthcheck` report sent from the AP.
|
||||
- `state` : This is emitted for every `state` report coming from the AP. This state report contains all the information of state reports.
|
||||
- `connection` : This is emitted whenever a device connects to the gateway. The report contains all ths information about the connection.
|
||||
- `wifiscan` : Whenever a `wifiscan` report is generated, it will be submitted here.
|
||||
- `service_events` : Inter-service traffic.
|
||||
- `security` : This will have application information (future use).
|
||||
- `command` : Allow to send commands (future use).
|
||||
- `alerts` : Alerts originating from devices (future use).
|
||||
- `command` : Allow to send commands (future use).
|
||||
- `service_events` : Inter-service traffic.
|
||||
- `device_event_queue` : device events
|
||||
- `device_telemetry` : device telemetry. Telemetry must be started manually or through the device configuration.
|
||||
- `provisioning_change` : venue, configuration, entity changes from provisioning.
|
||||
|
||||
## Structure of `kafka` messages
|
||||
Messages use 2 formats
|
||||
|
||||
320
PROTOCOL.md
320
PROTOCOL.md
@@ -89,6 +89,54 @@ Device sends a log message whenever necessary. The controller will log this mess
|
||||
}
|
||||
```
|
||||
|
||||
#### Events Channel
|
||||
Device sends unsolicited events to the controller.
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "event" ,
|
||||
"params" : {
|
||||
"serial" : "001122334455" ,
|
||||
"data" : {
|
||||
"event" : [
|
||||
1871263817263,
|
||||
{
|
||||
"type" : "the event type",
|
||||
"payload" : {
|
||||
"field1" : "value1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The first element of the `event` array is always the `timestamp` of the event. The `payload` is a JSON document contains addition information about the event. This _may not_ be empty.
|
||||
|
||||
#### Alarms Channel
|
||||
Device sends unsolicited alarms to the controller.
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "alarm" ,
|
||||
"params" : {
|
||||
"serial" : <serial number> ,
|
||||
"data" : <Optiona/may be empty: JSON Document providing additional information related to this event message>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Wifiscan Channel
|
||||
Device sends unsolicited wifiscans to the controller.
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "wifiscan" ,
|
||||
"params" : {
|
||||
"serial" : <serial number> ,
|
||||
"data" : <Optiona/may be empty: JSON Document providing additional information related to this wifiscan message>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### `severity`
|
||||
The `severity` matches the `syslog` levels. Here are the details:
|
||||
- 0 : LOG_EMERG 0 /* system is unusable */
|
||||
@@ -101,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
|
||||
@@ -193,7 +258,7 @@ venue where this device belongs and resend the same message to all other devices
|
||||
"params" : {
|
||||
"serial" : <serial number> ,
|
||||
"timestamp" : <the UTC timestamp when the message was sent>,
|
||||
"data" : <an opaque string from the AP. This could be Zipped and so on and most likely base64 encoded>
|
||||
"data" : <JSON document to broadcast>
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -241,8 +306,54 @@ The device should answer:
|
||||
},
|
||||
"id" : <same number>
|
||||
}
|
||||
```
|
||||
|
||||
#### Controller wants the device to apply a given fixed configuration
|
||||
|
||||
Controller sends this command when it requires the device to apply fixed configuration, eg. country code. The device
|
||||
should respond with message indicating failure or success.
|
||||
|
||||
```json
|
||||
{ "jsonrpc" : "2.0",
|
||||
"method" : "fixedconfig",
|
||||
"params" : {
|
||||
"serial" : <serial number>,
|
||||
"when" : Optional - <UTC time when to apply this config, 0 means immediate, this is a suggestion>
|
||||
"country" : "<country-code>"
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If AP supports compressed configuration feature by inidcating `compress_cmd=true` in its capabilities, controller
|
||||
will send a compressed configuration message where configuration payload (i.e. contents of `params`) is compressed
|
||||
and encoded in base64 format:
|
||||
```json
|
||||
{ "jsonrpc" : "2.0",
|
||||
"method" : "configure",
|
||||
"params" : {
|
||||
"compress_64" : "<b64 encoded zlib compressed payload>",
|
||||
"compress_sz" : "<size of uncompressed data in bytes>"
|
||||
},
|
||||
"id" : <some number>
|
||||
}
|
||||
```
|
||||
|
||||
The device should answer:
|
||||
```json
|
||||
{ "jsonrpc" : "2.0",
|
||||
"result" : {
|
||||
"serial": <serial number>,
|
||||
"status": {
|
||||
"error": 0 or an error number,
|
||||
"text": <description of the error or success, eg. "Applied fixed config, rebooting">
|
||||
},
|
||||
"uuid": <UUID>
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
##### The Answer
|
||||
The device can answer and tell the controller it has rejected certain parts of the config and potentially replaced them with
|
||||
appropriate values. This could be used to allow a device to replace frequencies for the regions it is located in. The device
|
||||
@@ -290,6 +401,39 @@ The device should answer:
|
||||
- 1 : the device is busy but will reboot soon. `text` may indicate why.
|
||||
- 2 : the device will not reboot. `text` contains information as to why.
|
||||
|
||||
#### Controller wants to power-cycle PoE port(s)
|
||||
Controller sends this command to power-cycle 1 or more PoE ports
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "powercycle" ,
|
||||
"params" : {
|
||||
"serial" : <serial number> ,
|
||||
"ports" : [ { "name" : "Ethernet1", "cycle" : 5000}, { "name" : "Ethernet8", "cycle" : 10000 } ],
|
||||
"when" : Optional - <UTC time when to reboot, 0 mean immediately, this is a suggestion>
|
||||
},
|
||||
"id" : <some number>
|
||||
}
|
||||
```
|
||||
|
||||
The device should answer:
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"result" : {
|
||||
"serial" : <serial number> ,
|
||||
"status" : {
|
||||
"error" : 0 or an error number,
|
||||
"text" : [ "Error 1" , "Error 2" ],
|
||||
"when" : <time when this will be performed as UTC seconds>,
|
||||
},
|
||||
"id" : <same id from request>
|
||||
}
|
||||
```
|
||||
|
||||
###### Error codes
|
||||
- 0 : is rebooting at `when` seconds.
|
||||
- 1 : the device is busy but will reboot soon. `text` may indicate why.
|
||||
- 2 : the device will not reboot. `text` contains information as to why.
|
||||
|
||||
#### Controller wants the device to upgrade its firmware
|
||||
Controller sends this command when it believes the device should upgrade its firmware.
|
||||
```json
|
||||
@@ -710,6 +854,172 @@ The device should answer:
|
||||
}
|
||||
```
|
||||
|
||||
#### Controller wants the device to replace its certificates
|
||||
Controller sends this command to run a predefined script. Extreme care must be taken.
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "certupdate" ,
|
||||
"params" : {
|
||||
"serial" : <serial number>,
|
||||
"certificates" : <BASE64 encoded tar file of the cert package from the certificate portal>
|
||||
},
|
||||
"id" : <some number>
|
||||
}
|
||||
```
|
||||
|
||||
The device should answer:
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"result" : {
|
||||
"serial" : <serial number> ,
|
||||
"status" : {
|
||||
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
|
||||
"txt" : <text describing the error or success>
|
||||
},
|
||||
"id" : <same number as request>
|
||||
}
|
||||
```
|
||||
|
||||
#### Controller wants the device to perform re-enrollment
|
||||
Controller sends this command to trigger re-enrollment, i.e. update of operational certificate. Extreme care must be taken.
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "reenroll" ,
|
||||
"params" : {
|
||||
"serial" : <serial number>,
|
||||
"when" : Optional - <UTC time when to apply this config, 0 mean immediate, this is a suggestion>
|
||||
},
|
||||
"id" : <some number>
|
||||
}
|
||||
```
|
||||
|
||||
The device should answer:
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"result" : {
|
||||
"serial" : <serial number> ,
|
||||
"status" : {
|
||||
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
|
||||
"txt" : <text describing the error or success>
|
||||
},
|
||||
"id" : <same number as request>
|
||||
}
|
||||
```
|
||||
|
||||
#### Controller wants the device to switch to another controller
|
||||
Controller sends this when the device should change the controller it connects to without looking up a new redirector.
|
||||
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "transfer" ,
|
||||
"params" : {
|
||||
"serial" : <serial number>,
|
||||
"server" : <controller hostname>,
|
||||
"port" : <controller port number (integer)>,
|
||||
},
|
||||
"id" : <some number>
|
||||
}
|
||||
```
|
||||
|
||||
The device should answer:
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"result" : {
|
||||
"serial" : <serial number> ,
|
||||
"status" : {
|
||||
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
|
||||
"txt" : <text describing the error or success>
|
||||
},
|
||||
"id" : <same number as request>
|
||||
}
|
||||
```
|
||||
|
||||
### RRM AP device commands
|
||||
The following command is used to send RRM commands to an AP. RRM commands are send to an AP, however the
|
||||
controller will not or cannot verify if they have been sent or the action was performed.
|
||||
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"method" : "rrm" ,
|
||||
"params" : {
|
||||
"serial" : <serial number>,
|
||||
"actions" : [ array of actions. Each possible action is defined next]
|
||||
},
|
||||
"id" : <some number>
|
||||
}
|
||||
```
|
||||
|
||||
The device should answer:
|
||||
```json
|
||||
{ "jsonrpc" : "2.0" ,
|
||||
"result" : {
|
||||
"serial" : <serial number> ,
|
||||
"status" : {
|
||||
"error" : <0 or the value of $? from the shell running the command, 255 signifies a timeout>,
|
||||
"txt" : <text describing the error or success>
|
||||
},
|
||||
"id" : <same number as request>
|
||||
}
|
||||
```
|
||||
|
||||
#### RRM Roam action
|
||||
|
||||
##### Kick
|
||||
```json
|
||||
{
|
||||
"action" : "kick" ,
|
||||
"addr" : <mac if the client that shall be kicked> ,
|
||||
"reason": <number>, (default: 5, https://www.cisco.com/assets/sol/sb/WAP371_Emulators/WAP371_Emulator_v1-0-1-5/help/Apx_ReasonCodes2.html)
|
||||
"ban_time": <number> (seconds, optional)
|
||||
}
|
||||
```
|
||||
|
||||
##### Channel Switch Announcement
|
||||
```json
|
||||
{
|
||||
"action" : "channel_switch" ,
|
||||
"bssid" : <mac of the SSID> , (all other SSIDs on the same radio will perform the same action)
|
||||
"channel" : <number> (HT/HW mode will be retained upon issuing the CSA)
|
||||
}
|
||||
```
|
||||
|
||||
##### Change TX-Power
|
||||
```json
|
||||
{
|
||||
"action" : "tx_power" ,
|
||||
"bssid" : <mac of the SSID> , (all other SSIDs on the same radio will perform the same action)
|
||||
"level" : <number> (DBm inside the positive number space)
|
||||
}
|
||||
```
|
||||
|
||||
##### Beacon Scan
|
||||
```json
|
||||
{
|
||||
"action" : "beacon_request" ,
|
||||
"addr" : <mac if the client that shall perform the scan> ,
|
||||
"ssid": <string>, (the SSID the client shall scan for on all frequencies),
|
||||
"channel": <number> (the channel that shall be scanned)
|
||||
}
|
||||
```
|
||||
|
||||
##### BSS Transition
|
||||
```json
|
||||
{
|
||||
"action" : "bss_transition" ,
|
||||
"addr" : <mac if the client that shall perform the roam> ,
|
||||
"neighbors": [ <string> ], (an array of BSSIDs the client shall consider as roamin candidates)
|
||||
}
|
||||
```
|
||||
|
||||
##### Update neighbours
|
||||
```json
|
||||
{
|
||||
"action" : "neighbors" ,
|
||||
"bssid" : <mac of the SSID> , (the SSID of the specific VAP)
|
||||
"neighbors": [ [ <BSS>, <ssid>, <neighbor report> ] ]
|
||||
}
|
||||
```
|
||||
|
||||
### `rtty server`
|
||||
More information about the [rtty server](https://github.com/zhaojh329/rtty) can be found here.
|
||||
|
||||
|
||||
618
README.md
618
README.md
@@ -1,227 +1,36 @@
|
||||
# uCentralGW
|
||||
<p align="center">
|
||||
<img src="images/project/logo.svg" width="200" alt="OpenWiFi Project"/>
|
||||
</p>
|
||||
|
||||
## What is this?
|
||||
The uCentralGW is an added service for the TIP controller that allows integration with the
|
||||
uCentral protocol. It supports a complete OpenAPI definition and uses the ucentral communication protocol. To use the uCentralGW,
|
||||
you either need to [build it](#building) or use the [Docker version](#docker).
|
||||
# OpenWiFI Gateway (OWGW)
|
||||
|
||||
## What is it?
|
||||
The OpenWiFi Gateway is a service for the TIP OpenWiFi CloudSDK (OWSDK).
|
||||
OWGW manages Access Points that implement the OpenWiFi uCentral protocol. OWGW, like all other OWSDK microservices, is
|
||||
defined using an OpenAPI definition and uses the ucentral communication protocol to interact with Access Points. To use
|
||||
the OWGW, you either need to [build it](#building) or use the [Docker version](#docker).
|
||||
|
||||
## Building
|
||||
In order to build the uCentralGW, you will need to install its dependencies, which includes the following:
|
||||
- cmake
|
||||
- boost
|
||||
- POCO 1.10.1 or later
|
||||
- a C++17 compiler
|
||||
- openssl
|
||||
- libpq-dev (PortgreSQL development libraries)
|
||||
- mysql-client (MySQL client)
|
||||
- librdkafka
|
||||
- cppkafka
|
||||
-
|
||||
To build the microservice from source, please follow the instructions in [here](./BUILDING.md)
|
||||
|
||||
The build is done in 2 parts. The first part is to build a local copy of the framework tailored to your environment. This
|
||||
framework is called [Poco](https://github.com/pocoproject/poco). The version used in this project has a couple of fixes
|
||||
from the master copy needed for cmake. Please use the version of this [Poco fix](https://github.com/AriliaWireless/poco). Building
|
||||
Poco may take several minutes depending on the platform you are building on.
|
||||
|
||||
### Ubuntu
|
||||
These instructions have proven to work on Ubuntu 20.4.
|
||||
```
|
||||
sudo apt install git cmake g++ libssl-dev libmariadb-dev
|
||||
sudo apt install libpq-dev libaprutil1-dev apache2-dev libboost-all-dev
|
||||
sudo apt install librdkafka-dev // default-libmysqlclient-dev
|
||||
sudo apt install nlohmann-json-dev
|
||||
|
||||
cd ~
|
||||
git clone https://github.com/AriliaWireless/poco --branch poco-tip-v1
|
||||
cd poco
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
|
||||
cd ~
|
||||
git clone https://github.com/AriliaWireless/cppkafka --branch tip-v1
|
||||
cd cppkafka
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
|
||||
cd ~
|
||||
git clone https://github.com/pboettch/json-schema-validator.git --branch 2.1.0
|
||||
cd json-schema-validator
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make -j
|
||||
sudo make install
|
||||
|
||||
git clone https://github.com/fmtlib/fmt --branch 9.0.0 /fmtlib
|
||||
cd fmtlib
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
|
||||
cd ~
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
cd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make -j 8
|
||||
|
||||
|
||||
```
|
||||
|
||||
### Fedora
|
||||
The following instructions have proven to work on Fedora 33
|
||||
```
|
||||
sudo yum install cmake g++ openssl-devel mysql-devel mysql apr-util-devel boost boost-devel
|
||||
sudo yum install yaml-cpp-devel lua-devel
|
||||
sudo dnf install postgresql.x86_64 librdkafka-devel
|
||||
sudo dnf install postgresql-devel json-devel
|
||||
|
||||
git clone https://github.com/AriliaWireless/poco --branch poco-tip-v1
|
||||
cd poco
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
|
||||
git clone https://github.com/AriliaWireless/cppkafka --branch tip-v1
|
||||
cd cppkafka
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
|
||||
cd ~
|
||||
git clone https://github.com/pboettch/json-schema-validator.git --branch 2.1.0
|
||||
cd json-schema-validator
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make -j
|
||||
sudo make install
|
||||
|
||||
cd ~
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
cd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
make
|
||||
|
||||
```
|
||||
|
||||
### macOS Build
|
||||
The following instructions have proven to work on macOS Big Sur. You need to install [Homebrew](https://brew.sh/). You must also have installed [XCode for OS X](https://www.freecodecamp.org/news/how-to-download-and-install-xcode/).
|
||||
```
|
||||
brew install openssl \
|
||||
cmake \
|
||||
libpq \
|
||||
mysql-client \
|
||||
apr \
|
||||
apr-util \
|
||||
boost \
|
||||
yaml-cpp \
|
||||
postgresql \
|
||||
librdkafka \
|
||||
nlohmann-json \
|
||||
fmt
|
||||
|
||||
git clone https://github.com/AriliaWireless/poco --branch poco-tip-v1
|
||||
pushd poco
|
||||
mkdir cmake-build
|
||||
push cmake-build
|
||||
cmake -DOPENSSL_ROOT_DIR=</path/to/openssl> -DENABLE_NETSSL=1 -DENABLE_JWT=1 -DENABLE_CRYPTO=1 ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
popd
|
||||
popd
|
||||
|
||||
git clone https://github.com/AriliaWireless/cppkafka --branch tip-v1
|
||||
pushd cppkafka
|
||||
mkdir cmake-build
|
||||
pushd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
popd
|
||||
popd
|
||||
|
||||
git clone https://github.com/pboettch/json-schema-validator.git --branch 2.1.0
|
||||
pushd json-schema-validator
|
||||
mkdir cmake-build
|
||||
pushd cmake-build
|
||||
cmake ..
|
||||
make -j
|
||||
sudo make install
|
||||
popd
|
||||
popd
|
||||
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
pushd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
pushd cmake-build
|
||||
cmake ..
|
||||
make -j
|
||||
popd
|
||||
popd
|
||||
```
|
||||
|
||||
### Raspberry
|
||||
The build on a rPI takes a while. You can shorten that build time and requirements by disabling all the larger database
|
||||
support. You can build with only SQLite support by not installing the packages for ODBC, PostgreSQL, and MySQL by
|
||||
adding -DSMALL_BUILD=1 on the cmake build line.
|
||||
|
||||
```
|
||||
sudo apt install git cmake g++ libssl-dev libaprutil1-dev apache2-dev libboost-all-dev libyaml-cpp-dev
|
||||
git clone https://github.com/stephb9959/poco
|
||||
cd poco
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake ..
|
||||
cmake --build . --config Release
|
||||
sudo cmake --build . --target install
|
||||
|
||||
cd ~
|
||||
git clone https://github.com/Telecominfraproject/wlan-cloud-ucentralgw
|
||||
cd wlan-cloud-ucentralgw
|
||||
mkdir cmake-build
|
||||
cd cmake-build
|
||||
cmake -DSMALL_BUILD=1 ..
|
||||
make
|
||||
```
|
||||
|
||||
### After completing the build
|
||||
After completing the build, you can remove the Poco source as it is no longer needed.
|
||||
## Docker
|
||||
To use the CLoudSDK deployment please follow [here](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy)
|
||||
|
||||
#### Expected directory layout
|
||||
From the directory where your cloned source is, you will need to create the `certs`, `logs`, and `uploads` directories.
|
||||
```shell
|
||||
```bash
|
||||
mkdir certs
|
||||
mkdir certs/cas
|
||||
mkdir logs
|
||||
mkdir uploads
|
||||
```
|
||||
|
||||
You should now have the following:
|
||||
|
||||
```
|
||||
-- cert_scripts
|
||||
|
|
||||
+-- certs
|
||||
```text
|
||||
--+-- certs
|
||||
| +--- cas
|
||||
+-- cmake
|
||||
+-- cmake-build
|
||||
+-- logs (dir)
|
||||
+-- logs
|
||||
+-- src
|
||||
+-- test_scripts
|
||||
+-- openapi
|
||||
@@ -230,12 +39,16 @@ You should now have the following:
|
||||
```
|
||||
|
||||
### Certificates
|
||||
Love'em of hate'em, we gotta use'em. So we tried to make this as easy as possible for you.
|
||||
The OWGW uses a number of certificates to provide security. There are 2 types of certificates required for
|
||||
a normal deployment:
|
||||
- A Server Certificate to secure the OWGW<->AP channel
|
||||
- A REST API Certificate to secure the Northbound API
|
||||
- Device Certificates
|
||||
|
||||
#### The `certs` directory
|
||||
For all deployments, you will need the following certs directory, populated with the proper files.
|
||||
|
||||
```asm
|
||||
```text
|
||||
certs ---+--- root.pem
|
||||
+--- issuer.pem
|
||||
+--- websocket-cert.pem
|
||||
@@ -250,183 +63,69 @@ certs ---+--- root.pem
|
||||
```
|
||||
|
||||
#### DigiCert files
|
||||
These are the files you should install on your gateway and devices. For your gateway, you will need to provide tge following files in the directory above
|
||||
These are the files you should install on your OWGW and devices. For your OWGW, you will need to provide tge following files in the directory above
|
||||
- `root.pem` is [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem).
|
||||
- `issuer.pem` is [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/issuer.pem).
|
||||
- `clientcas.pem` is [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/clientcas.pem).
|
||||
|
||||
#### Gateway certificates: TIP portion (* must be obtained from TIP)
|
||||
#### OWGW Server certificate (must be obtained from TIP)
|
||||
The gateway requires its own DigiCert certificate. Once obtained, you need to identify the `key` and the `certificate` rename
|
||||
them `websocket-key.pem` and `websocket-cert.pem`, and copy them in your `certs` directory. These files mus be obtained from TIP.
|
||||
|
||||
#### Gateway certificates: for REST API
|
||||
The gateway requires a key/vertificate/ca for the REST interface. These files you need to obtain on your own of generate them. This is beyond the scope of this
|
||||
document. Once you have these files, you need to renamed them `restapi-key.pem`, `restapi-cert.pem`, and `restapi-ca.pem`. This will guarantee proper HTTPS
|
||||
in your browner
|
||||
#### OWGW for certificate: for REST API (from your favourite Certificate Provider)
|
||||
The gateway requires a key/certificate/ca for the REST interface. These files you need to obtain on your own or generate them. This is beyond the scope of this
|
||||
document. You, may choose to select LestEncrypt or any other Certificate Authority. Once you have these files, you need to renamed them `restapi-key.pem`, `restapi-cert.pem`, and `restapi-ca.pem`.
|
||||
This will guarantee proper HTTPS in your browser and RESTAPI.
|
||||
|
||||
#### Configuration
|
||||
The configuration for this service is kept in a properties file. This file is called `owgw.properties` and you can
|
||||
see the latest version [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/owgw.properties). The file will be loaded from
|
||||
the directory set by the environment variable `UCENTRALGW_CONFIG`. To use environment variables in the configuration,
|
||||
you must use `$<varname>`. Only `path names` support the use of environment variables. The sample configuration requires very
|
||||
little changes if you keep the suggested directory structure. For the sample configuration to work, you need to define 2
|
||||
environment variables.
|
||||
```
|
||||
export OWGW_ROOT=`pwd`
|
||||
export OWGW_CONFIG=`pwd`
|
||||
```
|
||||
If you current working directory is the root of the project, this will set the variables properly. Otherwise, you can set the variables
|
||||
to point to wherever is necessary.
|
||||
|
||||
##### Important config entries
|
||||
###### This is the logging directory
|
||||
```
|
||||
logging.channels.c2.path = $OWGW_ROOT/logs/sample.log
|
||||
```
|
||||
|
||||
###### This is the type of storage in use
|
||||
```asm
|
||||
storage.type = sqlite
|
||||
```
|
||||
|
||||
###### Autoprovisioning settings
|
||||
```asm
|
||||
openwifi.autoprovisioning = true
|
||||
openwifi.devicetypes.0 = AP:linksys_ea8300,edgecore_eap101,linksys_e8450-ubi
|
||||
openwifi.devicetypes.1 = SWITCH:edgecore_ecs4100-12ph
|
||||
openwifi.devicetypes.2 = IOT:esp32
|
||||
```
|
||||
|
||||
###### This is the RESTAPI endpoint
|
||||
|
||||
```asm
|
||||
openwifi.restapi.host.0.backlog = 100
|
||||
openwifi.restapi.host.0.security = relaxed
|
||||
openwifi.restapi.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
|
||||
openwifi.restapi.host.0.address = *
|
||||
openwifi.restapi.host.0.port = 16002
|
||||
openwifi.restapi.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
|
||||
openwifi.restapi.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
|
||||
openwifi.restapi.host.0.key.password = mypassword
|
||||
```
|
||||
|
||||
##### This is the end point for the devices to connect with
|
||||
This is the crucial section. I bet that 97.4% of all your problems will come from here, and it's boring. So put some good music on,
|
||||
give the kids the iPad, get a cup of coffee, and pay attention. Every field will be explained.
|
||||
|
||||
###### ucentral.websocket.host.0.backlog
|
||||
This is the number of concurrent devices you are expecting to call all at once. Not the current number of devices. This is how many will connect in the same exact second.
|
||||
Take the total number of devices you have and divide by 100. That's a good rule of thumb. Never go above 500.
|
||||
|
||||
###### ucentral.websocket.host.0.rootca
|
||||
This is the root file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem)
|
||||
|
||||
###### ucentral.websocket.host.0.issuer
|
||||
This is the issuer file as supplied by Digicert. You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/issuer.pem)
|
||||
|
||||
###### ucentral.websocket.host.0.cert
|
||||
This is a `pem` file that you will receive from Digicert for the gateway itself. This is the certificate for the gateway.
|
||||
|
||||
###### ucentral.websocket.host.0.key
|
||||
This is a `pem` file that you will receive from Digicert for the gateway itself. The is the private key for the gateway.
|
||||
|
||||
###### ucentral.websocket.host.0.clientcas
|
||||
This is a `pem` file that contains both the issuer and the root CA certificates. You can find it You can find it [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/clientcas.pem)
|
||||
|
||||
###### ucentral.websocket.host.0.cas
|
||||
This is a directory where you will copy your own `cert.pem`, the `root.pem`, and the `issuer.pem` files.
|
||||
|
||||
###### ucentral.websocket.host.0.address
|
||||
Leve this a `*` in teh case you want to bind to all interfaces on your gateway host or select the address of a single interface.
|
||||
|
||||
###### ucentral.websocket.host.0.port
|
||||
Leave to 15002 for now.
|
||||
|
||||
###### ucentral.websocket.host.0.security
|
||||
Leave this as strict for now for devices.
|
||||
|
||||
###### ucentral.websocket.host.0.key.password
|
||||
If you key file uses a password, please enter it here.
|
||||
|
||||
###### ucentral.websocket.maxreactors
|
||||
A single reactor can handle between 1000-2000 devices. Never leave this smaller than 5 or larger than 50.
|
||||
|
||||
#### Conclusion
|
||||
You will need to get the `cert.pem` and `key.pem` from Digicert. The rest is here.
|
||||
|
||||
```asm
|
||||
ucentral.websocket.host.0.backlog = 500
|
||||
ucentral.websocket.host.0.rootca = $OWGW_ROOT/certs/root.pem
|
||||
ucentral.websocket.host.0.issuer = $OWGW_ROOT/certs/issuer.pem
|
||||
ucentral.websocket.host.0.cert = $OWGW_ROOT/certs/websocket-cert.pem
|
||||
ucentral.websocket.host.0.key = $OWGW_ROOT/certs/websocket-key.pem
|
||||
ucentral.websocket.host.0.clientcas = $OWGW_ROOT/certs/clientcas.pem
|
||||
ucentral.websocket.host.0.cas = $OWGW_ROOT/certs/cas
|
||||
ucentral.websocket.host.0.address = *
|
||||
ucentral.websocket.host.0.port = 15002
|
||||
ucentral.websocket.host.0.security = strict
|
||||
ucentral.websocket.host.0.key.password = mypassword
|
||||
ucentral.websocket.maxreactors = 20
|
||||
```
|
||||
|
||||
###### This is the end point for the devices when uploading files
|
||||
```asm
|
||||
openwifi.fileuploader.host.0.backlog = 100
|
||||
openwifi.fileuploader.host.0.rootca = $OWGW_ROOT/certs/restapi-ca.pem
|
||||
openwifi.fileuploader.host.0.security = relaxed
|
||||
openwifi.fileuploader.host.0.address = *
|
||||
openwifi.fileuploader.host.0.name = 192.168.1.176
|
||||
openwifi.fileuploader.host.0.port = 16003
|
||||
openwifi.fileuploader.host.0.cert = $OWGW_ROOT/certs/restapi-cert.pem
|
||||
openwifi.fileuploader.host.0.key = $OWGW_ROOT/certs/restapi-key.pem
|
||||
openwifi.fileuploader.host.0.key.password = mypassword
|
||||
openwifi.fileuploader.path = $OWGW_ROOT/uploads
|
||||
openwifi.fileuploader.maxsize = 10000
|
||||
```
|
||||
|
||||
###### host.0.address entries
|
||||
If you want to limit traffic to a specific interface, you should specify the IP address of that interface instead of
|
||||
the `*`. Using the `*` means all interfaces will be able to accept connections. You can add multiple interfaces
|
||||
by changing the `0` to another index. You need to repeat the whole configuration block for each index. Indexes must be sequential
|
||||
start at `0`.
|
||||
|
||||
###### openwifi.fileuploader.host.0.name
|
||||
This must point to the IP or FQDN of your uCentralGW.
|
||||
|
||||
#### Running the gateway
|
||||
Tu run the gateway, you must run the executable `ucentralgw`. You can use several command line options to run as a daemon or specify the configuration file location.
|
||||
|
||||
#### Device configuration
|
||||
Once you have the gateway configured, you will need to have some devices coming to it. For now, you will need to get
|
||||
the following in order to use the gateway:
|
||||
### OpenWiFi Device certificates
|
||||
This may have already done at the factory. If not, you will need to get the following in order to point your devices to use the OWGW:
|
||||
- A DigiCert certificate that you will call `cert.pem`
|
||||
- A DigiCert key that goes with that certificate. Please call this `key.pem`
|
||||
- The Digicert root certificate that you will find [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/certificates/root.pem). You must copy `root.pem`
|
||||
and rename it `cas.pem` on the device.
|
||||
and rename it `cas.pem` on the device.
|
||||
- A Device ID file called `dev-id` or something similar
|
||||
- Copy all the 4 files to the `/certificates` directory of the AP (you must have firmware created Jun 15th or later).
|
||||
|
||||
You will need to upgrade your device to the latest firmware. Once updated, you will need to copy the 4 files mentioned above in
|
||||
the `/certificates` directory. Please remove all old keys or certificates from the `/etc/ucentral` directory
|
||||
You will need to upgrade your device to the latest firmware. Once updated, you will need to copy the 4 files mentioned above in
|
||||
the `/certificates` directory. Please remove all old keys or certificates from the `/etc/ucentral` directory
|
||||
(anything ending in `.pem`).
|
||||
|
||||
#### Server key entry
|
||||
The gateway needs to encrypt information from time to time. In order to do so, it must have a crypto key. This key
|
||||
can be any of the keys you are already using. You must keep that keep secret and always use it. In the configutation,
|
||||
this is the entry
|
||||
|
||||
```asm
|
||||
openwifi.service.key = $OWGW_ROOT/certs/websocket-key.pem
|
||||
### Environment variables
|
||||
The following environment variables should be set from the root directory of the service. They tell the OWGW process where to find
|
||||
the configuration and the root directory.
|
||||
```bash
|
||||
export OWGW_ROOT=`pwd`
|
||||
export OWGW_CONFIG=`pwd`
|
||||
```
|
||||
|
||||
#### Command line options
|
||||
You can run the shell script `set_env.sh` from the microservice root.
|
||||
|
||||
### OWGW Service Configuration
|
||||
The configuration is kept in a file called `owgw.properties`. To understand the content of this file,
|
||||
please look [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/CONFIGURATION.md)
|
||||
|
||||
### Running the OWGW
|
||||
Tu run the OWGW, you must run the executable `owgw`. You can use several command line options to run as a daemon or
|
||||
specify the configuration file location.
|
||||
|
||||
### systemd: owgw.service
|
||||
`owgw.service` is a skeleton to allow to run the OWGW in a systemd based operating system (i.e. Debian). You will need
|
||||
to modify slightly to reflect your environment.
|
||||
|
||||
### Default device configuration
|
||||
By default, the devices will receive a built-in default configuration. This built-in default configuration is probably not what you need.
|
||||
So there are 2 options in order to specify your own default configuration:
|
||||
- In the OWGW data directory, you can create a `default_config.json` file that contains your own personal configuration
|
||||
- Using the OWGW UI, on the left hand pane, there is a `configurations` choice. There you can do the same but apply it with more granularity ot each device type
|
||||
you may be deploying.
|
||||
|
||||
### Command line options
|
||||
The current implementation supports the following. If you use the built-in configuration file, you do not need to use any command-line
|
||||
options. However, you may decide to use the `--daemon` or `umask` options.
|
||||
|
||||
```bash
|
||||
./ucentralgw --help
|
||||
usage: ucentralgw OPTIONS
|
||||
A uCentral gateway implementation for TIP.
|
||||
./owgw --help
|
||||
usage: owgw OPTIONS
|
||||
A owgw gateway implementation for TIP.
|
||||
|
||||
--daemon Run application as a daemon.
|
||||
--umask=mask Set the daemon's umask (octal, e.g. 027).
|
||||
@@ -437,119 +136,27 @@ A uCentral gateway implementation for TIP.
|
||||
--logs=dir specify the log directory and file (i.e. dir/file.log)
|
||||
```
|
||||
|
||||
##### file
|
||||
This allows you to point to another file without specifying the UCENTRALGW_CONFIG variable. The file name must end in `.properties`.
|
||||
##### daemon
|
||||
#### file
|
||||
This allows you to point to another file without specifying the OWGW_CONFIG variable. The file name must end in `.properties`.
|
||||
#### daemon
|
||||
Run this as a UNIX service
|
||||
##### pidfile
|
||||
#### pidfile
|
||||
When running as a daemon, the pid of the running service will be set in the speficied file
|
||||
##### debug
|
||||
#### debug
|
||||
Run the service in debug mode.
|
||||
##### logs
|
||||
#### logs
|
||||
Speficy where logs should be kept. You must include an existing directory and a file name. For example `/var/ucentral/logs/log.0`.
|
||||
##### umask
|
||||
#### umask
|
||||
Seet the umask for the running service.
|
||||
|
||||
### ALB Support
|
||||
Support for AWS ALB is provided through the following configuration elements
|
||||
```asm
|
||||
alb.enable = true
|
||||
alb.port = 16102
|
||||
```
|
||||
|
||||
### Docker
|
||||
So building this thing from scratch is not your thing? I can't blame you. It takes some patience and
|
||||
in the end, there's still more work. Here comes `docker` to the rescue. You can run a docker version following
|
||||
these instructions. The following is the content of the `docker_run.sh` script you can find
|
||||
[here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/docker_run.sh):
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
|
||||
HUBNAME=tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||
IMAGE_NAME=ucentralgw
|
||||
DOCKER_NAME=$HUBNAME/$IMAGE_NAME
|
||||
|
||||
CONTAINER_NAME=ucentralgw
|
||||
|
||||
#stop previously running images
|
||||
docker container stop $CONTAINER_NAME
|
||||
docker container rm $CONTAINER_NAME --force
|
||||
|
||||
if [[ ! -d logs ]]
|
||||
then
|
||||
mkdir logs
|
||||
fi
|
||||
|
||||
if [[ ! -d certs ]]
|
||||
then
|
||||
echo "certs directory does not exist. Please create and add the proper certificates."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f owgw.properties ]]
|
||||
then
|
||||
echo "Configuration file owgw.properties is missing in the current directory"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
docker run -d -p 15002:15002 \
|
||||
-p 16002:16002 \
|
||||
-p 16003:16003 \
|
||||
--init \
|
||||
--volume="$PWD:/ucentral-data" \
|
||||
-e UCENTRAL_ROOT="/ucentral-data" \
|
||||
-e UCENTRALGW_CONFIG="/ucentral-data" \
|
||||
--name="ucentralgw" $DOCKER_NAME
|
||||
|
||||
```
|
||||
|
||||
Create yourself a directory and copy that script which you can also get from [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/docker_run.sh).
|
||||
You must have the basic configuration file copied in the directory. This file must be called `owgw.properties`. You can bring your own or
|
||||
copy it from [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/owgw.properties). Please look at [this](#certificates-with-docker) to have the right
|
||||
certificates. You need to make sure that the names match the content of the `owgw.properties`
|
||||
file. Once all this is done, you can simply run `docker_run.sh`.
|
||||
|
||||
#### Docker installation directory layout
|
||||
Here is the layout expected for your Docker installation
|
||||
|
||||
```asm
|
||||
Run-time root
|
||||
|
|
||||
----- certs (same as above)
|
||||
+---- logs (dir)
|
||||
+---- uploads (dir)
|
||||
+---- owgw.properties (file)
|
||||
```
|
||||
|
||||
#### `owgw.properties` for Docker
|
||||
If you use the pre-made configuration file, and you follow the directory layout, the only line you must change
|
||||
is the following line:
|
||||
|
||||
```asm
|
||||
openwifi.fileuploader.host.0.name = 192.168.1.176
|
||||
```
|
||||
|
||||
This line should reflect the IP of your gateway or its FQDN. You must make sure that this name or IP is accessible
|
||||
from your devices. This is used during file uploads from the devices.
|
||||
|
||||
#### Certificates with Docker
|
||||
Please refer to the `certs` directory from the sections above.
|
||||
|
||||
#### Configuration with Docker
|
||||
The configuration for this service is kept in a properties file. Currently, this configuration file must be kept in the
|
||||
current directory of uCentral or one level up. This file is called `owgw.properties` and you can see the latest version
|
||||
[here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/owgw.properties). The file will be loaded from
|
||||
the directory set by the environment variable `UCENTRALGW_CONFIG`. To use environment variables in the configuration,
|
||||
you must use `$<varname>`. The path for the logs for the service must exist prior to starting the
|
||||
service. The path is defined under `logging.channels.c2.path`. Only `path names` support the use of
|
||||
environment variables. Here is a sample configuration:
|
||||
## Docker
|
||||
If you would rather launch the docker-compose or helm for the controller, please click [here](https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy).
|
||||
|
||||
## uCentral communication protocol
|
||||
The communication protocol between the device and the controller is detailed in this [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/PROTOCOL.md).
|
||||
The communication protocol between the device and the OGWG is detailed in this [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/PROTOCOL.md).
|
||||
|
||||
## OpenAPI
|
||||
The service supports an OpenAPI REST based interface for management. You can find the [definition here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/owgw.yaml).
|
||||
The OWGW supports an OpenAPI REST based interface for management. You can find the [definition here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/owgw.yaml).
|
||||
And here is [how to use it](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/OPENAPI.md)
|
||||
|
||||
## Using the API
|
||||
@@ -559,58 +166,31 @@ or [python](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/ma
|
||||
More scripts will be added in the future.
|
||||
|
||||
## Firewall Considerations
|
||||
- The protocol uses TCP port 15002 between the devices and the gateway. This port must be opened.
|
||||
- Devices use the TCP port 16003 to upload files. This port is configurable in the `owgw.properties` file. Look for `openwifi.fileuploader.host.0.port`.
|
||||
- The RESTAPI is accessed through TCP port 16002 by default. This port is configurable in the `owgw.properties` file. Look for the entry `openwifi.restapi.host.0.port`.
|
||||
| Port | Description | Configurable |
|
||||
| :--- | :--- |:------------:|
|
||||
| 15002 | Default port from the devices to the OWGW | yes |
|
||||
| 16002 | Default port for REST API Access to the OWGW | yes |
|
||||
| 5912 | Default port for RTTY connection | yes |
|
||||
| 5913 | Defailt port for RTTY connection | yes |
|
||||
|
||||
## Kafka integration
|
||||
So what about Kafka? Well, the gateway has basic integration with Kafka. It is turned off by default, to turn it on, in the configuration:
|
||||
|
||||
```asm
|
||||
openwifi.kafka.enable = false
|
||||
openwifi.kafka.brokerlist = 127.0.0.1:9092
|
||||
openwifi.kafka.commit = false
|
||||
openwifi.kafka.queue.buffering.max.ms = 50
|
||||
```
|
||||
|
||||
#### `openwifi.kafka.enable`
|
||||
Kind of obvious but hey, set `true` or `false`. Default is `false`
|
||||
|
||||
#### `openwifi.kafka.brokerlist`
|
||||
This is a comma separator list of the brokers in your `kafka` deployment.
|
||||
|
||||
#### Kafka topics
|
||||
## Kafka topics
|
||||
Toe read more about Kafka, follow the [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/KAFKA.md)
|
||||
|
||||
#### Securing `kafka`
|
||||
This is beyond the scope of this document. As it stands today, the communication between the gateway and `kafka` is expected to be behind a firewall.
|
||||
## Contributions
|
||||
We need more contributors. Should you wish to contribute,
|
||||
please follow the [contributions](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/CONTRIBUTING.md) document.
|
||||
|
||||
#### `iptocountry` feature
|
||||
In the UI, you will notice the presence of small flags showing where the device connections are from. This feature is
|
||||
available through the `iptocountry` settings in the configuration. This feature is then also available through the `OpenAPI` for the CLI
|
||||
and other applications.
|
||||
## Pull Requests
|
||||
Please create a branch with the Jira addressing the issue you are fixing or the feature you are implementing.
|
||||
Create a pull-request from the branch into master.
|
||||
|
||||
##### Config file entries
|
||||
In the configuration file, you must include the following lines:
|
||||
|
||||
```asm
|
||||
iptocountry.default = US
|
||||
iptocountry.provider = ipinfo
|
||||
#iptocountry.provider = ipdata
|
||||
#iptocountry.provider = ipdata
|
||||
iptocountry.ipinfo.token =
|
||||
#ip2location.ipinfo.token =
|
||||
#iptocountry.ipdata.apikey =
|
||||
#iptocountry.ip2location.apikey =
|
||||
```
|
||||
So you select your provider with the `iptocountry.provider` be specifying ipinfo, or ipdata, or ip2location.
|
||||
And then you provide the corresponding api key or token.
|
||||
Only select one. If you select 2, undefined behaviour. All the line you do not need, just put a `#` before to comment it
|
||||
out.
|
||||
You will find the supported providers at: `ip2location.com`, `ipinfo.io`, or `ipdata.co`. You MUST supply a valid default
|
||||
country code in `iptocountry.default`.
|
||||
|
||||
## Contributors
|
||||
We love ya! We need more of ya! If you want to contribute, make sure you review
|
||||
the [coding style](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/CODING_STYLE.md) document.
|
||||
Feel free to ask questions and post issues.
|
||||
## Additional OWSDK Microservices
|
||||
Here is a list of additional OWSDK microservices
|
||||
| Name | Description | Link | OpenAPI |
|
||||
| :--- | :--- | :---: | :---: |
|
||||
| OWSEC | Security Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralsec) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml) |
|
||||
| OWGW | Controller Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/openapi/owgw.yaml) |
|
||||
| OWFMS | Firmware Management Service | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralfms) | [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralfms/blob/main/openapi/owfms.yaml) |
|
||||
| OWPROV | Provisioning Service | [here](https://github.com/Telecominfraproject/wlan-cloud-owprov) | [here](https://github.com/Telecominfraproject/wlan-cloud-owprov/blob/main/openapi/owprov.yaml) |
|
||||
| OWANALYTICS | Analytics Service | [here](https://github.com/Telecominfraproject/wlan-cloud-analytics) | [here](https://github.com/Telecominfraproject/wlan-cloud-analytics/blob/main/openapi/owanalytics.yaml) |
|
||||
| OWSUB | Subscriber Service | [here](https://github.com/Telecominfraproject/wlan-cloud-userportal) | [here](https://github.com/Telecominfraproject/wlan-cloud-userportal/blob/main/openapi/userportal.yaml) |
|
||||
|
||||
@@ -6,35 +6,36 @@ 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"} \
|
||||
FILEUPLOADER_MAXSIZE=${FILEUPLOADER_MAXSIZE:-"10000"} \
|
||||
SERVICE_KEY=${SERVICE_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
|
||||
SERVICE_KEY_PASSWORD=${SERVICE_KEY_PASSWORD:-"mypassword"} \
|
||||
SYSTEM_DATA=${SYSTEM_DATA:-"\$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 +52,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 +65,42 @@ 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"} \
|
||||
LOGGING_LEVEL=${LOGGING_LEVEL:-"information"} \
|
||||
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 "$@"
|
||||
|
||||
@@ -43,6 +43,7 @@ services:
|
||||
rttys:
|
||||
servicePort: 5912
|
||||
targetPort: 5912
|
||||
protocol: TCP
|
||||
rttys-view:
|
||||
servicePort: 5913
|
||||
targetPort: 5913
|
||||
|
||||
BIN
images/project/logo.png
Normal file
BIN
images/project/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
165
images/project/logo.svg
Normal file
165
images/project/logo.svg
Normal file
@@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 141.5 185.6" style="enable-background:new 0 0 141.5 185.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#414141;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
.st2{fill:#FED206;}
|
||||
.st3{fill:#EB6F53;}
|
||||
.st4{fill:#3BA9B6;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M120.7,183.9H21.5c-10.8,0-19.5-8.7-19.5-19.5V20.5c0-10.8,8.7-19.5,19.5-19.5h99.2
|
||||
c10.8,0,19.5,8.7,19.5,19.5v143.9C140.2,175.2,131.5,183.9,120.7,183.9z"/>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M46.3,166.2v-3.4h-1.2v-0.6h3.1v0.6H47v3.4H46.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M49,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H49z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M52.6,166.2v-4h0.7v3.4h1.8v0.6H52.6z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M55.7,166.2v-4h2.7v0.6h-2v1h2v0.6h-2v1.1h2v0.6H55.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M59.1,164.2c0-1.2,0.9-2.1,2.1-2.1c0.8,0,1.3,0.4,1.6,0.9l-0.6,0.3c-0.2-0.3-0.6-0.6-1-0.6
|
||||
c-0.8,0-1.4,0.6-1.4,1.4c0,0.8,0.6,1.4,1.4,1.4c0.4,0,0.8-0.3,1-0.6l0.6,0.3c-0.3,0.5-0.8,0.9-1.6,0.9
|
||||
C60,166.3,59.1,165.5,59.1,164.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M63.2,164.2c0-1.2,0.8-2.1,2-2.1c1.2,0,2,0.9,2,2.1c0,1.2-0.8,2.1-2,2.1C64,166.3,63.2,165.4,63.2,164.2z
|
||||
M66.5,164.2c0-0.8-0.5-1.4-1.3-1.4c-0.8,0-1.3,0.6-1.3,1.4c0,0.8,0.5,1.4,1.3,1.4C66,165.7,66.5,165,66.5,164.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M71.3,166.2v-3.1l-1.2,3.1h-0.3l-1.2-3.1v3.1h-0.7v-4h1l1.1,2.7l1.1-2.7h1v4H71.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M75.7,166.2v-4h0.7v4H75.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M80.4,166.2l-2.1-2.8v2.8h-0.7v-4h0.7l2,2.8v-2.8h0.7v4H80.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M82.3,166.2v-4H85v0.6h-2v1h2v0.6h-2v1.7H82.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M87.9,166.2l-0.9-1.5h-0.7v1.5h-0.7v-4h1.7c0.8,0,1.3,0.5,1.3,1.2c0,0.7-0.5,1.1-0.9,1.2l1,1.6H87.9z
|
||||
M88,163.5c0-0.4-0.3-0.6-0.7-0.6h-1v1.3h1C87.7,164.1,88,163.9,88,163.5z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M92.4,166.2l-0.3-0.8h-1.8l-0.3,0.8h-0.8l1.6-4h0.9l1.6,4H92.4z M91.2,162.9l-0.7,1.9h1.4L91.2,162.9z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M95.8,166.2v-4h1.5c0.8,0,1.2,0.5,1.2,1.2c0,0.6-0.4,1.2-1.2,1.2h-1.2v1.7H95.8z M98.2,163.4
|
||||
c0-0.5-0.3-0.9-0.9-0.9h-1.1v1.7h1.1C97.8,164.3,98.2,163.9,98.2,163.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M101.5,166.2l-1.1-1.6h-0.9v1.6h-0.3v-4h1.5c0.7,0,1.2,0.4,1.2,1.2c0,0.7-0.5,1.1-1.1,1.1l1.2,1.7H101.5z
|
||||
M101.6,163.4c0-0.5-0.4-0.9-0.9-0.9h-1.1v1.7h1.1C101.2,164.3,101.6,163.9,101.6,163.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M102.8,164.2c0-1.2,0.8-2.1,1.9-2.1c1.2,0,1.9,0.9,1.9,2.1c0,1.2-0.8,2.1-1.9,2.1
|
||||
C103.6,166.3,102.8,165.4,102.8,164.2z M106.3,164.2c0-1-0.6-1.7-1.6-1.7c-1,0-1.6,0.7-1.6,1.7c0,1,0.6,1.7,1.6,1.7
|
||||
C105.7,166,106.3,165.2,106.3,164.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M106.9,165.8l0.2-0.3c0.2,0.2,0.4,0.4,0.8,0.4c0.5,0,0.9-0.4,0.9-0.9v-2.8h0.3v2.8c0,0.8-0.5,1.2-1.2,1.2
|
||||
C107.5,166.3,107.2,166.1,106.9,165.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M110.4,166.2v-4h2.5v0.3h-2.2v1.5h2.1v0.3h-2.1v1.6h2.2v0.3H110.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M113.5,164.2c0-1.2,0.9-2.1,2-2.1c0.6,0,1.1,0.3,1.5,0.7l-0.3,0.2c-0.3-0.3-0.7-0.6-1.2-0.6
|
||||
c-0.9,0-1.7,0.7-1.7,1.7c0,1,0.7,1.7,1.7,1.7c0.5,0,0.9-0.2,1.2-0.6l0.3,0.2c-0.4,0.4-0.8,0.7-1.5,0.7
|
||||
C114.4,166.3,113.5,165.5,113.5,164.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M118.7,166.2v-3.7h-1.3v-0.3h2.9v0.3H119v3.7H118.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<polygon class="st1" points="26.3,163.8 31.6,158.5 36.9,163.8 37.7,163.8 31.6,157.6 25.5,163.8 "/>
|
||||
<polygon class="st1" points="36.9,164.7 31.6,170 26.3,164.7 25.5,164.7 31.6,170.8 37.7,164.7 "/>
|
||||
<polygon class="st1" points="31,163.8 36.3,158.5 41.6,163.8 42.5,163.8 36.3,157.6 30.2,163.8 "/>
|
||||
<polygon class="st1" points="41.6,164.7 36.3,170 31,164.7 30.2,164.7 36.3,170.8 42.5,164.7 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M33.2,100.7c-4.6,0-8.3,3.7-8.3,8.3s3.7,8.3,8.3,8.3s8.3-3.7,8.3-8.3S37.8,100.7,33.2,100.7z"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st2" d="M33.2,35.2c40.7,0,73.8,33.1,73.8,73.8c0,0.7,0,1.4,0,2.1c0,1.7,0.6,3.3,1.7,4.6c1.2,1.2,2.8,1.9,4.5,2
|
||||
l0.2,0c3.5,0,6.3-2.7,6.4-6.2c0-0.8,0-1.7,0-2.5c0-47.7-38.8-86.6-86.6-86.6c-0.8,0-1.7,0-2.5,0c-1.7,0-3.3,0.8-4.5,2
|
||||
c-1.2,1.2-1.8,2.9-1.7,4.6c0.1,3.5,3,6.3,6.6,6.2C31.8,35.2,32.5,35.2,33.2,35.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st3" d="M33.2,60.5c26.7,0,48.5,21.7,48.5,48.5c0,0.6,0,1.3,0,2c-0.1,1.7,0.5,3.3,1.7,4.6c1.2,1.3,2.7,2,4.4,2.1
|
||||
c1.7,0.1,3.3-0.5,4.6-1.7c1.2-1.2,2-2.7,2-4.4c0-0.9,0.1-1.8,0.1-2.6c0-33.8-27.5-61.2-61.2-61.2c-0.8,0-1.6,0-2.6,0.1
|
||||
c-1.7,0.1-3.3,0.8-4.4,2.1c-1.2,1.3-1.8,2.9-1.7,4.6s0.8,3.3,2.1,4.4c1.3,1.2,2.9,1.8,4.6,1.7C31.9,60.5,32.6,60.5,33.2,60.5z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st4" d="M33.2,86.7c12.3,0,22.3,10,22.3,22.3c0,0.5,0,1.1-0.1,1.8c-0.3,3.5,2.3,6.6,5.8,6.9
|
||||
c3.5,0.3,6.6-2.3,6.9-5.8c0.1-1,0.1-1.9,0.1-2.8c0-19.3-15.7-35.1-35.1-35.1c-0.9,0-1.8,0-2.8,0.1c-1.7,0.1-3.2,0.9-4.3,2.2
|
||||
c-1.1,1.3-1.6,2.9-1.5,4.6c0.1,1.7,0.9,3.2,2.2,4.3c1.3,1.1,2.9,1.6,4.6,1.5C32.1,86.7,32.7,86.7,33.2,86.7z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M35.8,130.4c1.1,0.6,2.1,1.5,2.7,2.6c0.7,1.1,1,2.3,1,3.7s-0.3,2.6-1,3.7c-0.7,1.1-1.6,2-2.7,2.6
|
||||
c-1.1,0.6-2.4,1-3.8,1s-2.7-0.3-3.8-1c-1.1-0.6-2.1-1.5-2.7-2.6c-0.7-1.1-1-2.3-1-3.7c0-1.3,0.3-2.6,1-3.7c0.7-1.1,1.6-2,2.7-2.6
|
||||
c1.1-0.6,2.4-0.9,3.8-0.9C33.4,129.5,34.7,129.8,35.8,130.4z M29.9,132.9c-0.7,0.4-1.2,0.9-1.6,1.6s-0.6,1.4-0.6,2.2
|
||||
c0,0.8,0.2,1.6,0.6,2.3c0.4,0.7,0.9,1.2,1.6,1.6c0.7,0.4,1.4,0.6,2.1,0.6c0.8,0,1.5-0.2,2.1-0.6c0.6-0.4,1.2-0.9,1.5-1.6
|
||||
c0.4-0.7,0.6-1.4,0.6-2.3c0-0.8-0.2-1.6-0.6-2.2s-0.9-1.2-1.5-1.6c-0.6-0.4-1.4-0.6-2.1-0.6C31.3,132.3,30.6,132.5,29.9,132.9z"/>
|
||||
<path class="st1" d="M50.6,133.6c0.8,0.5,1.4,1.1,1.8,2c0.4,0.8,0.6,1.8,0.6,2.9c0,1.1-0.2,2-0.6,2.8c-0.4,0.8-1,1.5-1.8,1.9
|
||||
c-0.8,0.5-1.6,0.7-2.6,0.7c-0.7,0-1.4-0.1-2-0.4s-1.1-0.7-1.5-1.2v5.4h-3.1V133h3.1v1.6c0.4-0.5,0.9-1,1.4-1.2s1.2-0.4,2-0.4
|
||||
C48.9,132.9,49.8,133.1,50.6,133.6z M49.1,140.5c0.5-0.6,0.7-1.3,0.7-2.2c0-0.9-0.2-1.6-0.7-2.1c-0.5-0.6-1.1-0.8-1.9-0.8
|
||||
s-1.4,0.3-1.9,0.8c-0.5,0.6-0.8,1.3-0.8,2.1c0,0.9,0.2,1.6,0.8,2.2s1.1,0.8,1.9,0.8S48.6,141,49.1,140.5z"/>
|
||||
<path class="st1" d="M63.4,134.4c0.9,1,1.4,2.4,1.4,4.2c0,0.3,0,0.6,0,0.7H57c0.2,0.7,0.5,1.2,1,1.6c0.5,0.4,1.1,0.6,1.8,0.6
|
||||
c0.5,0,1-0.1,1.5-0.3s0.9-0.5,1.3-0.9l1.6,1.6c-0.5,0.6-1.2,1.1-2,1.4c-0.8,0.3-1.6,0.5-2.6,0.5c-1.1,0-2.1-0.2-3-0.7
|
||||
s-1.5-1.1-2-1.9c-0.5-0.8-0.7-1.8-0.7-2.9c0-1.1,0.2-2.1,0.7-2.9s1.1-1.5,2-1.9c0.8-0.5,1.8-0.7,2.9-0.7
|
||||
C61.2,132.9,62.5,133.4,63.4,134.4z M61.8,137.5c0-0.7-0.3-1.3-0.7-1.7s-1-0.6-1.7-0.6c-0.7,0-1.2,0.2-1.7,0.6
|
||||
c-0.4,0.4-0.7,1-0.9,1.7H61.8z"/>
|
||||
<path class="st1" d="M76.2,134c0.7,0.7,1.1,1.7,1.1,3v6.8h-3.1v-5.9c0-0.7-0.2-1.2-0.6-1.6s-0.9-0.6-1.5-0.6
|
||||
c-0.8,0-1.4,0.3-1.8,0.8c-0.4,0.5-0.7,1.2-0.7,2v5.3h-3.1V133h3.1v1.9c0.7-1.3,2-2,3.7-2C74.6,132.8,75.5,133.2,76.2,134z"/>
|
||||
<path class="st1" d="M96,129.7h3.3l-4.7,14h-3.3l-2.9-10.1l-3,10.1h-3.2l-4.7-14h3.4l3,10.7l3-10.7H90l3.1,10.7L96,129.7z"/>
|
||||
<path class="st1" d="M103.3,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
|
||||
c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C102.6,128.2,103,128.3,103.3,128.7z M100.6,133h3.1
|
||||
v10.8h-3.1V133z"/>
|
||||
<path class="st1" d="M106.5,129.7h10.1l0,2.6h-6.9v3.4h6.3v2.6h-6.3v5.3h-3.2V129.7z"/>
|
||||
<path class="st1" d="M120.9,128.7c0.3,0.3,0.5,0.7,0.5,1.2s-0.2,0.9-0.5,1.2c-0.3,0.3-0.7,0.5-1.2,0.5c-0.5,0-0.9-0.2-1.2-0.5
|
||||
c-0.3-0.3-0.5-0.7-0.5-1.2c0-0.5,0.2-0.9,0.5-1.2c0.3-0.3,0.7-0.5,1.2-0.5C120.1,128.2,120.5,128.3,120.9,128.7z M118.1,133h3.1
|
||||
v10.8h-3.1V133z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.0 KiB |
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
18
issues/WIFI-11388.txt
Normal file
18
issues/WIFI-11388.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Issue: https://telecominfraproject.atlassian.net/browse/WIFI-11388
|
||||
|
||||
Problem:
|
||||
If a configuration was accepted by the GW or Provisioning but is still not valid according to the firmware on teh device,
|
||||
the device will reject the configuration, however, that configuration is known as the kast good configuration in the GW.
|
||||
This mens that we will lock the device in a loop where it continuously wants to update the configuration to version X,
|
||||
and the device will continuously reject it.
|
||||
|
||||
Workaround:
|
||||
Simply send a valid configuration to the GW and this will allow the device you update and stop the cycle.
|
||||
|
||||
Fix:
|
||||
When a new configuration is submitted, store is a "pending". If it is accepted, move it to the current configuration. If
|
||||
not accepted, simply remove it. One corner case exists. For some configuration updates, the AP will never complete the
|
||||
update cycle, even if it has updated the configuration. In that case, we can detect the configuration during a connect
|
||||
later. At that moment, when we look for an upgrade, we must compare with the pending UUID and the current UUID. If it matches the pending,
|
||||
we know the last update worked. If it does not, we know to revert.
|
||||
|
||||
85
ols_samples/sample1.json
Normal file
85
ols_samples/sample1.json
Normal file
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"ethernet": [
|
||||
{
|
||||
"select-ports": [
|
||||
"Ethernet0",
|
||||
"Ethernet1",
|
||||
"Ethernet2",
|
||||
"Ethernet3",
|
||||
"Ethernet4",
|
||||
"Ethernet5",
|
||||
"Ethernet6",
|
||||
"Ethernet7"
|
||||
],
|
||||
"speed": 2500,
|
||||
"duplex": "full",
|
||||
"enabled": true,
|
||||
"poe": {
|
||||
"admin-mode": true,
|
||||
"power-limit": 60000
|
||||
}
|
||||
},
|
||||
{
|
||||
"select-ports": [
|
||||
"Ethernet8",
|
||||
"Ethernet9"
|
||||
],
|
||||
"speed": 10000,
|
||||
"duplex": "full",
|
||||
"media": "sfp-forced-1000sfp"
|
||||
}
|
||||
],
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "VLAN1",
|
||||
"vlan": {
|
||||
"id": 1
|
||||
},
|
||||
"ipv4": {
|
||||
"addressing": "dynamic"
|
||||
},
|
||||
"ethernet": [
|
||||
{
|
||||
"select-ports": [
|
||||
"Ethernet0",
|
||||
"Ethernet1",
|
||||
"Ethernet2",
|
||||
"Ethernet3",
|
||||
"Ethernet4",
|
||||
"Ethernet5",
|
||||
"Ethernet6",
|
||||
"Ethernet7",
|
||||
"Ethernet8",
|
||||
"Ethernet9"
|
||||
],
|
||||
"vlan-tag": "un-tagged"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"dhcp-snooping": {
|
||||
"filters": [
|
||||
"ack",
|
||||
"discover",
|
||||
"offer",
|
||||
"request",
|
||||
"solicit",
|
||||
"reply",
|
||||
"renew"
|
||||
]
|
||||
},
|
||||
"health": {
|
||||
"interval": 60
|
||||
},
|
||||
"statistics": {
|
||||
"interval": 120,
|
||||
"types": []
|
||||
}
|
||||
},
|
||||
"unit": {
|
||||
"leds-active": true,
|
||||
"usage-threshold": 95
|
||||
},
|
||||
"uuid": 1678263900
|
||||
}
|
||||
1014
openapi/owgw.yaml
1014
openapi/owgw.yaml
File diff suppressed because it is too large
Load Diff
@@ -52,7 +52,8 @@ openwifi.fileuploader.host.0.cert = ${FILEUPLOADER_HOST_CERT}
|
||||
openwifi.fileuploader.host.0.key = ${FILEUPLOADER_HOST_KEY}
|
||||
openwifi.fileuploader.host.0.key.password = ${FILEUPLOADER_HOST_KEY_PASSWORD}
|
||||
openwifi.fileuploader.path = ${FILEUPLOADER_PATH}
|
||||
openwifi.fileuploader.maxsize = 10000
|
||||
# maxsize in KB
|
||||
openwifi.fileuploader.maxsize = ${FILEUPLOADER_MAXSIZE}
|
||||
openwifi.fileuploader.uri = ${FILEUPLOADER_URI}
|
||||
|
||||
#
|
||||
@@ -85,6 +86,7 @@ iptocountry.ipdata.apikey = ${IPTOCOUNTRY_IPDATA_APIKEY}
|
||||
|
||||
autoprovisioning.process = ${AUTOPROVISIONING_PROCESS}
|
||||
|
||||
openwifi.session.timeout = ${DEVICE_SESSION_TIMEOUT}
|
||||
#
|
||||
# rtty
|
||||
#
|
||||
@@ -103,6 +105,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
|
||||
#############################
|
||||
@@ -138,7 +146,7 @@ storage.type.sqlite.db = devices.db
|
||||
storage.type.sqlite.idletime = 120
|
||||
storage.type.sqlite.maxsessions = 128
|
||||
|
||||
storage.type.postgresql.maxsessions = 64
|
||||
storage.type.postgresql.maxsessions = 250
|
||||
storage.type.postgresql.idletime = 60
|
||||
storage.type.postgresql.host = ${STORAGE_TYPE_POSTGRESQL_HOST}
|
||||
storage.type.postgresql.username = ${STORAGE_TYPE_POSTGRESQL_USERNAME}
|
||||
@@ -175,4 +183,4 @@ archiver.db.3.keep = 7
|
||||
########################################################################
|
||||
logging.type = console
|
||||
logging.path = $OWGW_ROOT/logs
|
||||
logging.level = information
|
||||
logging.level = ${LOGGING_LEVEL}
|
||||
|
||||
1849
regulatory/regulatory.txt
Normal file
1849
regulatory/regulatory.txt
Normal file
File diff suppressed because it is too large
Load Diff
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_ConfigAutoUpgradeAgent::Start() {
|
||||
poco_notice(Logger(), "Starting...");
|
||||
QueueManager_.start(*this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AP_WS_ConfigAutoUpgradeAgent::Stop() {
|
||||
poco_notice(Logger(), "Stopping...");
|
||||
Running_ = false;
|
||||
Queue_.wakeUpAll();
|
||||
QueueManager_.join();
|
||||
poco_notice(Logger(), "Stopped...");
|
||||
}
|
||||
|
||||
void AP_WS_ConfigAutoUpgradeAgent::run() {
|
||||
Utils::SetThreadName("auto:cfgmgr");
|
||||
Running_ = true;
|
||||
|
||||
while (Running_) {
|
||||
Poco::AutoPtr<Poco::Notification> NextMsg(Queue_.waitDequeueNotification());
|
||||
try {
|
||||
auto Entry = dynamic_cast<CheckConfiguration *>(NextMsg.get());
|
||||
if (Entry != nullptr) {
|
||||
GWObjects::Device DeviceInfo;
|
||||
std::string SerialNumber = Utils::IntToSerialNumber(Entry->serial_);
|
||||
if (StorageService()->GetDevice(SerialNumber, DeviceInfo)) {
|
||||
if(DeviceInfo.pendingUUID!=0 && Entry->uuid_==DeviceInfo.pendingUUID) {
|
||||
StorageService()->CompleteDeviceConfigurationChange(SerialNumber);
|
||||
SetDeviceCacheEntry(Entry->serial_, Utils::Now(), Entry->uuid_, 0);
|
||||
continue;
|
||||
}
|
||||
if(DeviceInfo.UUID==Entry->uuid_) {
|
||||
SetDeviceCacheEntry(Entry->serial_, Utils::Now(), Entry->uuid_, 0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(), "Exception occurred during run.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
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_ConfigAutoUpgradeAgent : public SubSystemServer, Poco::Runnable {
|
||||
public:
|
||||
int Start() final;
|
||||
void Stop() final;
|
||||
void run() final;
|
||||
|
||||
static auto instance() {
|
||||
static auto instance = new AP_WS_ConfigAutoUpgradeAgent;
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline void AddConfiguration(std::uint64_t serial, std::uint64_t config_uuid) {
|
||||
std::lock_guard Guard(CacheMutex_);
|
||||
auto hint = Cache_.find(serial);
|
||||
if(hint==end(Cache_)) {
|
||||
Cache_[serial] = { Utils::Now(),config_uuid , 0 };
|
||||
return;
|
||||
}
|
||||
if(hint->second.pending_config_==0) {
|
||||
hint->second.last_check_ = Utils::Now();
|
||||
hint->second.current_config_ = config_uuid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
inline void AddConfiguration(std::uint64_t serial, std::uint64_t config_uuid, std::uint64_t pending_config_uuid) {
|
||||
std::lock_guard Guard(CacheMutex_);
|
||||
auto hint = Cache_.find(serial);
|
||||
if(hint==end(Cache_)) {
|
||||
Cache_[serial] = { Utils::Now(), config_uuid , pending_config_uuid };
|
||||
return;
|
||||
}
|
||||
if(hint->second.pending_config_==0) {
|
||||
hint->second.last_check_ = Utils::Now();
|
||||
hint->second.current_config_ = config_uuid;
|
||||
hint->second.pending_config_ = pending_config_uuid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline ConfigurationCacheEntry GetSerialInfo(std::uint64_t serial) const {
|
||||
std::lock_guard Guard(CacheMutex_);
|
||||
auto hint = Cache_.find(serial);
|
||||
if(hint==end(Cache_)) {
|
||||
return {0,0,0};
|
||||
}
|
||||
return hint->second;
|
||||
}
|
||||
|
||||
inline bool UpdateConfiguration(std::uint64_t serial, std::uint64_t config) {
|
||||
|
||||
if(serial==0)
|
||||
return false;
|
||||
|
||||
std::lock_guard Guard(CacheMutex_);
|
||||
auto hint = Cache_.find(serial);
|
||||
if(hint!=end(Cache_)) {
|
||||
|
||||
if(hint->second.current_config_==config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(config==hint->second.pending_config_) {
|
||||
Queue_.enqueueNotification(new CheckConfiguration(serial,config));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(config!=hint->second.current_config_ && hint->second.pending_config_==0) {
|
||||
Queue_.enqueueNotification(new CheckConfiguration(serial,config));
|
||||
return true;
|
||||
}
|
||||
|
||||
if((Utils::Now()-hint->second.last_check_)<60*5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(hint->second.pending_config_!=0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void SetDeviceCacheEntry(std::uint64_t serial, std::uint64_t t, std::uint64_t uuid, std::uint64_t pending_uuid) {
|
||||
std::lock_guard Guard(CacheMutex_);
|
||||
Cache_[serial] = { t, uuid, pending_uuid };
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Poco::NotificationQueue Queue_;
|
||||
Poco::Thread QueueManager_;
|
||||
std::atomic_bool Running_=false;
|
||||
|
||||
mutable std::mutex CacheMutex_;
|
||||
std::map<std::uint64_t, ConfigurationCacheEntry> Cache_;
|
||||
|
||||
AP_WS_ConfigAutoUpgradeAgent() noexcept
|
||||
: SubSystemServer("AutoConfigUpgrade", "AUTO-CFG-MGR", "auto.config.updater") {
|
||||
}
|
||||
};
|
||||
|
||||
inline auto AP_WS_ConfigAutoUpgradeAgent() { return AP_WS_ConfigAutoUpgradeAgent::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,103 +4,95 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include <Poco/JSON/Parser.h>
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/Net/SocketNotification.h"
|
||||
#include "Poco/Net/SocketReactor.h"
|
||||
#include "Poco/Net/StreamSocket.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/Net/SocketNotification.h"
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/Net/WebSocket.h"
|
||||
#include <Poco/Data/Session.h>
|
||||
|
||||
#include "RESTObjects/RESTAPI_GWobjects.h"
|
||||
#include <AP_WS_Reactor_Pool.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class AP_WS_Connection {
|
||||
static constexpr int BufSize = 256000;
|
||||
static constexpr int BufSize = 512000;
|
||||
|
||||
public:
|
||||
explicit AP_WS_Connection( Poco::Net::HTTPServerRequest &request,
|
||||
Poco::Net::HTTPServerResponse &response,
|
||||
uint64_t connection_id,
|
||||
Poco::Logger &L,
|
||||
Poco::Net::SocketReactor &R);
|
||||
explicit AP_WS_Connection(Poco::Net::HTTPServerRequest &request,
|
||||
Poco::Net::HTTPServerResponse &response, uint64_t connection_id,
|
||||
Poco::Logger &L, std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> R);
|
||||
~AP_WS_Connection();
|
||||
|
||||
void EndConnection();
|
||||
void ProcessJSONRPCEvent(Poco::JSON::Object::Ptr & Doc);
|
||||
void ProcessJSONRPCEvent(Poco::JSON::Object::Ptr &Doc);
|
||||
void ProcessJSONRPCResult(Poco::JSON::Object::Ptr Doc);
|
||||
void ProcessWSFinalPayload();
|
||||
void ProcessIncomingFrame();
|
||||
void ProcessIncomingRadiusData(const Poco::JSON::Object::Ptr &Doc);
|
||||
|
||||
bool Send(const std::string &Payload);
|
||||
[[nodiscard]] bool Send(const std::string &Payload);
|
||||
[[nodiscard]] inline bool MustBeSecureRTTY() const { return RTTYMustBeSecure_; }
|
||||
|
||||
bool SendRadiusAuthenticationData(const unsigned char * buffer, std::size_t size);
|
||||
bool SendRadiusAccountingData(const unsigned char * buffer, std::size_t size);
|
||||
bool SendRadiusCoAData(const unsigned char * buffer, std::size_t size);
|
||||
bool SendRadiusAuthenticationData(const unsigned char *buffer, std::size_t size);
|
||||
bool SendRadiusAccountingData(const unsigned char *buffer, std::size_t size);
|
||||
bool SendRadiusCoAData(const unsigned char *buffer, std::size_t size);
|
||||
|
||||
void OnSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf);
|
||||
void OnSocketShutdown(const Poco::AutoPtr<Poco::Net::ShutdownNotification>& pNf);
|
||||
void OnSocketError(const Poco::AutoPtr<Poco::Net::ErrorNotification>& pNf);
|
||||
bool LookForUpgrade(uint64_t UUID, uint64_t & UpgradedUUID);
|
||||
static bool ExtractBase64CompressedData(const std::string & CompressedData, std::string & UnCompressedData, uint64_t compress_sz);
|
||||
void OnSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf);
|
||||
void OnSocketShutdown(const Poco::AutoPtr<Poco::Net::ShutdownNotification> &pNf);
|
||||
void OnSocketError(const Poco::AutoPtr<Poco::Net::ErrorNotification> &pNf);
|
||||
bool LookForUpgrade(Poco::Data::Session &Session, uint64_t UUID, uint64_t &UpgradedUUID);
|
||||
void LogException(const Poco::Exception &E);
|
||||
inline Poco::Logger & Logger() { return Logger_; }
|
||||
bool SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t interval, uint64_t TelemetryWebSocketTimer);
|
||||
bool SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t interval, uint64_t TelemetryKafkaTimer);
|
||||
inline Poco::Logger &Logger() { return Logger_; }
|
||||
bool SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t interval,
|
||||
uint64_t TelemetryWebSocketTimer,
|
||||
const std::vector<std::string> &TelemetryTypes);
|
||||
bool SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t interval,
|
||||
uint64_t TelemetryKafkaTimer,
|
||||
const std::vector<std::string> &TelemetryTypes);
|
||||
bool StopWebSocketTelemetry(uint64_t RPCID);
|
||||
bool StopKafkaTelemetry(uint64_t RPCID);
|
||||
|
||||
inline void GetLastStats(std::string &LastStats) const {
|
||||
std::shared_lock G(ConnectionMutex_);
|
||||
LastStats = RawLastStats_;
|
||||
}
|
||||
|
||||
inline void SetLastStats(const std::string &LastStats) {
|
||||
std::unique_lock G(ConnectionMutex_);
|
||||
RawLastStats_ = LastStats;
|
||||
}
|
||||
|
||||
inline void SetLastHealthCheck(const GWObjects::HealthCheck &H) {
|
||||
std::unique_lock G(ConnectionMutex_);
|
||||
RawLastHealthcheck_ = H;
|
||||
inline void GetLastStats(std::string &LastStats) {
|
||||
if(!Dead_) {
|
||||
std::lock_guard G(ConnectionMutex_);
|
||||
LastStats = RawLastStats_;
|
||||
}
|
||||
}
|
||||
|
||||
inline void GetLastHealthCheck(GWObjects::HealthCheck &H) {
|
||||
std::shared_lock G(ConnectionMutex_);
|
||||
H = RawLastHealthcheck_;
|
||||
if(!Dead_) {
|
||||
std::lock_guard G(ConnectionMutex_);
|
||||
H = RawLastHealthcheck_;
|
||||
}
|
||||
}
|
||||
|
||||
inline void GetState(GWObjects::ConnectionState &State) const {
|
||||
std::shared_lock G(ConnectionMutex_);
|
||||
State = State_;
|
||||
inline void GetState(GWObjects::ConnectionState &State) {
|
||||
if(!Dead_) {
|
||||
std::lock_guard G(ConnectionMutex_);
|
||||
State = State_;
|
||||
}
|
||||
}
|
||||
|
||||
inline void GetRestrictions(GWObjects::DeviceRestrictions & R) const {
|
||||
std::shared_lock G(ConnectionMutex_);
|
||||
R = Restrictions_;
|
||||
inline GWObjects::DeviceRestrictions GetRestrictions() {
|
||||
std::lock_guard G(ConnectionMutex_);
|
||||
return Restrictions_;
|
||||
}
|
||||
|
||||
void Process_connect(Poco::JSON::Object::Ptr ParamsObj, const std::string &Serial);
|
||||
void Process_state(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_healthcheck(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_log(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_crashlog(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_ping(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_cfgpending(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_recovery(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_deviceupdate(Poco::JSON::Object::Ptr ParamsObj, std::string &Serial);
|
||||
void Process_telemetry(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_venuebroadcast(Poco::JSON::Object::Ptr ParamsObj);
|
||||
[[nodiscard]] inline bool HasGPS() const { return hasGPS_; }
|
||||
[[nodiscard]] bool ValidatedDevice();
|
||||
|
||||
bool ValidatedDevice();
|
||||
|
||||
inline bool GetTelemetryParameters(bool & Reporting, uint64_t & Interval,
|
||||
uint64_t & WebSocketTimer, uint64_t & KafkaTimer,
|
||||
uint64_t &WebSocketCount, uint64_t & KafkaCount,
|
||||
inline bool GetTelemetryParameters(bool &Reporting, uint64_t &Interval,
|
||||
uint64_t &WebSocketTimer, uint64_t &KafkaTimer,
|
||||
uint64_t &WebSocketCount, uint64_t &KafkaCount,
|
||||
uint64_t &WebSocketPackets,
|
||||
uint64_t &KafkaPackets ) const {
|
||||
uint64_t &KafkaPackets) const {
|
||||
Reporting = TelemetryReporting_;
|
||||
WebSocketTimer = TelemetryWebSocketTimer_;
|
||||
KafkaTimer = TelemetryKafkaTimer_;
|
||||
@@ -114,48 +106,75 @@ namespace OpenWifi {
|
||||
|
||||
friend class AP_WS_Server;
|
||||
|
||||
inline GWObjects::DeviceRestrictions Restrictions() const {
|
||||
std::shared_lock G(ConnectionMutex_);
|
||||
return Restrictions_;
|
||||
}
|
||||
void Start();
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex ConnectionMutex_;
|
||||
std::shared_mutex TelemetryMutex_;
|
||||
Poco::Logger &Logger_;
|
||||
Poco::Net::SocketReactor &Reactor_;
|
||||
mutable std::recursive_mutex ConnectionMutex_;
|
||||
std::mutex TelemetryMutex_;
|
||||
Poco::Logger &Logger_;
|
||||
std::shared_ptr<Poco::Net::SocketReactor> Reactor_;
|
||||
std::shared_ptr<LockedDbSession> DbSession_;
|
||||
std::unique_ptr<Poco::Net::WebSocket> WS_;
|
||||
std::string SerialNumber_;
|
||||
uint64_t SerialNumberInt_=0;
|
||||
std::string Compatible_;
|
||||
std::atomic_bool Registered_ = false ;
|
||||
std::string CId_;
|
||||
std::string CN_;
|
||||
uint64_t Errors_=0;
|
||||
Poco::Net::IPAddress PeerAddress_;
|
||||
volatile bool TelemetryReporting_ = false;
|
||||
volatile uint64_t TelemetryWebSocketRefCount_ = 0;
|
||||
volatile uint64_t TelemetryKafkaRefCount_ = 0;
|
||||
volatile uint64_t TelemetryWebSocketTimer_ = 0;
|
||||
volatile uint64_t TelemetryKafkaTimer_ = 0 ;
|
||||
volatile uint64_t TelemetryInterval_ = 0;
|
||||
volatile uint64_t TelemetryWebSocketPackets_=0;
|
||||
volatile uint64_t TelemetryKafkaPackets_=0;
|
||||
GWObjects::ConnectionState State_;
|
||||
std::string RawLastStats_;
|
||||
GWObjects::HealthCheck RawLastHealthcheck_;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> ConnectionStart_ = std::chrono::high_resolution_clock::now();
|
||||
std::string SerialNumber_;
|
||||
uint64_t SerialNumberInt_ = 0;
|
||||
std::string Compatible_;
|
||||
std::atomic_bool Registered_ = false;
|
||||
std::string CId_;
|
||||
std::string CN_;
|
||||
uint64_t Errors_ = 0;
|
||||
Poco::Net::IPAddress PeerAddress_;
|
||||
volatile bool TelemetryReporting_ = false;
|
||||
std::atomic_uint64_t TelemetryWebSocketRefCount_ = 0;
|
||||
std::atomic_uint64_t TelemetryKafkaRefCount_ = 0;
|
||||
std::atomic_uint64_t TelemetryWebSocketTimer_ = 0;
|
||||
std::atomic_uint64_t TelemetryKafkaTimer_ = 0;
|
||||
std::atomic_uint64_t TelemetryInterval_ = 0;
|
||||
std::atomic_uint64_t TelemetryWebSocketPackets_ = 0;
|
||||
std::atomic_uint64_t TelemetryKafkaPackets_ = 0;
|
||||
GWObjects::ConnectionState State_;
|
||||
Utils::CompressedString RawLastStats_;
|
||||
GWObjects::HealthCheck RawLastHealthcheck_;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> ConnectionStart_ =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double, std::milli> ConnectionCompletionTime_{0.0};
|
||||
std::atomic_flag Dead_=false;
|
||||
std::atomic_bool DeviceValidated_=false;
|
||||
std::atomic_bool Valid_=false;
|
||||
OpenWifi::GWObjects::DeviceRestrictions Restrictions_;
|
||||
std::atomic<bool> Dead_ = false;
|
||||
std::atomic_bool DeviceValidated_ = false;
|
||||
OpenWifi::GWObjects::DeviceRestrictions Restrictions_;
|
||||
bool RTTYMustBeSecure_ = false;
|
||||
bool hasGPS_=false;
|
||||
std::double_t memory_used_=0.0, cpu_load_ = 0.0, temperature_ = 0.0;
|
||||
std::uint64_t uuid_=0;
|
||||
bool Simulated_=false;
|
||||
std::atomic_uint64_t LastContact_=0;
|
||||
Poco::Buffer<char> IncomingFrame_;
|
||||
|
||||
static inline std::atomic_uint64_t ConcurrentStartingDevices_=0;
|
||||
static inline std::atomic_uint64_t ConcurrentStartingDevices_ = 0;
|
||||
|
||||
bool StartTelemetry(uint64_t RPCID);
|
||||
bool StartTelemetry(uint64_t RPCID, const std::vector<std::string> &TelemetryTypes);
|
||||
bool StopTelemetry(uint64_t RPCID);
|
||||
void UpdateCounts();
|
||||
static void DeviceDisconnectionCleanup(const std::string &SerialNumber, std::uint64_t uuid);
|
||||
void SetLastStats(const std::string &LastStats);
|
||||
void Process_connect(Poco::JSON::Object::Ptr ParamsObj, const std::string &Serial);
|
||||
void Process_state(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_healthcheck(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_log(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_crashlog(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_ping(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_cfgpending(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_recovery(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_deviceupdate(Poco::JSON::Object::Ptr ParamsObj, std::string &Serial);
|
||||
void Process_telemetry(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_venuebroadcast(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_event(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_wifiscan(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_alarm(Poco::JSON::Object::Ptr ParamsObj);
|
||||
void Process_rebootLog(Poco::JSON::Object::Ptr ParamsObj);
|
||||
|
||||
inline void SetLastHealthCheck(const GWObjects::HealthCheck &H) {
|
||||
RawLastHealthcheck_ = H;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
111
src/AP_WS_LookForUpgrade.cpp
Normal file
111
src/AP_WS_LookForUpgrade.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
|
||||
#include <AP_WS_Connection.h>
|
||||
#include "ConfigurationCache.h"
|
||||
#include "UI_GW_WebSocketNotifications.h"
|
||||
#include "CommandManager.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
bool AP_WS_Connection::LookForUpgrade(Poco::Data::Session &Session, const uint64_t UUID, uint64_t &UpgradedUUID) {
|
||||
|
||||
// A UUID of zero means ignore updates for that connection.
|
||||
if (UUID == 0)
|
||||
return false;
|
||||
|
||||
uint64_t GoodConfig = GetCurrentConfigurationID(SerialNumberInt_);
|
||||
if (GoodConfig && (GoodConfig == UUID || GoodConfig == State_.PendingUUID)) {
|
||||
UpgradedUUID = UUID;
|
||||
State_.PendingUUID = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
GWObjects::Device D;
|
||||
if (!StorageService()->GetDevice(Session,SerialNumber_, D)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(State_.PendingUUID!=0 && UUID==State_.PendingUUID) {
|
||||
// so we sent an upgrade to a device, and now it is completing now...
|
||||
UpgradedUUID = UUID;
|
||||
StorageService()->CompleteDeviceConfigurationChange(Session, SerialNumber_);
|
||||
State_.PendingUUID = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// dont upgrade a switch if it does not have a real config. Config will always be more than 20 characters
|
||||
if (D.DeviceType==Platforms::SWITCH && D.Configuration.size()<20) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Config::Config Cfg(D.Configuration);
|
||||
// if this is a broken device (UUID==0) just fix it
|
||||
auto StoredConfigurationUUID = Cfg.UUID();
|
||||
if(D.UUID==0) {
|
||||
D.UUID = StoredConfigurationUUID;
|
||||
}
|
||||
|
||||
|
||||
if (D.UUID == UUID) {
|
||||
D.UUID = UpgradedUUID = UUID;
|
||||
State_.PendingUUID = D.pendingUUID = 0;
|
||||
D.pendingConfiguration.clear();
|
||||
D.pendingConfigurationCmd.clear();
|
||||
StorageService()->UpdateDevice(Session, D);
|
||||
SetCurrentConfigurationID(SerialNumberInt_, UUID);
|
||||
// std::cout << __LINE__ << ": " << SerialNumber_ << " GoodConfig: " << GoodConfig << " UUID:" << UUID << " Pending:" << State_.PendingUUID << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UUID > D.UUID) {
|
||||
// so we have a problem, the device has a newer config than we have. So we need to
|
||||
// make sure our config is newer.
|
||||
D.UUID = UUID + 2;
|
||||
UpgradedUUID = D.UUID;
|
||||
// std::cout << __LINE__ << ": " << SerialNumber_ << " GoodConfig: " << GoodConfig << " UUID:" << UUID << " Pending:" << State_.PendingUUID << std::endl;
|
||||
}
|
||||
|
||||
Cfg.SetUUID(D.UUID);
|
||||
D.Configuration = Cfg.get();
|
||||
D.pendingUUID = State_.PendingUUID = UpgradedUUID = D.UUID;
|
||||
StorageService()->UpdateDevice(Session, D);
|
||||
|
||||
GWObjects::CommandDetails Cmd;
|
||||
Cmd.SerialNumber = SerialNumber_;
|
||||
Cmd.UUID = MicroServiceCreateUUID();
|
||||
Cmd.SubmittedBy = uCentralProtocol::SUBMITTED_BY_SYSTEM;
|
||||
Cmd.Status = uCentralProtocol::PENDING;
|
||||
Cmd.Command = uCentralProtocol::CONFIGURE;
|
||||
Poco::JSON::Parser P;
|
||||
auto ParsedConfig = P.parse(D.Configuration).extract<Poco::JSON::Object::Ptr>();
|
||||
Poco::JSON::Object Params;
|
||||
Params.set(uCentralProtocol::SERIAL, SerialNumber_);
|
||||
Params.set(uCentralProtocol::UUID, D.UUID);
|
||||
Params.set(uCentralProtocol::WHEN, 0);
|
||||
Params.set(uCentralProtocol::CONFIG, ParsedConfig);
|
||||
|
||||
std::ostringstream O;
|
||||
Poco::JSON::Stringifier::stringify(Params, O);
|
||||
Cmd.Details = O.str();
|
||||
poco_information(Logger_,
|
||||
fmt::format("CFG-UPGRADE({}): Current ID: {}, newer configuration {}.",
|
||||
CId_, UUID, D.UUID));
|
||||
bool Sent;
|
||||
|
||||
StorageService()->AddCommand(SerialNumber_, Cmd,
|
||||
Storage::CommandExecutionType::COMMAND_EXECUTED);
|
||||
CommandManager()->PostCommand(
|
||||
CommandManager()->Next_RPC_ID(), APCommands::to_apcommand(Cmd.Command.c_str()),
|
||||
SerialNumber_, Cmd.Command, Params, Cmd.UUID, Sent, false, false);
|
||||
|
||||
GWWebSocketNotifications::SingleDeviceConfigurationChange_t Notification;
|
||||
Notification.content.serialNumber = D.SerialNumber;
|
||||
Notification.content.oldUUID = UUID;
|
||||
Notification.content.newUUID = UpgradedUUID;
|
||||
GWWebSocketNotifications::DeviceConfigurationChange(Notification);
|
||||
|
||||
// std::cout << __LINE__ << ": " << SerialNumber_ << " GoodConfig: " << GoodConfig << " UUID:" << UUID <<
|
||||
// " Pending:" << State_.PendingUUID << " Upgraded:" << UpgradedUUID << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
28
src/AP_WS_Process_alarm.cpp
Normal file
28
src/AP_WS_Process_alarm.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-01-22.
|
||||
//
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_alarm(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
poco_trace(Logger_, fmt::format("Alarm data received for {}", SerialNumber_));
|
||||
|
||||
if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
KafkaManager()->PostMessage(KafkaTopics::ALERTS, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -9,8 +9,9 @@
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_cfgpending(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_, fmt::format(
|
||||
"INVALID-PROTOCOL({}): Device '{}' is not following protocol", CId_, CN_));
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
@@ -18,9 +19,10 @@ namespace OpenWifi {
|
||||
|
||||
[[maybe_unused]] uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
|
||||
[[maybe_unused]] uint64_t Active = ParamsObj->get(uCentralProtocol::ACTIVE);
|
||||
poco_trace(Logger_, fmt::format("CFG-PENDING({}): Active: {} Target: {}", CId_, Active, UUID));
|
||||
poco_trace(Logger_,
|
||||
fmt::format("CFG-PENDING({}): Active: {} Target: {}", CId_, Active, UUID));
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("CFG-PENDING({}): Missing some parameters", CId_));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -4,37 +4,76 @@
|
||||
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "AP_WS_Server.h"
|
||||
#include "StorageService.h"
|
||||
#include "FindCountry.h"
|
||||
#include "Daemon.h"
|
||||
#include "CentralConfig.h"
|
||||
#include "Daemon.h"
|
||||
#include "FindCountry.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "CommandManager.h"
|
||||
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
#include "firmware_revision_cache.h"
|
||||
|
||||
#include "UI_GW_WebSocketNotifications.h"
|
||||
#include <GWKafkaEvents.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void AP_WS_Connection::Process_connect(Poco::JSON::Object::Ptr ParamsObj, const std::string &Serial) {
|
||||
if (ParamsObj->has(uCentralProtocol::UUID) &&
|
||||
ParamsObj->has(uCentralProtocol::FIRMWARE) &&
|
||||
[[maybe_unused]] static void SendKafkaFirmwareUpdate(const std::string &SerialNumber,
|
||||
const std::string &OldFirmware,
|
||||
const std::string &NewFirmware) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Object EventDetails;
|
||||
EventDetails.set("oldFirmware", OldFirmware);
|
||||
EventDetails.set("newFirmware", NewFirmware);
|
||||
Poco::JSON::Object Event;
|
||||
Event.set("type", "device.firmware_change");
|
||||
Event.set("timestamp", Utils::Now());
|
||||
Event.set("payload", EventDetails);
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] static void SendKafkaDeviceNotProvisioned( const std::string &SerialNumber,
|
||||
const std::string &Firmware,
|
||||
const std::string &DeviceType,
|
||||
const std::string &IP) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Object EventDetails;
|
||||
EventDetails.set("firmware", Firmware);
|
||||
EventDetails.set("deviceType", DeviceType);
|
||||
EventDetails.set("IP", IP);
|
||||
Poco::JSON::Object Event;
|
||||
Event.set("type", "device.not_provisioned");
|
||||
Event.set("timestamp", Utils::Now());
|
||||
Event.set("payload", EventDetails);
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
|
||||
}
|
||||
}
|
||||
|
||||
void AP_WS_Connection::Process_connect(Poco::JSON::Object::Ptr ParamsObj,
|
||||
const std::string &Serial) {
|
||||
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::FIRMWARE) &&
|
||||
ParamsObj->has(uCentralProtocol::CAPABILITIES)) {
|
||||
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
|
||||
auto Firmware = ParamsObj->get(uCentralProtocol::FIRMWARE).toString();
|
||||
auto Capabilities = ParamsObj->getObject(uCentralProtocol::CAPABILITIES);
|
||||
|
||||
std::string DevicePassword;
|
||||
if(ParamsObj->has("password")) {
|
||||
DevicePassword = ParamsObj->get("password").toString();
|
||||
}
|
||||
|
||||
SerialNumber_ = Serial;
|
||||
SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_);
|
||||
|
||||
CommandManager()->ClearQueue(SerialNumberInt_);
|
||||
|
||||
AP_WS_Server()->SetSessionDetails(State_.sessionId,SerialNumberInt_);
|
||||
AP_WS_Server()->StartSession(State_.sessionId, SerialNumberInt_);
|
||||
|
||||
std::lock_guard Lock(ConnectionMutex_);
|
||||
Config::Capabilities Caps(Capabilities);
|
||||
Config::Capabilities Caps(Capabilities);
|
||||
|
||||
Compatible_ = Caps.Compatible();
|
||||
|
||||
@@ -44,119 +83,225 @@ namespace OpenWifi {
|
||||
State_.Address = Utils::FormatIPv6(WS_->peerAddress().toString());
|
||||
CId_ = SerialNumber_ + "@" + CId_;
|
||||
|
||||
auto Platform = Poco::toLower(Caps.Platform());
|
||||
|
||||
if(ParamsObj->has("reason")) {
|
||||
State_.connectReason = ParamsObj->get("reason").toString();
|
||||
}
|
||||
|
||||
auto IP = PeerAddress_.toString();
|
||||
if(IP.substr(0,7)=="::ffff:") {
|
||||
if (IP.substr(0, 7) == "::ffff:") {
|
||||
IP = IP.substr(7);
|
||||
}
|
||||
|
||||
bool RestrictedDevice = false;
|
||||
if(Capabilities->has("restrictions")){
|
||||
RestrictedDevice = true;
|
||||
if (Capabilities->has("restrictions")) {
|
||||
RestrictedDevice = true;
|
||||
Poco::JSON::Object::Ptr RestrictionObject = Capabilities->getObject("restrictions");
|
||||
Restrictions_.from_json(RestrictionObject);
|
||||
}
|
||||
|
||||
if (Capabilities->has("developer") && !Capabilities->isNull("developer")) {
|
||||
Restrictions_.developer = Capabilities->getValue<bool>("developer");
|
||||
}
|
||||
|
||||
if (Capabilities->has("secure-rtty")) {
|
||||
RTTYMustBeSecure_ = Capabilities->getValue<bool>("secure-rtty");
|
||||
}
|
||||
|
||||
State_.locale = FindCountryFromIP()->Get(IP);
|
||||
GWObjects::Device DeviceInfo;
|
||||
auto DeviceExists = StorageService()->GetDevice(SerialNumber_,DeviceInfo);
|
||||
GWObjects::Device DeviceInfo;
|
||||
std::lock_guard DbSessionLock(DbSession_->Mutex());
|
||||
|
||||
auto DeviceExists = StorageService()->GetDevice(DbSession_->Session(), 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)) {
|
||||
Poco::JSON::Object UpgradeCommand, Params;
|
||||
UpgradeCommand.set(uCentralProtocol::JSONRPC,uCentralProtocol::JSONRPC_VERSION);
|
||||
UpgradeCommand.set(uCentralProtocol::METHOD,uCentralProtocol::UPGRADE);
|
||||
Params.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
|
||||
Params.set(uCentralProtocol::WHEN, 0);
|
||||
Params.set(uCentralProtocol::URI, MinimumFirmware.uri);
|
||||
Params.set(uCentralProtocol::KEEP_REDIRECTOR,1);
|
||||
UpgradeCommand.set(uCentralProtocol::PARAMS, Params);
|
||||
UpgradeCommand.set(uCentralProtocol::ID, 1);
|
||||
|
||||
std::ostringstream Command;
|
||||
UpgradeCommand.stringify(Command);
|
||||
if(Send(Command.str())) {
|
||||
poco_information(
|
||||
Logger(),
|
||||
fmt::format(
|
||||
"Forcing device {} to upgrade to {} before connection is allowed.",
|
||||
SerialNumber_, MinimumFirmware.revision));
|
||||
} else {
|
||||
poco_error(
|
||||
Logger(),
|
||||
fmt::format(
|
||||
"Could not force device {} to upgrade to {} before connection is allowed.",
|
||||
SerialNumber_, MinimumFirmware.revision));
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
StorageService()->CreateDefaultDevice( DbSession_->Session(),
|
||||
SerialNumber_, Caps, Firmware, PeerAddress_,
|
||||
State_.VerifiedCertificate == GWObjects::SIMULATED);
|
||||
}
|
||||
} else if (!Daemon()->AutoProvisioning() && !DeviceExists) {
|
||||
SendKafkaDeviceNotProvisioned(SerialNumber_, Firmware, Compatible_, CId_);
|
||||
poco_warning(Logger(),fmt::format("Device {} is a {} from {} and cannot be provisioned.",SerialNumber_,Compatible_, CId_));
|
||||
return EndConnection();
|
||||
} else if (DeviceExists) {
|
||||
StorageService()->UpdateDeviceCapabilities(SerialNumber_, Caps );
|
||||
StorageService()->UpdateDeviceCapabilities(DbSession_->Session(), SerialNumber_, Caps);
|
||||
int Updated{0};
|
||||
if(!Firmware.empty()) {
|
||||
if(Firmware!=DeviceInfo.Firmware) {
|
||||
if (!Firmware.empty()) {
|
||||
if (Firmware != DeviceInfo.Firmware) {
|
||||
DeviceFirmwareChangeKafkaEvent KEvent(SerialNumberInt_, Utils::Now(),
|
||||
DeviceInfo.Firmware, Firmware);
|
||||
DeviceInfo.Firmware = Firmware;
|
||||
DeviceInfo.LastFWUpdate = Utils::Now();
|
||||
++Updated;
|
||||
|
||||
GWWebSocketNotifications::SingleDeviceFirmwareChange_t Notification;
|
||||
GWWebSocketNotifications::SingleDeviceFirmwareChange_t Notification;
|
||||
Notification.content.serialNumber = SerialNumber_;
|
||||
Notification.content.newFirmware = Firmware;
|
||||
GWWebSocketNotifications::DeviceFirmwareUpdated(Notification);
|
||||
} else if(DeviceInfo.LastFWUpdate==0) {
|
||||
} else if (DeviceInfo.LastFWUpdate == 0) {
|
||||
DeviceInfo.LastFWUpdate = Utils::Now();
|
||||
++Updated;
|
||||
}
|
||||
}
|
||||
|
||||
if(DeviceInfo.locale != State_.locale) {
|
||||
if(ParamsObj->has("reason")) {
|
||||
State_.connectReason = ParamsObj->get("reason").toString();
|
||||
DeviceInfo.connectReason = State_.connectReason;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if(DeviceInfo.DevicePassword!=DevicePassword) {
|
||||
DeviceInfo.DevicePassword = DevicePassword.empty() ? "openwifi" : DevicePassword ;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if (DeviceInfo.lastRecordedContact==0) {
|
||||
DeviceInfo.lastRecordedContact = Utils::Now();
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if (DeviceInfo.simulated && (State_.VerifiedCertificate!=GWObjects::SIMULATED)) {
|
||||
DeviceInfo.simulated = false;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if (!DeviceInfo.simulated && (State_.VerifiedCertificate==GWObjects::SIMULATED)) {
|
||||
DeviceInfo.simulated = true;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if (DeviceInfo.locale != State_.locale) {
|
||||
DeviceInfo.locale = State_.locale;
|
||||
++Updated;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if(Compatible_ != DeviceInfo.DeviceType) {
|
||||
DeviceInfo.DeviceType = Compatible_;
|
||||
++Updated;
|
||||
if (Compatible_ != DeviceInfo.Compatible) {
|
||||
DeviceInfo.Compatible = Compatible_;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if(RestrictedDevice != DeviceInfo.restrictedDevice) {
|
||||
if (Platform != DeviceInfo.DeviceType) {
|
||||
DeviceInfo.DeviceType = Platform;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if (RestrictedDevice != DeviceInfo.restrictedDevice) {
|
||||
DeviceInfo.restrictedDevice = RestrictedDevice;
|
||||
++Updated;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if(Restrictions_ != DeviceInfo.restrictionDetails) {
|
||||
if (Restrictions_ != DeviceInfo.restrictionDetails) {
|
||||
DeviceInfo.restrictionDetails = Restrictions_;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
if(Updated) {
|
||||
StorageService()->UpdateDevice(DeviceInfo);
|
||||
if(DeviceInfo.certificateExpiryDate!=State_.certificateExpiryDate) {
|
||||
DeviceInfo.certificateExpiryDate = State_.certificateExpiryDate;
|
||||
++Updated;
|
||||
}
|
||||
|
||||
uint64_t UpgradedUUID=0;
|
||||
LookForUpgrade(UUID,UpgradedUUID);
|
||||
State_.UUID = UpgradedUUID;
|
||||
if (Updated) {
|
||||
StorageService()->UpdateDevice(DbSession_->Session(), DeviceInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Simulated_) {
|
||||
uint64_t UpgradedUUID = 0;
|
||||
if (LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID)) {
|
||||
State_.UUID = UpgradedUUID;
|
||||
}
|
||||
}
|
||||
|
||||
State_.Compatible = Compatible_;
|
||||
State_.Connected = true;
|
||||
ConnectionCompletionTime_ = std::chrono::high_resolution_clock::now() - ConnectionStart_;
|
||||
ConnectionCompletionTime_ =
|
||||
std::chrono::high_resolution_clock::now() - ConnectionStart_;
|
||||
State_.connectionCompletionTime = ConnectionCompletionTime_.count();
|
||||
|
||||
if(State_.VerifiedCertificate == GWObjects::VALID_CERTIFICATE) {
|
||||
if (( Utils::SerialNumberMatch(CN_, SerialNumber_, (int)AP_WS_Server()->MismatchDepth())) ||
|
||||
AP_WS_Server()->IsSimSerialNumber(CN_)) {
|
||||
if (State_.VerifiedCertificate == GWObjects::VALID_CERTIFICATE) {
|
||||
if ((Utils::SerialNumberMatch(CN_, SerialNumber_,
|
||||
(int)AP_WS_Server()->MismatchDepth())) ||
|
||||
AP_WS_Server()->IsSimSerialNumber(CN_)) {
|
||||
State_.VerifiedCertificate = GWObjects::VERIFIED;
|
||||
poco_information(Logger_, fmt::format("CONNECT({}): Fully validated and authenticated device. Session={} ConnectionCompletion Time={}",
|
||||
CId_,
|
||||
State_.sessionId,
|
||||
State_.connectionCompletionTime ));
|
||||
poco_information(Logger_,
|
||||
fmt::format("CONNECT({}): Fully validated and authenticated "
|
||||
"device. Session={} ConnectionCompletion Time={}",
|
||||
CId_, State_.sessionId,
|
||||
State_.connectionCompletionTime));
|
||||
} else {
|
||||
State_.VerifiedCertificate = GWObjects::MISMATCH_SERIAL;
|
||||
if(AP_WS_Server()->AllowSerialNumberMismatch()) {
|
||||
if (AP_WS_Server()->AllowSerialNumberMismatch()) {
|
||||
poco_information(
|
||||
Logger_, fmt::format("CONNECT({}): Serial number mismatch allowed. CN={} Serial={} Session={} ConnectionCompletion Time={}",
|
||||
CId_, CN_, SerialNumber_, State_.sessionId,
|
||||
State_.connectionCompletionTime));
|
||||
Logger_,
|
||||
fmt::format("CONNECT({}): Serial number mismatch allowed. CN={} "
|
||||
"Serial={} Session={} ConnectionCompletion Time={}",
|
||||
CId_, CN_, SerialNumber_, State_.sessionId,
|
||||
State_.connectionCompletionTime));
|
||||
} else {
|
||||
poco_information(
|
||||
Logger_, fmt::format("CONNECT({}): Serial number mismatch disallowed. Device rejected. CN={} Serial={} Session={}",
|
||||
Logger_, fmt::format("CONNECT({}): Serial number mismatch disallowed. "
|
||||
"Device rejected. CN={} Serial={} Session={}",
|
||||
CId_, CN_, SerialNumber_, State_.sessionId));
|
||||
return EndConnection();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
poco_information(Logger_,
|
||||
fmt::format("CONNECT({}): Simulator device. "
|
||||
"Session={} ConnectionCompletion Time={}",
|
||||
CId_, State_.sessionId,
|
||||
State_.connectionCompletionTime));
|
||||
}
|
||||
|
||||
GWWebSocketNotifications::SingleDevice_t Notification;
|
||||
GWWebSocketNotifications::SingleDevice_t Notification;
|
||||
Notification.content.serialNumber = SerialNumber_;
|
||||
GWWebSocketNotifications::DeviceConnected(Notification);
|
||||
|
||||
// std::cout << "Serial: " << SerialNumber_ << "Session: " << State_.sessionId << std::endl;
|
||||
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
ParamsObj->set(uCentralProtocol::CONNECTIONIP, CId_);
|
||||
ParamsObj->set("locale", State_.locale );
|
||||
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(Logger_,fmt::format("INVALID-PROTOCOL({}): Missing one of uuid, firmware, or capabilities", CId_));
|
||||
poco_warning(
|
||||
Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Missing one of uuid, firmware, or capabilities",
|
||||
CId_));
|
||||
Errors_++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -5,13 +5,14 @@
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "framework/utils.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "fmt/format.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include <GWKafkaEvents.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_crashlog(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::LOGLINES)) {
|
||||
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;
|
||||
@@ -25,14 +26,14 @@ namespace OpenWifi {
|
||||
.Log = LogText,
|
||||
.Data = "",
|
||||
.Severity = GWObjects::DeviceLog::LOG_EMERG,
|
||||
.Recorded = (uint64_t)time(nullptr),
|
||||
.Recorded = Utils::Now(),
|
||||
.LogType = 1,
|
||||
.UUID = 0};
|
||||
StorageService()->AddLog(DeviceLog);
|
||||
|
||||
.UUID = ParamsObj->get(uCentralProtocol::UUID)};
|
||||
StorageService()->AddLog(*DbSession_, DeviceLog);
|
||||
DeviceLogKafkaEvent E(DeviceLog);
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("LOG({}): Missing parameters.", CId_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -9,19 +9,23 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void AP_WS_Connection::Process_deviceupdate(Poco::JSON::Object::Ptr ParamsObj, std::string &Serial) {
|
||||
void AP_WS_Connection::Process_deviceupdate(Poco::JSON::Object::Ptr ParamsObj,
|
||||
std::string &Serial) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_, fmt::format(
|
||||
"INVALID-PROTOCOL({}): Device '{}' is not following protocol", CId_, CN_));
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
if (ParamsObj->has("currentPassword")) {
|
||||
auto Password = ParamsObj->get("currentPassword").toString();
|
||||
|
||||
StorageService()->SetDevicePassword(Serial, Password);
|
||||
poco_trace(Logger_, fmt::format("DEVICEUPDATE({}): Device is updating its login password.", Serial));
|
||||
StorageService()->SetDevicePassword(*DbSession_,Serial, Password);
|
||||
poco_trace(
|
||||
Logger_,
|
||||
fmt::format("DEVICE-UPDATE({}): Device is updating its login password.", Serial));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
51
src/AP_WS_Process_event.cpp
Normal file
51
src/AP_WS_Process_event.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-01-22.
|
||||
//
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_event(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
poco_trace(Logger_, fmt::format("Event data received for {}", SerialNumber_));
|
||||
|
||||
try {
|
||||
if (ParamsObj->has(uCentralProtocol::SERIAL) &&
|
||||
ParamsObj->has(uCentralProtocol::DATA)) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
auto Data = ParamsObj->getObject(uCentralProtocol::DATA);
|
||||
auto Event = Data->getArray("event");
|
||||
auto EventTimeStamp = Event->getElement<std::uint64_t>(0);
|
||||
auto EventDetails = Event->getObject(1);
|
||||
auto EventType = EventDetails->get("type").extract<std::string>();
|
||||
auto EventPayload = EventDetails->getObject("payload");
|
||||
|
||||
Poco::JSON::Object FullEvent;
|
||||
FullEvent.set("type", EventType);
|
||||
FullEvent.set("timestamp", EventTimeStamp);
|
||||
FullEvent.set("payload", EventPayload);
|
||||
if(strncmp(EventType.c_str(),"rrm.",4) == 0 ) {
|
||||
KafkaManager()->PostMessage(KafkaTopics::RRM, SerialNumber_,
|
||||
FullEvent);
|
||||
} else {
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber_,
|
||||
FullEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -3,25 +3,30 @@
|
||||
//
|
||||
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "AP_WS_Server.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/utils.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void AP_WS_Connection::Process_healthcheck(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_, fmt::format(
|
||||
"INVALID-PROTOCOL({}): Device '{}' is not following protocol", CId_, CN_));
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::SANITY) &&
|
||||
if (ParamsObj->has(uCentralProtocol::UUID) &&
|
||||
ParamsObj->has(uCentralProtocol::SANITY) &&
|
||||
ParamsObj->has(uCentralProtocol::DATA)) {
|
||||
|
||||
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
|
||||
auto Sanity = ParamsObj->get(uCentralProtocol::SANITY);
|
||||
State_.sanity = Sanity;
|
||||
auto CheckData = ParamsObj->get(uCentralProtocol::DATA).toString();
|
||||
if (CheckData.empty())
|
||||
CheckData = uCentralProtocol::EMPTY_JSON_DOC;
|
||||
@@ -31,18 +36,12 @@ namespace OpenWifi {
|
||||
request_uuid = ParamsObj->get(uCentralProtocol::REQUEST_UUID).toString();
|
||||
|
||||
if (request_uuid.empty()) {
|
||||
poco_trace(Logger_,
|
||||
fmt::format("HEALTHCHECK({}): UUID={} Updating.", CId_, UUID));
|
||||
poco_trace(Logger_, fmt::format("HEALTHCHECK({}): UUID={} Updating.", CId_, UUID));
|
||||
} else {
|
||||
poco_trace(Logger_,
|
||||
fmt::format("HEALTHCHECK({}): UUID={} Updating for CMD={}.", CId_,
|
||||
UUID, request_uuid));
|
||||
poco_trace(Logger_, fmt::format("HEALTHCHECK({}): UUID={} Updating for CMD={}.",
|
||||
CId_, UUID, request_uuid));
|
||||
}
|
||||
|
||||
uint64_t UpgradedUUID;
|
||||
LookForUpgrade(UUID,UpgradedUUID);
|
||||
State_.UUID = UpgradedUUID;
|
||||
|
||||
GWObjects::HealthCheck Check;
|
||||
|
||||
Check.SerialNumber = SerialNumber_;
|
||||
@@ -51,19 +50,15 @@ namespace OpenWifi {
|
||||
Check.Data = CheckData;
|
||||
Check.Sanity = Sanity;
|
||||
|
||||
StorageService()->AddHealthCheckData(Check);
|
||||
StorageService()->AddHealthCheckData(*DbSession_, Check);
|
||||
|
||||
if (!request_uuid.empty()) {
|
||||
StorageService()->SetCommandResult(request_uuid, CheckData);
|
||||
}
|
||||
|
||||
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());
|
||||
if (KafkaManager()->Enabled() && !AP_WS_Server()->KafkaDisableHealthChecks()) {
|
||||
KafkaManager()->PostMessage(KafkaTopics::HEALTHCHECK, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("HEALTHCHECK({}): Missing parameter", CId_));
|
||||
@@ -71,4 +66,4 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -5,15 +5,16 @@
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "framework/ow_constants.h"
|
||||
#include "fmt/format.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include <GWKafkaEvents.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_log(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(
|
||||
Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol", CId_, CN_));
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
@@ -35,10 +36,11 @@ namespace OpenWifi {
|
||||
.Recorded = (uint64_t)time(nullptr),
|
||||
.LogType = 0,
|
||||
.UUID = State_.UUID};
|
||||
StorageService()->AddLog(DeviceLog);
|
||||
StorageService()->AddLog(*DbSession_, DeviceLog);
|
||||
DeviceLogKafkaEvent E(DeviceLog);
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("LOG({}): Missing parameters.", CId_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -15,4 +15,4 @@ namespace OpenWifi {
|
||||
poco_warning(Logger_, fmt::format("PING({}): Missing parameter.", CId_));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
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(*DbSession_, DeviceLog);
|
||||
DeviceLogKafkaEvent E(DeviceLog);
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("REBOOT-LOG({}): Missing parameters.", CId_));
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -3,12 +3,11 @@
|
||||
//
|
||||
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "StorageService.h"
|
||||
#include "CommandManager.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "fmt/format.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -36,7 +35,7 @@ namespace OpenWifi {
|
||||
.LogType = 1,
|
||||
.UUID = 0};
|
||||
|
||||
StorageService()->AddLog(DeviceLog);
|
||||
StorageService()->AddLog(*DbSession_, DeviceLog);
|
||||
|
||||
if (ParamsObj->get(uCentralProtocol::REBOOT).toString() == "true") {
|
||||
GWObjects::CommandDetails Cmd;
|
||||
@@ -52,16 +51,24 @@ namespace OpenWifi {
|
||||
Poco::JSON::Stringifier::stringify(Params, O);
|
||||
Cmd.Details = O.str();
|
||||
bool Sent;
|
||||
CommandManager()->PostCommand(CommandManager()->Next_RPC_ID(), APCommands::Commands::reboot, SerialNumber_, Cmd.Command, Params, Cmd.UUID, Sent);
|
||||
StorageService()->AddCommand(SerialNumber_, Cmd, Storage::CommandExecutionType::COMMAND_EXECUTED);
|
||||
poco_information(Logger_, fmt::format("RECOVERY({}): Recovery mode received, need for a reboot.", CId_));
|
||||
CommandManager()->PostCommand(CommandManager()->Next_RPC_ID(),
|
||||
APCommands::Commands::reboot, SerialNumber_,
|
||||
Cmd.Command, Params, Cmd.UUID, Sent, false, false);
|
||||
StorageService()->AddCommand(SerialNumber_, Cmd,
|
||||
Storage::CommandExecutionType::COMMAND_EXECUTED);
|
||||
poco_information(
|
||||
Logger_,
|
||||
fmt::format("RECOVERY({}): Recovery mode received, need for a reboot.", CId_));
|
||||
} else {
|
||||
poco_information(Logger_, fmt::format(
|
||||
"RECOVERY({}): Recovery mode received, no need for a reboot.", CId_));
|
||||
poco_information(
|
||||
Logger_,
|
||||
fmt::format("RECOVERY({}): Recovery mode received, no need for a reboot.",
|
||||
CId_));
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("RECOVERY({}): Recovery missing one of serialnumber, firmware, uuid, loglines, reboot",
|
||||
CId_));
|
||||
poco_warning(Logger_, fmt::format("RECOVERY({}): Recovery missing one of serialnumber, "
|
||||
"firmware, uuid, loglines, reboot",
|
||||
CId_));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -3,8 +3,9 @@
|
||||
//
|
||||
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "StorageService.h"
|
||||
#include "AP_WS_Server.h"
|
||||
#include "StateUtils.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "UI_GW_WebSocketNotifications.h"
|
||||
|
||||
@@ -16,8 +17,9 @@
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_state(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_, fmt::format(
|
||||
"INVALID-PROTOCOL({}): Device '{}' is not following protocol", CId_, CN_));
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
@@ -34,41 +36,42 @@ namespace OpenWifi {
|
||||
if (request_uuid.empty()) {
|
||||
poco_trace(Logger_, fmt::format("STATE({}): UUID={} Updating.", CId_, UUID));
|
||||
} else {
|
||||
poco_trace(Logger_, fmt::format("STATE({}): UUID={} Updating for CMD={}.",
|
||||
CId_, UUID, request_uuid));
|
||||
poco_trace(Logger_, fmt::format("STATE({}): UUID={} Updating for CMD={}.", CId_,
|
||||
UUID, request_uuid));
|
||||
}
|
||||
|
||||
std::lock_guard Guard(DbSession_->Mutex());
|
||||
if(!Simulated_) {
|
||||
uint64_t UpgradedUUID;
|
||||
LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID);
|
||||
State_.UUID = UpgradedUUID;
|
||||
}
|
||||
|
||||
uint64_t UpgradedUUID;
|
||||
LookForUpgrade(UUID,UpgradedUUID);
|
||||
State_.UUID = UpgradedUUID;
|
||||
SetLastStats(StateStr);
|
||||
|
||||
GWObjects::Statistics Stats{
|
||||
.SerialNumber = SerialNumber_, .UUID = UUID, .Data = StateStr};
|
||||
Stats.Recorded = Utils::Now();
|
||||
StorageService()->AddStatisticsData(Stats);
|
||||
StorageService()->AddStatisticsData(DbSession_->Session(),Stats);
|
||||
if (!request_uuid.empty()) {
|
||||
StorageService()->SetCommandResult(request_uuid, StateStr);
|
||||
}
|
||||
|
||||
StateUtils::ComputeAssociations(StateObj, State_.Associations_2G,
|
||||
State_.Associations_5G,
|
||||
State_.Associations_6G
|
||||
);
|
||||
StateUtils::ComputeAssociations(StateObj, State_.Associations_2G,
|
||||
State_.Associations_5G, State_.Associations_6G, State_.uptime);
|
||||
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Stringifier Stringify;
|
||||
std::ostringstream OS;
|
||||
Stringify.condense(ParamsObj, OS);
|
||||
KafkaManager()->PostMessage(KafkaTopics::STATE, SerialNumber_, OS.str());
|
||||
if (KafkaManager()->Enabled() && !AP_WS_Server()->KafkaDisableState()) {
|
||||
KafkaManager()->PostMessage(KafkaTopics::STATE, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
|
||||
GWWebSocketNotifications::SingleDevice_t N;
|
||||
GWWebSocketNotifications::SingleDevice_t N;
|
||||
N.content.serialNumber = SerialNumber_;
|
||||
GWWebSocketNotifications::DeviceStatistics(N);
|
||||
|
||||
} else {
|
||||
poco_warning(Logger_, fmt::format("STATE({}): Invalid request. Missing serial, uuid, or state", CId_));
|
||||
poco_warning(
|
||||
Logger_,
|
||||
fmt::format("STATE({}): Invalid request. Missing serial, uuid, or state", CId_));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -3,57 +3,65 @@
|
||||
//
|
||||
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "TelemetryStream.h"
|
||||
#include "CommandManager.h"
|
||||
#include "TelemetryStream.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/utils.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_telemetry(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_, fmt::format(
|
||||
"INVALID-PROTOCOL({}): Device '{}' is not following protocol", CId_, CN_));
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
poco_trace(Logger_,fmt::format("Telemetry data received for {}", SerialNumber_));
|
||||
if (TelemetryReporting_) {
|
||||
poco_trace(Logger_, fmt::format("Telemetry data received for {}", SerialNumber_));
|
||||
if (TelemetryReporting_ || ParamsObj->has("adhoc")) {
|
||||
if (ParamsObj->has("data")) {
|
||||
auto Payload = ParamsObj->get("data").extract<Poco::JSON::Object::Ptr>();
|
||||
Payload->set("timestamp", Utils::Now());
|
||||
std::ostringstream SS;
|
||||
Payload->stringify(SS);
|
||||
auto now=Utils::Now();
|
||||
auto now = Utils::Now();
|
||||
auto KafkaPayload = SS.str();
|
||||
if (ParamsObj->has("adhoc")) {
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
|
||||
KafkaPayload);
|
||||
return;
|
||||
}
|
||||
if (TelemetryWebSocketRefCount_) {
|
||||
if(now<TelemetryWebSocketTimer_) {
|
||||
// std::cout << SerialNumber_ << ": Updating WebSocket telemetry" << std::endl;
|
||||
if (now < TelemetryWebSocketTimer_) {
|
||||
|
||||
TelemetryWebSocketPackets_++;
|
||||
State_.websocketPackets = TelemetryWebSocketPackets_;
|
||||
TelemetryStream()->NotifyEndPoint(SerialNumberInt_, SS.str());
|
||||
TelemetryStream()->NotifyEndPoint(SerialNumberInt_, KafkaPayload);
|
||||
} else {
|
||||
StopWebSocketTelemetry(CommandManager()->Next_RPC_ID());
|
||||
}
|
||||
}
|
||||
if (TelemetryKafkaRefCount_) {
|
||||
if(KafkaManager()->Enabled() && now<TelemetryKafkaTimer_) {
|
||||
// std::cout << SerialNumber_ << ": Updating Kafka telemetry" << std::endl;
|
||||
if (KafkaManager()->Enabled() && now < TelemetryKafkaTimer_) {
|
||||
TelemetryKafkaPackets_++;
|
||||
State_.kafkaPackets = TelemetryKafkaPackets_;
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
|
||||
SS.str());
|
||||
KafkaPayload);
|
||||
} else {
|
||||
StopKafkaTelemetry(CommandManager()->Next_RPC_ID());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
poco_debug(Logger_,fmt::format("TELEMETRY({}): Invalid telemetry packet.",SerialNumber_));
|
||||
poco_debug(Logger_,
|
||||
fmt::format("TELEMETRY({}): Invalid telemetry packet.", SerialNumber_));
|
||||
}
|
||||
} else {
|
||||
// if we are ignoring telemetry, then close it down on the device.
|
||||
poco_debug(Logger_,fmt::format("TELEMETRY({}): Stopping runaway telemetry.",SerialNumber_));
|
||||
poco_debug(Logger_,
|
||||
fmt::format("TELEMETRY({}): Stopping runaway telemetry.", SerialNumber_));
|
||||
StopTelemetry(CommandManager()->Next_RPC_ID());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -7,11 +7,10 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_venuebroadcast(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if(ParamsObj->has("data") && ParamsObj->has("serial") && ParamsObj->has("timestamp")) {
|
||||
VenueBroadcaster()->Broadcast(
|
||||
ParamsObj->get("serial").toString(),
|
||||
ParamsObj->get("data").toString(),
|
||||
ParamsObj->get("timestamp"));
|
||||
if (ParamsObj->has("data") && ParamsObj->has("serial") && ParamsObj->has("timestamp")) {
|
||||
VenueBroadcaster()->Broadcast(ParamsObj->get("serial").toString(),
|
||||
ParamsObj->getObject("data"),
|
||||
ParamsObj->get("timestamp"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
28
src/AP_WS_Process_wifiscan.cpp
Normal file
28
src/AP_WS_Process_wifiscan.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-01-22.
|
||||
//
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void AP_WS_Connection::Process_wifiscan(Poco::JSON::Object::Ptr ParamsObj) {
|
||||
if (!State_.Connected) {
|
||||
poco_warning(Logger_,
|
||||
fmt::format("INVALID-PROTOCOL({}): Device '{}' is not following protocol",
|
||||
CId_, CN_));
|
||||
Errors_++;
|
||||
return;
|
||||
}
|
||||
poco_trace(Logger_, fmt::format("Wifiscan data received for {}", SerialNumber_));
|
||||
|
||||
if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
KafkaManager()->PostMessage(KafkaTopics::WIFISCAN, SerialNumber_, *ParamsObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -1,64 +0,0 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-02-03.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "Poco/Net/SocketAcceptor.h"
|
||||
#include "Poco/Environment.h"
|
||||
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class AP_WS_ReactorThreadPool {
|
||||
public:
|
||||
explicit AP_WS_ReactorThreadPool() {
|
||||
NumberOfThreads_ = Poco::Environment::processorCount()*2;
|
||||
if(NumberOfThreads_==0)
|
||||
NumberOfThreads_=4;
|
||||
}
|
||||
|
||||
~ AP_WS_ReactorThreadPool() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Start() {
|
||||
for (uint64_t i = 0; i < NumberOfThreads_; ++i) {
|
||||
auto NewReactor = std::make_unique<Poco::Net::SocketReactor>();
|
||||
auto NewThread = std::make_unique<Poco::Thread>();
|
||||
NewThread->start(*NewReactor);
|
||||
std::string ThreadName{"ap:react:" + std::to_string(i)};
|
||||
Utils::SetThreadName(*NewThread,ThreadName.c_str());
|
||||
Reactors_.emplace_back(std::move(NewReactor));
|
||||
Threads_.emplace_back(std::move(NewThread));
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
for (auto &i : Reactors_)
|
||||
i->stop();
|
||||
for (auto &i : Threads_) {
|
||||
i->join();
|
||||
}
|
||||
Reactors_.clear();
|
||||
Threads_.clear();
|
||||
}
|
||||
|
||||
Poco::Net::SocketReactor &NextReactor() {
|
||||
std::shared_lock Lock(Mutex_);
|
||||
NextReactor_++;
|
||||
NextReactor_ %= NumberOfThreads_;
|
||||
return *Reactors_[NextReactor_];
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_mutex Mutex_;
|
||||
uint64_t NumberOfThreads_;
|
||||
uint64_t NextReactor_ = 0;
|
||||
std::vector<std::unique_ptr<Poco::Net::SocketReactor>> Reactors_;
|
||||
std::vector<std::unique_ptr<Poco::Thread>> Threads_;
|
||||
};
|
||||
}
|
||||
77
src/AP_WS_Reactor_Pool.h
Normal file
77
src/AP_WS_Reactor_Pool.h
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-02-03.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include <framework/utils.h>
|
||||
|
||||
#include <Poco/Environment.h>
|
||||
#include <Poco/Net/SocketAcceptor.h>
|
||||
#include <Poco/Data/SessionPool.h>
|
||||
|
||||
#include <StorageService.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class AP_WS_ReactorThreadPool {
|
||||
public:
|
||||
explicit AP_WS_ReactorThreadPool(Poco::Logger &Logger) : Logger_(Logger) {
|
||||
NumberOfThreads_ = Poco::Environment::processorCount() * 4;
|
||||
if (NumberOfThreads_ == 0)
|
||||
NumberOfThreads_ = 8;
|
||||
NumberOfThreads_ = std::min(NumberOfThreads_, (std::uint64_t) 128);
|
||||
}
|
||||
|
||||
~AP_WS_ReactorThreadPool() { Stop(); }
|
||||
|
||||
void Start() {
|
||||
Reactors_.reserve(NumberOfThreads_);
|
||||
DbSessions_.reserve(NumberOfThreads_);
|
||||
Threads_.reserve(NumberOfThreads_);
|
||||
Logger_.information(fmt::format("WebSocket Processor: starting {} threads.", NumberOfThreads_));
|
||||
for (uint64_t i = 0; i < NumberOfThreads_; ++i) {
|
||||
auto NewReactor = std::make_shared<Poco::Net::SocketReactor>();
|
||||
auto NewThread = std::make_unique<Poco::Thread>();
|
||||
NewThread->start(*NewReactor);
|
||||
std::string ThreadName{"ap:react:" + std::to_string(i)};
|
||||
Utils::SetThreadName(*NewThread, ThreadName.c_str());
|
||||
Reactors_.emplace_back(std::move(NewReactor));
|
||||
Threads_.emplace_back(std::move(NewThread));
|
||||
DbSessions_.emplace_back(std::make_shared<LockedDbSession>());
|
||||
}
|
||||
Logger_.information(fmt::format("WebSocket Processor: {} threads started.", NumberOfThreads_));
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
for (auto &i : Reactors_)
|
||||
i->stop();
|
||||
for (auto &i : Threads_) {
|
||||
i->join();
|
||||
}
|
||||
Reactors_.clear();
|
||||
Threads_.clear();
|
||||
DbSessions_.clear();
|
||||
}
|
||||
|
||||
auto NextReactor() {
|
||||
std::lock_guard Lock(Mutex_);
|
||||
NextReactor_++;
|
||||
NextReactor_ %= NumberOfThreads_;
|
||||
return std::make_pair(Reactors_[NextReactor_], DbSessions_[NextReactor_]);
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex Mutex_;
|
||||
uint64_t NumberOfThreads_;
|
||||
uint64_t NextReactor_ = 0;
|
||||
std::vector<std::shared_ptr<Poco::Net::SocketReactor>> Reactors_;
|
||||
std::vector<std::unique_ptr<Poco::Thread>> Threads_;
|
||||
std::vector<std::shared_ptr<LockedDbSession>> DbSessions_;
|
||||
Poco::Logger &Logger_;
|
||||
|
||||
};
|
||||
} // namespace OpenWifi
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,65 +8,67 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <array>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "Poco/AutoPtr.h"
|
||||
#include "Poco/Net/SocketReactor.h"
|
||||
#include "Poco/Net/ParallelSocketAcceptor.h"
|
||||
#include "Poco/Net/SocketAcceptor.h"
|
||||
#include "Poco/Timer.h"
|
||||
#include "Poco/Net/HTTPRequestHandler.h"
|
||||
#include "Poco/Net/HTTPRequestHandlerFactory.h"
|
||||
#include "Poco/Net/HTTPServer.h"
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include "Poco/Net/ParallelSocketAcceptor.h"
|
||||
#include "Poco/Net/SocketAcceptor.h"
|
||||
#include "Poco/Net/SocketReactor.h"
|
||||
#include "Poco/Timer.h"
|
||||
|
||||
#include "AP_WS_Connection.h"
|
||||
#include "AP_WS_ReactorPool.h"
|
||||
#include "AP_WS_Reactor_Pool.h"
|
||||
|
||||
#include "framework/utils.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class AP_WS_RequestHandler : public Poco::Net::HTTPRequestHandler {
|
||||
constexpr uint MACHashMax = 256;
|
||||
constexpr uint MACHashMask = MACHashMax-1;
|
||||
class MACHash {
|
||||
public:
|
||||
explicit AP_WS_RequestHandler(Poco::Logger &L, uint64_t id)
|
||||
: Logger_(L),
|
||||
id_(id){
|
||||
};
|
||||
|
||||
void handleRequest(Poco::Net::HTTPServerRequest &request,
|
||||
Poco::Net::HTTPServerResponse &response) override;
|
||||
private:
|
||||
Poco::Logger &Logger_;
|
||||
uint64_t id_=0;
|
||||
};
|
||||
|
||||
class AP_WS_RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
|
||||
public:
|
||||
inline explicit AP_WS_RequestHandlerFactory(Poco::Logger &L)
|
||||
: Logger_(L) {
|
||||
}
|
||||
|
||||
inline Poco::Net::HTTPRequestHandler *
|
||||
createRequestHandler(const Poco::Net::HTTPServerRequest &request) override {
|
||||
if (request.find("Upgrade") != request.end() &&
|
||||
Poco::icompare(request["Upgrade"], "websocket") == 0) {
|
||||
Utils::SetThreadName("ws:conn-init");
|
||||
return new AP_WS_RequestHandler(Logger_,id_++);
|
||||
} else {
|
||||
return nullptr;
|
||||
[[nodiscard]] static inline uint16_t Hash(std::uint64_t value) {
|
||||
uint8_t hash = 0, i=6;
|
||||
while(i) {
|
||||
hash ^= (value & MACHashMask) + 1;
|
||||
value >>= 8;
|
||||
--i;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline uint16_t Hash(const std::string & value) {
|
||||
return Hash(Utils::MACToInt(value));
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline uint16_t HashMax() {
|
||||
return MACHashMax;
|
||||
}
|
||||
private:
|
||||
Poco::Logger &Logger_;
|
||||
inline static uint64_t id_=1;
|
||||
};
|
||||
|
||||
class AP_WS_Server : public SubSystemServer {
|
||||
constexpr uint SessionHashMax = 256;
|
||||
constexpr uint SessionHashMask = SessionHashMax-1;
|
||||
class SessionHash {
|
||||
public:
|
||||
[[nodiscard]] static inline uint16_t Hash(std::uint64_t value) {
|
||||
return (value & SessionHashMask);
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline uint16_t HashMax() {
|
||||
return SessionHashMax;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class AP_WS_Server : public SubSystemServer, public Poco::Runnable {
|
||||
public:
|
||||
static auto instance() {
|
||||
static auto instance_ = new AP_WS_Server;
|
||||
@@ -75,136 +77,188 @@ namespace OpenWifi {
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
bool IsCertOk() { return IssuerCert_!= nullptr; }
|
||||
bool ValidateCertificate(const std::string & ConnectionId, const Poco::Crypto::X509Certificate & Certificate);
|
||||
// Poco::Net::SocketReactor & GetNextReactor() { return ReactorPool_.NextReactor(); }
|
||||
bool IsCertOk() { return IssuerCert_ != nullptr; }
|
||||
bool ValidateCertificate(const std::string &ConnectionId,
|
||||
const Poco::Crypto::X509Certificate &Certificate);
|
||||
|
||||
inline bool IsSimSerialNumber(const std::string & SerialNumber) const {
|
||||
return IsSim(Poco::toLower(SerialNumber)) && Poco::toLower(SerialNumber) == Poco::toLower(SimulatorId_);
|
||||
inline bool IsSimSerialNumber(const std::string &SerialNumber) const {
|
||||
return IsSim(SerialNumber) &&
|
||||
SerialNumber == SimulatorId_;
|
||||
}
|
||||
|
||||
inline static bool IsSim(const std::string & SerialNumber) {
|
||||
return SerialNumber.substr(0,6) == "53494d";
|
||||
inline static bool IsSim(const std::string &SerialNumber) {
|
||||
return SerialNumber.substr(0, 6) == "53494d";
|
||||
}
|
||||
|
||||
inline bool IsSimEnabled() const {
|
||||
return SimulatorEnabled_;
|
||||
}
|
||||
|
||||
inline bool AllowSerialNumberMismatch() const {
|
||||
return AllowSerialNumberMismatch_;
|
||||
}
|
||||
|
||||
inline uint64_t MismatchDepth() const {
|
||||
return MismatchDepth_;
|
||||
}
|
||||
|
||||
inline bool UseProvisioning() const { return LookAtProvisioning_; }
|
||||
inline bool UseDefaults() const { return UseDefaultConfig_; }
|
||||
|
||||
[[nodiscard]] inline Poco::Net::SocketReactor & NextReactor() { return Reactor_pool_->NextReactor(); }
|
||||
void run() override; // Garbage collector thread.
|
||||
[[nodiscard]] inline bool IsSimEnabled() const { return SimulatorEnabled_; }
|
||||
[[nodiscard]] inline bool AllowSerialNumberMismatch() const { return AllowSerialNumberMismatch_; }
|
||||
[[nodiscard]] inline uint64_t MismatchDepth() const { return MismatchDepth_; }
|
||||
[[nodiscard]] inline bool UseProvisioning() const { return LookAtProvisioning_; }
|
||||
[[nodiscard]] inline bool UseDefaults() const { return UseDefaultConfig_; }
|
||||
[[nodiscard]] inline bool Running() const { return Running_; }
|
||||
|
||||
inline void AddConnection(uint64_t session_id, std::shared_ptr<AP_WS_Connection> Connection ) {
|
||||
std::lock_guard Lock(WSServerMutex_);
|
||||
Sessions_[session_id] = std::move(Connection);
|
||||
[[nodiscard]] inline std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> NextReactor() {
|
||||
return Reactor_pool_->NextReactor();
|
||||
}
|
||||
|
||||
inline std::shared_ptr<AP_WS_Connection> FindConnection(uint64_t session_id) const {
|
||||
std::lock_guard Lock(WSServerMutex_);
|
||||
|
||||
auto Connection = Sessions_.find(session_id);
|
||||
if(Connection!=end(Sessions_))
|
||||
return Connection->second;
|
||||
return nullptr;
|
||||
inline void AddConnection(std::shared_ptr<AP_WS_Connection> Connection) {
|
||||
std::uint64_t sessionHash = SessionHash::Hash(Connection->State_.sessionId);
|
||||
std::lock_guard SessionLock(SessionMutex_[sessionHash]);
|
||||
if(Sessions_[sessionHash].find(Connection->State_.sessionId)==end(Sessions_[sessionHash])) {
|
||||
Sessions_[sessionHash][Connection->State_.sessionId] = std::move(Connection);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool GetStatistics(const std::string &SerialNumber, std::string & Statistics) const {
|
||||
return GetStatistics(Utils::SerialNumberToInt(SerialNumber),Statistics);
|
||||
[[nodiscard]] inline bool DeviceRequiresSecureRTTY(uint64_t serialNumber) const {
|
||||
std::shared_ptr<AP_WS_Connection> Connection;
|
||||
{
|
||||
auto hashIndex = MACHash::Hash(serialNumber);
|
||||
std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
|
||||
auto DeviceHint = SerialNumbers_[hashIndex].find(serialNumber);
|
||||
if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr)
|
||||
return false;
|
||||
Connection = DeviceHint->second;
|
||||
}
|
||||
return Connection->RTTYMustBeSecure_;
|
||||
}
|
||||
bool GetStatistics(uint64_t SerialNumber, std::string & Statistics) const ;
|
||||
|
||||
inline bool GetState(const std::string & SerialNumber, GWObjects::ConnectionState & State) const {
|
||||
inline bool GetStatistics(const std::string &SerialNumber, std::string &Statistics) const {
|
||||
return GetStatistics(Utils::SerialNumberToInt(SerialNumber), Statistics);
|
||||
}
|
||||
[[nodiscard]] bool GetStatistics(uint64_t SerialNumber, std::string &Statistics) const;
|
||||
|
||||
inline bool GetState(const std::string &SerialNumber,
|
||||
GWObjects::ConnectionState &State) const {
|
||||
return GetState(Utils::SerialNumberToInt(SerialNumber), State);
|
||||
}
|
||||
bool GetState(uint64_t SerialNumber, GWObjects::ConnectionState & State) const;
|
||||
bool GetState(uint64_t SerialNumber, GWObjects::ConnectionState &State) const;
|
||||
|
||||
inline bool GetHealthcheck(const std::string &SerialNumber, GWObjects::HealthCheck & CheckData) const {
|
||||
inline bool GetHealthcheck(const std::string &SerialNumber,
|
||||
GWObjects::HealthCheck &CheckData) const {
|
||||
return GetHealthcheck(Utils::SerialNumberToInt(SerialNumber), CheckData);
|
||||
}
|
||||
bool GetHealthcheck(uint64_t SerialNumber, GWObjects::HealthCheck & CheckData) const ;
|
||||
bool GetHealthcheck(uint64_t SerialNumber, GWObjects::HealthCheck &CheckData) const;
|
||||
|
||||
bool Connected(uint64_t SerialNumber, GWObjects::DeviceRestrictions & Restrictions) const ;
|
||||
bool Connected(uint64_t SerialNumber) const ;
|
||||
bool Connected(uint64_t SerialNumber, GWObjects::DeviceRestrictions &Restrictions) const;
|
||||
bool Connected(uint64_t SerialNumber) const;
|
||||
bool Disconnect(uint64_t SerialNumber);
|
||||
bool SendFrame(uint64_t SerialNumber, const std::string &Payload) const;
|
||||
bool SendRadiusAuthenticationData(const std::string &SerialNumber,
|
||||
const unsigned char *buffer, std::size_t size);
|
||||
bool SendRadiusAccountingData(const std::string &SerialNumber, const unsigned char *buffer,
|
||||
std::size_t size);
|
||||
bool SendRadiusCoAData(const std::string &SerialNumber, const unsigned char *buffer,
|
||||
std::size_t size);
|
||||
|
||||
inline bool SendFrame(const std::string & SerialNumber, const std::string & Payload) const {
|
||||
return SendFrame(Utils::SerialNumberToInt(SerialNumber), Payload);
|
||||
}
|
||||
|
||||
bool SendFrame(uint64_t SerialNumber, const std::string & Payload) const ;
|
||||
|
||||
bool SendRadiusAuthenticationData(const std::string & SerialNumber, const unsigned char * buffer, std::size_t size);
|
||||
bool SendRadiusAccountingData(const std::string & SerialNumber, const unsigned char * buffer, std::size_t size);
|
||||
bool SendRadiusCoAData(const std::string & SerialNumber, const unsigned char * buffer, std::size_t size);
|
||||
|
||||
void SetSessionDetails(uint64_t connection_id, uint64_t SerialNumber);
|
||||
bool EndSession(uint64_t connection_id, uint64_t serial_number);
|
||||
|
||||
void SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber, uint64_t Interval, uint64_t Lifetime);
|
||||
void StartSession(uint64_t session_id, uint64_t SerialNumber);
|
||||
bool EndSession(uint64_t session_id, uint64_t SerialNumber);
|
||||
void SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber,
|
||||
uint64_t Interval, uint64_t Lifetime,
|
||||
const std::vector<std::string> &TelemetryTypes);
|
||||
void StopWebSocketTelemetry(uint64_t RPCID, uint64_t SerialNumber);
|
||||
void SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber, uint64_t Interval, uint64_t Lifetime);
|
||||
void SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber, uint64_t Interval,
|
||||
uint64_t Lifetime,
|
||||
const std::vector<std::string> &TelemetryTypes);
|
||||
void StopKafkaTelemetry(uint64_t RPCID, uint64_t SerialNumber);
|
||||
void GetTelemetryParameters(uint64_t SerialNumber , bool & TelemetryRunning,
|
||||
uint64_t & TelemetryInterval,
|
||||
uint64_t & TelemetryWebSocketTimer,
|
||||
uint64_t & TelemetryKafkaTimer,
|
||||
uint64_t & TelemetryWebSocketCount,
|
||||
uint64_t & TelemetryKafkaCount,
|
||||
uint64_t & TelemetryWebSocketPackets,
|
||||
uint64_t & TelemetryKafkaPackets);
|
||||
void GetTelemetryParameters(uint64_t SerialNumber, bool &TelemetryRunning,
|
||||
uint64_t &TelemetryInterval, uint64_t &TelemetryWebSocketTimer,
|
||||
uint64_t &TelemetryKafkaTimer,
|
||||
uint64_t &TelemetryWebSocketCount,
|
||||
uint64_t &TelemetryKafkaCount,
|
||||
uint64_t &TelemetryWebSocketPackets,
|
||||
uint64_t &TelemetryKafkaPackets);
|
||||
|
||||
void onGarbageCollecting(Poco::Timer & timer);
|
||||
bool GetHealthDevices(std::uint64_t lowLimit, std::uint64_t highLimit, std::vector<std::string> & SerialNumbers);
|
||||
// bool ExtendedAttributes(const std::string &serialNumber, bool & hasGPS, std::uint64_t &Sanity,
|
||||
// std::double_t &MemoryUsed, std::double_t &Load, std::double_t &Temperature);
|
||||
|
||||
inline void AverageDeviceStatistics( uint64_t & Connections, uint64_t & AverageConnectionTime, uint64_t & NumberOfConnectingDevices) const {
|
||||
inline void AverageDeviceStatistics(uint64_t &Connections, uint64_t &AverageConnectionTime,
|
||||
uint64_t &NumberOfConnectingDevices) const {
|
||||
Connections = NumberOfConnectedDevices_;
|
||||
AverageConnectionTime = AverageDeviceConnectionTime_;
|
||||
NumberOfConnectingDevices = NumberOfConnectingDevices_;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::recursive_mutex WSServerMutex_;
|
||||
std::unique_ptr<Poco::Crypto::X509Certificate> IssuerCert_;
|
||||
std::list<std::unique_ptr<Poco::Net::HTTPServer>> WebServers_;
|
||||
Poco::Net::SocketReactor Reactor_;
|
||||
Poco::Thread ReactorThread_;
|
||||
std::string SimulatorId_;
|
||||
Poco::ThreadPool DeviceConnectionPool_{"ws:dev-pool", 2, 64};
|
||||
bool LookAtProvisioning_ = false;
|
||||
bool UseDefaultConfig_ = true;
|
||||
bool SimulatorEnabled_=false;
|
||||
std::unique_ptr<AP_WS_ReactorThreadPool> Reactor_pool_;
|
||||
std::atomic_bool Running_=false;
|
||||
// std::map<uint64_t, std::pair<std::shared_ptr<AP_WS_Connection>,bool>> Sessions_;
|
||||
std::map<std::uint64_t, std::shared_ptr<AP_WS_Connection>> Sessions_;
|
||||
std::map<uint64_t, std::pair<uint64_t,std::shared_ptr<AP_WS_Connection>>> SerialNumbers_;
|
||||
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::vector<std::shared_ptr<AP_WS_Connection>> Garbage_;
|
||||
|
||||
std::unique_ptr<Poco::TimerCallback<AP_WS_Server>> GarbageCollectorCallback_;
|
||||
Poco::Timer Timer_;
|
||||
Poco::Thread GarbageCollector_;
|
||||
|
||||
AP_WS_Server() noexcept:
|
||||
SubSystemServer("WebSocketServer", "WS-SVR", "ucentral.websocket") {
|
||||
inline bool SendFrame(const std::string &SerialNumber, const std::string &Payload) const {
|
||||
return SendFrame(Utils::SerialNumberToInt(SerialNumber), Payload);
|
||||
}
|
||||
|
||||
inline void AddRX(std::uint64_t bytes) {
|
||||
RX_ += bytes;
|
||||
}
|
||||
|
||||
inline void AddTX(std::uint64_t bytes) {
|
||||
TX_ += bytes;
|
||||
}
|
||||
|
||||
inline void GetTotalDataStatistics(std::uint64_t &TX, std::uint64_t &RX) const {
|
||||
TX = TX_;
|
||||
RX = RX_;
|
||||
}
|
||||
|
||||
bool KafkaDisableState() const { return KafkaDisableState_; }
|
||||
bool KafkaDisableHealthChecks() const { return KafkaDisableHealthChecks_; }
|
||||
|
||||
inline void IncrementConnectionCount() {
|
||||
++NumberOfConnectedDevices_;
|
||||
}
|
||||
|
||||
inline void DecrementConnectionCount() {
|
||||
--NumberOfConnectedDevices_;
|
||||
}
|
||||
|
||||
inline void AddCleanupSession(uint64_t session_id, uint64_t SerialNumber) {
|
||||
std::lock_guard G(CleanupMutex_);
|
||||
CleanupSessions_.emplace_back(session_id, SerialNumber);
|
||||
}
|
||||
|
||||
void CleanupSessions();
|
||||
|
||||
private:
|
||||
std::array<std::mutex,SessionHashMax> SessionMutex_;
|
||||
std::array<std::map<std::uint64_t, std::shared_ptr<AP_WS_Connection>>,SessionHashMax> Sessions_;
|
||||
using SerialNumberMap = std::map<uint64_t /* serial number */,
|
||||
std::shared_ptr<AP_WS_Connection>>;
|
||||
std::array<SerialNumberMap,MACHashMax> SerialNumbers_;
|
||||
mutable std::array<std::mutex,MACHashMax> SerialNumbersMutex_;
|
||||
|
||||
std::unique_ptr<Poco::Crypto::X509Certificate> IssuerCert_;
|
||||
std::vector<Poco::Crypto::X509Certificate> ClientCasCerts_;
|
||||
std::list<std::unique_ptr<Poco::Net::HTTPServer>> WebServers_;
|
||||
Poco::ThreadPool DeviceConnectionPool_{"ws:dev-pool", 4, 256};
|
||||
Poco::Net::SocketReactor Reactor_;
|
||||
Poco::Thread ReactorThread_;
|
||||
std::string SimulatorId_;
|
||||
bool LookAtProvisioning_ = false;
|
||||
bool UseDefaultConfig_ = true;
|
||||
bool SimulatorEnabled_ = false;
|
||||
bool AllowSerialNumberMismatch_ = true;
|
||||
|
||||
Poco::Thread CleanupThread_;
|
||||
std::mutex CleanupMutex_;
|
||||
std::deque<std::pair<uint64_t, uint64_t>> CleanupSessions_;
|
||||
|
||||
std::unique_ptr<AP_WS_ReactorThreadPool> Reactor_pool_;
|
||||
std::atomic_bool Running_ = false;
|
||||
|
||||
std::uint64_t MismatchDepth_ = 2;
|
||||
|
||||
std::atomic_uint64_t NumberOfConnectedDevices_ = 0;
|
||||
std::atomic_uint64_t AverageDeviceConnectionTime_ = 0;
|
||||
std::uint64_t NumberOfConnectingDevices_ = 0;
|
||||
std::uint64_t SessionTimeOut_ = 10*60;
|
||||
std::uint64_t LeftOverSessions_ = 0;
|
||||
std::atomic_uint64_t TX_=0,RX_=0;
|
||||
|
||||
std::atomic_bool KafkaDisableState_=false,
|
||||
KafkaDisableHealthChecks_=false;
|
||||
|
||||
Poco::Thread GarbageCollector_;
|
||||
|
||||
AP_WS_Server() noexcept
|
||||
: SubSystemServer("WebSocketServer", "WS-SVR", "ucentral.websocket") {}
|
||||
};
|
||||
|
||||
inline auto AP_WS_Server() { return AP_WS_Server::instance(); }
|
||||
|
||||
} //namespace
|
||||
} // namespace OpenWifi
|
||||
@@ -4,54 +4,54 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "CentralConfig.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
namespace OpenWifi {
|
||||
const std::string PlatformCacheFileName{"/plat_cache.json"};
|
||||
const std::string CapabilitiesCacheFileName{"/caps_cache.json"};
|
||||
|
||||
typedef std::map<std::string,nlohmann::json> CapabilitiesCache_t;
|
||||
typedef std::map<std::string, nlohmann::json> CapabilitiesCache_t;
|
||||
|
||||
class CapabilitiesCache {
|
||||
public:
|
||||
|
||||
static auto instance() {
|
||||
static auto instance = new CapabilitiesCache;
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline void Add(const Config::Capabilities &Caps) {
|
||||
if(Caps.Compatible().empty() || Caps.Platform().empty())
|
||||
if (Caps.Compatible().empty() || Caps.Platform().empty())
|
||||
return;
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
if(!PlatformsLoaded_)
|
||||
std::lock_guard G(Mutex_);
|
||||
if (!PlatformsLoaded_)
|
||||
LoadPlatforms();
|
||||
auto P = Poco::toUpper(Caps.Platform());
|
||||
auto P = Poco::toLower(Caps.Platform());
|
||||
auto Hint = Platforms_.find(Caps.Compatible());
|
||||
if(Hint==Platforms_.end()) {
|
||||
Platforms_.insert(std::make_pair(Caps.Compatible(),P));
|
||||
if (Hint == Platforms_.end()) {
|
||||
Platforms_.insert(std::make_pair(Caps.Compatible(), P));
|
||||
SavePlatforms();
|
||||
} else if(Hint->second != P) {
|
||||
} else if (Hint->second != P) {
|
||||
Hint->second = P;
|
||||
SavePlatforms();
|
||||
}
|
||||
|
||||
if(!CapabilitiesLoaded_)
|
||||
if (!CapabilitiesLoaded_)
|
||||
LoadCapabilities();
|
||||
|
||||
auto CapHint = Capabilities_.find(Caps.Compatible());
|
||||
if(CapHint==Capabilities_.end()) {
|
||||
auto C = nlohmann::json::parse(Caps.AsString());
|
||||
C.erase("restrictions");
|
||||
if (CapHint == Capabilities_.end()) {
|
||||
auto C = nlohmann::json::parse(Caps.AsString());
|
||||
C.erase("restrictions");
|
||||
Capabilities_[Caps.Compatible()] = nlohmann::json::parse(Caps.AsString());
|
||||
SaveCapabilities();
|
||||
} else {
|
||||
@@ -60,48 +60,49 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string GetPlatform(const std::string & DeviceType) {
|
||||
std::lock_guard G(Mutex_);
|
||||
inline std::string GetPlatform(const std::string &DeviceType) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
if(!PlatformsLoaded_) {
|
||||
if (!PlatformsLoaded_) {
|
||||
LoadPlatforms();
|
||||
}
|
||||
|
||||
auto Hint = Platforms_.find(DeviceType);
|
||||
if(Hint==Platforms_.end())
|
||||
return "AP";
|
||||
if (Hint == Platforms_.end())
|
||||
return Platforms::AP;
|
||||
return Hint->second;
|
||||
}
|
||||
|
||||
inline nlohmann::json GetCapabilities(const std::string & DeviceType) {
|
||||
std::lock_guard G(Mutex_);
|
||||
inline nlohmann::json GetCapabilities(const std::string &DeviceType) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
if(!CapabilitiesLoaded_) {
|
||||
if (!CapabilitiesLoaded_) {
|
||||
LoadCapabilities();
|
||||
}
|
||||
|
||||
auto Hint = Capabilities_.find(DeviceType);
|
||||
if(Hint==Capabilities_.end())
|
||||
if (Hint == Capabilities_.end())
|
||||
return nlohmann::json{};
|
||||
return Hint->second;
|
||||
}
|
||||
|
||||
inline const CapabilitiesCache_t & AllCapabilities() {
|
||||
std::lock_guard G(Mutex_);
|
||||
if(!CapabilitiesLoaded_) {
|
||||
inline const CapabilitiesCache_t &AllCapabilities() {
|
||||
std::lock_guard G(Mutex_);
|
||||
if (!CapabilitiesLoaded_) {
|
||||
LoadCapabilities();
|
||||
}
|
||||
return Capabilities_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
std::atomic_bool PlatformsLoaded_=false;
|
||||
std::atomic_bool CapabilitiesLoaded_=false;
|
||||
std::map<std::string,std::string> Platforms_;
|
||||
CapabilitiesCache_t Capabilities_;
|
||||
std::string PlatformCacheFileName_{ MicroServiceDataDirectory()+PlatformCacheFileName };
|
||||
std::string CapabilitiesCacheFileName_{ MicroServiceDataDirectory()+CapabilitiesCacheFileName };
|
||||
std::recursive_mutex Mutex_;
|
||||
std::atomic_bool PlatformsLoaded_ = false;
|
||||
std::atomic_bool CapabilitiesLoaded_ = false;
|
||||
std::map<std::string, std::string> Platforms_;
|
||||
CapabilitiesCache_t Capabilities_;
|
||||
std::string PlatformCacheFileName_{MicroServiceDataDirectory() + PlatformCacheFileName};
|
||||
std::string CapabilitiesCacheFileName_{MicroServiceDataDirectory() +
|
||||
CapabilitiesCacheFileName};
|
||||
|
||||
inline void LoadPlatforms() {
|
||||
try {
|
||||
@@ -109,11 +110,10 @@ namespace OpenWifi {
|
||||
nlohmann::json cache;
|
||||
i >> cache;
|
||||
|
||||
for(const auto &[Type,Platform]:cache.items()) {
|
||||
Platforms_[Type] = Platform;
|
||||
for (const auto &[Type, Platform] : cache.items()) {
|
||||
Platforms_[Type] = Poco::toLower(Platform.get<std::string>());
|
||||
}
|
||||
} catch(...) {
|
||||
|
||||
} catch (...) {
|
||||
}
|
||||
PlatformsLoaded_ = true;
|
||||
}
|
||||
@@ -124,36 +124,35 @@ namespace OpenWifi {
|
||||
nlohmann::json cache(Platforms_);
|
||||
i << cache;
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inline void LoadCapabilities() {
|
||||
try {
|
||||
std::ifstream i(CapabilitiesCacheFileName_, std::ios_base::binary|std::ios_base::in);
|
||||
std::ifstream i(CapabilitiesCacheFileName_,
|
||||
std::ios_base::binary | std::ios_base::in);
|
||||
nlohmann::json cache;
|
||||
i >> cache;
|
||||
|
||||
for(const auto &[Type,Caps]:cache.items()) {
|
||||
for (const auto &[Type, Caps] : cache.items()) {
|
||||
Capabilities_[Type] = Caps;
|
||||
}
|
||||
} catch(...) {
|
||||
|
||||
} catch (...) {
|
||||
}
|
||||
CapabilitiesLoaded_ = true;
|
||||
}
|
||||
|
||||
inline void SaveCapabilities() {
|
||||
try {
|
||||
std::ofstream i(CapabilitiesCacheFileName_, std::ios_base::trunc | std::ios_base::out | std::ios_base::binary );
|
||||
std::ofstream i(CapabilitiesCacheFileName_,
|
||||
std::ios_base::trunc | std::ios_base::out | std::ios_base::binary);
|
||||
nlohmann::json cache(Capabilities_);
|
||||
i << cache;
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline auto CapabilitiesCache() { return CapabilitiesCache::instance(); };
|
||||
inline auto CapabilitiesCache() { return CapabilitiesCache::instance(); };
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -7,17 +7,17 @@
|
||||
//
|
||||
#include <fstream>
|
||||
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/File.h"
|
||||
|
||||
#include "CentralConfig.h"
|
||||
#include "Daemon.h"
|
||||
|
||||
namespace OpenWifi::Config {
|
||||
|
||||
const static std::string BasicConfig {
|
||||
R"lit(
|
||||
const static std::string BasicConfig{
|
||||
R"lit(
|
||||
{
|
||||
"interfaces": [
|
||||
{
|
||||
@@ -150,10 +150,11 @@ R"lit(
|
||||
|
||||
void Config::SetBasicConfigFile() {
|
||||
try {
|
||||
Poco::File DefaultConfigFileName{MicroService::instance().DataDir() + "/default_config.json"};
|
||||
Poco::File DefaultConfigFileName{MicroService::instance().DataDir() +
|
||||
"/default_config.json"};
|
||||
DefaultConfiguration_ = BasicConfig;
|
||||
std::ofstream OS(DefaultConfigFileName.path(), std::ios::binary | std::ios::trunc );
|
||||
std::istringstream IS(DefaultConfiguration_);
|
||||
std::ofstream OS(DefaultConfigFileName.path(), std::ios::binary | std::ios::trunc);
|
||||
std::istringstream IS(DefaultConfiguration_);
|
||||
Poco::StreamCopier::copyStream(IS, OS);
|
||||
} catch (...) {
|
||||
DefaultConfiguration_ = BasicConfig;
|
||||
@@ -161,22 +162,23 @@ R"lit(
|
||||
}
|
||||
|
||||
Config::Config() {
|
||||
if(DefaultConfiguration_.empty())
|
||||
if (DefaultConfiguration_.empty())
|
||||
Init();
|
||||
Config_ = DefaultConfiguration_;
|
||||
}
|
||||
|
||||
void Config::Init() {
|
||||
if(DefaultConfiguration_.empty()) {
|
||||
if (DefaultConfiguration_.empty()) {
|
||||
// open the file
|
||||
try {
|
||||
Poco::File DefaultConfigFileName{MicroService::instance().DataDir()+"/default_config.json"};
|
||||
Poco::File DefaultConfigFileName{MicroService::instance().DataDir() +
|
||||
"/default_config.json"};
|
||||
if (!DefaultConfigFileName.exists()) {
|
||||
SetBasicConfigFile();
|
||||
} else {
|
||||
std::ifstream F(DefaultConfigFileName.path(),std::ios::binary | std::ios::in);
|
||||
std::ifstream F(DefaultConfigFileName.path(), std::ios::binary | std::ios::in);
|
||||
std::ostringstream C;
|
||||
Poco::StreamCopier::copyStream(F,C);
|
||||
Poco::StreamCopier::copyStream(F, C);
|
||||
DefaultConfiguration_ = C.str();
|
||||
}
|
||||
} catch (...) {
|
||||
@@ -185,101 +187,104 @@ R"lit(
|
||||
}
|
||||
}
|
||||
|
||||
bool Config::SetUUID(uint64_t UUID) {
|
||||
try {
|
||||
Poco::JSON::Parser Parser;
|
||||
auto Object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
|
||||
bool Config::SetUUID(uint64_t UUID) {
|
||||
try {
|
||||
Poco::JSON::Parser Parser;
|
||||
auto Object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
|
||||
Object->set("uuid", UUID);
|
||||
std::ostringstream NewConfig;
|
||||
Poco::JSON::Stringifier Stringifier;
|
||||
Poco::JSON::Stringifier Stringifier;
|
||||
Stringifier.condense(Object, NewConfig);
|
||||
Config_ = NewConfig.str();
|
||||
return true;
|
||||
}
|
||||
catch(const Poco::Exception &E)
|
||||
{
|
||||
std::cout << __func__ << ": new Configuration failed with " << E.displayText() << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
std::cout << __func__ << ": new Configuration failed with " << E.displayText()
|
||||
<< std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Config::Valid() {
|
||||
try {
|
||||
Poco::JSON::Parser Parser;
|
||||
auto object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
|
||||
if(object->has("uuid"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::uint64_t Config::UUID() {
|
||||
try {
|
||||
Poco::JSON::Parser Parser;
|
||||
auto object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
|
||||
if (object->has("uuid"))
|
||||
return object->get("uuid");
|
||||
} catch (...) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Config::Valid() {
|
||||
try {
|
||||
Poco::JSON::Parser Parser;
|
||||
auto object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
|
||||
if (object->has("uuid"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Poco::JSON::Object::Ptr Config::to_json() {
|
||||
Poco::JSON::Parser Parser;
|
||||
return Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
Poco::JSON::Parser Parser;
|
||||
return Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
|
||||
std::string Config::Default() {
|
||||
if(DefaultConfiguration_.empty())
|
||||
if (DefaultConfiguration_.empty())
|
||||
Init();
|
||||
return DefaultConfiguration_;
|
||||
}
|
||||
|
||||
/* std::string Capabilities::Default() {
|
||||
return std::string(R"lit({"model":{"id":"linksys,ea8300","name":"Linksys EA8300 (Dallas)"},
|
||||
"network":{"lan":{"ifname":"eth0","protocol":"static"},"wan":{"ifname":"eth1","protocol":"dhcp"}},
|
||||
"switch":{"switch0":{"enable":true,"reset":true,"ports":[{"num":0,"device":"eth0","need_tag":false,
|
||||
"want_untag":true},{"num":1,"role":"lan"},{"num":2,"role":"lan"},{"num":3,"role":"lan"},{"num":4,"role":"lan"}],
|
||||
"roles":[{"role":"lan","ports":"1 2 3 4 0","device":"eth0"}]}},
|
||||
"wifi":{"soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0":{"band":["5u"],"ht_capa":6639,
|
||||
"vht_capa":865696178,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,
|
||||
"channels":[100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165]},
|
||||
"platform/soc/a000000.wifi":{"band":["2"],"ht_capa":6639,"vht_capa":865687986,
|
||||
"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,"channels":[1,2,3,4,5,6,7,8,9,10,11]},
|
||||
"platform/soc/a800000.wifi":{"band":["5l"],"ht_capa":6639,"vht_capa":865687986,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],
|
||||
"tx_ant":3,"rx_ant":3,"channels":[36,40,44,48,52,56,60,64]}}})lit");
|
||||
}
|
||||
*/
|
||||
/* std::string Capabilities::Default() {
|
||||
return std::string(R"lit({"model":{"id":"linksys,ea8300","name":"Linksys EA8300
|
||||
(Dallas)"},
|
||||
"network":{"lan":{"ifname":"eth0","protocol":"static"},"wan":{"ifname":"eth1","protocol":"dhcp"}},
|
||||
"switch":{"switch0":{"enable":true,"reset":true,"ports":[{"num":0,"device":"eth0","need_tag":false,
|
||||
"want_untag":true},{"num":1,"role":"lan"},{"num":2,"role":"lan"},{"num":3,"role":"lan"},{"num":4,"role":"lan"}],
|
||||
"roles":[{"role":"lan","ports":"1 2 3 4 0","device":"eth0"}]}},
|
||||
"wifi":{"soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0":{"band":["5u"],"ht_capa":6639,
|
||||
"vht_capa":865696178,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,
|
||||
"channels":[100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165]},
|
||||
"platform/soc/a000000.wifi":{"band":["2"],"ht_capa":6639,"vht_capa":865687986,
|
||||
"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"tx_ant":3,"rx_ant":3,"channels":[1,2,3,4,5,6,7,8,9,10,11]},
|
||||
"platform/soc/a800000.wifi":{"band":["5l"],"ht_capa":6639,"vht_capa":865687986,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],
|
||||
"tx_ant":3,"rx_ant":3,"channels":[36,40,44,48,52,56,60,64]}}})lit");
|
||||
}
|
||||
*/
|
||||
|
||||
Capabilities::Capabilities(const Poco::JSON::Object::Ptr &Caps) {
|
||||
try {
|
||||
Capabilities::Capabilities(const Poco::JSON::Object::Ptr &Caps) {
|
||||
try {
|
||||
|
||||
if(Caps->has("compatible"))
|
||||
if (Caps->has("compatible"))
|
||||
Compatible_ = Caps->get("compatible").toString();
|
||||
|
||||
if(Caps->has("model"))
|
||||
if (Caps->has("model"))
|
||||
Model_ = Caps->get("model").toString();
|
||||
|
||||
if(Caps->has("platform"))
|
||||
Platform_ = Caps->get("platform").toString();
|
||||
if (Caps->has("platform"))
|
||||
Platform_ = Poco::toLower(Caps->get("platform").toString());
|
||||
|
||||
std::ostringstream OS;
|
||||
Caps->stringify(OS);
|
||||
AsString_ = OS.str();
|
||||
}
|
||||
catch ( const Poco::Exception & E )
|
||||
{
|
||||
Daemon()->logger().log(E);
|
||||
}
|
||||
}
|
||||
if(Compatible_.empty()) {
|
||||
Compatible_ = Model_;
|
||||
}
|
||||
|
||||
const std::string & Capabilities::Compatible() const {
|
||||
return Compatible_;
|
||||
std::ostringstream OS;
|
||||
Caps->stringify(OS);
|
||||
AsString_ = OS.str();
|
||||
} catch (const Poco::Exception &E) {
|
||||
Daemon()->logger().log(E);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string & Capabilities::Model() const {
|
||||
return Model_;
|
||||
}
|
||||
const std::string &Capabilities::Compatible() const { return Compatible_; }
|
||||
|
||||
const std::string & Capabilities::Platform() const {
|
||||
return Platform_;
|
||||
}
|
||||
const std::string &Capabilities::Model() const { return Model_; }
|
||||
|
||||
const std::string & Capabilities::AsString() const {
|
||||
return AsString_;
|
||||
}
|
||||
const std::string &Capabilities::Platform() const { return Platform_; }
|
||||
|
||||
} // namespace
|
||||
const std::string &Capabilities::AsString() const { return AsString_; }
|
||||
|
||||
} // namespace OpenWifi::Config
|
||||
|
||||
@@ -8,58 +8,56 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include <string>
|
||||
|
||||
namespace OpenWifi::Config {
|
||||
|
||||
class Config {
|
||||
public:
|
||||
explicit Config(const std::string &Config)
|
||||
:Config_(Config) {
|
||||
}
|
||||
public:
|
||||
explicit Config(const std::string &Config) : Config_(Config) {}
|
||||
|
||||
Config();
|
||||
bool SetUUID(uint64_t UUID);
|
||||
[[nodiscard]] bool Valid();
|
||||
Config();
|
||||
bool SetUUID(uint64_t UUID);
|
||||
[[nodiscard]] bool Valid();
|
||||
[[nodiscard]] std::string get() { return Config_; };
|
||||
[[nodiscard]] std::string Default();
|
||||
[[nodiscard]] Poco::JSON::Object::Ptr to_json();
|
||||
[[nodiscard]] std::uint64_t UUID();
|
||||
|
||||
private:
|
||||
void Init();
|
||||
void SetBasicConfigFile();
|
||||
inline static std::string DefaultConfiguration_ = "";
|
||||
std::string Config_;
|
||||
};
|
||||
private:
|
||||
void Init();
|
||||
void SetBasicConfigFile();
|
||||
inline static std::string DefaultConfiguration_ = "";
|
||||
std::string Config_;
|
||||
};
|
||||
|
||||
class Capabilities {
|
||||
public:
|
||||
explicit Capabilities(const Poco::JSON::Object::Ptr &Caps);
|
||||
class Capabilities {
|
||||
public:
|
||||
explicit Capabilities(const Poco::JSON::Object::Ptr &Caps);
|
||||
|
||||
/* Capabilities()
|
||||
{
|
||||
Capabilities_ = Default();
|
||||
}
|
||||
/* Capabilities()
|
||||
{
|
||||
Capabilities_ = Default();
|
||||
}
|
||||
|
||||
static std::string Default();
|
||||
static std::string Default();
|
||||
|
||||
[[nodiscard]] const std::string & Get() const { return Capabilities_; };
|
||||
*/
|
||||
[[nodiscard]] const std::string & Get() const { return Capabilities_; };
|
||||
*/
|
||||
|
||||
[[nodiscard]] const std::string & Compatible() const;
|
||||
[[nodiscard]] const std::string & Model() const;
|
||||
[[nodiscard]] const std::string & Platform() const;
|
||||
[[nodiscard]] const std::string & AsString() const;
|
||||
[[nodiscard]] const std::string &Compatible() const;
|
||||
[[nodiscard]] const std::string &Model() const;
|
||||
[[nodiscard]] const std::string &Platform() const;
|
||||
[[nodiscard]] const std::string &AsString() const;
|
||||
|
||||
private:
|
||||
std::string Compatible_;
|
||||
std::string Model_;
|
||||
private:
|
||||
std::string Compatible_;
|
||||
std::string Model_;
|
||||
std::string Platform_;
|
||||
std::string AsString_;
|
||||
std::string AsString_;
|
||||
|
||||
void Parse();
|
||||
};
|
||||
|
||||
} // namespace
|
||||
};
|
||||
|
||||
} // namespace OpenWifi::Config
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
|
||||
#include "Poco/JSON/Parser.h"
|
||||
|
||||
#include "CommandManager.h"
|
||||
#include "AP_WS_Server.h"
|
||||
#include "CommandManager.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
@@ -34,284 +34,395 @@ namespace OpenWifi {
|
||||
Poco::JSON::Object::Ptr Payload = Resp->Payload_;
|
||||
std::string SerialNumberStr = Utils::IntToSerialNumber(Resp->SerialNumber_);
|
||||
|
||||
bool NoReply = false;
|
||||
|
||||
if (!Payload->has(uCentralProtocol::ID)) {
|
||||
poco_error(Logger(), fmt::format("({}): Invalid RPC response.", SerialNumberStr));
|
||||
poco_error(Logger(),
|
||||
fmt::format("({}): Invalid RPC response.", SerialNumberStr));
|
||||
} else {
|
||||
uint64_t ID = Payload->get(uCentralProtocol::ID);
|
||||
std::shared_ptr<promise_type_t> TmpRpcEntry;
|
||||
poco_debug(Logger(),fmt::format("({}): Processing {} response.", SerialNumberStr, ID));
|
||||
if (ID > 1) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
poco_debug(Logger(), fmt::format("({}): Processing {} response.",
|
||||
SerialNumberStr, ID));
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
auto RPC = OutStandingRequests_.find(ID);
|
||||
if (RPC == OutStandingRequests_.end() ||
|
||||
RPC->second.SerialNumber != Resp->SerialNumber_) {
|
||||
poco_debug(Logger(),
|
||||
fmt::format("({}): RPC {} completed.", SerialNumberStr, ID));
|
||||
if (RPC == OutStandingRequests_.end()) {
|
||||
poco_debug(Logger(), fmt::format("({}): RPC {} cannot be found.",
|
||||
SerialNumberStr, ID));
|
||||
} else if (RPC->second.SerialNumber != Resp->SerialNumber_) {
|
||||
poco_debug(
|
||||
Logger(),
|
||||
fmt::format("({}): RPC {} serial number mismatch {}!={}.",
|
||||
SerialNumberStr, ID, RPC->second.SerialNumber,
|
||||
Resp->SerialNumber_));
|
||||
} else {
|
||||
std::shared_ptr<promise_type_t> TmpRpcEntry;
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time =
|
||||
std::chrono::high_resolution_clock::now() -
|
||||
RPC->second.submitted;
|
||||
|
||||
poco_debug(Logger(),
|
||||
fmt::format("({}): Received RPC answer {}. Command={}",
|
||||
SerialNumberStr, ID, APCommands::to_string(RPC->second.Command)));
|
||||
if(RPC->second.Command==APCommands::Commands::script) {
|
||||
if(RPC->second.State==2) {
|
||||
// look at the payload to see if we should continue or not...
|
||||
if (RPC->second.rpc_entry) {
|
||||
TmpRpcEntry = RPC->second.rpc_entry;
|
||||
}
|
||||
|
||||
// Payload->stringify(std::cout);
|
||||
|
||||
if (Payload->has("result")) {
|
||||
auto Result = Payload->getObject("result");
|
||||
if (Result->has("status")) {
|
||||
auto Status = Result->getObject("status");
|
||||
// Status->stringify(std::cout);
|
||||
std::uint64_t Error = Status->get("error");
|
||||
if(Error==0) {
|
||||
StorageService()->CommandCompleted(RPC->second.UUID, Payload,
|
||||
rpc_execution_time, true);
|
||||
RPC->second.State = 1 ;
|
||||
} else {
|
||||
StorageService()->CommandCompleted(RPC->second.UUID, Payload,
|
||||
rpc_execution_time, true);
|
||||
std::string ErrorTxt = Status->get("result");
|
||||
StorageService()->CancelWaitFile(RPC->second.UUID, ErrorTxt);
|
||||
RPC->second.State = 0 ;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// std::cout << "Bad payload on command result" << std::endl;
|
||||
RPC->second.State=0;
|
||||
}
|
||||
} else {
|
||||
// std::cout << "Completing script 2 phase commit." << std::endl;
|
||||
StorageService()->CommandCompleted(RPC->second.UUID, Payload,
|
||||
rpc_execution_time, true);
|
||||
NoReply = true;
|
||||
RPC->second.State=0;
|
||||
}
|
||||
fmt::format("({}): Received RPC answer {}. Command={}",
|
||||
SerialNumberStr, ID,
|
||||
APCommands::to_string(RPC->second.Command)));
|
||||
if (RPC->second.Command == APCommands::Commands::script) {
|
||||
CompleteScriptCommand(RPC->second, Payload, rpc_execution_time);
|
||||
} else if (RPC->second.Command == APCommands::Commands::telemetry) {
|
||||
CompleteTelemetryCommand(RPC->second, Payload,
|
||||
rpc_execution_time);
|
||||
} else if (RPC->second.Command == APCommands::Commands::configure && RPC->second.rpc_entry==nullptr) {
|
||||
CompleteConfigureCommand(RPC->second, Payload,
|
||||
rpc_execution_time);
|
||||
} else {
|
||||
if(RPC->second.Command!=APCommands::Commands::telemetry) {
|
||||
StorageService()->CommandCompleted(
|
||||
RPC->second.UUID, Payload, rpc_execution_time, true);
|
||||
}
|
||||
StorageService()->CommandCompleted(RPC->second.UUID, Payload,
|
||||
rpc_execution_time, true);
|
||||
if (RPC->second.rpc_entry) {
|
||||
TmpRpcEntry = RPC->second.rpc_entry;
|
||||
}
|
||||
RPC->second.State = 0 ;
|
||||
}
|
||||
|
||||
if(RPC->second.State==0) {
|
||||
RPC->second.State = 0;
|
||||
OutStandingRequests_.erase(ID);
|
||||
if (TmpRpcEntry != nullptr)
|
||||
TmpRpcEntry->set_value(Payload);
|
||||
}
|
||||
}
|
||||
if(!NoReply && TmpRpcEntry != nullptr)
|
||||
TmpRpcEntry->set_value(Payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(),"Exception occurred during run.");
|
||||
poco_warning(Logger(), "Exception occurred during run.");
|
||||
}
|
||||
NextMsg = ResponseQueue_.waitDequeueNotification();
|
||||
}
|
||||
poco_information(Logger(),"RPC Command processor stopping.");
|
||||
}
|
||||
poco_information(Logger(), "RPC Command processor stopping.");
|
||||
}
|
||||
|
||||
int CommandManager::Start() {
|
||||
poco_notice(Logger(),"Starting...");
|
||||
bool CommandManager::CompleteTelemetryCommand(
|
||||
CommandInfo &Command, [[maybe_unused]] const Poco::JSON::Object::Ptr &Payload,
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time) {
|
||||
std::shared_ptr<promise_type_t> TmpRpcEntry;
|
||||
|
||||
StorageService()->CommandCompleted(Command.UUID, Payload, rpc_execution_time, true);
|
||||
|
||||
if (Command.rpc_entry) {
|
||||
TmpRpcEntry = Command.rpc_entry;
|
||||
}
|
||||
Command.State = 0;
|
||||
|
||||
OutStandingRequests_.erase(Command.Id);
|
||||
if (TmpRpcEntry != nullptr)
|
||||
TmpRpcEntry->set_value(Payload);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandManager::CompleteConfigureCommand(
|
||||
CommandInfo &Command, [[maybe_unused]] const Poco::JSON::Object::Ptr &Payload,
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time) {
|
||||
std::shared_ptr<promise_type_t> TmpRpcEntry;
|
||||
|
||||
if (Command.rpc_entry) {
|
||||
TmpRpcEntry = Command.rpc_entry;
|
||||
}
|
||||
|
||||
StorageService()->CommandCompleted(Command.UUID, Payload, rpc_execution_time, true);
|
||||
|
||||
if (Payload->has("result")) {
|
||||
auto Result = Payload->getObject("result");
|
||||
if (Result->has("status") && Result->has("serial")) {
|
||||
auto Status = Result->getObject("status");
|
||||
auto SerialNumber = Result->get("serial").toString();
|
||||
std::uint64_t Error = Status->get("error");
|
||||
if (Error == 2) {
|
||||
StorageService()->RollbackDeviceConfigurationChange(SerialNumber);
|
||||
} else {
|
||||
StorageService()->CompleteDeviceConfigurationChange(SerialNumber);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
Command.State = 0;
|
||||
|
||||
if (Command.rpc_entry) {
|
||||
TmpRpcEntry = Command.rpc_entry;
|
||||
}
|
||||
|
||||
OutStandingRequests_.erase(Command.Id);
|
||||
if (TmpRpcEntry != nullptr)
|
||||
TmpRpcEntry->set_value(Payload);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CommandManager::CompleteScriptCommand(
|
||||
CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time) {
|
||||
bool Reply = true;
|
||||
std::shared_ptr<promise_type_t> TmpRpcEntry;
|
||||
|
||||
if (Command.rpc_entry) {
|
||||
TmpRpcEntry = Command.rpc_entry;
|
||||
}
|
||||
if (Command.State == 2) {
|
||||
// look at the payload to see if we should continue or not...
|
||||
if (Payload->has("result")) {
|
||||
auto Result = Payload->getObject("result");
|
||||
if (Result->has("status")) {
|
||||
auto Status = Result->getObject("status");
|
||||
|
||||
std::uint64_t Error = Status->get("error");
|
||||
if (Error == 0) {
|
||||
StorageService()->CommandCompleted(Command.UUID, Payload,
|
||||
rpc_execution_time, true);
|
||||
Command.State = 1;
|
||||
} else {
|
||||
StorageService()->CommandCompleted(Command.UUID, Payload,
|
||||
rpc_execution_time, true);
|
||||
std::string ErrorTxt = Status->get("result");
|
||||
StorageService()->CancelWaitFile(Command.UUID, ErrorTxt);
|
||||
Command.State = 0;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
Command.State = 0;
|
||||
}
|
||||
} else if (Command.State == 1) {
|
||||
StorageService()->CommandCompleted(Command.UUID, Payload, rpc_execution_time, true);
|
||||
if (Command.Deferred) {
|
||||
Reply = false;
|
||||
}
|
||||
Command.State = 0;
|
||||
}
|
||||
|
||||
if (Command.State == 0) {
|
||||
OutStandingRequests_.erase(Command.Id);
|
||||
}
|
||||
if (Reply && TmpRpcEntry != nullptr)
|
||||
TmpRpcEntry->set_value(Payload);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CommandManager::Start() {
|
||||
poco_notice(Logger(), "Starting...");
|
||||
|
||||
commandTimeOut_ = MicroServiceConfigGetInt("command.timeout", 4 * 60 * 60);
|
||||
commandRetry_ = MicroServiceConfigGetInt("command.retry", 120);
|
||||
janitorInterval_ = MicroServiceConfigGetInt("command.janitor", 2 * 60); // 1 hour
|
||||
queueInterval_ = MicroServiceConfigGetInt("command.queue", 30);
|
||||
|
||||
ManagerThread.start(*this);
|
||||
|
||||
JanitorCallback_ = std::make_unique<Poco::TimerCallback<CommandManager>>(*this,&CommandManager::onJanitorTimer);
|
||||
JanitorTimer_.setStartInterval( 10000 );
|
||||
JanitorTimer_.setPeriodicInterval(10 * 60 * 1000); // 1 hours
|
||||
JanitorCallback_ = std::make_unique<Poco::TimerCallback<CommandManager>>(
|
||||
*this, &CommandManager::onJanitorTimer);
|
||||
JanitorTimer_.setStartInterval(10000);
|
||||
JanitorTimer_.setPeriodicInterval(janitorInterval_ * 1000); // 1 hours
|
||||
JanitorTimer_.start(*JanitorCallback_, MicroServiceTimerPool());
|
||||
|
||||
CommandRunnerCallback_ = std::make_unique<Poco::TimerCallback<CommandManager>>(*this,&CommandManager::onCommandRunnerTimer);
|
||||
CommandRunnerTimer_.setStartInterval( 10000 );
|
||||
CommandRunnerTimer_.setPeriodicInterval(30 * 1000); // 1 hours
|
||||
CommandRunnerCallback_ = std::make_unique<Poco::TimerCallback<CommandManager>>(
|
||||
*this, &CommandManager::onCommandRunnerTimer);
|
||||
CommandRunnerTimer_.setStartInterval(10000);
|
||||
CommandRunnerTimer_.setPeriodicInterval(queueInterval_ * 1000); // 1 hours
|
||||
CommandRunnerTimer_.start(*CommandRunnerCallback_, MicroServiceTimerPool());
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CommandManager::Stop() {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
void CommandManager::Stop() {
|
||||
poco_notice(Logger(), "Stopping...");
|
||||
Running_ = false;
|
||||
JanitorTimer_.stop();
|
||||
CommandRunnerTimer_.stop();
|
||||
ResponseQueue_.wakeUpAll();
|
||||
ManagerThread.wakeUp();
|
||||
ManagerThread.join();
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
}
|
||||
ManagerThread.join();
|
||||
poco_notice(Logger(), "Stopped...");
|
||||
}
|
||||
|
||||
void CommandManager::WakeUp() {
|
||||
poco_notice(Logger(),"Waking up...");
|
||||
ManagerThread.wakeUp();
|
||||
}
|
||||
void CommandManager::WakeUp() {
|
||||
poco_notice(Logger(), "Waking up...");
|
||||
ManagerThread.wakeUp();
|
||||
}
|
||||
|
||||
void CommandManager::onJanitorTimer([[maybe_unused]] Poco::Timer & timer) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
void CommandManager::onJanitorTimer([[maybe_unused]] Poco::Timer &timer) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
Utils::SetThreadName("cmd:janitor");
|
||||
Poco::Logger & MyLogger = Poco::Logger::get("CMD-MGR-JANITOR");
|
||||
Poco::Logger &MyLogger = Poco::Logger::get("CMD-MGR-JANITOR");
|
||||
std::string TimeOutError("No response.");
|
||||
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
for(auto request=OutStandingRequests_.begin();request!=OutStandingRequests_.end();) {
|
||||
for (auto request = OutStandingRequests_.begin(); request != OutStandingRequests_.end();) {
|
||||
std::chrono::duration<double, std::milli> delta = now - request->second.submitted;
|
||||
if(delta > 10min) {
|
||||
MyLogger.debug(fmt::format("{}: Command={} for {} Timed out.",
|
||||
request->second.UUID,
|
||||
if (delta > 10min) {
|
||||
MyLogger.debug(fmt::format("{}: Command={} for {} Timed out.", request->second.UUID,
|
||||
APCommands::to_string(request->second.Command),
|
||||
Utils::IntToSerialNumber(request->second.SerialNumber)));
|
||||
if ((request->second.Command == APCommands::Commands::script &&
|
||||
request->second.Deferred) ||
|
||||
(request->second.Command == APCommands::Commands::trace)) {
|
||||
StorageService()->CancelWaitFile(request->second.UUID, TimeOutError);
|
||||
}
|
||||
StorageService()->SetCommandTimedOut(request->second.UUID);
|
||||
request = OutStandingRequests_.erase(request);
|
||||
} else {
|
||||
++request;
|
||||
}
|
||||
}
|
||||
poco_information(MyLogger,
|
||||
fmt::format("Outstanding-requests {}", OutStandingRequests_.size()));
|
||||
fmt::format("Outstanding-requests {}", OutStandingRequests_.size()));
|
||||
}
|
||||
|
||||
bool CommandManager::IsCommandRunning(const std::string &C) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
for (const auto &request : OutStandingRequests_) {
|
||||
if (request.second.UUID == C) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
return std::any_of(
|
||||
OutStandingRequests_.begin(), OutStandingRequests_.end(),
|
||||
[C](const std::pair<std::uint64_t, CommandInfo> &r) { return r.second.UUID == C; });
|
||||
}
|
||||
|
||||
void CommandManager::onCommandRunnerTimer([[maybe_unused]] Poco::Timer &timer) {
|
||||
Utils::SetThreadName("cmd:schdlr");
|
||||
Poco::Logger &MyLogger = Poco::Logger::get("CMD-MGR-SCHEDULER");
|
||||
|
||||
poco_trace(MyLogger,"Scheduler starting.");
|
||||
poco_trace(MyLogger, "Scheduler starting.");
|
||||
|
||||
try {
|
||||
|
||||
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 {
|
||||
poco_trace(MyLogger, fmt::format("Scheduler about to process {} commands.",
|
||||
Commands.size()));
|
||||
for (auto &Cmd : Commands) {
|
||||
if (!Running_) {
|
||||
poco_warning(MyLogger,
|
||||
"Scheduler quitting because service is stopping.");
|
||||
break;
|
||||
}
|
||||
poco_trace(MyLogger,
|
||||
fmt::format("{}: Serial={} Command={} Starting processing.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
try {
|
||||
|
||||
// Skip an already running command
|
||||
if(IsCommandRunning(Cmd.UUID))
|
||||
continue;
|
||||
// Skip an already running command
|
||||
if (IsCommandRunning(Cmd.UUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto now = Utils::Now();
|
||||
// 2 hour timeout for commands
|
||||
if ((now - Cmd.Submitted) > (1 * 60 * 60)) {
|
||||
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={} has expired.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
StorageService()->SetCommandTimedOut(Cmd.UUID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!AP_WS_Server()->Connected(
|
||||
Utils::SerialNumberToInt(Cmd.SerialNumber))) {
|
||||
poco_trace(
|
||||
MyLogger,
|
||||
fmt::format("{}: Serial={} Command={} Preparing execution.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
auto Params = P.parse(Cmd.Details).extract<Poco::JSON::Object::Ptr>();
|
||||
auto Result = PostCommandDisk(
|
||||
Next_RPC_ID(), APCommands::to_apcommand(Cmd.Command.c_str()),
|
||||
Cmd.SerialNumber, Cmd.Command, *Params, Cmd.UUID, Sent);
|
||||
if (Sent) {
|
||||
StorageService()->SetCommandExecuted(Cmd.UUID);
|
||||
poco_debug(MyLogger,
|
||||
fmt::format("{}: Serial={} Command={} Sent.", Cmd.UUID,
|
||||
Cmd.SerialNumber, Cmd.Command));
|
||||
} else {
|
||||
poco_debug(
|
||||
MyLogger,
|
||||
fmt::format("{}: Serial={} Command={} Re-queued command.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
StorageService()->SetCommandLastTry(Cmd.UUID);
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_debug(
|
||||
MyLogger,
|
||||
fmt::format(
|
||||
"{}: Serial={} Command={} Device is not connected.",
|
||||
"{}: Serial={} Command={} Failed. Command marked as completed.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string ExecutingUUID;
|
||||
APCommands::Commands ExecutingCommand=APCommands::Commands::unknown;
|
||||
if (CommandRunningForDevice(Utils::SerialNumberToInt(Cmd.SerialNumber),
|
||||
ExecutingUUID, ExecutingCommand)) {
|
||||
poco_trace(
|
||||
MyLogger,
|
||||
fmt::format(
|
||||
"{}: Serial={} Command={} Device is already busy with command {} (Command={})."
|
||||
, Cmd.UUID, Cmd.SerialNumber, Cmd.Command, ExecutingUUID, APCommands::to_string(ExecutingCommand)));
|
||||
continue;
|
||||
}
|
||||
|
||||
Poco::JSON::Parser P;
|
||||
bool Sent;
|
||||
poco_information(MyLogger, fmt::format("{}: Serial={} Command={} Preparing execution.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
auto Params = P.parse(Cmd.Details).extract<Poco::JSON::Object::Ptr>();
|
||||
auto Result = PostCommandDisk(Next_RPC_ID(), APCommands::to_apcommand(Cmd.Command.c_str()), Cmd.SerialNumber, Cmd.Command,
|
||||
*Params, Cmd.UUID, Sent);
|
||||
if (Sent) {
|
||||
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.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
fmt::format("{}: Serial={} Command={} Hard failure. "
|
||||
"Command marked as completed.",
|
||||
Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
|
||||
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) {
|
||||
MyLogger.log(E);
|
||||
} catch (...) {
|
||||
poco_warning(MyLogger,"Exception during command processing.");
|
||||
}
|
||||
poco_trace(MyLogger,"Scheduler done.");
|
||||
catch (Poco::Exception &E) {
|
||||
MyLogger.log(E);
|
||||
}
|
||||
catch (...) {
|
||||
poco_warning(MyLogger, "Exception during command processing.");
|
||||
}
|
||||
poco_trace(MyLogger, "Scheduler done.");
|
||||
}
|
||||
|
||||
std::shared_ptr<CommandManager::promise_type_t> CommandManager::PostCommand(
|
||||
uint64_t RPC_ID,
|
||||
APCommands::Commands Command,
|
||||
const std::string &SerialNumber,
|
||||
const std::string &CommandStr,
|
||||
const Poco::JSON::Object &Params,
|
||||
const std::string &UUID,
|
||||
bool oneway_rpc,
|
||||
bool disk_only,
|
||||
bool & Sent) {
|
||||
uint64_t RPC_ID, APCommands::Commands Command, const std::string &SerialNumber,
|
||||
const std::string &CommandStr, const Poco::JSON::Object &Params, const std::string &UUID,
|
||||
bool oneway_rpc, [[maybe_unused]] bool disk_only, bool &Sent, bool rpc, bool Deferred) {
|
||||
|
||||
auto SerialNumberInt = Utils::SerialNumberToInt(SerialNumber);
|
||||
Sent=false;
|
||||
Sent = false;
|
||||
|
||||
std::stringstream ToSend;
|
||||
std::stringstream ToSend;
|
||||
|
||||
CommandInfo Idx;
|
||||
Idx.Id = oneway_rpc ? 1 : RPC_ID;
|
||||
Idx.SerialNumber = SerialNumberInt;
|
||||
Idx.Command = Command;
|
||||
if(Command == APCommands::Commands::script)
|
||||
Idx.State=2;
|
||||
Idx.UUID = UUID;
|
||||
CommandInfo CInfo;
|
||||
CInfo.Id = oneway_rpc ? 1 : RPC_ID;
|
||||
CInfo.SerialNumber = SerialNumberInt;
|
||||
CInfo.Command = Command;
|
||||
CInfo.Deferred = Deferred;
|
||||
CInfo.UUID = UUID;
|
||||
if (Command == APCommands::Commands::script && Deferred) {
|
||||
CInfo.State = 2;
|
||||
} else {
|
||||
CInfo.State = 1;
|
||||
}
|
||||
|
||||
Poco::JSON::Object CompleteRPC;
|
||||
CompleteRPC.set(uCentralProtocol::JSONRPC, uCentralProtocol::JSONRPC_VERSION);
|
||||
@@ -319,20 +430,38 @@ namespace OpenWifi {
|
||||
CompleteRPC.set(uCentralProtocol::METHOD, CommandStr);
|
||||
CompleteRPC.set(uCentralProtocol::PARAMS, Params);
|
||||
Poco::JSON::Stringifier::stringify(CompleteRPC, ToSend);
|
||||
Idx.rpc_entry = disk_only ? nullptr : std::make_shared<CommandManager::promise_type_t>();
|
||||
CInfo.rpc_entry = rpc ? std::make_shared<CommandManager::promise_type_t>() : nullptr;
|
||||
|
||||
poco_debug(Logger(), fmt::format("{}: Sending command {} to {}. ID: {}", UUID, CommandStr, SerialNumber, RPC_ID));
|
||||
if(AP_WS_Server()->SendFrame(SerialNumber, ToSend.str())) {
|
||||
if(!oneway_rpc) {
|
||||
std::lock_guard M(Mutex_);
|
||||
OutStandingRequests_[RPC_ID] = Idx;
|
||||
}
|
||||
poco_debug(Logger(), fmt::format("{}: Sending command {} to {}. ID: {}", UUID, CommandStr,
|
||||
SerialNumber, RPC_ID));
|
||||
// Do not change the order. It is possible that an RPC completes before it is entered in
|
||||
// the map. So we insert it first, even if we may need to remove it later upon failure.
|
||||
if (!oneway_rpc) {
|
||||
std::lock_guard M(Mutex_);
|
||||
OutStandingRequests_[RPC_ID] = CInfo;
|
||||
}
|
||||
if (AP_WS_Server()->SendFrame(SerialNumber, ToSend.str())) {
|
||||
poco_debug(Logger(), fmt::format("{}: Sent command. ID: {}", UUID, RPC_ID));
|
||||
Sent=true;
|
||||
return Idx.rpc_entry;
|
||||
Sent = true;
|
||||
return CInfo.rpc_entry;
|
||||
} else if (!oneway_rpc) {
|
||||
std::lock_guard M(Mutex_);
|
||||
OutStandingRequests_.erase(RPC_ID);
|
||||
}
|
||||
|
||||
poco_warning(Logger(), fmt::format("{}: Failed to send command. ID: {}", UUID, RPC_ID));
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool CommandManager::FireAndForget(const std::string &SerialNumber, const std::string &Method, const Poco::JSON::Object &Params) {
|
||||
Poco::JSON::Object CompleteRPC;
|
||||
CompleteRPC.set(uCentralProtocol::JSONRPC, uCentralProtocol::JSONRPC_VERSION);
|
||||
CompleteRPC.set(uCentralProtocol::ID, 0);
|
||||
CompleteRPC.set(uCentralProtocol::METHOD, Method);
|
||||
CompleteRPC.set(uCentralProtocol::PARAMS, Params);
|
||||
std::stringstream ToSend;
|
||||
CompleteRPC.stringify(ToSend);
|
||||
poco_debug(Logger(), fmt::format("{}: Fire and forget command {}.", SerialNumber, Method));
|
||||
return AP_WS_Server()->SendFrame(SerialNumber, ToSend.str())>0;
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -9,213 +9,194 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
#include "Poco/Timer.h"
|
||||
#include "Poco/Notification.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
#include "Poco/Timer.h"
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "fmt/format.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
|
||||
#include "RESTObjects/RESTAPI_GWobjects.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RPCResponseNotification: public Poco::Notification {
|
||||
class RPCResponseNotification : public Poco::Notification {
|
||||
public:
|
||||
RPCResponseNotification(std::uint64_t ser,
|
||||
Poco::JSON::Object::Ptr pl) :
|
||||
SerialNumber_(ser),
|
||||
Payload_(std::move(pl))
|
||||
{
|
||||
|
||||
}
|
||||
std::uint64_t SerialNumber_;
|
||||
Poco::JSON::Object::Ptr Payload_;
|
||||
RPCResponseNotification(std::uint64_t ser, Poco::JSON::Object::Ptr pl)
|
||||
: SerialNumber_(ser), Payload_(std::move(pl)) {}
|
||||
std::uint64_t SerialNumber_;
|
||||
Poco::JSON::Object::Ptr Payload_;
|
||||
};
|
||||
|
||||
|
||||
class CommandManager : public SubSystemServer, Poco::Runnable {
|
||||
public:
|
||||
using objtype_t = Poco::JSON::Object::Ptr;
|
||||
using promise_type_t = std::promise<objtype_t>;
|
||||
public:
|
||||
using objtype_t = Poco::JSON::Object::Ptr;
|
||||
using promise_type_t = std::promise<objtype_t>;
|
||||
|
||||
struct CommandInfo {
|
||||
std::uint64_t Id=0;
|
||||
std::uint64_t SerialNumber=0;
|
||||
APCommands::Commands Command;
|
||||
std::string UUID;
|
||||
std::uint64_t State=1;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> submitted = std::chrono::high_resolution_clock::now();
|
||||
std::shared_ptr<promise_type_t> rpc_entry;
|
||||
};
|
||||
struct CommandInfo {
|
||||
std::uint64_t Id = 0;
|
||||
std::uint64_t SerialNumber = 0;
|
||||
APCommands::Commands Command;
|
||||
std::string UUID;
|
||||
std::uint64_t State = 1;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> submitted =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
std::shared_ptr<promise_type_t> rpc_entry;
|
||||
bool Deferred = false;
|
||||
};
|
||||
|
||||
struct RPCResponse {
|
||||
std::uint64_t serialNumber;
|
||||
Poco::JSON::Object::Ptr payload;
|
||||
struct RPCResponse {
|
||||
std::uint64_t serialNumber;
|
||||
Poco::JSON::Object::Ptr payload;
|
||||
|
||||
explicit RPCResponse(std::uint64_t ser, Poco::JSON::Object::Ptr pl)
|
||||
:
|
||||
serialNumber(ser),
|
||||
payload(std::move(pl)) {
|
||||
}
|
||||
};
|
||||
explicit RPCResponse(std::uint64_t ser, Poco::JSON::Object::Ptr pl)
|
||||
: serialNumber(ser), payload(std::move(pl)) {}
|
||||
};
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
void WakeUp();
|
||||
inline void PostCommandResult(const std::string &SerialNumber, Poco::JSON::Object::Ptr Obj) {
|
||||
ResponseQueue_.enqueueNotification(new RPCResponseNotification(Utils::SerialNumberToInt(SerialNumber),std::move(Obj)));
|
||||
}
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
void WakeUp();
|
||||
inline void PostCommandResult(const std::string &SerialNumber,
|
||||
Poco::JSON::Object::Ptr Obj) {
|
||||
ResponseQueue_.enqueueNotification(new RPCResponseNotification(
|
||||
Utils::SerialNumberToInt(SerialNumber), std::move(Obj)));
|
||||
}
|
||||
|
||||
std::shared_ptr<promise_type_t> PostCommandOneWayDisk(uint64_t RPC_ID,
|
||||
APCommands::Commands Command,
|
||||
const std::string &SerialNumber,
|
||||
const std::string &Method,
|
||||
const Poco::JSON::Object &Params,
|
||||
const std::string &UUID,
|
||||
bool & Sent) {
|
||||
return PostCommand(RPC_ID,
|
||||
Command,
|
||||
SerialNumber,
|
||||
Method,
|
||||
Params,
|
||||
UUID,
|
||||
true, true, Sent );
|
||||
}
|
||||
std::shared_ptr<promise_type_t> PostCommandOneWayDisk(uint64_t RPC_ID,
|
||||
APCommands::Commands Command,
|
||||
const std::string &SerialNumber,
|
||||
const std::string &Method,
|
||||
const Poco::JSON::Object &Params,
|
||||
const std::string &UUID, bool &Sent) {
|
||||
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, true, true,
|
||||
Sent, false);
|
||||
}
|
||||
|
||||
std::shared_ptr<promise_type_t> PostCommandDisk(
|
||||
uint64_t RPC_ID,
|
||||
APCommands::Commands Command,
|
||||
const std::string &SerialNumber,
|
||||
const std::string &Method,
|
||||
const Poco::JSON::Object &Params,
|
||||
const std::string &UUID,
|
||||
bool & Sent) {
|
||||
return PostCommand(RPC_ID,
|
||||
Command,
|
||||
SerialNumber,
|
||||
Method,
|
||||
Params,
|
||||
UUID,
|
||||
false, true, Sent );
|
||||
}
|
||||
std::shared_ptr<promise_type_t>
|
||||
PostCommandDisk(uint64_t RPC_ID, APCommands::Commands Command,
|
||||
const std::string &SerialNumber, const std::string &Method,
|
||||
const Poco::JSON::Object &Params, const std::string &UUID, bool &Sent) {
|
||||
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, false, true,
|
||||
Sent, false);
|
||||
}
|
||||
|
||||
std::shared_ptr<promise_type_t> PostCommand(
|
||||
uint64_t RPC_ID,
|
||||
APCommands::Commands Command,
|
||||
const std::string &SerialNumber,
|
||||
const std::string &Method,
|
||||
const Poco::JSON::Object &Params,
|
||||
const std::string &UUID,
|
||||
bool & Sent) {
|
||||
return PostCommand(RPC_ID,
|
||||
Command,
|
||||
SerialNumber,
|
||||
Method,
|
||||
Params,
|
||||
UUID,
|
||||
false,
|
||||
false, Sent );
|
||||
}
|
||||
std::shared_ptr<promise_type_t>
|
||||
PostCommand(uint64_t RPC_ID, APCommands::Commands Command, const std::string &SerialNumber,
|
||||
const std::string &Method, const Poco::JSON::Object &Params,
|
||||
const std::string &UUID, bool &Sent, bool rpc, bool Deferred) {
|
||||
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, false, false,
|
||||
Sent, rpc, Deferred);
|
||||
}
|
||||
|
||||
std::shared_ptr<promise_type_t> PostCommandOneWay(
|
||||
uint64_t RPC_ID,
|
||||
APCommands::Commands Command,
|
||||
const std::string &SerialNumber,
|
||||
const std::string &Method,
|
||||
const Poco::JSON::Object &Params,
|
||||
const std::string &UUID,
|
||||
bool & Sent) {
|
||||
return PostCommand(RPC_ID,
|
||||
Command,
|
||||
SerialNumber,
|
||||
Method,
|
||||
Params,
|
||||
UUID,
|
||||
true,
|
||||
false, Sent );
|
||||
}
|
||||
std::shared_ptr<promise_type_t>
|
||||
PostCommandOneWay(uint64_t RPC_ID, APCommands::Commands Command,
|
||||
const std::string &SerialNumber, const std::string &Method,
|
||||
const Poco::JSON::Object &Params, const std::string &UUID, bool &Sent) {
|
||||
return PostCommand(RPC_ID, Command, SerialNumber, Method, Params, UUID, true, false,
|
||||
Sent, false);
|
||||
}
|
||||
|
||||
bool IsCommandRunning(const std::string &C);
|
||||
bool IsCommandRunning(const std::string &C);
|
||||
|
||||
void run() override;
|
||||
void run() override;
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new CommandManager;
|
||||
return instance_;
|
||||
}
|
||||
static auto instance() {
|
||||
static auto instance_ = new CommandManager;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
inline bool Running() const { return Running_; }
|
||||
void onJanitorTimer(Poco::Timer & timer);
|
||||
void onCommandRunnerTimer(Poco::Timer & timer);
|
||||
inline uint64_t Next_RPC_ID() { return ++Id_; }
|
||||
inline bool Running() const { return Running_; }
|
||||
void onJanitorTimer(Poco::Timer &timer);
|
||||
void onCommandRunnerTimer(Poco::Timer &timer);
|
||||
inline uint64_t Next_RPC_ID() { return ++Id_; }
|
||||
|
||||
void RemovePendingCommand(std::uint64_t Id) {
|
||||
std::unique_lock Lock(LocalMutex_);
|
||||
OutStandingRequests_.erase(Id);
|
||||
}
|
||||
void RemovePendingCommand(std::uint64_t Id) {
|
||||
std::unique_lock Lock(LocalMutex_);
|
||||
OutStandingRequests_.erase(Id);
|
||||
}
|
||||
|
||||
inline bool CommandRunningForDevice(std::uint64_t SerialNumber, std::string & uuid, APCommands::Commands &command) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
inline bool CommandRunningForDevice(std::uint64_t SerialNumber, std::string &uuid,
|
||||
APCommands::Commands &command) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
|
||||
for(const auto &[Request,Command]:OutStandingRequests_) {
|
||||
if(Command.SerialNumber==SerialNumber) {
|
||||
uuid = Command.UUID;
|
||||
command = Command.Command;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void ClearQueue(std::uint64_t SerialNumber) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
for(auto Request = OutStandingRequests_.begin(); Request != OutStandingRequests_.end() ; ) {
|
||||
if(Request->second.SerialNumber==SerialNumber)
|
||||
Request = OutStandingRequests_.erase(Request);
|
||||
else
|
||||
++Request;
|
||||
for (const auto &[Request, Command] : OutStandingRequests_) {
|
||||
if (Command.SerialNumber == SerialNumber) {
|
||||
uuid = Command.UUID;
|
||||
command = Command.Command;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::recursive_mutex LocalMutex_;
|
||||
std::atomic_bool Running_ = false;
|
||||
Poco::Thread ManagerThread;
|
||||
std::atomic_uint64_t Id_=3; // do not start @1. We ignore ID=1 & 0 is illegal..
|
||||
std::map<std::uint64_t , CommandInfo> OutStandingRequests_;
|
||||
Poco::Timer JanitorTimer_;
|
||||
std::unique_ptr<Poco::TimerCallback<CommandManager>> JanitorCallback_;
|
||||
Poco::Timer CommandRunnerTimer_;
|
||||
std::unique_ptr<Poco::TimerCallback<CommandManager>> CommandRunnerCallback_;
|
||||
Poco::NotificationQueue ResponseQueue_;
|
||||
|
||||
std::shared_ptr<promise_type_t> PostCommand(
|
||||
uint64_t RPCID,
|
||||
APCommands::Commands Command,
|
||||
const std::string &SerialNumber,
|
||||
const std::string &Method,
|
||||
const Poco::JSON::Object &Params,
|
||||
const std::string &UUID,
|
||||
bool oneway_rpc,
|
||||
bool disk_only,
|
||||
bool & Sent);
|
||||
|
||||
CommandManager() noexcept:
|
||||
SubSystemServer("CommandManager", "CMD-MGR", "command.manager") {
|
||||
inline void ClearQueue(std::uint64_t SerialNumber) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
for (auto Request = OutStandingRequests_.begin();
|
||||
Request != OutStandingRequests_.end();) {
|
||||
if (Request->second.SerialNumber == SerialNumber)
|
||||
Request = OutStandingRequests_.erase(Request);
|
||||
else
|
||||
++Request;
|
||||
}
|
||||
}
|
||||
|
||||
inline void RemoveCommand(const std::string &UUID) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
for (const auto &[Id, Cmd] : OutStandingRequests_) {
|
||||
if (Cmd.UUID == UUID) {
|
||||
OutStandingRequests_.erase(Id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline auto CommandTimeout() const { return commandTimeOut_; }
|
||||
inline auto CommandRetry() const { return commandRetry_; }
|
||||
|
||||
bool FireAndForget(const std::string &SerialNumber, const std::string &Method,
|
||||
const Poco::JSON::Object &Params);
|
||||
private:
|
||||
mutable std::mutex LocalMutex_;
|
||||
std::atomic_bool Running_ = false;
|
||||
Poco::Thread ManagerThread;
|
||||
std::atomic_uint64_t Id_ = 3; // do not start @1. We ignore ID=1 & 0 is illegal..
|
||||
std::map<std::uint64_t, CommandInfo> OutStandingRequests_;
|
||||
Poco::Timer JanitorTimer_;
|
||||
std::unique_ptr<Poco::TimerCallback<CommandManager>> JanitorCallback_;
|
||||
Poco::Timer CommandRunnerTimer_;
|
||||
std::unique_ptr<Poco::TimerCallback<CommandManager>> CommandRunnerCallback_;
|
||||
Poco::NotificationQueue ResponseQueue_;
|
||||
std::uint64_t commandTimeOut_ = 0;
|
||||
std::uint64_t commandRetry_ = 0;
|
||||
std::uint64_t janitorInterval_ = 0;
|
||||
std::uint64_t queueInterval_ = 0;
|
||||
|
||||
std::shared_ptr<promise_type_t>
|
||||
PostCommand(uint64_t RPCID, APCommands::Commands Command, const std::string &SerialNumber,
|
||||
const std::string &Method, const Poco::JSON::Object &Params,
|
||||
const std::string &UUID, bool oneway_rpc, bool disk_only, bool &Sent,
|
||||
bool rpc_call, bool Deferred = false);
|
||||
|
||||
bool CompleteScriptCommand(CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time);
|
||||
bool CompleteTelemetryCommand(CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time);
|
||||
bool CompleteConfigureCommand(CommandInfo &Command, const Poco::JSON::Object::Ptr &Payload,
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time);
|
||||
|
||||
CommandManager() noexcept
|
||||
: SubSystemServer("CommandManager", "CMD-MGR", "command.manager") {}
|
||||
};
|
||||
|
||||
inline auto CommandManager() { return CommandManager::instance(); }
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -5,45 +5,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace OpenWifi {
|
||||
class ConfigurationCache {
|
||||
public:
|
||||
|
||||
static ConfigurationCache & instance() {
|
||||
static ConfigurationCache instance;
|
||||
static auto instance() {
|
||||
static auto instance = new ConfigurationCache;
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline uint64_t CurrentConfig(uint64_t SerialNumber) {
|
||||
inline uint64_t GetCurrentConfig(std::uint64_t SerialNumber) {
|
||||
std::lock_guard G(Mutex_);
|
||||
const auto Hint = Cache_.find(SerialNumber);
|
||||
if(Hint==end(Cache_))
|
||||
if (Hint == end(Cache_))
|
||||
return 0;
|
||||
return Hint->second;
|
||||
}
|
||||
|
||||
inline void Add(uint64_t SerialNumber, uint64_t Id) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Cache_[SerialNumber]=Id;
|
||||
inline void SetCurrentConfig(std::uint64_t SerialNumber, uint64_t Id) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Cache_[SerialNumber] = Id;
|
||||
}
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
std::map<uint64_t,uint64_t> Cache_;
|
||||
std::mutex Mutex_;
|
||||
std::map<uint64_t, uint64_t> Cache_;
|
||||
};
|
||||
|
||||
inline uint64_t GetCurrentConfigurationID(uint64_t SerialNumber) {
|
||||
return ConfigurationCache::instance().CurrentConfig(SerialNumber);
|
||||
inline auto GetCurrentConfigurationID(std::uint64_t SerialNumber) {
|
||||
return ConfigurationCache::instance()->GetCurrentConfig(SerialNumber);
|
||||
}
|
||||
|
||||
inline void SetCurrentConfigurationID(const std::string & SerialNumber, uint64_t ID) {
|
||||
return ConfigurationCache::instance().Add(Utils::SerialNumberToInt(SerialNumber), ID);
|
||||
inline void SetCurrentConfigurationID(const std::string &SerialNumber, std::uint64_t ID) {
|
||||
return ConfigurationCache::instance()->SetCurrentConfig(Utils::SerialNumberToInt(SerialNumber), ID);
|
||||
}
|
||||
|
||||
inline void SetCurrentConfigurationID(uint64_t SerialNumber, uint64_t ID) {
|
||||
return ConfigurationCache::instance().Add(SerialNumber, ID);
|
||||
inline void SetCurrentConfigurationID(uint64_t SerialNumber, std::uint64_t ID) {
|
||||
return ConfigurationCache::instance()->SetCurrentConfig(SerialNumber, ID);
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
131
src/Daemon.cpp
131
src/Daemon.cpp
@@ -6,11 +6,14 @@
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/Util/Option.h"
|
||||
#include "Poco/Environment.h"
|
||||
#include "Poco/Net/SSLManager.h"
|
||||
#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"
|
||||
@@ -18,108 +21,78 @@
|
||||
#include "FileUploader.h"
|
||||
#include "FindCountry.h"
|
||||
#include "OUIServer.h"
|
||||
#include "RADIUSSessionTracker.h"
|
||||
#include "RADIUS_proxy_server.h"
|
||||
#include "RegulatoryInfo.h"
|
||||
#include "ScriptManager.h"
|
||||
#include "SerialNumberCache.h"
|
||||
#include "SignatureMgr.h"
|
||||
#include "StorageArchiver.h"
|
||||
#include "StorageService.h"
|
||||
#include "TelemetryStream.h"
|
||||
#include "VenueBroadcaster.h"
|
||||
#include "framework/ConfigurationValidator.h"
|
||||
#include "rttys/RTTYS_server.h"
|
||||
#include "framework/UI_WebSocketClientServer.h"
|
||||
#include "GenericScheduler.h"
|
||||
#include "UI_GW_WebSocketNotifications.h"
|
||||
#include "ScriptManager.h"
|
||||
#include "SignatureMgr.h"
|
||||
#include "VenueBroadcaster.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()
|
||||
});
|
||||
return &instance;
|
||||
static Daemon instance(
|
||||
vDAEMON_PROPERTIES_FILENAME, vDAEMON_ROOT_ENV_VAR, vDAEMON_CONFIG_ENV_VAR,
|
||||
vDAEMON_APP_NAME, vDAEMON_BUS_TIMER,
|
||||
SubSystemVec{GenericScheduler(), StorageService(), SerialNumberCache(), ConfigurationValidator(),
|
||||
UI_WebSocketClientServer(), OUIServer(), FindCountryFromIP(),
|
||||
CommandManager(), FileUploader(), StorageArchiver(), TelemetryStream(),
|
||||
RTTYS_server(), RADIUS_proxy_server(), VenueBroadcaster(), ScriptManager(),
|
||||
SignatureManager(), AP_WS_Server(),
|
||||
RegulatoryInfo(),
|
||||
RADIUSSessionTracker(),
|
||||
AP_WS_ConfigAutoUpgradeAgent(),
|
||||
FirmwareRevisionCache()
|
||||
});
|
||||
return &instance;
|
||||
}
|
||||
|
||||
static std::string ALBHealthCallback() {
|
||||
uint64_t Connections, AverageConnectionTime, NumberOfConnectingDevices;
|
||||
AP_WS_Server()->AverageDeviceStatistics(Connections, AverageConnectionTime,
|
||||
NumberOfConnectingDevices);
|
||||
std::ostringstream os;
|
||||
os << "Connections: " << Connections << std::endl <<
|
||||
"ConnectingDevices: " << NumberOfConnectingDevices << std::endl <<
|
||||
"ConnectionTime: " << AverageConnectionTime << std::endl;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static const std::vector<std::pair<std::string,std::string>> DefaultDeviceTypes{
|
||||
{"cig_wf160d","AP"},
|
||||
{"cig_wf188","AP"},
|
||||
{"cig_wf188n","AP"},
|
||||
{"cig_wf194c","AP"},
|
||||
{"cig_wf194c4","AP"},
|
||||
{"edgecore_eap101","AP"},
|
||||
{"edgecore_eap102","AP"},
|
||||
{"edgecore_ecs4100-12ph","AP"},
|
||||
{"edgecore_ecw5211","AP"},
|
||||
{"edgecore_ecw5410","AP"},
|
||||
{"edgecore_oap100","AP"},
|
||||
{"edgecore_spw2ac1200","SWITCH"},
|
||||
{"edgecore_spw2ac1200-lan-poe","SWITCH"},
|
||||
{"edgecore_ssw2ac2600","SWITCH"},
|
||||
{"hfcl_ion4","AP"},
|
||||
{"indio_um-305ac","AP"},
|
||||
{"linksys_e8450-ubi","AP"},
|
||||
{"linksys_ea6350","AP"},
|
||||
{"linksys_ea6350-v4","AP"},
|
||||
{"linksys_ea8300","AP"},
|
||||
{"mikrotik_nand","AP"},
|
||||
{"tp-link_ec420-g1","AP"},
|
||||
{"tplink_cpe210_v3","AP"},
|
||||
{"tplink_cpe510_v3","AP"},
|
||||
{"tplink_eap225_outdoor_v1","AP"},
|
||||
{"tplink_ec420","AP"},
|
||||
{"tplink_ex227","AP"},
|
||||
{"tplink_ex228","AP"},
|
||||
{"tplink_ex447","AP"},
|
||||
{"wallys_dr40x9","AP"}
|
||||
};
|
||||
|
||||
void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) {
|
||||
AutoProvisioning_ = config().getBool("openwifi.autoprovisioning",false);
|
||||
DeviceTypes_ = DefaultDeviceTypes;
|
||||
|
||||
AutoProvisioning_ = config().getBool("openwifi.autoprovisioning", false);
|
||||
DeviceTypes_ = DefaultDeviceTypeList;
|
||||
WebSocketProcessor_ = std::make_unique<GwWebSocketClient>(logger());
|
||||
}
|
||||
MicroServiceALBCallback(ALBHealthCallback);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string Daemon::IdentifyDevice(const std::string & Id ) const {
|
||||
for(const auto &[DeviceType,Type]:DeviceTypes_)
|
||||
{
|
||||
if(Id == DeviceType)
|
||||
return Type;
|
||||
}
|
||||
return "AP";
|
||||
}
|
||||
[[nodiscard]] std::string Daemon::IdentifyDevice(const std::string &Id) const {
|
||||
for (const auto &[DeviceType, Type] : DeviceTypes_) {
|
||||
if (Id == DeviceType)
|
||||
return Type;
|
||||
}
|
||||
return Platforms::AP;
|
||||
}
|
||||
|
||||
void DaemonPostInitialization(Poco::Util::Application &self) {
|
||||
Daemon()->PostInitialization(self);
|
||||
GWWebSocketNotifications::Register();
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int ExitCode;
|
||||
try {
|
||||
Poco::Net::SSLManager::instance().initializeServer(nullptr, nullptr, nullptr);
|
||||
auto App = OpenWifi::Daemon::instance();
|
||||
ExitCode = App->run(argc, argv);
|
||||
ExitCode = App->run(argc, argv);
|
||||
Poco::Net::SSLManager::instance().shutdown();
|
||||
} catch (Poco::Exception &exc) {
|
||||
ExitCode = Poco::Util::Application::EXIT_SOFTWARE;
|
||||
|
||||
61
src/Daemon.h
61
src/Daemon.h
@@ -9,50 +9,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "framework/MicroServiceNames.h"
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/MicroServiceNames.h"
|
||||
|
||||
#include "Dashboard.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "GwWebSocketClient.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
[[maybe_unused]] static const char * vDAEMON_PROPERTIES_FILENAME = "owgw.properties";
|
||||
[[maybe_unused]] static const char * vDAEMON_ROOT_ENV_VAR = "OWGW_ROOT";
|
||||
[[maybe_unused]] static const char * vDAEMON_CONFIG_ENV_VAR = "OWGW_CONFIG";
|
||||
[[maybe_unused]] static const char * vDAEMON_APP_NAME = uSERVICE_GATEWAY.c_str();
|
||||
[[maybe_unused]] static const char *vDAEMON_PROPERTIES_FILENAME = "owgw.properties";
|
||||
[[maybe_unused]] static const char *vDAEMON_ROOT_ENV_VAR = "OWGW_ROOT";
|
||||
[[maybe_unused]] static const char *vDAEMON_CONFIG_ENV_VAR = "OWGW_CONFIG";
|
||||
[[maybe_unused]] static const char *vDAEMON_APP_NAME = uSERVICE_GATEWAY.c_str();
|
||||
[[maybe_unused]] static const uint64_t vDAEMON_BUS_TIMER = 10000;
|
||||
|
||||
class Daemon : public MicroService {
|
||||
public:
|
||||
explicit Daemon(const std::string & PropFile,
|
||||
const std::string & RootEnv,
|
||||
const std::string & ConfigEnv,
|
||||
const std::string & AppName,
|
||||
uint64_t BusTimer,
|
||||
const SubSystemVec & SubSystems) :
|
||||
MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {};
|
||||
class Daemon : public MicroService {
|
||||
public:
|
||||
explicit Daemon(const std::string &PropFile, const std::string &RootEnv,
|
||||
const std::string &ConfigEnv, const std::string &AppName, uint64_t BusTimer,
|
||||
const SubSystemVec &SubSystems)
|
||||
: MicroService(PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems){};
|
||||
|
||||
bool AutoProvisioning() const { return AutoProvisioning_ ; }
|
||||
[[nodiscard]] std::string IdentifyDevice(const std::string & Compatible) const;
|
||||
static Daemon *instance();
|
||||
inline DeviceDashboard & GetDashboard() { return DB_; }
|
||||
Poco::Logger & Log() { return Poco::Logger::get(AppName()); }
|
||||
void PostInitialization(Poco::Util::Application &self);
|
||||
private:
|
||||
bool AutoProvisioning_ = false;
|
||||
std::vector<std::pair<std::string,std::string>> DeviceTypes_;
|
||||
DeviceDashboard DB_;
|
||||
std::unique_ptr<GwWebSocketClient> WebSocketProcessor_;
|
||||
};
|
||||
bool AutoProvisioning() const { return AutoProvisioning_; }
|
||||
[[nodiscard]] std::string IdentifyDevice(const std::string &Compatible) const;
|
||||
static Daemon *instance();
|
||||
inline DeviceDashboard &GetDashboard() { return DB_; }
|
||||
Poco::Logger &Log() { return Poco::Logger::get(AppName()); }
|
||||
void PostInitialization(Poco::Util::Application &self);
|
||||
|
||||
inline Daemon * Daemon() { return Daemon::instance(); }
|
||||
private:
|
||||
bool AutoProvisioning_ = false;
|
||||
std::vector<std::pair<std::string, std::string>> DeviceTypes_;
|
||||
DeviceDashboard DB_;
|
||||
std::unique_ptr<GwWebSocketClient> WebSocketProcessor_;
|
||||
};
|
||||
|
||||
inline Daemon *Daemon() { return Daemon::instance(); }
|
||||
void DaemonPostInitialization(Poco::Util::Application &self);
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -8,44 +8,41 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
bool DeviceDashboard::Get(GWObjects::Dashboard &D, Poco::Logger & Logger) {
|
||||
bool DeviceDashboard::Get(GWObjects::Dashboard &D, Poco::Logger &Logger) {
|
||||
uint64_t Now = Utils::Now();
|
||||
if(!ValidDashboard_ || LastRun_==0 || (Now-LastRun_)>120) {
|
||||
if (!ValidDashboard_ || LastRun_ == 0 || (Now - LastRun_) > 120) {
|
||||
Generate(D, Logger);
|
||||
} else {
|
||||
std::lock_guard G(DataMutex_);
|
||||
std::lock_guard G(DataMutex_);
|
||||
D = DB_;
|
||||
}
|
||||
return ValidDashboard_;
|
||||
};
|
||||
|
||||
void DeviceDashboard::Generate(GWObjects::Dashboard &D, Poco::Logger & Logger ) {
|
||||
void DeviceDashboard::Generate(GWObjects::Dashboard &D, Poco::Logger &Logger) {
|
||||
if (GeneratingDashboard_.load()) {
|
||||
// std::cout << "Trying to generate dashboard but already being generated" << std::endl;
|
||||
while(GeneratingDashboard_.load()) {
|
||||
while (GeneratingDashboard_.load()) {
|
||||
Poco::Thread::trySleep(100);
|
||||
}
|
||||
std::lock_guard G(DataMutex_);
|
||||
std::lock_guard G(DataMutex_);
|
||||
D = DB_;
|
||||
} else {
|
||||
GeneratingDashboard_ = true;
|
||||
ValidDashboard_ = false;
|
||||
try {
|
||||
// std::cout << "Generating dashboard." << std::endl;
|
||||
poco_information(Logger, "DASHBOARD: Generating a new dashboard.");
|
||||
GWObjects::Dashboard NewData;
|
||||
GWObjects::Dashboard NewData;
|
||||
StorageService()->AnalyzeCommands(NewData.commands);
|
||||
StorageService()->AnalyzeDevices(NewData);
|
||||
LastRun_ = Utils::Now();
|
||||
NewData.snapshot = LastRun_;
|
||||
D = NewData;
|
||||
std::lock_guard G(DataMutex_);
|
||||
std::lock_guard G(DataMutex_);
|
||||
DB_ = NewData;
|
||||
ValidDashboard_=true;
|
||||
} catch(...) {
|
||||
|
||||
ValidDashboard_ = true;
|
||||
} catch (...) {
|
||||
}
|
||||
GeneratingDashboard_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -6,22 +6,22 @@
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "Poco/Logger.h"
|
||||
#include "RESTObjects//RESTAPI_GWobjects.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "Poco/Logger.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class DeviceDashboard {
|
||||
public:
|
||||
bool Get(GWObjects::Dashboard &D, Poco::Logger & Logger);
|
||||
bool Get(GWObjects::Dashboard &D, Poco::Logger &Logger);
|
||||
|
||||
private:
|
||||
std::mutex DataMutex_;
|
||||
volatile std::atomic_bool GeneratingDashboard_=false;
|
||||
volatile bool ValidDashboard_=false;
|
||||
GWObjects::Dashboard DB_;
|
||||
uint64_t LastRun_=0;
|
||||
std::mutex DataMutex_;
|
||||
volatile std::atomic_bool GeneratingDashboard_ = false;
|
||||
volatile bool ValidDashboard_ = false;
|
||||
GWObjects::Dashboard DB_;
|
||||
uint64_t LastRun_ = 0;
|
||||
|
||||
void Generate(GWObjects::Dashboard &D, Poco::Logger & Logger);
|
||||
void Generate(GWObjects::Dashboard &D, Poco::Logger &Logger);
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Poco/Net/HTTPServerParams.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
#include "Poco/DynamicAny.h"
|
||||
#include "Poco/Net/PartHandler.h"
|
||||
#include "Poco/Net/MessageHeader.h"
|
||||
#include "Poco/Net/MultipartReader.h"
|
||||
#include "Poco/CountingStream.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/DynamicAny.h"
|
||||
#include "Poco/Exception.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/Net/HTTPServerParams.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
#include "Poco/Net/MessageHeader.h"
|
||||
#include "Poco/Net/MultipartReader.h"
|
||||
#include "Poco/Net/PartHandler.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/StringTokenizer.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
@@ -30,25 +30,25 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
static const std::string URI_BASE{"/v1/upload/"};
|
||||
static const std::string URI_BASE{"/v1/upload/"};
|
||||
|
||||
int FileUploader::Start() {
|
||||
poco_notice(Logger(),"Starting.");
|
||||
int FileUploader::Start() {
|
||||
poco_notice(Logger(), "Starting.");
|
||||
|
||||
Poco::File UploadsDir(MicroServiceConfigPath("openwifi.fileuploader.path","/tmp"));
|
||||
Path_ = UploadsDir.path();
|
||||
if(!UploadsDir.exists()) {
|
||||
try {
|
||||
UploadsDir.createDirectory();
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
Path_ = "/tmp";
|
||||
}
|
||||
}
|
||||
Poco::File UploadsDir(MicroServiceConfigPath("openwifi.fileuploader.path", "/tmp"));
|
||||
Path_ = UploadsDir.path();
|
||||
if (!UploadsDir.exists()) {
|
||||
try {
|
||||
UploadsDir.createDirectory();
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
Path_ = "/tmp";
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto & Svr: ConfigServersList_) {
|
||||
if(MicroServiceNoAPISecurity()) {
|
||||
poco_notice(Logger(), fmt::format("Starting: {}:{}",Svr.Address(),Svr.Port()));
|
||||
for (const auto &Svr : ConfigServersList_) {
|
||||
if (MicroServiceNoAPISecurity()) {
|
||||
poco_notice(Logger(), fmt::format("Starting: {}:{}", Svr.Address(), Svr.Port()));
|
||||
|
||||
auto Sock{Svr.CreateSocket(Logger())};
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace OpenWifi {
|
||||
} else {
|
||||
FullName_ = TmpName + URI_BASE;
|
||||
}
|
||||
poco_information(Logger(),fmt::format("Uploader URI base is '{}'", FullName_));
|
||||
poco_information(Logger(), fmt::format("Uploader URI base is '{}'", FullName_));
|
||||
}
|
||||
|
||||
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(
|
||||
@@ -77,7 +77,7 @@ namespace OpenWifi {
|
||||
} else {
|
||||
std::string l{"Starting: " + Svr.Address() + ":" + std::to_string(Svr.Port()) +
|
||||
" key:" + Svr.KeyFile() + " cert:" + Svr.CertFile()};
|
||||
poco_information(Logger(),l);
|
||||
poco_information(Logger(), l);
|
||||
|
||||
auto Sock{Svr.CreateSecureSocket(Logger())};
|
||||
|
||||
@@ -107,66 +107,62 @@ namespace OpenWifi {
|
||||
NewServer->start();
|
||||
Servers_.push_back(std::move(NewServer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaxSize_ = 1000 * MicroServiceConfigGetInt("openwifi.fileuploader.maxsize", 10000);
|
||||
MaxSize_ = 1000 * MicroServiceConfigGetInt("openwifi.fileuploader.maxsize", 10000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FileUploader::reinitialize([[maybe_unused]] Poco::Util::Application &self) {
|
||||
MicroServiceLoadConfigurationFile();
|
||||
poco_information(Logger(),"Reinitializing.");
|
||||
poco_information(Logger(), "Reinitializing.");
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
|
||||
const std::string & FileUploader::FullName() {
|
||||
return FullName_;
|
||||
}
|
||||
const std::string &FileUploader::FullName() { return FullName_; }
|
||||
|
||||
// if you pass in an empty UUID, it will just clean the list and not add it.
|
||||
bool FileUploader::AddUUID( const std::string & UUID, std::chrono::seconds WaitTimeInSeconds, const std::string &Type) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
// if you pass in an empty UUID, it will just clean the list and not add it.
|
||||
bool FileUploader::AddUUID(const std::string &UUID, std::chrono::seconds WaitTimeInSeconds,
|
||||
const std::string &Type) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
uint64_t now = Utils::Now();
|
||||
auto Func=[now](const UploadId &I) -> bool {
|
||||
return (now > I.Expires);
|
||||
};
|
||||
OutStandingUploads_.erase(std::remove_if(OutStandingUploads_.begin(),OutStandingUploads_.end(),Func),OutStandingUploads_.end());
|
||||
uint64_t now = Utils::Now();
|
||||
auto Func = [now](const UploadId &I) -> bool { return (now > I.Expires); };
|
||||
OutStandingUploads_.erase(
|
||||
std::remove_if(OutStandingUploads_.begin(), OutStandingUploads_.end(), Func),
|
||||
OutStandingUploads_.end());
|
||||
OutStandingUploads_.emplace_back(UploadId{UUID, now + WaitTimeInSeconds.count(), Type});
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileUploader::ValidRequest(const std::string &UUID) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
auto Func = [UUID](const UploadId &P) -> bool {
|
||||
return (P.UUID==UUID);
|
||||
};
|
||||
return std::find_if(OutStandingUploads_.begin(), OutStandingUploads_.end(), Func) != end(OutStandingUploads_);
|
||||
}
|
||||
bool FileUploader::ValidRequest(const std::string &UUID) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
auto Func = [UUID](const UploadId &P) -> bool { return (P.UUID == UUID); };
|
||||
return std::find_if(OutStandingUploads_.begin(), OutStandingUploads_.end(), Func) !=
|
||||
end(OutStandingUploads_);
|
||||
}
|
||||
|
||||
void FileUploader::RemoveRequest(const std::string &UUID) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
auto Func = [UUID](const UploadId &P) -> bool {
|
||||
return (P.UUID==UUID);
|
||||
};
|
||||
OutStandingUploads_.erase(std::remove_if(OutStandingUploads_.begin(),OutStandingUploads_.end(),Func),OutStandingUploads_.end());
|
||||
}
|
||||
void FileUploader::RemoveRequest(const std::string &UUID) {
|
||||
std::lock_guard Guard(Mutex_);
|
||||
auto Func = [UUID](const UploadId &P) -> bool { return (P.UUID == UUID); };
|
||||
OutStandingUploads_.erase(
|
||||
std::remove_if(OutStandingUploads_.begin(), OutStandingUploads_.end(), Func),
|
||||
OutStandingUploads_.end());
|
||||
}
|
||||
|
||||
class FileUploaderPartHandler2 : public Poco::Net::PartHandler {
|
||||
public:
|
||||
FileUploaderPartHandler2(std::string Id, Poco::Logger &Logger, std::stringstream & ofs) :
|
||||
Id_(std::move(Id)),
|
||||
Logger_(Logger),
|
||||
OutputStream_(ofs){
|
||||
}
|
||||
FileUploaderPartHandler2(std::string Id, Poco::Logger &Logger, std::stringstream &ofs)
|
||||
: Id_(std::move(Id)), Logger_(Logger), OutputStream_(ofs) {}
|
||||
void handlePart(const Poco::Net::MessageHeader &Header, std::istream &Stream) {
|
||||
FileType_ = Header.get(RESTAPI::Protocol::CONTENTTYPE, RESTAPI::Protocol::UNSPECIFIED);
|
||||
if (Header.has(RESTAPI::Protocol::CONTENTDISPOSITION)) {
|
||||
std::string Disposition;
|
||||
Poco::Net::NameValueCollection Parameters;
|
||||
Poco::Net::MessageHeader::splitParameters(Header[RESTAPI::Protocol::CONTENTDISPOSITION], Disposition, Parameters);
|
||||
Poco::Net::MessageHeader::splitParameters(
|
||||
Header[RESTAPI::Protocol::CONTENTDISPOSITION], Disposition, Parameters);
|
||||
Name_ = Parameters.get(RESTAPI::Protocol::NAME, RESTAPI::Protocol::UNNAMED);
|
||||
}
|
||||
Poco::CountingInputStream InputStream(Stream);
|
||||
@@ -178,33 +174,30 @@ namespace OpenWifi {
|
||||
[[nodiscard]] std::string &ContentType() { return FileType_; }
|
||||
|
||||
private:
|
||||
uint64_t Length_ = 0;
|
||||
std::string FileType_;
|
||||
std::string Name_;
|
||||
std::string Id_;
|
||||
Poco::Logger &Logger_;
|
||||
uint64_t Length_ = 0;
|
||||
std::string FileType_;
|
||||
std::string Name_;
|
||||
std::string Id_;
|
||||
Poco::Logger &Logger_;
|
||||
std::stringstream &OutputStream_;
|
||||
|
||||
inline Poco::Logger & Logger() { return Logger_; };
|
||||
inline Poco::Logger &Logger() { return Logger_; };
|
||||
};
|
||||
|
||||
class FormRequestHandler: public Poco::Net::HTTPRequestHandler
|
||||
{
|
||||
public:
|
||||
explicit FormRequestHandler(std::string UUID, Poco::Logger & L, const std::string &Type):
|
||||
UUID_(std::move(UUID)),
|
||||
Logger_(L),
|
||||
Type_(Type)
|
||||
{
|
||||
}
|
||||
class FormRequestHandler : public Poco::Net::HTTPRequestHandler {
|
||||
public:
|
||||
explicit FormRequestHandler(std::string UUID, Poco::Logger &L, const std::string &Type)
|
||||
: UUID_(std::move(UUID)), Logger_(L), Type_(Type) {}
|
||||
|
||||
void handleRequest(Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) final {
|
||||
void handleRequest(Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response) final {
|
||||
|
||||
Utils::SetThreadName("FileUploader");
|
||||
const auto ContentType = Request.getContentType();
|
||||
const auto Tokens = Poco::StringTokenizer(ContentType,";",Poco::StringTokenizer::TOK_TRIM);
|
||||
const auto Tokens =
|
||||
Poco::StringTokenizer(ContentType, ";", Poco::StringTokenizer::TOK_TRIM);
|
||||
|
||||
poco_debug(Logger(),fmt::format("{}: Preparing to upload trace file.",UUID_));
|
||||
poco_debug(Logger(), fmt::format("{}: Preparing to upload a file.", UUID_));
|
||||
Poco::JSON::Object Answer;
|
||||
|
||||
try {
|
||||
@@ -229,8 +222,9 @@ namespace OpenWifi {
|
||||
Poco::StreamCopier::copyStream(Reader.stream(), FileContent);
|
||||
Answer.set("filename", UUID_);
|
||||
Answer.set("error", 0);
|
||||
poco_debug(Logger(),fmt::format("{}: File uploaded.", UUID_));
|
||||
StorageService()->AttachFileDataToCommand(UUID_, FileContent, Type_);
|
||||
poco_debug(Logger(), fmt::format("{}: File uploaded.", UUID_));
|
||||
StorageService()->AttachFileDataToCommand(UUID_, FileContent,
|
||||
Type_);
|
||||
std::ostream &ResponseStream = Response.send();
|
||||
Poco::JSON::Stringifier::stringify(Answer, ResponseStream);
|
||||
return;
|
||||
@@ -247,11 +241,11 @@ namespace OpenWifi {
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_debug(Logger(),"Exception while receiving trace file.");
|
||||
poco_debug(Logger(), "Exception while receiving uploaded file.");
|
||||
}
|
||||
|
||||
poco_debug(Logger(),fmt::format("{}: Failed to upload trace file.",UUID_));
|
||||
std::string Error{"Trace file rejected"};
|
||||
poco_debug(Logger(), fmt::format("{}: Failed to upload a file.", UUID_));
|
||||
std::string Error{"File rejected"};
|
||||
StorageService()->CancelWaitFile(UUID_, Error);
|
||||
Answer.set("filename", UUID_);
|
||||
Answer.set("error", 13);
|
||||
@@ -261,48 +255,50 @@ namespace OpenWifi {
|
||||
Poco::JSON::Stringifier::stringify(Answer, ResponseStream);
|
||||
}
|
||||
|
||||
inline Poco::Logger & Logger() { return Logger_; }
|
||||
inline Poco::Logger &Logger() { return Logger_; }
|
||||
|
||||
private:
|
||||
std::string UUID_;
|
||||
Poco::Logger & Logger_;
|
||||
std::string Type_;
|
||||
};
|
||||
private:
|
||||
std::string UUID_;
|
||||
Poco::Logger &Logger_;
|
||||
std::string Type_;
|
||||
};
|
||||
|
||||
Poco::Net::HTTPRequestHandler *FileUpLoaderRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & Request) {
|
||||
Poco::Net::HTTPRequestHandler *FileUpLoaderRequestHandlerFactory::createRequestHandler(
|
||||
const Poco::Net::HTTPServerRequest &Request) {
|
||||
|
||||
poco_debug(Logger(),fmt::format("REQUEST({}): {} {}", Utils::FormatIPv6(Request.clientAddress().toString()), Request.getMethod(), Request.getURI()));
|
||||
poco_debug(Logger(), fmt::format("REQUEST({}): {} {}",
|
||||
Utils::FormatIPv6(Request.clientAddress().toString()),
|
||||
Request.getMethod(), Request.getURI()));
|
||||
|
||||
if(Request.getMethod()!=Poco::Net::HTTPRequest::HTTP_POST ||
|
||||
Request.getURI().size()<(URI_BASE.size()+36)) {
|
||||
poco_warning(Logger(),fmt::format("ILLEGAL-REQUEST({}): {} {}. Dropped.", Utils::FormatIPv6(Request.clientAddress().toString()), Request.getMethod(), Request.getURI()));
|
||||
if (Request.getMethod() != Poco::Net::HTTPRequest::HTTP_POST ||
|
||||
Request.getURI().size() < (URI_BASE.size() + 36)) {
|
||||
poco_warning(Logger(),
|
||||
fmt::format("ILLEGAL-REQUEST({}): {} {}. Dropped.",
|
||||
Utils::FormatIPv6(Request.clientAddress().toString()),
|
||||
Request.getMethod(), Request.getURI()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The UUID should be after the /v1/upload/ part...
|
||||
auto UUIDLocation = Request.getURI().find_first_of(URI_BASE);
|
||||
// The UUID should be after the /v1/upload/ part...
|
||||
auto UUIDLocation = Request.getURI().find_first_of(URI_BASE);
|
||||
|
||||
if( UUIDLocation != std::string::npos )
|
||||
{
|
||||
auto UUID = Request.getURI().substr(UUIDLocation+URI_BASE.size());
|
||||
if (UUIDLocation != std::string::npos) {
|
||||
auto UUID = Request.getURI().substr(UUIDLocation + URI_BASE.size());
|
||||
|
||||
FileUploader::UploadId E;
|
||||
if(FileUploader()->Find(UUID,E))
|
||||
{
|
||||
FileUploader::UploadId E;
|
||||
if (FileUploader()->Find(UUID, E)) {
|
||||
FileUploader()->RemoveRequest(UUID);
|
||||
return new FormRequestHandler(UUID,Logger(),E.Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
poco_warning(Logger(),fmt::format("Unknown UUID={}",UUID));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return new FormRequestHandler(UUID, Logger(), E.Type);
|
||||
} else {
|
||||
poco_warning(Logger(), fmt::format("Unknown UUID={}", UUID));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FileUploader::Find(const std::string &UUID, UploadId &V) {
|
||||
std::lock_guard G(Mutex_);
|
||||
for(const auto &E:OutStandingUploads_) {
|
||||
bool FileUploader::Find(const std::string &UUID, UploadId &V) {
|
||||
std::lock_guard G(Mutex_);
|
||||
for (const auto &E : OutStandingUploads_) {
|
||||
if (E.UUID == UUID) {
|
||||
V = E;
|
||||
return true;
|
||||
@@ -311,12 +307,12 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileUploader::Stop() {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
for( const auto & svr : Servers_ )
|
||||
svr->stopAll(true);
|
||||
void FileUploader::Stop() {
|
||||
poco_notice(Logger(), "Stopping...");
|
||||
for (const auto &svr : Servers_)
|
||||
svr->stopAll(true);
|
||||
Servers_.clear();
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
}
|
||||
poco_notice(Logger(), "Stopped...");
|
||||
}
|
||||
|
||||
} // Namespace
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -8,68 +8,64 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Poco/Net/HTTPRequestHandler.h"
|
||||
#include "Poco/Net/HTTPRequestHandlerFactory.h"
|
||||
#include "Poco/Net/HTTPServer.h"
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Poco/Net/HTTPRequestHandlerFactory.h>
|
||||
#include <Poco/Net/HTTPServer.h>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include <framework/SubSystemServer.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class FileUploader : public SubSystemServer {
|
||||
public:
|
||||
|
||||
class FileUploader : public SubSystemServer {
|
||||
public:
|
||||
struct UploadId {
|
||||
std::string UUID;
|
||||
std::uint64_t Expires;
|
||||
std::string Type;
|
||||
std::string UUID;
|
||||
std::uint64_t Expires;
|
||||
std::string Type;
|
||||
};
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
void reinitialize(Poco::Util::Application &self) override;
|
||||
const std::string & FullName();
|
||||
bool AddUUID( const std::string & UUID, std::chrono::seconds WaitTimeInSecond, const std::string &Type);
|
||||
bool ValidRequest(const std::string & UUID);
|
||||
const std::string &FullName();
|
||||
bool AddUUID(const std::string &UUID, std::chrono::seconds WaitTimeInSecond,
|
||||
const std::string &Type);
|
||||
bool ValidRequest(const std::string &UUID);
|
||||
void RemoveRequest(const std::string &UUID);
|
||||
const std::string & Path() { return Path_; };
|
||||
const std::string &Path() { return Path_; };
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new FileUploader;
|
||||
static auto instance() {
|
||||
static auto instance_ = new FileUploader;
|
||||
return instance_;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uint64_t MaxSize() const { return MaxSize_; }
|
||||
|
||||
bool Find(const std::string &UUID, UploadId &V);
|
||||
private:
|
||||
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> Servers_;
|
||||
std::string FullName_;
|
||||
std::list<UploadId> OutStandingUploads_;
|
||||
std::string Path_;
|
||||
uint64_t MaxSize_=10000000;
|
||||
|
||||
explicit FileUploader() noexcept:
|
||||
SubSystemServer("FileUploader", "FILE-UPLOAD", "openwifi.fileuploader")
|
||||
{
|
||||
}
|
||||
};
|
||||
private:
|
||||
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> Servers_;
|
||||
std::string FullName_;
|
||||
std::list<UploadId> OutStandingUploads_;
|
||||
std::string Path_;
|
||||
uint64_t MaxSize_ = 10000000;
|
||||
|
||||
class FileUpLoaderRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
|
||||
public:
|
||||
explicit FileUpLoaderRequestHandlerFactory(Poco::Logger &L) :
|
||||
Logger_(L) {
|
||||
}
|
||||
explicit FileUploader() noexcept
|
||||
: SubSystemServer("FileUploader", "FILE-UPLOAD", "openwifi.fileuploader") {}
|
||||
};
|
||||
|
||||
Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &request) override;
|
||||
inline Poco::Logger & Logger() {
|
||||
return Logger_;
|
||||
}
|
||||
private:
|
||||
Poco::Logger & Logger_;
|
||||
};
|
||||
class FileUpLoaderRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
|
||||
public:
|
||||
explicit FileUpLoaderRequestHandlerFactory(Poco::Logger &L) : Logger_(L) {}
|
||||
|
||||
Poco::Net::HTTPRequestHandler *
|
||||
createRequestHandler(const Poco::Net::HTTPServerRequest &request) override;
|
||||
inline Poco::Logger &Logger() { return Logger_; }
|
||||
|
||||
private:
|
||||
Poco::Logger &Logger_;
|
||||
};
|
||||
|
||||
inline auto FileUploader() { return FileUploader::instance(); }
|
||||
} // namespace
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
#include "Poco/Net/IPAddress.h"
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
@@ -15,11 +15,10 @@ namespace OpenWifi {
|
||||
|
||||
class IPToCountryProvider {
|
||||
public:
|
||||
virtual bool Init() = 0 ;
|
||||
virtual Poco::URI URI(const std::string & IPAddress) = 0;
|
||||
virtual std::string Country( const std::string & Response ) = 0 ;
|
||||
virtual ~IPToCountryProvider() {
|
||||
};
|
||||
virtual bool Init() = 0;
|
||||
virtual Poco::URI URI(const std::string &IPAddress) = 0;
|
||||
virtual std::string Country(const std::string &Response) = 0;
|
||||
virtual ~IPToCountryProvider(){};
|
||||
};
|
||||
|
||||
class IPInfo : public IPToCountryProvider {
|
||||
@@ -30,28 +29,26 @@ namespace OpenWifi {
|
||||
return !Key_.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Poco::URI URI(const std::string & IPAddress) override {
|
||||
Poco::URI U("https://ipinfo.io");
|
||||
[[nodiscard]] inline Poco::URI URI(const std::string &IPAddress) override {
|
||||
Poco::URI U("https://ipinfo.io");
|
||||
U.setPath("/" + IPAddress);
|
||||
U.addQueryParameter("token",Key_);
|
||||
U.addQueryParameter("token", Key_);
|
||||
return U;
|
||||
}
|
||||
|
||||
inline std::string Country( const std::string & Response ) override {
|
||||
inline std::string Country(const std::string &Response) override {
|
||||
try {
|
||||
nlohmann::json IPInfo = nlohmann::json::parse(Response);
|
||||
if (IPInfo.contains("country") && IPInfo["country"].is_string()) {
|
||||
return IPInfo["country"];
|
||||
}
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string Key_;
|
||||
|
||||
};
|
||||
|
||||
class IPData : public IPToCountryProvider {
|
||||
@@ -62,24 +59,24 @@ namespace OpenWifi {
|
||||
return !Key_.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Poco::URI URI(const std::string & IPAddress) override {
|
||||
Poco::URI U("https://api.ipdata.co");
|
||||
[[nodiscard]] inline Poco::URI URI(const std::string &IPAddress) override {
|
||||
Poco::URI U("https://api.ipdata.co");
|
||||
U.setPath("/" + IPAddress);
|
||||
U.addQueryParameter("api-key",Key_);
|
||||
U.addQueryParameter("api-key", Key_);
|
||||
return U;
|
||||
}
|
||||
|
||||
inline std::string Country( const std::string & Response ) override {
|
||||
inline std::string Country(const std::string &Response) override {
|
||||
try {
|
||||
nlohmann::json IPInfo = nlohmann::json::parse(Response);
|
||||
if (IPInfo.contains("country_code") && IPInfo["country_code"].is_string()) {
|
||||
return IPInfo["country_code"];
|
||||
}
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string Key_;
|
||||
};
|
||||
@@ -92,33 +89,33 @@ namespace OpenWifi {
|
||||
return !Key_.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Poco::URI URI(const std::string & IPAddress) override {
|
||||
Poco::URI U("https://api.ip2location.com/v2");
|
||||
[[nodiscard]] inline Poco::URI URI(const std::string &IPAddress) override {
|
||||
Poco::URI U("https://api.ip2location.com/v2");
|
||||
U.setPath("/");
|
||||
U.addQueryParameter("ip", IPAddress);
|
||||
U.addQueryParameter("package", "WS1");
|
||||
U.addQueryParameter("key",Key_);
|
||||
U.addQueryParameter("key", Key_);
|
||||
return U;
|
||||
}
|
||||
|
||||
inline std::string Country( const std::string & Response ) override {
|
||||
inline std::string Country(const std::string &Response) override {
|
||||
try {
|
||||
nlohmann::json IPInfo = nlohmann::json::parse(Response);
|
||||
if (IPInfo.contains("country_code") && IPInfo["country_code"].is_string()) {
|
||||
return IPInfo["country_code"];
|
||||
}
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
std::string Key_;
|
||||
};
|
||||
|
||||
template<typename BaseClass, typename T, typename... Args>
|
||||
std::unique_ptr<BaseClass> IPLocationProvider(const std::string & RequestProvider ) {
|
||||
if(T::Name()==RequestProvider) {
|
||||
template <typename BaseClass, typename T, typename... Args>
|
||||
std::unique_ptr<BaseClass> IPLocationProvider(const std::string &RequestProvider) {
|
||||
if (T::Name() == RequestProvider) {
|
||||
return std::make_unique<T>();
|
||||
}
|
||||
if constexpr (sizeof...(Args) == 0) {
|
||||
@@ -136,11 +133,12 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
inline int Start() final {
|
||||
poco_notice(Logger(),"Starting...");
|
||||
ProviderName_ = MicroServiceConfigGetString("iptocountry.provider","");
|
||||
if(!ProviderName_.empty()) {
|
||||
Provider_ = IPLocationProvider<IPToCountryProvider, IPInfo, IPData, IP2Location>(ProviderName_);
|
||||
if(Provider_!= nullptr) {
|
||||
poco_notice(Logger(), "Starting...");
|
||||
ProviderName_ = MicroServiceConfigGetString("iptocountry.provider", "");
|
||||
if (!ProviderName_.empty()) {
|
||||
Provider_ = IPLocationProvider<IPToCountryProvider, IPInfo, IPData, IP2Location>(
|
||||
ProviderName_);
|
||||
if (Provider_ != nullptr) {
|
||||
Enabled_ = Provider_->Init();
|
||||
}
|
||||
}
|
||||
@@ -149,28 +147,26 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
inline void Stop() final {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
poco_notice(Logger(), "Stopping...");
|
||||
// Nothing to do - just to provide the same look at the others.
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
poco_notice(Logger(), "Stopped...");
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline std::string ReformatAddress(const std::string & I )
|
||||
{
|
||||
if(I.substr(0,7) == "::ffff:")
|
||||
{
|
||||
std::string ip = I.substr(7 );
|
||||
[[nodiscard]] static inline std::string ReformatAddress(const std::string &I) {
|
||||
if (I.substr(0, 7) == "::ffff:") {
|
||||
std::string ip = I.substr(7);
|
||||
return ip;
|
||||
}
|
||||
return I;
|
||||
}
|
||||
|
||||
inline std::string Get(const Poco::Net::IPAddress & IP) {
|
||||
inline std::string Get(const Poco::Net::IPAddress &IP) {
|
||||
if (!Enabled_)
|
||||
return Default_;
|
||||
return Get(ReformatAddress(IP.toString()));
|
||||
}
|
||||
|
||||
inline std::string Get(const std::string & IP) {
|
||||
inline std::string Get(const std::string &IP) {
|
||||
if (!Enabled_)
|
||||
return Default_;
|
||||
try {
|
||||
@@ -178,10 +174,10 @@ namespace OpenWifi {
|
||||
std::string Response;
|
||||
if (Utils::wgets(URL, Response)) {
|
||||
auto Answer = Provider_->Country(Response);
|
||||
if(!Answer.empty())
|
||||
if (!Answer.empty())
|
||||
return Answer;
|
||||
}
|
||||
} catch(...) {
|
||||
} catch (...) {
|
||||
}
|
||||
return Default_;
|
||||
}
|
||||
@@ -189,17 +185,14 @@ namespace OpenWifi {
|
||||
inline auto Enabled() const { return Enabled_; }
|
||||
|
||||
private:
|
||||
bool Enabled_=false;
|
||||
std::string Default_;
|
||||
std::unique_ptr<IPToCountryProvider> Provider_;
|
||||
std::string ProviderName_;
|
||||
bool Enabled_ = false;
|
||||
std::string Default_;
|
||||
std::unique_ptr<IPToCountryProvider> Provider_;
|
||||
std::string ProviderName_;
|
||||
|
||||
FindCountryFromIP() noexcept:
|
||||
SubSystemServer("IpToCountry", "IPTOC-SVR", "iptocountry")
|
||||
{
|
||||
}
|
||||
FindCountryFromIP() noexcept : SubSystemServer("IpToCountry", "IPTOC-SVR", "iptocountry") {}
|
||||
};
|
||||
|
||||
inline auto FindCountryFromIP() { return FindCountryFromIP::instance(); }
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
19
src/GWKafkaEvents.cpp
Normal file
19
src/GWKafkaEvents.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-02-03.
|
||||
//
|
||||
|
||||
#include "GWKafkaEvents.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void GWKafkaEvents::Send() {
|
||||
if (KafkaManager()->Enabled()) {
|
||||
Poco::JSON::Object Event;
|
||||
Event.set("type", type_);
|
||||
Event.set("timestamp", timestamp_);
|
||||
Event.set("payload", payload_);
|
||||
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, Utils::IntToSerialNumber(serialNumber_), Event);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
116
src/GWKafkaEvents.h
Normal file
116
src/GWKafkaEvents.h
Normal file
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// Created by stephane bourque on 2023-02-03.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Poco/JSON/Object.h>
|
||||
#include <RESTObjects/RESTAPI_GWobjects.h>
|
||||
#include <framework/KafkaManager.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class GWKafkaEvents {
|
||||
public:
|
||||
GWKafkaEvents(std::uint64_t serialNumber, const std::string &type,
|
||||
std::uint64_t timestamp)
|
||||
: 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_;};
|
||||
|
||||
protected:
|
||||
std::uint64_t serialNumber_;
|
||||
std::string type_;
|
||||
std::uint64_t timestamp_ = 0;
|
||||
Poco::JSON::Object::Ptr payload_;
|
||||
};
|
||||
|
||||
class DeviceFirmwareChangeKafkaEvent : public GWKafkaEvents {
|
||||
public:
|
||||
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() {
|
||||
payload_->set("oldFirmware", oldFirmware_);
|
||||
payload_->set("newFirmware", newFirmware_);
|
||||
Send();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string oldFirmware_, newFirmware_;
|
||||
};
|
||||
|
||||
class DeviceConfigurationChangeKafkaEvent : public GWKafkaEvents {
|
||||
public:
|
||||
DeviceConfigurationChangeKafkaEvent(std::uint64_t serialNumber,
|
||||
std::uint64_t timestamp,
|
||||
const Poco::JSON::Object::Ptr config)
|
||||
: GWKafkaEvents(serialNumber, "unit.configuration_change", timestamp), config_(config) {
|
||||
}
|
||||
|
||||
~DeviceConfigurationChangeKafkaEvent() {
|
||||
if(config_!= nullptr) {
|
||||
std::ostringstream os;
|
||||
config_->stringify(os);
|
||||
if(os.str().size()> KafkaManager()->KafkaManagerMaximumPayloadSize()) {
|
||||
payload_->set("configuration", "{}");
|
||||
payload_->set("configurationTooBig", true);
|
||||
} else {
|
||||
payload_->set("configuration", *config_);
|
||||
}
|
||||
}
|
||||
Send();
|
||||
}
|
||||
|
||||
private:
|
||||
Poco::JSON::Object::Ptr config_;
|
||||
};
|
||||
|
||||
class DeviceBlacklistedKafkaEvent : public GWKafkaEvents {
|
||||
public:
|
||||
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)
|
||||
: GWKafkaEvents(serialNumber, "blacklisted_device", timestamp), reason_(reason),
|
||||
author_(author), created_(created), IP_(IP) {}
|
||||
|
||||
~DeviceBlacklistedKafkaEvent() {
|
||||
payload_->set("reason", reason_);
|
||||
payload_->set("author", author_);
|
||||
payload_->set("created", created_);
|
||||
payload_->set("ipaddress", IP_);
|
||||
Send();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string reason_, author_;
|
||||
std::uint64_t created_;
|
||||
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
|
||||
@@ -7,23 +7,22 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
GwWebSocketClient::GwWebSocketClient(Poco::Logger &Logger):
|
||||
Logger_(Logger){
|
||||
GwWebSocketClient::GwWebSocketClient(Poco::Logger &Logger) : Logger_(Logger) {
|
||||
UI_WebSocketClientServer()->SetProcessor(this);
|
||||
}
|
||||
|
||||
GwWebSocketClient::~GwWebSocketClient() {
|
||||
UI_WebSocketClientServer()->SetProcessor(nullptr);
|
||||
}
|
||||
GwWebSocketClient::~GwWebSocketClient() { UI_WebSocketClientServer()->SetProcessor(nullptr); }
|
||||
|
||||
void GwWebSocketClient::Processor(const Poco::JSON::Object::Ptr &O, std::string &Answer, bool &Done ) {
|
||||
void GwWebSocketClient::Processor(const Poco::JSON::Object::Ptr &O, std::string &Answer,
|
||||
bool &Done,
|
||||
[[maybe_unused]] const SecurityObjects::UserInfo &UserInfo) {
|
||||
try {
|
||||
if (O->has("command")) {
|
||||
auto Command = O->get("command").toString();
|
||||
if (Command == "serial_number_search" && O->has("serial_prefix")) {
|
||||
ws_command_serial_number_search(O,Done,Answer);
|
||||
} else if (Command=="exit") {
|
||||
ws_command_exit(O,Done,Answer);
|
||||
ws_command_serial_number_search(O, Done, Answer);
|
||||
} else if (Command == "exit") {
|
||||
ws_command_exit(O, Done, Answer);
|
||||
} else {
|
||||
ws_command_invalid(O, Done, Answer);
|
||||
}
|
||||
@@ -34,7 +33,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void GwWebSocketClient::ws_command_serial_number_search(const Poco::JSON::Object::Ptr &O,
|
||||
bool &Done, std::string &Answer) {
|
||||
bool &Done, std::string &Answer) {
|
||||
Done = false;
|
||||
auto Prefix = O->get("serial_prefix").toString();
|
||||
if (!Prefix.empty() && Prefix.length() < 13) {
|
||||
@@ -51,13 +50,15 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void GwWebSocketClient::ws_command_exit([[maybe_unused]] const Poco::JSON::Object::Ptr &O, bool &Done, std::string &Answer) {
|
||||
void GwWebSocketClient::ws_command_exit([[maybe_unused]] const Poco::JSON::Object::Ptr &O,
|
||||
bool &Done, std::string &Answer) {
|
||||
Done = true;
|
||||
Answer = R"lit({ "closing" : "Goodbye! Aurevoir! Hasta la vista!" })lit";
|
||||
}
|
||||
|
||||
void GwWebSocketClient::ws_command_invalid([[maybe_unused]] const Poco::JSON::Object::Ptr &O, bool &Done, std::string &Answer) {
|
||||
void GwWebSocketClient::ws_command_invalid([[maybe_unused]] const Poco::JSON::Object::Ptr &O,
|
||||
bool &Done, std::string &Answer) {
|
||||
Done = false;
|
||||
Answer = std::string{R"lit({ "error" : "invalid command" })lit"};
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -11,13 +11,15 @@ namespace OpenWifi {
|
||||
public:
|
||||
explicit GwWebSocketClient(Poco::Logger &Logger);
|
||||
virtual ~GwWebSocketClient();
|
||||
virtual void Processor(const Poco::JSON::Object::Ptr &O, std::string &Answer, bool &Done );
|
||||
void ws_command_serial_number_search( const Poco::JSON::Object::Ptr &O, bool &Done, std::string &Answer);
|
||||
void ws_command_exit( const Poco::JSON::Object::Ptr &O, bool &Done, std::string &Answer);
|
||||
void ws_command_invalid( const Poco::JSON::Object::Ptr &O, bool &Done, std::string &Answer);
|
||||
virtual void Processor(const Poco::JSON::Object::Ptr &O, std::string &Answer, bool &Done,
|
||||
const SecurityObjects::UserInfo &UserInfo);
|
||||
void ws_command_serial_number_search(const Poco::JSON::Object::Ptr &O, bool &Done,
|
||||
std::string &Answer);
|
||||
void ws_command_exit(const Poco::JSON::Object::Ptr &O, bool &Done, std::string &Answer);
|
||||
void ws_command_invalid(const Poco::JSON::Object::Ptr &O, bool &Done, std::string &Answer);
|
||||
|
||||
private:
|
||||
Poco::Logger & Logger_;
|
||||
inline Poco::Logger & Logger() { return Logger_; }
|
||||
Poco::Logger &Logger_;
|
||||
inline Poco::Logger &Logger() { return Logger_; }
|
||||
};
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-17.
|
||||
//
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/String.h"
|
||||
#include "Poco/StringTokenizer.h"
|
||||
#include "Poco/URIStreamOpener.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/File.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/utils.h"
|
||||
@@ -22,25 +22,25 @@ namespace OpenWifi {
|
||||
|
||||
int OUIServer::Start() {
|
||||
Running_ = true;
|
||||
LatestOUIFileName_ = MicroServiceDataDirectory() + "/newOUIFile.txt";
|
||||
LatestOUIFileName_ = MicroServiceDataDirectory() + "/newOUIFile.txt";
|
||||
CurrentOUIFileName_ = MicroServiceDataDirectory() + "/current_oui.txt";
|
||||
|
||||
bool Recovered = false;
|
||||
Poco::File OuiFile(CurrentOUIFileName_);
|
||||
if(OuiFile.exists()) {
|
||||
std::unique_lock Lock(LocalMutex_);
|
||||
Recovered = ProcessFile(CurrentOUIFileName_,OUIs_);
|
||||
if(Recovered) {
|
||||
Poco::File OuiFile(CurrentOUIFileName_);
|
||||
if (OuiFile.exists()) {
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
Recovered = ProcessFile(CurrentOUIFileName_, OUIs_);
|
||||
if (Recovered) {
|
||||
poco_notice(Logger(),
|
||||
fmt::format("Recovered last OUI file - {}", CurrentOUIFileName_));
|
||||
fmt::format("Recovered last OUI file - {}", CurrentOUIFileName_));
|
||||
}
|
||||
} else {
|
||||
poco_notice(Logger(),
|
||||
fmt::format("No existing OUIFile.", CurrentOUIFileName_));
|
||||
poco_notice(Logger(), fmt::format("No existing OUIFile.", CurrentOUIFileName_));
|
||||
}
|
||||
|
||||
UpdaterCallBack_ = std::make_unique<Poco::TimerCallback<OUIServer>>(*this, &OUIServer::onTimer);
|
||||
if(Recovered) {
|
||||
UpdaterCallBack_ =
|
||||
std::make_unique<Poco::TimerCallback<OUIServer>>(*this, &OUIServer::onTimer);
|
||||
if (Recovered) {
|
||||
Timer_.setStartInterval(60 * 60 * 1000); // first run in 1 hour
|
||||
} else {
|
||||
Timer_.setStartInterval(30 * 1000); // first run in 5 minutes
|
||||
@@ -51,15 +51,15 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void OUIServer::Stop() {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
Running_=false;
|
||||
poco_notice(Logger(), "Stopping...");
|
||||
Running_ = false;
|
||||
Timer_.stop();
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
poco_notice(Logger(), "Stopped...");
|
||||
}
|
||||
|
||||
void OUIServer::reinitialize([[maybe_unused]] Poco::Util::Application &self) {
|
||||
MicroServiceLoadConfigurationFile();
|
||||
poco_information(Logger(),"Reinitializing.");
|
||||
poco_information(Logger(), "Reinitializing.");
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
@@ -67,14 +67,18 @@ namespace OpenWifi {
|
||||
bool OUIServer::GetFile(const std::string &FileName) {
|
||||
try {
|
||||
LastUpdate_ = Utils::Now();
|
||||
poco_information(Logger(), fmt::format("Start: Retrieving OUI file: {}",MicroServiceConfigGetString("oui.download.uri","")));
|
||||
std::unique_ptr<std::istream> pStr(
|
||||
Poco::URIStreamOpener::defaultOpener().open(MicroServiceConfigGetString("oui.download.uri","")));
|
||||
poco_information(Logger(),
|
||||
fmt::format("Start: Retrieving OUI file: {}",
|
||||
MicroServiceConfigGetString("oui.download.uri", "")));
|
||||
std::unique_ptr<std::istream> pStr(Poco::URIStreamOpener::defaultOpener().open(
|
||||
MicroServiceConfigGetString("oui.download.uri", "")));
|
||||
std::ofstream OS;
|
||||
OS.open(FileName);
|
||||
Poco::StreamCopier::copyStream(*pStr, OS);
|
||||
OS.close();
|
||||
poco_information(Logger(), fmt::format("Done: Retrieving OUI file: {}",MicroServiceConfigGetString("oui.download.uri","")));
|
||||
poco_information(Logger(),
|
||||
fmt::format("Done: Retrieving OUI file: {}",
|
||||
MicroServiceConfigGetString("oui.download.uri", "")));
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
@@ -82,13 +86,13 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OUIServer::ProcessFile( const std::string &FileName, OUIMap &Map) {
|
||||
bool OUIServer::ProcessFile(const std::string &FileName, OUIMap &Map) {
|
||||
try {
|
||||
std::ifstream Input;
|
||||
Input.open(FileName, std::ios::binary);
|
||||
|
||||
while (!Input.eof()) {
|
||||
if(!Running_)
|
||||
if (!Running_)
|
||||
return false;
|
||||
char buf[1024];
|
||||
Input.getline(buf, sizeof(buf));
|
||||
@@ -112,67 +116,68 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch ( const Poco::Exception &E) {
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void OUIServer::onTimer([[maybe_unused]] Poco::Timer & timer) {
|
||||
void OUIServer::onTimer([[maybe_unused]] Poco::Timer &timer) {
|
||||
Utils::SetThreadName("ouisvr-timer");
|
||||
if(Updating_)
|
||||
if (Updating_)
|
||||
return;
|
||||
Updating_ = true;
|
||||
|
||||
poco_information(Logger(),"Starting to process OUI file...");
|
||||
poco_information(Logger(), "Starting to process OUI file...");
|
||||
|
||||
// fetch data from server, if not available, just use the file we already have.
|
||||
Poco::File Current(CurrentOUIFileName_);
|
||||
if(Current.exists()) {
|
||||
if((Utils::Now()-Current.getLastModified().epochTime()) < (7*24*60*60)) {
|
||||
if(!Initialized_) {
|
||||
if(ProcessFile(CurrentOUIFileName_, OUIs_)) {
|
||||
Poco::File Current(CurrentOUIFileName_);
|
||||
if (Current.exists()) {
|
||||
if ((Utils::Now() - Current.getLastModified().epochTime()) < (7 * 24 * 60 * 60)) {
|
||||
if (!Initialized_) {
|
||||
if (ProcessFile(CurrentOUIFileName_, OUIs_)) {
|
||||
Initialized_ = true;
|
||||
Updating_=false;
|
||||
Updating_ = false;
|
||||
poco_information(Logger(), "Using cached file.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Updating_=false;
|
||||
Updating_ = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OUIMap TmpOUIs;
|
||||
if(GetFile(LatestOUIFileName_) && ProcessFile(LatestOUIFileName_, TmpOUIs)) {
|
||||
std::unique_lock G(LocalMutex_);
|
||||
if (GetFile(LatestOUIFileName_) && ProcessFile(LatestOUIFileName_, TmpOUIs)) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
OUIs_ = std::move(TmpOUIs);
|
||||
LastUpdate_ = Utils::Now();
|
||||
Poco::File F1(CurrentOUIFileName_);
|
||||
if(F1.exists())
|
||||
if (F1.exists())
|
||||
F1.remove();
|
||||
Poco::File F2(LatestOUIFileName_);
|
||||
F2.renameTo(CurrentOUIFileName_);
|
||||
poco_information(Logger(), fmt::format("New OUI file {} downloaded.",LatestOUIFileName_));
|
||||
} else if(OUIs_.empty()) {
|
||||
if(ProcessFile(CurrentOUIFileName_, TmpOUIs)) {
|
||||
poco_information(Logger(),
|
||||
fmt::format("New OUI file {} downloaded.", LatestOUIFileName_));
|
||||
} else if (OUIs_.empty()) {
|
||||
if (ProcessFile(CurrentOUIFileName_, TmpOUIs)) {
|
||||
LastUpdate_ = Utils::Now();
|
||||
std::unique_lock G(LocalMutex_);
|
||||
std::lock_guard G(LocalMutex_);
|
||||
OUIs_ = std::move(TmpOUIs);
|
||||
}
|
||||
}
|
||||
Initialized_=true;
|
||||
Initialized_ = true;
|
||||
Updating_ = false;
|
||||
poco_information(Logger(),"Done processing OUI file...");
|
||||
poco_information(Logger(), "Done processing OUI file...");
|
||||
}
|
||||
|
||||
std::string OUIServer::GetManufacturer(const std::string &MAC) {
|
||||
std::shared_lock Lock(LocalMutex_);
|
||||
std::lock_guard Lock(LocalMutex_);
|
||||
|
||||
auto Manufacturer = OUIs_.find(Utils::SerialNumberToOUI(MAC));
|
||||
if(Manufacturer != OUIs_.end())
|
||||
if (Manufacturer != OUIs_.end())
|
||||
return Manufacturer->second;
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}; // namespace OpenWifi
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <shared_mutex>
|
||||
#include <mutex>
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
|
||||
@@ -14,18 +14,17 @@ namespace OpenWifi {
|
||||
|
||||
class OUIServer : public SubSystemServer {
|
||||
public:
|
||||
|
||||
typedef std::map<uint64_t,std::string> OUIMap;
|
||||
typedef std::map<uint64_t, std::string> OUIMap;
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new OUIServer;
|
||||
static auto instance_ = new OUIServer;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
|
||||
void onTimer(Poco::Timer & timer);
|
||||
void onTimer(Poco::Timer &timer);
|
||||
|
||||
void reinitialize(Poco::Util::Application &self) override;
|
||||
[[nodiscard]] std::string GetManufacturer(const std::string &MAC);
|
||||
@@ -33,23 +32,19 @@ namespace OpenWifi {
|
||||
[[nodiscard]] bool ProcessFile(const std::string &FileName, OUIMap &Map);
|
||||
|
||||
private:
|
||||
std::shared_mutex LocalMutex_;
|
||||
uint64_t LastUpdate_ = 0 ;
|
||||
bool Initialized_ = false;
|
||||
OUIMap OUIs_;
|
||||
volatile std::atomic_bool Updating_=false;
|
||||
volatile std::atomic_bool Running_=false;
|
||||
Poco::Timer Timer_;
|
||||
std::unique_ptr<Poco::TimerCallback<OUIServer>> UpdaterCallBack_;
|
||||
std::string LatestOUIFileName_,CurrentOUIFileName_;
|
||||
std::mutex LocalMutex_;
|
||||
uint64_t LastUpdate_ = 0;
|
||||
bool Initialized_ = false;
|
||||
OUIMap OUIs_;
|
||||
volatile std::atomic_bool Updating_ = false;
|
||||
volatile std::atomic_bool Running_ = false;
|
||||
Poco::Timer Timer_;
|
||||
std::unique_ptr<Poco::TimerCallback<OUIServer>> UpdaterCallBack_;
|
||||
std::string LatestOUIFileName_, CurrentOUIFileName_;
|
||||
|
||||
OUIServer() noexcept:
|
||||
SubSystemServer("OUIServer", "OUI-SVR", "ouiserver")
|
||||
{
|
||||
}
|
||||
OUIServer() noexcept : SubSystemServer("OUIServer", "OUI-SVR", "ouiserver") {}
|
||||
};
|
||||
|
||||
inline auto OUIServer() { return OUIServer::instance(); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
1677
src/ParseWifiScan.h
1677
src/ParseWifiScan.h
File diff suppressed because it is too large
Load Diff
443
src/RADIUSSessionTracker.cpp
Normal file
443
src/RADIUSSessionTracker.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
std::lock_guard Guard(Mutex_);
|
||||
auto hint = AccountingSessions_.find(SerialNumber);
|
||||
if(hint==end(AccountingSessions_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
poco_information(Logger(),fmt::format("{}: Disconnecting.", SerialNumber));
|
||||
|
||||
// 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
|
||||
|
||||
747
src/RADIUS_Destination.h
Normal file
747
src/RADIUS_Destination.h
Normal file
@@ -0,0 +1,747 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-08-15.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#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"
|
||||
#include "Poco/Net/SocketReactor.h"
|
||||
#include "Poco/TemporaryFile.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include "AP_WS_Server.h"
|
||||
#include "RADIUS_helpers.h"
|
||||
#include <RESTObjects/RESTAPI_GWobjects.h>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RADIUS_Destination : public Poco::Runnable {
|
||||
public:
|
||||
RADIUS_Destination(Poco::Net::SocketReactor &R, const GWObjects::RadiusProxyPool &P)
|
||||
: Reactor_(R),
|
||||
Logger_(Poco::Logger::get(
|
||||
fmt::format("RADSEC: {}", P.name))),
|
||||
Pool_(P)
|
||||
{
|
||||
Type_ = GWObjects::RadiusEndpointType(P.radsecPoolType);
|
||||
Start();
|
||||
}
|
||||
|
||||
~RADIUS_Destination() override { Stop(); }
|
||||
|
||||
const int SMALLEST_RADIUS_PACKET = 20 + 19 + 4;
|
||||
const int DEFAULT_RADIUS_AUTHENTICATION_PORT = 1812;
|
||||
const int DEFAULT_RADIUS_ACCOUNTING_PORT = 1813;
|
||||
const int DEFAULT_RADIUS_CoA_PORT = 3799;
|
||||
|
||||
inline int Start() {
|
||||
ReconnectThread_.start(*this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void Stop() {
|
||||
TryAgain_ = false;
|
||||
Disconnect();
|
||||
ReconnectThread_.wakeUp();
|
||||
ReconnectThread_.join();
|
||||
}
|
||||
|
||||
inline void run() final {
|
||||
Poco::Thread::trySleep(5000);
|
||||
std::uint64_t CurrentDelay = 10, maxDelay=300, LastTry=0, LastKeepAlive=0;
|
||||
while (TryAgain_) {
|
||||
if (!Connected_) {
|
||||
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) > Pool_.radsecKeepAlive) {
|
||||
RADIUS::RadiusOutputPacket P(Pool_.authConfig.servers[ServerIndex_].radsecSecret);
|
||||
P.MakeStatusMessage(Pool_.authConfig.servers[ServerIndex_].name);
|
||||
if(Type_!=GWObjects::RadiusEndpointType::generic) {
|
||||
poco_trace(Logger_, fmt::format("{}: Keep-Alive message.", Pool_.authConfig.servers[ServerIndex_].name));
|
||||
Socket_->sendBytes(P.Data(), P.Len());
|
||||
}
|
||||
LastKeepAlive = Utils::Now();
|
||||
}
|
||||
Poco::Thread::trySleep(2000);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool SendData(const std::string &serial_number, const unsigned char *buffer,
|
||||
int length) {
|
||||
try {
|
||||
if (Connected_) {
|
||||
RADIUS::RadiusPacket P(buffer, length);
|
||||
int sent_bytes;
|
||||
if (P.VerifyMessageAuthenticator(Pool_.authConfig.servers[ServerIndex_].radsecSecret)) {
|
||||
poco_trace(Logger_, fmt::format("{}: {} Sending {} bytes", serial_number,
|
||||
P.PacketType(), length));
|
||||
sent_bytes = Socket_->sendBytes(buffer, length);
|
||||
} else {
|
||||
poco_trace(Logger_, fmt::format("{}: {} Sending {} bytes", serial_number,
|
||||
P.PacketType(), length));
|
||||
P.ComputeMessageAuthenticator(Pool_.authConfig.servers[ServerIndex_].radsecSecret);
|
||||
sent_bytes = Socket_->sendBytes(P.Buffer(), length);
|
||||
}
|
||||
return (sent_bytes == length);
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger_, "Exception occurred: while sending data.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void
|
||||
onData([[maybe_unused]] const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf) {
|
||||
unsigned char Buffer[4096];
|
||||
|
||||
try {
|
||||
auto NumberOfReceivedBytes = Socket_->receiveBytes(Buffer, sizeof(Buffer));
|
||||
std::string ReplySource;
|
||||
if (NumberOfReceivedBytes >= 20) {
|
||||
RADIUS::RadiusPacket P(Buffer, NumberOfReceivedBytes);
|
||||
if (P.IsAuthentication()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if (!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
fmt::format("{}: {}:{} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(),
|
||||
P.PacketTypeToString(),
|
||||
NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusAuthenticationData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else if(P.IsStatusMessageReply(ReplySource)) {
|
||||
poco_debug(Logger_,
|
||||
fmt::format("{}: Keepalive message received.", ReplySource));
|
||||
} else {
|
||||
poco_debug(Logger_, "AUTH packet dropped.");
|
||||
}
|
||||
} else if (P.IsAccounting()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if (!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
fmt::format("{}: {}:{} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(),
|
||||
P.PacketTypeToString(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusAccountingData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "ACCT packet dropped.");
|
||||
}
|
||||
} else if (P.IsAuthority()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberTIP();
|
||||
if (!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
fmt::format("{}: {}:{} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(),
|
||||
P.PacketTypeToString(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusCoAData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "CoA/DM packet dropped.");
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger_,
|
||||
fmt::format("Unknown packet: Type: {} (type={}) Length={}",
|
||||
P.PacketType(), P.PacketTypeInt(), P.BufferLen()));
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
poco_warning(Logger_, "Invalid packet received. Resetting the connection.");
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger_, "Exception occurred. Resetting the connection.");
|
||||
}
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
inline void
|
||||
onError([[maybe_unused]] const Poco::AutoPtr<Poco::Net::ErrorNotification> &pNf) {
|
||||
poco_warning(Logger_, "Socker error. Terminating connection.");
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
inline void
|
||||
onShutdown([[maybe_unused]] const Poco::AutoPtr<Poco::Net::ShutdownNotification> &pNf) {
|
||||
poco_warning(Logger_, "Socker socket shutdown. Terminating connection.");
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
inline void OnAccountingSocketReadable(
|
||||
const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf) {
|
||||
Poco::Net::SocketAddress Sender;
|
||||
RADIUS::RadiusPacket P;
|
||||
|
||||
auto ReceiveSize = pNf->socket().impl()->receiveBytes(P.Buffer(), P.BufferLen());
|
||||
if (ReceiveSize < SMALLEST_RADIUS_PACKET) {
|
||||
poco_warning(Logger_, "Accounting: bad packet received.");
|
||||
return;
|
||||
}
|
||||
P.Evaluate(ReceiveSize);
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if (SerialNumber.empty()) {
|
||||
poco_warning(Logger_, "Accounting: missing serial number. Dropping request.");
|
||||
return;
|
||||
}
|
||||
poco_debug(
|
||||
Logger_,
|
||||
fmt::format(
|
||||
"Accounting Packet Response received for {}", SerialNumber ));
|
||||
AP_WS_Server()->SendRadiusAccountingData(SerialNumber, P.Buffer(), P.Size());
|
||||
}
|
||||
|
||||
inline void OnAuthenticationSocketReadable(
|
||||
const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf) {
|
||||
Poco::Net::SocketAddress Sender;
|
||||
RADIUS::RadiusPacket P;
|
||||
|
||||
auto ReceiveSize = pNf->socket().impl()->receiveBytes(P.Buffer(), P.BufferLen());
|
||||
if (ReceiveSize < SMALLEST_RADIUS_PACKET) {
|
||||
poco_warning(Logger_, "Authentication: bad packet received.");
|
||||
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. Dropping request.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
|
||||
poco_debug(
|
||||
Logger_,
|
||||
fmt::format(
|
||||
"Authentication Packet received for {}, CalledStationID: {}, CallingStationID:{}",
|
||||
SerialNumber, CalledStationID, CallingStationID));
|
||||
AP_WS_Server()->SendRadiusAuthenticationData(SerialNumber, P.Buffer(), P.Size());
|
||||
}
|
||||
|
||||
inline void OnCoASocketReadable(
|
||||
const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf) {
|
||||
Poco::Net::SocketAddress Sender;
|
||||
RADIUS::RadiusPacket P;
|
||||
|
||||
auto ReceiveSize = pNf.get()->socket().impl()->receiveBytes(P.Buffer(), P.BufferLen());
|
||||
if (ReceiveSize < SMALLEST_RADIUS_PACKET) {
|
||||
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. Dropping request.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
|
||||
poco_debug(
|
||||
Logger_,
|
||||
fmt::format("CoA Packet received for {}, CalledStationID: {}, CallingStationID:{}",
|
||||
SerialNumber, CalledStationID, CallingStationID));
|
||||
AP_WS_Server()->SendRadiusCoAData(SerialNumber, P.Buffer(), P.Size());
|
||||
}
|
||||
|
||||
static inline bool IsExpired(const Poco::Crypto::X509Certificate &C) {
|
||||
return C.expiresOn().timestamp().epochTime() < (std::time_t)Utils::Now();
|
||||
}
|
||||
|
||||
static inline void Cat(const std::string &F1, const std::string & F2, const std::string &F) {
|
||||
std::ofstream of(F.c_str(),std::ios_base::trunc|std::ios_base::out|std::ios_base::binary);
|
||||
std::ifstream if1(F1.c_str(),std::ios_base::binary|std::ios_base::in);
|
||||
Poco::StreamCopier::copyStream(if1,of);
|
||||
of << std::endl;
|
||||
std::ifstream if2(F2.c_str(),std::ios_base::binary|std::ios_base::in);
|
||||
Poco::StreamCopier::copyStream(if2,of);
|
||||
of << std::endl;
|
||||
of.close();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
DecodeFile(KeyFile_.path(), Pool_.acctConfig.servers[ServerIndex_].radsecKey);
|
||||
DecodeFile(CertFile_.path(), Pool_.acctConfig.servers[ServerIndex_].radsecCert);
|
||||
DecodeFile(Intermediate0.path(), Pool_.acctConfig.servers[ServerIndex_].radsecCacerts[0]);
|
||||
DecodeFile(Intermediate1.path(), Pool_.acctConfig.servers[ServerIndex_].radsecCacerts[1]);
|
||||
|
||||
const static 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();
|
||||
|
||||
auto SecureContext = Poco::AutoPtr<Poco::Net::Context>(
|
||||
new Poco::Net::Context(Poco::Net::Context::TLS_CLIENT_USE, ""));
|
||||
|
||||
if (Pool_.acctConfig.servers[ServerIndex_].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.",
|
||||
Pool_.acctConfig.servers[ServerIndex_].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);
|
||||
ServerIndex_ = 0 ;
|
||||
for (const auto &PoolEntryServer : Pool_.acctConfig.servers) {
|
||||
Poco::Net::SocketAddress Destination(PoolEntryServer.ip, PoolEntryServer.port);
|
||||
try {
|
||||
poco_information(Logger_, fmt::format("Attempting to connect to {}", CommonName()));
|
||||
Socket_->connect(Destination, Poco::Timespan(20, 0));
|
||||
Socket_->completeHandshake();
|
||||
|
||||
if (!Pool_.authConfig.servers[ServerIndex_].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<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::onData));
|
||||
Reactor_.addEventHandler(
|
||||
*Socket_, Poco::NObserver<RADIUS_Destination, Poco::Net::ErrorNotification>(
|
||||
*this, &RADIUS_Destination::onError));
|
||||
Reactor_.addEventHandler(
|
||||
*Socket_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ShutdownNotification>(
|
||||
*this, &RADIUS_Destination::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.");
|
||||
}
|
||||
ServerIndex_++;
|
||||
}
|
||||
}
|
||||
ServerIndex_=0;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Connect_Orion() {
|
||||
if (TryAgain_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
|
||||
Poco::TemporaryFile CertFile_(MicroServiceDataDirectory());
|
||||
Poco::TemporaryFile KeyFile_(MicroServiceDataDirectory());
|
||||
std::vector<std::unique_ptr<Poco::TemporaryFile>> CaCertFiles_;
|
||||
|
||||
DecodeFile(CertFile_.path(), Pool_.acctConfig.servers[ServerIndex_].radsecCert);
|
||||
DecodeFile(KeyFile_.path(), Pool_.acctConfig.servers[ServerIndex_].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.", Pool_.acctConfig.servers[ServerIndex_].name));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto &cert : Pool_.acctConfig.servers[ServerIndex_].radsecCacerts) {
|
||||
CaCertFiles_.emplace_back(
|
||||
std::make_unique<Poco::TemporaryFile>(MicroServiceDataDirectory()));
|
||||
DecodeFile(CaCertFiles_[CaCertFiles_.size() - 1]->path(), cert);
|
||||
}
|
||||
|
||||
auto SecureContext =
|
||||
Poco::AutoPtr<Poco::Net::Context>(new Poco::Net::Context(
|
||||
Poco::Net::Context::TLS_CLIENT_USE, KeyFile_.path(), CertFile_.path(), ""));
|
||||
if (Pool_.acctConfig.servers[ServerIndex_].allowSelfSigned) {
|
||||
SecureContext->setSecurityLevel(Poco::Net::Context::SECURITY_LEVEL_NONE);
|
||||
SecureContext->enableExtendedCertificateVerification(false);
|
||||
}
|
||||
|
||||
for (const auto &ca : CaCertFiles_) {
|
||||
Poco::Crypto::X509Certificate cert(ca->path());
|
||||
SecureContext->addCertificateAuthority(cert);
|
||||
}
|
||||
|
||||
Socket_ = std::make_unique<Poco::Net::SecureStreamSocket>(SecureContext);
|
||||
ServerIndex_ = 0 ;
|
||||
for (const auto &PoolEntryServer : Pool_.acctConfig.servers) {
|
||||
Poco::Net::SocketAddress Destination(PoolEntryServer.ip, PoolEntryServer.port);
|
||||
try {
|
||||
poco_information(Logger_, "Attempting to connect");
|
||||
Socket_->connect(Destination, Poco::Timespan(100, 0));
|
||||
Socket_->completeHandshake();
|
||||
|
||||
if (!Pool_.authConfig.servers[ServerIndex_].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<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::onData));
|
||||
Reactor_.addEventHandler(
|
||||
*Socket_, Poco::NObserver<RADIUS_Destination, Poco::Net::ErrorNotification>(
|
||||
*this, &RADIUS_Destination::onError));
|
||||
Reactor_.addEventHandler(
|
||||
*Socket_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ShutdownNotification>(
|
||||
*this, &RADIUS_Destination::onShutdown));
|
||||
|
||||
Connected_ = true;
|
||||
poco_information(Logger_, fmt::format("Connected. CN={}", CommonName()));
|
||||
return true;
|
||||
} catch (const Poco::Net::NetException &E) {
|
||||
poco_information(Logger_, "Could not connect.");
|
||||
Logger_.log(E);
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_information(Logger_, "Could not connect.");
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_information(Logger_, "Could not connect.");
|
||||
}
|
||||
ServerIndex_++;
|
||||
}
|
||||
}
|
||||
ServerIndex_=0;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Connect_Generic() {
|
||||
poco_information(Logger_, fmt::format("Connecting {}", Pool_.name));
|
||||
if (TryAgain_ && !Connected_) {
|
||||
|
||||
std::lock_guard G(LocalMutex_);
|
||||
|
||||
Poco::Net::SocketAddress AuthSockAddrV4(
|
||||
Poco::Net::AddressFamily::IPv4,
|
||||
MicroServiceConfigGetInt("radius.proxy.authentication.port",
|
||||
DEFAULT_RADIUS_AUTHENTICATION_PORT));
|
||||
AuthenticationSocketV4_ =
|
||||
std::make_unique<Poco::Net::DatagramSocket>(AuthSockAddrV4, true, true);
|
||||
|
||||
Poco::Net::SocketAddress AcctSockAddrV4(
|
||||
Poco::Net::AddressFamily::IPv4,
|
||||
MicroServiceConfigGetInt("radius.proxy.accounting.port",
|
||||
DEFAULT_RADIUS_ACCOUNTING_PORT));
|
||||
AccountingSocketV4_ =
|
||||
std::make_unique<Poco::Net::DatagramSocket>(AcctSockAddrV4, true, true);
|
||||
|
||||
Poco::Net::SocketAddress CoASockAddrV4(
|
||||
Poco::Net::AddressFamily::IPv4,
|
||||
MicroServiceConfigGetInt("radius.proxy.coa.port", DEFAULT_RADIUS_CoA_PORT));
|
||||
CoASocketV4_ = std::make_unique<Poco::Net::DatagramSocket>(CoASockAddrV4, true, true);
|
||||
|
||||
Reactor_.addEventHandler(
|
||||
*AuthenticationSocketV4_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAuthenticationSocketReadable));
|
||||
Reactor_.addEventHandler(
|
||||
*AccountingSocketV4_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAccountingSocketReadable));
|
||||
Reactor_.addEventHandler(
|
||||
*CoASocketV4_, Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnCoASocketReadable));
|
||||
|
||||
/*
|
||||
Poco::Net::SocketAddress AuthSockAddrV6(
|
||||
Poco::Net::AddressFamily::IPv6,
|
||||
MicroServiceConfigGetInt("radius.proxy.authentication.port",
|
||||
DEFAULT_RADIUS_AUTHENTICATION_PORT));
|
||||
AuthenticationSocketV6_ =
|
||||
std::make_unique<Poco::Net::DatagramSocket>(AuthSockAddrV6, true, true);
|
||||
|
||||
Poco::Net::SocketAddress AcctSockAddrV6(
|
||||
Poco::Net::AddressFamily::IPv6,
|
||||
MicroServiceConfigGetInt("radius.proxy.accounting.port",
|
||||
DEFAULT_RADIUS_AUTHENTICATION_PORT));
|
||||
AccountingSocketV6_ =
|
||||
std::make_unique<Poco::Net::DatagramSocket>(AcctSockAddrV6, true, true);
|
||||
|
||||
Poco::Net::SocketAddress CoASockAddrV6(
|
||||
Poco::Net::AddressFamily::IPv6,
|
||||
MicroServiceConfigGetInt("radius.proxy.coa.port", DEFAULT_RADIUS_CoA_PORT));
|
||||
CoASocketV6_ = std::make_unique<Poco::Net::DatagramSocket>(CoASockAddrV6, true, true);
|
||||
|
||||
Reactor_.addEventHandler(
|
||||
*AuthenticationSocketV6_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAuthenticationSocketReadable));
|
||||
Reactor_.addEventHandler(
|
||||
*AccountingSocketV6_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAccountingSocketReadable));
|
||||
|
||||
Reactor_.addEventHandler(
|
||||
*CoASocketV6_, Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnCoASocketReadable));
|
||||
*/
|
||||
Connected_ = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Connect_Radsec() {
|
||||
if (TryAgain_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Connect() {
|
||||
switch(Type_) {
|
||||
case GWObjects::RadiusEndpointType::orion: return Connect_Orion();
|
||||
case GWObjects::RadiusEndpointType::globalreach: return Connect_GlobalReach();
|
||||
case GWObjects::RadiusEndpointType::radsec: return Connect_Radsec();
|
||||
default:
|
||||
return Connect_Generic();
|
||||
}
|
||||
}
|
||||
|
||||
inline void Disconnect() {
|
||||
if (Connected_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
if(Type_==GWObjects::RadiusEndpointType::generic) {
|
||||
poco_information(Logger_, fmt::format("Disconnecting {} generic server. Releasing all UDP resources.", Pool_.name));
|
||||
|
||||
if(AuthenticationSocketV4_) {
|
||||
Reactor_.removeEventHandler(
|
||||
*AuthenticationSocketV4_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAuthenticationSocketReadable));
|
||||
AuthenticationSocketV4_->close();
|
||||
AuthenticationSocketV4_.reset();
|
||||
}
|
||||
|
||||
if(AccountingSocketV4_) {
|
||||
Reactor_.removeEventHandler(
|
||||
*AccountingSocketV4_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAccountingSocketReadable));
|
||||
AccountingSocketV4_->close();
|
||||
AccountingSocketV4_.reset();
|
||||
}
|
||||
|
||||
if(CoASocketV4_) {
|
||||
Reactor_.removeEventHandler(
|
||||
*CoASocketV4_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnCoASocketReadable));
|
||||
CoASocketV4_->close();
|
||||
CoASocketV4_.reset();
|
||||
}
|
||||
|
||||
/*
|
||||
if(AuthenticationSocketV6_) {
|
||||
Reactor_.removeEventHandler(
|
||||
*AuthenticationSocketV6_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAuthenticationSocketReadable));
|
||||
AuthenticationSocketV6_->close();
|
||||
AuthenticationSocketV6_.reset();
|
||||
}
|
||||
|
||||
if(AccountingSocketV6_) {
|
||||
Reactor_.removeEventHandler(
|
||||
*AccountingSocketV6_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnAccountingSocketReadable));
|
||||
AccountingSocketV6_->close();
|
||||
AccountingSocketV6_.reset();
|
||||
}
|
||||
|
||||
if(CoASocketV6_) {
|
||||
Reactor_.removeEventHandler(
|
||||
*CoASocketV6_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::OnCoASocketReadable));
|
||||
CoASocketV6_->close();
|
||||
CoASocketV6_.reset();
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
if(Socket_!=nullptr) {
|
||||
Reactor_.removeEventHandler(
|
||||
*Socket_, Poco::NObserver<RADIUS_Destination, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_Destination::onData));
|
||||
Reactor_.removeEventHandler(
|
||||
*Socket_, Poco::NObserver<RADIUS_Destination, Poco::Net::ErrorNotification>(
|
||||
*this, &RADIUS_Destination::onError));
|
||||
Reactor_.removeEventHandler(
|
||||
*Socket_,
|
||||
Poco::NObserver<RADIUS_Destination, Poco::Net::ShutdownNotification>(
|
||||
*this, &RADIUS_Destination::onShutdown));
|
||||
Socket_->close();
|
||||
}
|
||||
}
|
||||
Connected_ = false;
|
||||
}
|
||||
poco_information(Logger_, fmt::format("Disconnecting {}", Pool_.name));
|
||||
}
|
||||
|
||||
static void DecodeFile(const std::string &filename, const std::string &s) {
|
||||
std::ofstream sec_file(filename, std::ios_base::out | std::ios_base::trunc |
|
||||
std::ios_base::binary);
|
||||
std::stringstream is(s);
|
||||
Poco::Base64Decoder ds(is);
|
||||
Poco::StreamCopier::copyStream(ds, sec_file);
|
||||
sec_file.close();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string CommonName() {
|
||||
if (Peer_Cert_)
|
||||
return Peer_Cert_->commonName();
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string IssuerName() {
|
||||
if (Peer_Cert_)
|
||||
return Peer_Cert_->issuerName();
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string SubjectName() {
|
||||
if (Peer_Cert_)
|
||||
return Peer_Cert_->subjectName();
|
||||
return "";
|
||||
}
|
||||
|
||||
const auto &Pool() const { return Pool_; }
|
||||
auto ServerType() const { return Type_; }
|
||||
|
||||
inline bool SendRadiusDataAuthData(const std::string &serialNumber, const unsigned char *buffer, std::size_t size) {
|
||||
poco_trace(Logger_, fmt::format("{}: Sending RADIUS Auth {} bytes.", serialNumber, size));
|
||||
AuthenticationSocketV4_->sendTo(buffer, size, Poco::Net::SocketAddress(Pool_.authConfig.servers[0].ip, Pool_.authConfig.servers[0].port));
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SendRadiusDataAcctData(const std::string &serialNumber, const unsigned char *buffer, std::size_t size) {
|
||||
poco_trace(Logger_, fmt::format("{}: Sending RADIUS Acct {} bytes.", serialNumber, size));
|
||||
AccountingSocketV4_->sendTo(buffer, size, Poco::Net::SocketAddress(Pool_.acctConfig.servers[0].ip, Pool_.acctConfig.servers[0].port));
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool SendRadiusDataCoAData(const std::string &serialNumber, const unsigned char *buffer, std::size_t size) {
|
||||
poco_trace(Logger_, fmt::format("{}: Sending RADIUS CoA {} bytes.", serialNumber, size));
|
||||
CoASocketV4_->sendTo(buffer, size, Poco::Net::SocketAddress(Pool_.coaConfig.servers[0].ip, Pool_.coaConfig.servers[0].port));
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::recursive_mutex LocalMutex_;
|
||||
Poco::Net::SocketReactor &Reactor_;
|
||||
Poco::Logger &Logger_;
|
||||
|
||||
std::unique_ptr<Poco::Net::SecureStreamSocket> Socket_;
|
||||
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AccountingSocketV4_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AuthenticationSocketV4_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> CoASocketV4_;
|
||||
|
||||
/*
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> CoASocketV6_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AccountingSocketV6_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AuthenticationSocketV6_;
|
||||
*/
|
||||
|
||||
Poco::Thread ReconnectThread_;
|
||||
std::unique_ptr<Poco::Crypto::X509Certificate> Peer_Cert_;
|
||||
volatile bool Connected_ = false;
|
||||
volatile bool TryAgain_ = true;
|
||||
enum GWObjects::RadiusEndpointType Type_{GWObjects::RadiusEndpointType::unknown};
|
||||
GWObjects::RadiusProxyPool Pool_;
|
||||
uint64_t ServerIndex_=0;
|
||||
};
|
||||
} // namespace OpenWifi
|
||||
1219
src/RADIUS_helpers.h
1219
src/RADIUS_helpers.h
File diff suppressed because it is too large
Load Diff
@@ -4,402 +4,265 @@
|
||||
|
||||
#include "Poco/JSON/Parser.h"
|
||||
|
||||
#include "RADIUS_proxy_server.h"
|
||||
#include "RADIUS_helpers.h"
|
||||
#include "AP_WS_Server.h"
|
||||
#include "RADIUS_helpers.h"
|
||||
#include "RADIUS_proxy_server.h"
|
||||
|
||||
#include "RADIUSSessionTracker.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
const int SMALLEST_RADIUS_PACKET = 20+19+4;
|
||||
/*
|
||||
const int SMALLEST_RADIUS_PACKET = 20 + 19 + 4;
|
||||
const int DEFAULT_RADIUS_AUTHENTICATION_PORT = 1812;
|
||||
const int DEFAULT_RADIUS_ACCOUNTING_PORT = 1813;
|
||||
const int DEFAULT_RADIUS_CoA_PORT = 3799;
|
||||
*/
|
||||
|
||||
int RADIUS_proxy_server::Start() {
|
||||
|
||||
ConfigFilename_ = MicroServiceDataDirectory()+"/radius_pool_config.json";
|
||||
Poco::File Config(ConfigFilename_);
|
||||
ConfigFilename_ = MicroServiceDataDirectory() + "/radius_pool_config.json";
|
||||
Poco::File Config(ConfigFilename_);
|
||||
|
||||
Enabled_ = MicroServiceConfigGetBool("radius.proxy.enable",false);
|
||||
if(!Enabled_ && !Config.exists()) {
|
||||
StopRADSECServers();
|
||||
Enabled_ = MicroServiceConfigGetBool("radius.proxy.enable", false);
|
||||
if (!Enabled_ && !Config.exists()) {
|
||||
StopRADIUSDestinations();
|
||||
return 0;
|
||||
}
|
||||
|
||||
poco_notice(Logger(),"Starting...");
|
||||
poco_notice(Logger(), "Starting...");
|
||||
|
||||
Enabled_ = true;
|
||||
|
||||
Poco::Net::SocketAddress AuthSockAddrV4(Poco::Net::AddressFamily::IPv4,
|
||||
MicroServiceConfigGetInt("radius.proxy.authentication.port",DEFAULT_RADIUS_AUTHENTICATION_PORT));
|
||||
AuthenticationSocketV4_ = std::make_unique<Poco::Net::DatagramSocket>(AuthSockAddrV4,true,true);
|
||||
Poco::Net::SocketAddress AuthSockAddrV6(Poco::Net::AddressFamily::IPv6,
|
||||
MicroServiceConfigGetInt("radius.proxy.authentication.port",DEFAULT_RADIUS_AUTHENTICATION_PORT));
|
||||
AuthenticationSocketV6_ = std::make_unique<Poco::Net::DatagramSocket>(AuthSockAddrV6,true,true);
|
||||
|
||||
Poco::Net::SocketAddress AcctSockAddrV4(Poco::Net::AddressFamily::IPv4,
|
||||
MicroServiceConfigGetInt("radius.proxy.accounting.port",DEFAULT_RADIUS_ACCOUNTING_PORT));
|
||||
AccountingSocketV4_ = std::make_unique<Poco::Net::DatagramSocket>(AcctSockAddrV4,true,true);
|
||||
Poco::Net::SocketAddress AcctSockAddrV6(Poco::Net::AddressFamily::IPv6,
|
||||
MicroServiceConfigGetInt("radius.proxy.accounting.port",DEFAULT_RADIUS_ACCOUNTING_PORT));
|
||||
AccountingSocketV6_ = std::make_unique<Poco::Net::DatagramSocket>(AcctSockAddrV6,true,true);
|
||||
|
||||
Poco::Net::SocketAddress CoASockAddrV4(Poco::Net::AddressFamily::IPv4,
|
||||
MicroServiceConfigGetInt("radius.proxy.coa.port",DEFAULT_RADIUS_CoA_PORT));
|
||||
CoASocketV4_ = std::make_unique<Poco::Net::DatagramSocket>(CoASockAddrV4,true,true);
|
||||
Poco::Net::SocketAddress CoASockAddrV6(Poco::Net::AddressFamily::IPv6,
|
||||
MicroServiceConfigGetInt("radius.proxy.coa.port",DEFAULT_RADIUS_CoA_PORT));
|
||||
CoASocketV6_ = std::make_unique<Poco::Net::DatagramSocket>(CoASockAddrV6,true,true);
|
||||
|
||||
RadiusReactor_.reset();
|
||||
RadiusReactor_ = std::make_unique<Poco::Net::SocketReactor>();
|
||||
RadiusReactor_->addEventHandler(*AuthenticationSocketV4_,Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAuthenticationSocketReadable));
|
||||
RadiusReactor_->addEventHandler(*AuthenticationSocketV6_,Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAuthenticationSocketReadable));
|
||||
|
||||
RadiusReactor_->addEventHandler(*AccountingSocketV4_,Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAccountingSocketReadable));
|
||||
RadiusReactor_->addEventHandler(*AccountingSocketV6_,Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAccountingSocketReadable));
|
||||
|
||||
|
||||
RadiusReactor_->addEventHandler(*CoASocketV4_,Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnCoASocketReadable));
|
||||
RadiusReactor_->addEventHandler(*CoASocketV6_,Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnCoASocketReadable));
|
||||
|
||||
ParseConfig();
|
||||
StartRADSECServers();
|
||||
RadiusReactorThread_.start(*RadiusReactor_);
|
||||
Utils::SetThreadName(RadiusReactorThread_,"rad:reactor");
|
||||
StartRADIUSDestinations();
|
||||
RadiusReactorThread_.start(RadiusReactor_);
|
||||
Utils::SetThreadName(RadiusReactorThread_, "rad:reactor");
|
||||
Running_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::Stop() {
|
||||
if(Enabled_ && Running_) {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
RadiusReactor_->removeEventHandler(
|
||||
*AuthenticationSocketV4_,
|
||||
Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAuthenticationSocketReadable));
|
||||
RadiusReactor_->removeEventHandler(
|
||||
*AuthenticationSocketV6_,
|
||||
Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAuthenticationSocketReadable));
|
||||
if (Enabled_ && Running_) {
|
||||
poco_information(Logger(), "Stopping...");
|
||||
|
||||
RadiusReactor_->removeEventHandler(
|
||||
*AccountingSocketV4_,
|
||||
Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAccountingSocketReadable));
|
||||
RadiusReactor_->removeEventHandler(
|
||||
*AccountingSocketV6_,
|
||||
Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnAccountingSocketReadable));
|
||||
|
||||
RadiusReactor_->removeEventHandler(
|
||||
*CoASocketV4_,
|
||||
Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnCoASocketReadable));
|
||||
RadiusReactor_->removeEventHandler(
|
||||
*CoASocketV6_,
|
||||
Poco::NObserver<RADIUS_proxy_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADIUS_proxy_server::OnCoASocketReadable));
|
||||
|
||||
AuthenticationSocketV4_->close();
|
||||
AuthenticationSocketV6_->close();
|
||||
AccountingSocketV4_->close();
|
||||
AccountingSocketV6_->close();
|
||||
CoASocketV4_->close();
|
||||
CoASocketV6_->close();
|
||||
|
||||
AuthenticationSocketV4_.reset();
|
||||
AuthenticationSocketV6_.reset();
|
||||
AccountingSocketV4_.reset();
|
||||
AccountingSocketV6_.reset();
|
||||
CoASocketV4_.reset();
|
||||
CoASocketV6_.reset();
|
||||
|
||||
StopRADSECServers();
|
||||
RadiusReactor_->stop();
|
||||
StopRADIUSDestinations();
|
||||
RadiusReactor_.stop();
|
||||
RadiusReactorThread_.join();
|
||||
Running_=false;
|
||||
poco_information(Logger(),"Stopped...");
|
||||
Running_ = false;
|
||||
poco_information(Logger(), "Stopped...");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/* inline static bool isRadsec(const GWObjects::RadiusProxyPool &Cfg) {
|
||||
return Cfg.radsecPoolType=="orion" || Cfg.radsecPoolType=="globalreach" || Cfg.radsecPoolType=="radsec";
|
||||
}
|
||||
*/
|
||||
|
||||
void RADIUS_proxy_server::StartRADIUSDestinations() {
|
||||
std::lock_guard G(Mutex_);
|
||||
for (const auto &pool : PoolList_.pools) {
|
||||
if(pool.enabled) {
|
||||
RADIUS_Destinations_[Utils::IPtoInt(pool.poolProxyIp)] =
|
||||
std::make_unique<RADIUS_Destination>(RadiusReactor_, pool);
|
||||
} else {
|
||||
poco_information(Logger(),fmt::format("Pool {} is not enabled.", pool.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::StopRADSECServers() {
|
||||
std::lock_guard G(Mutex_);
|
||||
RADSECservers_.clear();
|
||||
void RADIUS_proxy_server::StopRADIUSDestinations() {
|
||||
std::lock_guard G(Mutex_);
|
||||
RADIUS_Destinations_.clear();
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::OnAccountingSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf) {
|
||||
Poco::Net::SocketAddress Sender;
|
||||
RADIUS::RadiusPacket P;
|
||||
void RADIUS_proxy_server::RouteAndSendAccountingPacket(const std::string &Destination,const std::string &serialNumber, RADIUS::RadiusPacket &P, bool RecomputeAuthenticator, std::string &Secret) {
|
||||
try{
|
||||
|
||||
auto ReceiveSize = pNf->socket().impl()->receiveBytes(P.Buffer(),P.BufferLen());
|
||||
if(ReceiveSize<SMALLEST_RADIUS_PACKET) {
|
||||
poco_warning(Logger(),"Accounting: bad packet received.");
|
||||
return;
|
||||
}
|
||||
P.Evaluate(ReceiveSize);
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if(SerialNumber.empty()) {
|
||||
poco_warning(Logger(),"Accounting: missing serial number.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
// are we sending this to a pool?
|
||||
auto DstParts = Utils::Split(Destination, ':');
|
||||
std::uint32_t DtsIp = Utils::IPtoInt(DstParts[0]);
|
||||
|
||||
poco_debug(Logger(), fmt::format("Accounting Packet received for {}, CalledStationID: {}, CallingStationID:{}",SerialNumber, CalledStationID, CallingStationID));
|
||||
AP_WS_Server()->SendRadiusAccountingData(SerialNumber,P.Buffer(),P.Size());
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
auto DestinationServer = RADIUS_Destinations_.find(DtsIp);
|
||||
if (DestinationServer != end(RADIUS_Destinations_)) {
|
||||
if(Logger().trace()) {
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
auto SessionID = P.ExtractAccountingSessionID();
|
||||
auto MultiSessionID = P.ExtractAccountingMultiSessionID();
|
||||
Logger().trace(
|
||||
fmt::format("{}: Sending Accounting {} bytes to {}. CalledStationID={} CallingStationID={} SessionID={}:{}",
|
||||
serialNumber, P.Size(),
|
||||
DestinationServer->second->Pool().authConfig.servers[0].ip,
|
||||
CalledStationID, CallingStationID, SessionID, MultiSessionID));
|
||||
}
|
||||
if(DestinationServer->second->ServerType()!=GWObjects::RadiusEndpointType::generic) {
|
||||
Secret = DestinationServer->second->Pool().acctConfig.servers[0].secret;
|
||||
if(RecomputeAuthenticator) {
|
||||
P.RecomputeAuthenticator(Secret);
|
||||
}
|
||||
DestinationServer->second->SendData(serialNumber, (const unsigned char *)P.Buffer(),
|
||||
P.Size());
|
||||
} else {
|
||||
DestinationServer->second->SendRadiusDataAcctData(
|
||||
serialNumber, (const unsigned char *)P.Buffer(), P.Size());
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(),
|
||||
fmt::format("Bad RADIUS ACCT Packet from {}. Dropped.", serialNumber));
|
||||
}
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::OnAuthenticationSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf) {
|
||||
Poco::Net::SocketAddress Sender;
|
||||
RADIUS::RadiusPacket P;
|
||||
void store_packet(const std::string &serialNumber, const char *buffer, std::size_t size) {
|
||||
static std::uint64_t pkt=0;
|
||||
|
||||
auto ReceiveSize = pNf->socket().impl()->receiveBytes(P.Buffer(),P.BufferLen());
|
||||
if(ReceiveSize<SMALLEST_RADIUS_PACKET) {
|
||||
poco_warning(Logger(),"Authentication: bad packet received.");
|
||||
return;
|
||||
}
|
||||
P.Evaluate(ReceiveSize);
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if(SerialNumber.empty()) {
|
||||
poco_warning(Logger(),"Authentication: missing serial number.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
std::string filename = MicroServiceDataDirectory() + "/radius." + serialNumber + "." + std::to_string(pkt++) + ".bin";
|
||||
|
||||
poco_debug(Logger(), fmt::format("Authentication Packet received for {}, CalledStationID: {}, CallingStationID:{}",SerialNumber, CalledStationID, CallingStationID));
|
||||
AP_WS_Server()->SendRadiusAuthenticationData(SerialNumber,P.Buffer(),P.Size());
|
||||
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::OnCoASocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf) {
|
||||
Poco::Net::SocketAddress Sender;
|
||||
RADIUS::RadiusPacket P;
|
||||
void RADIUS_proxy_server::SendAccountingData( const std::string &serialNumber,
|
||||
const char *buffer, std::size_t size) {
|
||||
|
||||
auto ReceiveSize = pNf.get()->socket().impl()->receiveBytes(P.Buffer(),P.BufferLen());
|
||||
if(ReceiveSize<SMALLEST_RADIUS_PACKET) {
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
|
||||
poco_debug(Logger(), fmt::format("CoA Packet received for {}, CalledStationID: {}, CallingStationID:{}",SerialNumber, CalledStationID, CallingStationID));
|
||||
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())
|
||||
if (!Continue())
|
||||
return;
|
||||
|
||||
try {
|
||||
RADIUS::RadiusPacket P((unsigned char *)buffer, size);
|
||||
auto Destination = P.ExtractProxyStateDestination();
|
||||
std::string Secret;
|
||||
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) {
|
||||
|
||||
if (!Continue())
|
||||
return;
|
||||
|
||||
try {
|
||||
RADIUS::RadiusPacket P((unsigned char *)buffer, size);
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
std::uint32_t DstIp = P.ExtractProxyStateDestinationIPint();
|
||||
auto DestinationServer = RADIUS_Destinations_.find(DstIp);
|
||||
if (DestinationServer != end(RADIUS_Destinations_)) {
|
||||
if(Logger().trace()) {
|
||||
auto CallingStationID = P.ExtractCallingStationID();
|
||||
auto CalledStationID = P.ExtractCalledStationID();
|
||||
auto SessionID = P.ExtractAccountingSessionID();
|
||||
auto MultiSessionID = P.ExtractAccountingMultiSessionID();
|
||||
Logger().trace(
|
||||
fmt::format("{}: Sending Authentication {} bytes to {}. CalledStationID={} CallingStationID={} SessionID={}:{}",
|
||||
serialNumber, P.Size(),
|
||||
DestinationServer->second->Pool().authConfig.servers[0].ip,
|
||||
CalledStationID, CallingStationID, SessionID, MultiSessionID));
|
||||
}
|
||||
if(DestinationServer->second->ServerType()!=GWObjects::RadiusEndpointType::generic) {
|
||||
DestinationServer->second->SendData(serialNumber,
|
||||
(const unsigned char *)buffer, size);
|
||||
}
|
||||
else {
|
||||
DestinationServer->second->SendRadiusDataAuthData(
|
||||
serialNumber, (const unsigned char *)buffer, size);
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(),
|
||||
fmt::format("Bad RADIUS AUTH Packet from {}. Dropped.", serialNumber));
|
||||
}
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::SendCoAData(const std::string &serialNumber, const char *buffer,
|
||||
std::size_t size) {
|
||||
|
||||
if (!Continue())
|
||||
return;
|
||||
|
||||
try {
|
||||
RADIUS::RadiusPacket P((unsigned char *)buffer, size);
|
||||
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);
|
||||
if (UseRADSEC) {
|
||||
Poco::Net::SocketAddress RSP(FinalDestination.host(), 0);
|
||||
auto DestinationServer = RADSECservers_.find(RSP);
|
||||
if (DestinationServer != end(RADSECservers_)) {
|
||||
std::uint32_t DstIp = P.ExtractProxyStateDestinationIPint();
|
||||
auto DestinationServer = RADIUS_Destinations_.find(DstIp);
|
||||
if (DestinationServer != end(RADIUS_Destinations_)) {
|
||||
poco_trace(Logger(),fmt::format("{}: Sending CoA {} bytes to {}", serialNumber, P.Size(), DestinationServer->second->Pool().coaConfig.servers[0].ip));
|
||||
if(DestinationServer->second->ServerType()!=GWObjects::RadiusEndpointType::generic) {
|
||||
DestinationServer->second->SendData(serialNumber, (const unsigned char *)buffer,
|
||||
size);
|
||||
} else {
|
||||
DestinationServer->second->SendRadiusDataCoAData(
|
||||
serialNumber, (const unsigned char *)buffer, size);
|
||||
}
|
||||
} else {
|
||||
if ((Dst.family() == Poco::Net::SocketAddress::IPv4 &&
|
||||
AccountingSocketV4_ == nullptr) ||
|
||||
(Dst.family() == Poco::Net::SocketAddress::IPv6 &&
|
||||
AccountingSocketV6_ == nullptr)) {
|
||||
poco_debug(
|
||||
Logger(),
|
||||
fmt::format(
|
||||
"ACCT: Trying to use RADIUS GW PROXY but not configured. Device={}",
|
||||
serialNumber));
|
||||
return;
|
||||
}
|
||||
auto AllSent =
|
||||
SendData(Dst.family() == Poco::Net::SocketAddress::IPv4 ? *AccountingSocketV4_
|
||||
: *AccountingSocketV6_,
|
||||
(const unsigned char *)buffer, size, FinalDestination);
|
||||
if (!AllSent)
|
||||
poco_error(Logger(),
|
||||
fmt::format("{}: Could not send Accounting packet packet to {}.",
|
||||
serialNumber, Destination));
|
||||
else
|
||||
poco_debug(Logger(), fmt::format("{}: Sending Accounting Packet to {}, CalledStationID: {}, CallingStationID:{}",
|
||||
serialNumber, FinalDestination.toString(),
|
||||
CalledStationID, CallingStationID));
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(),fmt::format("Bad RADIUS ACCT Packet from {}. Dropped.",serialNumber));
|
||||
poco_warning(Logger(),
|
||||
fmt::format("Bad RADIUS AUTH 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::ParseServerList(const GWObjects::RadiusProxyServerConfig &Config,
|
||||
std::vector<Destination> &V4,
|
||||
std::vector<Destination> &V6, bool setAsDefault,
|
||||
const std::string &poolProxyIp) {
|
||||
uint64_t TotalV4 = 0, TotalV6 = 0;
|
||||
|
||||
void RADIUS_proxy_server::SendAuthenticationData(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();
|
||||
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::auth, Dst, P, UseRADSEC);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
if ((Dst.family() == Poco::Net::SocketAddress::IPv4 &&
|
||||
AuthenticationSocketV4_ == nullptr) ||
|
||||
(Dst.family() == Poco::Net::SocketAddress::IPv6 &&
|
||||
AuthenticationSocketV6_ == nullptr)) {
|
||||
poco_debug(
|
||||
Logger(),
|
||||
fmt::format("AUTH: Trying to use RADIUS GW PROXY but not configured. Device={}",
|
||||
serialNumber));
|
||||
return;
|
||||
}
|
||||
auto AllSent =
|
||||
SendData(Dst.family() == Poco::Net::SocketAddress::IPv4 ? *AuthenticationSocketV4_
|
||||
: *AuthenticationSocketV6_,
|
||||
(const unsigned char *)buffer, size, FinalDestination);
|
||||
if (!AllSent)
|
||||
poco_error(Logger(),
|
||||
fmt::format("{}: Could not send Authentication packet packet to {}.",
|
||||
serialNumber, Destination));
|
||||
else
|
||||
poco_debug(Logger(), fmt::format("{}: Sending Authentication Packet to {}, CalledStationID: {}, CallingStationID:{}",
|
||||
serialNumber, FinalDestination.toString(),
|
||||
CalledStationID, CallingStationID));
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(),fmt::format("Bad RADIUS AUTH Packet from {}. Dropped.",serialNumber));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::SendCoAData(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();
|
||||
|
||||
if(Destination.empty()) {
|
||||
Destination = "0.0.0.0:0";
|
||||
}
|
||||
|
||||
Poco::Net::SocketAddress Dst(Destination);
|
||||
std::lock_guard G(Mutex_);
|
||||
bool UseRADSEC = false;
|
||||
auto FinalDestination = Route(radius_type::coa, Dst, P, UseRADSEC);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
if( (Dst.family() == Poco::Net::SocketAddress::IPv4 && CoASocketV4_== nullptr) ||
|
||||
(Dst.family() == Poco::Net::SocketAddress::IPv6 && CoASocketV6_== nullptr)) {
|
||||
poco_debug(Logger(),fmt::format("CoA: Trying to use RADIUS GW PROXY but not configured. Device={}",serialNumber));
|
||||
return;
|
||||
}
|
||||
auto AllSent = SendData(Dst.family() == Poco::Net::SocketAddress::IPv4 ? *CoASocketV4_
|
||||
: *CoASocketV6_,
|
||||
(const unsigned char *)buffer, size, FinalDestination);
|
||||
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()));
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger(),fmt::format("Bad RADIUS CoA/DM Packet from {}. Dropped.",serialNumber));
|
||||
}
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::ParseServerList(const GWObjects::RadiusProxyServerConfig & Config, std::vector<Destination> &V4, std::vector<Destination> &V6, bool setAsDefault) {
|
||||
uint64_t TotalV4=0, TotalV6=0;
|
||||
|
||||
for(const auto &server:Config.servers) {
|
||||
for (const auto &server : Config.servers) {
|
||||
Poco::Net::IPAddress a;
|
||||
if(!Poco::Net::IPAddress::tryParse(server.ip,a)) {
|
||||
poco_error(Logger(),fmt::format("RADIUS-PARSE Config: server address {} is nto a valid address in v4 or v6. Entry skipped.",server.ip));
|
||||
if (!Poco::Net::IPAddress::tryParse(server.ip, a)) {
|
||||
poco_error(Logger(), fmt::format("RADIUS-PARSE Config: server address {} is nto a "
|
||||
"valid address in v4 or v6. Entry skipped.",
|
||||
server.ip));
|
||||
continue;
|
||||
}
|
||||
auto S = Poco::Net::SocketAddress(fmt::format("{}:{}",server.ip,server.port));
|
||||
Destination D{
|
||||
.Addr = S,
|
||||
.state = 0,
|
||||
.step = 0,
|
||||
.weight = server.weight,
|
||||
.available = true,
|
||||
.strategy = Config.strategy,
|
||||
.monitor = Config. monitor,
|
||||
.monitorMethod = Config.monitorMethod,
|
||||
.methodParameters = Config.methodParameters,
|
||||
.useAsDefault = setAsDefault,
|
||||
.useRADSEC = server.radsec,
|
||||
.realms = server.radsecRealms
|
||||
};
|
||||
auto S = Poco::Net::SocketAddress(fmt::format("{}:{}", server.ip, server.port));
|
||||
Destination D{.Addr = S,
|
||||
.state = 0,
|
||||
.step = 0,
|
||||
.weight = server.weight,
|
||||
.available = true,
|
||||
.strategy = Config.strategy,
|
||||
.monitor = Config.monitor,
|
||||
.monitorMethod = Config.monitorMethod,
|
||||
.methodParameters = Config.methodParameters,
|
||||
.useAsDefault = setAsDefault,
|
||||
.useRADSEC = server.radsec,
|
||||
.realms = server.radsecRealms,
|
||||
.secret = server.secret,
|
||||
.poolProxyIp = poolProxyIp};
|
||||
|
||||
if(setAsDefault && D.useRADSEC)
|
||||
if (setAsDefault && D.useRADSEC)
|
||||
DefaultIsRADSEC_ = true;
|
||||
|
||||
if(S.family()==Poco::Net::IPAddress::IPv4) {
|
||||
if (S.family() == Poco::Net::IPAddress::IPv4) {
|
||||
TotalV4 += server.weight;
|
||||
V4.push_back(D);
|
||||
} else {
|
||||
@@ -408,16 +271,16 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &i:V4) {
|
||||
if(TotalV4==0) {
|
||||
for (auto &i : V4) {
|
||||
if (TotalV4 == 0) {
|
||||
i.step = 1000;
|
||||
} else {
|
||||
i.step = 1000 - ((1000 * i.weight) / TotalV4);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &i:V6) {
|
||||
if(TotalV6==0) {
|
||||
for (auto &i : V6) {
|
||||
if (TotalV6 == 0) {
|
||||
i.step = 1000;
|
||||
} else {
|
||||
i.step = 1000 - ((1000 * i.weight) / TotalV6);
|
||||
@@ -428,60 +291,71 @@ namespace OpenWifi {
|
||||
void RADIUS_proxy_server::ParseConfig() {
|
||||
|
||||
try {
|
||||
Poco::File F(ConfigFilename_);
|
||||
Poco::File F(ConfigFilename_);
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
if(F.exists()) {
|
||||
std::ifstream ifs(ConfigFilename_,std::ios_base::binary);
|
||||
Poco::JSON::Parser P;
|
||||
if (F.exists()) {
|
||||
std::ifstream ifs(ConfigFilename_, std::ios_base::binary);
|
||||
Poco::JSON::Parser P;
|
||||
auto RawConfig = P.parse(ifs).extract<Poco::JSON::Object::Ptr>();
|
||||
GWObjects::RadiusProxyPoolList RPC;
|
||||
if(RPC.from_json(RawConfig)) {
|
||||
GWObjects::RadiusProxyPoolList RPC;
|
||||
if (RPC.from_json(RawConfig)) {
|
||||
ResetConfig();
|
||||
PoolList_ = RPC;
|
||||
for(const auto &pool:RPC.pools) {
|
||||
RadiusPool NewPool;
|
||||
ParseServerList(pool.authConfig, NewPool.AuthV4, NewPool.AuthV6, pool.useByDefault);
|
||||
ParseServerList(pool.acctConfig, NewPool.AcctV4, NewPool.AcctV6, pool.useByDefault);
|
||||
ParseServerList(pool.coaConfig, NewPool.CoaV4, NewPool.CoaV6, pool.useByDefault);
|
||||
for (const auto &pool : RPC.pools) {
|
||||
RadiusPool NewPool;
|
||||
ParseServerList(pool.authConfig, NewPool.AuthV4, NewPool.AuthV6,
|
||||
pool.useByDefault, pool.poolProxyIp);
|
||||
ParseServerList(pool.acctConfig, NewPool.AcctV4, NewPool.AcctV6,
|
||||
pool.useByDefault, pool.poolProxyIp);
|
||||
ParseServerList(pool.coaConfig, NewPool.CoaV4, NewPool.CoaV6,
|
||||
pool.useByDefault, pool.poolProxyIp);
|
||||
Pools_.push_back(NewPool);
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger(),fmt::format("Configuration file '{}' is bad.",ConfigFilename_));
|
||||
poco_warning(Logger(),
|
||||
fmt::format("Configuration file '{}' is bad.", ConfigFilename_));
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger(),fmt::format("No configuration file '{}' exists.",ConfigFilename_));
|
||||
poco_warning(Logger(),
|
||||
fmt::format("No configuration file '{}' exists.", ConfigFilename_));
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger().log(E);
|
||||
} catch (...) {
|
||||
poco_error(Logger(),fmt::format("Error while parsing configuration file '{}'",ConfigFilename_));
|
||||
poco_error(Logger(),
|
||||
fmt::format("Error while parsing configuration file '{}'", ConfigFilename_));
|
||||
}
|
||||
}
|
||||
|
||||
static bool RealmMatch(const std::string &user_realm, const std::string & realm) {
|
||||
if(realm.find_first_of('*') == std::string::npos)
|
||||
/*
|
||||
static bool RealmMatch(const std::string &user_realm, const std::string &realm) {
|
||||
if (realm.find_first_of('*') == std::string::npos)
|
||||
return user_realm == realm;
|
||||
return realm.find(user_realm) != std::string::npos;
|
||||
}
|
||||
|
||||
Poco::Net::SocketAddress RADIUS_proxy_server::DefaultRoute(radius_type rtype, const Poco::Net::SocketAddress &RequestedAddress, const RADIUS::RadiusPacket &P, bool &UseRADSEC) {
|
||||
bool IsV4 = RequestedAddress.family()==Poco::Net::SocketAddress::IPv4;
|
||||
Poco::Net::SocketAddress
|
||||
RADIUS_proxy_server::DefaultRoute(radius_type rtype,
|
||||
const Poco::Net::SocketAddress &RequestedAddress,
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC,
|
||||
std::string &Secret) {
|
||||
|
||||
bool IsV4 = RequestedAddress.family() == Poco::Net::SocketAddress::IPv4;
|
||||
|
||||
// find the realm...
|
||||
auto UserName = P.UserName();
|
||||
if(!UserName.empty()) {
|
||||
if (!UserName.empty()) {
|
||||
auto UserTokens = Poco::StringTokenizer(UserName, "@");
|
||||
auto UserRealm = ((UserTokens.count() > 1) ? UserTokens[1] : UserName);
|
||||
Poco::toLowerInPlace(UserRealm);
|
||||
|
||||
for(const auto &pool:Pools_) {
|
||||
for(const auto &server:pool.AuthV4) {
|
||||
if(!server.realms.empty()) {
|
||||
for(const auto &realm:server.realms) {
|
||||
if (RealmMatch(UserRealm,realm)) {
|
||||
std::cout << "Realm match..." << std::endl;
|
||||
for (const auto &pool : Pools_) {
|
||||
for (const auto &server : pool.AuthV4) {
|
||||
if (!server.realms.empty()) {
|
||||
for (const auto &realm : server.realms) {
|
||||
if (RealmMatch(UserRealm, realm)) {
|
||||
UseRADSEC = true;
|
||||
return server.Addr;
|
||||
}
|
||||
@@ -491,71 +365,86 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
if(DefaultIsRADSEC_) {
|
||||
if (DefaultIsRADSEC_) {
|
||||
UseRADSEC = true;
|
||||
return (IsV4 ? Pools_[DefaultPoolIndex_].AuthV4[0].Addr : Pools_[DefaultPoolIndex_].AuthV6[0].Addr );
|
||||
return (IsV4 ? Pools_[DefaultPoolIndex_].AuthV4[0].Addr
|
||||
: Pools_[DefaultPoolIndex_].AuthV6[0].Addr);
|
||||
}
|
||||
|
||||
switch(rtype) {
|
||||
case radius_type::auth: {
|
||||
return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].AuthV4
|
||||
: Pools_[DefaultPoolIndex_].AuthV6,
|
||||
RequestedAddress);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
switch (rtype) {
|
||||
case radius_type::auth: {
|
||||
return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].AuthV4
|
||||
: Pools_[DefaultPoolIndex_].AuthV6,
|
||||
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, Secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_);
|
||||
Poco::Net::SocketAddress
|
||||
RADIUS_proxy_server::Route([[maybe_unused]] radius_type rtype,
|
||||
const Poco::Net::SocketAddress &RequestedAddress,
|
||||
const RADIUS::RadiusPacket &P, bool &UseRADSEC,
|
||||
std::string &Secret) {
|
||||
|
||||
if(Pools_.empty()) {
|
||||
if (Pools_.empty()) {
|
||||
UseRADSEC = false;
|
||||
return RequestedAddress;
|
||||
}
|
||||
|
||||
bool IsV4 = RequestedAddress.family()==Poco::Net::SocketAddress::IPv4;
|
||||
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);
|
||||
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, Secret);
|
||||
}
|
||||
|
||||
auto isAddressInPool = [&](const std::vector<Destination> & D, bool &UseRADSEC) -> bool {
|
||||
for(const auto &entry:D)
|
||||
if(entry.Addr.host()==RequestedAddress.host()) {
|
||||
auto isAddressInPool = [&](const std::vector<Destination> &D, bool &UseRADSEC) -> bool {
|
||||
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_) {
|
||||
switch(rtype) {
|
||||
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;
|
||||
}
|
||||
@@ -565,10 +454,15 @@ namespace OpenWifi {
|
||||
return RequestedAddress;
|
||||
}
|
||||
|
||||
Poco::Net::SocketAddress RADIUS_proxy_server::ChooseAddress(std::vector<Destination> &Pool, const Poco::Net::SocketAddress & OriginalAddress) {
|
||||
Poco::Net::SocketAddress
|
||||
RADIUS_proxy_server::ChooseAddress(std::vector<Destination> &Pool,
|
||||
const Poco::Net::SocketAddress &OriginalAddress,
|
||||
std::string &Secret) {
|
||||
|
||||
if(Pool.size()==1) {
|
||||
return Pool[0].Addr;
|
||||
if (Pool.size() == 1) {
|
||||
Secret = Pool[0].secret;
|
||||
auto A = Pool[0].Addr;
|
||||
return A;
|
||||
}
|
||||
|
||||
if (Pool[0].strategy == "weighted") {
|
||||
@@ -584,6 +478,7 @@ namespace OpenWifi {
|
||||
index = pos;
|
||||
cur_state = i.state;
|
||||
found = true;
|
||||
Secret = i.secret ;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
@@ -591,9 +486,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();
|
||||
@@ -606,34 +501,37 @@ 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;
|
||||
}
|
||||
}
|
||||
return OriginalAddress;
|
||||
}
|
||||
|
||||
*/
|
||||
void RADIUS_proxy_server::SetConfig(const GWObjects::RadiusProxyPoolList &C) {
|
||||
std::lock_guard G(Mutex_);
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
Poco::JSON::Object Disk;
|
||||
Poco::JSON::Object Disk;
|
||||
C.to_json(Disk);
|
||||
|
||||
std::ofstream ofs(ConfigFilename_, std::ios_base::trunc | std::ios_base::binary );
|
||||
std::ofstream ofs(ConfigFilename_, std::ios_base::trunc | std::ios_base::binary);
|
||||
Disk.stringify(ofs);
|
||||
ofs.close();
|
||||
|
||||
@@ -646,27 +544,26 @@ namespace OpenWifi {
|
||||
void RADIUS_proxy_server::ResetConfig() {
|
||||
PoolList_.pools.clear();
|
||||
Pools_.clear();
|
||||
DefaultPoolIndex_=0;
|
||||
DefaultIsRADSEC_=false;
|
||||
DefaultPoolIndex_ = 0;
|
||||
DefaultIsRADSEC_ = false;
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::DeleteConfig() {
|
||||
std::lock_guard G(Mutex_);
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
try {
|
||||
Poco::File F(ConfigFilename_);
|
||||
if (F.exists())
|
||||
F.remove();
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
Stop();
|
||||
ResetConfig();
|
||||
}
|
||||
|
||||
void RADIUS_proxy_server::GetConfig(GWObjects::RadiusProxyPoolList &C) {
|
||||
std::lock_guard G(Mutex_);
|
||||
std::lock_guard G(Mutex_);
|
||||
C = PoolList_;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -11,19 +11,16 @@
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
|
||||
#include "RADSEC_server.h"
|
||||
#include "RADIUS_Destination.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
enum class radius_type {
|
||||
auth, acct, coa
|
||||
};
|
||||
|
||||
enum class radius_type { auth, acct, coa };
|
||||
|
||||
class RADIUS_proxy_server : public SubSystemServer {
|
||||
public:
|
||||
inline static auto instance() {
|
||||
static auto instance_= new RADIUS_proxy_server;
|
||||
static auto instance_ = new RADIUS_proxy_server;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
@@ -31,86 +28,87 @@ namespace OpenWifi {
|
||||
void Stop() final;
|
||||
inline bool Enabled() const { return Enabled_; }
|
||||
|
||||
void OnAccountingSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf);
|
||||
void OnAuthenticationSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf);
|
||||
void OnCoASocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf);
|
||||
|
||||
void SendAccountingData(const std::string &serialNumber, const char *buffer, std::size_t size);
|
||||
void SendAuthenticationData(const std::string &serialNumber, const char *buffer, std::size_t size);
|
||||
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);
|
||||
|
||||
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();
|
||||
void GetConfig(GWObjects::RadiusProxyPoolList &C);
|
||||
|
||||
void StartRADSECServers();
|
||||
void StopRADSECServers();
|
||||
void StartRADIUSDestinations();
|
||||
void StopRADIUSDestinations();
|
||||
|
||||
struct Destination {
|
||||
Poco::Net::SocketAddress Addr;
|
||||
uint64_t state = 0;
|
||||
uint64_t step = 0;
|
||||
uint64_t weight=0;
|
||||
bool available = true;
|
||||
std::string strategy;
|
||||
bool monitor=false;
|
||||
std::string monitorMethod;
|
||||
std::vector<std::string> methodParameters;
|
||||
bool useAsDefault=false;
|
||||
bool useRADSEC=false;
|
||||
std::vector<std::string> realms;
|
||||
Poco::Net::SocketAddress Addr;
|
||||
uint64_t state = 0;
|
||||
uint64_t step = 0;
|
||||
uint64_t weight = 0;
|
||||
bool available = true;
|
||||
std::string strategy;
|
||||
bool monitor = false;
|
||||
std::string monitorMethod;
|
||||
std::vector<std::string> methodParameters;
|
||||
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();
|
||||
}
|
||||
inline bool Continue() const { return Running_ && Enabled_ && !Pools_.empty(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AccountingSocketV4_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AccountingSocketV6_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AuthenticationSocketV4_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> AuthenticationSocketV6_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> CoASocketV4_;
|
||||
std::unique_ptr<Poco::Net::DatagramSocket> CoASocketV6_;
|
||||
std::unique_ptr<Poco::Net::SocketReactor> RadiusReactor_;
|
||||
Poco::Thread RadiusReactorThread_;
|
||||
Poco::Net::SocketReactor RadiusReactor_;
|
||||
Poco::Thread RadiusReactorThread_;
|
||||
|
||||
GWObjects::RadiusProxyPoolList PoolList_;
|
||||
std::string ConfigFilename_;
|
||||
GWObjects::RadiusProxyPoolList PoolList_;
|
||||
std::string ConfigFilename_;
|
||||
|
||||
std::map<Poco::Net::SocketAddress, std::unique_ptr<RADSEC_server>> RADSECservers_;
|
||||
std::map<std::uint32_t, std::unique_ptr<RADIUS_Destination>> RADIUS_Destinations_;
|
||||
|
||||
struct RadiusPool {
|
||||
std::vector<Destination> AuthV4;
|
||||
std::vector<Destination> AuthV6;
|
||||
std::vector<Destination> AcctV4;
|
||||
std::vector<Destination> AcctV6;
|
||||
std::vector<Destination> CoaV4;
|
||||
std::vector<Destination> CoaV6;
|
||||
std::vector<Destination> AuthV4;
|
||||
std::vector<Destination> AuthV6;
|
||||
std::vector<Destination> AcctV4;
|
||||
std::vector<Destination> AcctV6;
|
||||
std::vector<Destination> CoaV4;
|
||||
std::vector<Destination> CoaV6;
|
||||
};
|
||||
|
||||
std::vector<RadiusPool> Pools_;
|
||||
uint DefaultPoolIndex_=0;
|
||||
bool Enabled_=false;
|
||||
bool DefaultIsRADSEC_=false;
|
||||
std::atomic_bool Running_=false;
|
||||
std::vector<RadiusPool> Pools_;
|
||||
uint DefaultPoolIndex_ = 0;
|
||||
bool Enabled_ = false;
|
||||
bool DefaultIsRADSEC_ = false;
|
||||
std::atomic_bool Running_ = false;
|
||||
|
||||
RADIUS_proxy_server() noexcept:
|
||||
SubSystemServer("RADIUS-PROXY", "RADIUS-PROXY", "radius.proxy")
|
||||
{
|
||||
}
|
||||
RADIUS_proxy_server() noexcept
|
||||
: SubSystemServer("RADIUS-PROXY", "RADIUS-PROXY", "radius.proxy") {}
|
||||
|
||||
static bool SendData( Poco::Net::DatagramSocket & Sock, const unsigned char *buf , std::size_t size, const Poco::Net::SocketAddress &S);
|
||||
static bool SendData(Poco::Net::DatagramSocket &Sock, const unsigned char *buf,
|
||||
std::size_t size, const Poco::Net::SocketAddress &S);
|
||||
|
||||
void ParseConfig();
|
||||
void ResetConfig();
|
||||
Poco::Net::SocketAddress Route(radius_type rtype, const Poco::Net::SocketAddress &A, const RADIUS::RadiusPacket &P, bool &UseRADSEC);
|
||||
void ParseServerList(const GWObjects::RadiusProxyServerConfig & Config, std::vector<Destination> &V4, std::vector<Destination> &V6, bool setAsDefault);
|
||||
static Poco::Net::SocketAddress ChooseAddress(std::vector<Destination> &Pool, const Poco::Net::SocketAddress & OriginalAddress);
|
||||
Poco::Net::SocketAddress DefaultRoute([[maybe_unused]] radius_type rtype, const Poco::Net::SocketAddress &RequestedAddress, const RADIUS::RadiusPacket &P, bool &UseRADSEC);
|
||||
};
|
||||
// Poco::Net::SocketAddress Route(radius_type rtype, const Poco::Net::SocketAddress &A,
|
||||
// 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,
|
||||
const std::string &poolProxyIp);
|
||||
/* static Poco::Net::SocketAddress
|
||||
ChooseAddress(std::vector<Destination> &Pool,
|
||||
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,
|
||||
std::string &Secret);
|
||||
*/ };
|
||||
|
||||
inline auto RADIUS_proxy_server() { return RADIUS_proxy_server::instance(); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-08-15.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "RESTObjects/RESTAPI_GWobjects.h"
|
||||
|
||||
#include "Poco/Net/SocketReactor.h"
|
||||
#include "Poco/Net/SecureStreamSocket.h"
|
||||
#include "Poco/Net/Context.h"
|
||||
#include "Poco/Crypto/X509Certificate.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
#include "Poco/TemporaryFile.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include "RADIUS_helpers.h"
|
||||
#include "AP_WS_Server.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RADSEC_server : public Poco::Runnable {
|
||||
public:
|
||||
RADSEC_server(Poco::Net::SocketReactor & R, GWObjects::RadiusProxyServerEntry E) :
|
||||
Reactor_(R),
|
||||
Server_(std::move(E)),
|
||||
Logger_(Poco::Logger::get(fmt::format("RADSEC: {}@{}:{}",
|
||||
Server_.name ,
|
||||
Server_.ip,
|
||||
Server_.port))) {
|
||||
Start();
|
||||
}
|
||||
|
||||
~RADSEC_server() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
inline int Start() {
|
||||
ReconnectThread_.start(*this);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void Stop() {
|
||||
TryAgain_ = false;
|
||||
Disconnect();
|
||||
ReconnectThread_.wakeUp();
|
||||
ReconnectThread_.join();
|
||||
}
|
||||
|
||||
inline void run() final {
|
||||
Poco::Thread::trySleep(3000);
|
||||
std::uint64_t LastStatus=0 ;
|
||||
auto RadSecKeepAlive = MicroServiceConfigGetInt("radsec.keepalive",120);
|
||||
while(TryAgain_) {
|
||||
if(!Connected_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
LastStatus = Utils::Now() ;
|
||||
Connect();
|
||||
} else if( (Utils::Now() - LastStatus) > RadSecKeepAlive) {
|
||||
RADIUS::RadiusOutputPacket P(Server_.radsecSecret);
|
||||
P.MakeStatusMessage();
|
||||
poco_information(Logger_,"Keep-Alive message.");
|
||||
Socket_->sendBytes(P.Data(), P.Len());
|
||||
LastStatus = Utils::Now();
|
||||
}
|
||||
Poco::Thread::trySleep(!Connected_ ? 3000 : 10000);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool SendData(const std::string &serial_number, const unsigned char *buffer, int length) {
|
||||
try {
|
||||
if (Connected_) {
|
||||
RADIUS::RadiusPacket P(buffer, length);
|
||||
int sent_bytes;
|
||||
if (P.VerifyMessageAuthenticator(Server_.radsecSecret)) {
|
||||
poco_debug(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,
|
||||
P.PacketType(), length));
|
||||
P.ComputeMessageAuthenticator(Server_.radsecSecret);
|
||||
sent_bytes = Socket_->sendBytes(P.Buffer(), length);
|
||||
}
|
||||
return (sent_bytes == length);
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_warning(Logger_,"Exception occurred: while sending data.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void onData([[maybe_unused]] const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf) {
|
||||
unsigned char Buffer[4096];
|
||||
|
||||
try {
|
||||
auto NumberOfReceivedBytes = Socket_->receiveBytes(Buffer,sizeof(Buffer));
|
||||
if(NumberOfReceivedBytes>=20) {
|
||||
RADIUS::RadiusPacket P(Buffer,NumberOfReceivedBytes);
|
||||
if (P.IsAuthentication()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if(!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
fmt::format("{}: {} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusAuthenticationData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "AUTH packet dropped.");
|
||||
}
|
||||
} else if (P.IsAccounting()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberFromProxyState();
|
||||
if(!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
fmt::format("{}: {} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusAccountingData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "ACCT packet dropped.");
|
||||
}
|
||||
} else if (P.IsAuthority()) {
|
||||
auto SerialNumber = P.ExtractSerialNumberTIP();
|
||||
if(!SerialNumber.empty()) {
|
||||
poco_debug(Logger_,
|
||||
fmt::format("{}: {} Received {} bytes.", SerialNumber,
|
||||
P.PacketType(), NumberOfReceivedBytes));
|
||||
AP_WS_Server()->SendRadiusCoAData(SerialNumber, Buffer,
|
||||
NumberOfReceivedBytes);
|
||||
} else {
|
||||
poco_debug(Logger_, "CoA/DM packet dropped.");
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger_,fmt::format("Unknown packet: Type: {} (type={}) Length={}", P.PacketType(), P.PacketTypeInt(), P.BufferLen()));
|
||||
}
|
||||
} else {
|
||||
poco_warning(Logger_,"Invalid packet received. Resetting the connection.");
|
||||
Disconnect();
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
Disconnect();
|
||||
} catch (...) {
|
||||
Disconnect();
|
||||
poco_warning(Logger_,"Exception occurred. Resetting the connection.");
|
||||
}
|
||||
}
|
||||
|
||||
inline void onError([[maybe_unused]] const Poco::AutoPtr<Poco::Net::ErrorNotification>& pNf) {
|
||||
poco_warning(Logger_,"Socker error. Terminating connection.");
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
inline void onShutdown([[maybe_unused]] const Poco::AutoPtr<Poco::Net::ShutdownNotification>& pNf) {
|
||||
poco_warning(Logger_,"Socker socket shutdown. Terminating connection.");
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
inline bool Connect() {
|
||||
if(TryAgain_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
|
||||
Poco::TemporaryFile CertFile_(MicroServiceDataDirectory());
|
||||
Poco::TemporaryFile KeyFile_(MicroServiceDataDirectory());
|
||||
std::vector<std::unique_ptr<Poco::TemporaryFile>> CaCertFiles_;
|
||||
|
||||
DecodeFile(CertFile_.path(), Server_.radsecCert);
|
||||
DecodeFile(KeyFile_.path(), Server_.radsecKey);
|
||||
|
||||
for(auto &cert:Server_.radsecCacerts) {
|
||||
CaCertFiles_.emplace_back(std::make_unique<Poco::TemporaryFile>(MicroServiceDataDirectory()));
|
||||
DecodeFile(CaCertFiles_[CaCertFiles_.size()-1]->path(), cert);
|
||||
}
|
||||
|
||||
Poco::Net::Context::Ptr SecureContext = Poco::AutoPtr<Poco::Net::Context>(
|
||||
new Poco::Net::Context(Poco::Net::Context::TLS_CLIENT_USE,
|
||||
KeyFile_.path(),
|
||||
CertFile_.path(),""));
|
||||
if(Server_.allowSelfSigned) {
|
||||
SecureContext->setSecurityLevel(Poco::Net::Context::SECURITY_LEVEL_NONE);
|
||||
SecureContext->enableExtendedCertificateVerification(false);
|
||||
}
|
||||
|
||||
for(const auto &ca:CaCertFiles_) {
|
||||
Poco::Crypto::X509Certificate cert(ca->path());
|
||||
SecureContext->addCertificateAuthority(cert);
|
||||
}
|
||||
|
||||
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(100, 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_information(Logger_,"Could not connect.");
|
||||
Logger_.log(E);
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_information(Logger_,"Could not connect.");
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_information(Logger_,"Could not connect.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void Disconnect() {
|
||||
if(Connected_) {
|
||||
std::lock_guard G(LocalMutex_);
|
||||
|
||||
Reactor_.removeEventHandler(
|
||||
*Socket_, Poco::NObserver<RADSEC_server, Poco::Net::ReadableNotification>(
|
||||
*this, &RADSEC_server::onData));
|
||||
Reactor_.removeEventHandler(
|
||||
*Socket_, Poco::NObserver<RADSEC_server, Poco::Net::ErrorNotification>(
|
||||
*this, &RADSEC_server::onError));
|
||||
Reactor_.removeEventHandler(
|
||||
*Socket_, Poco::NObserver<RADSEC_server, Poco::Net::ShutdownNotification>(
|
||||
*this, &RADSEC_server::onShutdown));
|
||||
Socket_->close();
|
||||
Connected_ = false;
|
||||
}
|
||||
poco_information(Logger_,"Disconnecting.");
|
||||
}
|
||||
|
||||
static void DecodeFile(const std::string &filename, const std::string &s) {
|
||||
std::ofstream sec_file(filename,std::ios_base::out|std::ios_base::trunc|std::ios_base::binary);
|
||||
std::stringstream is(s);
|
||||
Poco::Base64Decoder ds(is);
|
||||
Poco::StreamCopier::copyStream(ds,sec_file);
|
||||
sec_file.close();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string CommonName() {
|
||||
if(Peer_Cert_)
|
||||
return Peer_Cert_->commonName();
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string IssuerName() {
|
||||
if(Peer_Cert_)
|
||||
return Peer_Cert_->issuerName();
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string SubjectName() {
|
||||
if(Peer_Cert_)
|
||||
return Peer_Cert_->subjectName();
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
std::recursive_mutex LocalMutex_;
|
||||
Poco::Net::SocketReactor &Reactor_;
|
||||
GWObjects::RadiusProxyServerEntry Server_;
|
||||
Poco::Logger &Logger_;
|
||||
std::unique_ptr<Poco::Net::SecureStreamSocket> Socket_;
|
||||
Poco::Thread ReconnectThread_;
|
||||
std::unique_ptr<Poco::Crypto::X509Certificate> Peer_Cert_;
|
||||
volatile bool Connected_=false;
|
||||
volatile bool TryAgain_=true;
|
||||
};
|
||||
}
|
||||
@@ -1,128 +1,172 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-06-28.
|
||||
//
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
#include "RESTAPI_RPC.h"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <iterator>
|
||||
|
||||
#include "CommandManager.h"
|
||||
#include "AP_WS_Server.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "CommandManager.h"
|
||||
#include "ParseWifiScan.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/utils.h"
|
||||
#include <GWKafkaEvents.h>
|
||||
|
||||
namespace OpenWifi::RESTAPI_RPC {
|
||||
void SetCommandStatus(GWObjects::CommandDetails &Cmd,
|
||||
[[maybe_unused]] Poco::Net::HTTPServerRequest &Request,
|
||||
[[maybe_unused]] Poco::Net::HTTPServerResponse &Response,
|
||||
RESTAPIHandler *Handler,
|
||||
OpenWifi::Storage::CommandExecutionType Status,
|
||||
[[maybe_unused]] Poco::Logger &Logger) {
|
||||
[[maybe_unused]] Poco::Net::HTTPServerRequest &Request,
|
||||
[[maybe_unused]] Poco::Net::HTTPServerResponse &Response,
|
||||
RESTAPIHandler *Handler, OpenWifi::Storage::CommandExecutionType Status,
|
||||
[[maybe_unused]] Poco::Logger &Logger) {
|
||||
if (StorageService()->AddCommand(Cmd.SerialNumber, Cmd, Status)) {
|
||||
Poco::JSON::Object RetObj;
|
||||
Cmd.to_json(RetObj);
|
||||
if(Handler!= nullptr)
|
||||
return Handler->ReturnObject(RetObj);
|
||||
return;
|
||||
if (Handler == nullptr) {
|
||||
// nothing to process/return
|
||||
return;
|
||||
}
|
||||
Poco::Net::HTTPResponse::HTTPStatus cmd_status = Poco::Net::HTTPResponse::HTTP_OK;
|
||||
if (Cmd.ErrorCode > 0) {
|
||||
// command returned error
|
||||
cmd_status = Poco::Net::HTTPResponse::HTTP_BAD_REQUEST;
|
||||
if (Cmd.Command == uCentralProtocol::CONFIGURE) {
|
||||
// special handling for configure command
|
||||
if (!Handler->GetBoolParameter("strict", false)) {
|
||||
// in non-strict mode return success for failed configure command
|
||||
cmd_status = Poco::Net::HTTPResponse::HTTP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Handler->ReturnObject(RetObj, cmd_status);
|
||||
}
|
||||
if(Handler!= nullptr)
|
||||
if (Handler != nullptr)
|
||||
return Handler->ReturnStatus(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
void WaitForCommand(uint64_t RPCID,
|
||||
APCommands::Commands Command,
|
||||
bool RetryLater,
|
||||
GWObjects::CommandDetails &Cmd,
|
||||
Poco::JSON::Object & Params,
|
||||
void WaitForCommand(uint64_t RPCID, APCommands::Commands Command, bool RetryLater,
|
||||
GWObjects::CommandDetails &Cmd, Poco::JSON::Object &Params,
|
||||
Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response,
|
||||
std::chrono::milliseconds WaitTimeInMs,
|
||||
Poco::JSON::Object * ObjectToReturn,
|
||||
RESTAPIHandler * Handler,
|
||||
Poco::Logger &Logger) {
|
||||
std::chrono::milliseconds WaitTimeInMs, Poco::JSON::Object *ObjectToReturn,
|
||||
RESTAPIHandler *Handler, Poco::Logger &Logger, bool Deferred) {
|
||||
|
||||
Logger.information(fmt::format("{},{}: New {} command. User={} Serial={}. ", Cmd.UUID, RPCID, Cmd.Command, Cmd.SubmittedBy, Cmd.SerialNumber));
|
||||
Logger.information(fmt::format("{},{}: New {} command. User={} Serial={} Details={}. ", Cmd.UUID,
|
||||
RPCID, Cmd.Command, Cmd.SubmittedBy, Cmd.SerialNumber, Cmd.Details));
|
||||
Cmd.Submitted = Utils::Now();
|
||||
Cmd.Executed = 0;
|
||||
|
||||
// if the command should be executed in the future, or if the device is not connected,
|
||||
// then we should just add the command to
|
||||
// the DB and let it figure out when to deliver the command.
|
||||
auto SerialNumberInt = Utils::SerialNumberToInt(Cmd.SerialNumber);
|
||||
if (Cmd.RunAt || (!AP_WS_Server()->Connected(SerialNumberInt) && RetryLater)) {
|
||||
Logger.information(fmt::format("{},{}: Command will be run in the future or when device is connected again.", Cmd.UUID, RPCID));
|
||||
SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_PENDING, Logger);
|
||||
Logger.information(fmt::format(
|
||||
"{},{}: Command will be run in the future or when device is connected again.",
|
||||
Cmd.UUID, RPCID));
|
||||
SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_PENDING, Logger);
|
||||
return;
|
||||
} else if ((!AP_WS_Server()->Connected(SerialNumberInt) && !RetryLater)){
|
||||
Logger.information(fmt::format("{},{}: Command canceled. Device is not connected. Command will not be retried.", Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
} else if ((!AP_WS_Server()->Connected(SerialNumberInt) && !RetryLater)) {
|
||||
Logger.information(fmt::format(
|
||||
"{},{}: Command canceled. Device is not connected. Command will not be retried.",
|
||||
Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
}
|
||||
|
||||
bool Sent;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> rpc_submitted =
|
||||
std::chrono::high_resolution_clock::now();
|
||||
std::shared_ptr<CommandManager::promise_type_t> rpc_endpoint =
|
||||
CommandManager()->PostCommand(RPCID, Command, Cmd.SerialNumber, Cmd.Command, Params,
|
||||
Cmd.UUID, Sent, true, Deferred);
|
||||
|
||||
if (RetryLater && (!Sent || rpc_endpoint == nullptr)) {
|
||||
Logger.information(fmt::format("{},{}: Pending completion. Device is not connected.",
|
||||
Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_PENDING, Logger);
|
||||
}
|
||||
|
||||
if (!RetryLater && !Sent) {
|
||||
Logger.information(fmt::format(
|
||||
"{},{}: Command canceled. Device is not connected. Command will not be retried.",
|
||||
Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
}
|
||||
|
||||
Cmd.Executed = Utils::Now();
|
||||
|
||||
bool Sent;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> rpc_submitted = std::chrono::high_resolution_clock::now();
|
||||
std::shared_ptr<CommandManager::promise_type_t> rpc_endpoint =
|
||||
CommandManager()->PostCommand(RPCID, Command, Cmd.SerialNumber, Cmd.Command, Params, Cmd.UUID, Sent);
|
||||
|
||||
if(RetryLater && (!Sent || rpc_endpoint== nullptr)) {
|
||||
Logger.information(fmt::format("{},{}: Pending completion. Device is not connected.", Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_PENDING, Logger);
|
||||
}
|
||||
|
||||
if(!RetryLater && !Sent) {
|
||||
Logger.information(fmt::format("{},{}: Command canceled. Device is not connected. Command will not be retried.", Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
}
|
||||
|
||||
Logger.information(fmt::format("{},{}: Command sent.", Cmd.UUID, RPCID));
|
||||
std::future<CommandManager::objtype_t> rpc_future(rpc_endpoint->get_future());
|
||||
auto rpc_result = rpc_future.wait_for(WaitTimeInMs);
|
||||
if (rpc_result == std::future_status::ready) {
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time = std::chrono::high_resolution_clock::now() - rpc_submitted;
|
||||
std::chrono::duration<double, std::milli> rpc_execution_time =
|
||||
std::chrono::high_resolution_clock::now() - rpc_submitted;
|
||||
auto rpc_answer = rpc_future.get();
|
||||
if (!rpc_answer->has(uCentralProtocol::RESULT) || !rpc_answer->isObject(uCentralProtocol::RESULT)) {
|
||||
SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
Logger.information(fmt::format("{},{}: Invalid response. Missing result.", Cmd.UUID, RPCID));
|
||||
if (!rpc_answer->has(uCentralProtocol::RESULT) ||
|
||||
!rpc_answer->isObject(uCentralProtocol::RESULT)) {
|
||||
SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
Logger.information(
|
||||
fmt::format("{},{}: Invalid response. Missing result.", Cmd.UUID, RPCID));
|
||||
return;
|
||||
}
|
||||
|
||||
auto ResultFields = rpc_answer->get(uCentralProtocol::RESULT).extract<Poco::JSON::Object::Ptr>();
|
||||
if (!ResultFields->has(uCentralProtocol::STATUS) || !ResultFields->isObject(uCentralProtocol::STATUS)) {
|
||||
auto ResultFields =
|
||||
rpc_answer->get(uCentralProtocol::RESULT).extract<Poco::JSON::Object::Ptr>();
|
||||
if (!ResultFields->has(uCentralProtocol::STATUS) ||
|
||||
!ResultFields->isObject(uCentralProtocol::STATUS)) {
|
||||
Cmd.executionTime = rpc_execution_time.count();
|
||||
if(Cmd.Command=="ping") {
|
||||
SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_COMPLETED, Logger);
|
||||
Logger.information(fmt::format("{},{}: Invalid response from device (ping: fix override). Missing status.", Cmd.UUID, RPCID));
|
||||
if (Cmd.Command == "ping") {
|
||||
SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_COMPLETED, Logger);
|
||||
Logger.information(fmt::format(
|
||||
"{},{}: Invalid response from device (ping: fix override). Missing status.",
|
||||
Cmd.UUID, RPCID));
|
||||
} else {
|
||||
SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
Logger.information(fmt::format("{},{}: Invalid response from device. Missing status.", Cmd.UUID,RPCID));
|
||||
SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
Logger.information(fmt::format(
|
||||
"{},{}: Invalid response from device. Missing status.", Cmd.UUID, RPCID));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto StatusInnerObj = ResultFields->get(uCentralProtocol::STATUS).extract<Poco::JSON::Object::Ptr>();
|
||||
std::ostringstream ResultFieldsLog;
|
||||
ResultFields->stringify(ResultFieldsLog);
|
||||
Logger.debug(
|
||||
fmt::format("{},{}: RPC response: {}.", Cmd.UUID, RPCID, ResultFieldsLog.str()));
|
||||
|
||||
auto StatusInnerObj =
|
||||
ResultFields->get(uCentralProtocol::STATUS).extract<Poco::JSON::Object::Ptr>();
|
||||
if (StatusInnerObj->has(uCentralProtocol::ERROR))
|
||||
Cmd.ErrorCode = StatusInnerObj->get(uCentralProtocol::ERROR);
|
||||
if (StatusInnerObj->has(uCentralProtocol::TEXT))
|
||||
Cmd.ErrorText = StatusInnerObj->get(uCentralProtocol::TEXT).toString();
|
||||
std::stringstream ResultText;
|
||||
if(rpc_answer->has(uCentralProtocol::RESULT)) {
|
||||
if(Cmd.Command==uCentralProtocol::WIFISCAN) {
|
||||
auto ScanObj = rpc_answer->get(uCentralProtocol::RESULT).extract<Poco::JSON::Object::Ptr>();
|
||||
if (rpc_answer->has(uCentralProtocol::RESULT)) {
|
||||
if (Cmd.Command == uCentralProtocol::WIFISCAN) {
|
||||
auto ScanObj = rpc_answer->get(uCentralProtocol::RESULT)
|
||||
.extract<Poco::JSON::Object::Ptr>();
|
||||
ParseWifiScan(ScanObj, ResultText, Logger);
|
||||
} else {
|
||||
Poco::JSON::Stringifier::stringify(
|
||||
rpc_answer->get(uCentralProtocol::RESULT), ResultText);
|
||||
Poco::JSON::Stringifier::stringify(rpc_answer->get(uCentralProtocol::RESULT),
|
||||
ResultText);
|
||||
}
|
||||
} if (rpc_answer->has(uCentralProtocol::RESULT_64)) {
|
||||
uint64_t sz=0;
|
||||
if(rpc_answer->has(uCentralProtocol::RESULT_SZ))
|
||||
sz=rpc_answer->get(uCentralProtocol::RESULT_SZ);
|
||||
}
|
||||
if (rpc_answer->has(uCentralProtocol::RESULT_64)) {
|
||||
uint64_t sz = 0;
|
||||
if (rpc_answer->has(uCentralProtocol::RESULT_SZ))
|
||||
sz = rpc_answer->get(uCentralProtocol::RESULT_SZ);
|
||||
std::string UnCompressedData;
|
||||
Utils::ExtractBase64CompressedData(rpc_answer->get(uCentralProtocol::RESULT_64).toString(),
|
||||
UnCompressedData,sz);
|
||||
Utils::ExtractBase64CompressedData(
|
||||
rpc_answer->get(uCentralProtocol::RESULT_64).toString(), UnCompressedData, sz);
|
||||
Poco::JSON::Stringifier::stringify(UnCompressedData, ResultText);
|
||||
}
|
||||
Cmd.Results = ResultText.str();
|
||||
@@ -130,33 +174,66 @@ namespace OpenWifi::RESTAPI_RPC {
|
||||
Cmd.Completed = Utils::Now();
|
||||
Cmd.executionTime = rpc_execution_time.count();
|
||||
|
||||
if (Cmd.ErrorCode && (Cmd.Command == uCentralProtocol::TRACE || Cmd.Command == uCentralProtocol::SCRIPT)) {
|
||||
if (Cmd.ErrorCode && (Cmd.Command == uCentralProtocol::TRACE ||
|
||||
Cmd.Command == uCentralProtocol::SCRIPT)) {
|
||||
Cmd.WaitingForFile = 0;
|
||||
Cmd.AttachDate = Cmd.AttachSize = 0;
|
||||
Cmd.AttachType = "";
|
||||
}
|
||||
|
||||
// If the command fails on the device we should show it as failed and not return 200 OK
|
||||
// exception is configure command which only reported failed in strict validation mode
|
||||
if (Cmd.ErrorCode &&
|
||||
(Cmd.Command != uCentralProtocol::CONFIGURE ||
|
||||
(Cmd.Command == uCentralProtocol::CONFIGURE && Handler->GetBoolParameter("strict", false))
|
||||
))
|
||||
{
|
||||
Logger.information(fmt::format(
|
||||
"Command failed with error on device: {} Reason: {}.",
|
||||
Cmd.ErrorCode, Cmd.ErrorText));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
}
|
||||
|
||||
if (Cmd.ErrorCode == 0 && Cmd.Command == uCentralProtocol::CONFIGURE) {
|
||||
// we need to post a kafka event for this.
|
||||
if (Params.has(uCentralProtocol::CONFIG) && Params.isObject(uCentralProtocol::CONFIG)) {
|
||||
auto Config = Params.get(uCentralProtocol::CONFIG)
|
||||
.extract<Poco::JSON::Object::Ptr>();
|
||||
DeviceConfigurationChangeKafkaEvent KEvent(
|
||||
Utils::SerialNumberToInt(Cmd.SerialNumber), Utils::Now(),
|
||||
Config);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Add the completed command to the database...
|
||||
StorageService()->AddCommand(Cmd.SerialNumber, Cmd, Storage::CommandExecutionType::COMMAND_COMPLETED);
|
||||
StorageService()->AddCommand(Cmd.SerialNumber, Cmd,
|
||||
Storage::CommandExecutionType::COMMAND_COMPLETED);
|
||||
|
||||
if (ObjectToReturn && Handler) {
|
||||
Handler->ReturnObject(*ObjectToReturn);
|
||||
} else {
|
||||
Poco::JSON::Object O;
|
||||
Cmd.to_json(O);
|
||||
if(Handler)
|
||||
if (Handler)
|
||||
Handler->ReturnObject(O);
|
||||
}
|
||||
Logger.information( fmt::format("{},{}: Completed in {:.3f}ms.", Cmd.UUID, RPCID, Cmd.executionTime));
|
||||
Logger.information(
|
||||
fmt::format("{},{}: Completed in {:.3f}ms.", Cmd.UUID, RPCID, Cmd.executionTime));
|
||||
return;
|
||||
}
|
||||
CommandManager()->RemovePendingCommand(RPCID);
|
||||
if(RetryLater) {
|
||||
if (RetryLater) {
|
||||
Logger.information(fmt::format("{},{}: Pending completion.", Cmd.UUID, RPCID));
|
||||
SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_PENDING, Logger);
|
||||
SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_PENDING, Logger);
|
||||
} else {
|
||||
Logger.information(fmt::format("{},{}: Command canceled. Device is not connected. Command will not be retried.", Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler, Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
Logger.information(fmt::format(
|
||||
"{},{}: Command canceled. Device is not connected. Command will not be retried.",
|
||||
Cmd.UUID, RPCID));
|
||||
return SetCommandStatus(Cmd, Request, Response, Handler,
|
||||
Storage::CommandExecutionType::COMMAND_FAILED, Logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi::RESTAPI_RPC
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Poco/URI.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/Net/HTTPRequestHandler.h"
|
||||
#include "Poco/Net/HTTPRequestHandlerFactory.h"
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/File.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/URI.h"
|
||||
|
||||
#include "RESTObjects/RESTAPI_GWobjects.h"
|
||||
#include "StorageService.h"
|
||||
@@ -22,23 +22,15 @@
|
||||
|
||||
namespace OpenWifi::RESTAPI_RPC {
|
||||
|
||||
void WaitForCommand(
|
||||
uint64_t RPCID,
|
||||
APCommands::Commands Command,
|
||||
bool RetryLater,
|
||||
GWObjects::CommandDetails &Cmd,
|
||||
Poco::JSON::Object & Params,
|
||||
Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response,
|
||||
std::chrono::milliseconds WaitTimeInMs,
|
||||
Poco::JSON::Object * ObjectToReturn,
|
||||
RESTAPIHandler * Handler,
|
||||
Poco::Logger &Logger);
|
||||
void WaitForCommand(uint64_t RPCID, APCommands::Commands Command, bool RetryLater,
|
||||
GWObjects::CommandDetails &Cmd, Poco::JSON::Object &Params,
|
||||
Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response,
|
||||
std::chrono::milliseconds WaitTimeInMs, Poco::JSON::Object *ObjectToReturn,
|
||||
RESTAPIHandler *Handler, Poco::Logger &Logger, bool Deferred = false);
|
||||
|
||||
void SetCommandStatus( GWObjects::CommandDetails &Cmd,
|
||||
Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response, RESTAPIHandler * handler,
|
||||
OpenWifi::Storage::CommandExecutionType Status,
|
||||
Poco::Logger &Logger);
|
||||
void SetCommandStatus(GWObjects::CommandDetails &Cmd, Poco::Net::HTTPServerRequest &Request,
|
||||
Poco::Net::HTTPServerResponse &Response, RESTAPIHandler *handler,
|
||||
OpenWifi::Storage::CommandExecutionType Status, Poco::Logger &Logger);
|
||||
|
||||
}
|
||||
} // namespace OpenWifi::RESTAPI_RPC
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "RESTAPI_blacklist.h"
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/utils.h"
|
||||
@@ -16,14 +16,14 @@ namespace OpenWifi {
|
||||
void RESTAPI_blacklist::DoDelete() {
|
||||
auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
|
||||
|
||||
if(!Utils::NormalizeMac(SerialNumber)) {
|
||||
if (!Utils::NormalizeMac(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
poco_debug(Logger(),fmt::format("BLACKLIST-DELETE: {}", SerialNumber));
|
||||
poco_debug(Logger(), fmt::format("BLACKLIST-DELETE: {}", SerialNumber));
|
||||
|
||||
GWObjects::BlackListedDevice D;
|
||||
if(!StorageService()->GetBlackListDevice(SerialNumber, D)) {
|
||||
GWObjects::BlackListedDevice D;
|
||||
if (!StorageService()->GetBlackListDevice(SerialNumber, D)) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
@@ -36,13 +36,13 @@ namespace OpenWifi {
|
||||
void RESTAPI_blacklist::DoGet() {
|
||||
auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
|
||||
|
||||
if(!Utils::NormalizeMac(SerialNumber)) {
|
||||
if (!Utils::NormalizeMac(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
poco_debug(Logger(),fmt::format("BLACKLIST-GET: {}", SerialNumber));
|
||||
GWObjects::BlackListedDevice D;
|
||||
if(!StorageService()->GetBlackListDevice(SerialNumber, D)) {
|
||||
poco_debug(Logger(), fmt::format("BLACKLIST-GET: {}", SerialNumber));
|
||||
GWObjects::BlackListedDevice D;
|
||||
if (!StorageService()->GetBlackListDevice(SerialNumber, D)) {
|
||||
return NotFound();
|
||||
}
|
||||
return Object(D);
|
||||
@@ -51,28 +51,28 @@ namespace OpenWifi {
|
||||
void RESTAPI_blacklist::DoPost() {
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
GWObjects::BlackListedDevice D;
|
||||
if(!D.from_json(Obj)) {
|
||||
GWObjects::BlackListedDevice D;
|
||||
if (!D.from_json(Obj)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
if(D.serialNumber.empty() || !Utils::NormalizeMac(D.serialNumber)) {
|
||||
if (D.serialNumber.empty() || !Utils::NormalizeMac(D.serialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
poco_debug(Logger(),fmt::format("BLACKLIST-POST: {}", D.serialNumber));
|
||||
poco_debug(Logger(), fmt::format("BLACKLIST-POST: {}", D.serialNumber));
|
||||
|
||||
Poco::toLowerInPlace(D.serialNumber);
|
||||
if(StorageService()->IsBlackListed(D.serialNumber)) {
|
||||
if (StorageService()->IsBlackListed(Utils::MACToInt(D.serialNumber))) {
|
||||
return BadRequest(RESTAPI::Errors::SerialNumberExists);
|
||||
}
|
||||
|
||||
D.author = UserInfo_.userinfo.email;
|
||||
D.created = Utils::Now();
|
||||
|
||||
if(StorageService()->AddBlackListDevice(D)) {
|
||||
GWObjects::BlackListedDevice CreatedDevice;
|
||||
StorageService()->GetBlackListDevice(D.serialNumber,CreatedDevice);
|
||||
if (StorageService()->AddBlackListDevice(D)) {
|
||||
GWObjects::BlackListedDevice CreatedDevice;
|
||||
StorageService()->GetBlackListDevice(D.serialNumber, CreatedDevice);
|
||||
return Object(CreatedDevice);
|
||||
}
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
@@ -80,33 +80,33 @@ namespace OpenWifi {
|
||||
|
||||
void RESTAPI_blacklist::DoPut() {
|
||||
auto SerialNumber = Poco::toLower(GetBinding(RESTAPI::Protocol::SERIALNUMBER, ""));
|
||||
if(!Utils::NormalizeMac(SerialNumber)) {
|
||||
if (!Utils::NormalizeMac(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
GWObjects::BlackListedDevice Existing;
|
||||
if(!StorageService()->GetBlackListDevice(SerialNumber, Existing)) {
|
||||
GWObjects::BlackListedDevice Existing;
|
||||
if (!StorageService()->GetBlackListDevice(SerialNumber, Existing)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
poco_debug(Logger(),fmt::format("BLACKLIST-PUT: {}", SerialNumber));
|
||||
poco_debug(Logger(), fmt::format("BLACKLIST-PUT: {}", SerialNumber));
|
||||
|
||||
GWObjects::BlackListedDevice NewDevice;
|
||||
if(!NewDevice.from_json(Obj)) {
|
||||
GWObjects::BlackListedDevice NewDevice;
|
||||
if (!NewDevice.from_json(Obj)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
Existing.reason = NewDevice.reason;
|
||||
Existing.author = UserInfo_.userinfo.email;
|
||||
|
||||
if(StorageService()->UpdateBlackListDevice(SerialNumber, Existing)) {
|
||||
GWObjects::BlackListedDevice CreatedDevice;
|
||||
if (StorageService()->UpdateBlackListDevice(SerialNumber, Existing)) {
|
||||
GWObjects::BlackListedDevice CreatedDevice;
|
||||
|
||||
StorageService()->GetBlackListDevice(SerialNumber,CreatedDevice);
|
||||
StorageService()->GetBlackListDevice(SerialNumber, CreatedDevice);
|
||||
return Object(CreatedDevice);
|
||||
}
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -13,20 +13,22 @@
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_blacklist : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_blacklist(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server , uint64_t TransactionId , bool Internal)
|
||||
RESTAPI_blacklist(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/blacklist/{serialNumber}"};}
|
||||
Server, TransactionId, Internal) {}
|
||||
static auto PathName() {
|
||||
return std::list<std::string>{"/api/v1/blacklist/{serialNumber}"};
|
||||
}
|
||||
void DoGet() final;
|
||||
void DoDelete() final;
|
||||
void DoPost() final;
|
||||
void DoPut() final;
|
||||
};
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -10,16 +10,16 @@
|
||||
namespace OpenWifi {
|
||||
void RESTAPI_blacklist_list::DoGet() {
|
||||
|
||||
poco_debug(Logger(),fmt::format("BLACKLIST-GET: Device serial number list"));
|
||||
poco_debug(Logger(), fmt::format("BLACKLIST-GET: Device serial number list"));
|
||||
|
||||
std::vector<GWObjects::BlackListedDevice> Devices;
|
||||
std::vector<GWObjects::BlackListedDevice> Devices;
|
||||
|
||||
if(QB_.CountOnly) {
|
||||
if (QB_.CountOnly) {
|
||||
auto Count = StorageService()->GetBlackListDeviceCount();
|
||||
return ReturnCountOnly(Count);
|
||||
} else if(StorageService()->GetBlackListDevices(QB_.Offset, QB_.Limit, Devices)) {
|
||||
return Object("devices",Devices);
|
||||
} else if (StorageService()->GetBlackListDevices(QB_.Offset, QB_.Limit, Devices)) {
|
||||
return Object("devices", Devices);
|
||||
}
|
||||
NotFound();
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -9,17 +9,17 @@
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_blacklist_list : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_blacklist_list(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/blacklist"};}
|
||||
RESTAPI_blacklist_list(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/blacklist"}; }
|
||||
void DoGet() final;
|
||||
void DoDelete() final {};
|
||||
void DoPost() final {};
|
||||
void DoPut() final {};
|
||||
void DoDelete() final{};
|
||||
void DoPost() final{};
|
||||
void DoPut() final{};
|
||||
};
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -10,19 +10,19 @@ namespace OpenWifi {
|
||||
void RESTAPI_capabilities_handler::DoGet() {
|
||||
CapabilitiesCache_t Caps = CapabilitiesCache()->AllCapabilities();
|
||||
|
||||
Poco::JSON::Array ObjArr;
|
||||
for(const auto &[deviceType,capabilities]:Caps) {
|
||||
Poco::JSON::Array ObjArr;
|
||||
for (const auto &[deviceType, capabilities] : Caps) {
|
||||
// std::cout << "C:" << to_string(capabilities) << std::endl;
|
||||
Poco::JSON::Object Inner;
|
||||
Inner.set("deviceType",deviceType);
|
||||
Poco::JSON::Parser P;
|
||||
Poco::JSON::Object Inner;
|
||||
Inner.set("deviceType", deviceType);
|
||||
Poco::JSON::Parser P;
|
||||
auto R = P.parse(to_string(capabilities)).extract<Poco::JSON::Object::Ptr>();
|
||||
Inner.set("capabilities", R);
|
||||
ObjArr.add(Inner);
|
||||
}
|
||||
Poco::JSON::Object Answer;
|
||||
Poco::JSON::Object Answer;
|
||||
|
||||
Answer.set("device_types",ObjArr);
|
||||
Answer.set("device_types", ObjArr);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -9,18 +9,17 @@
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_capabilities_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_capabilities_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_OPTIONS},
|
||||
Server,
|
||||
TransactionId,
|
||||
Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/capabilities"};}
|
||||
RESTAPI_capabilities_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_OPTIONS},
|
||||
Server, TransactionId, Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/capabilities"}; }
|
||||
void DoGet() final;
|
||||
void DoDelete() final {};
|
||||
void DoPost() final {};
|
||||
void DoPut() final {};
|
||||
void DoDelete() final{};
|
||||
void DoPost() final{};
|
||||
void DoPut() final{};
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
|
||||
#include "RESTAPI_command.h"
|
||||
|
||||
#include "CommandManager.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/ow_constants.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void RESTAPI_command::DoGet() {
|
||||
auto CommandUUID = GetBinding(RESTAPI::Protocol::COMMANDUUID, "");
|
||||
if(!Utils::ValidUUID(CommandUUID)) {
|
||||
if (!Utils::ValidUUID(CommandUUID)) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
@@ -27,22 +28,23 @@ namespace OpenWifi {
|
||||
|
||||
void RESTAPI_command::DoDelete() {
|
||||
auto CommandUUID = GetBinding(RESTAPI::Protocol::COMMANDUUID, "");
|
||||
if(CommandUUID.empty()) {
|
||||
if (CommandUUID.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingUUID);
|
||||
}
|
||||
|
||||
if(!Utils::ValidUUID(CommandUUID)) {
|
||||
if (!Utils::ValidUUID(CommandUUID)) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
GWObjects::CommandDetails C;
|
||||
if(!StorageService()->GetCommand(CommandUUID, C)) {
|
||||
GWObjects::CommandDetails C;
|
||||
if (!StorageService()->GetCommand(CommandUUID, C)) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
CommandManager()->RemoveCommand(CommandUUID);
|
||||
if (StorageService()->DeleteCommand(CommandUUID)) {
|
||||
return OK();
|
||||
}
|
||||
return InternalError(RESTAPI::Errors::NoRecordsDeleted);
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -11,20 +11,20 @@
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_command : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_command(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_DELETE,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server,
|
||||
TransactionId,
|
||||
Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/command/{commandUUID}"};}
|
||||
class RESTAPI_command : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_command(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_DELETE,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server, TransactionId, Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/command/{commandUUID}"}; }
|
||||
void DoGet() final;
|
||||
void DoDelete() final;
|
||||
void DoPost() final {};
|
||||
void DoPut() final {};
|
||||
void DoPost() final{};
|
||||
void DoPut() final{};
|
||||
};
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
namespace OpenWifi {
|
||||
void RESTAPI_commands::DoGet() {
|
||||
auto SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, "");
|
||||
if(!Utils::NormalizeMac(SerialNumber)) {
|
||||
if (!Utils::NormalizeMac(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
@@ -21,15 +21,15 @@ namespace OpenWifi {
|
||||
if (QB_.Newest) {
|
||||
StorageService()->GetNewestCommands(SerialNumber, QB_.Limit, Commands);
|
||||
} else {
|
||||
StorageService()->GetCommands(SerialNumber, QB_.StartDate, QB_.EndDate, QB_.Offset, QB_.Limit,
|
||||
Commands);
|
||||
StorageService()->GetCommands(SerialNumber, QB_.StartDate, QB_.EndDate, QB_.Offset,
|
||||
QB_.Limit, Commands);
|
||||
}
|
||||
return Object(RESTAPI::Protocol::COMMANDS, Commands);
|
||||
}
|
||||
|
||||
void RESTAPI_commands::DoDelete() {
|
||||
auto SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, "");
|
||||
if(!Utils::NormalizeMac(SerialNumber)) {
|
||||
if (!Utils::NormalizeMac(SerialNumber)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
|
||||
}
|
||||
|
||||
@@ -38,4 +38,4 @@ namespace OpenWifi {
|
||||
}
|
||||
InternalError(RESTAPI::Errors::NoRecordsDeleted);
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -13,18 +13,18 @@
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_commands : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_commands(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
RESTAPI_commands(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_DELETE,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server,
|
||||
TransactionId,
|
||||
Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/commands"};}
|
||||
Server, TransactionId, Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/commands"}; }
|
||||
void DoGet() final;
|
||||
void DoDelete() final;
|
||||
void DoPost() final {};
|
||||
void DoPut() final {};
|
||||
void DoPost() final{};
|
||||
void DoPut() final{};
|
||||
};
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
#include "RESTObjects/RESTAPI_GWobjects.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/ConfigurationValidator.h"
|
||||
#include "framework/orm.h"
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -29,7 +29,7 @@ namespace OpenWifi {
|
||||
|
||||
void RESTAPI_default_configuration::DoDelete() {
|
||||
std::string Name = ORM::Escape(GetBinding(RESTAPI::Protocol::NAME, ""));
|
||||
if(Name.empty()) {
|
||||
if (Name.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ namespace OpenWifi {
|
||||
void RESTAPI_default_configuration::DoPost() {
|
||||
std::string Name = GetBinding(RESTAPI::Protocol::NAME, "");
|
||||
|
||||
if(Name.empty()) {
|
||||
if (Name.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
if(StorageService()->DefaultConfigurationAlreadyExists(Name)) {
|
||||
if (StorageService()->DefaultConfigurationAlreadyExists(Name)) {
|
||||
return BadRequest(RESTAPI::Errors::DefConfigNameExists);
|
||||
}
|
||||
|
||||
@@ -56,16 +56,27 @@ namespace OpenWifi {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
if(DefConfig.Models.empty()) {
|
||||
if (DefConfig.models.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::ModelIDListCannotBeEmpty);
|
||||
}
|
||||
|
||||
std::string Error;
|
||||
if (!ValidateUCentralConfiguration(DefConfig.Configuration, Error)) {
|
||||
return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
|
||||
DefConfig.platform = DefConfig.platform.empty() ? Platforms::AP : DefConfig.platform;
|
||||
if(DefConfig.platform != Platforms::AP && DefConfig.platform != Platforms::SWITCH) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
DefConfig.Created = DefConfig.LastModified = Utils::Now();
|
||||
if(DefConfig.configuration.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
std::string Error;
|
||||
if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(DefConfig.platform),
|
||||
DefConfig.configuration, Error,
|
||||
GetBoolParameter("strict", false))) {
|
||||
return BadRequest(RESTAPI::Errors::ConfigBlockInvalid, Error);
|
||||
}
|
||||
|
||||
DefConfig.created = DefConfig.lastModified = Utils::Now();
|
||||
if (StorageService()->CreateDefaultConfiguration(Name, DefConfig)) {
|
||||
return OK();
|
||||
}
|
||||
@@ -77,36 +88,49 @@ namespace OpenWifi {
|
||||
std::string Name = GetBinding(RESTAPI::Protocol::NAME, "");
|
||||
|
||||
const auto &Obj = ParsedBody_;
|
||||
GWObjects::DefaultConfiguration NewConfig;
|
||||
GWObjects::DefaultConfiguration NewConfig;
|
||||
if (!NewConfig.from_json(Obj)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
GWObjects::DefaultConfiguration Existing;
|
||||
if(!StorageService()->GetDefaultConfiguration(Name,Existing)) {
|
||||
GWObjects::DefaultConfiguration Existing;
|
||||
if (!StorageService()->GetDefaultConfiguration(Name, Existing)) {
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!NewConfig.Configuration.empty()) {
|
||||
std::string Error;
|
||||
if(!ValidateUCentralConfiguration(NewConfig.Configuration, Error)) {
|
||||
return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
|
||||
}
|
||||
Existing.Configuration = NewConfig.Configuration;
|
||||
if(Existing.platform.empty()) {
|
||||
Existing.platform = Platforms::AP;
|
||||
}
|
||||
|
||||
Existing.LastModified = Utils::Now();
|
||||
AssignIfPresent(Obj,"description",Existing.Description);
|
||||
if(Obj->has("modelIds"))
|
||||
Existing.Models = NewConfig.Models;
|
||||
if(ParsedBody_->has("platform")) {
|
||||
if(NewConfig.platform.empty() || (NewConfig.platform != Platforms::AP && NewConfig.platform != Platforms::SWITCH)) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
Existing.platform = NewConfig.platform;
|
||||
}
|
||||
|
||||
if (!NewConfig.configuration.empty()) {
|
||||
std::string Error;
|
||||
if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(Existing.platform),
|
||||
NewConfig.configuration, Error,
|
||||
GetBoolParameter("strict", false))) {
|
||||
return BadRequest(RESTAPI::Errors::ConfigBlockInvalid, Error);
|
||||
}
|
||||
Existing.configuration = NewConfig.configuration;
|
||||
}
|
||||
|
||||
Existing.lastModified = Utils::Now();
|
||||
AssignIfPresent(Obj, "description", Existing.description);
|
||||
if (Obj->has("modelIds"))
|
||||
Existing.models = NewConfig.models;
|
||||
|
||||
if (StorageService()->UpdateDefaultConfiguration(Name, Existing)) {
|
||||
GWObjects::DefaultConfiguration ModifiedConfig;
|
||||
GWObjects::DefaultConfiguration ModifiedConfig;
|
||||
|
||||
StorageService()->GetDefaultConfiguration(Name,ModifiedConfig);
|
||||
StorageService()->GetDefaultConfiguration(Name, ModifiedConfig);
|
||||
return Object(ModifiedConfig);
|
||||
}
|
||||
|
||||
BadRequest(RESTAPI::Errors::RecordNotUpdated);
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
@@ -13,19 +13,22 @@
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_default_configuration : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_default_configuration(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
RESTAPI_default_configuration(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_configuration/{name}"};}
|
||||
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_configuration/{name}"};
|
||||
}
|
||||
void DoGet() final;
|
||||
void DoDelete() final;
|
||||
void DoPost() final;
|
||||
void DoPut() final;
|
||||
};
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
#include "Poco/Array.h"
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
|
||||
#include "RESTAPI_default_configurations.h"
|
||||
#include "StorageService.h"
|
||||
@@ -16,7 +15,7 @@
|
||||
namespace OpenWifi {
|
||||
void RESTAPI_default_configurations::DoGet() {
|
||||
|
||||
if(QB_.CountOnly) {
|
||||
if (QB_.CountOnly) {
|
||||
auto Count = StorageService()->GetDefaultConfigurationsCount();
|
||||
return ReturnCountOnly(Count);
|
||||
}
|
||||
@@ -25,4 +24,4 @@ namespace OpenWifi {
|
||||
StorageService()->GetDefaultConfigurations(QB_.Offset, QB_.Limit, DefConfigs);
|
||||
return Object(RESTAPI::Protocol::CONFIGURATIONS, DefConfigs);
|
||||
}
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -11,19 +11,19 @@
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_default_configurations : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_default_configurations(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_configurations"};}
|
||||
void DoGet() final;
|
||||
void DoDelete() final {};
|
||||
void DoPost() final {};
|
||||
void DoPut() final {};
|
||||
class RESTAPI_default_configurations : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_default_configurations(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_configurations"}; }
|
||||
void DoGet() final;
|
||||
void DoDelete() final{};
|
||||
void DoPost() final{};
|
||||
void DoPut() final{};
|
||||
};
|
||||
}
|
||||
} // namespace OpenWifi
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user