User story RICPLT-2620
[ric-app/admin.git] / src / protector-plugin / NetworkProtector.cc
1 /*
2 ==================================================================================
3
4         Copyright (c) 2018-2019 AT&T Intellectual Property.
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17 ==================================================================================
18 */
19
20
21 #include "NetworkProtector.h"
22
23
24 #include <mutex>          // std::mutex
25 #include <ctime>    // For time()
26 #include <cstdlib>  // For srand() and rand()
27
28
29 protector::protector( bool report){     
30   m_access = std::make_unique<std::mutex>();
31   report_mode_only = report;
32
33   // there is always a default policy with id 0 (never gets deleted, can only be re-configured)
34   // default values from policy constructor will be used.
35   policy_list.insert(std::pair<int, protector_policy>(0, protector_policy()));
36
37 }
38
39 // constructor that over-rides default values for policy 0
40 protector::protector( bool enforce, int window_size, int threshold, double blocking_rate, bool report){ 
41   m_access = std::make_unique<std::mutex>();
42   report_mode_only = report;
43
44   // there is always a default policy with id 0 (never gets deleted, can only be re-configured)
45   policy_list.insert(std::pair<int, protector_policy>(0, protector_policy(enforce, window_size, threshold, blocking_rate)));
46   
47 }
48
49
50 bool protector::operator()(unsigned char *msg_ref, size_t msg_size, unsigned char * buffer, size_t *buf_size ){
51   
52   bool res = true;
53   protector_policy * policy_ref;
54   
55   std::lock_guard<std::mutex> lck(*(m_access.get()));
56   
57   X2N_X2AP_PDU_t * x2ap_recv = 0;
58   asn_dec_rval_t dec_res;
59   
60   // /* Decode */
61   dec_res = asn_decode(0,ATS_ALIGNED_BASIC_PER, &asn_DEF_X2N_X2AP_PDU, (void **)&x2ap_recv, msg_ref, msg_size);
62   
63   if (dec_res.code == RC_OK){
64     /* Is this an SgNB Addition request ? */
65     mdclog_write(MDCLOG_DEBUG, "Decoded X2AP PDU successfully. Processing X2 message fields to ascertain type etc ...\n");
66     if (x2ap_recv->present == X2N_X2AP_PDU_PR_initiatingMessage){
67       if (x2ap_recv->choice.initiatingMessage->procedureCode ==  X2N_ProcedureCode_id_sgNBAdditionPreparation ){
68         if (x2ap_recv->choice.initiatingMessage->value.present ==  X2N_InitiatingMessage__value_PR_SgNBAdditionRequest){
69           mdclog_write(MDCLOG_DEBUG, "Processing X2AP SgNB Addition Request message\n");
70           res = true;
71         }
72         else{
73           mdclog_write(MDCLOG_ERR, "Error :: %s, %d:: X2AP Message (%d) not an SgNB Addition Request\n", __FILE__, __LINE__, x2ap_recv->choice.initiatingMessage->value.present);
74           res = false;
75         }
76       }
77       else{
78         mdclog_write(MDCLOG_ERR, "Error :: %s, %d:: X2AP procedure code  %ld does not match required %ld\n", __FILE__, __LINE__, x2ap_recv->choice.initiatingMessage->procedureCode, X2N_ProcedureCode_id_sgNBAdditionPreparation);
79         res = false;
80       }
81     }
82     else{
83       mdclog_write(MDCLOG_ERR, "Error :: %s, %d:: Not an X2AP initiating message. X2AP Message type %d\n", __FILE__, __LINE__, x2ap_recv->present);
84       res = false;
85     }
86   }
87   else{
88     mdclog_write(MDCLOG_ERR, "Error :: %s, %d :: Could not decode X2AP PDU of size %lu bytes\n", __FILE__, __LINE__, msg_size);
89     res = false;
90   }
91   
92   if (res){
93     
94     mdclog_write(MDCLOG_DEBUG, "Extracting SgNB Addition Request fields...");
95
96     //std::cout <<"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl;
97     //xer_fprint(stdout,  &asn_DEF_X2AP_PDU, x2ap_recv);
98     //std::cout <<"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << std::endl;
99
100     res = sgnb_req.get_fields(x2ap_recv->choice.initiatingMessage, sgnb_data);
101     if (!res){
102       mdclog_write(MDCLOG_ERR, "Error :: %s, %d :: could not get  fields from SgNB Addition Request. Reason = %s\n", __FILE__, __LINE__, sgnb_req.get_error().c_str());
103     }
104     
105     if (res){
106       mdclog_write(MDCLOG_DEBUG, "Decoded and extracted X2AP PDU data. Number of erabs = %lu\n", sgnb_data.get_list()->size());
107       mdclog_write(MDCLOG_DEBUG, "Applying admission control logic ...");
108
109       // Find if policy associated with this subscription id
110       auto it_policy = policy_list.find(sgnb_data.subscriber_profile_id);
111       
112       if (it_policy == policy_list.end()){
113         // apply default policy
114         policy_ref = & policy_list[0];
115       }
116       else{
117         policy_ref = & (it_policy->second);
118       }
119
120       net_requests ++;
121       policy_ref->_req++;       
122       policy_ref->_window_ref.get()->update_window(1);
123
124       // apply blocking probability if m_enforce
125       if ( policy_ref->_enforce && policy_ref->_window_ref.get()->net_events  > policy_ref->_threshold){
126         res = selectiveBlock(policy_ref->_block_rate);
127       }
128       else{
129         res = true;
130       }
131
132       if (!res){
133         policy_ref->_rej ++;
134         net_rejects ++;
135       }
136       
137       mdclog_write(MDCLOG_DEBUG, "Plugin decision for sgnb request = %d\n", res);
138       
139       /*
140         Generate response message if flag is set
141       */
142       
143       if(! report_mode_only){
144         // generate sgnb addition response message (ack or reject)
145         // if rejecting , we use cause = Misc and sub-cause  = om_intervention
146         mdclog_write(MDCLOG_DEBUG, "Generating X2AP response ..\n");
147         sgnb_data.cause = X2N_Cause_PR_misc;
148         sgnb_data.cause_desc = X2N_CauseMisc_om_intervention;
149         try{
150           res = sgnb_resp.encode_sgnb_addition_response(buffer, buf_size, sgnb_data, res);
151           if (! res){
152             mdclog_write(MDCLOG_ERR, "Error :: %s, %d :: could not encode SgNB Addition Response PDU. Reason = %s\n", __FILE__, __LINE__, sgnb_resp.get_error().c_str());
153           }
154         }
155         catch(std::exception &e){
156           mdclog_write(MDCLOG_ERR, "Error:: %s, %d : Caught exception %s\n", __FILE__, __LINE__, e.what());
157         }
158         
159       }
160       else{
161         res = true;
162       }
163     }
164   }
165  
166   
167   ASN_STRUCT_FREE(asn_DEF_X2N_X2AP_PDU, x2ap_recv);
168   return res;
169   
170 }
171   
172 // configure an existing policy 
173 bool protector::configure(bool enforce, int windowSize_, int threshold_, double blockRate_, int id){
174   std::lock_guard<std::mutex> lck(*(m_access.get()));
175   std::stringstream ss;
176   
177   // basic validation of  input
178   if(windowSize_ <= 0){
179     ss << "Illegal value for window size = " << windowSize_ << " when configuring policy " << std::endl;
180     error_string = ss.str();
181     return false;
182   }
183
184   if(threshold_ < 0){
185     ss << "Illegal value for trigger threshold = " << threshold_ << " when configuring policy " << std::endl;
186     error_string = ss.str();
187     return false;
188   }
189
190   if(blockRate_ < 0 || blockRate_ > 100){
191     ss << "Illegal value for blocking rate = " << blockRate_ << " when configuring policy " << std::endl;
192     error_string = ss.str();
193     return false;
194   }
195   if (id < 0){
196     ss << "Illegal value for class id  = " << id << " when configuring policy " << std::endl;
197     error_string = ss.str();
198     return false;
199   }
200                
201   // find policy
202   auto policy_it = policy_list.find(id);
203   if (policy_it == policy_list.end()){
204     mdclog_write(MDCLOG_ERR, "Error : %s, %d : No policy with id %d found for configuration\n", __FILE__, __LINE__,  id);
205     return false;
206   }
207   
208   bool res = policy_it->second._window_ref.get()->resize_window(windowSize_);
209   if (!res){
210     error_string = policy_it->second._window_ref.get()->get_error();
211     return false;
212   }
213   policy_it->second._window_size = windowSize_;
214   
215   // enforce is set globally
216   policy_it->second._enforce = enforce;
217   
218   policy_it->second._threshold=threshold_;
219   policy_it->second._block_rate=blockRate_;
220   mdclog_write(MDCLOG_DEBUG, "Configured policy with id %d with enforce=%d, window size = %d, threshold = %d, blocking rate = %f\n", id, policy_it->second._enforce, policy_it->second._window_size, policy_it->second._threshold, policy_it->second._block_rate);
221   
222   return true;
223 }
224
225 // add a policy
226 bool protector::add_policy(bool enforce, int windowSize_, int threshold_, double blockRate_, int id){
227   std::lock_guard<std::mutex> lck(*(m_access.get()));
228   std::stringstream ss;
229
230
231     // find policy
232   auto policy_it = policy_list.find(id);
233   if (policy_it != policy_list.end()){
234     ss <<"Error : " << __FILE__ << "," << __LINE__ << ": " << "Policy with id " << id << " already exists. Cannot be added" << std::endl;
235     error_string = ss.str();
236     mdclog_write(MDCLOG_ERR, "%s\n", error_string.c_str());
237     return false;
238   }
239
240   // basic validation of  input
241   if(windowSize_ <= 0){
242     ss << "Illegal value for window size = " << windowSize_ << " when adding policy " << std::endl;
243     error_string = ss.str();
244     return false;
245   }
246   if(threshold_ < 0){
247     ss << "Illegal value for trigger threshold = " << threshold_ << " when adding policy " << std::endl;
248     error_string = ss.str();
249     return false;
250   }
251
252   if(blockRate_ < 0 || blockRate_ > 100){
253     ss << "Illegal value for blocking rate = " << blockRate_ << " when adding policy " << std::endl;
254     error_string = ss.str();
255     return false;
256   }
257   if (id < 0){
258     ss << "Illegal value for class id  = " << id << " when adding policy " << std::endl;
259     error_string = ss.str();
260     return false;
261   }
262                
263
264   // create the policy
265   try{
266     policy_list.insert(std::pair<int,  protector_policy> (id, protector_policy(enforce, windowSize_, threshold_, blockRate_)));
267   }
268   catch(std::exception &e){
269     ss <<"Error : " << __FILE__ << "," << __LINE__ << ": " << "Error creating policy. Reason = " << e.what() << std::endl;
270     error_string = ss.str();
271     mdclog_write(MDCLOG_ERR, "%s\n", error_string.c_str());
272     return false;
273   }
274
275   mdclog_write(MDCLOG_DEBUG, "Added new policy with id %d with enforce=%d, window size = %d, threshold = %d, blocking rate = %f\n", id, enforce, windowSize_, threshold_, blockRate_);
276
277   return true;
278 }
279
280 // delete a policy
281 bool protector::delete_policy(int id){
282   std::lock_guard<std::mutex> lck(*(m_access.get()));
283   std::stringstream ss;
284   auto policy_it = policy_list.find(id);
285   if (policy_it == policy_list.end()){
286     ss <<"Error : " << __FILE__ << "," << __LINE__ << ": " << " No policy with id  = " << id << " found" << std::endl;
287     error_string = ss.str();
288     mdclog_write(MDCLOG_ERR, "%s\n", error_string.c_str());
289     return false;
290   }
291
292   policy_list.erase(policy_it);
293   mdclog_write(MDCLOG_DEBUG, "Deleted policy %d\n", id);
294   return true;
295 }
296
297
298 // query a policy : responsibility of caller to ensure
299 // vector is empty
300 // returns parameters of policy in the vector
301 bool protector::query_policy(int id, std::vector<double> & info){
302
303   std::lock_guard<std::mutex> lck(*(m_access.get()));
304   auto policy_it = policy_list.find(id);
305   if (policy_it == policy_list.end()){
306     return false;
307   }
308
309   info.push_back(policy_it->second._enforce);
310   info.push_back(policy_it->second._window_size);
311   info.push_back(policy_it->second._threshold);
312   info.push_back(policy_it->second._block_rate);
313   return true;
314 }
315
316 // returns requests that fall under a policy
317 // if id is -1, returns total requests
318 // if non-existent policy, returns -1
319 // counters are cumulative
320 long int protector::get_requests(int id) const {
321   if (id == -1){
322     return net_requests;
323   }
324
325   std::lock_guard<std::mutex> lck(*(m_access.get()));
326   auto policy_it = policy_list.find(id);
327   if (policy_it == policy_list.end()){
328     return -1;
329   }
330   else{
331     return policy_it->second._req;
332   }
333   
334 }
335
336 // returns requests that fall under a policy
337 // if id is -1 , returns total rejects
338 // if non-existent policy, returns -1
339 // counters are cumulative
340 long int protector::get_rejects(int id) const {
341
342   if (id == -1){
343     return net_rejects;
344   }
345
346   std::lock_guard<std::mutex> lck(*(m_access.get()));
347   auto policy_it = policy_list.find(id);
348   if (policy_it == policy_list.end()){
349     return -1;
350   }
351   else{
352     return policy_it->second._rej;
353   }
354
355 }
356
357 // returns list of active policies in
358 // supplied vector (policy is indexed by subscriber profile id)
359 void protector::get_active_policies(std::vector<int> & active){
360   std::lock_guard<std::mutex> lck(*(m_access.get()));
361   for (const auto &e : policy_list){
362     active.push_back(e.first);
363   }
364 }
365
366 // returns true if policy active else false
367 bool protector::is_active(int id){
368   auto policy_it = policy_list.find(id);
369   if (policy_it == policy_list.end()){
370     return false;
371   }
372   else{
373     return true;
374   }
375 }
376
377 // clears counters for all policies 
378 void protector::clear()
379 {
380
381   std::lock_guard<std::mutex> lck(*(m_access.get()));
382   
383   for(auto &e : policy_list){
384     e.second._window_ref.get()->clear();
385     e.second._counter = 0;
386     e.second._req = 0;
387     e.second._rej = 0;
388   }
389
390   net_requests = 0;
391   net_rejects  = 0;
392 }
393
394
395 bool protector::selectiveBlock(double block_rate) 
396 {
397   unsigned int num = (rand() % 100) + 1;    
398   if (num > block_rate)  //not blocking
399     return true;
400   else                    //blocking
401     return false;
402 }
403