proberesponseMitigation.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. /**
  2. * This module is part of the TraCINg CIDS and introduces mitigation methodologies
  3. * against the probe response attack by analyzing statistical anomalies. On identification
  4. * of an attack, countermeasures are introduced which modify the way attack events get
  5. * presented to the user (see postHandler.js).
  6. */
  7. // load node modules
  8. var Chance = require("chance");
  9. var fs = require('fs');
  10. var path = require('path');
  11. var Map = require("collections/map");
  12. var Set = require("collections/set");
  13. var es = require("event-stream");
  14. var debugInstance = 0;
  15. var Debug = function(){
  16. var output = "";
  17. var instance = debugInstance++;
  18. this.get = function(){
  19. return output;
  20. };
  21. this.add = function(text){
  22. text = "Proberesponse["+instance+"]: " + text;
  23. console.log(text);
  24. output += text + "\n";
  25. };
  26. };
  27. var debug = new Debug();
  28. var chance = new Chance();
  29. // TBA: duration of one time slot in a window
  30. // 10 fits for destination port, 6ms
  31. var TIMESLOT_SECONDS = 60;
  32. // TBA: amount of "TIMESLOT_SECONDS" for a time window to save, total seconds = WINDOW_MAX_TIMESLOTS_RATIO * TIMESLOT_SECONDS
  33. var WINDOW_MAX_TIMESLOTS_RATIO = 60;
  34. // Attack count for every source IP address: { ip_source : { ip_destination : count}, ... }
  35. var ip_source_dest_count = {};
  36. // List of ip_source_dest_count for every slot in the time window [ ip_count1, ip_count2, ... ]
  37. // This is a round robbin buffer where the oldest entries get overwritten by the newest.
  38. var window_ip_source_dest = [];
  39. // TBA; Ratio of (attacks/unique monitors) for the whole time window which triggers an alarm.
  40. // There should be a save margin upwards to a "normal" ratio.
  41. // ratio can not be lower than 1 (at minimum 1 attack per monitor)
  42. var THRESHOLD_ATTACKS_MONITOR_RATIO = 3; // depends on TIMESLOT_SECONDS and WINDOW_MAX_TIMESLOTS_RATIO and amount of events/s
  43. // >>> TBA: Alarm thresholds for the whole time window WINDOW_MAX_TIMESLOTS_RATIO
  44. // depents on TIMESLOT_SECONDS * WINDOW_MAX_TIMESLOTS_RATIO
  45. // TBA: Amount of unique monitors per source (adress:monitors -> 1:n -> check n)
  46. var THRESHOLD_MONITORS_PER_SOURCE_IP = 50000; // ~20% of all monitor nodes.
  47. // TBA: Amount of unique sources for one monitors (monitor:address -> 1:n -> check n)
  48. var THRESHOLD_SOURCE_IPS_PER_MONITOR = 100;
  49. // TBA: Amount of attacks from one source to a different monitors (monitor:address -> 1:1 -> check amount of attacks)
  50. var THRESHOLD_SOURCE_IPS_MONITOR_ATTACKS = 6000;
  51. // TBA: Amount of total attacks for one monitors (monitor:attacks -> 1:n -> check n)
  52. var THRESHOLD_ATTACKS_PER_MONITOR = 15000;
  53. // >>> Alarm thresholds for destination port statistics
  54. // TBA: amount of "TIMESLOT_SECONDS" for a time windowto save, total seconds = WINDOW_MAX_TIMESLOTS_RATIO * TIMESLOT_SECONDS
  55. var WINDOW_MAX_TIMESLOTS_PORT_DST = 60;
  56. // Attack count for every destination port: { port : count, ...}
  57. var port_dest_count = new Map();
  58. // List of dicts of ports [ port_dest_count1, port_dest_count2, ... ]
  59. // This is a round robbin buffer where the oldest entries get overwritten by the newest.
  60. var window_port_dest = [];
  61. // TBA: max amount of attacked destination ports in TIMESLOT_SECONDS*WINDOW_MAX_TIMESLOTS_PORT_DST seconds
  62. var THRESHOLD_PORT_DEST_ATTACKED = 7000;
  63. // TBA: Ban duration AFTTER source count falls below SOURCE_IP_THRESHOLD
  64. var SOURCE_IP_BAN_MINUTES = 1;
  65. // { ip : ban_time_minutes }
  66. var banned_ips = {}
  67. // Sampling of events: just take 0% -> 100% of all events
  68. var sampling_percent = 100;
  69. var sampling_active = false;
  70. // noise information
  71. var events_dshield_total = 0;
  72. var events_dshield_added = 0;
  73. var events_external_total = 0;
  74. var events_external_added = 0;
  75. // handle mitigation if activated (does NOT influence metrics -> those are further calculated but external events are ignored)
  76. var is_active = true;
  77. var dshield_entries = [];
  78. // 24 events per second
  79. var noise_interval_ms = 41;
  80. //var noise_interval_ms = 7;
  81. var FILE_STATS = "attack_stats.txt";
  82. var dshield_read_linenr = 0;
  83. var init_started = false;
  84. function init_stats_window() {
  85. // Amount of events for: TIMESLOT_SECONDS * WINDOW_MAX_TIMESLOTS_RATIO
  86. // 27 events/s = 97200 events/window (60*60 seconds) = ~ ratio 5.8
  87. //var attacks_init_win = 97200;
  88. var attacks_init_win = Math.round(1000/noise_interval_ms)*TIMESLOT_SECONDS*WINDOW_MAX_TIMESLOTS_RATIO;
  89. var attacks_per_slot = Math.round(attacks_init_win / WINDOW_MAX_TIMESLOTS_RATIO);
  90. var port_count = new Map();
  91. debug.add("amount of dshield entries: "+ dshield_entries.length);
  92. debug.add("initiating window ip: "+WINDOW_MAX_TIMESLOTS_RATIO+"*"+TIMESLOT_SECONDS+
  93. " sec, window port: "+WINDOW_MAX_TIMESLOTS_PORT_DST+"*"+TIMESLOT_SECONDS+" sec");
  94. var cnt = 0;
  95. for(var x=0; x < WINDOW_MAX_TIMESLOTS_RATIO; ++x){
  96. //window_ip_source_dest[x] = {"0.0.0.0" : {"0.0.0.0" : 0}};
  97. window_ip_source_dest[x] = {};
  98. window_port_dest[x] = new Map();
  99. for (var y=0; y < attacks_per_slot; ++y) {
  100. entry = dshield_entries.pop();
  101. //entry = dshield_entries.shift();
  102. //l = [ipsrc, ipmon, chance.integer({min:0, max:65535})];
  103. l = [entry[0], entry[2], entry[3]];
  104. if (l in window_ip_source_dest[x]) {
  105. window_ip_source_dest[x][l] += 1;
  106. //debug.add("adding, total: "+ window_ip_source_dest[x][l]);
  107. }
  108. else
  109. window_ip_source_dest[x][l] = 1;
  110. if (port_count.get(entry[3]) == undefined) {
  111. port_count.set(entry[3], 1);
  112. } else {
  113. port_count.set(entry[3], port_count.get(entry[3]) + 1);
  114. }
  115. if (window_port_dest[x].get(entry[3]) == undefined) {
  116. window_port_dest[x].set(entry[3], 1);
  117. } else {
  118. window_port_dest[x].set(entry[3], window_port_dest[x].get(entry[3]) + 1);
  119. }
  120. cnt += 1;
  121. }
  122. }
  123. var port_cnt_check = 0;
  124. port_count.forEach( function merge(count, port, mapObj) {
  125. port_cnt_check += count;
  126. });
  127. debug.add("unique ports: "+ port_count.length);
  128. debug.add("finished initiating, count: "+attacks_init_win+"="+port_cnt_check+"=~"+cnt);
  129. // add noise using DShield data
  130. var noise_ref = setInterval(add_entry_dshield, noise_interval_ms);
  131. // mitigation check-logic
  132. //var check_attack_status_ref = setInterval(check_attack_status, TIMESLOT_SECONDS * 1000);
  133. }
  134. var FILE_DSHIELD_EVENTS = path.join(__dirname, 'dshield_report.csv');
  135. debug.add("reading DShield events from "+FILE_DSHIELD_EVENTS+", standy by");
  136. s = fs.createReadStream(FILE_DSHIELD_EVENTS)
  137. .pipe(es.split())
  138. .pipe(es.mapSync(function(line){
  139. // pause the readstream
  140. s.pause();
  141. dshield_read_linenr += 1;
  142. (function(){
  143. var columns = line.toString().split("\t", -1);
  144. if (columns.length == 6) {
  145. try {
  146. // 3 4 [8 -> IP] 5 8: ip_src port_src ip_dst port_dst id
  147. // 0 1 [5 -> IP] 2 5: ip_src port_src ip_dst port_dst id
  148. // convert monitor ID to an IP address
  149. a = parseInt(columns[5].substring(2, 4), 16);
  150. b = parseInt(columns[5].substring(4, 6), 16);
  151. c = parseInt(columns[5].substring(6, 8), 16);
  152. d = parseInt(columns[5].substring(8, 10), 16);
  153. //debug.add("IP is: " + a+"."+b+"."+c+"."+d);
  154. dshield_entries.push([columns[0],
  155. parseInt(columns[1]),
  156. a+"."+b+"."+c+"."+d,
  157. columns[2],
  158. columns[5]]);
  159. } catch (err) { /* ignore malformed lines */ }
  160. }
  161. // process line here and call s.resume() when rdy
  162. //logMemoryUsage(lineNr);
  163. if (dshield_read_linenr % 200000 == 0) debug.add("read DShield events: "+dshield_read_linenr);
  164. if (!init_started && dshield_read_linenr > 2000000) {
  165. s.destroy(); init_stats_window(); init_started = true;
  166. }
  167. // resume the readstream
  168. s.resume();
  169. })();
  170. })
  171. .on('error', function(){ debug.add('Error while reading DShield report'); })
  172. .on('end', function(){ debug.add('Read entire DShield report') })
  173. );
  174. function update_slot(monitor_id, ip_source, ip_dest, port_dest) {
  175. //debug.add("updating window: "+monitor_id+" / "+ip_source+" / "+ip_dest+" / "+port_dest);
  176. //if (!is_active) { return; }
  177. /*
  178. if (!(ip_source === undefined) && (ip_source in banned_ips)) {
  179. // don't count banned IP addresses
  180. //debug.add("won't count banned ip: "+ ip_source)
  181. return;
  182. }
  183. */
  184. //l = [ip_source, ip_dest, port_dest];
  185. l = [ip_source, monitor_id, port_dest];
  186. if (ip_source_dest_count[l] == undefined) { ip_source_dest_count[l] = 0; }
  187. ip_source_dest_count[l] += 1;
  188. if (port_dest_count.get(port_dest) == undefined) {
  189. port_dest_count.set(port_dest, 1);
  190. } else {
  191. port_dest_count.set(port_dest, port_dest_count.get(port_dest) + 1);
  192. }
  193. }
  194. // clear old attack stats file
  195. fs.writeFile(FILE_STATS, "ratio\tports\talarm_ratio\talarm_port\tevents_dshield_added\tevents_dshield_total\tevents_external_added\tevents_extern_total\n", function (err) { if (err) throw err; });
  196. // update ban status of any source IP address
  197. function check_attack_status() {
  198. //debug.add("checking attack status");
  199. // >>> Update window with new slot
  200. var time_seconds_now = Math.round(new Date().getTime() / 1000);
  201. var time_minutes_now = Math.round(time_seconds_now / 60);
  202. var timeslot_ratio = Math.round(time_seconds_now / TIMESLOT_SECONDS) % WINDOW_MAX_TIMESLOTS_RATIO;
  203. // window_ip_source_dest[x] = [a, b, c]
  204. window_ip_source_dest[timeslot_ratio] = ip_source_dest_count;
  205. // reset count for next slot
  206. ip_source_dest_count = {};
  207. var timeslot_ports = Math.round(time_seconds_now / TIMESLOT_SECONDS) % WINDOW_MAX_TIMESLOTS_PORT_DST;
  208. window_port_dest[timeslot_ports] = port_dest_count;
  209. // reset count for next slot
  210. port_dest_count = new Map();
  211. var alarm_ratio_triggered = false;
  212. var alarm_portdest_triggered = false;
  213. var alarm_attacks_per_monitor_triggered = false;
  214. var sampling = 100;
  215. // >>> check threshold: amount of unique attacked destination ports
  216. //debug.add("counting ports...");
  217. var port_dest_count_merged = new Map();
  218. var top_ports = new Set();
  219. alarm_portdest_triggered = false;
  220. /**/
  221. // MITIGATION block start: comment in to activate mitigation based on port frequency if needed
  222. for (var timeslot in window_port_dest) {
  223. window_port_dest[timeslot].forEach( function merge(count, port, mapObj) {
  224. if (port_dest_count_merged.get(port) == undefined) {
  225. port_dest_count_merged.set(port, count);
  226. //debug.add("setting port: "+port+" = count "+count);
  227. } else {
  228. port_dest_count_merged.set(port, port_dest_count_merged.get(port) + count);
  229. }
  230. });
  231. }
  232. // MITIGATION block end
  233. /**/
  234. // sort keys by value descending
  235. var top_ports_all = port_dest_count_merged.keys().sort(function(a,b){
  236. return port_dest_count_merged.get(b) - port_dest_count_merged.get(a)}
  237. );
  238. for (var x=0; x < 2 && x < top_ports_all.length; ++x) {
  239. top_ports.add(top_ports_all[x]);
  240. }
  241. debug.add("port: slot="+timeslot_ports+", unique dest ports: "+port_dest_count_merged.length);
  242. //", top 3 ports="+top_ports_all[0]+","+top_ports_all[1]+","+top_ports_all[2]);
  243. if (port_dest_count_merged.length > THRESHOLD_PORT_DEST_ATTACKED) {
  244. //debug.add("PRA-WARNING: too many unique destination ports: " +
  245. //" ("+ port_dest_count_merged.length +" > "+ THRESHOLD_PORT_DEST_ATTACKED +")");
  246. sampling = ((THRESHOLD_PORT_DEST_ATTACKED / port_dest_count_merged.length) * 100)
  247. alarm_portdest_triggered = true;
  248. }
  249. // amount of attacks for one monitor {id : count}
  250. var attacks_per_monitor = new Map();
  251. var attacks_total = 0;
  252. //debug.add("counting monitors...");
  253. var skipped_top_ports = 0;
  254. /*
  255. // MITIGATION block start: comment in to activate mitigation based on attack/monitor ratio if needed
  256. for (var slot_search in window_ip_source_dest) {
  257. for (var ipsrc_ipdst_portdst in window_ip_source_dest[slot_search]) {
  258. ipsrc_ipdst_portdst_split = ipsrc_ipdst_portdst.split(",");
  259. // TODO: activate for port normalizing (optional)
  260. //if (top_ports.contains(ipsrc_ipdst_portdst_split[2])) {
  261. // // skip top x ports
  262. // //debug.add("skipping port: "+ipsrc_ipdst_portdst_split[2]);
  263. // skipped_top_ports += 1;
  264. // continue;
  265. //}
  266. ip_dst = ipsrc_ipdst_portdst_split[1];
  267. count = window_ip_source_dest[slot_search][ipsrc_ipdst_portdst];
  268. //if (count > 100) {debug.add(ip_dst+"="+count);}
  269. if (attacks_per_monitor.get(ip_dst) == undefined) {
  270. //debug.add(ip_dst);
  271. attacks_per_monitor.set(ip_dst, count);
  272. } else {
  273. attacks_per_monitor.set(ip_dst, attacks_per_monitor.get(ip_dst) + count);
  274. }
  275. attacks_total += count;
  276. }
  277. }
  278. // MITIGATION block end
  279. */
  280. //debug.add("output...");
  281. // >>> check treshold:
  282. // ratio -> attacks / unique monitors
  283. var unique_monitors_total = attacks_per_monitor.length;
  284. var ratio = attacks_total / unique_monitors_total;
  285. var alarm_ratio_triggered = false;
  286. /**/
  287. if (isNaN(ratio)) {
  288. // ratio 0 means: no attacks and/or no monitors -> nothing happened -> no attack
  289. ratio = 0;
  290. }
  291. /*
  292. if (is_active) {
  293. debug.add("mon: slot="+timeslot_ratio+", attacks="+attacks_total+", mon="+unique_monitors_total+
  294. ", ratio="+ratio+", top removed="+skipped_top_ports);
  295. }*/
  296. if (ratio != 0 && ratio <= THRESHOLD_ATTACKS_MONITOR_RATIO) {
  297. //debug.add("PRA-WARNING: Ratio attacks/monitors is too low: "+ratio+" <= "+THRESHOLD_ATTACKS_MONITOR_RATIO);
  298. // ratio is at minimum 1, so minimum is: 1/(threshold_ratio) -> set minimum to 0/(threshold_ratio-1)
  299. sampling_ratio = (((ratio-1)/(THRESHOLD_ATTACKS_MONITOR_RATIO-1)) * 100);
  300. if (sampling_ratio < sampling) {
  301. sampling = sampling_ratio;
  302. }
  303. alarm_ratio_triggered = true;
  304. }
  305. alarm_attacks_per_monitor_triggered = false;
  306. /*
  307. // >>> check threshold: attacks per monitor
  308. attacks_per_monitor.forEach(function each(amount, monitor, mapObj) {
  309. //debug.add(monitor+"="+amount);
  310. if (amount > THRESHOLD_ATTACKS_PER_MONITOR) {
  311. //debug.add("PRA-WARNING: Monitor "+monitor+" is attacked too many times("+
  312. //amount+" > "+THRESHOLD_ATTACKS_PER_MONITOR+")");
  313. sampling_attack_per_mon = ((THRESHOLD_ATTACKS_PER_MONITOR/amount) * 100);
  314. if (sampling_attack_per_mon < sampling) {
  315. sampling = sampling_attack_per_mon;
  316. }
  317. alarm_attacks_per_monitor_triggered = true;
  318. }
  319. });
  320. */
  321. if (is_active) {
  322. sampling_percent = sampling;
  323. if ((!alarm_ratio_triggered) && (!alarm_portdest_triggered) && (!alarm_attacks_per_monitor_triggered)) {
  324. sampling_percent = 100;
  325. sampling_active = false;
  326. } else if (sampling_percent < 100) {
  327. debug.add("sampling="+sampling_percent+"%, ratio="+alarm_ratio_triggered+
  328. ", port_dest="+alarm_portdest_triggered+", atttacks_per_mon="+alarm_attacks_per_monitor_triggered+
  329. ", noise added/total="+events_dshield_added+"/"+events_dshield_total);
  330. sampling_active = true;
  331. }
  332. } else {
  333. ratio = 0;
  334. }
  335. // unban IPs which ban timeout exceeded
  336. /*
  337. for (var ip in banned_ips) {
  338. if ( (time_minutes_now - banned_ips[ip]) >= SOURCE_IP_BAN_MINUTES) {
  339. debug.add("unbanning ip: "+ ip +" ("+ (time_minutes_now - banned_ips[ip]) +" minutes passed)");
  340. delete banned_ips[ip];
  341. }
  342. }
  343. */
  344. fs.appendFile(FILE_STATS, ratio+"\t"+port_dest_count_merged.length+"\t"+alarm_ratio_triggered+"\t"
  345. +alarm_portdest_triggered+"\t"+events_dshield_added+"\t"
  346. +events_dshield_total+"\t"+events_external_added+"\t"+events_external_total+"\n", function (err) {});
  347. //debug.add("finished checking");
  348. }
  349. // this is exclusively used by addNoiseEvent()
  350. var data = {
  351. sensorname: null,
  352. sensortype: "RandomType",
  353. src: {
  354. ip: null,
  355. port: null,
  356. ll: [0,0],
  357. country: "",
  358. cc: 0,
  359. city: ""
  360. },
  361. dst: {
  362. ip: null,
  363. port: null,
  364. ll: [0,0],
  365. country: "",
  366. cc: 0,
  367. city: ""
  368. },
  369. type: 11,
  370. log: null,
  371. md5sum: "0123456789",
  372. date: new Date(),
  373. authorized: false
  374. };
  375. function addNoiseEvent(ip_src, port_src, ip_dst, port_dst, monitorname, doEmit) {
  376. if (typeof io == 'undefined') {
  377. //debug.add("no io, no emit -> I quit");
  378. return;
  379. }
  380. //debug.add("adding, name/ip/port: " + data.sensorname + " / " + data.src.ip + " / " +
  381. // data.dst.ip + " / " + data.src.port + " / " + data.dst.port);
  382. /*
  383. // not needed for PRA
  384. ipa = lookUpIp(ip_src);
  385. if(ipa){
  386. data.src["ll"] = [ipa.longitude, ipa.latitude];
  387. data.src["country"] = ipa.country_name;
  388. data.src["cc"] = ipa.country_code;
  389. data.src["city"] = ipa.city || "";
  390. }
  391. ipd = lookUpIp(ip_dst);
  392. if(ipd){
  393. data.dst["ll"] = [ipd.longitude, ipd.latitude];
  394. data.dst["country"] = ipd.country_name;
  395. data.dst["cc"] = ipd.country_code;
  396. data.dst["city"] = ipd.city || "";
  397. }
  398. */
  399. //debug.add("> " + chance.integer({min: 0, max: 65535}));
  400. //debug.add("> " + chance.ip());
  401. //debug.add("Sending random entry: " + data["src"]["ip"]);
  402. update_slot(monitorname, ip_src, ip_dst, port_dst);
  403. // do emit data to client if sampled
  404. if (doEmit) {
  405. data.src.ip = ip_src;
  406. data.src.port = port_src;
  407. data.dst.ip = ip_dst;
  408. data.dst.port = port_dst;
  409. data.sensorname = monitorname;
  410. io.sockets.emit("markIncident", data);
  411. }
  412. }
  413. // add artificial noise to the report
  414. function add_entry_random() {
  415. addNoiseEvent(chance.ip(),
  416. chance.integer({min: 0, max: 65535}),
  417. chance.ip(),
  418. chance.integer({min: 0, max: 65535}),
  419. "RandomMonitor_" + chance.integer({min: 0, max: 100}),
  420. true
  421. );
  422. }
  423. function add_entry_dshield() {
  424. e = dshield_entries.pop();
  425. //e = dshield_entries.shift();
  426. //debug.add("adding noise using dshield data: "+e);
  427. // we can not differentiate between PRA and non-PRA events so we have to sample both
  428. // accept entry if: sampling not active (sample 100%) or random value tells us so
  429. // add noise if: attack mitigation is not active, mitigation not triggered or random tells us to
  430. sample_entry = (!is_active || !sampling_active || chance.floating({min: 1, max: 100}) <= sampling_percent);
  431. try {
  432. addNoiseEvent(e[0], e[1], e[2], e[3], e[4], sample_entry);
  433. if (sample_entry) {
  434. events_dshield_added += 1;
  435. }
  436. events_dshield_total += 1;
  437. }
  438. catch (err) {
  439. // no events left
  440. }
  441. }
  442. function applyMitigation(ip_src, ip_dst, port_dst, monitor_id) {
  443. // praMitigation.applyMitigation(items[i].src.ip, items[i].dst.ip, items[i].dst.port
  444. events_external_total += 1;
  445. if (!is_active) {
  446. return false;
  447. } else {
  448. update_slot(monitor_id, ip_src, ip_dst, port_dst);
  449. // allow entry if sampling is not active or random tells us to
  450. is_sampled = (!sampling_active || chance.floating({min: 1, max: 100}) > sampling_percent);
  451. if (is_sampled) {
  452. events_external_added += 1;
  453. }
  454. return !is_sampled;
  455. }
  456. }
  457. function setActive(activeState) {
  458. debug.add("setting new PRA mitigation active state: " + activeState);
  459. is_active = activeState;
  460. }
  461. module.exports = {
  462. //init: init,
  463. setActive: setActive,
  464. applyMitigation: applyMitigation
  465. };