mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralgw.git
				synced 2025-11-04 04:37:46 +00:00 
			
		
		
		
	Compare commits
	
		
			712 Commits
		
	
	
		
			v2.9.0
			...
			add_enroll
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3b7a24ea30 | ||
| 
						 | 
					438309714f | ||
| 
						 | 
					a9130eeb75 | ||
| 
						 | 
					33068fca9e | ||
| 
						 | 
					d329151f6c | ||
| 
						 | 
					ec846006bb | ||
| 
						 | 
					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 | ||
| 
						 | 
					54d94eef84 | ||
| 
						 | 
					397799278c | ||
| 
						 | 
					d8d0eed1fb | ||
| 
						 | 
					d1eb584430 | ||
| 
						 | 
					c76e10299d | ||
| 
						 | 
					2949231f67 | ||
| 
						 | 
					d6731e4e5b | ||
| 
						 | 
					d92b064561 | ||
| 
						 | 
					ec4a7f64de | ||
| 
						 | 
					14877301e4 | ||
| 
						 | 
					d92cba15bb | ||
| 
						 | 
					33d92c9240 | ||
| 
						 | 
					737ed24d91 | ||
| 
						 | 
					670f497af4 | ||
| 
						 | 
					11ba1cc6e4 | ||
| 
						 | 
					c69c4754cf | ||
| 
						 | 
					8478d761db | ||
| 
						 | 
					500a4dc130 | ||
| 
						 | 
					c420d9c572 | ||
| 
						 | 
					c94d90911f | ||
| 
						 | 
					04fd524b52 | ||
| 
						 | 
					b49744779e | ||
| 
						 | 
					60de792147 | ||
| 
						 | 
					e6d6da53c7 | ||
| 
						 | 
					cd62cb8b84 | ||
| 
						 | 
					85eb8546c3 | ||
| 
						 | 
					bc5a8bbe5b | ||
| 
						 | 
					d92a93a872 | ||
| 
						 | 
					0def242b40 | ||
| 
						 | 
					eeb5e2d3be | ||
| 
						 | 
					e76132f464 | ||
| 
						 | 
					ac41327bf8 | ||
| 
						 | 
					0084b5d21c | ||
| 
						 | 
					69b4de1b74 | ||
| 
						 | 
					0c3e8d4630 | ||
| 
						 | 
					af739bc32b | 
							
								
								
									
										2
									
								
								.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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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>
 | 
			
		||||
							
								
								
									
										62
									
								
								BUILDING.md
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								BUILDING.md
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
# Building from source
 | 
			
		||||
In order to build the OWGW, you will need to install its dependencies, which includes the following:
 | 
			
		||||
In order to build OWGW, you will need to install its dependencies, which includes the following:
 | 
			
		||||
- cmake
 | 
			
		||||
- boost
 | 
			
		||||
- POCO 1.10.1 or later
 | 
			
		||||
@@ -12,43 +12,43 @@ In order to build the OWGW, you will need to install its dependencies, which inc
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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 
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
cd ~
 | 
			
		||||
git clone https://github.com/AriliaWireless/poco --branch poco-tip-v1
 | 
			
		||||
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 ../..
 | 
			
		||||
 | 
			
		||||
cd ~
 | 
			
		||||
git clone https://github.com/AriliaWireless/cppkafka --branch tip-v1
 | 
			
		||||
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 ../..
 | 
			
		||||
 | 
			
		||||
cd ~
 | 
			
		||||
git clone https://github.com/AriliaWireless/valijson --branch tip-v1
 | 
			
		||||
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
 | 
			
		||||
@@ -57,56 +57,59 @@ cd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
make
 | 
			
		||||
make install
 | 
			
		||||
cd ../..
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
sudo yum install yaml-cpp-devel lua-devel 
 | 
			
		||||
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/AriliaWireless/poco --branch poco-tip-v1
 | 
			
		||||
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/AriliaWireless/cppkafka --branch tip-v1
 | 
			
		||||
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 ../..
 | 
			
		||||
 | 
			
		||||
cd ~
 | 
			
		||||
git clone https://github.com/AriliaWireless/valijson --branch tip-v1
 | 
			
		||||
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 ../..
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -125,7 +128,7 @@ brew install openssl \
 | 
			
		||||
	nlohmann-json \
 | 
			
		||||
	fmt
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/AriliaWireless/poco --branch poco-tip-v1
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-poco --branch poco-tip-v1 poco
 | 
			
		||||
pushd poco
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
push cmake-build
 | 
			
		||||
@@ -135,7 +138,7 @@ sudo cmake --build . --target install
 | 
			
		||||
popd
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/AriliaWireless/cppkafka --branch tip-v1
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-cppkafka --branch tip-v1 cppkafka
 | 
			
		||||
pushd cppkafka
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
pushd cmake-build
 | 
			
		||||
@@ -145,10 +148,10 @@ sudo cmake --build . --target install
 | 
			
		||||
popd
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
git clone https://github.com/AriliaWireless/valijson --branch tip-v1
 | 
			
		||||
cd valijson
 | 
			
		||||
git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch tip-v1 valijson
 | 
			
		||||
pushd valijson
 | 
			
		||||
mkdir cmake-build
 | 
			
		||||
cd cmake-build
 | 
			
		||||
pushd cmake-build
 | 
			
		||||
cmake ..
 | 
			
		||||
cmake --build . --config Release
 | 
			
		||||
sudo cmake --build . --target install
 | 
			
		||||
@@ -171,20 +174,23 @@ support. You can build with only SQLite support by not installing the packages f
 | 
			
		||||
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/stephb9959/poco
 | 
			
		||||
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 ../..
 | 
			
		||||
 | 
			
		||||
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 ../..
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(owgw VERSION 2.9.0)
 | 
			
		||||
project(owgw VERSION 4.1.0)
 | 
			
		||||
 | 
			
		||||
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,7 +50,7 @@ if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
 | 
			
		||||
    string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_definitions(-DTIP_GATEWAY_SERVICE="1" -DPOCO_LOG_DEBUG="1")
 | 
			
		||||
add_definitions(-DTIP_GATEWAY_SERVICE="1" -DPOCO_LOG_DEBUG="1" -DBOOST_NO_CXX98_FUNCTION_BASE=1)
 | 
			
		||||
 | 
			
		||||
find_package(OpenSSL REQUIRED)
 | 
			
		||||
find_package(ZLIB REQUIRED)
 | 
			
		||||
@@ -144,9 +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.h src/AP_WS_Process_event.cpp src/AP_WS_Process_wifiscan.cpp src/AP_WS_Process_alarm.cpp src/GWKafkaEvents.cpp src/GWKafkaEvents.h src/RegulatoryInfo.cpp src/RegulatoryInfo.h src/RESTAPI/RESTAPI_regulatory.cpp src/RESTAPI/RESTAPI_regulatory.h)
 | 
			
		||||
        src/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,14 +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
 | 
			
		||||
                fmt::fmt
 | 
			
		||||
             )
 | 
			
		||||
            ${MySQL_LIBRARIES}
 | 
			
		||||
            ${ZLIB_LIBRARIES}
 | 
			
		||||
            CppKafka::cppkafka
 | 
			
		||||
            fmt::fmt
 | 
			
		||||
            resolv
 | 
			
		||||
    )
 | 
			
		||||
    if(UNIX AND NOT APPLE)
 | 
			
		||||
        target_link_libraries(owgw PUBLIC PocoJSON)
 | 
			
		||||
    endif()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -1,7 +1,9 @@
 | 
			
		||||
ARG DEBIAN_VERSION=11.5-slim
 | 
			
		||||
ARG POCO_VERSION=poco-tip-v2
 | 
			
		||||
ARG CPPKAFKA_VERSION=tip-v1
 | 
			
		||||
ARG VALIJASON_VERSION=tip-v1
 | 
			
		||||
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
 | 
			
		||||
@@ -43,8 +45,8 @@ FROM build-base AS valijson-build
 | 
			
		||||
 | 
			
		||||
ARG VALIJASON_VERSION
 | 
			
		||||
 | 
			
		||||
ADD https://api.github.com/repos/AriliaWireless/valijson/git/refs/tags/${VALIJASON_VERSION} version.json
 | 
			
		||||
RUN git clone https://github.com/AriliaWireless/valijson --branch ${VALIJASON_VERSION} /valijson
 | 
			
		||||
ADD https://api.github.com/repos/Telecominfraproject/wlan-cloud-lib-valijson/git/refs/tags/${VALIJASON_VERSION} version.json
 | 
			
		||||
RUN git clone https://github.com/Telecominfraproject/wlan-cloud-lib-valijson --branch ${VALIJASON_VERSION} /valijson
 | 
			
		||||
 | 
			
		||||
WORKDIR /valijson
 | 
			
		||||
RUN mkdir cmake-build
 | 
			
		||||
@@ -53,12 +55,14 @@ RUN cmake ..
 | 
			
		||||
RUN cmake --build . --config Release -j8
 | 
			
		||||
RUN cmake --build . --target install
 | 
			
		||||
 | 
			
		||||
FROM build-base AS owgw-build
 | 
			
		||||
FROM build-base AS app-build
 | 
			
		||||
 | 
			
		||||
ADD CMakeLists.txt build /owgw/
 | 
			
		||||
ADD cmake /owgw/cmake
 | 
			
		||||
ADD src /owgw/src
 | 
			
		||||
ADD .git /owgw/.git
 | 
			
		||||
ARG APP_NAME
 | 
			
		||||
 | 
			
		||||
ADD CMakeLists.txt build /${APP_NAME}/
 | 
			
		||||
ADD cmake /${APP_NAME}/cmake
 | 
			
		||||
ADD src /${APP_NAME}/src
 | 
			
		||||
ADD .git /${APP_NAME}/.git
 | 
			
		||||
 | 
			
		||||
COPY --from=poco-build /usr/local/include /usr/local/include
 | 
			
		||||
COPY --from=poco-build /usr/local/lib /usr/local/lib
 | 
			
		||||
@@ -66,23 +70,33 @@ COPY --from=cppkafka-build /usr/local/include /usr/local/include
 | 
			
		||||
COPY --from=cppkafka-build /usr/local/lib /usr/local/lib
 | 
			
		||||
COPY --from=valijson-build /usr/local/include /usr/local/include
 | 
			
		||||
 | 
			
		||||
WORKDIR /owgw
 | 
			
		||||
WORKDIR /${APP_NAME}
 | 
			
		||||
RUN mkdir cmake-build
 | 
			
		||||
WORKDIR /owgw/cmake-build
 | 
			
		||||
WORKDIR /${APP_NAME}/cmake-build
 | 
			
		||||
RUN cmake ..
 | 
			
		||||
RUN cmake --build . --config Release -j8
 | 
			
		||||
 | 
			
		||||
FROM debian:$DEBIAN_VERSION
 | 
			
		||||
 | 
			
		||||
ENV OWGW_USER=owgw \
 | 
			
		||||
    OWGW_ROOT=/owgw-data \
 | 
			
		||||
    OWGW_CONFIG=/owgw-data
 | 
			
		||||
ARG APP_NAME
 | 
			
		||||
ARG APP_HOME_DIR
 | 
			
		||||
 | 
			
		||||
RUN useradd "$OWGW_USER"
 | 
			
		||||
ENV APP_NAME=$APP_NAME \
 | 
			
		||||
    APP_USER=$APP_NAME \
 | 
			
		||||
    APP_ROOT=/$APP_NAME-data \
 | 
			
		||||
    APP_CONFIG=/$APP_NAME-data \
 | 
			
		||||
    APP_HOME_DIR=$APP_HOME_DIR
 | 
			
		||||
 | 
			
		||||
RUN mkdir /openwifi
 | 
			
		||||
RUN mkdir -p "$OWGW_ROOT" "$OWGW_CONFIG" && \
 | 
			
		||||
    chown "$OWGW_USER": "$OWGW_ROOT" "$OWGW_CONFIG"
 | 
			
		||||
# 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 \
 | 
			
		||||
@@ -91,14 +105,14 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
 | 
			
		||||
COPY readiness_check /readiness_check
 | 
			
		||||
COPY test_scripts/curl/cli /cli
 | 
			
		||||
 | 
			
		||||
COPY owgw.properties.tmpl /
 | 
			
		||||
COPY $APP_NAME.properties.tmpl /
 | 
			
		||||
COPY docker-entrypoint.sh /
 | 
			
		||||
COPY wait-for-postgres.sh /
 | 
			
		||||
COPY rtty_ui /dist/rtty_ui
 | 
			
		||||
RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \
 | 
			
		||||
    -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.crt
 | 
			
		||||
 | 
			
		||||
COPY --from=owgw-build /owgw/cmake-build/owgw /openwifi/owgw
 | 
			
		||||
COPY --from=app-build /$APP_NAME/cmake-build/$APP_NAME $APP_HOME_DIR/$APP_NAME
 | 
			
		||||
COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib /usr/local/lib/
 | 
			
		||||
COPY --from=poco-build /poco/cmake-build/lib /usr/local/lib/
 | 
			
		||||
 | 
			
		||||
@@ -107,4 +121,4 @@ RUN ldconfig
 | 
			
		||||
EXPOSE 15002 16002 16003 17002 16102
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/docker-entrypoint.sh"]
 | 
			
		||||
CMD ["/openwifi/owgw"]
 | 
			
		||||
CMD ${APP_HOME_DIR}/${APP_NAME}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										270
									
								
								PROTOCOL.md
									
									
									
									
									
								
							
							
						
						
									
										270
									
								
								PROTOCOL.md
									
									
									
									
									
								
							@@ -149,18 +149,35 @@ The `severity` matches the `syslog` levels. Here are the details:
 | 
			
		||||
- 7 : LOG_DEBUG       7       /* debug-level messages */
 | 
			
		||||
 | 
			
		||||
#### Crash Log event
 | 
			
		||||
Device may send a crash log event after rebooting after a crash. The event cannot be sent until a connection event has been sent.
 | 
			
		||||
Device may send a `crash log event` during rebooting after a crash. The event cannot be sent until a connection event has been established.
 | 
			
		||||
```json
 | 
			
		||||
{   "jsonrpc" : "2.0" , 
 | 
			
		||||
    "method" : "crashlog" , 
 | 
			
		||||
    "params" : {
 | 
			
		||||
        "serial" : <serial number> ,
 | 
			
		||||
        "uuid"   : <the UUID of the configuration that generated the crash log>,
 | 
			
		||||
        "loglines" : [ an array of strings representing the logs from the log file ]
 | 
			
		||||
        "serial"    : <serial number> ,
 | 
			
		||||
        "uuid"      : <the UUID of the configuration that generated the crash log>,
 | 
			
		||||
        "loglines"  : [ an array of strings representing the logs from the log file ]
 | 
			
		||||
      }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Reboot Log event
 | 
			
		||||
The device may send a `reboot log event` after a reboot. This maybe a scheduled reboot or caused in some other way.
 | 
			
		||||
```json
 | 
			
		||||
{   "jsonrpc" : "2.0" , 
 | 
			
		||||
    "method" : "rebootLog" , 
 | 
			
		||||
    "params" : {
 | 
			
		||||
        "serial"  : <serial number> ,
 | 
			
		||||
        "uuid"    : <the UUID of the configuration that generated the reboot log>,
 | 
			
		||||
        "date"    : <Unix time when this reboot occurred>,
 | 
			
		||||
        "type"    : <string>,
 | 
			
		||||
        "info"    : [ "info 1", "info 2"]
 | 
			
		||||
      }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Here is a possible list of reboot reasons:
 | 
			
		||||
 | 
			
		||||
#### Config change pending event
 | 
			
		||||
Device sends this message to tell the controller that the device 
 | 
			
		||||
has received a configuration but is still running an older configuration. The controller will not
 | 
			
		||||
@@ -289,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 
 | 
			
		||||
@@ -338,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
 | 
			
		||||
@@ -758,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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,35 +6,35 @@ if [ "$SELFSIGNED_CERTS" = 'true' ]; then
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
 | 
			
		||||
    WEBSOCKET_HOST_ROOTCA=${WEBSOCKET_HOST_ROOTCA:-"\$OWGW_ROOT/certs/root.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_ISSUER=${WEBSOCKET_HOST_ISSUER:-"\$OWGW_ROOT/certs/issuer.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_CERT=${WEBSOCKET_HOST_CERT:-"\$OWGW_ROOT/certs/websocket-cert.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_KEY=${WEBSOCKET_HOST_KEY:-"\$OWGW_ROOT/certs/websocket-key.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_CLIENTCAS=${WEBSOCKET_HOST_CLIENTCAS:-"\$OWGW_ROOT/certs/clientcas.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_CAS=${WEBSOCKET_HOST_CAS:-"\$OWGW_ROOT/certs/cas"} \
 | 
			
		||||
    WEBSOCKET_HOST_ROOTCA=${WEBSOCKET_HOST_ROOTCA:-"\${APP_ROOT}/certs/root.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_ISSUER=${WEBSOCKET_HOST_ISSUER:-"\${APP_ROOT}/certs/issuer.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_CERT=${WEBSOCKET_HOST_CERT:-"\${APP_ROOT}/certs/websocket-cert.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_KEY=${WEBSOCKET_HOST_KEY:-"\${APP_ROOT}/certs/websocket-key.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_CLIENTCAS=${WEBSOCKET_HOST_CLIENTCAS:-"\${APP_ROOT}/certs/clientcas.pem"} \
 | 
			
		||||
    WEBSOCKET_HOST_CAS=${WEBSOCKET_HOST_CAS:-"\${APP_ROOT}/certs/cas"} \
 | 
			
		||||
    WEBSOCKET_HOST_PORT=${WEBSOCKET_HOST_PORT:-"15002"} \
 | 
			
		||||
    WEBSOCKET_HOST_KEY_PASSWORD=${WEBSOCKET_HOST_KEY_PASSWORD:-"mypassword"} \
 | 
			
		||||
    RESTAPI_HOST_ROOTCA=${RESTAPI_HOST_ROOTCA:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
 | 
			
		||||
    RESTAPI_HOST_ROOTCA=${RESTAPI_HOST_ROOTCA:-"\${APP_ROOT}/certs/restapi-ca.pem"} \
 | 
			
		||||
    RESTAPI_HOST_PORT=${RESTAPI_HOST_PORT:-"16002"} \
 | 
			
		||||
    RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
 | 
			
		||||
    RESTAPI_HOST_KEY=${RESTAPI_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
 | 
			
		||||
    RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\${APP_ROOT}/certs/restapi-cert.pem"} \
 | 
			
		||||
    RESTAPI_HOST_KEY=${RESTAPI_HOST_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
 | 
			
		||||
    RESTAPI_HOST_KEY_PASSWORD=${RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_ROOTCA=${INTERNAL_RESTAPI_HOST_ROOTCA:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_ROOTCA=${INTERNAL_RESTAPI_HOST_ROOTCA:-"\${APP_ROOT}/certs/restapi-ca.pem"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_PORT=${INTERNAL_RESTAPI_HOST_PORT:-"17002"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_CERT=${INTERNAL_RESTAPI_HOST_CERT:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_KEY=${INTERNAL_RESTAPI_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_CERT=${INTERNAL_RESTAPI_HOST_CERT:-"\${APP_ROOT}/certs/restapi-cert.pem"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_KEY=${INTERNAL_RESTAPI_HOST_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
 | 
			
		||||
    INTERNAL_RESTAPI_HOST_KEY_PASSWORD=${INTERNAL_RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \
 | 
			
		||||
    FILEUPLOADER_HOST_ROOTCA=${FILEUPLOADER_HOST_ROOTCA:-"\$OWGW_ROOT/certs/restapi-ca.pem"} \
 | 
			
		||||
    FILEUPLOADER_HOST_ROOTCA=${FILEUPLOADER_HOST_ROOTCA:-"\${APP_ROOT}/certs/restapi-ca.pem"} \
 | 
			
		||||
    FILEUPLOADER_HOST_NAME=${FILEUPLOADER_HOST_NAME:-"localhost"} \
 | 
			
		||||
    FILEUPLOADER_HOST_PORT=${FILEUPLOADER_HOST_PORT:-"16003"} \
 | 
			
		||||
    FILEUPLOADER_HOST_CERT=${FILEUPLOADER_HOST_CERT:-"\$OWGW_ROOT/certs/restapi-cert.pem"} \
 | 
			
		||||
    FILEUPLOADER_HOST_KEY=${FILEUPLOADER_HOST_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
 | 
			
		||||
    FILEUPLOADER_HOST_CERT=${FILEUPLOADER_HOST_CERT:-"\${APP_ROOT}/certs/restapi-cert.pem"} \
 | 
			
		||||
    FILEUPLOADER_HOST_KEY=${FILEUPLOADER_HOST_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
 | 
			
		||||
    FILEUPLOADER_HOST_KEY_PASSWORD=${FILEUPLOADER_HOST_KEY_PASSWORD:-"mypassword"} \
 | 
			
		||||
    FILEUPLOADER_PATH=${FILEUPLOADER_PATH:-"\$OWGW_ROOT/uploads"} \
 | 
			
		||||
    FILEUPLOADER_PATH=${FILEUPLOADER_PATH:-"\${APP_ROOT}/uploads"} \
 | 
			
		||||
    FILEUPLOADER_URI=${FILEUPLOADER_URI:-"https://localhost:16003"} \
 | 
			
		||||
    SERVICE_KEY=${SERVICE_KEY:-"\$OWGW_ROOT/certs/restapi-key.pem"} \
 | 
			
		||||
    SERVICE_KEY=${SERVICE_KEY:-"\${APP_ROOT}/certs/restapi-key.pem"} \
 | 
			
		||||
    SERVICE_KEY_PASSWORD=${SERVICE_KEY_PASSWORD:-"mypassword"} \
 | 
			
		||||
    SYSTEM_DATA=${SYSTEM_DATA:-"\$OWGW_ROOT/data"} \
 | 
			
		||||
    SYSTEM_DATA=${SYSTEM_DATA:-"\${APP_ROOT}/data"} \
 | 
			
		||||
    SYSTEM_URI_PRIVATE=${SYSTEM_URI_PRIVATE:-"https://localhost:17002"} \
 | 
			
		||||
    SYSTEM_URI_PUBLIC=${SYSTEM_URI_PUBLIC:-"https://localhost:16002"} \
 | 
			
		||||
    SYSTEM_URI_UI=${SYSTEM_URI_UI:-"http://localhost"} \
 | 
			
		||||
@@ -51,7 +51,7 @@ if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
 | 
			
		||||
    RTTY_TOKEN=${RTTY_TOKEN:-""} \
 | 
			
		||||
    RTTY_TIMEOUT=${RTTY_TIMEOUT:-"60"} \
 | 
			
		||||
    RTTY_VIEWPORT=${RTTY_VIEWPORT:-"5913"} \
 | 
			
		||||
    RTTY_ASSETS=${RTTY_ASSETS:-"\$OWGW_ROOT/rtty_ui"} \
 | 
			
		||||
    RTTY_ASSETS=${RTTY_ASSETS:-"\${APP_ROOT}/rtty_ui"} \
 | 
			
		||||
    RADIUS_PROXY_ENABLE=${RADIUS_PROXY_ENABLE:-"false"} \
 | 
			
		||||
    RADIUS_PROXY_ACCOUNTING_PORT=${RADIUS_PROXY_ACCOUNTING_PORT:-"1813"} \
 | 
			
		||||
    RADIUS_PROXY_AUTHENTICATION_PORT=${RADIUS_PROXY_AUTHENTICATION_PORT:-"1812"} \
 | 
			
		||||
@@ -64,39 +64,41 @@ if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
 | 
			
		||||
    KAFKA_SSL_KEY_PASSWORD=${KAFKA_SSL_KEY_PASSWORD:-""} \
 | 
			
		||||
    STORAGE_TYPE=${STORAGE_TYPE:-"sqlite"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_HOST=${STORAGE_TYPE_POSTGRESQL_HOST:-"localhost"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_USERNAME=${STORAGE_TYPE_POSTGRESQL_USERNAME:-"owgw"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_PASSWORD=${STORAGE_TYPE_POSTGRESQL_PASSWORD:-"owgw"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_DATABASE=${STORAGE_TYPE_POSTGRESQL_DATABASE:-"owgw"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_USERNAME=${STORAGE_TYPE_POSTGRESQL_USERNAME:-"${APP_USER}"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_PASSWORD=${STORAGE_TYPE_POSTGRESQL_PASSWORD:-"${APP_USER}"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_DATABASE=${STORAGE_TYPE_POSTGRESQL_DATABASE:-"${APP_NAME}"} \
 | 
			
		||||
    STORAGE_TYPE_POSTGRESQL_PORT=${STORAGE_TYPE_POSTGRESQL_PORT:-"5432"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_HOST=${STORAGE_TYPE_MYSQL_HOST:-"localhost"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_USERNAME=${STORAGE_TYPE_MYSQL_USERNAME:-"owgw"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owgw"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owgw"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_USERNAME=${STORAGE_TYPE_MYSQL_USERNAME:-"${APP_USER}"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"${APP_USER}"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"${APP_NAME}"} \
 | 
			
		||||
    STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \
 | 
			
		||||
    CERTIFICATES_ALLOWMISMATCH=${CERTIFICATES_ALLOWMISMATCH:-"false"} \
 | 
			
		||||
    envsubst < /owgw.properties.tmpl > $OWGW_CONFIG/owgw.properties
 | 
			
		||||
    IPINFO_DEFAULT_COUNTRY=${IPINFO_DEFAULT_COUNTRY:-"US"} \
 | 
			
		||||
    DEVICE_SESSION_TIMEOUT=${DEVICE_SESSION_TIMEOUT:-"600"} \
 | 
			
		||||
    envsubst < /"${APP_NAME}".properties.tmpl > "${APP_CONFIG}"/"${APP_NAME}".properties
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Check if rtty_ui directory exists
 | 
			
		||||
export RTTY_ASSETS=$(grep 'rtty.assets' $OWGW_CONFIG/owgw.properties | awk -F '=' '{print $2}' | xargs | envsubst)
 | 
			
		||||
export RTTY_ASSETS=$(grep 'rtty.assets' "${APP_CONFIG}"/"${APP_NAME}".properties | awk -F '=' '{print $2}' | xargs | envsubst)
 | 
			
		||||
 | 
			
		||||
if [ -z "$RTTY_ASSETS" ]; then
 | 
			
		||||
    export RTTY_ASSETS="$OWGW_ROOT/rtty_ui"
 | 
			
		||||
    export RTTY_ASSETS="${APP_ROOT}/rtty_ui"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ ! -d "$(dirname $RTTY_ASSETS)" ]]; then
 | 
			
		||||
  mkdir -p $(dirname $RTTY_ASSETS)
 | 
			
		||||
  mkdir -p "$(dirname $RTTY_ASSETS)"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [[ ! -d "$RTTY_ASSETS" ]]; then
 | 
			
		||||
    cp -r /dist/rtty_ui $RTTY_ASSETS
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$1" = '/openwifi/owgw' -a "$(id -u)" = '0' ]; then
 | 
			
		||||
if [ "$1" = "${APP_HOME_DIR}/${APP_NAME}" -a "$(id -u)" = '0' ]; then
 | 
			
		||||
    if [ "$RUN_CHOWN" = 'true' ]; then
 | 
			
		||||
        chown -R "$OWGW_USER": "$OWGW_ROOT" "$OWGW_CONFIG"
 | 
			
		||||
        chown -R "$APP_USER": "${APP_ROOT}" "$APP_CONFIG"
 | 
			
		||||
    fi
 | 
			
		||||
    exec gosu "$OWGW_USER" "$@"
 | 
			
		||||
    exec gosu "$APP_USER" "$@"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
exec "$@"
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ fullnameOverride: ""
 | 
			
		||||
images:
 | 
			
		||||
  owgw:
 | 
			
		||||
    repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw
 | 
			
		||||
    tag: v2.9.0-RC2
 | 
			
		||||
    tag: master
 | 
			
		||||
    pullPolicy: Always
 | 
			
		||||
#    regcred:
 | 
			
		||||
#      registry: tip-tip-wlan-cloud-ucentral.jfrog.io
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22213
									
								
								issues/OWGW Logs.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22213
									
								
								issues/OWGW Logs.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2158
									
								
								issues/OWLS Logs.rtf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2158
									
								
								issues/OWLS Logs.rtf
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2154
									
								
								issues/OWLS Logs.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2154
									
								
								issues/OWLS Logs.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
@@ -2,7 +2,7 @@ openapi: 3.0.1
 | 
			
		||||
info:
 | 
			
		||||
  title: uCentral gateway API
 | 
			
		||||
  description: A process to manage configuration for devices.
 | 
			
		||||
  version: 2.5.0
 | 
			
		||||
  version: 2.11.0
 | 
			
		||||
  license:
 | 
			
		||||
    name: BSD3
 | 
			
		||||
    url: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
 | 
			
		||||
@@ -12,7 +12,7 @@ info:
 | 
			
		||||
    url: https://www.ucentral.info/support
 | 
			
		||||
 | 
			
		||||
servers:
 | 
			
		||||
  - url: 'https://localhost:16001/api/v1'
 | 
			
		||||
  - url: 'https://localhost:16002/api/v1'
 | 
			
		||||
 | 
			
		||||
security:
 | 
			
		||||
  - bearerAuth: []
 | 
			
		||||
@@ -42,12 +42,10 @@ components:
 | 
			
		||||
  schemas:
 | 
			
		||||
    DeviceType:
 | 
			
		||||
      type: string
 | 
			
		||||
      default: AP
 | 
			
		||||
      default: ap
 | 
			
		||||
      enum:
 | 
			
		||||
        - AP
 | 
			
		||||
        - SWITCH
 | 
			
		||||
        - IOT
 | 
			
		||||
        - MESH
 | 
			
		||||
        - ap
 | 
			
		||||
        - switch
 | 
			
		||||
 | 
			
		||||
    DeviceRestrictionsKeyInfo:
 | 
			
		||||
      type: object
 | 
			
		||||
@@ -152,6 +150,14 @@ components:
 | 
			
		||||
          format: uuid
 | 
			
		||||
        restrictionDetails:
 | 
			
		||||
          $ref: '#/components/schemas/DeviceRestrictions'
 | 
			
		||||
        simulated:
 | 
			
		||||
          type: boolean
 | 
			
		||||
        lastRecordedContact:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        blackListed:
 | 
			
		||||
          type: boolean
 | 
			
		||||
          readOnly: true
 | 
			
		||||
 | 
			
		||||
    DeviceWithStatus:
 | 
			
		||||
      type: object
 | 
			
		||||
@@ -260,6 +266,25 @@ components:
 | 
			
		||||
          format: uuid
 | 
			
		||||
        restrictionDetails:
 | 
			
		||||
          $ref: '#/components/schemas/DeviceRestrictions'
 | 
			
		||||
        hasGPS:
 | 
			
		||||
          type: boolean
 | 
			
		||||
        sanity:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        memoryUsed:
 | 
			
		||||
          type: number
 | 
			
		||||
          format: float
 | 
			
		||||
        load:
 | 
			
		||||
          type: number
 | 
			
		||||
          format: float
 | 
			
		||||
        temperature:
 | 
			
		||||
          type: number
 | 
			
		||||
          format: float
 | 
			
		||||
        connectReason:
 | 
			
		||||
          type: string
 | 
			
		||||
        blackListed:
 | 
			
		||||
          type: boolean
 | 
			
		||||
          readOnly: true
 | 
			
		||||
 | 
			
		||||
    DeviceList:
 | 
			
		||||
      type: object
 | 
			
		||||
@@ -329,6 +354,9 @@ components:
 | 
			
		||||
        associations_5G:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        associations_6G:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        verifiedCertificate:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
@@ -336,6 +364,7 @@ components:
 | 
			
		||||
            - VALID_CERTIFICATE,
 | 
			
		||||
            - MISMATCH_SERIAL,
 | 
			
		||||
            - VERIFIED
 | 
			
		||||
            - SIMULATED
 | 
			
		||||
 | 
			
		||||
    DeviceCapabilities:
 | 
			
		||||
      type: object
 | 
			
		||||
@@ -420,7 +449,7 @@ components:
 | 
			
		||||
          type: string
 | 
			
		||||
 | 
			
		||||
    CommandSubmitSuccess:
 | 
			
		||||
      description: The command was submitted succesfully.
 | 
			
		||||
      description: The command was submitted successfully.
 | 
			
		||||
      properties:
 | 
			
		||||
        serialNumber:
 | 
			
		||||
          type: string
 | 
			
		||||
@@ -458,6 +487,10 @@ components:
 | 
			
		||||
        logType:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
          example:
 | 
			
		||||
            - 0  normal logs
 | 
			
		||||
            - 1  crash logs
 | 
			
		||||
            - 2  reboot logs
 | 
			
		||||
        UUID:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
@@ -516,6 +549,12 @@ components:
 | 
			
		||||
        lastModified:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        platform:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - ap
 | 
			
		||||
            - switch
 | 
			
		||||
          default: ap
 | 
			
		||||
 | 
			
		||||
    DefaultConfigurationList:
 | 
			
		||||
      properties:
 | 
			
		||||
@@ -524,6 +563,35 @@ components:
 | 
			
		||||
          items:
 | 
			
		||||
            $ref : '#/components/schemas/DefaultConfiguration'
 | 
			
		||||
 | 
			
		||||
    DefaultFirmware:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        deviceType:
 | 
			
		||||
          type: string
 | 
			
		||||
        description:
 | 
			
		||||
          type: string
 | 
			
		||||
        uri:
 | 
			
		||||
          type: string
 | 
			
		||||
        revision:
 | 
			
		||||
          type: string
 | 
			
		||||
        imageCreationDate:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        created:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        lastModified:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
 | 
			
		||||
    DefaultFirmwareList:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        firmwares:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            $ref: '#/components/schemas/DefaultFirmware'
 | 
			
		||||
 | 
			
		||||
    UpgradeRequest:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
@@ -845,6 +913,114 @@ components:
 | 
			
		||||
            kafkaClients:
 | 
			
		||||
              type: integer
 | 
			
		||||
 | 
			
		||||
    RRM_Kick:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        action:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - kick
 | 
			
		||||
        addr:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: mac
 | 
			
		||||
        reason:
 | 
			
		||||
          type: integer
 | 
			
		||||
          default: 5
 | 
			
		||||
        ban_time:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
 | 
			
		||||
    RRM_channel_switch:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        action:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - channel_switch
 | 
			
		||||
        bssid:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: mac
 | 
			
		||||
        channel:
 | 
			
		||||
          type: integer
 | 
			
		||||
 | 
			
		||||
    RRM_tx_power:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        action:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - tx_power
 | 
			
		||||
        bssid:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: mac
 | 
			
		||||
        level:
 | 
			
		||||
          type: integer
 | 
			
		||||
 | 
			
		||||
    RRM_beacon_request:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        action:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - beacon_request
 | 
			
		||||
        addr:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: mac
 | 
			
		||||
        ssid:
 | 
			
		||||
          type: string
 | 
			
		||||
        channel:
 | 
			
		||||
          type: integer
 | 
			
		||||
 | 
			
		||||
    RRM_bss_transition:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        action:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - bss_transition
 | 
			
		||||
        addr:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: mac
 | 
			
		||||
        neighbors:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            type: string
 | 
			
		||||
            format: mac
 | 
			
		||||
 | 
			
		||||
    RRM_neighbors:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        action:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - neighbors
 | 
			
		||||
        bssid:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: mac
 | 
			
		||||
        neighbors:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            type: string
 | 
			
		||||
            format: mac
 | 
			
		||||
 | 
			
		||||
    RRM_action:
 | 
			
		||||
      type: object
 | 
			
		||||
      oneOf:
 | 
			
		||||
        - $ref: '#/components/schemas/RRM_Kick'
 | 
			
		||||
        - $ref: '#/components/schemas/RRM_channel_switch'
 | 
			
		||||
        - $ref: '#/components/schemas/RRM_tx_power'
 | 
			
		||||
        - $ref: '#/components/schemas/RRM_beacon_request'
 | 
			
		||||
        - $ref: '#/components/schemas/RRM_bss_transition'
 | 
			
		||||
        - $ref: '#/components/schemas/RRM_neighbors'
 | 
			
		||||
 | 
			
		||||
    RRM_actions:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        actions:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            $ref: '#/components/schemas/RRM_action'
 | 
			
		||||
 | 
			
		||||
    #########################################################################################
 | 
			
		||||
    ##
 | 
			
		||||
    ## These are endpoints that all services in the uCentral stack must provide
 | 
			
		||||
@@ -913,12 +1089,6 @@ components:
 | 
			
		||||
            - $ref: '#/components/schemas/StringList'
 | 
			
		||||
            - $ref: '#/components/schemas/TagValuePairList'
 | 
			
		||||
 | 
			
		||||
    SystemCommandResults:
 | 
			
		||||
      type: object
 | 
			
		||||
      oneOf:
 | 
			
		||||
        - $ref: '#/components/schemas/StringList'
 | 
			
		||||
        - $ref: '#/components/schemas/TagValuePairList'
 | 
			
		||||
 | 
			
		||||
    NoteInfo:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
@@ -958,6 +1128,33 @@ components:
 | 
			
		||||
                type: integer
 | 
			
		||||
                format: int64
 | 
			
		||||
 | 
			
		||||
    SystemResources:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        numberOfFileDescriptors:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        currRealMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        peakRealMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        currVirtMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        peakVirtMem:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
 | 
			
		||||
    SystemCommandResults:
 | 
			
		||||
      type: object
 | 
			
		||||
      oneOf:
 | 
			
		||||
        - $ref: '#/components/schemas/SystemResources'
 | 
			
		||||
        - $ref: '#/components/schemas/SystemInfoResults'
 | 
			
		||||
        - $ref: '#/components/schemas/StringList'
 | 
			
		||||
        - $ref: '#/components/schemas/TagValuePairList'
 | 
			
		||||
 | 
			
		||||
    SystemCommandSetLogLevel:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
@@ -1260,6 +1457,30 @@ components:
 | 
			
		||||
          $ref: '#/components/schemas/RadiusProxyServerConfig'
 | 
			
		||||
        coaConfig:
 | 
			
		||||
          $ref: '#/components/schemas/RadiusProxyServerConfig'
 | 
			
		||||
        radsecPoolType:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - generic
 | 
			
		||||
            - orion
 | 
			
		||||
            - globalreach
 | 
			
		||||
            - radsec
 | 
			
		||||
          default:
 | 
			
		||||
            generic
 | 
			
		||||
        poolProxyIp:
 | 
			
		||||
          type: string
 | 
			
		||||
          description: This is the fake IP for the entire pool
 | 
			
		||||
          example:
 | 
			
		||||
            - These addresses must match the addresses in the AP configuration and must start with 0.0
 | 
			
		||||
            - 0.0.0.1
 | 
			
		||||
            - 0.0.1.1
 | 
			
		||||
        radsecPoolKeepAlive:
 | 
			
		||||
          type: integer
 | 
			
		||||
          description: The keep alive value in seconds. Usually 30s or less.
 | 
			
		||||
          format: int64
 | 
			
		||||
          default: 25
 | 
			
		||||
        enabled:
 | 
			
		||||
          type: boolean
 | 
			
		||||
          default: true
 | 
			
		||||
 | 
			
		||||
    RadiusProxyPoolList:
 | 
			
		||||
      type: object
 | 
			
		||||
@@ -1269,6 +1490,125 @@ components:
 | 
			
		||||
          items:
 | 
			
		||||
            $ref: '#/components/schemas/RadiusProxyPool'
 | 
			
		||||
 | 
			
		||||
    RadiusSession:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        started:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        lastTransaction:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        inputPackets:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        outputPackets:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        inputOctets:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        outputOctets:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        inputGigaWords:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        outputGigaWords:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        sessionTime:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        destination:
 | 
			
		||||
          type: string
 | 
			
		||||
        userName:
 | 
			
		||||
          type: string
 | 
			
		||||
        accountingSessionId:
 | 
			
		||||
          type: string
 | 
			
		||||
        accountingMultiSessionId:
 | 
			
		||||
          type: string
 | 
			
		||||
        callingStationId:
 | 
			
		||||
          type: string
 | 
			
		||||
 | 
			
		||||
    RadiusSessionList:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        sessions:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            $ref: '#/components/schemas/RadiusSession'
 | 
			
		||||
 | 
			
		||||
    RadiusCoADMParameters:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        accountingSessionId:
 | 
			
		||||
          type: string
 | 
			
		||||
        accountingMultiSessionId:
 | 
			
		||||
          type: string
 | 
			
		||||
        callingStationId:
 | 
			
		||||
          type: string
 | 
			
		||||
        chargeableUserIdentity:
 | 
			
		||||
          type: string
 | 
			
		||||
        userName:
 | 
			
		||||
          type: string
 | 
			
		||||
 | 
			
		||||
    DeviceTransferRequest:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        serialNumber:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: uuid
 | 
			
		||||
        server:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: hostname
 | 
			
		||||
        port:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int32
 | 
			
		||||
 | 
			
		||||
    DeviceCertificateUpdateRequest:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        serialNumber:
 | 
			
		||||
          type: string
 | 
			
		||||
        encodedCertificate:
 | 
			
		||||
          type: string
 | 
			
		||||
          format: base64
 | 
			
		||||
          description: This is a base64 encoded string of the certificate bundle (the current bundle .tar.gz file from the PKI portal)
 | 
			
		||||
 | 
			
		||||
    ReenrollRequest:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        serialNumber:
 | 
			
		||||
          type: string
 | 
			
		||||
        when:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
 | 
			
		||||
    PowerCycleRequest:
 | 
			
		||||
      type: object
 | 
			
		||||
      properties:
 | 
			
		||||
        serial:
 | 
			
		||||
          type: string
 | 
			
		||||
        when:
 | 
			
		||||
          type: integer
 | 
			
		||||
          format: int64
 | 
			
		||||
        ports:
 | 
			
		||||
          type: array
 | 
			
		||||
          items:
 | 
			
		||||
            type: object
 | 
			
		||||
            properties:
 | 
			
		||||
              name:
 | 
			
		||||
                type: string
 | 
			
		||||
                example:
 | 
			
		||||
                  - Ethernet0
 | 
			
		||||
              cycle:
 | 
			
		||||
                type: integer
 | 
			
		||||
                default: 10000
 | 
			
		||||
                minimum: 1
 | 
			
		||||
                maximum: 60000
 | 
			
		||||
                description: off time in milliseconds
 | 
			
		||||
 | 
			
		||||
paths:
 | 
			
		||||
  /devices:
 | 
			
		||||
    get:
 | 
			
		||||
@@ -1360,6 +1700,22 @@ paths:
 | 
			
		||||
            type: integer
 | 
			
		||||
            default: 70
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: return only devices matching a certain platform of AP or SWITCH
 | 
			
		||||
          name: platform
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            default: ALL
 | 
			
		||||
            enum:
 | 
			
		||||
              - all
 | 
			
		||||
              - ap
 | 
			
		||||
              - switch
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: only devices which are not provisioned
 | 
			
		||||
          name: includeProvisioned
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: List devices
 | 
			
		||||
@@ -1377,6 +1733,58 @@ paths:
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    delete:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Devices
 | 
			
		||||
      summary: Delete a list of devices matching a criteria
 | 
			
		||||
      description: Delete a list of devices matching a criteria
 | 
			
		||||
      operationId: deleteDeviceList
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: Supply a list of devices comma separated
 | 
			
		||||
          name: select
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            example: serial1,serial2,serial3
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: Only simulated devices
 | 
			
		||||
          name: simulatedOnly
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
            default: false
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: MAC address must match this pattern.  Mutually exclusive with oldestContact
 | 
			
		||||
          name: macPattern
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            example:
 | 
			
		||||
              - "aabbcc*"
 | 
			
		||||
              - "*aabbcc*"
 | 
			
		||||
              - "*cccddee"
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: lastRecordedContact older than this value. Mutually exclusive with macPattern
 | 
			
		||||
          name: oldestContact
 | 
			
		||||
          schema:
 | 
			
		||||
            type: integer
 | 
			
		||||
            format: int64
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          description: Filter the results
 | 
			
		||||
          name: simulatedDevices
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
          required: false
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /commands:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
@@ -1633,6 +2041,123 @@ paths:
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /default_firmwares:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Firmware
 | 
			
		||||
      summary: Retrieve the lists of all default firmwares.
 | 
			
		||||
      description: Retrieve the lists of all default firmwares.
 | 
			
		||||
      operationId: getDefaultFirmwares
 | 
			
		||||
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: List of default firmwares
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                $ref: '#/components/schemas/DefaultFirmwareList'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /default_firmware/{deviceType}:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Firmware
 | 
			
		||||
      summary: Retrieve a default firmware.
 | 
			
		||||
      description: Retrieve a default firmware.
 | 
			
		||||
      operationId: getDefaultFirmware
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: deviceType
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: Default firmware included
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                $ref: '#/components/schemas/DefaultFirmware'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Firmware
 | 
			
		||||
      summary: Create a default firmware.
 | 
			
		||||
      description: Create a default firmware.
 | 
			
		||||
      operationId: createDefaultFirmware
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: deviceType
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Information used to create the new firmware entry
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              $ref: '#/components/schemas/DefaultFirmware'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    delete:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Firmware
 | 
			
		||||
      summary: Delete a default default firmware
 | 
			
		||||
      description: Delete a default default firmware
 | 
			
		||||
      operationId: deleteDefaultFirmware
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: deviceType
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      responses:
 | 
			
		||||
        204:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
    put:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Firmware
 | 
			
		||||
      summary: Update a default firmware
 | 
			
		||||
      description: Update a default firmware
 | 
			
		||||
      operationId: updateDefaultFirmware
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: deviceType
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Firmware details
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              $ref: '#/components/schemas/DefaultFirmware'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
@@ -1775,7 +2300,7 @@ paths:
 | 
			
		||||
            format: int64
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: logType
 | 
			
		||||
          description: 0=any kind of logs (default) 0=normal logs only 1=crash logs only
 | 
			
		||||
          description: 0=any kind of logs (default) 0=normal logs, 1=crash logs, 2=reboot logs only
 | 
			
		||||
          schema:
 | 
			
		||||
            type: integer
 | 
			
		||||
            format: int64
 | 
			
		||||
@@ -2113,32 +2638,6 @@ paths:
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/command:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Commands
 | 
			
		||||
      summary: Post a command to a device
 | 
			
		||||
      operationId: executeCommand
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Command details
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              $ref: '#/components/schemas/CommandDetails'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/schemas/CommandInfo'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/configure:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
@@ -2270,7 +2769,7 @@ paths:
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/:
 | 
			
		||||
  /device/{serialNumber}/script:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Commands
 | 
			
		||||
@@ -2484,6 +2983,142 @@ paths:
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/rrm:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Commands
 | 
			
		||||
      summary: Send RRM commands to a device.
 | 
			
		||||
      operationId: sendRRMcommandsForADevice
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Commands to send
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              $ref: '#/components/schemas/RRM_actions'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/transfer:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Commands
 | 
			
		||||
      summary: Transfer a device to a new redirector.
 | 
			
		||||
      operationId: transferDevice
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Transfer details
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              type: array
 | 
			
		||||
              items:
 | 
			
		||||
                $ref: '#/components/schemas/DeviceTransferRequest'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/certupdate:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Commands
 | 
			
		||||
      summary: Update the certificates for a device.
 | 
			
		||||
      operationId: updateCertificates
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Certificate update details
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              type: array
 | 
			
		||||
              items:
 | 
			
		||||
                $ref: '#/components/schemas/DeviceCertificateUpdateRequest'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/reenroll:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Commands
 | 
			
		||||
      summary: Reenroll operational certificate for the device.
 | 
			
		||||
      operationId: reenrollCertificate
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Reenroll operational certificate for the device
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              $ref: '#/components/schemas/ReenrollRequest'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /device/{serialNumber}/powercycle:
 | 
			
		||||
    post:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Commands
 | 
			
		||||
      summary: Perform PoE power cycle for some PoE ports.
 | 
			
		||||
      operationId: performPowerCycle
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: Certificate update details
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              type: array
 | 
			
		||||
              items:
 | 
			
		||||
                $ref: '#/components/schemas/PowerCycleRequest'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /ouis:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
@@ -2723,9 +3358,6 @@ paths:
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /blacklist:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
@@ -2918,6 +3550,81 @@ paths:
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
 | 
			
		||||
  /radiusSessions/{serialNumber}:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Radius Sessions
 | 
			
		||||
      summary: Retrieve the RADIUS sessions for a given AP
 | 
			
		||||
      operationId: getAPRadiusSessions
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            example: for searches or listing only serial number, set the serialNumber to 0
 | 
			
		||||
          required: true
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: serialNumberOnly
 | 
			
		||||
          schema:
 | 
			
		||||
            type: boolean
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: userName
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: false
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: mac
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: false
 | 
			
		||||
          example: aa:bb:cc:dd:ee:ff
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: AP List
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                oneOf:
 | 
			
		||||
                  - $ref: '#/components/schemas/RadiusSessionList'
 | 
			
		||||
                  - $ref: '#/components/schemas/SerialNumberList'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
    put:
 | 
			
		||||
      tags:
 | 
			
		||||
        - Radius Sessions
 | 
			
		||||
      summary: Retrieve the RADIUS sessions for a given AP
 | 
			
		||||
      operationId: putAPRadiusSessions
 | 
			
		||||
      parameters:
 | 
			
		||||
        - in: path
 | 
			
		||||
          name: serialNumber
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
          required: true
 | 
			
		||||
        - in: query
 | 
			
		||||
          name: operation
 | 
			
		||||
          schema:
 | 
			
		||||
            type: string
 | 
			
		||||
            enum:
 | 
			
		||||
              - coadm
 | 
			
		||||
              - disconnectUser
 | 
			
		||||
      requestBody:
 | 
			
		||||
        description: operationParameters
 | 
			
		||||
        content:
 | 
			
		||||
          application/json:
 | 
			
		||||
            schema:
 | 
			
		||||
              oneOf:
 | 
			
		||||
                - $ref: '#/components/schemas/RadiusCoADMParameters'
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          $ref: '#/components/responses/Success'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
          $ref: '#/components/responses/NotFound'
 | 
			
		||||
 | 
			
		||||
  /deviceDashboard:
 | 
			
		||||
    get:
 | 
			
		||||
      tags:
 | 
			
		||||
@@ -3014,16 +3721,12 @@ paths:
 | 
			
		||||
            type: string
 | 
			
		||||
            enum:
 | 
			
		||||
              - info
 | 
			
		||||
              - extraConfiguration
 | 
			
		||||
              - resources
 | 
			
		||||
          required: true
 | 
			
		||||
 | 
			
		||||
      responses:
 | 
			
		||||
        200:
 | 
			
		||||
          description: Successful command execution
 | 
			
		||||
          content:
 | 
			
		||||
            application/json:
 | 
			
		||||
              schema:
 | 
			
		||||
                oneOf:
 | 
			
		||||
                  - $ref: '#/components/schemas/SystemInfoResults'
 | 
			
		||||
          $ref: '#/components/schemas/SystemCommandResults'
 | 
			
		||||
        403:
 | 
			
		||||
          $ref: '#/components/responses/Unauthorized'
 | 
			
		||||
        404:
 | 
			
		||||
 
 | 
			
		||||
@@ -85,6 +85,7 @@ iptocountry.ipdata.apikey = ${IPTOCOUNTRY_IPDATA_APIKEY}
 | 
			
		||||
 | 
			
		||||
autoprovisioning.process = ${AUTOPROVISIONING_PROCESS}
 | 
			
		||||
 | 
			
		||||
openwifi.session.timeout = ${DEVICE_SESSION_TIMEOUT}
 | 
			
		||||
#
 | 
			
		||||
# rtty
 | 
			
		||||
#
 | 
			
		||||
@@ -103,6 +104,12 @@ radius.proxy.accounting.port = ${RADIUS_PROXY_ACCOUNTING_PORT}
 | 
			
		||||
radius.proxy.authentication.port = ${RADIUS_PROXY_AUTHENTICATION_PORT}
 | 
			
		||||
radius.proxy.coa.port = ${RADIUS_PROXY_COA_PORT}
 | 
			
		||||
 | 
			
		||||
iptocountry.default = ${IPINFO_DEFAULT_COUNTRY}
 | 
			
		||||
#iptocountry.provider = ipinfo
 | 
			
		||||
#iptocountry.provider = ipdata
 | 
			
		||||
#iptocountry.ipinfo.token =
 | 
			
		||||
#iptocountry.ipdata.apikey =
 | 
			
		||||
 | 
			
		||||
#############################
 | 
			
		||||
# Generic information for all micro services
 | 
			
		||||
#############################
 | 
			
		||||
@@ -138,7 +145,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}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 | 
			
		||||
@@ -2,55 +2,49 @@
 | 
			
		||||
// Created by stephane bourque on 2022-02-03.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Connection.h"
 | 
			
		||||
 | 
			
		||||
#include "Poco/Base64Decoder.h"
 | 
			
		||||
#include "Poco/Net/Context.h"
 | 
			
		||||
#include "Poco/Net/HTTPServerRequestImpl.h"
 | 
			
		||||
#include "Poco/Net/HTTPServerResponseImpl.h"
 | 
			
		||||
#include "Poco/Net/NetException.h"
 | 
			
		||||
#include "Poco/Net/SSLException.h"
 | 
			
		||||
#include "Poco/Net/SecureStreamSocketImpl.h"
 | 
			
		||||
#include "Poco/Net/WebSocketImpl.h"
 | 
			
		||||
#include "Poco/zlib.h"
 | 
			
		||||
#include <Poco/Base64Decoder.h>
 | 
			
		||||
#include <Poco/Net/Context.h>
 | 
			
		||||
#include <Poco/Net/HTTPServerRequestImpl.h>
 | 
			
		||||
#include <Poco/Net/HTTPServerResponseImpl.h>
 | 
			
		||||
#include <Poco/Net/NetException.h>
 | 
			
		||||
#include <Poco/Net/SSLException.h>
 | 
			
		||||
#include <Poco/Net/SecureStreamSocketImpl.h>
 | 
			
		||||
#include <Poco/Net/WebSocketImpl.h>
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Server.h"
 | 
			
		||||
#include "CentralConfig.h"
 | 
			
		||||
#include "CommandManager.h"
 | 
			
		||||
#include "ConfigurationCache.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "TelemetryStream.h"
 | 
			
		||||
#include <framework/KafkaManager.h>
 | 
			
		||||
#include <framework/MicroServiceFuncs.h>
 | 
			
		||||
#include <framework/utils.h>
 | 
			
		||||
#include <framework/ow_constants.h>
 | 
			
		||||
 | 
			
		||||
#include "GWKafkaEvents.h"
 | 
			
		||||
#include "UI_GW_WebSocketNotifications.h"
 | 
			
		||||
#include "framework/KafkaManager.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "framework/utils.h"
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
#include "RADIUS_proxy_server.h"
 | 
			
		||||
#include <AP_WS_Connection.h>
 | 
			
		||||
#include <AP_WS_Server.h>
 | 
			
		||||
#include <CentralConfig.h>
 | 
			
		||||
#include <CommandManager.h>
 | 
			
		||||
#include <StorageService.h>
 | 
			
		||||
#include <RADIUSSessionTracker.h>
 | 
			
		||||
#include <RADIUS_proxy_server.h>
 | 
			
		||||
#include <GWKafkaEvents.h>
 | 
			
		||||
#include <UI_GW_WebSocketNotifications.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
#define DBL                                                                                        \
 | 
			
		||||
	{                                                                                              \
 | 
			
		||||
		std::cout << __LINE__ << "  ID: " << ConnectionId_ << "  Ser: " << SerialNumber_           \
 | 
			
		||||
				  << std::endl;                                                                    \
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::LogException(const Poco::Exception &E) {
 | 
			
		||||
		poco_information(Logger_, fmt::format("EXCEPTION({}): {}", CId_, E.displayText()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AP_WS_Connection::AP_WS_Connection(Poco::Net::HTTPServerRequest &request,
 | 
			
		||||
									   Poco::Net::HTTPServerResponse &response,
 | 
			
		||||
									   uint64_t connection_id, Poco::Logger &L,
 | 
			
		||||
									   Poco::Net::SocketReactor &R)
 | 
			
		||||
		: Logger_(L), Reactor_(R) {
 | 
			
		||||
		State_.sessionId = connection_id;
 | 
			
		||||
									   uint64_t session_id, Poco::Logger &L,
 | 
			
		||||
									   std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> R)
 | 
			
		||||
		: Logger_(L) {
 | 
			
		||||
 | 
			
		||||
		Reactor_ = R.first;
 | 
			
		||||
		DbSession_ = R.second;
 | 
			
		||||
		State_.sessionId = session_id;
 | 
			
		||||
 | 
			
		||||
		WS_ = std::make_unique<Poco::Net::WebSocket>(request, response);
 | 
			
		||||
 | 
			
		||||
		auto TS = Poco::Timespan(360, 0);
 | 
			
		||||
@@ -60,28 +54,86 @@ namespace OpenWifi {
 | 
			
		||||
		WS_->setNoDelay(false);
 | 
			
		||||
		WS_->setKeepAlive(true);
 | 
			
		||||
		WS_->setBlocking(false);
 | 
			
		||||
		uuid_ = MicroServiceRandom(std::numeric_limits<std::uint64_t>::max()-1);
 | 
			
		||||
 | 
			
		||||
		Reactor_.addEventHandler(*WS_,
 | 
			
		||||
								 Poco::NObserver<AP_WS_Connection, Poco::Net::ReadableNotification>(
 | 
			
		||||
									 *this, &AP_WS_Connection::OnSocketReadable));
 | 
			
		||||
		Reactor_.addEventHandler(*WS_,
 | 
			
		||||
								 Poco::NObserver<AP_WS_Connection, Poco::Net::ShutdownNotification>(
 | 
			
		||||
									 *this, &AP_WS_Connection::OnSocketShutdown));
 | 
			
		||||
		Reactor_.addEventHandler(*WS_,
 | 
			
		||||
								 Poco::NObserver<AP_WS_Connection, Poco::Net::ErrorNotification>(
 | 
			
		||||
									 *this, &AP_WS_Connection::OnSocketError));
 | 
			
		||||
		AP_WS_Server()->IncrementConnectionCount();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::Start() {
 | 
			
		||||
		Registered_ = true;
 | 
			
		||||
		Valid_ = true;
 | 
			
		||||
		LastContact_ = Utils::Now();
 | 
			
		||||
 | 
			
		||||
		Reactor_->addEventHandler(*WS_,
 | 
			
		||||
								  Poco::NObserver<AP_WS_Connection, Poco::Net::ReadableNotification>(
 | 
			
		||||
									  *this, &AP_WS_Connection::OnSocketReadable));
 | 
			
		||||
		Reactor_->addEventHandler(*WS_,
 | 
			
		||||
								  Poco::NObserver<AP_WS_Connection, Poco::Net::ShutdownNotification>(
 | 
			
		||||
									  *this, &AP_WS_Connection::OnSocketShutdown));
 | 
			
		||||
		Reactor_->addEventHandler(*WS_,
 | 
			
		||||
								  Poco::NObserver<AP_WS_Connection, Poco::Net::ErrorNotification>(
 | 
			
		||||
									  *this, &AP_WS_Connection::OnSocketError));
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AP_WS_Connection::~AP_WS_Connection() {
 | 
			
		||||
		std::lock_guard G(ConnectionMutex_);
 | 
			
		||||
		AP_WS_Server()->DecrementConnectionCount();
 | 
			
		||||
		EndConnection();
 | 
			
		||||
		poco_debug(Logger_, fmt::format("TERMINATION({}): Session={}, Connection removed.", SerialNumber_,
 | 
			
		||||
										State_.sessionId));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void NotifyKafkaDisconnect(const std::string &SerialNumber, std::uint64_t uuid) {
 | 
			
		||||
		try {
 | 
			
		||||
			Poco::JSON::Object Disconnect;
 | 
			
		||||
			Poco::JSON::Object Details;
 | 
			
		||||
			Details.set(uCentralProtocol::SERIALNUMBER, SerialNumber);
 | 
			
		||||
			Details.set(uCentralProtocol::TIMESTAMP, Utils::Now());
 | 
			
		||||
			Details.set(uCentralProtocol::UUID,uuid);
 | 
			
		||||
			Disconnect.set(uCentralProtocol::DISCONNECTION, Details);
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber, Disconnect);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::EndConnection() {
 | 
			
		||||
		bool expectedValue=false;
 | 
			
		||||
		if (Dead_.compare_exchange_strong(expectedValue,true,std::memory_order_release,std::memory_order_relaxed)) {
 | 
			
		||||
 | 
			
		||||
			if(!SerialNumber_.empty() && State_.LastContact!=0) {
 | 
			
		||||
				StorageService()->SetDeviceLastRecordedContact(SerialNumber_, State_.LastContact);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (Registered_) {
 | 
			
		||||
				Registered_ = false;
 | 
			
		||||
				Reactor_->removeEventHandler(
 | 
			
		||||
					*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ReadableNotification>(
 | 
			
		||||
							  *this, &AP_WS_Connection::OnSocketReadable));
 | 
			
		||||
				Reactor_->removeEventHandler(
 | 
			
		||||
					*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ShutdownNotification>(
 | 
			
		||||
							  *this, &AP_WS_Connection::OnSocketShutdown));
 | 
			
		||||
				Reactor_->removeEventHandler(
 | 
			
		||||
					*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ErrorNotification>(
 | 
			
		||||
							  *this, &AP_WS_Connection::OnSocketError));
 | 
			
		||||
				Registered_=false;
 | 
			
		||||
			}
 | 
			
		||||
			WS_->close();
 | 
			
		||||
 | 
			
		||||
			if(!SerialNumber_.empty()) {
 | 
			
		||||
				DeviceDisconnectionCleanup(SerialNumber_, uuid_);
 | 
			
		||||
			}
 | 
			
		||||
			AP_WS_Server()->AddCleanupSession(State_.sessionId, SerialNumberInt_);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Connection::ValidatedDevice() {
 | 
			
		||||
 | 
			
		||||
		if(Dead_)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		if (DeviceValidated_)
 | 
			
		||||
			return true;
 | 
			
		||||
 | 
			
		||||
		if (!Valid_)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		std::lock_guard Lock(ConnectionMutex_);
 | 
			
		||||
		try {
 | 
			
		||||
			auto SockImpl = dynamic_cast<Poco::Net::WebSocketImpl *>(WS_->impl());
 | 
			
		||||
			auto SS =
 | 
			
		||||
@@ -96,7 +148,6 @@ namespace OpenWifi {
 | 
			
		||||
				poco_warning(Logger_, fmt::format("TLS-CONNECTION({}): Session={} Connection is "
 | 
			
		||||
												  "NOT secure. Device is not allowed.",
 | 
			
		||||
												  CId_, State_.sessionId));
 | 
			
		||||
				EndConnection();
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -109,7 +160,6 @@ namespace OpenWifi {
 | 
			
		||||
					Logger_,
 | 
			
		||||
					fmt::format("TLS-CONNECTION({}): Session={} No certificates available..", CId_,
 | 
			
		||||
								State_.sessionId));
 | 
			
		||||
				EndConnection();
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -120,13 +170,21 @@ namespace OpenWifi {
 | 
			
		||||
							 fmt::format("TLS-CONNECTION({}): Session={} Device certificate is not "
 | 
			
		||||
										 "valid. Device is not allowed.",
 | 
			
		||||
										 CId_, State_.sessionId));
 | 
			
		||||
				EndConnection();
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			CN_ = Poco::trim(Poco::toLower(PeerCert.commonName()));
 | 
			
		||||
			if(!Utils::ValidSerialNumber(CN_)) {
 | 
			
		||||
				poco_trace(Logger_,
 | 
			
		||||
						   fmt::format("TLS-CONNECTION({}): Session={} Invalid serial number: CN={}", CId_,
 | 
			
		||||
									   State_.sessionId, CN_));
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			SerialNumber_ = CN_;
 | 
			
		||||
			SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_);
 | 
			
		||||
 | 
			
		||||
			State_.VerifiedCertificate = GWObjects::VALID_CERTIFICATE;
 | 
			
		||||
			poco_information(Logger_,
 | 
			
		||||
			poco_trace(Logger_,
 | 
			
		||||
					   fmt::format("TLS-CONNECTION({}): Session={} Valid certificate: CN={}", CId_,
 | 
			
		||||
								   State_.sessionId, CN_));
 | 
			
		||||
 | 
			
		||||
@@ -134,26 +192,28 @@ namespace OpenWifi {
 | 
			
		||||
				poco_warning(Logger_, fmt::format("TLS-CONNECTION({}): Session={} Sim Device {} is "
 | 
			
		||||
												  "not allowed. Disconnecting.",
 | 
			
		||||
												  CId_, State_.sessionId, CN_));
 | 
			
		||||
				EndConnection();
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(AP_WS_Server::IsSim(SerialNumber_)) {
 | 
			
		||||
				State_.VerifiedCertificate = GWObjects::SIMULATED;
 | 
			
		||||
				Simulated_ = true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			std::string reason, author;
 | 
			
		||||
			std::uint64_t created;
 | 
			
		||||
			if (!CN_.empty() && StorageService()->IsBlackListed(CN_, reason, author, created)) {
 | 
			
		||||
				DeviceBlacklistedKafkaEvent KE(CN_, Utils::Now(), reason, author, created, CId_);
 | 
			
		||||
			if (!CN_.empty() && StorageService()->IsBlackListed(SerialNumberInt_, reason, author, created)) {
 | 
			
		||||
				DeviceBlacklistedKafkaEvent KE(Utils::SerialNumberToInt(CN_), Utils::Now(), reason, author, created, CId_);
 | 
			
		||||
				poco_warning(
 | 
			
		||||
					Logger_,
 | 
			
		||||
					fmt::format(
 | 
			
		||||
						"TLS-CONNECTION({}): Session={} Device {} is black listed. Disconnecting.",
 | 
			
		||||
						CId_, State_.sessionId, CN_));
 | 
			
		||||
				EndConnection();
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			State_.certificateExpiryDate = PeerCert.expiresOn().timestamp().epochTime();
 | 
			
		||||
			SerialNumber_ = CN_;
 | 
			
		||||
			SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_);
 | 
			
		||||
			State_.certificateIssuerName = PeerCert.issuerName();
 | 
			
		||||
 | 
			
		||||
			poco_trace(Logger_,
 | 
			
		||||
					   fmt::format("TLS-CONNECTION({}): Session={} CN={} Completed. (t={})", CId_,
 | 
			
		||||
@@ -217,138 +277,14 @@ namespace OpenWifi {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static void NotifyKafkaDisconnect(const std::string &SerialNumber) {
 | 
			
		||||
		try {
 | 
			
		||||
			Poco::JSON::Object Disconnect;
 | 
			
		||||
			Poco::JSON::Object Details;
 | 
			
		||||
			Details.set(uCentralProtocol::SERIALNUMBER, SerialNumber);
 | 
			
		||||
			Details.set(uCentralProtocol::TIMESTAMP, Utils::Now());
 | 
			
		||||
			Disconnect.set(uCentralProtocol::DISCONNECTION, Details);
 | 
			
		||||
			Poco::JSON::Stringifier Stringify;
 | 
			
		||||
			std::ostringstream OS;
 | 
			
		||||
			Stringify.condense(Disconnect, OS);
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber, OS.str());
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
	void AP_WS_Connection::DeviceDisconnectionCleanup(const std::string &SerialNumber, std::uint64_t uuid) {
 | 
			
		||||
		if (KafkaManager()->Enabled()) {
 | 
			
		||||
			NotifyKafkaDisconnect(SerialNumber, uuid);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AP_WS_Connection::~AP_WS_Connection() {
 | 
			
		||||
		Valid_ = false;
 | 
			
		||||
		EndConnection();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::EndConnection() {
 | 
			
		||||
		Valid_ = false;
 | 
			
		||||
		if (!Dead_.test_and_set()) {
 | 
			
		||||
 | 
			
		||||
			if (Registered_) {
 | 
			
		||||
				Registered_ = false;
 | 
			
		||||
				Reactor_.removeEventHandler(
 | 
			
		||||
					*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ReadableNotification>(
 | 
			
		||||
							  *this, &AP_WS_Connection::OnSocketReadable));
 | 
			
		||||
				Reactor_.removeEventHandler(
 | 
			
		||||
					*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ShutdownNotification>(
 | 
			
		||||
							  *this, &AP_WS_Connection::OnSocketShutdown));
 | 
			
		||||
				Reactor_.removeEventHandler(
 | 
			
		||||
					*WS_, Poco::NObserver<AP_WS_Connection, Poco::Net::ErrorNotification>(
 | 
			
		||||
							  *this, &AP_WS_Connection::OnSocketError));
 | 
			
		||||
			}
 | 
			
		||||
			WS_->close();
 | 
			
		||||
 | 
			
		||||
			if (KafkaManager()->Enabled() && !SerialNumber_.empty()) {
 | 
			
		||||
				std::string s(SerialNumber_);
 | 
			
		||||
				std::thread t([s]() { NotifyKafkaDisconnect(s); });
 | 
			
		||||
				t.detach();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			auto SessionDeleted = AP_WS_Server()->EndSession(State_.sessionId, SerialNumberInt_);
 | 
			
		||||
			if (SessionDeleted) {
 | 
			
		||||
				GWWebSocketNotifications::SingleDevice_t N;
 | 
			
		||||
				N.content.serialNumber = SerialNumber_;
 | 
			
		||||
				GWWebSocketNotifications::DeviceDisconnected(N);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Connection::LookForUpgrade(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 = ConfigurationCache().CurrentConfig(SerialNumberInt_);
 | 
			
		||||
		if (GoodConfig && (GoodConfig == UUID || GoodConfig == State_.PendingUUID)) {
 | 
			
		||||
			UpgradedUUID = UUID;
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::Device D;
 | 
			
		||||
		if (StorageService()->GetDevice(SerialNumber_, D)) {
 | 
			
		||||
 | 
			
		||||
			if(D.pendingUUID!=0 && UUID==D.pendingUUID) {
 | 
			
		||||
				//	so we sent an upgrade to a device, and now it is completing now...
 | 
			
		||||
				UpgradedUUID = D.pendingUUID;
 | 
			
		||||
				StorageService()->CompleteDeviceConfigurationChange(SerialNumber_);
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			//	This is the case where the cache is empty after a restart. So GoodConfig will 0. If
 | 
			
		||||
			// the device already 	has the right UUID, we just return.
 | 
			
		||||
			if (D.UUID == UUID) {
 | 
			
		||||
				UpgradedUUID = UUID;
 | 
			
		||||
				ConfigurationCache().Add(SerialNumberInt_, UUID);
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Config::Config Cfg(D.Configuration);
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Cfg.SetUUID(D.UUID);
 | 
			
		||||
			D.Configuration = Cfg.get();
 | 
			
		||||
			State_.PendingUUID = UpgradedUUID = D.UUID;
 | 
			
		||||
 | 
			
		||||
			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);
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
		RADIUSSessionTracker()->DeviceDisconnect(SerialNumber);
 | 
			
		||||
		GWWebSocketNotifications::SingleDevice_t N;
 | 
			
		||||
		N.content.serialNumber = SerialNumber;
 | 
			
		||||
		GWWebSocketNotifications::DeviceDisconnected(N);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::ProcessJSONRPCResult(Poco::JSON::Object::Ptr Doc) {
 | 
			
		||||
@@ -429,8 +365,8 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		std::string reason, author;
 | 
			
		||||
		std::uint64_t created;
 | 
			
		||||
		if (StorageService()->IsBlackListed(Serial, reason, author, created)) {
 | 
			
		||||
			DeviceBlacklistedKafkaEvent KE(CN_, Utils::Now(), reason, author, created, CId_);
 | 
			
		||||
		if (StorageService()->IsBlackListed(SerialNumberInt_, reason, author, created)) {
 | 
			
		||||
			DeviceBlacklistedKafkaEvent KE(Utils::SerialNumberToInt(CN_), Utils::Now(), reason, author, created, CId_);
 | 
			
		||||
			Poco::Exception E(
 | 
			
		||||
				fmt::format("BLACKLIST({}): device is blacklisted and not allowed to connect.",
 | 
			
		||||
							Serial),
 | 
			
		||||
@@ -495,6 +431,10 @@ namespace OpenWifi {
 | 
			
		||||
			Process_wifiscan(ParamsObj);
 | 
			
		||||
		} break;
 | 
			
		||||
 | 
			
		||||
		case uCentralProtocol::Events::ET_REBOOTLOG: {
 | 
			
		||||
			Process_rebootLog(ParamsObj);
 | 
			
		||||
		} break;
 | 
			
		||||
 | 
			
		||||
		// 	this will never be called but some compilers will complain if we do not have a case for
 | 
			
		||||
		//	every single values of an enum
 | 
			
		||||
		case uCentralProtocol::Events::ET_UNKNOWN: {
 | 
			
		||||
@@ -556,17 +496,17 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Connection::SetWebSocketTelemetryReporting(
 | 
			
		||||
		uint64_t RPCID, uint64_t Interval, uint64_t LifeTime,
 | 
			
		||||
		std::uint64_t RPCID, std::uint64_t Interval, std::uint64_t LifeTime,
 | 
			
		||||
		const std::vector<std::string> &TelemetryTypes) {
 | 
			
		||||
		std::unique_lock Lock(TelemetryMutex_);
 | 
			
		||||
		TelemetryWebSocketRefCount_++;
 | 
			
		||||
		TelemetryInterval_ = TelemetryInterval_
 | 
			
		||||
								 ? (Interval < TelemetryInterval_ ? Interval : TelemetryInterval_)
 | 
			
		||||
								 ? (Interval < (std::uint64_t)TelemetryInterval_ ? Interval : (std::uint64_t )TelemetryInterval_)
 | 
			
		||||
								 : Interval;
 | 
			
		||||
		auto TelemetryWebSocketTimer = LifeTime + Utils::Now();
 | 
			
		||||
		TelemetryWebSocketTimer_ = TelemetryWebSocketTimer > TelemetryWebSocketTimer_
 | 
			
		||||
									   ? TelemetryWebSocketTimer
 | 
			
		||||
									   : TelemetryWebSocketTimer_;
 | 
			
		||||
		TelemetryWebSocketTimer_ = TelemetryWebSocketTimer > (std::uint64_t)TelemetryWebSocketTimer_
 | 
			
		||||
									   ? (std::uint64_t)TelemetryWebSocketTimer
 | 
			
		||||
									   : (std::uint64_t)TelemetryWebSocketTimer_;
 | 
			
		||||
		UpdateCounts();
 | 
			
		||||
		if (!TelemetryReporting_) {
 | 
			
		||||
			TelemetryReporting_ = true;
 | 
			
		||||
@@ -582,11 +522,11 @@ namespace OpenWifi {
 | 
			
		||||
		std::unique_lock Lock(TelemetryMutex_);
 | 
			
		||||
		TelemetryKafkaRefCount_++;
 | 
			
		||||
		TelemetryInterval_ = TelemetryInterval_
 | 
			
		||||
								 ? (Interval < TelemetryInterval_ ? Interval : TelemetryInterval_)
 | 
			
		||||
								 ? (Interval < (std::uint64_t)TelemetryInterval_ ? (std::uint64_t)Interval : (std::uint64_t)TelemetryInterval_)
 | 
			
		||||
								 : Interval;
 | 
			
		||||
		auto TelemetryKafkaTimer = LifeTime + Utils::Now();
 | 
			
		||||
		TelemetryKafkaTimer_ =
 | 
			
		||||
			TelemetryKafkaTimer > TelemetryKafkaTimer_ ? TelemetryKafkaTimer : TelemetryKafkaTimer_;
 | 
			
		||||
			TelemetryKafkaTimer > (std::uint64_t)TelemetryKafkaTimer_ ? (std::uint64_t)TelemetryKafkaTimer : (std::uint64_t)TelemetryKafkaTimer_;
 | 
			
		||||
		UpdateCounts();
 | 
			
		||||
		if (!TelemetryReporting_) {
 | 
			
		||||
			TelemetryReporting_ = true;
 | 
			
		||||
@@ -622,49 +562,50 @@ namespace OpenWifi {
 | 
			
		||||
	void AP_WS_Connection::OnSocketShutdown(
 | 
			
		||||
		[[maybe_unused]] const Poco::AutoPtr<Poco::Net::ShutdownNotification> &pNf) {
 | 
			
		||||
		poco_trace(Logger_, fmt::format("SOCKET-SHUTDOWN({}): Closing.", CId_));
 | 
			
		||||
//		std::lock_guard	G(ConnectionMutex_);
 | 
			
		||||
		return EndConnection();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::OnSocketError(
 | 
			
		||||
		[[maybe_unused]] const Poco::AutoPtr<Poco::Net::ErrorNotification> &pNf) {
 | 
			
		||||
		poco_trace(Logger_, fmt::format("SOCKET-ERROR({}): Closing.", CId_));
 | 
			
		||||
//		std::lock_guard	G(ConnectionMutex_);
 | 
			
		||||
		return EndConnection();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::OnSocketReadable(
 | 
			
		||||
		[[maybe_unused]] const Poco::AutoPtr<Poco::Net::ReadableNotification> &pNf) {
 | 
			
		||||
 | 
			
		||||
		if (!Valid_)
 | 
			
		||||
		if (Dead_) //	we are dead, so we do not process anything.
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		if (!AP_WS_Server()->Running())
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
		std::lock_guard	G(ConnectionMutex_);
 | 
			
		||||
 | 
			
		||||
		if (!ValidatedDevice())
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			return ProcessIncomingFrame();
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
			Logger_.log(E);
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
		} catch (const std::exception &E) {
 | 
			
		||||
			std::string W = E.what();
 | 
			
		||||
			poco_information(
 | 
			
		||||
				Logger_,
 | 
			
		||||
				fmt::format("std::exception caught: {}. Connection terminated with {}", W, CId_));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			poco_information(Logger_,
 | 
			
		||||
							 fmt::format("Unknown exception for {}. Connection terminated.", CId_));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
		State_.LastContact = LastContact_ = Utils::Now();
 | 
			
		||||
		if (AP_WS_Server()->Running() && (DeviceValidated_ || ValidatedDevice())) {
 | 
			
		||||
			try {
 | 
			
		||||
				return ProcessIncomingFrame();
 | 
			
		||||
			} catch (const Poco::Exception &E) {
 | 
			
		||||
				Logger_.log(E);
 | 
			
		||||
			} catch (const std::exception &E) {
 | 
			
		||||
				std::string W = E.what();
 | 
			
		||||
				poco_information(
 | 
			
		||||
					Logger_, fmt::format("std::exception caught: {}. Connection terminated with {}",
 | 
			
		||||
										 W, CId_));
 | 
			
		||||
			} catch (...) {
 | 
			
		||||
				poco_information(
 | 
			
		||||
					Logger_, fmt::format("Unknown exception for {}. Connection terminated.", CId_));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		EndConnection();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::ProcessIncomingFrame() {
 | 
			
		||||
		Poco::Buffer<char> IncomingFrame(0);
 | 
			
		||||
 | 
			
		||||
		bool	KillConnection=false;
 | 
			
		||||
		try {
 | 
			
		||||
			int Op, flags;
 | 
			
		||||
			int 	Op, flags;
 | 
			
		||||
			auto IncomingSize = WS_->receiveFrame(IncomingFrame, flags);
 | 
			
		||||
 | 
			
		||||
			Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK;
 | 
			
		||||
@@ -684,84 +625,81 @@ namespace OpenWifi {
 | 
			
		||||
			State_.LastContact = Utils::Now();
 | 
			
		||||
 | 
			
		||||
			switch (Op) {
 | 
			
		||||
			case Poco::Net::WebSocket::FRAME_OP_PING: {
 | 
			
		||||
				poco_trace(Logger_, fmt::format("WS-PING({}): received. PONG sent back.", CId_));
 | 
			
		||||
				WS_->sendFrame("", 0,
 | 
			
		||||
							   (int)Poco::Net::WebSocket::FRAME_OP_PONG |
 | 
			
		||||
								   (int)Poco::Net::WebSocket::FRAME_FLAG_FIN);
 | 
			
		||||
				case Poco::Net::WebSocket::FRAME_OP_PING: {
 | 
			
		||||
					poco_trace(Logger_, fmt::format("WS-PING({}): received. PONG sent back.", CId_));
 | 
			
		||||
					WS_->sendFrame("", 0,
 | 
			
		||||
								   (int)Poco::Net::WebSocket::FRAME_OP_PONG |
 | 
			
		||||
									   (int)Poco::Net::WebSocket::FRAME_FLAG_FIN);
 | 
			
		||||
 | 
			
		||||
				if (KafkaManager()->Enabled()) {
 | 
			
		||||
					Poco::JSON::Object PingObject;
 | 
			
		||||
					Poco::JSON::Object PingDetails;
 | 
			
		||||
					PingDetails.set(uCentralProtocol::FIRMWARE, State_.Firmware);
 | 
			
		||||
					PingDetails.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
 | 
			
		||||
					PingDetails.set(uCentralProtocol::COMPATIBLE, Compatible_);
 | 
			
		||||
					PingDetails.set(uCentralProtocol::CONNECTIONIP, CId_);
 | 
			
		||||
					PingDetails.set(uCentralProtocol::TIMESTAMP, Utils::Now());
 | 
			
		||||
					PingDetails.set("locale", State_.locale);
 | 
			
		||||
					PingObject.set(uCentralProtocol::PING, PingDetails);
 | 
			
		||||
					Poco::JSON::Stringifier Stringify;
 | 
			
		||||
					std::ostringstream OS;
 | 
			
		||||
					Stringify.condense(PingObject, OS);
 | 
			
		||||
					KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, OS.str());
 | 
			
		||||
				}
 | 
			
		||||
				return;
 | 
			
		||||
			} break;
 | 
			
		||||
					if (KafkaManager()->Enabled()) {
 | 
			
		||||
						Poco::JSON::Object PingObject;
 | 
			
		||||
						Poco::JSON::Object PingDetails;
 | 
			
		||||
						PingDetails.set(uCentralProtocol::FIRMWARE, State_.Firmware);
 | 
			
		||||
						PingDetails.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
 | 
			
		||||
						PingDetails.set(uCentralProtocol::COMPATIBLE, Compatible_);
 | 
			
		||||
						PingDetails.set(uCentralProtocol::CONNECTIONIP, CId_);
 | 
			
		||||
						PingDetails.set(uCentralProtocol::TIMESTAMP, Utils::Now());
 | 
			
		||||
						PingDetails.set(uCentralProtocol::UUID, uuid_);
 | 
			
		||||
						PingDetails.set("locale", State_.locale);
 | 
			
		||||
						PingObject.set(uCentralProtocol::PING, PingDetails);
 | 
			
		||||
						poco_trace(Logger_,fmt::format("Sending PING for {}", SerialNumber_));
 | 
			
		||||
						KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_,PingObject);
 | 
			
		||||
					}
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
			case Poco::Net::WebSocket::FRAME_OP_PONG: {
 | 
			
		||||
				poco_trace(Logger_, fmt::format("PONG({}): received and ignored.", CId_));
 | 
			
		||||
				return;
 | 
			
		||||
			} break;
 | 
			
		||||
				case Poco::Net::WebSocket::FRAME_OP_PONG: {
 | 
			
		||||
					poco_trace(Logger_, fmt::format("PONG({}): received and ignored.", CId_));
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
			case Poco::Net::WebSocket::FRAME_OP_TEXT: {
 | 
			
		||||
				poco_trace(Logger_,
 | 
			
		||||
						   fmt::format("FRAME({}): Frame received (length={}, flags={}). Msg={}",
 | 
			
		||||
									   CId_, IncomingSize, flags, IncomingFrame.begin()));
 | 
			
		||||
				case Poco::Net::WebSocket::FRAME_OP_TEXT: {
 | 
			
		||||
					poco_trace(Logger_,
 | 
			
		||||
							   fmt::format("FRAME({}): Frame received (length={}, flags={}). Msg={}",
 | 
			
		||||
										   CId_, IncomingSize, flags, IncomingFrame.begin()));
 | 
			
		||||
 | 
			
		||||
				Poco::JSON::Parser parser;
 | 
			
		||||
				auto ParsedMessage = parser.parse(IncomingFrame.begin());
 | 
			
		||||
				auto IncomingJSON = ParsedMessage.extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
					Poco::JSON::Parser parser;
 | 
			
		||||
					auto ParsedMessage = parser.parse(IncomingFrame.begin());
 | 
			
		||||
					auto IncomingJSON = ParsedMessage.extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
 | 
			
		||||
				if (IncomingJSON->has(uCentralProtocol::JSONRPC)) {
 | 
			
		||||
					if (IncomingJSON->has(uCentralProtocol::METHOD) &&
 | 
			
		||||
						IncomingJSON->has(uCentralProtocol::PARAMS)) {
 | 
			
		||||
						ProcessJSONRPCEvent(IncomingJSON);
 | 
			
		||||
					} else if (IncomingJSON->has(uCentralProtocol::RESULT) &&
 | 
			
		||||
							   IncomingJSON->has(uCentralProtocol::ID)) {
 | 
			
		||||
						poco_trace(Logger_, fmt::format("RPC-RESULT({}): payload: {}", CId_,
 | 
			
		||||
														IncomingFrame.begin()));
 | 
			
		||||
						ProcessJSONRPCResult(IncomingJSON);
 | 
			
		||||
					if (IncomingJSON->has(uCentralProtocol::JSONRPC)) {
 | 
			
		||||
						if (IncomingJSON->has(uCentralProtocol::METHOD) &&
 | 
			
		||||
							IncomingJSON->has(uCentralProtocol::PARAMS)) {
 | 
			
		||||
							ProcessJSONRPCEvent(IncomingJSON);
 | 
			
		||||
						} else if (IncomingJSON->has(uCentralProtocol::RESULT) &&
 | 
			
		||||
								   IncomingJSON->has(uCentralProtocol::ID)) {
 | 
			
		||||
							poco_trace(Logger_, fmt::format("RPC-RESULT({}): payload: {}", CId_,
 | 
			
		||||
															IncomingFrame.begin()));
 | 
			
		||||
							ProcessJSONRPCResult(IncomingJSON);
 | 
			
		||||
						} else {
 | 
			
		||||
							poco_warning(
 | 
			
		||||
								Logger_,
 | 
			
		||||
								fmt::format("INVALID-PAYLOAD({}): Payload is not JSON-RPC 2.0: {}",
 | 
			
		||||
											CId_, IncomingFrame.begin()));
 | 
			
		||||
						}
 | 
			
		||||
					} else if (IncomingJSON->has(uCentralProtocol::RADIUS)) {
 | 
			
		||||
						ProcessIncomingRadiusData(IncomingJSON);
 | 
			
		||||
					} else {
 | 
			
		||||
						std::ostringstream iS;
 | 
			
		||||
						IncomingJSON->stringify(iS);
 | 
			
		||||
						poco_warning(
 | 
			
		||||
							Logger_,
 | 
			
		||||
							fmt::format("INVALID-PAYLOAD({}): Payload is not JSON-RPC 2.0: {}",
 | 
			
		||||
										CId_, IncomingFrame.begin()));
 | 
			
		||||
							fmt::format("FRAME({}): illegal transaction header, missing 'jsonrpc': {}",
 | 
			
		||||
										CId_, iS.str()));
 | 
			
		||||
						Errors_++;
 | 
			
		||||
					}
 | 
			
		||||
				} else if (IncomingJSON->has(uCentralProtocol::RADIUS)) {
 | 
			
		||||
					ProcessIncomingRadiusData(IncomingJSON);
 | 
			
		||||
				} else {
 | 
			
		||||
					std::ostringstream iS;
 | 
			
		||||
					IncomingJSON->stringify(iS);
 | 
			
		||||
					std::cout << iS.str() << std::endl;
 | 
			
		||||
					poco_warning(
 | 
			
		||||
						Logger_,
 | 
			
		||||
						fmt::format("FRAME({}): illegal transaction header, missing 'jsonrpc'",
 | 
			
		||||
									CId_));
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				case Poco::Net::WebSocket::FRAME_OP_CLOSE: {
 | 
			
		||||
					poco_information(Logger_,
 | 
			
		||||
									 fmt::format("CLOSE({}): Device is closing its connection.", CId_));
 | 
			
		||||
					KillConnection=true;
 | 
			
		||||
				} break;
 | 
			
		||||
 | 
			
		||||
				default: {
 | 
			
		||||
					poco_warning(Logger_, fmt::format("UNKNOWN({}): unknown WS Frame operation: {}",
 | 
			
		||||
													  CId_, std::to_string(Op)));
 | 
			
		||||
					Errors_++;
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				return;
 | 
			
		||||
			} break;
 | 
			
		||||
 | 
			
		||||
			case Poco::Net::WebSocket::FRAME_OP_CLOSE: {
 | 
			
		||||
				poco_information(Logger_,
 | 
			
		||||
								 fmt::format("CLOSE({}): Device is closing its connection.", CId_));
 | 
			
		||||
				return EndConnection();
 | 
			
		||||
			} break;
 | 
			
		||||
 | 
			
		||||
			default: {
 | 
			
		||||
				poco_warning(Logger_, fmt::format("UNKNOWN({}): unknown WS Frame operation: {}",
 | 
			
		||||
												  CId_, std::to_string(Op)));
 | 
			
		||||
			} break;
 | 
			
		||||
			}
 | 
			
		||||
		} catch (const Poco::Net::ConnectionResetException &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
@@ -769,21 +707,21 @@ namespace OpenWifi {
 | 
			
		||||
									 CId_, E.displayText(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const Poco::JSON::JSONException &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
						 fmt::format("JSONException({}): Text:{} Payload:{} Session:{}", CId_,
 | 
			
		||||
									 E.displayText(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const Poco::Net::WebSocketException &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
						 fmt::format("WebSocketException({}): Text:{} Payload:{} Session:{}", CId_,
 | 
			
		||||
									 E.displayText(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const Poco::Net::SSLConnectionUnexpectedlyClosedException &E) {
 | 
			
		||||
			poco_warning(
 | 
			
		||||
				Logger_,
 | 
			
		||||
@@ -792,54 +730,54 @@ namespace OpenWifi {
 | 
			
		||||
					CId_, E.displayText(),
 | 
			
		||||
					IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
					State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const Poco::Net::SSLException &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
						 fmt::format("SSLException({}): Text:{} Payload:{} Session:{}", CId_,
 | 
			
		||||
									 E.displayText(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const Poco::Net::NetException &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
						 fmt::format("NetException({}): Text:{} Payload:{} Session:{}", CId_,
 | 
			
		||||
									 E.displayText(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const Poco::IOException &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
						 fmt::format("IOException({}): Text:{} Payload:{} Session:{}", CId_,
 | 
			
		||||
									 E.displayText(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
						 fmt::format("Exception({}): Text:{} Payload:{} Session:{}", CId_,
 | 
			
		||||
									 E.displayText(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (const std::exception &E) {
 | 
			
		||||
			poco_warning(Logger_,
 | 
			
		||||
						 fmt::format("std::exception({}): Text:{} Payload:{} Session:{}", CId_,
 | 
			
		||||
									 E.what(),
 | 
			
		||||
									 IncomingFrame.begin() == nullptr ? "" : IncomingFrame.begin(),
 | 
			
		||||
									 State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			poco_error(Logger_, fmt::format("UnknownException({}): Device must be disconnected. "
 | 
			
		||||
											"Unknown exception.  Session:{}",
 | 
			
		||||
											CId_, State_.sessionId));
 | 
			
		||||
			return EndConnection();
 | 
			
		||||
			KillConnection=true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (Errors_ < 10)
 | 
			
		||||
		if (!KillConnection && Errors_ < 10)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		poco_warning(Logger_, fmt::format("DISCONNECTING({}): Too many errors", CId_));
 | 
			
		||||
		return EndConnection();
 | 
			
		||||
		poco_warning(Logger_, fmt::format("DISCONNECTING({}): ConnectionException: {} Errors: {}", CId_, KillConnection, Errors_ ));
 | 
			
		||||
		EndConnection();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Connection::Send(const std::string &Payload) {
 | 
			
		||||
@@ -951,4 +889,36 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Connection::SetLastStats(const std::string &LastStats) {
 | 
			
		||||
		RawLastStats_ = LastStats;
 | 
			
		||||
		try {
 | 
			
		||||
			Poco::JSON::Parser P;
 | 
			
		||||
			auto Stats = P.parse(LastStats).extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
			State_.hasGPS = Stats->isObject("gps");
 | 
			
		||||
			auto Unit = Stats->getObject("unit");
 | 
			
		||||
			auto Memory = Unit->getObject("memory");
 | 
			
		||||
			std::uint64_t TotalMemory = Memory->get("total");
 | 
			
		||||
			std::uint64_t FreeMemory = Memory->get("free");
 | 
			
		||||
			if (TotalMemory > 0) {
 | 
			
		||||
				State_.memoryUsed =
 | 
			
		||||
					(100.0 * ((double)TotalMemory - (double)FreeMemory)) / (double)TotalMemory;
 | 
			
		||||
			}
 | 
			
		||||
			if (Unit->isArray("load")) {
 | 
			
		||||
				Poco::JSON::Array::Ptr Load = Unit->getArray("load");
 | 
			
		||||
				if (Load->size() > 1) {
 | 
			
		||||
					State_.load = Load->get(1);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if (Unit->isArray("temperature")) {
 | 
			
		||||
				Poco::JSON::Array::Ptr Temperature = Unit->getArray("temperature");
 | 
			
		||||
				if (Temperature->size() > 1) {
 | 
			
		||||
					State_.temperature = Temperature->get(0);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
			poco_error(Logger_, "Failed to parse last stats: " + E.displayText());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -4,17 +4,20 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <shared_mutex>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "Poco/JSON/Object.h"
 | 
			
		||||
#include <Poco/JSON/Parser.h>
 | 
			
		||||
#include "Poco/Logger.h"
 | 
			
		||||
#include "Poco/Net/SocketNotification.h"
 | 
			
		||||
#include "Poco/Net/SocketReactor.h"
 | 
			
		||||
#include "Poco/Net/StreamSocket.h"
 | 
			
		||||
#include "Poco/Net/WebSocket.h"
 | 
			
		||||
#include <Poco/Data/Session.h>
 | 
			
		||||
 | 
			
		||||
#include "RESTObjects/RESTAPI_GWobjects.h"
 | 
			
		||||
#include <AP_WS_Reactor_Pool.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
@@ -24,7 +27,7 @@ namespace OpenWifi {
 | 
			
		||||
	  public:
 | 
			
		||||
		explicit AP_WS_Connection(Poco::Net::HTTPServerRequest &request,
 | 
			
		||||
								  Poco::Net::HTTPServerResponse &response, uint64_t connection_id,
 | 
			
		||||
								  Poco::Logger &L, Poco::Net::SocketReactor &R);
 | 
			
		||||
								  Poco::Logger &L, std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> R);
 | 
			
		||||
		~AP_WS_Connection();
 | 
			
		||||
 | 
			
		||||
		void EndConnection();
 | 
			
		||||
@@ -34,6 +37,7 @@ namespace OpenWifi {
 | 
			
		||||
		void ProcessIncomingRadiusData(const Poco::JSON::Object::Ptr &Doc);
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] bool Send(const std::string &Payload);
 | 
			
		||||
		[[nodiscard]] inline bool MustBeSecureRTTY() const { return RTTYMustBeSecure_; }
 | 
			
		||||
 | 
			
		||||
		bool SendRadiusAuthenticationData(const unsigned char *buffer, std::size_t size);
 | 
			
		||||
		bool SendRadiusAccountingData(const unsigned char *buffer, std::size_t size);
 | 
			
		||||
@@ -42,10 +46,7 @@ namespace OpenWifi {
 | 
			
		||||
		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);
 | 
			
		||||
		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,
 | 
			
		||||
@@ -57,52 +58,34 @@ namespace OpenWifi {
 | 
			
		||||
		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);
 | 
			
		||||
		void Process_event(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_wifiscan(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_alarm(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
 | 
			
		||||
		bool ValidatedDevice();
 | 
			
		||||
		[[nodiscard]] inline bool HasGPS() const { return hasGPS_; }
 | 
			
		||||
		[[nodiscard]] bool ValidatedDevice();
 | 
			
		||||
 | 
			
		||||
		inline bool GetTelemetryParameters(bool &Reporting, uint64_t &Interval,
 | 
			
		||||
										   uint64_t &WebSocketTimer, uint64_t &KafkaTimer,
 | 
			
		||||
@@ -122,18 +105,14 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		friend class AP_WS_Server;
 | 
			
		||||
 | 
			
		||||
		inline GWObjects::DeviceRestrictions Restrictions() const {
 | 
			
		||||
			std::shared_lock G(ConnectionMutex_);
 | 
			
		||||
			return Restrictions_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline bool MustBeSecureRtty() const { return RttyMustBeSecure_; }
 | 
			
		||||
		void Start();
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		mutable std::shared_mutex ConnectionMutex_;
 | 
			
		||||
		std::shared_mutex TelemetryMutex_;
 | 
			
		||||
		mutable std::recursive_mutex ConnectionMutex_;
 | 
			
		||||
		std::mutex TelemetryMutex_;
 | 
			
		||||
		Poco::Logger &Logger_;
 | 
			
		||||
		Poco::Net::SocketReactor &Reactor_;
 | 
			
		||||
		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;
 | 
			
		||||
@@ -144,30 +123,56 @@ namespace OpenWifi {
 | 
			
		||||
		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;
 | 
			
		||||
		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_;
 | 
			
		||||
		std::string RawLastStats_;
 | 
			
		||||
		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> 	Dead_ = false;
 | 
			
		||||
		std::atomic_bool DeviceValidated_ = false;
 | 
			
		||||
		std::atomic_bool Valid_ = false;
 | 
			
		||||
		OpenWifi::GWObjects::DeviceRestrictions Restrictions_;
 | 
			
		||||
		bool 			RttyMustBeSecure_ = false;
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
		static inline std::atomic_uint64_t ConcurrentStartingDevices_ = 0;
 | 
			
		||||
 | 
			
		||||
		bool StartTelemetry(uint64_t RPCID, const std::vector<std::string> &TelemetryTypes);
 | 
			
		||||
		bool StopTelemetry(uint64_t RPCID);
 | 
			
		||||
		void UpdateCounts();
 | 
			
		||||
		static void DeviceDisconnectionCleanup(const std::string &SerialNumber, std::uint64_t uuid);
 | 
			
		||||
		void SetLastStats(const std::string &LastStats);
 | 
			
		||||
		void Process_connect(Poco::JSON::Object::Ptr ParamsObj, const std::string &Serial);
 | 
			
		||||
		void Process_state(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_healthcheck(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_log(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_crashlog(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_ping(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_cfgpending(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_recovery(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_deviceupdate(Poco::JSON::Object::Ptr ParamsObj, std::string &Serial);
 | 
			
		||||
		void Process_telemetry(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_venuebroadcast(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_event(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_wifiscan(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_alarm(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
		void Process_rebootLog(Poco::JSON::Object::Ptr ParamsObj);
 | 
			
		||||
 | 
			
		||||
		inline void SetLastHealthCheck(const GWObjects::HealthCheck &H) {
 | 
			
		||||
			RawLastHealthcheck_ = H;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -21,11 +21,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
 | 
			
		||||
			if (KafkaManager()->Enabled()) {
 | 
			
		||||
				auto Data = ParamsObj->get(uCentralProtocol::DATA);
 | 
			
		||||
				Poco::JSON::Stringifier Stringify;
 | 
			
		||||
				std::ostringstream OS;
 | 
			
		||||
				Stringify.condense(ParamsObj, OS);
 | 
			
		||||
				KafkaManager()->PostMessage(KafkaTopics::ALERTS, SerialNumber_, OS.str());
 | 
			
		||||
				KafkaManager()->PostMessage(KafkaTopics::ALERTS, SerialNumber_, *ParamsObj);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,8 @@
 | 
			
		||||
#include "framework/KafkaManager.h"
 | 
			
		||||
#include "framework/utils.h"
 | 
			
		||||
 | 
			
		||||
#include "firmware_revision_cache.h"
 | 
			
		||||
 | 
			
		||||
#include "UI_GW_WebSocketNotifications.h"
 | 
			
		||||
#include <GWKafkaEvents.h>
 | 
			
		||||
 | 
			
		||||
@@ -30,9 +32,7 @@ namespace OpenWifi {
 | 
			
		||||
			Event.set("type", "device.firmware_change");
 | 
			
		||||
			Event.set("timestamp", Utils::Now());
 | 
			
		||||
			Event.set("payload", EventDetails);
 | 
			
		||||
			std::ostringstream OS;
 | 
			
		||||
			Event.stringify(OS);
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, OS.str());
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -49,9 +49,7 @@ namespace OpenWifi {
 | 
			
		||||
			Event.set("type", "device.not_provisioned");
 | 
			
		||||
			Event.set("timestamp", Utils::Now());
 | 
			
		||||
			Event.set("payload", EventDetails);
 | 
			
		||||
			std::ostringstream OS;
 | 
			
		||||
			Event.stringify(OS);
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, OS.str());
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -63,14 +61,18 @@ namespace OpenWifi {
 | 
			
		||||
			auto Firmware = ParamsObj->get(uCentralProtocol::FIRMWARE).toString();
 | 
			
		||||
			auto Capabilities = ParamsObj->getObject(uCentralProtocol::CAPABILITIES);
 | 
			
		||||
 | 
			
		||||
			std::string DevicePassword;
 | 
			
		||||
			if(ParamsObj->has("password")) {
 | 
			
		||||
				DevicePassword = ParamsObj->get("password").toString();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			SerialNumber_ = Serial;
 | 
			
		||||
			SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_);
 | 
			
		||||
 | 
			
		||||
			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);
 | 
			
		||||
 | 
			
		||||
			Compatible_ = Caps.Compatible();
 | 
			
		||||
@@ -81,6 +83,12 @@ 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:") {
 | 
			
		||||
				IP = IP.substr(7);
 | 
			
		||||
@@ -93,29 +101,65 @@ namespace OpenWifi {
 | 
			
		||||
				Restrictions_.from_json(RestrictionObject);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (Capabilities->has("developer")) {
 | 
			
		||||
			if (Capabilities->has("developer") && !Capabilities->isNull("developer")) {
 | 
			
		||||
				Restrictions_.developer = Capabilities->getValue<bool>("developer");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(Capabilities->has("secure-rtty")) {
 | 
			
		||||
				RttyMustBeSecure_ = Capabilities->getValue<bool>("secure-rtty");
 | 
			
		||||
				RTTYMustBeSecure_ = Capabilities->getValue<bool>("secure-rtty");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			State_.locale = FindCountryFromIP()->Get(IP);
 | 
			
		||||
			GWObjects::Device DeviceInfo;
 | 
			
		||||
			auto DeviceExists = StorageService()->GetDevice(SerialNumber_, 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) {
 | 
			
		||||
						DeviceFirmwareChangeKafkaEvent KEvent(SerialNumber_, Utils::Now(),
 | 
			
		||||
						DeviceFirmwareChangeKafkaEvent KEvent(SerialNumberInt_, Utils::Now(),
 | 
			
		||||
															  DeviceInfo.Firmware, Firmware);
 | 
			
		||||
						DeviceInfo.Firmware = Firmware;
 | 
			
		||||
						DeviceInfo.LastFWUpdate = Utils::Now();
 | 
			
		||||
@@ -131,13 +175,44 @@ namespace OpenWifi {
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if(ParamsObj->has("reason")) {
 | 
			
		||||
					State_.connectReason = ParamsObj->get("reason").toString();
 | 
			
		||||
					DeviceInfo.connectReason = State_.connectReason;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if(DeviceInfo.DevicePassword!=DevicePassword) {
 | 
			
		||||
					DeviceInfo.DevicePassword = DevicePassword.empty() ? "openwifi" : DevicePassword ;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (DeviceInfo.lastRecordedContact==0) {
 | 
			
		||||
					DeviceInfo.lastRecordedContact = Utils::Now();
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (DeviceInfo.simulated && (State_.VerifiedCertificate!=GWObjects::SIMULATED)) {
 | 
			
		||||
					DeviceInfo.simulated = false;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (!DeviceInfo.simulated && (State_.VerifiedCertificate==GWObjects::SIMULATED)) {
 | 
			
		||||
					DeviceInfo.simulated = true;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (DeviceInfo.locale != State_.locale) {
 | 
			
		||||
					DeviceInfo.locale = State_.locale;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (Compatible_ != DeviceInfo.DeviceType) {
 | 
			
		||||
					DeviceInfo.DeviceType = Compatible_;
 | 
			
		||||
				if (Compatible_ != DeviceInfo.Compatible) {
 | 
			
		||||
					DeviceInfo.Compatible = Compatible_;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (Platform != DeviceInfo.DeviceType) {
 | 
			
		||||
					DeviceInfo.DeviceType = Platform;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
@@ -151,13 +226,21 @@ namespace OpenWifi {
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (Updated) {
 | 
			
		||||
					StorageService()->UpdateDevice(DeviceInfo);
 | 
			
		||||
				if(DeviceInfo.certificateExpiryDate!=State_.certificateExpiryDate) {
 | 
			
		||||
					DeviceInfo.certificateExpiryDate = State_.certificateExpiryDate;
 | 
			
		||||
					++Updated;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (Updated) {
 | 
			
		||||
					StorageService()->UpdateDevice(DbSession_->Session(), DeviceInfo);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(!Simulated_) {
 | 
			
		||||
				uint64_t UpgradedUUID = 0;
 | 
			
		||||
				LookForUpgrade(UUID, UpgradedUUID);
 | 
			
		||||
				State_.UUID = UpgradedUUID;
 | 
			
		||||
				if (LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID)) {
 | 
			
		||||
					State_.UUID = UpgradedUUID;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			State_.Compatible = Compatible_;
 | 
			
		||||
@@ -193,6 +276,12 @@ namespace OpenWifi {
 | 
			
		||||
						return EndConnection();
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				poco_information(Logger_,
 | 
			
		||||
								 fmt::format("CONNECT({}): Simulator device. "
 | 
			
		||||
											 "Session={} ConnectionCompletion Time={}",
 | 
			
		||||
											 CId_, State_.sessionId,
 | 
			
		||||
											 State_.connectionCompletionTime));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GWWebSocketNotifications::SingleDevice_t Notification;
 | 
			
		||||
@@ -200,14 +289,11 @@ namespace OpenWifi {
 | 
			
		||||
			GWWebSocketNotifications::DeviceConnected(Notification);
 | 
			
		||||
 | 
			
		||||
			if (KafkaManager()->Enabled()) {
 | 
			
		||||
				Poco::JSON::Stringifier Stringify;
 | 
			
		||||
 | 
			
		||||
				ParamsObj->set(uCentralProtocol::CONNECTIONIP, CId_);
 | 
			
		||||
				ParamsObj->set("locale", State_.locale);
 | 
			
		||||
				ParamsObj->set(uCentralProtocol::TIMESTAMP, Utils::Now());
 | 
			
		||||
				std::ostringstream OS;
 | 
			
		||||
				Stringify.condense(ParamsObj, OS);
 | 
			
		||||
				KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, OS.str());
 | 
			
		||||
				ParamsObj->set(uCentralProtocol::UUID, uuid_);
 | 
			
		||||
				KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, *ParamsObj);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			poco_warning(
 | 
			
		||||
 
 | 
			
		||||
@@ -7,10 +7,12 @@
 | 
			
		||||
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
#include <GWKafkaEvents.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void AP_WS_Connection::Process_crashlog(Poco::JSON::Object::Ptr ParamsObj) {
 | 
			
		||||
		if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::LOGLINES)) {
 | 
			
		||||
		if (ParamsObj->has(uCentralProtocol::UUID)
 | 
			
		||||
			&& ParamsObj->has(uCentralProtocol::LOGLINES)) {
 | 
			
		||||
			poco_trace(Logger_, fmt::format("CRASH-LOG({}): new entry.", CId_));
 | 
			
		||||
			auto LogLines = ParamsObj->get(uCentralProtocol::LOGLINES);
 | 
			
		||||
			std::string LogText;
 | 
			
		||||
@@ -24,11 +26,11 @@ namespace OpenWifi {
 | 
			
		||||
										   .Log = LogText,
 | 
			
		||||
										   .Data = "",
 | 
			
		||||
										   .Severity = GWObjects::DeviceLog::LOG_EMERG,
 | 
			
		||||
										   .Recorded = (uint64_t)time(nullptr),
 | 
			
		||||
										   .Recorded = Utils::Now(),
 | 
			
		||||
										   .LogType = 1,
 | 
			
		||||
										   .UUID = 0};
 | 
			
		||||
			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;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,10 @@ namespace OpenWifi {
 | 
			
		||||
		if (ParamsObj->has("currentPassword")) {
 | 
			
		||||
			auto Password = ParamsObj->get("currentPassword").toString();
 | 
			
		||||
 | 
			
		||||
			StorageService()->SetDevicePassword(Serial, Password);
 | 
			
		||||
			StorageService()->SetDevicePassword(*DbSession_,Serial, Password);
 | 
			
		||||
			poco_trace(
 | 
			
		||||
				Logger_,
 | 
			
		||||
				fmt::format("DEVICEUPDATE({}): Device is updating its login password.", Serial));
 | 
			
		||||
				fmt::format("DEVICE-UPDATE({}): Device is updating its login password.", Serial));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,11 +34,13 @@ namespace OpenWifi {
 | 
			
		||||
					FullEvent.set("type", EventType);
 | 
			
		||||
					FullEvent.set("timestamp", EventTimeStamp);
 | 
			
		||||
					FullEvent.set("payload", EventPayload);
 | 
			
		||||
 | 
			
		||||
					std::ostringstream OS;
 | 
			
		||||
					FullEvent.stringify(OS);
 | 
			
		||||
					KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber_,
 | 
			
		||||
												OS.str());
 | 
			
		||||
					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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Connection.h"
 | 
			
		||||
#include "AP_WS_Server.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
@@ -19,10 +20,13 @@ namespace OpenWifi {
 | 
			
		||||
			Errors_++;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::SANITY) &&
 | 
			
		||||
		if (ParamsObj->has(uCentralProtocol::UUID) &&
 | 
			
		||||
			ParamsObj->has(uCentralProtocol::SANITY) &&
 | 
			
		||||
			ParamsObj->has(uCentralProtocol::DATA)) {
 | 
			
		||||
 | 
			
		||||
			uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
 | 
			
		||||
			auto Sanity = ParamsObj->get(uCentralProtocol::SANITY);
 | 
			
		||||
			State_.sanity = Sanity;
 | 
			
		||||
			auto CheckData = ParamsObj->get(uCentralProtocol::DATA).toString();
 | 
			
		||||
			if (CheckData.empty())
 | 
			
		||||
				CheckData = uCentralProtocol::EMPTY_JSON_DOC;
 | 
			
		||||
@@ -38,10 +42,6 @@ namespace OpenWifi {
 | 
			
		||||
												CId_, UUID, request_uuid));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			uint64_t UpgradedUUID;
 | 
			
		||||
			LookForUpgrade(UUID, UpgradedUUID);
 | 
			
		||||
			State_.UUID = UpgradedUUID;
 | 
			
		||||
 | 
			
		||||
			GWObjects::HealthCheck Check;
 | 
			
		||||
 | 
			
		||||
			Check.SerialNumber = SerialNumber_;
 | 
			
		||||
@@ -50,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_));
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
#include <GWKafkaEvents.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void AP_WS_Connection::Process_log(Poco::JSON::Object::Ptr ParamsObj) {
 | 
			
		||||
@@ -35,7 +36,8 @@ 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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
@@ -35,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Connection.h"
 | 
			
		||||
#include "AP_WS_Server.h"
 | 
			
		||||
#include "StateUtils.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
 | 
			
		||||
@@ -39,27 +40,28 @@ namespace OpenWifi {
 | 
			
		||||
												UUID, request_uuid));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			uint64_t UpgradedUUID;
 | 
			
		||||
			LookForUpgrade(UUID, UpgradedUUID);
 | 
			
		||||
			State_.UUID = UpgradedUUID;
 | 
			
		||||
			std::lock_guard	Guard(DbSession_->Mutex());
 | 
			
		||||
			if(!Simulated_) {
 | 
			
		||||
				uint64_t UpgradedUUID;
 | 
			
		||||
				LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID);
 | 
			
		||||
				State_.UUID = UpgradedUUID;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			SetLastStats(StateStr);
 | 
			
		||||
 | 
			
		||||
			GWObjects::Statistics Stats{
 | 
			
		||||
				.SerialNumber = SerialNumber_, .UUID = UUID, .Data = StateStr};
 | 
			
		||||
			Stats.Recorded = Utils::Now();
 | 
			
		||||
			StorageService()->AddStatisticsData(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);
 | 
			
		||||
											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;
 | 
			
		||||
 
 | 
			
		||||
@@ -27,29 +27,28 @@ namespace OpenWifi {
 | 
			
		||||
				std::ostringstream SS;
 | 
			
		||||
				Payload->stringify(SS);
 | 
			
		||||
				auto now = Utils::Now();
 | 
			
		||||
				auto KafkaPayload = SS.str();
 | 
			
		||||
				if (ParamsObj->has("adhoc")) {
 | 
			
		||||
					KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
 | 
			
		||||
												SS.str());
 | 
			
		||||
												KafkaPayload);
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				if (TelemetryWebSocketRefCount_) {
 | 
			
		||||
					if (now < TelemetryWebSocketTimer_) {
 | 
			
		||||
						// std::cout << SerialNumber_ << ": Updating WebSocket telemetry" <<
 | 
			
		||||
						// std::endl;
 | 
			
		||||
 | 
			
		||||
						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;
 | 
			
		||||
						TelemetryKafkaPackets_++;
 | 
			
		||||
						State_.kafkaPackets = TelemetryKafkaPackets_;
 | 
			
		||||
						KafkaManager()->PostMessage(KafkaTopics::DEVICE_TELEMETRY, SerialNumber_,
 | 
			
		||||
													SS.str());
 | 
			
		||||
													KafkaPayload);
 | 
			
		||||
					} else {
 | 
			
		||||
						StopKafkaTelemetry(CommandManager()->Next_RPC_ID());
 | 
			
		||||
					}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::DATA)) {
 | 
			
		||||
			if (KafkaManager()->Enabled()) {
 | 
			
		||||
				auto Data = ParamsObj->get(uCentralProtocol::DATA);
 | 
			
		||||
				Poco::JSON::Stringifier Stringify;
 | 
			
		||||
				std::ostringstream OS;
 | 
			
		||||
				Stringify.condense(ParamsObj, OS);
 | 
			
		||||
				KafkaManager()->PostMessage(KafkaTopics::WIFISCAN, SerialNumber_, OS.str());
 | 
			
		||||
				KafkaManager()->PostMessage(KafkaTopics::WIFISCAN, SerialNumber_, *ParamsObj);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2022-02-03.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <shared_mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "Poco/Environment.h"
 | 
			
		||||
#include "Poco/Net/SocketAcceptor.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_;
 | 
			
		||||
	};
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
@@ -6,44 +6,83 @@
 | 
			
		||||
//	Arilia Wireless Inc.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "Poco/Net/Context.h"
 | 
			
		||||
#include "Poco/Net/HTTPHeaderStream.h"
 | 
			
		||||
#include "Poco/Net/HTTPServerRequest.h"
 | 
			
		||||
#include <Poco/Net/Context.h>
 | 
			
		||||
#include <Poco/Net/HTTPHeaderStream.h>
 | 
			
		||||
#include <Poco/Net/HTTPServerRequest.h>
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Connection.h"
 | 
			
		||||
#include "AP_WS_Server.h"
 | 
			
		||||
#include "ConfigurationCache.h"
 | 
			
		||||
#include "TelemetryStream.h"
 | 
			
		||||
#include <AP_WS_Connection.h>
 | 
			
		||||
#include <AP_WS_Server.h>
 | 
			
		||||
#include <ConfigurationCache.h>
 | 
			
		||||
#include <TelemetryStream.h>
 | 
			
		||||
 | 
			
		||||
#include "UI_GW_WebSocketNotifications.h"
 | 
			
		||||
#include "fmt/format.h"
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "framework/utils.h"
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include <framework/MicroServiceFuncs.h>
 | 
			
		||||
#include <framework/utils.h>
 | 
			
		||||
#include <framework/KafkaManager.h>
 | 
			
		||||
 | 
			
		||||
#include <UI_GW_WebSocketNotifications.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	void AP_WS_RequestHandler::handleRequest(Poco::Net::HTTPServerRequest &request,
 | 
			
		||||
											 Poco::Net::HTTPServerResponse &response) {
 | 
			
		||||
		try {
 | 
			
		||||
			AP_WS_Server()->AddConnection(
 | 
			
		||||
				id_, std::make_shared<AP_WS_Connection>(request, response, id_, Logger_,
 | 
			
		||||
														AP_WS_Server()->NextReactor()));
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			poco_warning(Logger_, "Exception during WS creation");
 | 
			
		||||
	class AP_WS_RequestHandler : public Poco::Net::HTTPRequestHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
		explicit AP_WS_RequestHandler(Poco::Logger &L, std::uint64_t session_id) : Logger_(L),
 | 
			
		||||
								   		session_id_(session_id) {
 | 
			
		||||
		 };
 | 
			
		||||
 | 
			
		||||
		void handleRequest(	Poco::Net::HTTPServerRequest &request,
 | 
			
		||||
						 	Poco::Net::HTTPServerResponse &response) override {
 | 
			
		||||
			try {
 | 
			
		||||
				auto NewConnection = std::make_shared<AP_WS_Connection>(request, response, session_id_, Logger_,
 | 
			
		||||
																		AP_WS_Server()->NextReactor());
 | 
			
		||||
				AP_WS_Server()->AddConnection(NewConnection);
 | 
			
		||||
				NewConnection->Start();
 | 
			
		||||
			} catch (...) {
 | 
			
		||||
				poco_warning(Logger_, "Exception during WS creation");
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		Poco::Logger &Logger_;
 | 
			
		||||
		std::uint64_t session_id_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class AP_WS_RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
 | 
			
		||||
	  public:
 | 
			
		||||
		inline explicit AP_WS_RequestHandlerFactory(Poco::Logger &L) : Logger_(L) {}
 | 
			
		||||
 | 
			
		||||
		inline Poco::Net::HTTPRequestHandler *
 | 
			
		||||
		createRequestHandler(const Poco::Net::HTTPServerRequest &request) override {
 | 
			
		||||
			if (request.find("Upgrade") != request.end() &&
 | 
			
		||||
				Poco::icompare(request["Upgrade"], "websocket") == 0) {
 | 
			
		||||
				Utils::SetThreadName("ws:conn-init");
 | 
			
		||||
				session_id_++;
 | 
			
		||||
				return new AP_WS_RequestHandler(Logger_, session_id_);
 | 
			
		||||
			} else {
 | 
			
		||||
				return nullptr;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	  private:
 | 
			
		||||
		Poco::Logger &Logger_;
 | 
			
		||||
		inline static std::atomic_uint64_t session_id_ = 0;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::ValidateCertificate(const std::string &ConnectionId,
 | 
			
		||||
										   const Poco::Crypto::X509Certificate &Certificate) {
 | 
			
		||||
		if (IsCertOk()) {
 | 
			
		||||
			if (!Certificate.issuedBy(*IssuerCert_)) {
 | 
			
		||||
				poco_warning(
 | 
			
		||||
					Logger(),
 | 
			
		||||
					fmt::format("CERTIFICATE({}): issuer mismatch. Local='{}' Incoming='{}'",
 | 
			
		||||
								ConnectionId, IssuerCert_->issuerName(), Certificate.issuerName()));
 | 
			
		||||
				return false;
 | 
			
		||||
			// validate certificate agains trusted chain
 | 
			
		||||
			for (const auto &cert : ClientCasCerts_) {
 | 
			
		||||
				if (Certificate.issuedBy(cert)) {
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return true;
 | 
			
		||||
			poco_warning(
 | 
			
		||||
					Logger(),
 | 
			
		||||
					fmt::format(
 | 
			
		||||
						"CERTIFICATE({}): issuer mismatch. Certificate not issued by any trusted CA",
 | 
			
		||||
						ConnectionId)
 | 
			
		||||
					);
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
@@ -54,7 +93,9 @@ namespace OpenWifi {
 | 
			
		||||
			MicroServiceConfigGetBool("openwifi.certificates.allowmismatch", true);
 | 
			
		||||
		MismatchDepth_ = MicroServiceConfigGetInt("openwifi.certificates.mismatchdepth", 2);
 | 
			
		||||
 | 
			
		||||
		Reactor_pool_ = std::make_unique<AP_WS_ReactorThreadPool>();
 | 
			
		||||
		SessionTimeOut_ = MicroServiceConfigGetInt("openwifi.session.timeout", 10*60);
 | 
			
		||||
 | 
			
		||||
		Reactor_pool_ = std::make_unique<AP_WS_ReactorThreadPool>(Logger());
 | 
			
		||||
		Reactor_pool_->Start();
 | 
			
		||||
 | 
			
		||||
		for (const auto &Svr : ConfigServersList_) {
 | 
			
		||||
@@ -96,6 +137,13 @@ namespace OpenWifi {
 | 
			
		||||
			Context->addChainCertificate(Issuing);
 | 
			
		||||
			Context->addCertificateAuthority(Issuing);
 | 
			
		||||
 | 
			
		||||
			// add certificates from clientcas to trust chain
 | 
			
		||||
			ClientCasCerts_ = Poco::Net::X509Certificate::readPEM(Svr.ClientCas());
 | 
			
		||||
			for (const auto &cert : ClientCasCerts_) {
 | 
			
		||||
				Context->addChainCertificate(cert);
 | 
			
		||||
				Context->addCertificateAuthority(cert);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Poco::Crypto::RSAKey Key("", Svr.KeyFile(), Svr.KeyFilePassword());
 | 
			
		||||
			Context->usePrivateKey(Key);
 | 
			
		||||
 | 
			
		||||
@@ -104,7 +152,6 @@ namespace OpenWifi {
 | 
			
		||||
			Context->flushSessionCache();
 | 
			
		||||
			Context->enableSessionCache(true);
 | 
			
		||||
			Context->enableExtendedCertificateVerification(false);
 | 
			
		||||
			// Context->disableStatelessSessionResumption();
 | 
			
		||||
			Context->disableProtocols(Poco::Net::Context::PROTO_TLSV1 |
 | 
			
		||||
									  Poco::Net::Context::PROTO_TLSV1_1);
 | 
			
		||||
 | 
			
		||||
@@ -133,6 +180,9 @@ namespace OpenWifi {
 | 
			
		||||
					WebServerHttpParams);
 | 
			
		||||
				WebServers_.push_back(std::move(NewWebServer));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			KafkaDisableState_ = MicroServiceConfigGetBool("openwifi.kafka.disablestate", false);
 | 
			
		||||
			KafkaDisableHealthChecks_ = MicroServiceConfigGetBool("openwifi.kafka.disablehealthchecks", false);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto &server : WebServers_) {
 | 
			
		||||
@@ -154,204 +204,420 @@ namespace OpenWifi {
 | 
			
		||||
			UseDefaultConfig_ = true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		SimulatorId_ = MicroServiceConfigGetString("simulatorid", "");
 | 
			
		||||
		SimulatorId_ = Poco::toLower(MicroServiceConfigGetString("simulatorid", ""));
 | 
			
		||||
		SimulatorEnabled_ = !SimulatorId_.empty();
 | 
			
		||||
		Utils::SetThreadName(ReactorThread_, "dev:react:head");
 | 
			
		||||
 | 
			
		||||
		GarbageCollectorCallback_ = std::make_unique<Poco::TimerCallback<AP_WS_Server>>(
 | 
			
		||||
			*this, &AP_WS_Server::onGarbageCollecting);
 | 
			
		||||
		Timer_.setStartInterval(10 * 1000);
 | 
			
		||||
		Timer_.setPeriodicInterval(5 * 1000); // every minute
 | 
			
		||||
		Timer_.start(*GarbageCollectorCallback_, MicroServiceTimerPool());
 | 
			
		||||
 | 
			
		||||
		Running_ = true;
 | 
			
		||||
		GarbageCollector_.setName("ws:garbage");
 | 
			
		||||
		GarbageCollector_.start(*this);
 | 
			
		||||
 | 
			
		||||
		std::thread CleanupThread([this](){ CleanupSessions(); });
 | 
			
		||||
		CleanupThread.detach();
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::onGarbageCollecting([[maybe_unused]] Poco::Timer &timer) {
 | 
			
		||||
		std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
		if (!Garbage_.empty()) {
 | 
			
		||||
			Garbage_.clear();
 | 
			
		||||
	bool AP_WS_Server::Disconnect(uint64_t SerialNumber) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (DeviceHint == SerialNumbers_[hashIndex].end() || DeviceHint->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
			SerialNumbers_[hashIndex].erase(DeviceHint);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static uint64_t last_log = Utils::Now();
 | 
			
		||||
		{
 | 
			
		||||
			auto H = SessionHash::Hash(Connection->State_.sessionId);
 | 
			
		||||
			std::lock_guard SessionLock(SessionMutex_[H]);
 | 
			
		||||
			Sessions_[H].erase(Connection->State_.sessionId);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		NumberOfConnectedDevices_ = 0;
 | 
			
		||||
		NumberOfConnectingDevices_ = 0;
 | 
			
		||||
		AverageDeviceConnectionTime_ = 0;
 | 
			
		||||
		uint64_t total_connected_time = 0;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		auto now = Utils::Now();
 | 
			
		||||
		for (const auto &connection : SerialNumbers_) {
 | 
			
		||||
			if (connection.second.second == nullptr) {
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			if (connection.second.second->State_.Connected) {
 | 
			
		||||
				NumberOfConnectedDevices_++;
 | 
			
		||||
				total_connected_time += (now - connection.second.second->State_.started);
 | 
			
		||||
			} else {
 | 
			
		||||
				NumberOfConnectingDevices_++;
 | 
			
		||||
	void AP_WS_Server::CleanupSessions() {
 | 
			
		||||
 | 
			
		||||
		while(Running_) {
 | 
			
		||||
			std::this_thread::sleep_for(std::chrono::seconds(10));
 | 
			
		||||
 | 
			
		||||
			while(Running_ && !CleanupSessions_.empty()) {
 | 
			
		||||
				std::pair<uint64_t, uint64_t> Session;
 | 
			
		||||
				{
 | 
			
		||||
					std::lock_guard G(CleanupMutex_);
 | 
			
		||||
					Session = CleanupSessions_.front();
 | 
			
		||||
					CleanupSessions_.pop_front();
 | 
			
		||||
				}
 | 
			
		||||
				poco_trace(this->Logger(),fmt::format("Cleaning up session: {} for device: {}", Session.first, Utils::IntToSerialNumber(Session.second)));
 | 
			
		||||
				EndSession(Session.first, Session.second);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		AverageDeviceConnectionTime_ =
 | 
			
		||||
			(NumberOfConnectedDevices_ != 0) ? total_connected_time / NumberOfConnectedDevices_ : 0;
 | 
			
		||||
		if ((now - last_log) > 120) {
 | 
			
		||||
			last_log = now;
 | 
			
		||||
			poco_information(
 | 
			
		||||
				Logger(),
 | 
			
		||||
				fmt::format(
 | 
			
		||||
					"Active AP connections: {} Connecting: {} Average connection time: {} seconds",
 | 
			
		||||
					NumberOfConnectedDevices_, NumberOfConnectingDevices_,
 | 
			
		||||
					AverageDeviceConnectionTime_));
 | 
			
		||||
	void AP_WS_Server::run() {
 | 
			
		||||
		uint64_t last_log = Utils::Now(),
 | 
			
		||||
				 last_zombie_run = 0,
 | 
			
		||||
				 last_garbage_run = 0;
 | 
			
		||||
 | 
			
		||||
		Poco::Logger &LocalLogger = Poco::Logger::create(
 | 
			
		||||
			"WS-Session-Janitor", Poco::Logger::root().getChannel(), Poco::Logger::root().getLevel());
 | 
			
		||||
 | 
			
		||||
		while(Running_) {
 | 
			
		||||
 | 
			
		||||
			if(!Poco::Thread::trySleep(30000)) {
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			LocalLogger.information(fmt::format("Garbage collecting starting run."	));
 | 
			
		||||
 | 
			
		||||
			uint64_t total_connected_time = 0, now = Utils::Now();
 | 
			
		||||
 | 
			
		||||
			if(now-last_zombie_run > 60) {
 | 
			
		||||
				try {
 | 
			
		||||
					poco_information(LocalLogger,
 | 
			
		||||
									 fmt::format("Garbage collecting zombies... (step 1)"));
 | 
			
		||||
					NumberOfConnectingDevices_ = 0;
 | 
			
		||||
					AverageDeviceConnectionTime_ = 0;
 | 
			
		||||
					int waits = 0;
 | 
			
		||||
					for (int hashIndex = 0; hashIndex < MACHash::HashMax(); hashIndex++) {
 | 
			
		||||
						last_zombie_run = now;
 | 
			
		||||
						waits = 0;
 | 
			
		||||
						while (true) {
 | 
			
		||||
							if (SerialNumbersMutex_[hashIndex].try_lock()) {
 | 
			
		||||
								waits = 0;
 | 
			
		||||
								auto hint = SerialNumbers_[hashIndex].begin();
 | 
			
		||||
								while (hint != end(SerialNumbers_[hashIndex])) {
 | 
			
		||||
									if (hint->second == nullptr) {
 | 
			
		||||
										poco_information(
 | 
			
		||||
											LocalLogger,
 | 
			
		||||
											fmt::format("Dead device found in hash index {}", hashIndex));
 | 
			
		||||
										hint = SerialNumbers_[hashIndex].erase(hint);
 | 
			
		||||
									} else {
 | 
			
		||||
										auto Device = hint->second;
 | 
			
		||||
										auto RightNow = Utils::Now();
 | 
			
		||||
										if (Device->Dead_) {
 | 
			
		||||
											AddCleanupSession(Device->State_.sessionId, Device->SerialNumberInt_);
 | 
			
		||||
											++hint;
 | 
			
		||||
											// hint = SerialNumbers_[hashIndex].erase(hint);
 | 
			
		||||
										} else if (RightNow > Device->LastContact_ &&
 | 
			
		||||
												   (RightNow - Device->LastContact_) > SessionTimeOut_) {
 | 
			
		||||
											poco_information(
 | 
			
		||||
												LocalLogger,
 | 
			
		||||
												fmt::format(
 | 
			
		||||
													"{}: Session seems idle. Controller disconnecting device.",
 | 
			
		||||
													Device->SerialNumber_));
 | 
			
		||||
											// hint = SerialNumbers_[hashIndex].erase(hint);
 | 
			
		||||
											AddCleanupSession(Device->State_.sessionId, Device->SerialNumberInt_);
 | 
			
		||||
											++hint;
 | 
			
		||||
										} else {
 | 
			
		||||
											if (Device->State_.Connected) {
 | 
			
		||||
												total_connected_time +=
 | 
			
		||||
													(RightNow - Device->State_.started);
 | 
			
		||||
											}
 | 
			
		||||
											++hint;
 | 
			
		||||
										}
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
								SerialNumbersMutex_[hashIndex].unlock();
 | 
			
		||||
								break;
 | 
			
		||||
							} else if (waits < 5) {
 | 
			
		||||
								waits++;
 | 
			
		||||
								Poco::Thread::trySleep(10);
 | 
			
		||||
							} else {
 | 
			
		||||
								break;
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					poco_information(LocalLogger, fmt::format("Garbage collecting zombies... (step 2)"));
 | 
			
		||||
					LeftOverSessions_ = 0;
 | 
			
		||||
					for (int i = 0; i < SessionHash::HashMax(); i++) {
 | 
			
		||||
						waits = 0;
 | 
			
		||||
						while (true) {
 | 
			
		||||
							if (SessionMutex_[i].try_lock()) {
 | 
			
		||||
								waits = 0;
 | 
			
		||||
								auto hint = Sessions_[i].begin();
 | 
			
		||||
								auto RightNow = Utils::Now();
 | 
			
		||||
								while (hint != end(Sessions_[i])) {
 | 
			
		||||
									if (hint->second == nullptr) {
 | 
			
		||||
										hint = Sessions_[i].erase(hint);
 | 
			
		||||
									} else if (hint->second->Dead_) {
 | 
			
		||||
										// hint = Sessions_[i].erase(hint);
 | 
			
		||||
										AddCleanupSession(hint->second->State_.sessionId, hint->second->SerialNumberInt_);
 | 
			
		||||
										++hint;
 | 
			
		||||
									} else if (RightNow > hint->second->LastContact_ &&
 | 
			
		||||
											   (RightNow - hint->second->LastContact_) >
 | 
			
		||||
												   SessionTimeOut_) {
 | 
			
		||||
										poco_information(
 | 
			
		||||
											LocalLogger,
 | 
			
		||||
											fmt::format("{}: Session seems idle. Controller disconnecting device.",
 | 
			
		||||
														hint->second->SerialNumber_));
 | 
			
		||||
										AddCleanupSession(hint->second->State_.sessionId, hint->second->SerialNumberInt_);
 | 
			
		||||
										++hint;
 | 
			
		||||
										// hint = Sessions_[i].erase(hint);
 | 
			
		||||
									} else {
 | 
			
		||||
										++LeftOverSessions_;
 | 
			
		||||
										++hint;
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
								SessionMutex_[i].unlock();
 | 
			
		||||
								break;
 | 
			
		||||
							} else if (waits < 5) {
 | 
			
		||||
								Poco::Thread::trySleep(10);
 | 
			
		||||
								waits++;
 | 
			
		||||
							} else {
 | 
			
		||||
								break;
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					AverageDeviceConnectionTime_ = NumberOfConnectedDevices_ > 0
 | 
			
		||||
													   ? total_connected_time / NumberOfConnectedDevices_
 | 
			
		||||
													   : 0;
 | 
			
		||||
					poco_information(LocalLogger, fmt::format("Garbage collecting zombies done..."));
 | 
			
		||||
				} catch (const Poco::Exception &E) {
 | 
			
		||||
					poco_error(LocalLogger, fmt::format("Poco::Exception: Garbage collecting zombies failed: {}", E.displayText()));
 | 
			
		||||
				} catch (const std::exception &E) {
 | 
			
		||||
					poco_error(LocalLogger, fmt::format("std::exception: Garbage collecting zombies failed: {}", E.what()));
 | 
			
		||||
				} catch (...) {
 | 
			
		||||
					poco_error(LocalLogger, fmt::format("exception:Garbage collecting zombies failed: {}", "unknown"));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(NumberOfConnectedDevices_) {
 | 
			
		||||
				if (last_garbage_run > 0) {
 | 
			
		||||
					AverageDeviceConnectionTime_ += (now - last_garbage_run);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			try {
 | 
			
		||||
				if ((now - last_log) > 60) {
 | 
			
		||||
					last_log = now;
 | 
			
		||||
					poco_information(
 | 
			
		||||
						LocalLogger,
 | 
			
		||||
						fmt::format("Active AP connections: {} Connecting: {} Average connection time: {} seconds. Left Over Sessions: {}",
 | 
			
		||||
									NumberOfConnectedDevices_, NumberOfConnectingDevices_,
 | 
			
		||||
									AverageDeviceConnectionTime_, LeftOverSessions_));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				GWWebSocketNotifications::NumberOfConnection_t Notification;
 | 
			
		||||
				Notification.content.numberOfConnectingDevices = NumberOfConnectingDevices_;
 | 
			
		||||
				Notification.content.numberOfDevices = NumberOfConnectedDevices_;
 | 
			
		||||
				Notification.content.averageConnectedTime = AverageDeviceConnectionTime_;
 | 
			
		||||
				GetTotalDataStatistics(Notification.content.tx, Notification.content.rx);
 | 
			
		||||
				GWWebSocketNotifications::NumberOfConnections(Notification);
 | 
			
		||||
 | 
			
		||||
				Poco::JSON::Object KafkaNotification;
 | 
			
		||||
				Notification.to_json(KafkaNotification);
 | 
			
		||||
 | 
			
		||||
				Poco::JSON::Object FullEvent;
 | 
			
		||||
				FullEvent.set("type", "load-update");
 | 
			
		||||
				FullEvent.set("timestamp", now);
 | 
			
		||||
				FullEvent.set("payload", KafkaNotification);
 | 
			
		||||
 | 
			
		||||
				KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, "system", FullEvent);
 | 
			
		||||
				LocalLogger.information(fmt::format("Garbage collection finished run."));
 | 
			
		||||
				last_garbage_run = now;
 | 
			
		||||
			} catch (const Poco::Exception &E) {
 | 
			
		||||
				LocalLogger.error(fmt::format("Poco::Exception: Garbage collecting failed: {}", E.displayText()));
 | 
			
		||||
			} catch (const std::exception &E) {
 | 
			
		||||
				LocalLogger.error(fmt::format("std::exception: Garbage collecting failed: {}", E.what()));
 | 
			
		||||
			} catch (...) {
 | 
			
		||||
				LocalLogger.error(fmt::format("exception:Garbage collecting failed: {}", "unknown"));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWWebSocketNotifications::NumberOfConnection_t Notification;
 | 
			
		||||
		Notification.content.numberOfConnectingDevices = NumberOfConnectingDevices_;
 | 
			
		||||
		Notification.content.numberOfDevices = NumberOfConnectedDevices_;
 | 
			
		||||
		Notification.content.averageConnectedTime = AverageDeviceConnectionTime_;
 | 
			
		||||
		GetTotalDataStatistics(Notification.content.tx,Notification.content.rx);
 | 
			
		||||
		GWWebSocketNotifications::NumberOfConnections(Notification);
 | 
			
		||||
		LocalLogger.information(fmt::format("Garbage collector done for the day."	));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::Stop() {
 | 
			
		||||
		poco_information(Logger(), "Stopping...");
 | 
			
		||||
		Running_ = false;
 | 
			
		||||
 | 
			
		||||
		Timer_.stop();
 | 
			
		||||
		GarbageCollector_.wakeUp();
 | 
			
		||||
		GarbageCollector_.join();
 | 
			
		||||
 | 
			
		||||
		for (auto &server : WebServers_) {
 | 
			
		||||
			server->stopAll();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Reactor_pool_->Stop();
 | 
			
		||||
		Reactor_.stop();
 | 
			
		||||
		ReactorThread_.join();
 | 
			
		||||
		poco_information(Logger(), "Stopped...");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::GetHealthDevices(std::uint64_t lowLimit, std::uint64_t  highLimit, std::vector<std::string> & SerialNumbers) {
 | 
			
		||||
		SerialNumbers.clear();
 | 
			
		||||
		for(int i=0;i<SessionHash::HashMax();i++) {
 | 
			
		||||
			std::lock_guard Lock(SessionMutex_[i]);
 | 
			
		||||
			for (const auto &connection : Sessions_[i]) {
 | 
			
		||||
				if (connection.second->RawLastHealthcheck_.Sanity >= lowLimit &&
 | 
			
		||||
					connection.second->RawLastHealthcheck_.Sanity <= highLimit) {
 | 
			
		||||
					SerialNumbers.push_back(connection.second->SerialNumber_);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::GetStatistics(uint64_t SerialNumber, std::string &Statistics) const {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == SerialNumbers_.end() || Device->second.second == nullptr) {
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (DeviceHint == SerialNumbers_[hashIndex].end() || DeviceHint->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->GetLastStats(Statistics);
 | 
			
		||||
		Connection->GetLastStats(Statistics);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::GetState(uint64_t SerialNumber, GWObjects::ConnectionState &State) const {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == SerialNumbers_.end() || Device->second.second == nullptr) {
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (DeviceHint == SerialNumbers_[hashIndex].end() ||
 | 
			
		||||
				DeviceHint->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->GetState(State);
 | 
			
		||||
		Connection->GetState(State);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::GetHealthcheck(uint64_t SerialNumber,
 | 
			
		||||
									  GWObjects::HealthCheck &CheckData) const {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == SerialNumbers_.end() || Device->second.second == nullptr) {
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto Device = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (Device == SerialNumbers_[hashIndex].end() || Device->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = Device->second;
 | 
			
		||||
		}
 | 
			
		||||
		Connection->GetLastHealthCheck(CheckData);
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::StartSession(uint64_t session_id, uint64_t SerialNumber) {
 | 
			
		||||
		auto sessionHash = SessionHash::Hash(session_id);
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard SessionLock(SessionMutex_[sessionHash]);
 | 
			
		||||
			auto SessionHint = Sessions_[sessionHash].find(session_id);
 | 
			
		||||
			if (SessionHint == end(Sessions_[sessionHash])) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			Connection = SessionHint->second;
 | 
			
		||||
			Sessions_[sessionHash].erase(SessionHint);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto deviceHash = MACHash::Hash(SerialNumber);
 | 
			
		||||
		std::lock_guard DeviceLock(SerialNumbersMutex_[deviceHash]);
 | 
			
		||||
		SerialNumbers_[deviceHash][SerialNumber] = Connection;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::EndSession(uint64_t session_id, uint64_t SerialNumber) {
 | 
			
		||||
		{
 | 
			
		||||
			poco_trace(Logger(), fmt::format("Ending session 1: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
 | 
			
		||||
			auto sessionHash = SessionHash::Hash(session_id);
 | 
			
		||||
			std::lock_guard SessionLock(SessionMutex_[sessionHash]);
 | 
			
		||||
			Sessions_[sessionHash].erase(session_id);
 | 
			
		||||
			poco_trace(Logger(), fmt::format("Ended session 1: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			poco_trace(Logger(), fmt::format("Ending session 2.0: {} for device: {} hi:{}", session_id, Utils::IntToSerialNumber(SerialNumber), hashIndex));
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			poco_trace(Logger(), fmt::format("Ending session 2.1: {} for device: {} hi:{}", session_id, Utils::IntToSerialNumber(SerialNumber), hashIndex));
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			poco_trace(Logger(), fmt::format("Ending session 2.2: {} for device: {} hi:{}", session_id, Utils::IntToSerialNumber(SerialNumber), hashIndex));
 | 
			
		||||
			if (DeviceHint == SerialNumbers_[hashIndex].end()
 | 
			
		||||
				|| DeviceHint->second == nullptr
 | 
			
		||||
				|| DeviceHint->second->State_.sessionId != session_id) {
 | 
			
		||||
				poco_trace(Logger(), fmt::format("Did not end session 2: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			SerialNumbers_[hashIndex].erase(DeviceHint);
 | 
			
		||||
			poco_trace(Logger(), fmt::format("Ended session 2: {} for device: {}", session_id, Utils::IntToSerialNumber(SerialNumber)));
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->GetLastHealthCheck(CheckData);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::SetSessionDetails(uint64_t connection_id, uint64_t SerialNumber) {
 | 
			
		||||
		std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
 | 
			
		||||
		auto Conn = Sessions_.find(connection_id);
 | 
			
		||||
		if (Conn == end(Sessions_))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		auto CurrentSerialNumber = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
		if ((CurrentSerialNumber == SerialNumbers_.end()) ||
 | 
			
		||||
			(CurrentSerialNumber->second.first < connection_id)) {
 | 
			
		||||
			SerialNumbers_[SerialNumber] = std::make_pair(connection_id, Conn->second);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::EndSession(uint64_t session_id, uint64_t serial_number) {
 | 
			
		||||
		std::lock_guard G(WSServerMutex_);
 | 
			
		||||
 | 
			
		||||
		auto Session = Sessions_.find(session_id);
 | 
			
		||||
		if (Session == end(Sessions_))
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		Garbage_.push_back(Session->second);
 | 
			
		||||
 | 
			
		||||
		auto Device = SerialNumbers_.find(serial_number);
 | 
			
		||||
		if (Device == end(SerialNumbers_)) {
 | 
			
		||||
			Sessions_.erase(Session);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (Device->second.first == session_id) {
 | 
			
		||||
			Sessions_.erase(Session);
 | 
			
		||||
			SerialNumbers_.erase(Device);
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Sessions_.erase(Session);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::Connected(uint64_t SerialNumber,
 | 
			
		||||
								 GWObjects::DeviceRestrictions &Restrictions) const {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_) || Device->second.second == nullptr) {
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->GetRestrictions(Restrictions);
 | 
			
		||||
		return DevicePtr->State_.Connected;
 | 
			
		||||
 | 
			
		||||
		if(Connection->Dead_) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		Restrictions = Connection->GetRestrictions();
 | 
			
		||||
		return Connection->State_.Connected;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::Connected(uint64_t SerialNumber) const {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_) || Device->second.second == nullptr) {
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		return DevicePtr->State_.Connected;
 | 
			
		||||
 | 
			
		||||
		if(Connection->Dead_) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		return Connection->State_.Connected;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::SendFrame(uint64_t SerialNumber, const std::string &Payload) const {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == SerialNumbers_.end() || Device->second.second == nullptr) {
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(Connection->Dead_) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			return DevicePtr->Send(Payload);
 | 
			
		||||
			return Connection->Send(Payload);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			poco_debug(Logger(), fmt::format(": SendFrame: Could not send data to device '{}'",
 | 
			
		||||
											 Utils::IntToSerialNumber(SerialNumber)));
 | 
			
		||||
@@ -360,61 +626,64 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::StopWebSocketTelemetry(uint64_t RPCID, uint64_t SerialNumber) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_) || Device->second.second == nullptr) {
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto Device = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_[hashIndex]) || Device->second == nullptr) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = Device->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->StopWebSocketTelemetry(RPCID);
 | 
			
		||||
		Connection->StopWebSocketTelemetry(RPCID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void
 | 
			
		||||
	AP_WS_Server::SetWebSocketTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber,
 | 
			
		||||
												 uint64_t Interval, uint64_t Lifetime,
 | 
			
		||||
												 const std::vector<std::string> &TelemetryTypes) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_) || Device->second.second == nullptr) {
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->SetWebSocketTelemetryReporting(RPCID, Interval, Lifetime, TelemetryTypes);
 | 
			
		||||
		Connection->SetWebSocketTelemetryReporting(RPCID, Interval, Lifetime, TelemetryTypes);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::SetKafkaTelemetryReporting(uint64_t RPCID, uint64_t SerialNumber,
 | 
			
		||||
												  uint64_t Interval, uint64_t Lifetime,
 | 
			
		||||
												  const std::vector<std::string> &TelemetryTypes) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_) || Device->second.second == nullptr) {
 | 
			
		||||
			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;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->SetKafkaTelemetryReporting(RPCID, Interval, Lifetime, TelemetryTypes);
 | 
			
		||||
		Connection->SetKafkaTelemetryReporting(RPCID, Interval, Lifetime, TelemetryTypes);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::StopKafkaTelemetry(uint64_t RPCID, uint64_t SerialNumber) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_) || Device->second.second == nullptr) {
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->StopKafkaTelemetry(RPCID);
 | 
			
		||||
		Connection->StopKafkaTelemetry(RPCID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void AP_WS_Server::GetTelemetryParameters(
 | 
			
		||||
@@ -422,16 +691,19 @@ namespace OpenWifi {
 | 
			
		||||
		uint64_t &TelemetryWebSocketTimer, uint64_t &TelemetryKafkaTimer,
 | 
			
		||||
		uint64_t &TelemetryWebSocketCount, uint64_t &TelemetryKafkaCount,
 | 
			
		||||
		uint64_t &TelemetryWebSocketPackets, uint64_t &TelemetryKafkaPackets) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(SerialNumber);
 | 
			
		||||
			if (Device == end(SerialNumbers_) || Device->second.second == nullptr) {
 | 
			
		||||
			auto hashIndex = MACHash::Hash(SerialNumber);
 | 
			
		||||
			std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(SerialNumber);
 | 
			
		||||
			if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
		DevicePtr->GetTelemetryParameters(TelemetryRunning, TelemetryInterval,
 | 
			
		||||
 | 
			
		||||
		Connection->GetTelemetryParameters(TelemetryRunning, TelemetryInterval,
 | 
			
		||||
										  TelemetryWebSocketTimer, TelemetryKafkaTimer,
 | 
			
		||||
										  TelemetryWebSocketCount, TelemetryKafkaCount,
 | 
			
		||||
										  TelemetryWebSocketPackets, TelemetryKafkaPackets);
 | 
			
		||||
@@ -439,18 +711,25 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::SendRadiusAccountingData(const std::string &SerialNumber,
 | 
			
		||||
												const unsigned char *buffer, std::size_t size) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(Utils::SerialNumberToInt(SerialNumber));
 | 
			
		||||
			if (Device == SerialNumbers_.end() || Device->second.second == nullptr) {
 | 
			
		||||
			auto IntSerialNumber = Utils::SerialNumberToInt(SerialNumber);
 | 
			
		||||
			auto hashIndex = MACHash::Hash(IntSerialNumber);
 | 
			
		||||
			std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(IntSerialNumber);
 | 
			
		||||
			if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(Connection->Dead_) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			return DevicePtr->SendRadiusAccountingData(buffer, size);
 | 
			
		||||
			return Connection->SendRadiusAccountingData(buffer, size);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			poco_debug(
 | 
			
		||||
				Logger(),
 | 
			
		||||
@@ -462,18 +741,24 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::SendRadiusAuthenticationData(const std::string &SerialNumber,
 | 
			
		||||
													const unsigned char *buffer, std::size_t size) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(Utils::SerialNumberToInt(SerialNumber));
 | 
			
		||||
			if (Device == SerialNumbers_.end() || Device->second.second == nullptr) {
 | 
			
		||||
			auto IntSerialNumber = Utils::SerialNumberToInt(SerialNumber);
 | 
			
		||||
			auto hashIndex = MACHash::Hash(IntSerialNumber);
 | 
			
		||||
			std::lock_guard DevicesLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(IntSerialNumber);
 | 
			
		||||
			if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(Connection->Dead_) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			return DevicePtr->SendRadiusAuthenticationData(buffer, size);
 | 
			
		||||
			return Connection->SendRadiusAuthenticationData(buffer, size);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			poco_debug(
 | 
			
		||||
				Logger(),
 | 
			
		||||
@@ -485,18 +770,23 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	bool AP_WS_Server::SendRadiusCoAData(const std::string &SerialNumber,
 | 
			
		||||
										 const unsigned char *buffer, std::size_t size) {
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> DevicePtr;
 | 
			
		||||
		std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
			auto Device = SerialNumbers_.find(Utils::SerialNumberToInt(SerialNumber));
 | 
			
		||||
			if (Device == SerialNumbers_.end() || Device->second.second == nullptr) {
 | 
			
		||||
			auto IntSerialNumber = Utils::SerialNumberToInt(SerialNumber);
 | 
			
		||||
			auto hashIndex = MACHash::Hash(IntSerialNumber);
 | 
			
		||||
			std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
			auto DeviceHint = SerialNumbers_[hashIndex].find(IntSerialNumber);
 | 
			
		||||
			if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr) {
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
			DevicePtr = Device->second.second;
 | 
			
		||||
			Connection = DeviceHint->second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(Connection->Dead_) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		try {
 | 
			
		||||
			return DevicePtr->SendRadiusCoAData(buffer, size);
 | 
			
		||||
			return Connection->SendRadiusCoAData(buffer, size);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			poco_debug(Logger(),
 | 
			
		||||
					   fmt::format(": SendRadiusCoAData: Could not send data to device '{}'",
 | 
			
		||||
@@ -505,4 +795,4 @@ namespace OpenWifi {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -24,46 +24,51 @@
 | 
			
		||||
#include "Poco/Timer.h"
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Connection.h"
 | 
			
		||||
#include "AP_WS_ReactorPool.h"
 | 
			
		||||
#include "AP_WS_Reactor_Pool.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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		Poco::Logger &Logger_;
 | 
			
		||||
		inline static uint64_t id_ = 1;
 | 
			
		||||
		[[nodiscard]] static inline uint16_t Hash(const std::string & value) {
 | 
			
		||||
			return Hash(Utils::MACToInt(value));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		[[nodiscard]] static inline uint16_t HashMax() {
 | 
			
		||||
			return MACHashMax;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	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,59 +80,52 @@ namespace OpenWifi {
 | 
			
		||||
		bool IsCertOk() { return IssuerCert_ != nullptr; }
 | 
			
		||||
		bool ValidateCertificate(const std::string &ConnectionId,
 | 
			
		||||
								 const Poco::Crypto::X509Certificate &Certificate);
 | 
			
		||||
		// Poco::Net::SocketReactor & GetNextReactor() { return ReactorPool_.NextReactor(); }
 | 
			
		||||
 | 
			
		||||
		inline bool IsSimSerialNumber(const std::string &SerialNumber) const {
 | 
			
		||||
			return IsSim(Poco::toLower(SerialNumber)) &&
 | 
			
		||||
				   Poco::toLower(SerialNumber) == Poco::toLower(SimulatorId_);
 | 
			
		||||
			return IsSim(SerialNumber) &&
 | 
			
		||||
				   SerialNumber == SimulatorId_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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() {
 | 
			
		||||
		void run() override;		//	Garbage collector thread.
 | 
			
		||||
		[[nodiscard]] inline bool IsSimEnabled() const { return SimulatorEnabled_; }
 | 
			
		||||
		[[nodiscard]] inline bool AllowSerialNumberMismatch() const { return AllowSerialNumberMismatch_; }
 | 
			
		||||
		[[nodiscard]] inline uint64_t MismatchDepth() const { return MismatchDepth_; }
 | 
			
		||||
		[[nodiscard]] inline bool UseProvisioning() const { return LookAtProvisioning_; }
 | 
			
		||||
		[[nodiscard]] inline bool UseDefaults() const { return UseDefaultConfig_; }
 | 
			
		||||
		[[nodiscard]] inline bool Running() const { return Running_; }
 | 
			
		||||
		[[nodiscard]] inline std::pair<std::shared_ptr<Poco::Net::SocketReactor>, std::shared_ptr<LockedDbSession>> NextReactor() {
 | 
			
		||||
			return Reactor_pool_->NextReactor();
 | 
			
		||||
		}
 | 
			
		||||
		[[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);
 | 
			
		||||
		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 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 bool DeviceRequiresSecureRtty(uint64_t serialNumber) const {
 | 
			
		||||
			std::lock_guard Lock(WSServerMutex_);
 | 
			
		||||
 | 
			
		||||
			auto Connection = SerialNumbers_.find(serialNumber);
 | 
			
		||||
			if (Connection==end(SerialNumbers_) || Connection->second.second==nullptr)
 | 
			
		||||
				return false;
 | 
			
		||||
			return Connection->second.second->RttyMustBeSecure_;
 | 
			
		||||
		[[nodiscard]] inline bool DeviceRequiresSecureRTTY(uint64_t serialNumber) const {
 | 
			
		||||
			std::shared_ptr<AP_WS_Connection> Connection;
 | 
			
		||||
			{
 | 
			
		||||
				auto hashIndex = MACHash::Hash(serialNumber);
 | 
			
		||||
				std::lock_guard DeviceLock(SerialNumbersMutex_[hashIndex]);
 | 
			
		||||
				auto DeviceHint = SerialNumbers_[hashIndex].find(serialNumber);
 | 
			
		||||
				if (DeviceHint == end(SerialNumbers_[hashIndex]) || DeviceHint->second == nullptr)
 | 
			
		||||
					return false;
 | 
			
		||||
				Connection = DeviceHint->second;
 | 
			
		||||
			}
 | 
			
		||||
			return Connection->RTTYMustBeSecure_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline bool GetStatistics(const std::string &SerialNumber, std::string &Statistics) const {
 | 
			
		||||
			return GetStatistics(Utils::SerialNumberToInt(SerialNumber), Statistics);
 | 
			
		||||
		}
 | 
			
		||||
		bool GetStatistics(uint64_t SerialNumber, std::string &Statistics) const;
 | 
			
		||||
		[[nodiscard]] bool GetStatistics(uint64_t SerialNumber, std::string &Statistics) const;
 | 
			
		||||
 | 
			
		||||
		inline bool GetState(const std::string &SerialNumber,
 | 
			
		||||
							 GWObjects::ConnectionState &State) const {
 | 
			
		||||
@@ -143,13 +141,8 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		bool Connected(uint64_t SerialNumber, GWObjects::DeviceRestrictions &Restrictions) const;
 | 
			
		||||
		bool Connected(uint64_t SerialNumber) const;
 | 
			
		||||
 | 
			
		||||
		inline bool SendFrame(const std::string &SerialNumber, const std::string &Payload) const {
 | 
			
		||||
			return SendFrame(Utils::SerialNumberToInt(SerialNumber), Payload);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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,
 | 
			
		||||
@@ -157,9 +150,8 @@ namespace OpenWifi {
 | 
			
		||||
		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 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);
 | 
			
		||||
@@ -176,7 +168,9 @@ namespace OpenWifi {
 | 
			
		||||
									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 {
 | 
			
		||||
@@ -185,63 +179,81 @@ namespace OpenWifi {
 | 
			
		||||
			NumberOfConnectingDevices = NumberOfConnectingDevices_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline bool SendFrame(const std::string &SerialNumber, const std::string &Payload) const {
 | 
			
		||||
			return SendFrame(Utils::SerialNumberToInt(SerialNumber), Payload);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline void AddRX(std::uint64_t bytes) {
 | 
			
		||||
			std::lock_guard		G(StatsMutex_);
 | 
			
		||||
			RX_ += bytes;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline void AddTX(std::uint64_t bytes) {
 | 
			
		||||
			std::lock_guard		G(StatsMutex_);
 | 
			
		||||
			TX_ += bytes;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline void GetTotalDataStatistics(std::uint64_t &TX, std::uint64_t &RX) const {
 | 
			
		||||
			std::lock_guard		G(StatsMutex_);
 | 
			
		||||
			TX = TX_;
 | 
			
		||||
			RX = RX_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline bool GetHealthDevices(std::uint64_t lowLimit, std::uint64_t  highLimit, std::vector<std::string> & SerialNumbers) {
 | 
			
		||||
			std::lock_guard		G(WSServerMutex_);
 | 
			
		||||
		bool KafkaDisableState() const { return KafkaDisableState_; }
 | 
			
		||||
		bool KafkaDisableHealthChecks() const { return KafkaDisableHealthChecks_; }
 | 
			
		||||
 | 
			
		||||
			for(const auto &connection:Sessions_) {
 | 
			
		||||
				if(	connection.second->RawLastHealthcheck_.Sanity>=lowLimit 	&&
 | 
			
		||||
					connection.second->RawLastHealthcheck_.Sanity<=highLimit) {
 | 
			
		||||
					SerialNumbers.push_back(connection.second->SerialNumber_);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return true;
 | 
			
		||||
		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:
 | 
			
		||||
		mutable std::recursive_mutex WSServerMutex_;
 | 
			
		||||
		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_;
 | 
			
		||||
		Poco::ThreadPool DeviceConnectionPool_{"ws:dev-pool", 2, 64};
 | 
			
		||||
		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::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::uint64_t 			MismatchDepth_ = 2;
 | 
			
		||||
 | 
			
		||||
		mutable std::mutex		StatsMutex_;
 | 
			
		||||
		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::vector<std::shared_ptr<AP_WS_Connection>> Garbage_;
 | 
			
		||||
		std::atomic_bool 		KafkaDisableState_=false,
 | 
			
		||||
						 		KafkaDisableHealthChecks_=false;
 | 
			
		||||
 | 
			
		||||
		std::unique_ptr<Poco::TimerCallback<AP_WS_Server>> GarbageCollectorCallback_;
 | 
			
		||||
		Poco::Timer Timer_;
 | 
			
		||||
		Poco::Thread GarbageCollector_;
 | 
			
		||||
		Poco::Thread 			GarbageCollector_;
 | 
			
		||||
 | 
			
		||||
		AP_WS_Server() noexcept
 | 
			
		||||
			: SubSystemServer("WebSocketServer", "WS-SVR", "ucentral.websocket") {}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
#include "CentralConfig.h"
 | 
			
		||||
#include "nlohmann/json.hpp"
 | 
			
		||||
@@ -34,7 +35,7 @@ namespace OpenWifi {
 | 
			
		||||
			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));
 | 
			
		||||
@@ -68,7 +69,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
			auto Hint = Platforms_.find(DeviceType);
 | 
			
		||||
			if (Hint == Platforms_.end())
 | 
			
		||||
				return "AP";
 | 
			
		||||
				return Platforms::AP;
 | 
			
		||||
			return Hint->second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -110,7 +111,7 @@ namespace OpenWifi {
 | 
			
		||||
				i >> cache;
 | 
			
		||||
 | 
			
		||||
				for (const auto &[Type, Platform] : cache.items()) {
 | 
			
		||||
					Platforms_[Type] = Platform;
 | 
			
		||||
					Platforms_[Type] = Poco::toLower(Platform.get<std::string>());
 | 
			
		||||
				}
 | 
			
		||||
			} catch (...) {
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -204,6 +204,17 @@ namespace OpenWifi::Config {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::uint64_t Config::UUID() {
 | 
			
		||||
		try {
 | 
			
		||||
			Poco::JSON::Parser Parser;
 | 
			
		||||
			auto object = Parser.parse(Config_).extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
			if (object->has("uuid"))
 | 
			
		||||
				return object->get("uuid");
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool Config::Valid() {
 | 
			
		||||
		try {
 | 
			
		||||
			Poco::JSON::Parser Parser;
 | 
			
		||||
@@ -254,7 +265,11 @@ namespace OpenWifi::Config {
 | 
			
		||||
				Model_ = Caps->get("model").toString();
 | 
			
		||||
 | 
			
		||||
			if (Caps->has("platform"))
 | 
			
		||||
				Platform_ = Caps->get("platform").toString();
 | 
			
		||||
				Platform_ = Poco::toLower(Caps->get("platform").toString());
 | 
			
		||||
 | 
			
		||||
			if(Compatible_.empty()) {
 | 
			
		||||
				Compatible_ = Model_;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			std::ostringstream OS;
 | 
			
		||||
			Caps->stringify(OS);
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ namespace OpenWifi::Config {
 | 
			
		||||
		[[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();
 | 
			
		||||
 
 | 
			
		||||
@@ -45,11 +45,9 @@ namespace OpenWifi {
 | 
			
		||||
							std::lock_guard Lock(LocalMutex_);
 | 
			
		||||
							auto RPC = OutStandingRequests_.find(ID);
 | 
			
		||||
							if (RPC == OutStandingRequests_.end()) {
 | 
			
		||||
								//								std::cout << __LINE__ << std::endl;
 | 
			
		||||
								poco_debug(Logger(), fmt::format("({}): RPC {} cannot be found.",
 | 
			
		||||
																 SerialNumberStr, ID));
 | 
			
		||||
							} else if (RPC->second.SerialNumber != Resp->SerialNumber_) {
 | 
			
		||||
								//								std::cout << __LINE__ << std::endl;
 | 
			
		||||
								poco_debug(
 | 
			
		||||
									Logger(),
 | 
			
		||||
									fmt::format("({}): RPC {} serial number mismatch {}!={}.",
 | 
			
		||||
@@ -60,7 +58,6 @@ namespace OpenWifi {
 | 
			
		||||
								std::chrono::duration<double, std::milli> rpc_execution_time =
 | 
			
		||||
									std::chrono::high_resolution_clock::now() -
 | 
			
		||||
									RPC->second.submitted;
 | 
			
		||||
								//								std::cout << __LINE__ << std::endl;
 | 
			
		||||
								poco_debug(Logger(),
 | 
			
		||||
										   fmt::format("({}): Received RPC answer {}. Command={}",
 | 
			
		||||
													   SerialNumberStr, ID,
 | 
			
		||||
@@ -140,7 +137,6 @@ namespace OpenWifi {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			//				std::cout << __LINE__ << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
		Command.State = 0;
 | 
			
		||||
 | 
			
		||||
@@ -163,7 +159,6 @@ namespace OpenWifi {
 | 
			
		||||
		if (Command.rpc_entry) {
 | 
			
		||||
			TmpRpcEntry = Command.rpc_entry;
 | 
			
		||||
		}
 | 
			
		||||
		//		std::cout << __LINE__ << "  State=" << Command.State << std::endl;
 | 
			
		||||
		if (Command.State == 2) {
 | 
			
		||||
			//	 look at the payload to see if we should continue or not...
 | 
			
		||||
			if (Payload->has("result")) {
 | 
			
		||||
@@ -173,12 +168,10 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
					std::uint64_t Error = Status->get("error");
 | 
			
		||||
					if (Error == 0) {
 | 
			
		||||
						//						std::cout << __LINE__ << std::endl;
 | 
			
		||||
						StorageService()->CommandCompleted(Command.UUID, Payload,
 | 
			
		||||
														   rpc_execution_time, true);
 | 
			
		||||
						Command.State = 1;
 | 
			
		||||
					} else {
 | 
			
		||||
						//						std::cout << __LINE__ << std::endl;
 | 
			
		||||
						StorageService()->CommandCompleted(Command.UUID, Payload,
 | 
			
		||||
														   rpc_execution_time, true);
 | 
			
		||||
						std::string ErrorTxt = Status->get("result");
 | 
			
		||||
@@ -186,14 +179,11 @@ namespace OpenWifi {
 | 
			
		||||
						Command.State = 0;
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					//					std::cout << __LINE__ << std::endl;
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				//				std::cout << __LINE__ << std::endl;
 | 
			
		||||
				Command.State = 0;
 | 
			
		||||
			}
 | 
			
		||||
		} else if (Command.State == 1) {
 | 
			
		||||
			//			std::cout << "Completing script 2 phase commit." << std::endl;
 | 
			
		||||
			StorageService()->CommandCompleted(Command.UUID, Payload, rpc_execution_time, true);
 | 
			
		||||
			if (Command.Deferred) {
 | 
			
		||||
				Reply = false;
 | 
			
		||||
@@ -202,7 +192,6 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (Command.State == 0) {
 | 
			
		||||
			//			std::cout << __LINE__ << "  State=" << Command.State << std::endl;
 | 
			
		||||
			OutStandingRequests_.erase(Command.Id);
 | 
			
		||||
		}
 | 
			
		||||
		if (Reply && TmpRpcEntry != nullptr)
 | 
			
		||||
@@ -262,8 +251,6 @@ namespace OpenWifi {
 | 
			
		||||
		for (auto request = OutStandingRequests_.begin(); request != OutStandingRequests_.end();) {
 | 
			
		||||
			std::chrono::duration<double, std::milli> delta = now - request->second.submitted;
 | 
			
		||||
			if (delta > 10min) {
 | 
			
		||||
				//				std::cout << __LINE__ << "  -->> " << request->second.Id <<
 | 
			
		||||
				// std::endl;
 | 
			
		||||
				MyLogger.debug(fmt::format("{}: Command={} for {} Timed out.", request->second.UUID,
 | 
			
		||||
										   APCommands::to_string(request->second.Command),
 | 
			
		||||
										   Utils::IntToSerialNumber(request->second.SerialNumber)));
 | 
			
		||||
@@ -275,8 +262,6 @@ namespace OpenWifi {
 | 
			
		||||
				StorageService()->SetCommandTimedOut(request->second.UUID);
 | 
			
		||||
				request = OutStandingRequests_.erase(request);
 | 
			
		||||
			} else {
 | 
			
		||||
				//				std::cout << __LINE__ << "  -->> " << request->second.Id <<
 | 
			
		||||
				// std::endl;
 | 
			
		||||
				++request;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -302,97 +287,116 @@ namespace OpenWifi {
 | 
			
		||||
			StorageService()->RemovedExpiredCommands();
 | 
			
		||||
			StorageService()->RemoveTimedOutCommands();
 | 
			
		||||
 | 
			
		||||
			std::vector<GWObjects::CommandDetails> Commands;
 | 
			
		||||
			if (StorageService()->GetReadyToExecuteCommands(0, 200, Commands)) {
 | 
			
		||||
				poco_trace(MyLogger,
 | 
			
		||||
						   fmt::format("Scheduler about to process {} commands.", Commands.size()));
 | 
			
		||||
				for (auto &Cmd : Commands) {
 | 
			
		||||
					if (!Running_) {
 | 
			
		||||
						poco_warning(MyLogger, "Scheduler quitting because service is stopping.");
 | 
			
		||||
						break;
 | 
			
		||||
			std::uint64_t offset = 0;
 | 
			
		||||
			bool Done = false;
 | 
			
		||||
			while (!Done) {
 | 
			
		||||
				std::vector<GWObjects::CommandDetails> Commands;
 | 
			
		||||
				if (StorageService()->GetReadyToExecuteCommands(offset, 200, Commands)) {
 | 
			
		||||
					if(Commands.empty()) {
 | 
			
		||||
						Done=true;
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					poco_trace(MyLogger,
 | 
			
		||||
							   fmt::format("{}: Serial={} Command={} Starting processing.",
 | 
			
		||||
										   Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
					try {
 | 
			
		||||
 | 
			
		||||
						//	Skip an already running command
 | 
			
		||||
						if (IsCommandRunning(Cmd.UUID)) {
 | 
			
		||||
							continue;
 | 
			
		||||
					poco_trace(MyLogger, fmt::format("Scheduler about to process {} commands.",
 | 
			
		||||
													 Commands.size()));
 | 
			
		||||
					for (auto &Cmd : Commands) {
 | 
			
		||||
						if (!Running_) {
 | 
			
		||||
							poco_warning(MyLogger,
 | 
			
		||||
										 "Scheduler quitting because service is stopping.");
 | 
			
		||||
							break;
 | 
			
		||||
						}
 | 
			
		||||
						poco_trace(MyLogger,
 | 
			
		||||
								   fmt::format("{}: Serial={} Command={} Starting processing.",
 | 
			
		||||
											   Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
						try {
 | 
			
		||||
 | 
			
		||||
						auto now = Utils::Now();
 | 
			
		||||
						// 2 hour timeout for commands
 | 
			
		||||
						if ((now - Cmd.Submitted) > commandTimeOut_) {
 | 
			
		||||
							poco_information(MyLogger,
 | 
			
		||||
											 fmt::format("{}: Serial={} Command={} has expired.",
 | 
			
		||||
														 Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
							StorageService()->SetCommandTimedOut(Cmd.UUID);
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
							//	Skip an already running command
 | 
			
		||||
							if (IsCommandRunning(Cmd.UUID)) {
 | 
			
		||||
								continue;
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
						auto SerialNumberInt = Utils::SerialNumberToInt(Cmd.SerialNumber);
 | 
			
		||||
						if (!AP_WS_Server()->Connected(SerialNumberInt)) {
 | 
			
		||||
							poco_trace(
 | 
			
		||||
							auto now = Utils::Now();
 | 
			
		||||
							// 2 hour timeout for commands
 | 
			
		||||
							if ((now - Cmd.Submitted) > commandTimeOut_) {
 | 
			
		||||
								poco_information(
 | 
			
		||||
									MyLogger, fmt::format("{}: Serial={} Command={} has expired.",
 | 
			
		||||
														  Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
								StorageService()->SetCommandTimedOut(Cmd.UUID);
 | 
			
		||||
								continue;
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							auto SerialNumberInt = Utils::SerialNumberToInt(Cmd.SerialNumber);
 | 
			
		||||
							if (!AP_WS_Server()->Connected(SerialNumberInt)) {
 | 
			
		||||
								poco_trace(
 | 
			
		||||
									MyLogger,
 | 
			
		||||
									fmt::format("{}: Serial={} Command={} Device is not connected.",
 | 
			
		||||
												Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
								StorageService()->SetCommandLastTry(Cmd.UUID);
 | 
			
		||||
								continue;
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							std::string ExecutingUUID;
 | 
			
		||||
							APCommands::Commands ExecutingCommand = APCommands::Commands::unknown;
 | 
			
		||||
							if (CommandRunningForDevice(SerialNumberInt, ExecutingUUID,
 | 
			
		||||
														ExecutingCommand)) {
 | 
			
		||||
								poco_trace(
 | 
			
		||||
									MyLogger,
 | 
			
		||||
									fmt::format("{}: Serial={} Command={} Device is already busy "
 | 
			
		||||
												"with command {} (Command={}).",
 | 
			
		||||
												Cmd.UUID, Cmd.SerialNumber, Cmd.Command,
 | 
			
		||||
												ExecutingUUID,
 | 
			
		||||
												APCommands::to_string(ExecutingCommand)));
 | 
			
		||||
								continue;
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							Poco::JSON::Parser P;
 | 
			
		||||
							bool Sent;
 | 
			
		||||
							poco_information(
 | 
			
		||||
								MyLogger,
 | 
			
		||||
								fmt::format("{}: Serial={} Command={} Device is not connected.",
 | 
			
		||||
								fmt::format("{}: Serial={} Command={} Preparing execution.",
 | 
			
		||||
											Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
							StorageService()->SetCommandLastTry(Cmd.UUID);
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						std::string ExecutingUUID;
 | 
			
		||||
						APCommands::Commands ExecutingCommand = APCommands::Commands::unknown;
 | 
			
		||||
						if (CommandRunningForDevice(SerialNumberInt, ExecutingUUID,
 | 
			
		||||
													ExecutingCommand)) {
 | 
			
		||||
							poco_trace(
 | 
			
		||||
							auto Params = P.parse(Cmd.Details).extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
							auto Result = PostCommandDisk(
 | 
			
		||||
								Next_RPC_ID(), APCommands::to_apcommand(Cmd.Command.c_str()),
 | 
			
		||||
								Cmd.SerialNumber, Cmd.Command, *Params, Cmd.UUID, Sent);
 | 
			
		||||
							if (Sent) {
 | 
			
		||||
								StorageService()->SetCommandExecuted(Cmd.UUID);
 | 
			
		||||
								poco_debug(MyLogger,
 | 
			
		||||
										   fmt::format("{}: Serial={} Command={} Sent.", Cmd.UUID,
 | 
			
		||||
													   Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
							} else {
 | 
			
		||||
								poco_debug(
 | 
			
		||||
									MyLogger,
 | 
			
		||||
									fmt::format("{}: Serial={} Command={} Re-queued command.",
 | 
			
		||||
												Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
								StorageService()->SetCommandLastTry(Cmd.UUID);
 | 
			
		||||
							}
 | 
			
		||||
						} catch (const Poco::Exception &E) {
 | 
			
		||||
							poco_debug(
 | 
			
		||||
								MyLogger,
 | 
			
		||||
								fmt::format("{}: Serial={} Command={} Device is already busy "
 | 
			
		||||
											"with command {} (Command={}).",
 | 
			
		||||
											Cmd.UUID, Cmd.SerialNumber, Cmd.Command, ExecutingUUID,
 | 
			
		||||
											APCommands::to_string(ExecutingCommand)));
 | 
			
		||||
							continue;
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						Poco::JSON::Parser P;
 | 
			
		||||
						bool Sent;
 | 
			
		||||
						poco_information(
 | 
			
		||||
							MyLogger, fmt::format("{}: Serial={} Command={} Preparing execution.",
 | 
			
		||||
												  Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
						auto Params = P.parse(Cmd.Details).extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
						auto Result = PostCommandDisk(
 | 
			
		||||
							Next_RPC_ID(), APCommands::to_apcommand(Cmd.Command.c_str()),
 | 
			
		||||
							Cmd.SerialNumber, Cmd.Command, *Params, Cmd.UUID, Sent);
 | 
			
		||||
						if (Sent) {
 | 
			
		||||
								fmt::format(
 | 
			
		||||
									"{}: Serial={} Command={} Failed. Command marked as completed.",
 | 
			
		||||
									Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
							MyLogger.log(E);
 | 
			
		||||
							StorageService()->SetCommandExecuted(Cmd.UUID);
 | 
			
		||||
						} catch (...) {
 | 
			
		||||
							poco_debug(MyLogger,
 | 
			
		||||
									   fmt::format("{}: Serial={} Command={} Sent.", Cmd.UUID,
 | 
			
		||||
												   Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
						} else {
 | 
			
		||||
							poco_debug(MyLogger,
 | 
			
		||||
									   fmt::format("{}: Serial={} Command={} Re-queued command.",
 | 
			
		||||
									   fmt::format("{}: Serial={} Command={} Hard failure. "
 | 
			
		||||
												   "Command marked as completed.",
 | 
			
		||||
												   Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
							StorageService()->SetCommandLastTry(Cmd.UUID);
 | 
			
		||||
							StorageService()->SetCommandExecuted(Cmd.UUID);
 | 
			
		||||
						}
 | 
			
		||||
					} catch (const Poco::Exception &E) {
 | 
			
		||||
						poco_debug(
 | 
			
		||||
							MyLogger,
 | 
			
		||||
							fmt::format(
 | 
			
		||||
								"{}: Serial={} Command={} Failed. Command marked as completed.",
 | 
			
		||||
								Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
						MyLogger.log(E);
 | 
			
		||||
						StorageService()->SetCommandExecuted(Cmd.UUID);
 | 
			
		||||
					} catch (...) {
 | 
			
		||||
						poco_debug(MyLogger, fmt::format("{}: Serial={} Command={} Hard failure. "
 | 
			
		||||
														 "Command marked as completed.",
 | 
			
		||||
														 Cmd.UUID, Cmd.SerialNumber, Cmd.Command));
 | 
			
		||||
						StorageService()->SetCommandExecuted(Cmd.UUID);
 | 
			
		||||
					}
 | 
			
		||||
					offset += Commands.size();
 | 
			
		||||
				} else {
 | 
			
		||||
					Done=true;
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} catch (Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		catch (Poco::Exception &E) {
 | 
			
		||||
			MyLogger.log(E);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
		}
 | 
			
		||||
		catch (...) {
 | 
			
		||||
			poco_warning(MyLogger, "Exception during command processing.");
 | 
			
		||||
		}
 | 
			
		||||
		poco_trace(MyLogger, "Scheduler done.");
 | 
			
		||||
@@ -448,4 +452,16 @@ namespace OpenWifi {
 | 
			
		||||
		poco_warning(Logger(), fmt::format("{}: Failed to send command. ID: {}", UUID, RPC_ID));
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool CommandManager::FireAndForget(const std::string &SerialNumber, const std::string &Method, const Poco::JSON::Object &Params) {
 | 
			
		||||
		Poco::JSON::Object CompleteRPC;
 | 
			
		||||
		CompleteRPC.set(uCentralProtocol::JSONRPC, uCentralProtocol::JSONRPC_VERSION);
 | 
			
		||||
		CompleteRPC.set(uCentralProtocol::ID, 0);
 | 
			
		||||
		CompleteRPC.set(uCentralProtocol::METHOD, Method);
 | 
			
		||||
		CompleteRPC.set(uCentralProtocol::PARAMS, Params);
 | 
			
		||||
		std::stringstream ToSend;
 | 
			
		||||
		CompleteRPC.stringify(ToSend);
 | 
			
		||||
		poco_debug(Logger(), fmt::format("{}: Fire and forget command {}.", SerialNumber, Method));
 | 
			
		||||
		return AP_WS_Server()->SendFrame(SerialNumber, ToSend.str())>0;
 | 
			
		||||
	}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -12,7 +12,7 @@
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <future>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <shared_mutex>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "Poco/JSON/Object.h"
 | 
			
		||||
@@ -162,8 +162,10 @@ namespace OpenWifi {
 | 
			
		||||
		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::recursive_mutex LocalMutex_;
 | 
			
		||||
		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..
 | 
			
		||||
 
 | 
			
		||||
@@ -11,12 +11,12 @@
 | 
			
		||||
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_))
 | 
			
		||||
@@ -24,25 +24,25 @@ namespace OpenWifi {
 | 
			
		||||
			return Hint->second;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline void Add(uint64_t SerialNumber, uint64_t 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::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
 | 
			
		||||
 
 | 
			
		||||
@@ -11,12 +11,17 @@
 | 
			
		||||
#include "Poco/Util/Application.h"
 | 
			
		||||
#include "Poco/Util/Option.h"
 | 
			
		||||
 | 
			
		||||
#include <framework/ConfigurationValidator.h>
 | 
			
		||||
#include <framework/UI_WebSocketClientServer.h>
 | 
			
		||||
#include <framework/default_device_types.h>
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Server.h"
 | 
			
		||||
#include "CommandManager.h"
 | 
			
		||||
#include "Daemon.h"
 | 
			
		||||
#include "FileUploader.h"
 | 
			
		||||
#include "FindCountry.h"
 | 
			
		||||
#include "OUIServer.h"
 | 
			
		||||
#include "RADIUSSessionTracker.h"
 | 
			
		||||
#include "RADIUS_proxy_server.h"
 | 
			
		||||
#include "RegulatoryInfo.h"
 | 
			
		||||
#include "ScriptManager.h"
 | 
			
		||||
@@ -25,64 +30,47 @@
 | 
			
		||||
#include "StorageArchiver.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "TelemetryStream.h"
 | 
			
		||||
#include "GenericScheduler.h"
 | 
			
		||||
#include "UI_GW_WebSocketNotifications.h"
 | 
			
		||||
#include "VenueBroadcaster.h"
 | 
			
		||||
#include "framework/ConfigurationValidator.h"
 | 
			
		||||
#include "framework/UI_WebSocketClientServer.h"
 | 
			
		||||
#include "AP_WS_ConfigAutoUpgrader.h"
 | 
			
		||||
#include "rttys/RTTYS_server.h"
 | 
			
		||||
#include "firmware_revision_cache.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class Daemon *Daemon::instance() {
 | 
			
		||||
		static Daemon instance(
 | 
			
		||||
			vDAEMON_PROPERTIES_FILENAME, vDAEMON_ROOT_ENV_VAR, vDAEMON_CONFIG_ENV_VAR,
 | 
			
		||||
			vDAEMON_APP_NAME, vDAEMON_BUS_TIMER,
 | 
			
		||||
			SubSystemVec{StorageService(), SerialNumberCache(), ConfigurationValidator(),
 | 
			
		||||
						 UI_WebSocketClientServer(), OUIServer(), FindCountryFromIP(),
 | 
			
		||||
						 CommandManager(), FileUploader(), StorageArchiver(), TelemetryStream(),
 | 
			
		||||
						 RTTYS_server(), RADIUS_proxy_server(), VenueBroadcaster(), ScriptManager(),
 | 
			
		||||
						 SignatureManager(), AP_WS_Server(),
 | 
			
		||||
						 RegulatoryInfo()
 | 
			
		||||
			SubSystemVec{GenericScheduler(), StorageService(), SerialNumberCache(), ConfigurationValidator(),
 | 
			
		||||
				UI_WebSocketClientServer(), OUIServer(), FindCountryFromIP(),
 | 
			
		||||
				CommandManager(), FileUploader(), StorageArchiver(), TelemetryStream(),
 | 
			
		||||
				RTTYS_server(), RADIUS_proxy_server(), VenueBroadcaster(), ScriptManager(),
 | 
			
		||||
				SignatureManager(), AP_WS_Server(),
 | 
			
		||||
				RegulatoryInfo(),
 | 
			
		||||
				RADIUSSessionTracker(),
 | 
			
		||||
			 	AP_WS_ConfigAutoUpgradeAgent(),
 | 
			
		||||
				FirmwareRevisionCache()
 | 
			
		||||
			});
 | 
			
		||||
		return &instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static const std::vector<std::pair<std::string, std::string>> DefaultDeviceTypes{
 | 
			
		||||
		{"cig_wf160d", "AP"},
 | 
			
		||||
		{"cig_wf188", "AP"},
 | 
			
		||||
		{"cig_wf188n", "AP"},
 | 
			
		||||
		{"cig_wf194c", "AP"},
 | 
			
		||||
		{"cig_wf194c4", "AP"},
 | 
			
		||||
		{"edgecore_eap101", "AP"},
 | 
			
		||||
		{"edgecore_eap102", "AP"},
 | 
			
		||||
		{"edgecore_ecs4100-12ph", "AP"},
 | 
			
		||||
		{"edgecore_ecw5211", "AP"},
 | 
			
		||||
		{"edgecore_ecw5410", "AP"},
 | 
			
		||||
		{"edgecore_oap100", "AP"},
 | 
			
		||||
		{"edgecore_spw2ac1200", "SWITCH"},
 | 
			
		||||
		{"edgecore_spw2ac1200-lan-poe", "SWITCH"},
 | 
			
		||||
		{"edgecore_ssw2ac2600", "SWITCH"},
 | 
			
		||||
		{"hfcl_ion4", "AP"},
 | 
			
		||||
		{"indio_um-305ac", "AP"},
 | 
			
		||||
		{"linksys_e8450-ubi", "AP"},
 | 
			
		||||
		{"linksys_ea6350", "AP"},
 | 
			
		||||
		{"linksys_ea6350-v4", "AP"},
 | 
			
		||||
		{"linksys_ea8300", "AP"},
 | 
			
		||||
		{"mikrotik_nand", "AP"},
 | 
			
		||||
		{"tp-link_ec420-g1", "AP"},
 | 
			
		||||
		{"tplink_cpe210_v3", "AP"},
 | 
			
		||||
		{"tplink_cpe510_v3", "AP"},
 | 
			
		||||
		{"tplink_eap225_outdoor_v1", "AP"},
 | 
			
		||||
		{"tplink_ec420", "AP"},
 | 
			
		||||
		{"tplink_ex227", "AP"},
 | 
			
		||||
		{"tplink_ex228", "AP"},
 | 
			
		||||
		{"tplink_ex447", "AP"},
 | 
			
		||||
		{"wallys_dr40x9", "AP"}};
 | 
			
		||||
	static std::string ALBHealthCallback() {
 | 
			
		||||
		uint64_t Connections, AverageConnectionTime, NumberOfConnectingDevices;
 | 
			
		||||
		AP_WS_Server()->AverageDeviceStatistics(Connections, AverageConnectionTime,
 | 
			
		||||
								NumberOfConnectingDevices);
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		os << 	"Connections: " << Connections << std::endl <<
 | 
			
		||||
				"ConnectingDevices: " << NumberOfConnectingDevices << std::endl <<
 | 
			
		||||
				"ConnectionTime: " << AverageConnectionTime << std::endl;
 | 
			
		||||
		return os.str();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) {
 | 
			
		||||
		AutoProvisioning_ = config().getBool("openwifi.autoprovisioning", false);
 | 
			
		||||
		DeviceTypes_ = DefaultDeviceTypes;
 | 
			
		||||
 | 
			
		||||
		DeviceTypes_ = DefaultDeviceTypeList;
 | 
			
		||||
		WebSocketProcessor_ = std::make_unique<GwWebSocketClient>(logger());
 | 
			
		||||
		MicroServiceALBCallback(ALBHealthCallback);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] std::string Daemon::IdentifyDevice(const std::string &Id) const {
 | 
			
		||||
@@ -90,7 +78,7 @@ namespace OpenWifi {
 | 
			
		||||
			if (Id == DeviceType)
 | 
			
		||||
				return Type;
 | 
			
		||||
		}
 | 
			
		||||
		return "AP";
 | 
			
		||||
		return Platforms::AP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void DaemonPostInitialization(Poco::Util::Application &self) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	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()) {
 | 
			
		||||
				Poco::Thread::trySleep(100);
 | 
			
		||||
			}
 | 
			
		||||
@@ -31,7 +30,6 @@ namespace OpenWifi {
 | 
			
		||||
			GeneratingDashboard_ = true;
 | 
			
		||||
			ValidDashboard_ = false;
 | 
			
		||||
			try {
 | 
			
		||||
				// std::cout << "Generating dashboard." << std::endl;
 | 
			
		||||
				poco_information(Logger, "DASHBOARD: Generating a new dashboard.");
 | 
			
		||||
				GWObjects::Dashboard NewData;
 | 
			
		||||
				StorageService()->AnalyzeCommands(NewData.commands);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,12 @@
 | 
			
		||||
 | 
			
		||||
#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 {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,9 +12,7 @@ namespace OpenWifi {
 | 
			
		||||
			Event.set("type", type_);
 | 
			
		||||
			Event.set("timestamp", timestamp_);
 | 
			
		||||
			Event.set("payload", payload_);
 | 
			
		||||
			std::ostringstream OS;
 | 
			
		||||
			Event.stringify(OS);
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, serialNumber_, OS.str());
 | 
			
		||||
			KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, Utils::IntToSerialNumber(serialNumber_), Event);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,23 +4,26 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <Poco/JSON/Object.h>
 | 
			
		||||
#include <framework/KafkaManager.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <Poco/JSON/Object.h>
 | 
			
		||||
#include <RESTObjects/RESTAPI_GWobjects.h>
 | 
			
		||||
#include <framework/KafkaManager.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class GWKafkaEvents {
 | 
			
		||||
	  public:
 | 
			
		||||
		GWKafkaEvents(const std::string &serialNumber, const std::string &type,
 | 
			
		||||
		GWKafkaEvents(std::uint64_t serialNumber, const std::string &type,
 | 
			
		||||
					  std::uint64_t timestamp)
 | 
			
		||||
			: serialNumber_(serialNumber), type_(type), timestamp_(timestamp) {}
 | 
			
		||||
 | 
			
		||||
		inline void SetPayload(Poco::JSON::Object::Ptr payload) { payload_ = std::move(payload); }
 | 
			
		||||
			: serialNumber_(serialNumber), type_(type), timestamp_(timestamp) {
 | 
			
		||||
			payload_ = Poco::SharedPtr<Poco::JSON::Object>(new Poco::JSON::Object);
 | 
			
		||||
		}
 | 
			
		||||
		void Send();
 | 
			
		||||
		[[nodiscard]] inline std::uint64_t Serial() const { return serialNumber_;};
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		std::string serialNumber_;
 | 
			
		||||
	  protected:
 | 
			
		||||
		std::uint64_t serialNumber_;
 | 
			
		||||
		std::string type_;
 | 
			
		||||
		std::uint64_t timestamp_ = 0;
 | 
			
		||||
		Poco::JSON::Object::Ptr payload_;
 | 
			
		||||
@@ -28,17 +31,15 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class DeviceFirmwareChangeKafkaEvent : public GWKafkaEvents {
 | 
			
		||||
	  public:
 | 
			
		||||
		DeviceFirmwareChangeKafkaEvent(const std::string &serialNumber, std::uint64_t timestamp,
 | 
			
		||||
		DeviceFirmwareChangeKafkaEvent(std::uint64_t serialNumber, std::uint64_t timestamp,
 | 
			
		||||
									   const std::string &oldFirmware,
 | 
			
		||||
									   const std::string &newFirmware)
 | 
			
		||||
			: GWKafkaEvents(serialNumber, "unit.firmware_change", timestamp),
 | 
			
		||||
			  oldFirmware_(oldFirmware), newFirmware_(newFirmware) {}
 | 
			
		||||
 | 
			
		||||
		~DeviceFirmwareChangeKafkaEvent() {
 | 
			
		||||
			Poco::JSON::Object::Ptr payload = new Poco::JSON::Object;
 | 
			
		||||
			payload->set("oldFirmware", oldFirmware_);
 | 
			
		||||
			payload->set("newFirmware", newFirmware_);
 | 
			
		||||
			SetPayload(payload);
 | 
			
		||||
			payload_->set("oldFirmware", oldFirmware_);
 | 
			
		||||
			payload_->set("newFirmware", newFirmware_);
 | 
			
		||||
			Send();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -48,25 +49,33 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class DeviceConfigurationChangeKafkaEvent : public GWKafkaEvents {
 | 
			
		||||
	  public:
 | 
			
		||||
		DeviceConfigurationChangeKafkaEvent(const std::string &serialNumber,
 | 
			
		||||
											std::uint64_t timestamp, const std::string config)
 | 
			
		||||
		DeviceConfigurationChangeKafkaEvent(std::uint64_t serialNumber,
 | 
			
		||||
											std::uint64_t timestamp,
 | 
			
		||||
											const Poco::JSON::Object::Ptr config)
 | 
			
		||||
			: GWKafkaEvents(serialNumber, "unit.configuration_change", timestamp), config_(config) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~DeviceConfigurationChangeKafkaEvent() {
 | 
			
		||||
			Poco::JSON::Object::Ptr payload = new Poco::JSON::Object;
 | 
			
		||||
			payload->set("configuration", config_);
 | 
			
		||||
			SetPayload(payload);
 | 
			
		||||
			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:
 | 
			
		||||
		std::string config_;
 | 
			
		||||
		Poco::JSON::Object::Ptr config_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class DeviceBlacklistedKafkaEvent : public GWKafkaEvents {
 | 
			
		||||
	  public:
 | 
			
		||||
		explicit DeviceBlacklistedKafkaEvent(const std::string &serialNumber,
 | 
			
		||||
		explicit DeviceBlacklistedKafkaEvent(std::uint64_t serialNumber,
 | 
			
		||||
											 std::uint64_t timestamp, const std::string &reason,
 | 
			
		||||
											 const std::string &author, std::uint64_t created,
 | 
			
		||||
											 std::string &IP)
 | 
			
		||||
@@ -74,12 +83,10 @@ namespace OpenWifi {
 | 
			
		||||
			  author_(author), created_(created), IP_(IP) {}
 | 
			
		||||
 | 
			
		||||
		~DeviceBlacklistedKafkaEvent() {
 | 
			
		||||
			Poco::JSON::Object::Ptr payload = new Poco::JSON::Object;
 | 
			
		||||
			payload->set("reason", reason_);
 | 
			
		||||
			payload->set("author", author_);
 | 
			
		||||
			payload->set("created", created_);
 | 
			
		||||
			payload->set("ipaddress", IP_);
 | 
			
		||||
			SetPayload(payload);
 | 
			
		||||
			payload_->set("reason", reason_);
 | 
			
		||||
			payload_->set("author", author_);
 | 
			
		||||
			payload_->set("created", created_);
 | 
			
		||||
			payload_->set("ipaddress", IP_);
 | 
			
		||||
			Send();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -89,4 +96,21 @@ namespace OpenWifi {
 | 
			
		||||
		std::string IP_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class DeviceLogKafkaEvent : public GWKafkaEvents {
 | 
			
		||||
	  public:
 | 
			
		||||
		explicit DeviceLogKafkaEvent( const GWObjects::DeviceLog &L)
 | 
			
		||||
			: GWKafkaEvents(Utils::MACToInt(L.SerialNumber), "device_log", L.Recorded),
 | 
			
		||||
			  DL_(L)
 | 
			
		||||
		{
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		~DeviceLogKafkaEvent() {
 | 
			
		||||
			DL_.to_json(*payload_);
 | 
			
		||||
			Send();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		GWObjects::DeviceLog	DL_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								src/GenericScheduler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/GenericScheduler.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-04-19.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "GenericScheduler.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	int GenericScheduler::Start() {
 | 
			
		||||
		poco_information(Logger(),"Starting...");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void GenericScheduler::Stop() {
 | 
			
		||||
		poco_information(Logger(),"Stopping...");
 | 
			
		||||
		poco_information(Logger(),"Stopped...");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										37
									
								
								src/GenericScheduler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/GenericScheduler.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-04-19.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <framework/SubSystemServer.h>
 | 
			
		||||
#include <libs/Scheduler.h>
 | 
			
		||||
#include <Poco/Environment.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class GenericScheduler : public SubSystemServer {
 | 
			
		||||
	  public:
 | 
			
		||||
 | 
			
		||||
		static auto instance() {
 | 
			
		||||
			static auto instance_ = new GenericScheduler;
 | 
			
		||||
			return instance_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int Start() override;
 | 
			
		||||
		void Stop() override;
 | 
			
		||||
 | 
			
		||||
		auto & Scheduler() { return Scheduler_; }
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		GenericScheduler() noexcept
 | 
			
		||||
			: SubSystemServer("Scheduler", "SCHEDULER", "scheduler"),
 | 
			
		||||
			  Scheduler_(Poco::Environment::processorCount()*2) {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		Bosma::Scheduler	Scheduler_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	inline auto GenericScheduler() { return GenericScheduler::instance(); }
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -28,7 +28,7 @@ namespace OpenWifi {
 | 
			
		||||
		bool Recovered = false;
 | 
			
		||||
		Poco::File OuiFile(CurrentOUIFileName_);
 | 
			
		||||
		if (OuiFile.exists()) {
 | 
			
		||||
			std::unique_lock Lock(LocalMutex_);
 | 
			
		||||
			std::lock_guard Lock(LocalMutex_);
 | 
			
		||||
			Recovered = ProcessFile(CurrentOUIFileName_, OUIs_);
 | 
			
		||||
			if (Recovered) {
 | 
			
		||||
				poco_notice(Logger(),
 | 
			
		||||
@@ -150,7 +150,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		OUIMap TmpOUIs;
 | 
			
		||||
		if (GetFile(LatestOUIFileName_) && ProcessFile(LatestOUIFileName_, TmpOUIs)) {
 | 
			
		||||
			std::unique_lock G(LocalMutex_);
 | 
			
		||||
			std::lock_guard G(LocalMutex_);
 | 
			
		||||
			OUIs_ = std::move(TmpOUIs);
 | 
			
		||||
			LastUpdate_ = Utils::Now();
 | 
			
		||||
			Poco::File F1(CurrentOUIFileName_);
 | 
			
		||||
@@ -163,7 +163,7 @@ namespace OpenWifi {
 | 
			
		||||
		} 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);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -173,7 +173,7 @@ namespace OpenWifi {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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())
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <shared_mutex>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
 | 
			
		||||
@@ -32,7 +32,7 @@ namespace OpenWifi {
 | 
			
		||||
		[[nodiscard]] bool ProcessFile(const std::string &FileName, OUIMap &Map);
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		std::shared_mutex LocalMutex_;
 | 
			
		||||
		std::mutex LocalMutex_;
 | 
			
		||||
		uint64_t LastUpdate_ = 0;
 | 
			
		||||
		bool Initialized_ = false;
 | 
			
		||||
		OUIMap OUIs_;
 | 
			
		||||
 
 | 
			
		||||
@@ -1753,7 +1753,6 @@ namespace OpenWifi {
 | 
			
		||||
		nlohmann::json new_ie;
 | 
			
		||||
		nlohmann::json content;
 | 
			
		||||
 | 
			
		||||
		// std::cout << BufferToHex(&data[0],data.size()) << std::endl;
 | 
			
		||||
		uint offset = 0;
 | 
			
		||||
		auto sub_ie = data[offset++];
 | 
			
		||||
		switch (sub_ie) {
 | 
			
		||||
@@ -1788,7 +1787,6 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			nlohmann::json D = nlohmann::json::parse(ofs.str());
 | 
			
		||||
			// std::cout << "Start of parsing wifi" << std::endl;
 | 
			
		||||
			if (D.contains("status")) {
 | 
			
		||||
				auto Status = D["status"];
 | 
			
		||||
				if (Status.contains("scan") && Status["scan"].is_array()) {
 | 
			
		||||
@@ -1803,8 +1801,6 @@ namespace OpenWifi {
 | 
			
		||||
									if (ie.contains("type") && ie.contains("data")) {
 | 
			
		||||
										uint64_t ie_type = ie["type"];
 | 
			
		||||
										std::string ie_data = ie["data"];
 | 
			
		||||
										// std::cout << "TYPE:" << ie_type << "  DATA:" << ie_data
 | 
			
		||||
										// << std::endl;
 | 
			
		||||
										auto data = Base64Decode2Vec(ie_data);
 | 
			
		||||
										if (ie_type == ieee80211_eid::WLAN_EID_COUNTRY) {
 | 
			
		||||
											new_ies.push_back(WFS_WLAN_EID_COUNTRY(data));
 | 
			
		||||
@@ -1858,18 +1854,12 @@ namespace OpenWifi {
 | 
			
		||||
										} else if (ie_type == ieee80211_eid::WLAN_EID_EXTENSION) {
 | 
			
		||||
											new_ies.push_back(WFS_WLAN_EID_EXTENSION(data));
 | 
			
		||||
										} else {
 | 
			
		||||
											// std::cout
 | 
			
		||||
											//	<< "Skipping IE: no parsing available: " << ie_type
 | 
			
		||||
											//	<< std::endl;
 | 
			
		||||
											new_ies.push_back(ie);
 | 
			
		||||
										}
 | 
			
		||||
									} else {
 | 
			
		||||
										// std::cout << "Skipping IE: no data and type" <<
 | 
			
		||||
										// std::endl;
 | 
			
		||||
										new_ies.push_back(ie);
 | 
			
		||||
									}
 | 
			
		||||
								} catch (...) {
 | 
			
		||||
									// std::cout << "Skipping IE: exception" << std::endl;
 | 
			
		||||
									Logger.information(fmt::format("Error parsing IEs"));
 | 
			
		||||
									new_ies.push_back(ie);
 | 
			
		||||
								}
 | 
			
		||||
@@ -1877,7 +1867,6 @@ namespace OpenWifi {
 | 
			
		||||
							scan_entry["ies"] = new_ies;
 | 
			
		||||
							ParsedScan.push_back(scan_entry);
 | 
			
		||||
						} else {
 | 
			
		||||
							// std::cout << "Skipping scan" << std::endl;
 | 
			
		||||
							ParsedScan.push_back(scan_entry);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
@@ -1886,7 +1875,6 @@ namespace OpenWifi {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			Result << to_string(D);
 | 
			
		||||
			// std::cout << "End of parsing wifi" << std::endl;
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
			Logger.log(E);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -8,14 +8,17 @@
 | 
			
		||||
#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() {
 | 
			
		||||
 | 
			
		||||
@@ -24,7 +27,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		Enabled_ = MicroServiceConfigGetBool("radius.proxy.enable", false);
 | 
			
		||||
		if (!Enabled_ && !Config.exists()) {
 | 
			
		||||
			StopRADSECServers();
 | 
			
		||||
			StopRADIUSDestinations();
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -32,227 +35,101 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		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_);
 | 
			
		||||
		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));
 | 
			
		||||
 | 
			
		||||
			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...");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RADIUS_proxy_server::StartRADSECServers() {
 | 
			
		||||
/*	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) {
 | 
			
		||||
			for (const auto &entry : pool.authConfig.servers) {
 | 
			
		||||
				if (entry.radsec) {
 | 
			
		||||
					RADSECservers_[Poco::Net::SocketAddress(entry.ip, 0)] =
 | 
			
		||||
						std::make_unique<RADSEC_server>(*RadiusReactor_, entry);
 | 
			
		||||
				}
 | 
			
		||||
			if(pool.enabled) {
 | 
			
		||||
				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() {
 | 
			
		||||
	void RADIUS_proxy_server::StopRADIUSDestinations() {
 | 
			
		||||
		std::lock_guard G(Mutex_);
 | 
			
		||||
		RADSECservers_.clear();
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
		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,
 | 
			
		||||
	void RADIUS_proxy_server::SendAccountingData( const std::string &serialNumber,
 | 
			
		||||
												 const char *buffer, std::size_t size) {
 | 
			
		||||
 | 
			
		||||
		if (!Continue())
 | 
			
		||||
@@ -261,46 +138,9 @@ namespace OpenWifi {
 | 
			
		||||
		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::acct, 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 &&
 | 
			
		||||
					 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));
 | 
			
		||||
			}
 | 
			
		||||
			std::string Secret;
 | 
			
		||||
			RouteAndSendAccountingPacket(Destination, serialNumber, P, false, Secret);
 | 
			
		||||
			RADIUSSessionTracker()->AddAccountingSession(Destination, serialNumber, P, Secret);
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
			Logger().log(E);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
@@ -322,46 +162,31 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
			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));
 | 
			
		||||
				}
 | 
			
		||||
			} 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;
 | 
			
		||||
				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);
 | 
			
		||||
				}
 | 
			
		||||
				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);
 | 
			
		||||
@@ -379,54 +204,35 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			RADIUS::RadiusPacket P((unsigned char *)buffer, size);
 | 
			
		||||
			auto Destination = P.ExtractProxyStateDestination();
 | 
			
		||||
 | 
			
		||||
			if (Destination.empty()) {
 | 
			
		||||
				Destination = "0.0.0.0:0";
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			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::coa, 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 && 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));
 | 
			
		||||
						 fmt::format("Bad RADIUS AUTH Packet from {}. Dropped.", serialNumber));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RADIUS_proxy_server::ParseServerList(const GWObjects::RadiusProxyServerConfig &Config,
 | 
			
		||||
											  std::vector<Destination> &V4,
 | 
			
		||||
											  std::vector<Destination> &V6, bool setAsDefault) {
 | 
			
		||||
											  std::vector<Destination> &V6, bool setAsDefault,
 | 
			
		||||
											  const std::string &poolProxyIp) {
 | 
			
		||||
		uint64_t TotalV4 = 0, TotalV6 = 0;
 | 
			
		||||
 | 
			
		||||
		for (const auto &server : Config.servers) {
 | 
			
		||||
@@ -449,7 +255,9 @@ namespace OpenWifi {
 | 
			
		||||
						  .methodParameters = Config.methodParameters,
 | 
			
		||||
						  .useAsDefault = setAsDefault,
 | 
			
		||||
						  .useRADSEC = server.radsec,
 | 
			
		||||
						  .realms = server.radsecRealms};
 | 
			
		||||
						  .realms = server.radsecRealms,
 | 
			
		||||
						  .secret = server.secret,
 | 
			
		||||
						  .poolProxyIp = poolProxyIp};
 | 
			
		||||
 | 
			
		||||
			if (setAsDefault && D.useRADSEC)
 | 
			
		||||
				DefaultIsRADSEC_ = true;
 | 
			
		||||
@@ -498,11 +306,11 @@ namespace OpenWifi {
 | 
			
		||||
					for (const auto &pool : RPC.pools) {
 | 
			
		||||
						RadiusPool NewPool;
 | 
			
		||||
						ParseServerList(pool.authConfig, NewPool.AuthV4, NewPool.AuthV6,
 | 
			
		||||
										pool.useByDefault);
 | 
			
		||||
										pool.useByDefault, pool.poolProxyIp);
 | 
			
		||||
						ParseServerList(pool.acctConfig, NewPool.AcctV4, NewPool.AcctV6,
 | 
			
		||||
										pool.useByDefault);
 | 
			
		||||
										pool.useByDefault, pool.poolProxyIp);
 | 
			
		||||
						ParseServerList(pool.coaConfig, NewPool.CoaV4, NewPool.CoaV6,
 | 
			
		||||
										pool.useByDefault);
 | 
			
		||||
										pool.useByDefault, pool.poolProxyIp);
 | 
			
		||||
						Pools_.push_back(NewPool);
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
@@ -521,6 +329,7 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	static bool RealmMatch(const std::string &user_realm, const std::string &realm) {
 | 
			
		||||
		if (realm.find_first_of('*') == std::string::npos)
 | 
			
		||||
			return user_realm == realm;
 | 
			
		||||
@@ -530,7 +339,9 @@ namespace OpenWifi {
 | 
			
		||||
	Poco::Net::SocketAddress
 | 
			
		||||
	RADIUS_proxy_server::DefaultRoute(radius_type rtype,
 | 
			
		||||
									  const Poco::Net::SocketAddress &RequestedAddress,
 | 
			
		||||
									  const RADIUS::RadiusPacket &P, bool &UseRADSEC) {
 | 
			
		||||
									  const RADIUS::RadiusPacket &P, bool &UseRADSEC,
 | 
			
		||||
									  std::string &Secret) {
 | 
			
		||||
 | 
			
		||||
		bool IsV4 = RequestedAddress.family() == Poco::Net::SocketAddress::IPv4;
 | 
			
		||||
 | 
			
		||||
		// find the realm...
 | 
			
		||||
@@ -545,7 +356,6 @@ namespace OpenWifi {
 | 
			
		||||
					if (!server.realms.empty()) {
 | 
			
		||||
						for (const auto &realm : server.realms) {
 | 
			
		||||
							if (RealmMatch(UserRealm, realm)) {
 | 
			
		||||
								std::cout << "Realm match..." << std::endl;
 | 
			
		||||
								UseRADSEC = true;
 | 
			
		||||
								return server.Addr;
 | 
			
		||||
							}
 | 
			
		||||
@@ -565,18 +375,18 @@ namespace OpenWifi {
 | 
			
		||||
		case radius_type::auth: {
 | 
			
		||||
			return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].AuthV4
 | 
			
		||||
									  : Pools_[DefaultPoolIndex_].AuthV6,
 | 
			
		||||
								 RequestedAddress);
 | 
			
		||||
								 RequestedAddress, Secret);
 | 
			
		||||
		}
 | 
			
		||||
		case radius_type::coa: {
 | 
			
		||||
			return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].CoaV4
 | 
			
		||||
									  : Pools_[DefaultPoolIndex_].CoaV6,
 | 
			
		||||
								 RequestedAddress, Secret);
 | 
			
		||||
		}
 | 
			
		||||
		case radius_type::acct:
 | 
			
		||||
		default: {
 | 
			
		||||
			return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].AcctV4
 | 
			
		||||
									  : Pools_[DefaultPoolIndex_].AcctV6,
 | 
			
		||||
								 RequestedAddress);
 | 
			
		||||
		}
 | 
			
		||||
		case radius_type::coa: {
 | 
			
		||||
			return ChooseAddress(IsV4 ? Pools_[DefaultPoolIndex_].CoaV4
 | 
			
		||||
									  : Pools_[DefaultPoolIndex_].CoaV6,
 | 
			
		||||
								 RequestedAddress);
 | 
			
		||||
								 RequestedAddress, Secret);
 | 
			
		||||
		}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -584,8 +394,8 @@ namespace OpenWifi {
 | 
			
		||||
	Poco::Net::SocketAddress
 | 
			
		||||
	RADIUS_proxy_server::Route([[maybe_unused]] radius_type rtype,
 | 
			
		||||
							   const Poco::Net::SocketAddress &RequestedAddress,
 | 
			
		||||
							   const RADIUS::RadiusPacket &P, bool &UseRADSEC) {
 | 
			
		||||
		std::lock_guard G(Mutex_);
 | 
			
		||||
							   const RADIUS::RadiusPacket &P, bool &UseRADSEC,
 | 
			
		||||
							   std::string &Secret) {
 | 
			
		||||
 | 
			
		||||
		if (Pools_.empty()) {
 | 
			
		||||
			UseRADSEC = false;
 | 
			
		||||
@@ -594,39 +404,47 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		bool IsV4 = RequestedAddress.family() == Poco::Net::SocketAddress::IPv4;
 | 
			
		||||
		bool useDefault;
 | 
			
		||||
 | 
			
		||||
		useDefault = IsV4 ? RequestedAddress.host() ==
 | 
			
		||||
								Poco::Net::IPAddress::wildcard(Poco::Net::IPAddress::IPv4)
 | 
			
		||||
						  : RequestedAddress.host() ==
 | 
			
		||||
								Poco::Net::IPAddress::wildcard(Poco::Net::IPAddress::IPv6);
 | 
			
		||||
 | 
			
		||||
		if (useDefault) {
 | 
			
		||||
			return DefaultRoute(rtype, RequestedAddress, P, UseRADSEC);
 | 
			
		||||
			return DefaultRoute(rtype, RequestedAddress, P, UseRADSEC, Secret);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto isAddressInPool = [&](const std::vector<Destination> &D, bool &UseRADSEC) -> bool {
 | 
			
		||||
			for (const auto &entry : D)
 | 
			
		||||
			for (const auto &entry : D) {
 | 
			
		||||
				if (!entry.poolProxyIp.empty() &&
 | 
			
		||||
					entry.poolProxyIp == RequestedAddress.host().toString()) {
 | 
			
		||||
					UseRADSEC = entry.useRADSEC;
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
				if (entry.Addr.host() == RequestedAddress.host()) {
 | 
			
		||||
					UseRADSEC = entry.useRADSEC;
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		for (auto &i : Pools_) {
 | 
			
		||||
		for (auto &pool : Pools_) {
 | 
			
		||||
			// try and match the pool's address to the destination
 | 
			
		||||
			switch (rtype) {
 | 
			
		||||
			case radius_type::coa: {
 | 
			
		||||
				if (isAddressInPool((IsV4 ? i.CoaV4 : i.CoaV6), UseRADSEC)) {
 | 
			
		||||
					return ChooseAddress(IsV4 ? i.CoaV4 : i.CoaV6, RequestedAddress);
 | 
			
		||||
				if (isAddressInPool((IsV4 ? pool.CoaV4 : pool.CoaV6), UseRADSEC)) {
 | 
			
		||||
					return ChooseAddress(IsV4 ? pool.CoaV4 : pool.CoaV6, RequestedAddress, Secret);
 | 
			
		||||
				}
 | 
			
		||||
			} break;
 | 
			
		||||
			case radius_type::auth: {
 | 
			
		||||
				if (isAddressInPool((IsV4 ? i.AuthV4 : i.AuthV6), UseRADSEC)) {
 | 
			
		||||
					return ChooseAddress(IsV4 ? i.AuthV4 : i.AuthV6, RequestedAddress);
 | 
			
		||||
				if (isAddressInPool((IsV4 ? pool.AuthV4 : pool.AuthV6), UseRADSEC)) {
 | 
			
		||||
					return ChooseAddress(IsV4 ? pool.AuthV4 : pool.AuthV6, RequestedAddress, Secret);
 | 
			
		||||
				}
 | 
			
		||||
			} break;
 | 
			
		||||
			case radius_type::acct: {
 | 
			
		||||
				if (isAddressInPool((IsV4 ? i.AcctV4 : i.AcctV6), UseRADSEC)) {
 | 
			
		||||
					return ChooseAddress(IsV4 ? i.AcctV4 : i.AcctV6, RequestedAddress);
 | 
			
		||||
				if (isAddressInPool((IsV4 ? pool.AcctV4 : pool.AcctV6), UseRADSEC)) {
 | 
			
		||||
					return ChooseAddress(IsV4 ? pool.AcctV4 : pool.AcctV6, RequestedAddress, Secret);
 | 
			
		||||
				}
 | 
			
		||||
			} break;
 | 
			
		||||
			}
 | 
			
		||||
@@ -638,10 +456,13 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	Poco::Net::SocketAddress
 | 
			
		||||
	RADIUS_proxy_server::ChooseAddress(std::vector<Destination> &Pool,
 | 
			
		||||
									   const Poco::Net::SocketAddress &OriginalAddress) {
 | 
			
		||||
									   const Poco::Net::SocketAddress &OriginalAddress,
 | 
			
		||||
									   std::string &Secret) {
 | 
			
		||||
 | 
			
		||||
		if (Pool.size() == 1) {
 | 
			
		||||
			return Pool[0].Addr;
 | 
			
		||||
			Secret = Pool[0].secret;
 | 
			
		||||
			auto A = Pool[0].Addr;
 | 
			
		||||
			return A;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (Pool[0].strategy == "weighted") {
 | 
			
		||||
@@ -657,6 +478,7 @@ namespace OpenWifi {
 | 
			
		||||
					index = pos;
 | 
			
		||||
					cur_state = i.state;
 | 
			
		||||
					found = true;
 | 
			
		||||
					Secret = i.secret ;
 | 
			
		||||
				}
 | 
			
		||||
				pos++;
 | 
			
		||||
			}
 | 
			
		||||
@@ -664,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();
 | 
			
		||||
@@ -679,27 +501,30 @@ 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_);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
 | 
			
		||||
#include "RADSEC_server.h"
 | 
			
		||||
#include "RADIUS_Destination.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
@@ -28,23 +28,19 @@ 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 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 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;
 | 
			
		||||
@@ -59,24 +55,20 @@ namespace OpenWifi {
 | 
			
		||||
			bool useAsDefault = false;
 | 
			
		||||
			bool useRADSEC = false;
 | 
			
		||||
			std::vector<std::string> realms;
 | 
			
		||||
			std::string secret;
 | 
			
		||||
			std::string poolProxyIp;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		inline bool Continue() const { return Running_ && Enabled_ && !Pools_.empty(); }
 | 
			
		||||
 | 
			
		||||
	  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_;
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
@@ -101,18 +93,21 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		void ParseConfig();
 | 
			
		||||
		void ResetConfig();
 | 
			
		||||
		Poco::Net::SocketAddress Route(radius_type rtype, const Poco::Net::SocketAddress &A,
 | 
			
		||||
									   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);
 | 
			
		||||
		static Poco::Net::SocketAddress
 | 
			
		||||
							 std::vector<Destination> &V4,
 | 
			
		||||
							 std::vector<Destination> &V6, bool setAsDefault,
 | 
			
		||||
							 const std::string &poolProxyIp);
 | 
			
		||||
/*		static Poco::Net::SocketAddress
 | 
			
		||||
		ChooseAddress(std::vector<Destination> &Pool,
 | 
			
		||||
					  const Poco::Net::SocketAddress &OriginalAddress);
 | 
			
		||||
					  const Poco::Net::SocketAddress &OriginalAddress, std::string &Secret);
 | 
			
		||||
		Poco::Net::SocketAddress DefaultRoute([[maybe_unused]] radius_type rtype,
 | 
			
		||||
											  const Poco::Net::SocketAddress &RequestedAddress,
 | 
			
		||||
											  const RADIUS::RadiusPacket &P, bool &UseRADSEC);
 | 
			
		||||
	};
 | 
			
		||||
											  const RADIUS::RadiusPacket &P, bool &UseRADSEC,
 | 
			
		||||
											  std::string &Secret);
 | 
			
		||||
*/	};
 | 
			
		||||
 | 
			
		||||
	inline auto RADIUS_proxy_server() { return RADIUS_proxy_server::instance(); }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,304 +0,0 @@
 | 
			
		||||
//
 | 
			
		||||
// 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/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"
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	};
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -25,9 +25,23 @@ namespace OpenWifi::RESTAPI_RPC {
 | 
			
		||||
		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)
 | 
			
		||||
			return Handler->ReturnStatus(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
 | 
			
		||||
@@ -40,8 +54,8 @@ namespace OpenWifi::RESTAPI_RPC {
 | 
			
		||||
						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;
 | 
			
		||||
 | 
			
		||||
@@ -167,12 +181,29 @@ namespace OpenWifi::RESTAPI_RPC {
 | 
			
		||||
				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)) {
 | 
			
		||||
				if (Params.has(uCentralProtocol::CONFIG) && Params.isObject(uCentralProtocol::CONFIG)) {
 | 
			
		||||
					auto Config = Params.get(uCentralProtocol::CONFIG)
 | 
			
		||||
									  .extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
					DeviceConfigurationChangeKafkaEvent KEvent(
 | 
			
		||||
						Cmd.SerialNumber, Utils::Now(),
 | 
			
		||||
						Params.get(uCentralProtocol::CONFIG).toString());
 | 
			
		||||
						Utils::SerialNumberToInt(Cmd.SerialNumber), Utils::Now(),
 | 
			
		||||
						Config);
 | 
			
		||||
						
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ namespace OpenWifi {
 | 
			
		||||
		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);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -56,17 +56,27 @@ namespace OpenWifi {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (DefConfig.Models.empty()) {
 | 
			
		||||
		if (DefConfig.models.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::ModelIDListCannotBeEmpty);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::vector<std::string> Error;
 | 
			
		||||
		if (!ValidateUCentralConfiguration(DefConfig.Configuration, Error,
 | 
			
		||||
										   GetBoolParameter("strict", false))) {
 | 
			
		||||
			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();
 | 
			
		||||
		}
 | 
			
		||||
@@ -88,19 +98,31 @@ namespace OpenWifi {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!NewConfig.Configuration.empty()) {
 | 
			
		||||
			std::vector<std::string> Error;
 | 
			
		||||
			if (!ValidateUCentralConfiguration(NewConfig.Configuration, Error,
 | 
			
		||||
											   GetBoolParameter("strict", false))) {
 | 
			
		||||
				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(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;
 | 
			
		||||
			Existing.models = NewConfig.models;
 | 
			
		||||
 | 
			
		||||
		if (StorageService()->UpdateDefaultConfiguration(Name, Existing)) {
 | 
			
		||||
			GWObjects::DefaultConfiguration ModifiedConfig;
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "Poco/Array.h"
 | 
			
		||||
#include "Poco/JSON/Stringifier.h"
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_default_configurations.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								src/RESTAPI/RESTAPI_default_firmware.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/RESTAPI/RESTAPI_default_firmware.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
//
 | 
			
		||||
//	License type: BSD 3-Clause License
 | 
			
		||||
//	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-07-11.
 | 
			
		||||
//	Arilia Wireless Inc.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI/RESTAPI_default_firmware.h"
 | 
			
		||||
 | 
			
		||||
#include "RESTObjects/RESTAPI_GWobjects.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/orm.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
#include "framework/utils.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_default_firmware::DoGet() {
 | 
			
		||||
		std::string deviceType = ORM::Escape(GetBinding(RESTAPI::Protocol::DEVICETYPE, ""));
 | 
			
		||||
		GWObjects::DefaultFirmware Firmware;
 | 
			
		||||
		if (StorageService()->GetDefaultFirmware(deviceType, Firmware)) {
 | 
			
		||||
			return Object(Firmware);
 | 
			
		||||
		}
 | 
			
		||||
		NotFound();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_default_firmware::DoDelete() {
 | 
			
		||||
		std::string deviceType = ORM::Escape(GetBinding(RESTAPI::Protocol::DEVICETYPE, ""));
 | 
			
		||||
		if (deviceType.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (StorageService()->DeleteDefaultFirmware(deviceType)) {
 | 
			
		||||
			return OK();
 | 
			
		||||
		}
 | 
			
		||||
		BadRequest(RESTAPI::Errors::CouldNotBeDeleted);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_default_firmware::DoPost() {
 | 
			
		||||
		std::string deviceType = GetBinding(RESTAPI::Protocol::DEVICETYPE, "");
 | 
			
		||||
 | 
			
		||||
		if (deviceType.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (StorageService()->DefaultFirmwareAlreadyExists(deviceType)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::DefFirmwareNameExists);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		GWObjects::DefaultFirmware Firmware;
 | 
			
		||||
		if (!Firmware.from_json(Obj)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(Firmware.uri.empty() || Firmware.revision.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			Poco::URI FirmwareURI(Firmware.uri);
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidURI);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Firmware.Created = Firmware.LastModified = Utils::Now();
 | 
			
		||||
		if (StorageService()->CreateDefaultFirmware(Firmware)) {
 | 
			
		||||
			GWObjects::DefaultFirmware ModifiedFirmware;
 | 
			
		||||
			StorageService()->GetDefaultFirmware(deviceType, ModifiedFirmware);
 | 
			
		||||
			return Object(ModifiedFirmware);
 | 
			
		||||
		}
 | 
			
		||||
		BadRequest(RESTAPI::Errors::RecordNotCreated);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_default_firmware::DoPut() {
 | 
			
		||||
		std::string deviceType = GetBinding(RESTAPI::Protocol::DEVICETYPE, "");
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		GWObjects::DefaultFirmware NewFirmware;
 | 
			
		||||
		if (!NewFirmware.from_json(Obj)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::DefaultFirmware Existing;
 | 
			
		||||
		if (!StorageService()->GetDefaultFirmware(deviceType, Existing)) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Existing.LastModified = Utils::Now();
 | 
			
		||||
		AssignIfPresent(Obj, "description", Existing.Description);
 | 
			
		||||
		AssignIfPresent(Obj, "imageCreationDate", Existing.imageCreationDate);
 | 
			
		||||
		AssignIfPresent(Obj, "revision", Existing.revision);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		if (Obj->has("uri")) {
 | 
			
		||||
			if(NewFirmware.uri.empty()) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
			}
 | 
			
		||||
			try {
 | 
			
		||||
				Poco::URI FirmwareURI(NewFirmware.uri);
 | 
			
		||||
			} catch (...) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::InvalidURI);
 | 
			
		||||
			}
 | 
			
		||||
			Existing.uri = NewFirmware.uri;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		if (StorageService()->UpdateDefaultFirmware(Existing)) {
 | 
			
		||||
			GWObjects::DefaultFirmware ModifiedFirmware;
 | 
			
		||||
 | 
			
		||||
			StorageService()->GetDefaultFirmware(deviceType, ModifiedFirmware);
 | 
			
		||||
			return Object(ModifiedFirmware);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		BadRequest(RESTAPI::Errors::RecordNotUpdated);
 | 
			
		||||
	}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										30
									
								
								src/RESTAPI/RESTAPI_default_firmware.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/RESTAPI/RESTAPI_default_firmware.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-07-11.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_default_firmware : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
		RESTAPI_default_firmware(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
									  RESTAPI_GenericServerAccounting &Server,
 | 
			
		||||
									  uint64_t TransactionId, bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_POST,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
							 Server, TransactionId, Internal) {}
 | 
			
		||||
		static auto PathName() {
 | 
			
		||||
			return std::list<std::string>{"/api/v1/default_firmware/{deviceType}"};
 | 
			
		||||
		}
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
		void DoPost() final;
 | 
			
		||||
		void DoPut() final;
 | 
			
		||||
	};
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										25
									
								
								src/RESTAPI/RESTAPI_default_firmwares.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/RESTAPI/RESTAPI_default_firmwares.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-07-11.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_default_firmwares.h"
 | 
			
		||||
 | 
			
		||||
#include "Poco/Array.h"
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_default_firmwares.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_default_firmwares::DoGet() {
 | 
			
		||||
 | 
			
		||||
		if (QB_.CountOnly) {
 | 
			
		||||
			auto Count = StorageService()->GetDefaultFirmwaresCount();
 | 
			
		||||
			return ReturnCountOnly(Count);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::vector<GWObjects::DefaultFirmware> Firmwares;
 | 
			
		||||
		StorageService()->GetDefaultFirmwares(QB_.Offset, QB_.Limit, Firmwares);
 | 
			
		||||
		return Object(RESTAPI::Protocol::FIRMWARES, Firmwares);
 | 
			
		||||
	}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										25
									
								
								src/RESTAPI/RESTAPI_default_firmwares.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/RESTAPI/RESTAPI_default_firmwares.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-07-11.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_default_firmwares : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
		RESTAPI_default_firmwares(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
									   RESTAPI_GenericServerAccounting &Server,
 | 
			
		||||
									   uint64_t TransactionId, bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
							 Server, TransactionId, Internal){};
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/default_firmwares"}; }
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -87,7 +87,7 @@ namespace OpenWifi {
 | 
			
		||||
			poco_debug(
 | 
			
		||||
				Logger_,
 | 
			
		||||
				fmt::format(
 | 
			
		||||
					"Command rtty TID={} can proceed. Identified as {} and RPCID as {}. thr_id={}",
 | 
			
		||||
					"Command RTTY TID={} can proceed. Identified as {} and RPCID as {}. thr_id={}",
 | 
			
		||||
					TransactionId_, UUID, RPC, Poco::Thread::current()->id()));
 | 
			
		||||
			return Rtty(UUID, RPC, 60000ms, Restrictions);
 | 
			
		||||
		};
 | 
			
		||||
@@ -163,8 +163,16 @@ namespace OpenWifi {
 | 
			
		||||
		{APCommands::Commands::telemetry, false, true, &RESTAPI_device_commandHandler::Telemetry,
 | 
			
		||||
		 30000ms},
 | 
			
		||||
		{APCommands::Commands::ping, false, true, &RESTAPI_device_commandHandler::Ping, 60000ms},
 | 
			
		||||
		{APCommands::Commands::script, false, true, &RESTAPI_device_commandHandler::Script,
 | 
			
		||||
		 300000ms}};
 | 
			
		||||
		{APCommands::Commands::rrm, false, true, &RESTAPI_device_commandHandler::RRM, 60000ms},
 | 
			
		||||
		{APCommands::Commands::certupdate, false, true, &RESTAPI_device_commandHandler::CertUpdate, 60000ms},
 | 
			
		||||
		{APCommands::Commands::transfer, false, true, &RESTAPI_device_commandHandler::Transfer, 60000ms},
 | 
			
		||||
		{APCommands::Commands::script, false, true, &RESTAPI_device_commandHandler::Script, 60000ms},
 | 
			
		||||
		{APCommands::Commands::powercycle, false, true, &RESTAPI_device_commandHandler::PowerCycle, 60000ms},
 | 
			
		||||
		{APCommands::Commands::fixedconfig, false, true, &RESTAPI_device_commandHandler::FixedConfig, 120000ms},
 | 
			
		||||
		{APCommands::Commands::cablediagnostics, false, true, &RESTAPI_device_commandHandler::CableDiagnostics, 120000ms},
 | 
			
		||||
		{APCommands::Commands::reenroll, false, true, &RESTAPI_device_commandHandler::ReEnroll, 120000ms},
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::DoPost() {
 | 
			
		||||
		if (!ValidateParameters()) {
 | 
			
		||||
@@ -309,7 +317,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		if (AP_WS_Server()->GetState(SerialNumber_, State)) {
 | 
			
		||||
			Poco::JSON::Object RetObject;
 | 
			
		||||
			State.to_json(RetObject);
 | 
			
		||||
			State.to_json(SerialNumber_, RetObject);
 | 
			
		||||
			return ReturnObject(RetObject);
 | 
			
		||||
		} else {
 | 
			
		||||
			Poco::JSON::Object RetObject;
 | 
			
		||||
@@ -406,6 +414,7 @@ namespace OpenWifi {
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("PING({},{}): TID={} user={} serial={}", CMD_UUID, CMD_RPC,
 | 
			
		||||
										TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		if (Obj->has(RESTAPI::Protocol::SERIALNUMBER)) {
 | 
			
		||||
			auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
 | 
			
		||||
@@ -459,6 +468,14 @@ namespace OpenWifi {
 | 
			
		||||
		return BadRequest(RESTAPI::Errors::MissingSerialNumber);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool RESTAPI_device_commandHandler::IsDeviceSimulated(std::string &Serial) {
 | 
			
		||||
		GWObjects::Device	Device;
 | 
			
		||||
		if(StorageService()->GetDevice(Serial,Device)) {
 | 
			
		||||
			return Device.simulated;
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::CallCanceled(const char *Cmd, const std::string &UUID,
 | 
			
		||||
													 uint64_t RPC,
 | 
			
		||||
													 const OpenWifi::RESTAPI::Errors::msg &Err) {
 | 
			
		||||
@@ -474,6 +491,10 @@ namespace OpenWifi {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("SCRIPT({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		GWObjects::ScriptRequest SCR;
 | 
			
		||||
		if (!SCR.from_json(Obj)) {
 | 
			
		||||
@@ -637,13 +658,18 @@ namespace OpenWifi {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GWObjects::Device DeviceInfo;
 | 
			
		||||
			if (!StorageService()->GetDevice(SerialNumber_, DeviceInfo)) {
 | 
			
		||||
				return NotFound();
 | 
			
		||||
			}
 | 
			
		||||
			auto Configuration =
 | 
			
		||||
				GetS(RESTAPI::Protocol::CONFIGURATION, Obj, uCentralProtocol::EMPTY_JSON_DOC);
 | 
			
		||||
			std::vector<std::string> Error;
 | 
			
		||||
			if (!ValidateUCentralConfiguration(Configuration, Error,
 | 
			
		||||
			std::string Error;
 | 
			
		||||
			if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(DeviceInfo.DeviceType),
 | 
			
		||||
											   Configuration, Error,
 | 
			
		||||
											   GetBoolParameter("strict", false))) {
 | 
			
		||||
				CallCanceled("CONFIGURE", CMD_UUID, CMD_RPC, RESTAPI::Errors::ConfigBlockInvalid);
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::ConfigBlockInvalid, Error);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			auto When = GetWhen(Obj);
 | 
			
		||||
@@ -669,9 +695,31 @@ namespace OpenWifi {
 | 
			
		||||
				Params.stringify(ParamStream);
 | 
			
		||||
				Cmd.Details = ParamStream.str();
 | 
			
		||||
 | 
			
		||||
				// retrieve capabilities and encode/compress parameters, if required
 | 
			
		||||
				Poco::JSON::Object ConfigParams = Params;
 | 
			
		||||
				GWObjects::Capabilities Caps;
 | 
			
		||||
				if (StorageService()->GetDeviceCapabilities(SerialNumber_, Caps)) {
 | 
			
		||||
					Poco::JSON::Object CapsJson;
 | 
			
		||||
					Caps.to_json(CapsJson);
 | 
			
		||||
					auto DeviceCaps = CapsJson.getObject(uCentralProtocol::CAPABILITIES);
 | 
			
		||||
					if (DeviceCaps->has("compress_cmd") && DeviceCaps->get("compress_cmd")) {
 | 
			
		||||
						// compressed command capability present and it is set, compress parameters
 | 
			
		||||
						Poco::JSON::Object CompressedParams;
 | 
			
		||||
						std::string CompressedBase64Data;
 | 
			
		||||
						std::uint64_t UncompressedDataLen = ParamStream.str().length();
 | 
			
		||||
						if (Utils::CompressAndEncodeBase64(ParamStream.str(), CompressedBase64Data)) {
 | 
			
		||||
							// set compressed, base 64 encoded data and length of uncompressed data
 | 
			
		||||
							CompressedParams.set(uCentralProtocol::COMPRESS_64, CompressedBase64Data);
 | 
			
		||||
							CompressedParams.set(uCentralProtocol::COMPRESS_SZ, UncompressedDataLen);
 | 
			
		||||
							ConfigParams = CompressedParams;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
				// AP_WS_Server()->SetPendingUUID(SerialNumber_, NewUUID);
 | 
			
		||||
				RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::configure, true,
 | 
			
		||||
												   Cmd, Params, *Request, *Response, timeout,
 | 
			
		||||
												   Cmd, ConfigParams, *Request, *Response, timeout,
 | 
			
		||||
												   nullptr, this, Logger_);
 | 
			
		||||
 | 
			
		||||
				if(!Cmd.Executed) {
 | 
			
		||||
@@ -899,6 +947,10 @@ namespace OpenWifi {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("TRACE({},{}): TID={} user={} serial={}", CMD_UUID, CMD_RPC,
 | 
			
		||||
										TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
 | 
			
		||||
		if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) &&
 | 
			
		||||
@@ -956,6 +1008,12 @@ namespace OpenWifi {
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("WIFISCAN({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
 | 
			
		||||
		auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
 | 
			
		||||
@@ -1011,6 +1069,10 @@ namespace OpenWifi {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("EVENT-QUEUE({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) && Obj->isArray(RESTAPI::Protocol::TYPES)) {
 | 
			
		||||
 | 
			
		||||
@@ -1055,6 +1117,10 @@ namespace OpenWifi {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("FORCE-REQUEST({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
		if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) && Obj->has(uCentralProtocol::MESSAGE)) {
 | 
			
		||||
 | 
			
		||||
@@ -1095,15 +1161,16 @@ namespace OpenWifi {
 | 
			
		||||
		BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#define DBGLINE                                                                                    \
 | 
			
		||||
	{ std::cout << __LINE__ << std::endl; }
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::Rtty(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("RTTY({},{}): TID={} user={} serial={}", CMD_UUID, CMD_RPC,
 | 
			
		||||
										TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!Restrictions.developer && Restrictions.rtty) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::DeviceIsRestricted);
 | 
			
		||||
		}
 | 
			
		||||
@@ -1112,6 +1179,11 @@ namespace OpenWifi {
 | 
			
		||||
			GWObjects::Device Device;
 | 
			
		||||
 | 
			
		||||
			if (StorageService()->GetDevice(SerialNumber_, Device)) {
 | 
			
		||||
 | 
			
		||||
				if(Device.simulated) {
 | 
			
		||||
					return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				static std::uint64_t rtty_sid = 0;
 | 
			
		||||
				rtty_sid += std::rand();
 | 
			
		||||
				GWObjects::RttySessionDetails Rtty{
 | 
			
		||||
@@ -1129,7 +1201,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
				if (RTTYS_server()->UseInternal()) {
 | 
			
		||||
					std::uint64_t SN = Utils::SerialNumberToInt(SerialNumber_);
 | 
			
		||||
					bool mTLS = AP_WS_Server()->DeviceRequiresSecureRtty(SN);
 | 
			
		||||
					bool mTLS = AP_WS_Server()->DeviceRequiresSecureRTTY(SN);
 | 
			
		||||
					auto Hash =  Utils::ComputeHash(UserInfo_.webtoken.refresh_token_, Utils::Now());
 | 
			
		||||
					Rtty.Token = Hash.substr(0, RTTY_DEVICE_TOKEN_LENGTH);
 | 
			
		||||
					if (!RTTYS_server()->CreateEndPoint(Rtty.ConnectionId, Rtty.Token, Requester(),
 | 
			
		||||
@@ -1185,6 +1257,10 @@ namespace OpenWifi {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("TELEMETRY({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Obj = ParsedBody_;
 | 
			
		||||
 | 
			
		||||
		if (Obj->has(RESTAPI::Protocol::SERIALNUMBER) && Obj->has(RESTAPI::Protocol::INTERVAL) &&
 | 
			
		||||
@@ -1298,4 +1374,323 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
		return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::RRM(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC,
 | 
			
		||||
		[[maybe_unused]] std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
 | 
			
		||||
		poco_debug(Logger_, fmt::format("RRM({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(!ParsedBody_->has("actions") || !ParsedBody_->isArray("actions")) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const auto &Actions = *ParsedBody_->getArray("actions");
 | 
			
		||||
		//	perform some validation on the commands.
 | 
			
		||||
		for(const auto &action:Actions) {
 | 
			
		||||
			auto ActionDetails = action.extract<Poco::JSON::Object::Ptr>();
 | 
			
		||||
			if(!ActionDetails->has("action")) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
			}
 | 
			
		||||
			auto ActionStr = ActionDetails->get("action").toString();
 | 
			
		||||
			if(	ActionStr != "kick"
 | 
			
		||||
				&& ActionStr != "channel_switch"
 | 
			
		||||
				&& ActionStr != "tx_power"
 | 
			
		||||
				&& ActionStr != "beacon_request"
 | 
			
		||||
				&& ActionStr != "bss_transition"
 | 
			
		||||
				&& ActionStr != "neighbors" ) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::InvalidRRMAction);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Poco::JSON::Object Params;
 | 
			
		||||
		Params.set(uCentralProtocol::SERIAL, SerialNumber_);
 | 
			
		||||
		Params.set(uCentralProtocol::ACTIONS, Actions);
 | 
			
		||||
 | 
			
		||||
		GWObjects::CommandDetails Cmd;
 | 
			
		||||
		Cmd.SerialNumber = SerialNumber_;
 | 
			
		||||
		Cmd.SubmittedBy = Requester();
 | 
			
		||||
		Cmd.UUID = CMD_UUID;
 | 
			
		||||
		Cmd.Command = uCentralProtocol::RRM;
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		Params.stringify(os);
 | 
			
		||||
		Cmd.Details = os.str();
 | 
			
		||||
		Cmd.RunAt = 0;
 | 
			
		||||
		Cmd.ErrorCode = 0;
 | 
			
		||||
		Cmd.WaitingForFile = 0;
 | 
			
		||||
		Cmd.Status= "completed";
 | 
			
		||||
		if(CommandManager()->FireAndForget(SerialNumber_, uCentralProtocol::RRM, Params)) {
 | 
			
		||||
			Cmd.Status= "completed";
 | 
			
		||||
			StorageService()->AddCommand(SerialNumber_, Cmd,
 | 
			
		||||
										 Storage::CommandExecutionType::COMMAND_COMPLETED);
 | 
			
		||||
			return OK();
 | 
			
		||||
		}
 | 
			
		||||
		Cmd.Status= "failed";	//	should never happen
 | 
			
		||||
		StorageService()->AddCommand(SerialNumber_, Cmd,
 | 
			
		||||
									 Storage::CommandExecutionType::COMMAND_COMPLETED);
 | 
			
		||||
		return BadRequest(RESTAPI::Errors::CouldNotPerformCommand);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::Transfer(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC,
 | 
			
		||||
		[[maybe_unused]] std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
 | 
			
		||||
		if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		poco_debug(Logger_, fmt::format("TRANSFER({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::DeviceTransferRequest	TR;
 | 
			
		||||
		if(!TR.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::CommandDetails Cmd;
 | 
			
		||||
		Cmd.SerialNumber = SerialNumber_;
 | 
			
		||||
		Cmd.SubmittedBy = Requester();
 | 
			
		||||
		Cmd.UUID = CMD_UUID;
 | 
			
		||||
		Cmd.Command = uCentralProtocol::TRANSFER;
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		ParsedBody_->stringify(os);
 | 
			
		||||
		Cmd.Details = os.str();
 | 
			
		||||
		Cmd.RunAt = 0;
 | 
			
		||||
		Cmd.ErrorCode = 0;
 | 
			
		||||
		Cmd.WaitingForFile = 0;
 | 
			
		||||
 | 
			
		||||
		return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::transfer, false, Cmd,
 | 
			
		||||
										   *ParsedBody_, *Request, *Response, timeout, nullptr, this,
 | 
			
		||||
										   Logger_);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::CertUpdate(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC,
 | 
			
		||||
		[[maybe_unused]] std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
 | 
			
		||||
		poco_debug(Logger_, fmt::format("CERTUPDATE({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::DeviceCertificateUpdateRequest	CR;
 | 
			
		||||
		if(!CR.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::DeviceTransferRequest	TR;
 | 
			
		||||
		if(!TR.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::CommandDetails Cmd;
 | 
			
		||||
		Cmd.SerialNumber = SerialNumber_;
 | 
			
		||||
		Cmd.SubmittedBy = Requester();
 | 
			
		||||
		Cmd.UUID = CMD_UUID;
 | 
			
		||||
		Cmd.Command = uCentralProtocol::CERTUPDATE;
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		ParsedBody_->stringify(os);
 | 
			
		||||
		Cmd.Details = os.str();
 | 
			
		||||
		Cmd.RunAt = 0;
 | 
			
		||||
		Cmd.ErrorCode = 0;
 | 
			
		||||
		Cmd.WaitingForFile = 0;
 | 
			
		||||
 | 
			
		||||
		return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::certupdate, false, Cmd,
 | 
			
		||||
										   *ParsedBody_, *Request, *Response, timeout, nullptr, this,
 | 
			
		||||
										   Logger_);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::PowerCycle(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC,
 | 
			
		||||
		[[maybe_unused]] std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
 | 
			
		||||
		if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		poco_debug(Logger_, fmt::format("POWERCYCLE({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			CallCanceled("RRM", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::PowerCycleRequest	PR;
 | 
			
		||||
		if(!PR.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::CommandDetails Cmd;
 | 
			
		||||
		Cmd.SerialNumber = SerialNumber_;
 | 
			
		||||
		Cmd.SubmittedBy = Requester();
 | 
			
		||||
		Cmd.UUID = CMD_UUID;
 | 
			
		||||
		Cmd.Command = uCentralProtocol::POWERCYCLE;
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		ParsedBody_->stringify(os);
 | 
			
		||||
		Cmd.Details = os.str();
 | 
			
		||||
		Cmd.RunAt = PR.when;
 | 
			
		||||
		Cmd.ErrorCode = 0;
 | 
			
		||||
		Cmd.WaitingForFile = 0;
 | 
			
		||||
 | 
			
		||||
		return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::powercycle, false, Cmd,
 | 
			
		||||
										   *ParsedBody_, *Request, *Response, timeout, nullptr, this,
 | 
			
		||||
										   Logger_);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// `fixedconfig` command is used set country propery on AP
 | 
			
		||||
	// This handler uses `fixedconfig` command definitions
 | 
			
		||||
	void RESTAPI_device_commandHandler::FixedConfig(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
		poco_debug(Logger_, fmt::format("FIXEDCONFIG({},{}): TID={} user={} serial={}", CMD_UUID, CMD_RPC,
 | 
			
		||||
										TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
		// do not allow `fixedconfig` command for simulated devices
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			CallCanceled("FIXEDCONFIG", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// setup and validate fixedconfig object
 | 
			
		||||
		GWObjects::FixedConfig fixed_config;
 | 
			
		||||
		if(!fixed_config.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// setup command message
 | 
			
		||||
		GWObjects::CommandDetails Cmd;
 | 
			
		||||
		Cmd.SerialNumber = SerialNumber_;
 | 
			
		||||
		Cmd.SubmittedBy = Requester();
 | 
			
		||||
		Cmd.UUID = CMD_UUID;
 | 
			
		||||
		Cmd.Command = uCentralProtocol::FIXEDCONFIG;
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		ParsedBody_->stringify(os);
 | 
			
		||||
		Cmd.Details = os.str();
 | 
			
		||||
		Cmd.RunAt = 0;
 | 
			
		||||
		Cmd.ErrorCode = 0;
 | 
			
		||||
		Cmd.WaitingForFile = 0;
 | 
			
		||||
 | 
			
		||||
		// send fixedconfig command to device and return status
 | 
			
		||||
		return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::fixedconfig, false, Cmd,
 | 
			
		||||
										   *ParsedBody_, *Request, *Response, timeout, nullptr, this,
 | 
			
		||||
										   Logger_);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::CableDiagnostics(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC,
 | 
			
		||||
		[[maybe_unused]] std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
 | 
			
		||||
		if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) {
 | 
			
		||||
			CallCanceled("CABLEDIAGNOSTICS", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		poco_debug(Logger_, fmt::format("CABLEDIAGNOSTICS({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			CallCanceled("CABLEDIAGNOSTICS", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::CableDiagnostics	PR;
 | 
			
		||||
		if(!PR.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::CommandDetails Cmd;
 | 
			
		||||
		Cmd.SerialNumber = SerialNumber_;
 | 
			
		||||
		Cmd.SubmittedBy = Requester();
 | 
			
		||||
		Cmd.UUID = CMD_UUID;
 | 
			
		||||
		Cmd.Command = uCentralProtocol::CABLEDIAGNOSTICS;
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		ParsedBody_->stringify(os);
 | 
			
		||||
		Cmd.Details = os.str();
 | 
			
		||||
		Cmd.RunAt = PR.when;
 | 
			
		||||
		Cmd.ErrorCode = 0;
 | 
			
		||||
		Cmd.WaitingForFile = 0;
 | 
			
		||||
 | 
			
		||||
		return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::cablediagnostics, false, Cmd,
 | 
			
		||||
										   *ParsedBody_, *Request, *Response, timeout, nullptr, this,
 | 
			
		||||
										   Logger_);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_device_commandHandler::ReEnroll(
 | 
			
		||||
		const std::string &CMD_UUID, uint64_t CMD_RPC,
 | 
			
		||||
		[[maybe_unused]] std::chrono::milliseconds timeout,
 | 
			
		||||
		[[maybe_unused]] const GWObjects::DeviceRestrictions &Restrictions) {
 | 
			
		||||
 | 
			
		||||
		if(UserInfo_.userinfo.userRole != SecurityObjects::ROOT &&
 | 
			
		||||
			UserInfo_.userinfo.userRole != SecurityObjects::ADMIN) {
 | 
			
		||||
			CallCanceled("REENROLL", CMD_UUID, CMD_RPC, RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		poco_debug(Logger_, fmt::format("REENROLL({},{}): TID={} user={} serial={}", CMD_UUID,
 | 
			
		||||
										CMD_RPC, TransactionId_, Requester(), SerialNumber_));
 | 
			
		||||
 | 
			
		||||
		if(IsDeviceSimulated(SerialNumber_)) {
 | 
			
		||||
			CallCanceled("REENROLL", CMD_UUID, CMD_RPC, RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SimulatedDeviceNotSupported);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::ReEnroll PR;
 | 
			
		||||
		if(!PR.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::CommandDetails Cmd;
 | 
			
		||||
		Cmd.SerialNumber = SerialNumber_;
 | 
			
		||||
		Cmd.SubmittedBy = Requester();
 | 
			
		||||
		Cmd.UUID = CMD_UUID;
 | 
			
		||||
		Cmd.Command = uCentralProtocol::REENROLL;
 | 
			
		||||
		std::ostringstream os;
 | 
			
		||||
		ParsedBody_->stringify(os);
 | 
			
		||||
		Cmd.Details = os.str();
 | 
			
		||||
		Cmd.RunAt = PR.when;
 | 
			
		||||
		Cmd.ErrorCode = 0;
 | 
			
		||||
		Cmd.WaitingForFile = 0;
 | 
			
		||||
 | 
			
		||||
		return RESTAPI_RPC::WaitForCommand(CMD_RPC, APCommands::Commands::reenroll, false, Cmd,
 | 
			
		||||
										   *ParsedBody_, *Request, *Response, timeout, nullptr, this,
 | 
			
		||||
										   Logger_);
 | 
			
		||||
	}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,8 @@ namespace OpenWifi {
 | 
			
		||||
		void GetChecks();
 | 
			
		||||
		void DeleteChecks();
 | 
			
		||||
 | 
			
		||||
		bool IsDeviceSimulated(std::string &Serial);
 | 
			
		||||
 | 
			
		||||
		void Configure(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					   const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void Upgrade(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
@@ -60,6 +62,20 @@ namespace OpenWifi {
 | 
			
		||||
				  const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void Script(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void RRM(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void CertUpdate(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void Transfer(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void PowerCycle(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					  const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void FixedConfig(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					  const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void CableDiagnostics(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					  const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
		void ReEnroll(const std::string &UUID, uint64_t RPC, std::chrono::milliseconds timeout,
 | 
			
		||||
					  const GWObjects::DeviceRestrictions &R);
 | 
			
		||||
 | 
			
		||||
		static auto PathName() {
 | 
			
		||||
			return std::list<std::string>{"/api/v1/device/{serialNumber}/{command}"};
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,8 @@
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_device_helper.h"
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_Server.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_device_handler::DoGet() {
 | 
			
		||||
		std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
 | 
			
		||||
@@ -41,6 +43,10 @@ namespace OpenWifi {
 | 
			
		||||
	void RESTAPI_device_handler::DoDelete() {
 | 
			
		||||
		std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
 | 
			
		||||
 | 
			
		||||
		if(!RESTAPI_utils::IsRootOrAdmin(UserInfo_.userinfo)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!Utils::NormalizeMac(SerialNumber)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingSerialNumber);
 | 
			
		||||
		}
 | 
			
		||||
@@ -76,6 +82,9 @@ namespace OpenWifi {
 | 
			
		||||
			return OK();
 | 
			
		||||
 | 
			
		||||
		} else if (StorageService()->DeleteDevice(SerialNumber)) {
 | 
			
		||||
			if(AP_WS_Server()->Connected(Utils::SerialNumberToInt(SerialNumber))) {
 | 
			
		||||
				AP_WS_Server()->Disconnect(Utils::SerialNumberToInt(SerialNumber));
 | 
			
		||||
			}
 | 
			
		||||
			return OK();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -97,9 +106,10 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
			auto Config = Obj->get("configuration").toString();
 | 
			
		||||
			Poco::JSON::Object Answer;
 | 
			
		||||
			std::vector<std::string> Error;
 | 
			
		||||
			std::string Error;
 | 
			
		||||
			auto DeviceType = Poco::toLower(GetParameter("deviceType", Platforms::AP));
 | 
			
		||||
			auto Res =
 | 
			
		||||
				ValidateUCentralConfiguration(Config, Error, GetBoolParameter("strict", false));
 | 
			
		||||
				ValidateUCentralConfiguration(ConfigurationValidator::GetType(DeviceType),Config, Error, GetBoolParameter("strict", false));
 | 
			
		||||
			Answer.set("valid", Res);
 | 
			
		||||
			if (!Error.empty())
 | 
			
		||||
				Answer.set("error", Error);
 | 
			
		||||
@@ -119,12 +129,13 @@ namespace OpenWifi {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::vector<std::string> Error;
 | 
			
		||||
		std::string Error;
 | 
			
		||||
		if (Device.Configuration.empty() ||
 | 
			
		||||
			(!Device.Configuration.empty() &&
 | 
			
		||||
			 !ValidateUCentralConfiguration(Device.Configuration, Error,
 | 
			
		||||
			 !ValidateUCentralConfiguration(ConfigurationValidator::GetType(Device.DeviceType),
 | 
			
		||||
											Device.Configuration, Error,
 | 
			
		||||
											GetBoolParameter("strict", false)))) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::ConfigBlockInvalid, Error);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (auto &i : Device.Notes) {
 | 
			
		||||
@@ -165,10 +176,11 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!NewDevice.Configuration.empty()) {
 | 
			
		||||
			std::vector<std::string> Error;
 | 
			
		||||
			if (!ValidateUCentralConfiguration(NewDevice.Configuration, Error,
 | 
			
		||||
			std::string Error;
 | 
			
		||||
			if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(Existing.DeviceType),
 | 
			
		||||
											   NewDevice.Configuration, Error,
 | 
			
		||||
											   GetBoolParameter("strict", false))) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::ConfigBlockInvalid, Error);
 | 
			
		||||
			}
 | 
			
		||||
			Config::Config NewConfig(NewDevice.Configuration);
 | 
			
		||||
			uint64_t NewConfigUUID = Utils::Now();
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ namespace OpenWifi {
 | 
			
		||||
		Device.to_json(DeviceInfo);
 | 
			
		||||
		Answer.set("deviceInfo", DeviceInfo);
 | 
			
		||||
		Poco::JSON::Object CSInfo;
 | 
			
		||||
		CS.to_json(CSInfo);
 | 
			
		||||
		CS.to_json(Device.SerialNumber, CSInfo);
 | 
			
		||||
		Answer.set("connectionInfo", CSInfo);
 | 
			
		||||
		Poco::JSON::Object HCInfo;
 | 
			
		||||
		HC.to_json(HCInfo);
 | 
			
		||||
 
 | 
			
		||||
@@ -82,15 +82,24 @@ namespace OpenWifi {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto platform = Poco::toLower(GetParameter("platform", ""));
 | 
			
		||||
		auto serialOnly = GetBoolParameter(RESTAPI::Protocol::SERIALONLY, false);
 | 
			
		||||
		auto deviceWithStatus = GetBoolParameter(RESTAPI::Protocol::DEVICEWITHSTATUS, false);
 | 
			
		||||
		auto completeInfo = GetBoolParameter("completeInfo", false);
 | 
			
		||||
		auto includeProvisioned = GetBoolParameter("includeProvisioned", true);
 | 
			
		||||
 | 
			
		||||
		if(!platform.empty() && (platform!=Platforms::AP && platform!=Platforms::SWITCH && platform!="all")) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(platform=="all")
 | 
			
		||||
			platform="";
 | 
			
		||||
 | 
			
		||||
		Poco::JSON::Object RetObj;
 | 
			
		||||
		if (!QB_.Select.empty()) {
 | 
			
		||||
			Poco::JSON::Array Objects;
 | 
			
		||||
			for (auto &i : SelectedRecords()) {
 | 
			
		||||
				auto SerialNumber = i;
 | 
			
		||||
				auto &SerialNumber = i;
 | 
			
		||||
				if (!Utils::ValidSerialNumber(i))
 | 
			
		||||
					continue;
 | 
			
		||||
				GWObjects::Device D;
 | 
			
		||||
@@ -116,14 +125,14 @@ namespace OpenWifi {
 | 
			
		||||
			else
 | 
			
		||||
				RetObj.set(RESTAPI::Protocol::DEVICES, Objects);
 | 
			
		||||
 | 
			
		||||
		} else if (QB_.CountOnly == true) {
 | 
			
		||||
		} else if (QB_.CountOnly) {
 | 
			
		||||
			uint64_t Count = 0;
 | 
			
		||||
			if (StorageService()->GetDeviceCount(Count)) {
 | 
			
		||||
			if (StorageService()->GetDeviceCount(Count, platform)) {
 | 
			
		||||
				return ReturnCountOnly(Count);
 | 
			
		||||
			}
 | 
			
		||||
		} else if (serialOnly) {
 | 
			
		||||
			std::vector<std::string> SerialNumbers;
 | 
			
		||||
			StorageService()->GetDeviceSerialNumbers(QB_.Offset, QB_.Limit, SerialNumbers, OrderBy);
 | 
			
		||||
			StorageService()->GetDeviceSerialNumbers(QB_.Offset, QB_.Limit, SerialNumbers, OrderBy, platform, includeProvisioned);
 | 
			
		||||
			Poco::JSON::Array Objects;
 | 
			
		||||
			for (const auto &i : SerialNumbers) {
 | 
			
		||||
				Objects.add(i);
 | 
			
		||||
@@ -141,7 +150,7 @@ namespace OpenWifi {
 | 
			
		||||
			RetObj.set("serialNumbers", Objects);
 | 
			
		||||
		} else {
 | 
			
		||||
			std::vector<GWObjects::Device> Devices;
 | 
			
		||||
			StorageService()->GetDevices(QB_.Offset, QB_.Limit, Devices, OrderBy);
 | 
			
		||||
			StorageService()->GetDevices(QB_.Offset, QB_.Limit, Devices, OrderBy, platform, includeProvisioned);
 | 
			
		||||
			Poco::JSON::Array Objects;
 | 
			
		||||
			for (const auto &i : Devices) {
 | 
			
		||||
				Poco::JSON::Object Obj;
 | 
			
		||||
@@ -158,4 +167,63 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
		ReturnObject(RetObj);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static bool ValidMacPatternOnlyChars(const std::string &s) {
 | 
			
		||||
		return std::for_each(s.begin(),s.end(),[](const char c) {
 | 
			
		||||
			if(c=='%') return true;
 | 
			
		||||
			if(c>='0' && c<='9') return true;
 | 
			
		||||
			if(c>='a' && c<='f') return true;
 | 
			
		||||
			return false;
 | 
			
		||||
 		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_devices_handler::DoDelete() {
 | 
			
		||||
 | 
			
		||||
		if(!RESTAPI_utils::IsRootOrAdmin(UserInfo_.userinfo)) {
 | 
			
		||||
			return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(GetBoolParameter("simulatedDevices",false)) {
 | 
			
		||||
			auto F = []() ->void {
 | 
			
		||||
				StorageService()->DeleteSimulatedDevice("");
 | 
			
		||||
			};
 | 
			
		||||
			std::thread T(F);
 | 
			
		||||
			T.detach();
 | 
			
		||||
			return OK();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(!QB_.Select.empty() && !Utils::ValidSerialNumbers(QB_.Select)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(!QB_.Select.empty()) {
 | 
			
		||||
			for(auto &serialNumber:QB_.Select) {
 | 
			
		||||
				StorageService()->DeleteDevice(serialNumber);
 | 
			
		||||
			}
 | 
			
		||||
			return OK();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto SimulatedOnly = GetBoolParameter("simulatedOnly",false);
 | 
			
		||||
		auto oldestContact = GetParameter("oldestContact",0);
 | 
			
		||||
		if(oldestContact!=0) {
 | 
			
		||||
			StorageService()->DeleteDevices(oldestContact,SimulatedOnly);
 | 
			
		||||
			return OK();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto macPattern = GetParameter("macPattern","");
 | 
			
		||||
		if(macPattern.empty()) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//	rules out wrong values.
 | 
			
		||||
		Poco::toLowerInPlace(macPattern);
 | 
			
		||||
		Poco::replaceInPlace(macPattern,"*","%");
 | 
			
		||||
		if(!ValidMacPatternOnlyChars(macPattern)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		StorageService()->DeleteDevices(macPattern, SimulatedOnly);
 | 
			
		||||
		return OK();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -18,11 +18,12 @@ namespace OpenWifi {
 | 
			
		||||
								bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_DELETE,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
							 Server, TransactionId, Internal){};
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/devices"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoDelete() final;
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoPut() final{};
 | 
			
		||||
	};
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,15 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		std::string FileType;
 | 
			
		||||
		std::string FileContent;
 | 
			
		||||
		if (!StorageService()->GetAttachedFileContent(UUID, SerialNumber, FileContent, FileType) || FileContent.empty()) {
 | 
			
		||||
		int WaitingForFile = 0;
 | 
			
		||||
		if (!StorageService()->GetAttachedFileContent(UUID, SerialNumber, FileContent, FileType, WaitingForFile) && !WaitingForFile) {
 | 
			
		||||
			return NotFound();
 | 
			
		||||
		}
 | 
			
		||||
		else if (WaitingForFile) {
 | 
			
		||||
			// waiting for file to be uploaded, return Accepted
 | 
			
		||||
			return Accepted();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (FileType == "pcap") {
 | 
			
		||||
			SendFileContent(FileContent, "application/vnd.tcpdump.pcap", UUID + ".pcap");
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
#include <framework/RESTAPI_Handler.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class RESTAPI_file : public RESTAPIHandler {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,11 @@
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	static bool ValidRadiusPoolServerType(const std::string &T) {
 | 
			
		||||
		static std::set<std::string> Types{ "radsec", "generic", "orion", "globalreach"};
 | 
			
		||||
		return Types.find(T)!=Types.end();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_radiusProxyConfig_handler::DoGet() {
 | 
			
		||||
		Logger_.information(fmt::format("GET-RADIUS-PROXY-CONFIG: TID={} user={} thr_id={}",
 | 
			
		||||
										TransactionId_, Requester(),
 | 
			
		||||
@@ -44,10 +49,22 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//	Logically validate the config.
 | 
			
		||||
		for (const auto &pool : C.pools) {
 | 
			
		||||
		for (auto &pool : C.pools) {
 | 
			
		||||
			if (pool.name.empty()) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::PoolNameInvalid);
 | 
			
		||||
			}
 | 
			
		||||
			if (pool.radsecPoolType.empty()) {
 | 
			
		||||
				pool.radsecPoolType = "generic";
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(!ValidRadiusPoolServerType(pool.radsecPoolType)) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::NotAValidRadiusPoolType);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(pool.radsecKeepAlive==0) {
 | 
			
		||||
				pool.radsecKeepAlive=25;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (const auto &config : {pool.acctConfig, pool.authConfig, pool.coaConfig}) {
 | 
			
		||||
				if (config.servers.empty())
 | 
			
		||||
					continue;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								src/RESTAPI/RESTAPI_radiussessions_handler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/RESTAPI/RESTAPI_radiussessions_handler.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-04-02.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_radiussessions_handler.h"
 | 
			
		||||
#include <RESTObjects/RESTAPI_GWobjects.h>
 | 
			
		||||
#include <RADIUSSessionTracker.h>
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	bool MayBeAMAC(const std::string &mac) {
 | 
			
		||||
		return std::all_of(mac.begin(),mac.end(),[](char c)->bool {
 | 
			
		||||
			if ((c>='0' && c<='9') 	||
 | 
			
		||||
				(c>='a' && c<='f') 	||
 | 
			
		||||
				(c>='A' && c<='F')	||
 | 
			
		||||
				(c==':') ||
 | 
			
		||||
				(c=='-') ||
 | 
			
		||||
				(c=='*')) return true;
 | 
			
		||||
			return false;
 | 
			
		||||
			});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string InsertDelimiters(const std::string &mac, int first=1, char delimiter=':') {
 | 
			
		||||
		std::string res;
 | 
			
		||||
		std::size_t index=0;
 | 
			
		||||
		for(auto c:mac) {
 | 
			
		||||
			res += c;
 | 
			
		||||
			index++;
 | 
			
		||||
			if(index<mac.size()) {
 | 
			
		||||
				if (!first)
 | 
			
		||||
					res += delimiter;
 | 
			
		||||
			}
 | 
			
		||||
			first = 1-first;
 | 
			
		||||
		}
 | 
			
		||||
		return res;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string StripDelimiters(const std::string &V) {
 | 
			
		||||
		std::string Res;
 | 
			
		||||
		std::for_each(V.begin(),V.end(),[&](char c){ if(c!=':' && c!='-') { Res += c; }});
 | 
			
		||||
		return Res;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static std::string ConvertToMac(const std::string & V) {
 | 
			
		||||
		auto res = V;
 | 
			
		||||
		Poco::toUpperInPlace(res);
 | 
			
		||||
		res = StripDelimiters(res);
 | 
			
		||||
		if(res.size()==12) {
 | 
			
		||||
			res = InsertDelimiters(res);
 | 
			
		||||
		} else {
 | 
			
		||||
			if(res.find_first_of('*')==std::string::npos) {
 | 
			
		||||
				return "";
 | 
			
		||||
			}
 | 
			
		||||
			if(res[0]=='*') {
 | 
			
		||||
				res = InsertDelimiters(res, 1 - (res.size() % 2) );
 | 
			
		||||
			} else {
 | 
			
		||||
				res = InsertDelimiters(res);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return res;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_radiussessions_handler::DoGet() {
 | 
			
		||||
 | 
			
		||||
		if(GetBoolParameter("serialNumberOnly")) {
 | 
			
		||||
			std::vector<std::string>	L;
 | 
			
		||||
			RADIUSSessionTracker()->GetAPList(L);
 | 
			
		||||
			return ReturnObject("serialNumbers",L);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto mac = GetParameter("mac","");
 | 
			
		||||
		auto userName = GetParameter("userName","");
 | 
			
		||||
		if(!userName.empty()) {
 | 
			
		||||
			GWObjects::RADIUSSessionList	L;
 | 
			
		||||
			Poco::toLowerInPlace(userName);
 | 
			
		||||
			RADIUSSessionTracker()->GetUserNameAPSessions(userName,L);
 | 
			
		||||
			if(L.sessions.empty() && MayBeAMAC(userName)) {
 | 
			
		||||
				mac = ConvertToMac(userName);
 | 
			
		||||
			} else {
 | 
			
		||||
				return ReturnObject("sessions", L.sessions);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(!mac.empty()) {
 | 
			
		||||
			Poco::toUpperInPlace(mac);
 | 
			
		||||
			Poco::replaceInPlace(mac,":","-");
 | 
			
		||||
			GWObjects::RADIUSSessionList	L;
 | 
			
		||||
			RADIUSSessionTracker()->GetMACAPSessions(mac,L);
 | 
			
		||||
			return ReturnObject("sessions",L.sessions);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto SerialNumber = GetBinding("serialNumber","");
 | 
			
		||||
		if(SerialNumber.empty() || !Utils::ValidSerialNumber(SerialNumber)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::RADIUSSessionList	L;
 | 
			
		||||
		RADIUSSessionTracker()->GetAPSessions(SerialNumber,L);
 | 
			
		||||
		return ReturnObject("sessions",L.sessions);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RESTAPI_radiussessions_handler::DoPut() {
 | 
			
		||||
		auto SerialNumber = GetBinding("serialNumber","");
 | 
			
		||||
		if(SerialNumber.empty() || !Utils::ValidSerialNumber(SerialNumber)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		GWObjects::RadiusCoADMParameters	Parameters;
 | 
			
		||||
		if(!Parameters.from_json(ParsedBody_)) {
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto Command = GetParameter("operation","");
 | 
			
		||||
 | 
			
		||||
		if(Command=="coadm") {
 | 
			
		||||
			if(Parameters.callingStationId.empty() || Parameters.accountingSessionId.empty() || Parameters.accountingMultiSessionId.empty()) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
			}
 | 
			
		||||
			auto Index = Parameters.accountingSessionId + Parameters.accountingMultiSessionId;
 | 
			
		||||
			poco_information(Logger(), fmt::format("Disconnecting session {},{}", Parameters.accountingSessionId, Parameters.accountingMultiSessionId ));
 | 
			
		||||
			if(RADIUSSessionTracker()->SendCoADM(SerialNumber, Index)) {
 | 
			
		||||
				return OK();
 | 
			
		||||
			}
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::CouldNotPerformCommand);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(Command=="disconnectUser" && !Parameters.userName.empty()) {
 | 
			
		||||
			if(Parameters.userName.empty()) {
 | 
			
		||||
				return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
 | 
			
		||||
			}
 | 
			
		||||
			poco_information(Logger(), fmt::format("Disconnecting sessions for user: {}", Parameters.userName ));
 | 
			
		||||
			if(RADIUSSessionTracker()->DisconnectUser(Parameters.userName)) {
 | 
			
		||||
				return OK();
 | 
			
		||||
			}
 | 
			
		||||
			return BadRequest(RESTAPI::Errors::CouldNotPerformCommand);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return BadRequest(RESTAPI::Errors::InvalidCommand);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
							
								
								
									
										28
									
								
								src/RESTAPI/RESTAPI_radiussessions_handler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/RESTAPI/RESTAPI_radiussessions_handler.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-04-02.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_Handler.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class RESTAPI_radiussessions_handler : public RESTAPIHandler {
 | 
			
		||||
	  public:
 | 
			
		||||
		RESTAPI_radiussessions_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
 | 
			
		||||
						   RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
 | 
			
		||||
						   bool Internal)
 | 
			
		||||
			: RESTAPIHandler(bindings, L,
 | 
			
		||||
							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_PUT,
 | 
			
		||||
													  Poco::Net::HTTPRequest::HTTP_OPTIONS},
 | 
			
		||||
							 Server, TransactionId, Internal){};
 | 
			
		||||
		static auto PathName() { return std::list<std::string>{"/api/v1/radiusSessions/{serialNumber}"}; };
 | 
			
		||||
		void DoGet() final;
 | 
			
		||||
		void DoDelete() final{};
 | 
			
		||||
		void DoPost() final{};
 | 
			
		||||
		void DoPut();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -21,6 +21,9 @@
 | 
			
		||||
#include "RESTAPI/RESTAPI_script_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_scripts_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_telemetryWebSocket.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_radiussessions_handler.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_default_firmware.h"
 | 
			
		||||
#include "RESTAPI/RESTAPI_default_firmwares.h"
 | 
			
		||||
 | 
			
		||||
#include "framework/RESTAPI_SystemCommand.h"
 | 
			
		||||
#include "framework/RESTAPI_SystemConfiguration.h"
 | 
			
		||||
@@ -39,8 +42,9 @@ namespace OpenWifi {
 | 
			
		||||
			RESTAPI_system_configuration, RESTAPI_deviceDashboardHandler, RESTAPI_webSocketServer,
 | 
			
		||||
			RESTAPI_blacklist, RESTAPI_blacklist_list, RESTAPI_iptocountry_handler,
 | 
			
		||||
			RESTAPI_radiusProxyConfig_handler, RESTAPI_scripts_handler, RESTAPI_script_handler,
 | 
			
		||||
			RESTAPI_capabilities_handler, RESTAPI_telemetryWebSocket,
 | 
			
		||||
			RESTAPI_regulatory>(Path, Bindings, L, S,
 | 
			
		||||
			RESTAPI_capabilities_handler, RESTAPI_telemetryWebSocket, RESTAPI_radiussessions_handler,
 | 
			
		||||
			RESTAPI_regulatory, RESTAPI_default_firmwares,
 | 
			
		||||
			RESTAPI_default_firmware>(Path, Bindings, L, S,
 | 
			
		||||
																	  TransactionId);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +57,8 @@ namespace OpenWifi {
 | 
			
		||||
			RESTAPI_default_configurations, RESTAPI_default_configuration, RESTAPI_command,
 | 
			
		||||
			RESTAPI_commands, RESTAPI_ouis, RESTAPI_file, RESTAPI_blacklist,
 | 
			
		||||
			RESTAPI_iptocountry_handler, RESTAPI_radiusProxyConfig_handler, RESTAPI_scripts_handler,
 | 
			
		||||
			RESTAPI_script_handler, RESTAPI_blacklist_list,
 | 
			
		||||
			RESTAPI_regulatory>(Path, Bindings, L, S, TransactionId);
 | 
			
		||||
			RESTAPI_script_handler, RESTAPI_blacklist_list, RESTAPI_radiussessions_handler,
 | 
			
		||||
			RESTAPI_regulatory, RESTAPI_default_firmwares,
 | 
			
		||||
			RESTAPI_default_firmware>(Path, Bindings, L, S, TransactionId);
 | 
			
		||||
	}
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
#include "RESTAPI_ProvObjects.h"
 | 
			
		||||
#include "framework/utils.h"
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "framework/ow_constants.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,9 @@
 | 
			
		||||
#include "Daemon.h"
 | 
			
		||||
#ifdef TIP_GATEWAY_SERVICE
 | 
			
		||||
#include "AP_WS_Server.h"
 | 
			
		||||
#include "StorageService.h"
 | 
			
		||||
#include "CapabilitiesCache.h"
 | 
			
		||||
#include "RADIUSSessionTracker.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "RESTAPI_GWobjects.h"
 | 
			
		||||
@@ -28,7 +30,8 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
	void Device::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		field_to_json(Obj, "serialNumber", SerialNumber);
 | 
			
		||||
#ifdef TIP_GATEWAY_SERVICE
 | 
			
		||||
		field_to_json(Obj, "deviceType", CapabilitiesCache::instance()->GetPlatform(Compatible));
 | 
			
		||||
		field_to_json(Obj, "deviceType", StorageService()->GetPlatform(SerialNumber));
 | 
			
		||||
		field_to_json(Obj, "blackListed", StorageService()->IsBlackListed(Utils::MACToInt(SerialNumber)));
 | 
			
		||||
#endif
 | 
			
		||||
		field_to_json(Obj, "macAddress", MACAddress);
 | 
			
		||||
		field_to_json(Obj, "manufacturer", Manufacturer);
 | 
			
		||||
@@ -55,6 +58,10 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		field_to_json(Obj, "pendingConfigurationCmd", pendingConfigurationCmd);
 | 
			
		||||
		field_to_json(Obj, "restrictionDetails", restrictionDetails);
 | 
			
		||||
		field_to_json(Obj, "pendingUUID", pendingUUID);
 | 
			
		||||
		field_to_json(Obj, "simulated", simulated);
 | 
			
		||||
		field_to_json(Obj, "lastRecordedContact", lastRecordedContact);
 | 
			
		||||
		field_to_json(Obj, "certificateExpiryDate", certificateExpiryDate);
 | 
			
		||||
		field_to_json(Obj, "connectReason", connectReason);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void Device::to_json_with_status(Poco::JSON::Object &Obj) const {
 | 
			
		||||
@@ -62,9 +69,14 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
 | 
			
		||||
#ifdef TIP_GATEWAY_SERVICE
 | 
			
		||||
		ConnectionState ConState;
 | 
			
		||||
 | 
			
		||||
		if (AP_WS_Server()->GetState(SerialNumber, ConState)) {
 | 
			
		||||
			ConState.to_json(Obj);
 | 
			
		||||
#ifdef USE_MEDUSA_CLIENT
 | 
			
		||||
        auto Res = GS()->GetState(SerialNumber);
 | 
			
		||||
        if (Res.has_value()) {
 | 
			
		||||
            Res.value().to_json(SerialNumber,Obj);
 | 
			
		||||
#else
 | 
			
		||||
        if (AP_WS_Server()->GetState(SerialNumber, ConState)) {
 | 
			
		||||
			ConState.to_json(SerialNumber,Obj);
 | 
			
		||||
#endif
 | 
			
		||||
		} else {
 | 
			
		||||
			field_to_json(Obj, "ipAddress", "");
 | 
			
		||||
			field_to_json(Obj, "txBytes", (uint64_t)0);
 | 
			
		||||
@@ -76,6 +88,13 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
			field_to_json(Obj, "associations_2G", (uint64_t)0);
 | 
			
		||||
			field_to_json(Obj, "associations_5G", (uint64_t)0);
 | 
			
		||||
			field_to_json(Obj, "associations_6G", (uint64_t)0);
 | 
			
		||||
			field_to_json(Obj, "hasRADIUSSessions", false);
 | 
			
		||||
			field_to_json(Obj, "hasGPS", ConState.hasGPS);
 | 
			
		||||
			field_to_json(Obj, "sanity", ConState.sanity);
 | 
			
		||||
			field_to_json(Obj, "memoryUsed", ConState.memoryUsed);
 | 
			
		||||
			field_to_json(Obj, "sanity", ConState.sanity);
 | 
			
		||||
			field_to_json(Obj, "load", ConState.load);
 | 
			
		||||
			field_to_json(Obj, "temperature", ConState.temperature);
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
@@ -109,6 +128,10 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
			field_from_json(Obj, "pendingConfigurationCmd", pendingConfigurationCmd);
 | 
			
		||||
			field_from_json(Obj, "restrictionDetails", restrictionDetails);
 | 
			
		||||
			field_from_json(Obj, "pendingUUID", pendingUUID);
 | 
			
		||||
			field_from_json(Obj, "simulated", simulated);
 | 
			
		||||
			field_from_json(Obj, "lastRecordedContact", lastRecordedContact);
 | 
			
		||||
			field_from_json(Obj, "certificateExpiryDate", certificateExpiryDate);
 | 
			
		||||
			field_from_json(Obj, "connectReason", connectReason);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
@@ -149,15 +172,43 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		field_to_json(Obj, "recorded", Recorded);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		EmbedDocument("configuration", Obj, Configuration);
 | 
			
		||||
		field_to_json(Obj, "name", Name);
 | 
			
		||||
		field_to_json(Obj, "modelIds", Models);
 | 
			
		||||
    bool HealthCheck::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            field_from_json(Obj, "UUID", UUID);
 | 
			
		||||
            field_from_json(Obj, "sanity", Sanity);
 | 
			
		||||
            field_from_json(Obj, "recorded", Recorded);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch(...) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	void DefaultFirmware::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		field_to_json(Obj, "deviceType", deviceType);
 | 
			
		||||
		field_to_json(Obj, "description", Description);
 | 
			
		||||
		field_to_json(Obj, "uri", uri);
 | 
			
		||||
		field_to_json(Obj, "revision", revision);
 | 
			
		||||
		field_to_json(Obj, "imageCreationDate", imageCreationDate);
 | 
			
		||||
		field_to_json(Obj, "created", Created);
 | 
			
		||||
		field_to_json(Obj, "lastModified", LastModified);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool DefaultFirmware::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "deviceType", deviceType);
 | 
			
		||||
			field_from_json(Obj, "description", Description);
 | 
			
		||||
			field_from_json(Obj, "uri", uri);
 | 
			
		||||
			field_from_json(Obj, "revision", revision);
 | 
			
		||||
			field_from_json(Obj, "imageCreationDate", imageCreationDate);
 | 
			
		||||
			field_from_json(Obj, "created", Created);
 | 
			
		||||
			field_from_json(Obj, "lastModified", LastModified);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void CommandDetails::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		EmbedDocument("details", Obj, Details);
 | 
			
		||||
		EmbedDocument("results", Obj, Results);
 | 
			
		||||
@@ -180,12 +231,25 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		field_to_json(Obj, "deferred", deferred);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		EmbedDocument("configuration", Obj, configuration);
 | 
			
		||||
		field_to_json(Obj, "name", name);
 | 
			
		||||
		field_to_json(Obj, "modelIds", models);
 | 
			
		||||
		field_to_json(Obj, "description", description);
 | 
			
		||||
		field_to_json(Obj, "created", created);
 | 
			
		||||
		field_to_json(Obj, "lastModified", lastModified);
 | 
			
		||||
		field_to_json(Obj, "platform", platform);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool DefaultConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "name", Name);
 | 
			
		||||
			field_from_json(Obj, "configuration", Configuration);
 | 
			
		||||
			field_from_json(Obj, "modelIds", Models);
 | 
			
		||||
			field_from_json(Obj, "description", Description);
 | 
			
		||||
			field_from_json(Obj, "configuration", configuration);
 | 
			
		||||
			field_from_json(Obj, "name", name);
 | 
			
		||||
			field_from_json(Obj, "modelIds", models);
 | 
			
		||||
			field_from_json(Obj, "description", description);
 | 
			
		||||
			field_from_json(Obj, "created", created);
 | 
			
		||||
			field_from_json(Obj, "lastModified", lastModified);
 | 
			
		||||
			field_from_json(Obj, "platform", platform);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
@@ -211,7 +275,7 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void ConnectionState::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
	void ConnectionState::to_json([[maybe_unused]] const std::string &SerialNumber, Poco::JSON::Object &Obj)  {
 | 
			
		||||
		field_to_json(Obj, "ipAddress", Address);
 | 
			
		||||
		field_to_json(Obj, "txBytes", TX);
 | 
			
		||||
		field_to_json(Obj, "rxBytes", RX);
 | 
			
		||||
@@ -233,6 +297,21 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		field_to_json(Obj, "connectionCompletionTime", connectionCompletionTime);
 | 
			
		||||
		field_to_json(Obj, "totalConnectionTime", Utils::Now() - started);
 | 
			
		||||
		field_to_json(Obj, "certificateExpiryDate", certificateExpiryDate);
 | 
			
		||||
		field_to_json(Obj, "certificateIssuerName", certificateIssuerName);
 | 
			
		||||
		field_to_json(Obj, "connectReason", connectReason);
 | 
			
		||||
		field_to_json(Obj, "uptime", uptime);
 | 
			
		||||
        field_to_json(Obj, "compatible", Compatible);
 | 
			
		||||
 | 
			
		||||
#ifdef TIP_GATEWAY_SERVICE
 | 
			
		||||
		hasRADIUSSessions = RADIUSSessionTracker()->HasSessions(SerialNumber);
 | 
			
		||||
#endif
 | 
			
		||||
		field_to_json(Obj, "hasRADIUSSessions", hasRADIUSSessions );
 | 
			
		||||
		field_to_json(Obj, "hasGPS", hasGPS);
 | 
			
		||||
		field_to_json(Obj, "sanity", sanity);
 | 
			
		||||
		field_to_json(Obj, "memoryUsed", memoryUsed);
 | 
			
		||||
		field_to_json(Obj, "sanity", sanity);
 | 
			
		||||
		field_to_json(Obj, "load", load);
 | 
			
		||||
		field_to_json(Obj, "temperature", temperature);
 | 
			
		||||
 | 
			
		||||
		switch (VerifiedCertificate) {
 | 
			
		||||
		case NO_CERTIFICATE:
 | 
			
		||||
@@ -247,12 +326,55 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		case VERIFIED:
 | 
			
		||||
			field_to_json(Obj, "verifiedCertificate", "VERIFIED");
 | 
			
		||||
			break;
 | 
			
		||||
		case SIMULATED:
 | 
			
		||||
			field_to_json(Obj, "verifiedCertificate", "SIMULATED");
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			field_to_json(Obj, "verifiedCertificate", "NO_CERTIFICATE");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    bool ConnectionState::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            field_from_json(Obj, "compatible", Compatible);
 | 
			
		||||
            field_from_json(Obj, "ipAddress", Address);
 | 
			
		||||
            field_from_json(Obj, "txBytes", TX);
 | 
			
		||||
            field_from_json(Obj, "rxBytes", RX);
 | 
			
		||||
            field_from_json(Obj, "messageCount", MessageCount);
 | 
			
		||||
            field_from_json(Obj, "UUID", UUID);
 | 
			
		||||
            field_from_json(Obj, "connected", Connected);
 | 
			
		||||
            field_from_json(Obj, "firmware", Firmware);
 | 
			
		||||
            field_from_json(Obj, "lastContact", LastContact);
 | 
			
		||||
            field_from_json(Obj, "associations_2G", Associations_2G);
 | 
			
		||||
            field_from_json(Obj, "associations_5G", Associations_5G);
 | 
			
		||||
            field_from_json(Obj, "associations_6G", Associations_6G);
 | 
			
		||||
            field_from_json(Obj, "webSocketClients", webSocketClients);
 | 
			
		||||
            field_from_json(Obj, "websocketPackets", websocketPackets);
 | 
			
		||||
            field_from_json(Obj, "kafkaClients", kafkaClients);
 | 
			
		||||
            field_from_json(Obj, "kafkaPackets", kafkaPackets);
 | 
			
		||||
            field_from_json(Obj, "locale", locale);
 | 
			
		||||
            field_from_json(Obj, "started", started);
 | 
			
		||||
            field_from_json(Obj, "sessionId", sessionId);
 | 
			
		||||
            field_from_json(Obj, "connectionCompletionTime", connectionCompletionTime);
 | 
			
		||||
            field_from_json(Obj, "totalConnectionTime", totalConnectionTime);
 | 
			
		||||
            field_from_json(Obj, "certificateExpiryDate", certificateExpiryDate);
 | 
			
		||||
			field_from_json(Obj, "certificateIssuerName", certificateIssuerName);
 | 
			
		||||
            field_from_json(Obj, "connectReason", connectReason);
 | 
			
		||||
            field_from_json(Obj, "uptime", uptime);
 | 
			
		||||
            field_from_json(Obj, "hasRADIUSSessions", hasRADIUSSessions );
 | 
			
		||||
            field_from_json(Obj, "hasGPS", hasGPS);
 | 
			
		||||
            field_from_json(Obj, "sanity", sanity);
 | 
			
		||||
            field_from_json(Obj, "memoryUsed", memoryUsed);
 | 
			
		||||
            field_from_json(Obj, "sanity", sanity);
 | 
			
		||||
            field_from_json(Obj, "load", load);
 | 
			
		||||
            field_from_json(Obj, "temperature", temperature);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch(const Poco::Exception &E) {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	void DeviceConnectionStatistics::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		field_to_json(Obj, "averageConnectionTime", averageConnectionTime);
 | 
			
		||||
		field_to_json(Obj, "connectedDevices", connectedDevices);
 | 
			
		||||
@@ -373,6 +495,10 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		field_to_json(Obj, "acctConfig", acctConfig);
 | 
			
		||||
		field_to_json(Obj, "coaConfig", coaConfig);
 | 
			
		||||
		field_to_json(Obj, "useByDefault", useByDefault);
 | 
			
		||||
		field_to_json(Obj, "radsecKeepAlive", radsecKeepAlive);
 | 
			
		||||
		field_to_json(Obj, "poolProxyIp", poolProxyIp);
 | 
			
		||||
		field_to_json(Obj, "radsecPoolType", radsecPoolType);
 | 
			
		||||
		field_to_json(Obj, "enabled", enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool RadiusProxyPool::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
@@ -383,6 +509,10 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
			field_from_json(Obj, "acctConfig", acctConfig);
 | 
			
		||||
			field_from_json(Obj, "coaConfig", coaConfig);
 | 
			
		||||
			field_from_json(Obj, "useByDefault", useByDefault);
 | 
			
		||||
			field_from_json(Obj, "radsecKeepAlive", radsecKeepAlive);
 | 
			
		||||
			field_from_json(Obj, "poolProxyIp", poolProxyIp);
 | 
			
		||||
			field_from_json(Obj, "radsecPoolType", radsecPoolType);
 | 
			
		||||
			field_from_json(Obj, "enabled", enabled);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
@@ -580,4 +710,125 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
				(T.commands != commands) || (T.developer != developer) || (T.ssh != ssh) ||
 | 
			
		||||
				(T.key_info != key_info) || (T.country != country));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RADIUSSession::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		field_to_json(Obj, "started", started);
 | 
			
		||||
		field_to_json(Obj, "lastTransaction", lastTransaction);
 | 
			
		||||
		field_to_json(Obj, "destination", destination);
 | 
			
		||||
		field_to_json(Obj, "serialNumber", serialNumber);
 | 
			
		||||
		field_to_json(Obj, "userName", userName);
 | 
			
		||||
		field_to_json(Obj, "accountingSessionId", accountingSessionId);
 | 
			
		||||
		field_to_json(Obj, "accountingMultiSessionId", accountingMultiSessionId);
 | 
			
		||||
		field_to_json(Obj, "inputPackets", inputPackets);
 | 
			
		||||
		field_to_json(Obj, "outputPackets", outputPackets);
 | 
			
		||||
		field_to_json(Obj, "inputOctets", inputOctets);
 | 
			
		||||
		field_to_json(Obj, "outputOctets", outputOctets);
 | 
			
		||||
		field_to_json(Obj, "inputGigaWords", inputGigaWords);
 | 
			
		||||
		field_to_json(Obj, "outputGigaWords", outputGigaWords);
 | 
			
		||||
		field_to_json(Obj, "sessionTime", sessionTime);
 | 
			
		||||
		field_to_json(Obj, "callingStationId", callingStationId);
 | 
			
		||||
		field_to_json(Obj, "chargeableUserIdentity", chargeableUserIdentity);
 | 
			
		||||
		field_to_json(Obj, "interface", interface);
 | 
			
		||||
		field_to_json(Obj, "secret", secret);
 | 
			
		||||
		field_to_json(Obj, "nasId", nasId);
 | 
			
		||||
		field_to_json(Obj, "calledStationId", calledStationId);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RADIUSSessionList::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		field_to_json(Obj, "sessions", sessions);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void RadiusCoADMParameters::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		field_to_json(Obj, "accountingSessionId", accountingSessionId);
 | 
			
		||||
		field_to_json(Obj, "accountingMultiSessionId", accountingMultiSessionId);
 | 
			
		||||
		field_to_json(Obj, "callingStationId", callingStationId);
 | 
			
		||||
		field_to_json(Obj, "chargeableUserIdentity", chargeableUserIdentity);
 | 
			
		||||
		field_to_json(Obj, "userName", userName);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool RadiusCoADMParameters::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "accountingSessionId", accountingSessionId);
 | 
			
		||||
			field_from_json(Obj, "accountingMultiSessionId", accountingMultiSessionId);
 | 
			
		||||
			field_from_json(Obj, "callingStationId", callingStationId);
 | 
			
		||||
			field_from_json(Obj, "chargeableUserIdentity", chargeableUserIdentity);
 | 
			
		||||
			field_from_json(Obj, "userName", userName);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool DeviceTransferRequest::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "serialNumber", serialNumber);
 | 
			
		||||
			field_from_json(Obj, "server", server);
 | 
			
		||||
			field_from_json(Obj, "port", port);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool DeviceCertificateUpdateRequest::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "serial", serialNumber);
 | 
			
		||||
			field_from_json(Obj, "encodedCertificate", encodedCertificate);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PowerCyclePort::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "name", name);
 | 
			
		||||
			field_from_json(Obj, "cycle", cycle);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool PowerCycleRequest::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "serial", serialNumber);
 | 
			
		||||
			field_from_json(Obj, "when", when);
 | 
			
		||||
			field_from_json(Obj, "ports", ports);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool FixedConfig::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "serial", serialNumber);
 | 
			
		||||
			field_from_json(Obj, "country", country);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool CableDiagnostics::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "serial", serialNumber);
 | 
			
		||||
			field_from_json(Obj, "when", when);
 | 
			
		||||
			field_from_json(Obj, "ports", ports);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool ReEnroll::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
		try {
 | 
			
		||||
			field_from_json(Obj, "serial", serialNumber);
 | 
			
		||||
			field_from_json(Obj, "when", when);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (const Poco::Exception &E) {
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
} // namespace OpenWifi::GWObjects
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,13 @@
 | 
			
		||||
#include "Poco/JSON/Object.h"
 | 
			
		||||
#include "RESTAPI_SecurityObjects.h"
 | 
			
		||||
 | 
			
		||||
#ifdef TIP_GATEWAY_SERVICE
 | 
			
		||||
#include <RADIUS_helpers.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi::GWObjects {
 | 
			
		||||
 | 
			
		||||
	enum CertificateValidation { NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED };
 | 
			
		||||
	enum CertificateValidation { NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED, SIMULATED };
 | 
			
		||||
 | 
			
		||||
	struct ConnectionState {
 | 
			
		||||
		uint64_t MessageCount = 0;
 | 
			
		||||
@@ -38,8 +42,19 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		uint64_t sessionId = 0;
 | 
			
		||||
		double connectionCompletionTime = 0.0;
 | 
			
		||||
		std::uint64_t certificateExpiryDate = 0;
 | 
			
		||||
		std::string certificateIssuerName;
 | 
			
		||||
		std::uint64_t hasRADIUSSessions = 0;
 | 
			
		||||
		bool hasGPS = false;
 | 
			
		||||
		std::uint64_t sanity=0;
 | 
			
		||||
		std::double_t memoryUsed=0.0;
 | 
			
		||||
		std::double_t load=0.0;
 | 
			
		||||
		std::double_t temperature=0.0;
 | 
			
		||||
		std::string 	connectReason;
 | 
			
		||||
		std::uint64_t 	uptime=0;
 | 
			
		||||
        std::uint64_t 	totalConnectionTime=0;
 | 
			
		||||
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		void to_json(const std::string &SerialNumber, Poco::JSON::Object &Obj) ;
 | 
			
		||||
        bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct DeviceRestrictionsKeyInfo {
 | 
			
		||||
@@ -97,6 +112,11 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		std::string pendingConfigurationCmd;
 | 
			
		||||
		DeviceRestrictions restrictionDetails;
 | 
			
		||||
		std::uint64_t pendingUUID = 0;
 | 
			
		||||
		bool simulated=false;
 | 
			
		||||
		std::uint64_t 	lastRecordedContact=0;
 | 
			
		||||
		std::uint64_t 	certificateExpiryDate = 0;
 | 
			
		||||
		std::string 	connectReason;
 | 
			
		||||
		bool			blackListed=false;
 | 
			
		||||
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		void to_json_with_status(Poco::JSON::Object &Obj) const;
 | 
			
		||||
@@ -122,13 +142,15 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct HealthCheck {
 | 
			
		||||
		std::string SerialNumber;
 | 
			
		||||
		uint64_t UUID = 0;
 | 
			
		||||
		std::string Data;
 | 
			
		||||
		uint64_t Recorded = 0;
 | 
			
		||||
		uint64_t Sanity = 0;
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
	};
 | 
			
		||||
        std::string SerialNumber;
 | 
			
		||||
        uint64_t UUID = 0;
 | 
			
		||||
        std::string Data;
 | 
			
		||||
        uint64_t Recorded = 0;
 | 
			
		||||
        uint64_t Sanity = 0;
 | 
			
		||||
 | 
			
		||||
        void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
        bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
	struct Capabilities {
 | 
			
		||||
		std::string Capabilities;
 | 
			
		||||
@@ -159,12 +181,33 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct DefaultConfiguration {
 | 
			
		||||
		std::string Name;
 | 
			
		||||
		std::string Configuration;
 | 
			
		||||
		Types::StringVec Models;
 | 
			
		||||
		std::string name;
 | 
			
		||||
		std::string configuration;
 | 
			
		||||
		Types::StringVec models;
 | 
			
		||||
		std::string description;
 | 
			
		||||
		uint64_t created;
 | 
			
		||||
		uint64_t lastModified;
 | 
			
		||||
		std::string platform;
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct DefaultFirmware {
 | 
			
		||||
		std::string deviceType;
 | 
			
		||||
		std::string Description;
 | 
			
		||||
		std::string uri;
 | 
			
		||||
		std::string revision;
 | 
			
		||||
		uint64_t imageCreationDate;
 | 
			
		||||
		uint64_t Created;
 | 
			
		||||
		uint64_t LastModified;
 | 
			
		||||
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct DefaultFirmwareList {
 | 
			
		||||
		std::vector<DefaultFirmware>	firmwares;
 | 
			
		||||
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
@@ -327,6 +370,10 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
		RadiusProxyServerConfig acctConfig;
 | 
			
		||||
		RadiusProxyServerConfig coaConfig;
 | 
			
		||||
		bool useByDefault = false;
 | 
			
		||||
		std::string 	radsecPoolType;
 | 
			
		||||
		std::string 	poolProxyIp;
 | 
			
		||||
		std::uint64_t 	radsecKeepAlive=25;
 | 
			
		||||
		bool			enabled=true;
 | 
			
		||||
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
@@ -370,5 +417,141 @@ namespace OpenWifi::GWObjects {
 | 
			
		||||
 | 
			
		||||
	using RegulatoryInfoCountryMap = std::map<std::string,RegulatoryCountryInfo>;
 | 
			
		||||
 | 
			
		||||
	struct RADIUSSession {
 | 
			
		||||
		std::uint64_t 			started=0,
 | 
			
		||||
								lastTransaction=0;
 | 
			
		||||
		std::string 			serialNumber,
 | 
			
		||||
								destination,
 | 
			
		||||
								userName,
 | 
			
		||||
					 			accountingSessionId,
 | 
			
		||||
								accountingMultiSessionId,
 | 
			
		||||
					 			callingStationId,
 | 
			
		||||
								chargeableUserIdentity,
 | 
			
		||||
								secret,
 | 
			
		||||
								interface,
 | 
			
		||||
								nasId;
 | 
			
		||||
		std::uint64_t 			inputPackets = 0,
 | 
			
		||||
								outputPackets = 0,
 | 
			
		||||
								inputOctets = 0,
 | 
			
		||||
								outputOctets = 0,
 | 
			
		||||
								inputGigaWords = 0,
 | 
			
		||||
								outputGigaWords = 0;
 | 
			
		||||
		std::uint32_t 			sessionTime = 0;
 | 
			
		||||
		std::string 			calledStationId;
 | 
			
		||||
 | 
			
		||||
#ifdef TIP_GATEWAY_SERVICE
 | 
			
		||||
		RADIUS::RadiusPacket	accountingPacket;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct RADIUSSessionList {
 | 
			
		||||
		std::vector<RADIUSSession>	sessions;
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct RadiusCoADMParameters {
 | 
			
		||||
		std::string 			accountingSessionId,
 | 
			
		||||
								accountingMultiSessionId,
 | 
			
		||||
								callingStationId,
 | 
			
		||||
								chargeableUserIdentity,
 | 
			
		||||
								userName;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum class RadiusPoolStrategy {
 | 
			
		||||
		round_robin, random, weighted, unknown
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	enum class RadiusEndpointType {
 | 
			
		||||
		generic, radsec, globalreach, orion, unknown
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static inline RadiusEndpointType RadiusEndpointType(const std::string &T) {
 | 
			
		||||
		if(T=="generic") return RadiusEndpointType::generic;
 | 
			
		||||
		if(T=="radsec") return RadiusEndpointType::radsec;
 | 
			
		||||
		if(T=="globalreach") return RadiusEndpointType::globalreach;
 | 
			
		||||
		if(T=="orion") return RadiusEndpointType::orion;
 | 
			
		||||
		return RadiusEndpointType::unknown;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static inline RadiusPoolStrategy RadiusPoolStrategy(const std::string &T) {
 | 
			
		||||
		if(T=="round_robin") return RadiusPoolStrategy::round_robin;
 | 
			
		||||
		if(T=="random") return RadiusPoolStrategy::random;
 | 
			
		||||
		if(T=="weighted") return RadiusPoolStrategy::weighted;
 | 
			
		||||
		return RadiusPoolStrategy::unknown;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static inline std::string to_string(enum RadiusEndpointType T) {
 | 
			
		||||
		switch(T) {
 | 
			
		||||
		case RadiusEndpointType::generic: return "generic";
 | 
			
		||||
		case RadiusEndpointType::radsec: return "radsec";
 | 
			
		||||
		case RadiusEndpointType::globalreach: return "globalreach";
 | 
			
		||||
		case RadiusEndpointType::orion: return "orion";
 | 
			
		||||
		default:
 | 
			
		||||
			return "unknown";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static inline std::string to_string(enum RadiusPoolStrategy T) {
 | 
			
		||||
		switch(T) {
 | 
			
		||||
		case RadiusPoolStrategy::round_robin: return "round_robin";
 | 
			
		||||
		case RadiusPoolStrategy::random: return "random";
 | 
			
		||||
		case RadiusPoolStrategy::weighted: return "weighted";
 | 
			
		||||
		default:
 | 
			
		||||
			return "unknown";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct DeviceTransferRequest {
 | 
			
		||||
		std::string 	serialNumber;
 | 
			
		||||
		std::string 	server;
 | 
			
		||||
		std::uint64_t 	port;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct DeviceCertificateUpdateRequest {
 | 
			
		||||
		std::string 	serialNumber;
 | 
			
		||||
		std::string 	encodedCertificate;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct PowerCyclePort {
 | 
			
		||||
		std::string 	name;
 | 
			
		||||
		std::uint64_t 	cycle=10000;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct PowerCycleRequest {
 | 
			
		||||
		std::string 	serialNumber;
 | 
			
		||||
		std::uint64_t 	when;
 | 
			
		||||
		std::vector<PowerCyclePort> ports;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
	struct FixedConfig {
 | 
			
		||||
		std::string 	serialNumber;
 | 
			
		||||
		std::string 	country;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
	struct CableDiagnostics {
 | 
			
		||||
		std::string 	serialNumber;
 | 
			
		||||
		std::uint64_t 	when;
 | 
			
		||||
		std::vector<std::string> ports;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
	struct ReEnroll {
 | 
			
		||||
		std::string 	serialNumber;
 | 
			
		||||
		std::uint64_t 	when;
 | 
			
		||||
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
} // namespace OpenWifi::GWObjects
 | 
			
		||||
 
 | 
			
		||||
@@ -78,21 +78,22 @@ namespace OpenWifi::OWLSObjects {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void SimulationStatus::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
		field_to_json(Obj, "id", id);
 | 
			
		||||
		field_to_json(Obj, "simulationId", simulationId);
 | 
			
		||||
		field_to_json(Obj, "state", state);
 | 
			
		||||
		field_to_json(Obj, "tx", tx);
 | 
			
		||||
		field_to_json(Obj, "rx", rx);
 | 
			
		||||
		field_to_json(Obj, "msgsTx", msgsTx);
 | 
			
		||||
		field_to_json(Obj, "msgsRx", msgsRx);
 | 
			
		||||
		field_to_json(Obj, "liveDevices", liveDevices);
 | 
			
		||||
		field_to_json(Obj, "timeToFullDevices", timeToFullDevices);
 | 
			
		||||
		field_to_json(Obj, "startTime", startTime);
 | 
			
		||||
		field_to_json(Obj, "endTime", endTime);
 | 
			
		||||
		field_to_json(Obj, "errorDevices", errorDevices);
 | 
			
		||||
		field_to_json(Obj, "owner", owner);
 | 
			
		||||
	}
 | 
			
		||||
    void SimulationStatus::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        field_to_json(Obj, "id", id);
 | 
			
		||||
        field_to_json(Obj, "simulationId", simulationId);
 | 
			
		||||
        field_to_json(Obj, "state", state);
 | 
			
		||||
        field_to_json(Obj, "tx", tx);
 | 
			
		||||
        field_to_json(Obj, "rx", rx);
 | 
			
		||||
        field_to_json(Obj, "msgsTx", msgsTx);
 | 
			
		||||
        field_to_json(Obj, "msgsRx", msgsRx);
 | 
			
		||||
        field_to_json(Obj, "liveDevices", liveDevices);
 | 
			
		||||
        field_to_json(Obj, "timeToFullDevices", timeToFullDevices);
 | 
			
		||||
        field_to_json(Obj, "startTime", startTime);
 | 
			
		||||
        field_to_json(Obj, "endTime", endTime);
 | 
			
		||||
        field_to_json(Obj, "errorDevices", errorDevices);
 | 
			
		||||
        field_to_json(Obj, "owner", owner);
 | 
			
		||||
        field_to_json(Obj, "expectedDevices", expectedDevices);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	void Dashboard::to_json([[maybe_unused]] Poco::JSON::Object &Obj) const {}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,23 +43,24 @@ namespace OpenWifi::OWLSObjects {
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct SimulationStatus {
 | 
			
		||||
		std::string id;
 | 
			
		||||
		std::string simulationId;
 | 
			
		||||
		std::string state;
 | 
			
		||||
		uint64_t tx;
 | 
			
		||||
		uint64_t rx;
 | 
			
		||||
		uint64_t msgsTx;
 | 
			
		||||
		uint64_t msgsRx;
 | 
			
		||||
		uint64_t liveDevices;
 | 
			
		||||
		uint64_t timeToFullDevices;
 | 
			
		||||
		uint64_t startTime;
 | 
			
		||||
		uint64_t endTime;
 | 
			
		||||
		uint64_t errorDevices;
 | 
			
		||||
		std::string owner;
 | 
			
		||||
    struct SimulationStatus {
 | 
			
		||||
        std::string id;
 | 
			
		||||
        std::string simulationId;
 | 
			
		||||
        std::string state;
 | 
			
		||||
        uint64_t tx;
 | 
			
		||||
        uint64_t rx;
 | 
			
		||||
        uint64_t msgsTx;
 | 
			
		||||
        uint64_t msgsRx;
 | 
			
		||||
        uint64_t liveDevices;
 | 
			
		||||
        uint64_t timeToFullDevices;
 | 
			
		||||
        uint64_t startTime;
 | 
			
		||||
        uint64_t endTime;
 | 
			
		||||
        uint64_t errorDevices;
 | 
			
		||||
        std::string owner;
 | 
			
		||||
        uint64_t expectedDevices;
 | 
			
		||||
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
	};
 | 
			
		||||
        void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
	struct Dashboard {
 | 
			
		||||
		int O;
 | 
			
		||||
 
 | 
			
		||||
@@ -587,6 +587,9 @@ namespace OpenWifi::ProvObjects {
 | 
			
		||||
		field_to_json(Obj, "locale", locale);
 | 
			
		||||
		field_to_json(Obj, "realMacAddress", realMacAddress);
 | 
			
		||||
		field_to_json(Obj, "doNotAllowOverrides", doNotAllowOverrides);
 | 
			
		||||
        field_to_json(Obj, "imported", imported);
 | 
			
		||||
        field_to_json(Obj, "connected", connected);
 | 
			
		||||
        field_to_json(Obj, "platform", platform);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
@@ -609,6 +612,9 @@ namespace OpenWifi::ProvObjects {
 | 
			
		||||
			field_from_json(Obj, "locale", locale);
 | 
			
		||||
			field_from_json(Obj, "realMacAddress", realMacAddress);
 | 
			
		||||
			field_from_json(Obj, "doNotAllowOverrides", doNotAllowOverrides);
 | 
			
		||||
            field_from_json(Obj, "imported", imported);
 | 
			
		||||
            field_from_json(Obj, "connected", connected);
 | 
			
		||||
            field_from_json(Obj, "platform", platform);
 | 
			
		||||
			return true;
 | 
			
		||||
		} catch (...) {
 | 
			
		||||
		}
 | 
			
		||||
@@ -1194,4 +1200,243 @@ namespace OpenWifi::ProvObjects {
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    void GLBLRAccountInfo::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        info.to_json(Obj);
 | 
			
		||||
        field_to_json(Obj, "privateKey", privateKey);
 | 
			
		||||
        field_to_json(Obj, "country", country);
 | 
			
		||||
        field_to_json(Obj, "province", province);
 | 
			
		||||
        field_to_json(Obj, "city", city);
 | 
			
		||||
        field_to_json(Obj, "organization", organization);
 | 
			
		||||
        field_to_json(Obj, "commonName", commonName);
 | 
			
		||||
        field_to_json(Obj, "CSR", CSR);
 | 
			
		||||
        field_to_json(Obj, "CSRPrivateKey", CSRPrivateKey);
 | 
			
		||||
        field_to_json(Obj, "CSRPublicKey", CSRPublicKey);
 | 
			
		||||
        field_to_json(Obj, "GlobalReachAcctId", GlobalReachAcctId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool GLBLRAccountInfo::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            info.from_json(Obj);
 | 
			
		||||
            field_from_json(Obj, "privateKey", privateKey);
 | 
			
		||||
            field_from_json(Obj, "country", country);
 | 
			
		||||
            field_from_json(Obj, "province", province);
 | 
			
		||||
            field_from_json(Obj, "city", city);
 | 
			
		||||
            field_from_json(Obj, "organization", organization);
 | 
			
		||||
            field_from_json(Obj, "commonName", commonName);
 | 
			
		||||
            field_from_json(Obj, "CSR", CSR);
 | 
			
		||||
            field_from_json(Obj, "CSRPrivateKey", CSRPrivateKey);
 | 
			
		||||
            field_from_json(Obj, "CSRPublicKey", CSRPublicKey);
 | 
			
		||||
            field_from_json(Obj, "GlobalReachAcctId", GlobalReachAcctId);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GLBLRCertificateInfo::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        field_to_json(Obj, "id", id);
 | 
			
		||||
        field_to_json(Obj, "name", name);
 | 
			
		||||
        field_to_json(Obj, "accountId", accountId);
 | 
			
		||||
        field_to_json(Obj, "csr", csr);
 | 
			
		||||
        field_to_json(Obj, "certificate", certificate);
 | 
			
		||||
        field_to_json(Obj, "certificateChain", certificateChain);
 | 
			
		||||
        field_to_json(Obj, "certificateId", certificateId);
 | 
			
		||||
        field_to_json(Obj, "expiresAt", expiresAt);
 | 
			
		||||
        field_to_json(Obj, "created", created);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool GLBLRCertificateInfo::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            field_from_json(Obj, "id", id);
 | 
			
		||||
            field_from_json(Obj, "name", name);
 | 
			
		||||
            field_from_json(Obj, "accountId", accountId);
 | 
			
		||||
            field_from_json(Obj, "csr", csr);
 | 
			
		||||
            field_from_json(Obj, "certificate", certificate);
 | 
			
		||||
            field_from_json(Obj, "certificateChain", certificateChain);
 | 
			
		||||
            field_from_json(Obj, "certificateId", certificateId);
 | 
			
		||||
            field_from_json(Obj, "expiresAt", expiresAt);
 | 
			
		||||
            field_from_json(Obj, "created", created);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void GooglOrionAccountInfo::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        info.to_json(Obj);
 | 
			
		||||
        field_to_json(Obj, "privateKey", privateKey);
 | 
			
		||||
        field_to_json(Obj, "certificate", certificate);
 | 
			
		||||
        field_to_json(Obj, "cacerts", cacerts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool GooglOrionAccountInfo::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            info.from_json(Obj);
 | 
			
		||||
            field_from_json(Obj, "privateKey", privateKey);
 | 
			
		||||
            field_from_json(Obj, "certificate", certificate);
 | 
			
		||||
            field_from_json(Obj, "cacerts", cacerts);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RADIUSServer::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        field_to_json(Obj, "Hostname", Hostname);
 | 
			
		||||
        field_to_json(Obj, "IP", IP);
 | 
			
		||||
        field_to_json(Obj, "Port", Port);
 | 
			
		||||
        field_to_json(Obj, "Secret", Secret);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSServer::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            field_from_json(Obj, "Hostname", Hostname);
 | 
			
		||||
            field_from_json(Obj, "IP", IP);
 | 
			
		||||
            field_from_json(Obj, "Port", Port);
 | 
			
		||||
            field_from_json(Obj, "Secret", Secret);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RADIUSEndPointRadiusType::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        field_to_json(Obj, "Authentication", Authentication);
 | 
			
		||||
        field_to_json(Obj, "Accounting", Accounting);
 | 
			
		||||
        field_to_json(Obj, "CoA", CoA);
 | 
			
		||||
        field_to_json(Obj, "AccountingInterval", AccountingInterval);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSEndPointRadiusType::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            field_from_json(Obj, "Authentication", Authentication);
 | 
			
		||||
            field_from_json(Obj, "Accounting", Accounting);
 | 
			
		||||
            field_from_json(Obj, "CoA", CoA);
 | 
			
		||||
            field_from_json(Obj, "AccountingInterval", AccountingInterval);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RADIUSEndPointRadsecType::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        field_to_json(Obj, "Hostname", Hostname);
 | 
			
		||||
        field_to_json(Obj, "IP", IP);
 | 
			
		||||
        field_to_json(Obj, "Port", Port);
 | 
			
		||||
        field_to_json(Obj, "Secret", Secret);
 | 
			
		||||
        field_to_json(Obj, "OpenRoamingType", OpenRoamingType);
 | 
			
		||||
        field_to_json(Obj, "UseOpenRoamingAccount", UseOpenRoamingAccount);
 | 
			
		||||
        field_to_json(Obj, "Weight", Weight);
 | 
			
		||||
        field_to_json(Obj, "Certificate", Certificate);
 | 
			
		||||
        field_to_json(Obj, "PrivateKey", PrivateKey);
 | 
			
		||||
        field_to_json(Obj, "CaCerts", CaCerts);
 | 
			
		||||
        field_to_json(Obj, "AllowSelfSigned", AllowSelfSigned);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSEndPointRadsecType::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            field_from_json(Obj, "Hostname", Hostname);
 | 
			
		||||
            field_from_json(Obj, "IP", IP);
 | 
			
		||||
            field_from_json(Obj, "Port", Port);
 | 
			
		||||
            field_from_json(Obj, "Secret", Secret);
 | 
			
		||||
            field_from_json(Obj, "OpenRoamingType", OpenRoamingType);
 | 
			
		||||
            field_from_json(Obj, "UseOpenRoamingAccount", UseOpenRoamingAccount);
 | 
			
		||||
            field_from_json(Obj, "Weight", Weight);
 | 
			
		||||
            field_from_json(Obj, "Certificate", Certificate);
 | 
			
		||||
            field_from_json(Obj, "PrivateKey", PrivateKey);
 | 
			
		||||
            field_from_json(Obj, "CaCerts", CaCerts);
 | 
			
		||||
            field_from_json(Obj, "AllowSelfSigned", AllowSelfSigned);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RADIUSEndPoint::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        info.to_json(Obj);
 | 
			
		||||
        field_to_json(Obj, "Type", Type);
 | 
			
		||||
        field_to_json(Obj, "RadsecServers", RadsecServers);
 | 
			
		||||
        field_to_json(Obj, "RadiusServers", RadiusServers);
 | 
			
		||||
        field_to_json(Obj, "PoolStrategy", PoolStrategy);
 | 
			
		||||
        field_to_json(Obj, "Index", Index);
 | 
			
		||||
        field_to_json(Obj, "UsedBy", UsedBy);
 | 
			
		||||
        field_to_json(Obj, "UseGWProxy", UseGWProxy);
 | 
			
		||||
        field_to_json(Obj, "NasIdentifier", NasIdentifier);
 | 
			
		||||
        field_to_json(Obj, "AccountingInterval", AccountingInterval);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSEndPoint::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            info.from_json(Obj);
 | 
			
		||||
            field_from_json(Obj, "Type", Type);
 | 
			
		||||
            field_from_json(Obj, "RadsecServers", RadsecServers);
 | 
			
		||||
            field_from_json(Obj, "RadiusServers", RadiusServers);
 | 
			
		||||
            field_from_json(Obj, "PoolStrategy", PoolStrategy);
 | 
			
		||||
            field_from_json(Obj, "Index", Index);
 | 
			
		||||
            field_from_json(Obj, "UsedBy", UsedBy);
 | 
			
		||||
            field_from_json(Obj, "UseGWProxy", UseGWProxy);
 | 
			
		||||
            field_from_json(Obj, "NasIdentifier", NasIdentifier);
 | 
			
		||||
            field_from_json(Obj, "AccountingInterval", AccountingInterval);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void RADIUSEndpointUpdateStatus::to_json(Poco::JSON::Object &Obj) const {
 | 
			
		||||
        field_to_json(Obj, "lastUpdate", lastUpdate);
 | 
			
		||||
        field_to_json(Obj, "lastConfigurationChange", lastConfigurationChange);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSEndpointUpdateStatus::from_json(const Poco::JSON::Object::Ptr &Obj) {
 | 
			
		||||
        try {
 | 
			
		||||
            field_from_json(Obj, "lastUpdate", lastUpdate);
 | 
			
		||||
            field_from_json(Obj, "lastConfigurationChange", lastConfigurationChange);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (const Poco::Exception &E) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSEndpointUpdateStatus::Read() {
 | 
			
		||||
        Poco::File  F(OpenWifi::MicroServiceDataDirectory()+"/RADIUSEndpointUpdateStatus.json");
 | 
			
		||||
        try {
 | 
			
		||||
            if (F.exists()) {
 | 
			
		||||
                Poco::JSON::Parser P;
 | 
			
		||||
                std::ifstream ifs(F.path(), std::ios_base::in | std::ios_base::binary);
 | 
			
		||||
                auto Obj = P.parse(ifs);
 | 
			
		||||
                return from_json(Obj.extract<Poco::JSON::Object::Ptr>());
 | 
			
		||||
            }
 | 
			
		||||
        } catch (...) {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSEndpointUpdateStatus::Save() {
 | 
			
		||||
        Poco::File  F(OpenWifi::MicroServiceDataDirectory()+"/RADIUSEndpointUpdateStatus.json");
 | 
			
		||||
        try {
 | 
			
		||||
            Poco::JSON::Object Obj;
 | 
			
		||||
            to_json(Obj);
 | 
			
		||||
            std::ofstream O(F.path(), std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
 | 
			
		||||
            Poco::JSON::Stringifier::stringify(Obj, O);
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (...) {
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool RADIUSEndpointUpdateStatus::ChangeConfiguration() {
 | 
			
		||||
        Read();
 | 
			
		||||
        lastConfigurationChange = Utils::Now();
 | 
			
		||||
        return Save();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi::ProvObjects
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -5,7 +5,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <shared_mutex>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
#include "framework/MicroServiceFuncs.h"
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
@@ -38,7 +38,7 @@ namespace OpenWifi {
 | 
			
		||||
		inline int Start() final {
 | 
			
		||||
			poco_notice(Logger(), "Starting...");
 | 
			
		||||
 | 
			
		||||
			std::shared_lock L(KeyMutex_);
 | 
			
		||||
			std::lock_guard L(KeyMutex_);
 | 
			
		||||
 | 
			
		||||
			CacheFilename_ = MicroServiceDataDirectory() + "/signature_cache";
 | 
			
		||||
			Poco::File CacheFile(CacheFilename_);
 | 
			
		||||
@@ -91,7 +91,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		inline std::string Sign(const GWObjects::DeviceRestrictions &Restrictions,
 | 
			
		||||
								const std::string &Data) const {
 | 
			
		||||
			std::shared_lock L(KeyMutex_);
 | 
			
		||||
			std::lock_guard L(KeyMutex_);
 | 
			
		||||
			try {
 | 
			
		||||
				if (Restrictions.key_info.algo == "static") {
 | 
			
		||||
					return "aaaaaaaaaa";
 | 
			
		||||
@@ -120,7 +120,7 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		inline std::string Sign(const GWObjects::DeviceRestrictions &Restrictions,
 | 
			
		||||
								const Poco::URI &uri) {
 | 
			
		||||
			std::shared_lock L(KeyMutex_);
 | 
			
		||||
			std::lock_guard L(KeyMutex_);
 | 
			
		||||
			try {
 | 
			
		||||
				if (Restrictions.key_info.algo == "static") {
 | 
			
		||||
					return "aaaaaaaaaa";
 | 
			
		||||
@@ -172,7 +172,7 @@ namespace OpenWifi {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		mutable std::shared_mutex KeyMutex_;
 | 
			
		||||
		mutable std::mutex KeyMutex_;
 | 
			
		||||
		std::map<std::string, Poco::SharedPtr<Poco::Crypto::RSAKey>> Keys_;
 | 
			
		||||
		std::map<std::string, std::string> SignatureCache_;
 | 
			
		||||
		std::string CacheFilename_;
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ namespace OpenWifi::StateUtils {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool ComputeAssociations(const Poco::JSON::Object::Ptr RawObject, uint64_t &Radios_2G,
 | 
			
		||||
							 uint64_t &Radios_5G, uint64_t &Radios_6G) {
 | 
			
		||||
							 uint64_t &Radios_5G, uint64_t &Radios_6G, uint64_t &UpTime	) {
 | 
			
		||||
		Radios_2G = 0;
 | 
			
		||||
		Radios_5G = 0;
 | 
			
		||||
		Radios_6G = 0;
 | 
			
		||||
@@ -90,9 +90,15 @@ namespace OpenWifi::StateUtils {
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			//			std::cout << Radios_2G << " " << Radios_5G << " " << Radios_6G << std::endl;
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(RawObject->has("unit") && !RawObject->isNull("unit") && RawObject->isObject("unit")) {
 | 
			
		||||
			auto unit = RawObject->getObject("unit");
 | 
			
		||||
			if(unit->has("uptime")) {
 | 
			
		||||
				UpTime = unit->get("uptime");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
} // namespace OpenWifi::StateUtils
 | 
			
		||||
@@ -8,5 +8,5 @@
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi::StateUtils {
 | 
			
		||||
	bool ComputeAssociations(const Poco::JSON::Object::Ptr RawObject, uint64_t &Radios_2G,
 | 
			
		||||
							 uint64_t &Radios_5G, uint64_t &Radio_6G);
 | 
			
		||||
							 uint64_t &Radios_5G, uint64_t &Radio_6G, uint64_t &UpTime);
 | 
			
		||||
}
 | 
			
		||||
@@ -22,6 +22,8 @@ namespace OpenWifi {
 | 
			
		||||
		ScriptDB_->Create();
 | 
			
		||||
		ScriptDB_->Initialize();
 | 
			
		||||
 | 
			
		||||
		FixDeviceTypeBug();
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,22 @@
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
	class LockedDbSession {
 | 
			
		||||
	  public:
 | 
			
		||||
		explicit LockedDbSession();
 | 
			
		||||
		~LockedDbSession() = default;
 | 
			
		||||
		inline std::mutex &Mutex() { return *Mutex_; };
 | 
			
		||||
		inline Poco::Data::Session &Session() {
 | 
			
		||||
			if(!Session_->isConnected()) {
 | 
			
		||||
				Session_->reconnect();
 | 
			
		||||
			}
 | 
			
		||||
			return *Session_;
 | 
			
		||||
		};
 | 
			
		||||
	  private:
 | 
			
		||||
		std::shared_ptr<Poco::Data::Session> 	Session_;
 | 
			
		||||
		std::shared_ptr<std::mutex> 			Mutex_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class Storage : public StorageClass {
 | 
			
		||||
 | 
			
		||||
	  public:
 | 
			
		||||
@@ -90,7 +106,8 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		// typedef std::map<std::string,std::string>	DeviceCapabilitiesCache;
 | 
			
		||||
 | 
			
		||||
		bool AddLog(const GWObjects::DeviceLog &Log);
 | 
			
		||||
		bool AddLog(LockedDbSession &Session, const GWObjects::DeviceLog &Log);
 | 
			
		||||
		bool AddStatisticsData(Poco::Data::Session &Session, const GWObjects::Statistics &Stats);
 | 
			
		||||
		bool AddStatisticsData(const GWObjects::Statistics &Stats);
 | 
			
		||||
		bool GetStatisticsData(std::string &SerialNumber, uint64_t FromDate, uint64_t ToDate,
 | 
			
		||||
							   uint64_t Offset, uint64_t HowMany,
 | 
			
		||||
@@ -102,6 +119,7 @@ namespace OpenWifi {
 | 
			
		||||
									 std::vector<GWObjects::Statistics> &Stats);
 | 
			
		||||
 | 
			
		||||
		bool AddHealthCheckData(const GWObjects::HealthCheck &Check);
 | 
			
		||||
		bool AddHealthCheckData(LockedDbSession &Session, const GWObjects::HealthCheck &Check);
 | 
			
		||||
		bool GetHealthCheckData(std::string &SerialNumber, uint64_t FromDate, uint64_t ToDate,
 | 
			
		||||
								uint64_t Offset, uint64_t HowMany,
 | 
			
		||||
								std::vector<GWObjects::HealthCheck> &Checks);
 | 
			
		||||
@@ -115,27 +133,43 @@ namespace OpenWifi {
 | 
			
		||||
									   uint64_t &NewUUID);
 | 
			
		||||
 | 
			
		||||
		bool RollbackDeviceConfigurationChange(std::string & SerialNumber);
 | 
			
		||||
		bool CompleteDeviceConfigurationChange(Poco::Data::Session &Session, std::string & SerialNumber);
 | 
			
		||||
		bool CompleteDeviceConfigurationChange(std::string & SerialNumber);
 | 
			
		||||
 | 
			
		||||
		bool CreateDevice(LockedDbSession &Session, GWObjects::Device &);
 | 
			
		||||
		bool CreateDevice(GWObjects::Device &);
 | 
			
		||||
		bool CreateDefaultDevice(std::string &SerialNumber, const Config::Capabilities &Caps,
 | 
			
		||||
								 std::string &Firmware, const Poco::Net::IPAddress &IPAddress);
 | 
			
		||||
		bool CreateDefaultDevice(Poco::Data::Session &Session,std::string &SerialNumber,
 | 
			
		||||
								 const Config::Capabilities &Caps,
 | 
			
		||||
								 std::string &Firmware, const Poco::Net::IPAddress &IPAddress,
 | 
			
		||||
								 bool simulated);
 | 
			
		||||
		bool CreateDevice(Poco::Data::Session &Sess, GWObjects::Device &DeviceDetails);
 | 
			
		||||
 | 
			
		||||
		bool GetDevice(std::string &SerialNumber, GWObjects::Device &);
 | 
			
		||||
		bool GetDevice(LockedDbSession &Session, const std::string &SerialNumber, GWObjects::Device &);
 | 
			
		||||
		bool GetDevice(Poco::Data::Session &Session, const std::string &SerialNumber, GWObjects::Device &DeviceDetails);
 | 
			
		||||
		bool GetDevice(const std::string &SerialNumber, GWObjects::Device &);
 | 
			
		||||
		bool GetDevices(uint64_t From, uint64_t HowMany, std::vector<GWObjects::Device> &Devices,
 | 
			
		||||
						const std::string &orderBy = "");
 | 
			
		||||
						const std::string &orderBy = "",
 | 
			
		||||
						const std::string &platform = "",
 | 
			
		||||
						bool includeProvisioned = true);
 | 
			
		||||
		//		bool GetDevices(uint64_t From, uint64_t HowMany, const std::string & Select,
 | 
			
		||||
		// std::vector<GWObjects::Device> &Devices, const std::string & orderBy="");
 | 
			
		||||
		bool DeleteDevice(std::string &SerialNumber);
 | 
			
		||||
		bool DeleteDevices(std::string &SerialPattern, bool SimulatedOnly);
 | 
			
		||||
		bool DeleteDevices(std::uint64_t OlderContact, bool SimulatedOnly);
 | 
			
		||||
		std::string GetPlatform(const std::string &SerialNumber);
 | 
			
		||||
 | 
			
		||||
		bool UpdateDevice(GWObjects::Device &);
 | 
			
		||||
		bool UpdateDevice(LockedDbSession &Session, GWObjects::Device &);
 | 
			
		||||
		bool UpdateDevice(Poco::Data::Session &Sess, GWObjects::Device &NewDeviceDetails);
 | 
			
		||||
		bool DeviceExists(std::string &SerialNumber);
 | 
			
		||||
		bool SetConnectInfo(std::string &SerialNumber, std::string &Firmware);
 | 
			
		||||
		bool GetDeviceCount(uint64_t &Count);
 | 
			
		||||
		bool GetDeviceCount(uint64_t &Count, const std::string &platform = "");
 | 
			
		||||
		bool GetDeviceSerialNumbers(uint64_t From, uint64_t HowMany,
 | 
			
		||||
									std::vector<std::string> &SerialNumbers,
 | 
			
		||||
									const std::string &orderBy = "");
 | 
			
		||||
									const std::string &orderBy = "",
 | 
			
		||||
									const std::string &platform = "",
 | 
			
		||||
									bool includeProvisioned = true);									
 | 
			
		||||
		bool GetDeviceFWUpdatePolicy(std::string &SerialNumber, std::string &Policy);
 | 
			
		||||
		bool SetDevicePassword(std::string &SerialNumber, std::string &Password);
 | 
			
		||||
		bool SetDevicePassword(LockedDbSession &Session, std::string &SerialNumber, std::string &Password);
 | 
			
		||||
		bool UpdateSerialNumberCache();
 | 
			
		||||
		static void GetDeviceDbFieldList(Types::StringVec &Fields);
 | 
			
		||||
 | 
			
		||||
@@ -144,9 +178,11 @@ namespace OpenWifi {
 | 
			
		||||
 | 
			
		||||
		bool UpdateDeviceCapabilities(std::string &SerialNumber,
 | 
			
		||||
									  const Config::Capabilities &Capabilities);
 | 
			
		||||
		bool UpdateDeviceCapabilities(Poco::Data::Session &Session, std::string &SerialNumber,
 | 
			
		||||
									  const Config::Capabilities &Capabilities);
 | 
			
		||||
		bool GetDeviceCapabilities(std::string &SerialNumber, GWObjects::Capabilities &);
 | 
			
		||||
		bool DeleteDeviceCapabilities(std::string &SerialNumber);
 | 
			
		||||
		bool CreateDeviceCapabilities(std::string &SerialNumber,
 | 
			
		||||
		bool CreateDeviceCapabilities(Poco::Data::Session &Session, std::string &SerialNumber,
 | 
			
		||||
									  const Config::Capabilities &Capabilities);
 | 
			
		||||
		bool InitCapabilitiesCache();
 | 
			
		||||
 | 
			
		||||
@@ -167,10 +203,24 @@ namespace OpenWifi {
 | 
			
		||||
		bool GetDefaultConfigurations(uint64_t From, uint64_t HowMany,
 | 
			
		||||
									  std::vector<GWObjects::DefaultConfiguration> &Devices);
 | 
			
		||||
		bool FindDefaultConfigurationForModel(const std::string &Model,
 | 
			
		||||
											  const std::string &Platform,
 | 
			
		||||
											  GWObjects::DefaultConfiguration &DefConfig);
 | 
			
		||||
		uint64_t GetDefaultConfigurationsCount();
 | 
			
		||||
		bool DefaultConfigurationAlreadyExists(std::string &Name);
 | 
			
		||||
 | 
			
		||||
		bool UpdateDefaultFirmware(GWObjects::DefaultFirmware &DefFirmware);
 | 
			
		||||
		bool CreateDefaultFirmware(GWObjects::DefaultFirmware &DefConfig);
 | 
			
		||||
		bool DeleteDefaultFirmware(std::string &name);
 | 
			
		||||
		bool GetDefaultFirmware(std::string &name, GWObjects::DefaultFirmware &DefConfig);
 | 
			
		||||
		bool GetDefaultFirmwares(uint64_t From, uint64_t HowMany,
 | 
			
		||||
									  std::vector<GWObjects::DefaultFirmware> &Devices);
 | 
			
		||||
		bool FindDefaultFirmwareForModel(const std::string &Model,
 | 
			
		||||
											  GWObjects::DefaultFirmware &DefConfig);
 | 
			
		||||
		uint64_t GetDefaultFirmwaresCount();
 | 
			
		||||
		bool DefaultFirmwareAlreadyExists(std::string &Name);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		bool AddCommand(std::string &SerialNumber, GWObjects::CommandDetails &Command,
 | 
			
		||||
						CommandExecutionType Type);
 | 
			
		||||
		bool GetCommands(std::string &SerialNumber, uint64_t FromDate, uint64_t ToDate,
 | 
			
		||||
@@ -193,7 +243,7 @@ namespace OpenWifi {
 | 
			
		||||
									 const std::string &Type);
 | 
			
		||||
		bool CancelWaitFile(std::string &UUID, std::string &ErrorText);
 | 
			
		||||
		bool GetAttachedFileContent(std::string &UUID, const std::string &SerialNumber,
 | 
			
		||||
									std::string &FileContent, std::string &Type);
 | 
			
		||||
									std::string &FileContent, std::string &Type, int& WaitingForFile);
 | 
			
		||||
		bool RemoveAttachedFile(std::string &UUID);
 | 
			
		||||
		bool SetCommandResult(std::string &UUID, std::string &Result);
 | 
			
		||||
		bool GetNewestCommands(std::string &SerialNumber, uint64_t HowMany,
 | 
			
		||||
@@ -205,27 +255,33 @@ namespace OpenWifi {
 | 
			
		||||
		void RemovedExpiredCommands();
 | 
			
		||||
		void RemoveTimedOutCommands();
 | 
			
		||||
 | 
			
		||||
		bool RemoveOldCommands(std::string &SerilNumber, std::string &Command);
 | 
			
		||||
		bool RemoveOldCommands(std::string &SerialNumber, std::string &Command);
 | 
			
		||||
 | 
			
		||||
		bool AddBlackListDevices(std::vector<GWObjects::BlackListedDevice> &Devices);
 | 
			
		||||
		bool AddBlackListDevice(GWObjects::BlackListedDevice &Device);
 | 
			
		||||
		bool GetBlackListDevice(std::string &SerialNumber, GWObjects::BlackListedDevice &Device);
 | 
			
		||||
		bool DeleteBlackListDevice(std::string &SerialNumber);
 | 
			
		||||
		bool IsBlackListed(const std::string &SerialNumber, std::string &reason,
 | 
			
		||||
		bool IsBlackListed(std::uint64_t SerialNumber, std::string &reason,
 | 
			
		||||
						   std::string &author, std::uint64_t &created);
 | 
			
		||||
		bool IsBlackListed(const std::string &SerialNumber);
 | 
			
		||||
		bool IsBlackListed(std::uint64_t SerialNumber);
 | 
			
		||||
		bool InitializeBlackListCache();
 | 
			
		||||
		bool GetBlackListDevices(uint64_t Offset, uint64_t HowMany,
 | 
			
		||||
								 std::vector<GWObjects::BlackListedDevice> &Devices);
 | 
			
		||||
		bool UpdateBlackListDevice(std::string &SerialNumber, GWObjects::BlackListedDevice &Device);
 | 
			
		||||
		uint64_t GetBlackListDeviceCount();
 | 
			
		||||
 | 
			
		||||
		bool DeleteSimulatedDevice(const std::string &SerialNumber);
 | 
			
		||||
 | 
			
		||||
		bool RemoveHealthChecksRecordsOlderThan(uint64_t Date);
 | 
			
		||||
		bool RemoveDeviceLogsRecordsOlderThan(uint64_t Date);
 | 
			
		||||
		bool RemoveStatisticsRecordsOlderThan(uint64_t Date);
 | 
			
		||||
		bool RemoveCommandListRecordsOlderThan(uint64_t Date);
 | 
			
		||||
		bool RemoveUploadedFilesRecordsOlderThan(uint64_t Date);
 | 
			
		||||
 | 
			
		||||
		bool SetDeviceLastRecordedContact(LockedDbSession &Session, std::string & SerialNumber, std::uint64_t lastRecordedContact);
 | 
			
		||||
		bool SetDeviceLastRecordedContact(std::string & SerialNumber, std::uint64_t lastRecordedContact);
 | 
			
		||||
		bool SetDeviceLastRecordedContact(Poco::Data::Session & Session, std::string & SerialNumber, std::uint64_t lastRecordedContact);
 | 
			
		||||
 | 
			
		||||
		int Create_Tables();
 | 
			
		||||
		int Create_Statistics();
 | 
			
		||||
		int Create_Devices();
 | 
			
		||||
@@ -236,17 +292,29 @@ namespace OpenWifi {
 | 
			
		||||
		int Create_CommandList();
 | 
			
		||||
		int Create_BlackList();
 | 
			
		||||
		int Create_FileUploads();
 | 
			
		||||
		int Create_DefaultFirmwares();
 | 
			
		||||
 | 
			
		||||
		bool AnalyzeCommands(Types::CountedMap &R);
 | 
			
		||||
		bool AnalyzeDevices(GWObjects::Dashboard &D);
 | 
			
		||||
 | 
			
		||||
		void FixDeviceTypeBug();
 | 
			
		||||
 | 
			
		||||
		int Start() override;
 | 
			
		||||
		void Stop() override;
 | 
			
		||||
 | 
			
		||||
		inline Poco::Data::Session	StartSession() {
 | 
			
		||||
			return Pool_->get();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		std::unique_ptr<OpenWifi::ScriptDB> ScriptDB_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	inline auto StorageService() { return Storage::instance(); }
 | 
			
		||||
 | 
			
		||||
	inline LockedDbSession::LockedDbSession() {
 | 
			
		||||
		Session_ = std::make_shared<Poco::Data::Session>(Poco::Data::Session(StorageService()->StartSession()));
 | 
			
		||||
		Mutex_ = std::make_shared<std::mutex>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,6 @@ namespace OpenWifi {
 | 
			
		||||
					}
 | 
			
		||||
				} break;
 | 
			
		||||
				case TelemetryNotification::NotificationType::unregister: {
 | 
			
		||||
					std::lock_guard G(Mutex_);
 | 
			
		||||
 | 
			
		||||
					auto client = Clients_.find(Notification->Data_);
 | 
			
		||||
					if (client != Clients_.end()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
 | 
			
		||||
#include "AP_WS_ReactorPool.h"
 | 
			
		||||
#include "AP_WS_Reactor_Pool.h"
 | 
			
		||||
#include "TelemetryClient.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,8 @@ namespace OpenWifi::GWWebSocketNotifications {
 | 
			
		||||
 | 
			
		||||
	struct SingleDevice {
 | 
			
		||||
		std::string serialNumber;
 | 
			
		||||
		inline void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct SingleDeviceConfigurationChange {
 | 
			
		||||
@@ -20,15 +20,15 @@ namespace OpenWifi::GWWebSocketNotifications {
 | 
			
		||||
		uint64_t oldUUID;
 | 
			
		||||
		uint64_t newUUID;
 | 
			
		||||
 | 
			
		||||
		inline void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct SingleDeviceFirmwareChange {
 | 
			
		||||
		std::string serialNumber;
 | 
			
		||||
		std::string newFirmware;
 | 
			
		||||
		inline void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct NumberOfConnection {
 | 
			
		||||
@@ -38,8 +38,8 @@ namespace OpenWifi::GWWebSocketNotifications {
 | 
			
		||||
		std::uint64_t rx = 0;
 | 
			
		||||
		std::uint64_t tx = 0;
 | 
			
		||||
 | 
			
		||||
		inline void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		inline bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
		void to_json(Poco::JSON::Object &Obj) const;
 | 
			
		||||
		bool from_json(const Poco::JSON::Object::Ptr &Obj);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void Register();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										66
									
								
								src/firmware_revision_cache.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/firmware_revision_cache.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
//
 | 
			
		||||
// Created by stephane bourque on 2023-07-12.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "framework/SubSystemServer.h"
 | 
			
		||||
#include "Poco/ExpireLRUCache.h"
 | 
			
		||||
#include "sdks/sdk_fms.h"
 | 
			
		||||
 | 
			
		||||
namespace OpenWifi {
 | 
			
		||||
	class FirmwareRevisionCache : public SubSystemServer {
 | 
			
		||||
	  public:
 | 
			
		||||
		static auto instance() {
 | 
			
		||||
			static auto instance_ = new FirmwareRevisionCache;
 | 
			
		||||
			return instance_;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline int Start() override {
 | 
			
		||||
			poco_notice(Logger(), "Starting...");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline void Stop() override {
 | 
			
		||||
			poco_notice(Logger(), "Stopping...");
 | 
			
		||||
			poco_notice(Logger(), "Stopped...");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		inline bool DeviceMustUpgrade([[maybe_unused]] std::string &deviceType,
 | 
			
		||||
									  [[maybe_unused]] const std::string &firmware_string,
 | 
			
		||||
									  [[maybe_unused]] GWObjects::DefaultFirmware &Firmware) {
 | 
			
		||||
			return false;
 | 
			
		||||
			if(StorageService()->GetDefaultFirmware(deviceType,Firmware)) {
 | 
			
		||||
 | 
			
		||||
				std::string	key{ deviceType + Firmware.revision };
 | 
			
		||||
 | 
			
		||||
				if(!Cache_.has(key)) {
 | 
			
		||||
					FMSObjects::FirmwareAgeDetails	FAD;
 | 
			
		||||
					if(SDK::FMS::GetFirmwareAge(deviceType,Firmware.revision,FAD,Logger())) {
 | 
			
		||||
						Cache_.add(key,FAD);
 | 
			
		||||
					} else {
 | 
			
		||||
						//  if we cannot establish the age of the currently running firmware,
 | 
			
		||||
						//	then we assume it is too old.
 | 
			
		||||
						return true;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				auto FAD = Cache_.get(key);
 | 
			
		||||
				if(FAD->imageDate < Firmware.imageCreationDate) {
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	  private:
 | 
			
		||||
		Poco::ExpireLRUCache<std::string, FMSObjects::FirmwareAgeDetails> Cache_{
 | 
			
		||||
			512, 1200000};
 | 
			
		||||
 | 
			
		||||
		FirmwareRevisionCache() noexcept
 | 
			
		||||
			: SubSystemServer("FirmwareRevisionCache", "FWCACHE-SVR", "firmwarecache") {
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	inline auto FirmwareRevisionCache() { return FirmwareRevisionCache::instance(); }
 | 
			
		||||
 | 
			
		||||
} // namespace OpenWifi
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user