proberesponseMitigation.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  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. if (!init_started && dshield_read_linenr > 3000000) {
  166. //if (!init_started && dshield_read_linenr > 5000000) {
  167. //if (!init_started && dshield_read_linenr > 7000000) {
  168. //if (!init_started && dshield_read_linenr > 6000000) {
  169. //if (!init_started && dshield_read_linenr > 9000000) {
  170. //if (!init_started && dshield_read_linenr > 12000000) {
  171. s.destroy(); init_stats_window(); init_started = true;
  172. }
  173. // resume the readstream
  174. s.resume();
  175. })();
  176. })
  177. .on('error', function(){ debug.add('Error while reading DShield report'); })
  178. .on('end', function(){ debug.add('Read entire DShield report') })
  179. );
  180. function update_slot(monitor_id, ip_source, ip_dest, port_dest) {
  181. //debug.add("updating window: "+monitor_id+" / "+ip_source+" / "+ip_dest+" / "+port_dest);
  182. //if (!is_active) { return; }
  183. /*
  184. if (!(ip_source === undefined) && (ip_source in banned_ips)) {
  185. // don't count banned IP addresses
  186. //debug.add("won't count banned ip: "+ ip_source)
  187. return;
  188. }
  189. */
  190. //l = [ip_source, ip_dest, port_dest];
  191. l = [ip_source, monitor_id, port_dest];
  192. if (ip_source_dest_count[l] == undefined) { ip_source_dest_count[l] = 0; }
  193. ip_source_dest_count[l] += 1;
  194. if (port_dest_count.get(port_dest) == undefined) {
  195. port_dest_count.set(port_dest, 1);
  196. } else {
  197. port_dest_count.set(port_dest, port_dest_count.get(port_dest) + 1);
  198. }
  199. }
  200. // clear old attack stats file
  201. 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; });
  202. // update ban status of any source IP address
  203. function check_attack_status() {
  204. //debug.add("checking attack status");
  205. // >>> Update window with new slot
  206. var time_seconds_now = Math.round(new Date().getTime() / 1000);
  207. var time_minutes_now = Math.round(time_seconds_now / 60);
  208. var timeslot_ratio = Math.round(time_seconds_now / TIMESLOT_SECONDS) % WINDOW_MAX_TIMESLOTS_RATIO;
  209. // window_ip_source_dest[x] = [a, b, c]
  210. window_ip_source_dest[timeslot_ratio] = ip_source_dest_count;
  211. // reset count for next slot
  212. ip_source_dest_count = {};
  213. var timeslot_ports = Math.round(time_seconds_now / TIMESLOT_SECONDS) % WINDOW_MAX_TIMESLOTS_PORT_DST;
  214. window_port_dest[timeslot_ports] = port_dest_count;
  215. // reset count for next slot
  216. port_dest_count = new Map();
  217. var alarm_ratio_triggered = false;
  218. var alarm_portdest_triggered = false;
  219. var alarm_attacks_per_monitor_triggered = false;
  220. var sampling = 100;
  221. // >>> check threshold: amount of unique attacked destination ports
  222. //debug.add("counting ports...");
  223. var port_dest_count_merged = new Map();
  224. var top_ports = new Set();
  225. alarm_portdest_triggered = false;
  226. /**/
  227. // TODO: activate mitigation based on port frequency if needed
  228. for (var timeslot in window_port_dest) {
  229. window_port_dest[timeslot].forEach( function merge(count, port, mapObj) {
  230. if (port_dest_count_merged.get(port) == undefined) {
  231. port_dest_count_merged.set(port, count);
  232. //debug.add("setting port: "+port+" = count "+count);
  233. } else {
  234. port_dest_count_merged.set(port, port_dest_count_merged.get(port) + count);
  235. }
  236. });
  237. }
  238. // sort keys by value descending
  239. var top_ports_all = port_dest_count_merged.keys().sort(function(a,b){
  240. return port_dest_count_merged.get(b) - port_dest_count_merged.get(a)}
  241. );
  242. for (var x=0; x < 2 && x < top_ports_all.length; ++x) {
  243. top_ports.add(top_ports_all[x]);
  244. }
  245. debug.add("port: slot="+timeslot_ports+", unique dest ports: "+port_dest_count_merged.length);
  246. //", top 3 ports="+top_ports_all[0]+","+top_ports_all[1]+","+top_ports_all[2]);
  247. if (port_dest_count_merged.length > THRESHOLD_PORT_DEST_ATTACKED) {
  248. //debug.add("PRA-WARNING: too many unique destination ports: " +
  249. //" ("+ port_dest_count_merged.length +" > "+ THRESHOLD_PORT_DEST_ATTACKED +")");
  250. sampling = ((THRESHOLD_PORT_DEST_ATTACKED / port_dest_count_merged.length) * 100)
  251. alarm_portdest_triggered = true;
  252. }
  253. /**/
  254. // amount of attacks for one monitor {id : count}
  255. var attacks_per_monitor = new Map();
  256. var attacks_total = 0;
  257. //debug.add("counting monitors...");
  258. var skipped_top_ports = 0;
  259. /*
  260. // TODO: activate ratio mitigation if needed
  261. for (var slot_search in window_ip_source_dest) {
  262. for (var ipsrc_ipdst_portdst in window_ip_source_dest[slot_search]) {
  263. ipsrc_ipdst_portdst_split = ipsrc_ipdst_portdst.split(",");
  264. // TODO: activate for port normalizing (optional)
  265. //if (top_ports.contains(ipsrc_ipdst_portdst_split[2])) {
  266. // // skip top x ports
  267. // //debug.add("skipping port: "+ipsrc_ipdst_portdst_split[2]);
  268. // skipped_top_ports += 1;
  269. // continue;
  270. //}
  271. ip_dst = ipsrc_ipdst_portdst_split[1];
  272. count = window_ip_source_dest[slot_search][ipsrc_ipdst_portdst];
  273. //if (count > 100) {debug.add(ip_dst+"="+count);}
  274. if (attacks_per_monitor.get(ip_dst) == undefined) {
  275. //debug.add(ip_dst);
  276. attacks_per_monitor.set(ip_dst, count);
  277. } else {
  278. attacks_per_monitor.set(ip_dst, attacks_per_monitor.get(ip_dst) + count);
  279. }
  280. attacks_total += count;
  281. }
  282. }
  283. */
  284. //debug.add("output...");
  285. // >>> check treshold:
  286. // ratio -> attacks / unique monitors
  287. var unique_monitors_total = attacks_per_monitor.length;
  288. var ratio = attacks_total / unique_monitors_total;
  289. var alarm_ratio_triggered = false;
  290. /**/
  291. if (isNaN(ratio)) {
  292. // ratio 0 means: no attacks and/or no monitors -> nothing happened -> no attack
  293. ratio = 0;
  294. }
  295. /*
  296. if (is_active) {
  297. debug.add("mon: slot="+timeslot_ratio+", attacks="+attacks_total+", mon="+unique_monitors_total+
  298. ", ratio="+ratio+", top removed="+skipped_top_ports);
  299. }*/
  300. if (ratio != 0 && ratio <= THRESHOLD_ATTACKS_MONITOR_RATIO) {
  301. //debug.add("PRA-WARNING: Ratio attacks/monitors is too low: "+ratio+" <= "+THRESHOLD_ATTACKS_MONITOR_RATIO);
  302. // ratio is at minimum 1, so minimum is: 1/(threshold_ratio) -> set minimum to 0/(threshold_ratio-1)
  303. sampling_ratio = (((ratio-1)/(THRESHOLD_ATTACKS_MONITOR_RATIO-1)) * 100);
  304. if (sampling_ratio < sampling) {
  305. sampling = sampling_ratio;
  306. }
  307. alarm_ratio_triggered = true;
  308. }
  309. /**/
  310. alarm_attacks_per_monitor_triggered = false;
  311. /*
  312. // >>> check threshold: attacks per monitor
  313. attacks_per_monitor.forEach(function each(amount, monitor, mapObj) {
  314. //debug.add(monitor+"="+amount);
  315. if (amount > THRESHOLD_ATTACKS_PER_MONITOR) {
  316. //debug.add("PRA-WARNING: Monitor "+monitor+" is attacked too many times("+
  317. //amount+" > "+THRESHOLD_ATTACKS_PER_MONITOR+")");
  318. sampling_attack_per_mon = ((THRESHOLD_ATTACKS_PER_MONITOR/amount) * 100);
  319. if (sampling_attack_per_mon < sampling) {
  320. sampling = sampling_attack_per_mon;
  321. }
  322. alarm_attacks_per_monitor_triggered = true;
  323. }
  324. });
  325. */
  326. if (is_active) {
  327. sampling_percent = sampling;
  328. if ((!alarm_ratio_triggered) && (!alarm_portdest_triggered) && (!alarm_attacks_per_monitor_triggered)) {
  329. sampling_percent = 100;
  330. sampling_active = false;
  331. } else if (sampling_percent < 100) {
  332. debug.add("sampling="+sampling_percent+"%, ratio="+alarm_ratio_triggered+
  333. ", port_dest="+alarm_portdest_triggered+", atttacks_per_mon="+alarm_attacks_per_monitor_triggered+
  334. ", noise added/total="+events_dshield_added+"/"+events_dshield_total);
  335. sampling_active = true;
  336. }
  337. } else {
  338. ratio = 0;
  339. }
  340. // unban IPs which ban timeout exceeded
  341. /*
  342. for (var ip in banned_ips) {
  343. if ( (time_minutes_now - banned_ips[ip]) >= SOURCE_IP_BAN_MINUTES) {
  344. debug.add("unbanning ip: "+ ip +" ("+ (time_minutes_now - banned_ips[ip]) +" minutes passed)");
  345. delete banned_ips[ip];
  346. }
  347. }
  348. */
  349. fs.appendFile(FILE_STATS, ratio+"\t"+port_dest_count_merged.length+"\t"+alarm_ratio_triggered+"\t"
  350. +alarm_portdest_triggered+"\t"+events_dshield_added+"\t"
  351. +events_dshield_total+"\t"+events_external_added+"\t"+events_external_total+"\n", function (err) {});
  352. //debug.add("finished checking");
  353. }
  354. // this is exclusively used by addNoiseEvent()
  355. var data = {
  356. sensorname: null,
  357. sensortype: "RandomType",
  358. src: {
  359. ip: null,
  360. port: null,
  361. ll: [0,0],
  362. country: "",
  363. cc: 0,
  364. city: ""
  365. },
  366. dst: {
  367. ip: null,
  368. port: null,
  369. ll: [0,0],
  370. country: "",
  371. cc: 0,
  372. city: ""
  373. },
  374. type: 11,
  375. log: null,
  376. md5sum: "0123456789",
  377. date: new Date(),
  378. authorized: false
  379. };
  380. function addNoiseEvent(ip_src, port_src, ip_dst, port_dst, monitorname, doEmit) {
  381. if (typeof io == 'undefined') {
  382. //debug.add("no io, no emit -> I quit");
  383. return;
  384. }
  385. //debug.add("adding, name/ip/port: " + data.sensorname + " / " + data.src.ip + " / " +
  386. // data.dst.ip + " / " + data.src.port + " / " + data.dst.port);
  387. /*
  388. // not needed for PRA
  389. ipa = lookUpIp(ip_src);
  390. if(ipa){
  391. data.src["ll"] = [ipa.longitude, ipa.latitude];
  392. data.src["country"] = ipa.country_name;
  393. data.src["cc"] = ipa.country_code;
  394. data.src["city"] = ipa.city || "";
  395. }
  396. ipd = lookUpIp(ip_dst);
  397. if(ipd){
  398. data.dst["ll"] = [ipd.longitude, ipd.latitude];
  399. data.dst["country"] = ipd.country_name;
  400. data.dst["cc"] = ipd.country_code;
  401. data.dst["city"] = ipd.city || "";
  402. }
  403. */
  404. //debug.add("> " + chance.integer({min: 0, max: 65535}));
  405. //debug.add("> " + chance.ip());
  406. //debug.add("Sending random entry: " + data["src"]["ip"]);
  407. update_slot(monitorname, ip_src, ip_dst, port_dst);
  408. // do emit data to client if sampled
  409. if (doEmit) {
  410. data.src.ip = ip_src;
  411. data.src.port = port_src;
  412. data.dst.ip = ip_dst;
  413. data.dst.port = port_dst;
  414. data.sensorname = monitorname;
  415. io.sockets.emit("markIncident", data);
  416. }
  417. }
  418. // add artificial noise to the report
  419. function add_entry_random() {
  420. addNoiseEvent(chance.ip(),
  421. chance.integer({min: 0, max: 65535}),
  422. chance.ip(),
  423. chance.integer({min: 0, max: 65535}),
  424. "RandomMonitor_" + chance.integer({min: 0, max: 100}),
  425. true
  426. );
  427. }
  428. function add_entry_dshield() {
  429. e = dshield_entries.pop();
  430. //e = dshield_entries.shift();
  431. //debug.add("adding noise using dshield data: "+e);
  432. // we can not differentiate between PRA and non-PRA events so we have to sample both
  433. // accept entry if: sampling not active (sample 100%) or random value tells us so
  434. // add noise if: attack mitigation is not active, mitigation not triggered or random tells us to
  435. sample_entry = (!is_active || !sampling_active || chance.floating({min: 1, max: 100}) <= sampling_percent);
  436. try {
  437. addNoiseEvent(e[0], e[1], e[2], e[3], e[4], sample_entry);
  438. if (sample_entry) {
  439. events_dshield_added += 1;
  440. }
  441. events_dshield_total += 1;
  442. }
  443. catch (err) {
  444. // no events left
  445. }
  446. }
  447. function applyMitigation(ip_src, ip_dst, port_dst, monitor_id) {
  448. // praMitigation.applyMitigation(items[i].src.ip, items[i].dst.ip, items[i].dst.port
  449. events_external_total += 1;
  450. if (!is_active) {
  451. return false;
  452. } else {
  453. update_slot(monitor_id, ip_src, ip_dst, port_dst);
  454. // allow entry if sampling is not active or random tells us to
  455. is_sampled = (!sampling_active || chance.floating({min: 1, max: 100}) > sampling_percent);
  456. if (is_sampled) {
  457. events_external_added += 1;
  458. }
  459. return !is_sampled;
  460. }
  461. }
  462. function setActive(activeState) {
  463. debug.add("setting new PRA mitigation active state: " + activeState);
  464. is_active = activeState;
  465. }
  466. module.exports = {
  467. //init: init,
  468. setActive: setActive,
  469. applyMitigation: applyMitigation
  470. };