/**
* Copyright (c) <2013> <Radware Ltd.> and others. All rights reserved.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License
* v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
* @author Gera Goft
* @author Konstantin Pozdeev
* @version 0.1
*/
package org.opendaylight.defense4all.core.impl;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import org.opendaylight.defense4all.core.CounterStat;
import org.opendaylight.defense4all.core.DFAppRoot;
import org.opendaylight.defense4all.core.DFDetector;
import org.opendaylight.defense4all.core.Detection;
import org.opendaylight.defense4all.core.DetectorInfo;
import org.opendaylight.defense4all.core.PN;
import org.opendaylight.defense4all.core.ProtocolPort;
import org.opendaylight.defense4all.core.TrafficTuple;
import org.opendaylight.defense4all.core.CounterStat.Status;
import org.opendaylight.defense4all.core.Detection.DetectionConfidence;
import org.opendaylight.defense4all.core.DetectorInfo.DetectorConfidence;
import org.opendaylight.defense4all.core.interactionstructures.EndDetectionNotification;
import org.opendaylight.defense4all.core.interactionstructures.PNStatReport;
import org.opendaylight.defense4all.core.interactionstructures.StatReport;
import org.opendaylight.defense4all.framework.core.ExceptionControlApp;
import org.opendaylight.defense4all.framework.core.HealthTracker;
import org.opendaylight.defense4all.framework.core.FrameworkMain.ResetLevel;
public class RateBasedDetectorImpl extends DFAppCoreModule implements DFDetector {
/**
* Name space allocation of DF Detector Repo minor IDs
*/
public enum RepoMinor {
INVALID,
}
/**
* Decoupled actions for ActionSwitcher
*/
protected static final int ACTION_INVALID = -1; // Already defined in Module. Brought here for brevity
protected static final int ACTION_RESERVED = 0; // Already defined in Module. Brought here for brevity
protected static final int ACTION_PROCESS_STATS = 1;
protected static final int ACTION_REMOVE_PN = 3; // Asynchronously remove PN against other modules
private static final int ACTION_PROCESS_BASELINES = 5; // Periodically drop average to PN repo
private static final int ACTION_NOTIFY_END_DETECTION = 6;
DetectorInfo detectorInfo;
protected ArrayBlockingQueue<StatReport> statsQueue;
protected int statsQueueCapacity;
protected ConcurrentHashMap<String,CounterStat> counterStats = null; // Cache to hold and flash periodically all counters
protected Hashtable<String,PNStatSum> pnStatSums;
/* Use the values set below if not set anywhere else */
protected int movingAveragePeriod = 1000;
protected int gracePeriod = 100;
protected int warmupPeriod = 15;
protected int upperDetectionDeviationPercentage = 100; // Per counter deviatiocheckAggrForAttacksn threshold in percents -
// triggers immediate check at PN level (all counters).
protected int lowerDetectionDeviationPercentage = 50;
protected int durationOfDetection = 300;
protected long baselinesProcessingInterval = 0;
protected int pnSuspicionsThreshold = 3;
protected float bytesIgnoreThreshold = 0;
protected float packetsIgnoreThreshold = 0;
/* Detectors Repo column names */
public static final String MOVING_AVERAGE_PERIOD = "moving_average_period";
public static final String GRACE_PERIOD = "grace_period";
public static final String WARMUP_PERIOD = "warmup_period";
public static final String UPPER_DETECTION_DEVIATION_PERCENTAGE = "upper_detection_deviation_percentage";
public static final String LOWER_DETECTION_DEVIATION_PERCENTAGE = "lower_detection_deviation_percentage";
public static final String DURATION_OF_DETECTION = "duration_of_detection";
public static final String PN_PROCESSING_INTERVAL = "pn_processing_interval";
public static final String BASELINES_PROCESSING_INTERVAL = "baselines_processing_interval";
public static final String COUNTER_SUSPICIONS_THRESHOLD = "counter_suspicions_threshold";
public static final String PN_SUSPICIONS_THRESHOLD = "pn_suspicions_threshold";
public static final String BYTES_IGNORE_THRESHOLD = "bytes_ignore_threshold";
public static final String PACKETS_IGNORE_THRESHOLD = "packets_ignore_threshold";
protected boolean initialized = false;
/* Constructor for Spring */
public RateBasedDetectorImpl(int statsQueueCapacity) {
super();
boolean ofBasedDetector = true; boolean externalDetector = false;
detectorInfo = new DetectorInfo(DFAppRoot.OF_RATE_BASED_DETECTOR_LABEL, DetectorConfidence.VERY_HIGH,
ofBasedDetector, externalDetector);
this.statsQueueCapacity = statsQueueCapacity; // spring injection
statsQueue = new ArrayBlockingQueue<StatReport>(statsQueueCapacity);
counterStats = new ConcurrentHashMap<String,CounterStat>();
pnStatSums = new Hashtable<String,PNStatSum>();
}
/* Setters for Spring */
public void setMovingAveragePeriod(int period) {this.movingAveragePeriod = period;}
public void setGracePeriod(int period) {this.gracePeriod = period;}
public void setWarmupPeriod(int period) {this.warmupPeriod = period;}
public void setUpperDetectionDeviationPercentage(int percentage) {this.upperDetectionDeviationPercentage = percentage;}
public void setLowerDetectionDeviationPercentage(int percentage) {this.lowerDetectionDeviationPercentage = percentage;}
public void setDurationOfDetection(int duration) {this.durationOfDetection = duration;}
public void setBaselinesInterval(long interval) {this.baselinesProcessingInterval = interval;}
public void setDetectorInfoStr(String detectorInfoStr) {
detectorInfo = new DetectorInfo(detectorInfoStr);
detectorInfo.ofBasedDetector = true;
detectorInfo.externalDetector = false;
}
public void setPnSuspicionsThreshold(int threshold) {this.pnSuspicionsThreshold = threshold;}
public void setLabel ( String label ) {
detectorInfo.setLabel(label);
}
public void setBytesIgnoreThreshold ( float bytesIgnoreThreshold ) {this.bytesIgnoreThreshold = bytesIgnoreThreshold;}
public void setPacketsIgnoreThreshold ( float packetsIgnoreThreshold ) {this.packetsIgnoreThreshold = packetsIgnoreThreshold;}
/** Post-constructor initialization */
public void init() throws ExceptionControlApp {
super.init();
fr.logRecord(DFAppRoot.FR_DF_OPERATIONAL,"Rate based detector starting");
try {
loadCounters();
} catch (Throwable e) {
log.error("Failed to load data from countersRepo in RateBasedDetector. ", e );
fr.logRecord(DFAppRoot.FR_DF_FAILURE,"Rate based detector failed to start");
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Failed to load data from countersRepo in RateBasedDetector. ", e);
}
addBackgroundTask(ACTION_PROCESS_STATS, null);
if ( baselinesProcessingInterval != 0)
addPeriodicExecution(ACTION_PROCESS_BASELINES, null, baselinesProcessingInterval);
initialized = true;
}
/** Pre-shutdown cleanup */
public void finit() {
fr.logRecord(DFAppRoot.FR_DF_OPERATIONAL,"Rate-based detector stopping");
try {
persistCounters();
} catch ( Throwable e ) {
// log and ignore
log.error("Failed to persist data into countersRepo in RateBasedDetector. ", e );
fr.logRecord(DFAppRoot.FR_DF_FAILURE,"Rate-based detector failed to stop");
}
super.finit();
}
/** Reset
* @throws ExceptionControlApp */
public void reset(ResetLevel resetLevel) throws ExceptionControlApp {
log.info("RateBased Detector is resetting to level " + resetLevel);
super.reset(resetLevel);
pnStatSums.clear();
statsQueue.clear();
counterStats.clear();
}
/* Load all counters from repo into counters cache */
protected void loadCounters() throws ExceptionControlApp {
Hashtable<String,Hashtable<String,Object>> counterTable = dfAppRoot.countersStatsRepo.getTable();
if ( counterTable == null ) return;
Iterator<Map.Entry<String,Hashtable<String,Object>>> iter = counterTable.entrySet().iterator();
Map.Entry<String,Hashtable<String,Object>> entry; CounterStat counterStat;
while(iter.hasNext()) {
entry = iter.next();
counterStat = new CounterStat(entry.getValue());
counterStats.put(entry.getKey(), counterStat);
}
}
/* Persist counters to repo, including dynamic information. */
protected void persistCounters() throws ExceptionControlApp {
if (counterStats == null ) return;
Iterator<Map.Entry<String,CounterStat>> iter = counterStats.entrySet().iterator();
Map.Entry<String,CounterStat> entry; CounterStat counterStat;
while(iter.hasNext()) {
entry = iter.next();
counterStat = entry.getValue();
try {
dfAppRoot.countersStatsRepo.setRow(entry.getKey(), counterStat.toRow());
} catch (Throwable e) {
log.error("Failed to persist counters for detector "+detectorInfo.label, e );
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
continue;
}
}
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws Exception
* @throws exception_type circumstances description
*/
public void addPN(String pnKey) throws ExceptionControlApp {
// delete invalidated counters from possible deleted PN
cleanup();
try {
dfAppRootFullImpl.statsCollectorImpl.addPN(pnKey);
} catch (ExceptionControlApp e) {
log.error("Failed to add protected network to statistics counters. " + pnKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp ("Failed to add protected network to statistics counters. " + pnKey, e );
}
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
public void removePN(String pnKey) throws ExceptionControlApp {
try {
invokeDecoupledSerially(ACTION_REMOVE_PN, pnKey);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerialiy " + ACTION_REMOVE_PN + " " + pnKey, e);
throw e;
}
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
protected void decoupledRemovePN(String pnKey) {
try {
invalidatePNcounters(pnKey);
dfAppRootFullImpl.attackDecisionPointImpl.removeDetection(pnKey); // if previously notified attack detection for this object - cancel it.
dfAppRootFullImpl.statsCollectorImpl.removePN(pnKey);
} catch (ExceptionControlApp e) {
log.error("Excepted in removing PN from detector " +detectorInfo.label+ " PN key "+ pnKey , e);
}
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws exception_type circumstances description
*/
@Override
public void handleStatReport(StatReport statsReport) {
try {
statsQueue.put(statsReport);
}
catch (InterruptedException e1) { // ignore - termination request
}
catch ( Throwable e ){
log.error("Failed to handleStatReport. PN key: " + statsReport.pnKey+" Reading time: "+statsReport.readingTime, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MODERATE_HEALTH_ISSUE);
}
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws exception_type circumstances description
*/
protected void processStatReports() {
StatReport statReport = null;
while(true) {
try {
statReport = statsQueue.take();
processStatReport(statReport);
}
catch (InterruptedException e1) { // termination request
break;
}
catch (Throwable e) {
String msg = "Failed to process stat report: PN key: " + ( statReport != null ?statReport.pnKey:"");
log.error(msg, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MODERATE_HEALTH_ISSUE);
}
}
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
protected void processStatReport(StatReport statReport) throws ExceptionControlApp {
if ( statReport == null || statReport.stats == null ) {
log.error("Unexpected zero statReport recived." );
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
boolean zeroStat = statReport.stats.isZero();
/* Get the counter from cache. create and persist if non-existent */
String counterStatKey = CounterStat.generateKey(statReport.trafficFloorKey);
CounterStat cStat = counterStats.get(counterStatKey);
if(cStat == null) { // New counter. Need to create it, add to cache and to repo
if(zeroStat) return;
// TODO: check if the counter is in repo first before creating. Can be the case in a distributed DF
cStat = new CounterStat(statReport.trafficFloorKey, statReport.pnKey );
if ( statReport.stats.isForTrafficLearning()) {
cStat.status = Status.WARMUP_PERIOD;
} else {
cStat.status = Status.ACTIVE;
}
counterStats.put(counterStatKey, cStat);
}
try {
cStat.lock(); // lock counter for modification
/* Zero stat is only used to clear latest rate of EXISTING counterStats. This can happen for instance,
* when a diversion ends and the location is deleted, so the counter will not be refreshed anymore
* (deleted eventually), but is taken into account when calculating PN aggregated stats. */
if(zeroStat) {
cStat.updateStatsWithZero();
return;
}
/* If warmup - check for end of warmup - into grace */
if(cStat.status == Status.WARMUP_PERIOD && cStat.lastReadTime - cStat.firstReadTime > warmupPeriod) {
cStat.status = Status.LEARNING_PERIOD;
fr.logRecord(DFAppRoot.FR_DF_OPERATIONAL,"PO "+PN.getPrintableKey(statReport.pnKey) + " is currently in learning period" );
return;
}
/* Update the latest reading, latest reading time, and if not under attack - moving average.
* Return the latest rate or null if first time reading. */
boolean updateAverages = cStat.status == Status.ACTIVE || cStat.status == Status.LEARNING_PERIOD;
float averagePeriod = cStat.status == Status.ACTIVE ? movingAveragePeriod : gracePeriod;
cStat.updateStats(statReport, averagePeriod, updateAverages, bytesIgnoreThreshold, packetsIgnoreThreshold);
/* If grace - check for end of grace */
if(cStat.status == Status.LEARNING_PERIOD && cStat.lastReadTime - cStat.firstReadTime > gracePeriod) {
cStat.status = Status.ACTIVE;
fr.logRecord(DFAppRoot.FR_DF_OPERATIONAL,"PO "+PN.getPrintableKey(statReport.pnKey) + " is active" );
// setup initial baselines
aggregatePNStat(statReport.pnKey);
periodicProcessBaselines();
return;
}
if(cStat.status != Status.ACTIVE) return;
/* Periodically record in flight recorder the counter moving averages */
cStat.periodicallyRecordAverages(fr, dfAppRoot.baselineRecordingIntervalInSecs);
// Aggregate and process PN level counter
PNStatSum pnStatSum = aggregatePNStat(statReport.pnKey); // averages, latest rates from all counters
processPNcheckAttack(statReport.pnKey, pnStatSum);
} finally { // Persist to repo. TODO: optimize to do it periodically - according to per-PN calculations.
try {
cStat.unlock();
log.debug("Update counter : "+cStat.toString());
dfAppRoot.countersStatsRepo.setRow(cStat.getKey(), cStat.toRow());
} catch ( Throwable e) {
log.error("Failed to persist counter status for counter "+cStat.getKey());
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
}
protected PNStatSum aggregatePNStat(String pnKey) {
PNStatSum pnStatSum = new PNStatSum(pnKey);
Iterator<Map.Entry<String,CounterStat>> iter = counterStats.entrySet().iterator();
CounterStat counterStat;
while(iter.hasNext()) {
counterStat = iter.next().getValue();
if(counterStat.pnKey.equals(pnKey))
pnStatSum.add(counterStat);
}
synchronized (pnStatSums) {
pnStatSums.put(pnKey, pnStatSum);
}
return pnStatSum;
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
protected void processPNcheckAttack(String pnKey, PNStatSum pnStatSum) throws ExceptionControlApp {
Hashtable<String,Object> pnRow = dfAppRoot.pNsRepo.getRow(pnKey);
Hashtable<String,Object> pnUpdateCells = new Hashtable<String,Object>();
try {
pnRow = dfAppRoot.pNsRepo.getRow(pnKey);
} catch (ExceptionControlApp e) {
log.error("Failed to get pnRow from repo for " + pnKey, e);
fr.logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to process statistics for PO " + PN.getPrintableKey(pnKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw e;
}
long currentTime = System.currentTimeMillis() / 1000;
try {
/* Update moving average, latest rate, latestRate time */
TrafficTuple average;
if (pnStatSum.status == Status.WARMUP_PERIOD)
average = new TrafficTuple(); // Zeros
else if(pnStatSum.average.isZero())
average = pnStatSum.latestRate; // 1st aggregation, all counters active - initial average set to latest
else
average = pnStatSum.average ;
String averageStr = average.serialize();
pnUpdateCells.put(PN.AVERAGES, averageStr);
pnUpdateCells.put(PN.LATEST_RATES, pnStatSum.latestRate.serialize());
pnUpdateCells.put(PN.LATEST_RATES_TIME, currentTime);
if(pnStatSum.status != Status.ACTIVE ) return ;
/* Load current attack suspicions */
String statusOfPNSuspicions = ( String ) pnRow.get(PN.ATTACK_SUSPICIONS);
pnStatSum.loadStatusData (statusOfPNSuspicions );
/* Check for attacks */
List<ProtocolPort> attackedProtocolPorts = checkAggrForAttacks(pnStatSum);
/* Store updated status to PN repo */
pnUpdateCells.put(PN.ATTACK_SUSPICIONS, pnStatSum.serializeStatusData());
setSuspicInCounters(pnStatSum);
if(attackedProtocolPorts == null) return;
/* Set attack in counters, and notify AttackDecisionPoint about generated/updated attack detections */
setAttackInCounters(pnKey, attackedProtocolPorts, true);
addAllAttackDetections(pnKey, attackedProtocolPorts, currentTime);
}
finally {
// String s; TrafficTuple latest, averages;
// s = (String) pnRow.get(PN.LATEST_RATES); latest = new TrafficTuple(s);
// s = (String) pnRow.get(PN.AVERAGES); averages = new TrafficTuple(s);
// frameworkMain.getMyLogger().logRow(pnKey + " tcp values:" +
// " latest bps=" + (int)Math.ceil(latest.tcpbytes) +
// ", average bps=" + (int)Math.ceil(averages.tcpbytes) +
// ", latest pps=" + (int)Math.ceil(latest.tcppackets) +
// ", average pps=" + (int)Math.ceil(averages.tcppackets));
try {
dfAppRoot.pNsRepo.setRow(pnKey, pnUpdateCells);
} catch (ExceptionControlApp e) {
log.error("Failed to update attack status in pnRow for " + pnKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw e;
}
}
}
protected void setAttackInCounters(String pnKey, List<ProtocolPort> attackedProtocolPorts, boolean attacked) {
CounterStat counterStat = null;
Iterator<Map.Entry<String,CounterStat>> iter = counterStats.entrySet().iterator();
while(iter.hasNext()) {
counterStat = iter.next().getValue();
if ( counterStat == null ) {
log.error("Unexpected null counter in detector repo" );
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
continue;
}
counterStat.lock();
try {
if(counterStat.pnKey.equals(pnKey) ) {
for (ProtocolPort protocolPort:attackedProtocolPorts ) {
counterStat.setAttacked(protocolPort.protocol.getProtocolNumber(), protocolPort.port, attacked );
}
}
} catch (Throwable e) {}
counterStat.unlock();
}
}
protected void setSuspicInCounters( CounterStat statusSum ) {
if ( statusSum == null || statusSum.pnKey == null ) return;
CounterStat counterStat = null;
Iterator<Map.Entry<String,CounterStat>> iter = counterStats.entrySet().iterator();
String pnKey = statusSum.pnKey;
while(iter.hasNext()) {
counterStat = iter.next().getValue();
if ( counterStat == null ) {
log.error("Unexpected null counter in detector repo" );
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
continue;
}
if(counterStat.pnKey.equals(pnKey) ) {
counterStat.lock();
counterStat.copySuspicions(statusSum);
counterStat.unlock();
}
}
}
protected List<ProtocolPort> checkAggrForAttacks(PNStatSum pnStatSum ) throws ExceptionControlApp {
ArrayList<ProtocolPort> attackedProtocolPorts = new ArrayList<ProtocolPort>();
List<ProtocolPort> suspectedProtocolPorts = pnStatSum.deviationExceeds(pnStatSum.average,
lowerDetectionDeviationPercentage, upperDetectionDeviationPercentage );
/* Can add as many attack detection mechanism here, adding to detected attackedProtocolPorts list */
log.debug("Detector :"+detectorInfo.label+" Suspicions "+((suspectedProtocolPorts!=null)?suspectedProtocolPorts.size():null));
// add attack suspicion for each detected deviation */
// decrease attack suspicion for other protocol / ports
pnStatSum.resetAddAttackSuspicions(suspectedProtocolPorts);
if (suspectedProtocolPorts == null) return null;
/* When are attack detections reporting to attack decision point:
* 1. If suspicions counter = 0 -> do not report;
* 2. If suspicions counter exceeded threshold -> report
* 3. If 0 < suspicions counter <= threshold -> if attack then report, if peace - not */
for ( ProtocolPort protocolPort : suspectedProtocolPorts) {
int nSuspict = pnStatSum.getAttackSuspicions(protocolPort);
if (nSuspict > pnSuspicionsThreshold || (nSuspict <= pnSuspicionsThreshold && pnStatSum.isAttacked(protocolPort)))
attackedProtocolPorts.add(protocolPort);
}
log.debug("Detector :"+detectorInfo.label+" Attacked "+attackedProtocolPorts.size());
return attackedProtocolPorts;
}
/* Add all detected attacks*/
protected void addAllAttackDetections(String pnKey, List<ProtocolPort> attackedProtocolPorts, long currentTime) {
Detection detection; String detectionKey;
for(ProtocolPort protocolPort : attackedProtocolPorts) {
detectionKey = Detection.generateDetectionKey(detectorInfo.label, pnKey, protocolPort);
detection = new Detection(detectionKey, detectorInfo.label, DetectionConfidence.VERY_HIGH, currentTime,
durationOfDetection, pnKey, protocolPort);
//fr.logRecord(DFAppRoot.FR_DF_SECURITY, "Rate-based detector adding attack detection on "+
// detection.getPrintableDetectionTarget());
log.info("Rate-based detector adding attack detection on "+ detection.getPrintableDetectionTarget());
dfAppRootFullImpl.attackDecisionPointImpl.addDetection(detection);
}
}
/**
* Notify DF detector that the attack corresponding to the detection denoted by the passed in detectionKey
* is over. RateBasedDetectorImpl updates the state of the stat collection counters to resume calculating moving
* average.
* @param detectionKey
* @throws ExceptionControlApp
*/
@Override
public void notifyEndDetection(EndDetectionNotification endDetectionNotification) {
try {
invokeDecoupledSerially(ACTION_NOTIFY_END_DETECTION, endDetectionNotification);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerialiy " + ACTION_NOTIFY_END_DETECTION + " " +
endDetectionNotification.detection.key, e);
}
}
public void decoupledNotifyEndDetection(EndDetectionNotification endDetectionNotification) {
String pnKey = endDetectionNotification.detection.pnKey;
ProtocolPort protocolPort = endDetectionNotification.detection.protocolPort;
// invalidate counters for case zero stat was not send
for ( String mitigationTrafficFloor : endDetectionNotification.trafficFloorKeys ) {
invalidateTrafficFloorCounters (mitigationTrafficFloor);
}
List<ProtocolPort> protoPorts = new ArrayList<ProtocolPort>();
protoPorts.add(protocolPort);
setAttackInCounters(pnKey, protoPorts, false);
try {
String statusOfPNSuspicions = (String)dfAppRootFullImpl.pNsRepo.getCellValue(pnKey, PN.ATTACK_SUSPICIONS );
CounterStat status = new PNStatSum(pnKey);
status.loadStatusData (statusOfPNSuspicions );
status.setAttacked( protocolPort.protocol.getProtocolNumber(), protocolPort.port, false);
status.setAttackSuspicions(protocolPort.protocol.getProtocolNumber(), protocolPort.port, 0);
setSuspicInCounters(status);
dfAppRootFullImpl.pNsRepo.setCell(pnKey, PN.ATTACK_SUSPICIONS, status.serializeStatusData());
} catch (Throwable e) {
log.error("Failed to update PN attack status for prtocolPort " + protocolPort.toString());
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
private void invalidatePNcounters(String pnKey) throws ExceptionControlApp {
/* Retrieve all qualifiedCounterNames from OFC and PNs repo */
Hashtable<String, Object> pnRow;
try {
pnRow = dfAppRootFullImpl.pNsRepo.getRow(pnKey);
} catch (Throwable e) {
log.error("Failed to get pnRow from pNsRepo ", e );
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Failed to get pnRow from pNsRepo ", e );
}
Iterator<Map.Entry<String,Object>> iter = pnRow.entrySet().iterator();
Map.Entry<String,Object> entry; String trafficFloorKey;
while(iter.hasNext()) {
entry = iter.next();
if(! entry.getKey().startsWith(PN.TRAFFIC_FLOOR_KEY_PREFIX)) continue;
trafficFloorKey = (String) (entry.getValue());
invalidateTrafficFloorCounters( trafficFloorKey);
}
}
private void invalidateTrafficFloorCounters(String trafficFloorKey) {
CounterStat cStat = null;
try {
String mitigationCounterStatKey = CounterStat.generateKey(trafficFloorKey);
cStat = counterStats.get(mitigationCounterStatKey);
if(cStat != null) {
cStat.lock();
cStat.status = Status.INVALID;
try {
cStat.updateStatsWithZero();
} catch (Throwable e) {}
cStat.unlock();
dfAppRoot.countersStatsRepo.setRow(cStat.getKey(), cStat.toRow());
}
} catch (Throwable e) {
String cStatInfo = (cStat == null) ? "" : cStat.getKey();
log.error("Failed to set invalid state to PN counter " + cStatInfo);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
/**
* ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
public void cleanup() {
// Move over invalidated counters. If location has been deleted from PN - delete detector also
for ( String key : counterStats.keySet() ) {
try {
cleanupCounterStat(key);
} catch (Throwable e) {/* Ignore */}
}
}
/**
* ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
public void cleanupCounterStat(String key) {
CounterStat cStat = counterStats.get(key);
if ( cStat == null || cStat.status != Status.INVALID) return;
boolean found = false;
Hashtable<String, Object> pnRow = null;
try {
pnRow = dfAppRootFullImpl.pNsRepo.getRow(cStat.pnKey);
} catch (ExceptionControlApp e) {
log.error("Failed to get pnRow from repo for " + cStat.pnKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
if(pnRow == null) return;
Iterator<Map.Entry<String,Object>> iter = pnRow.entrySet().iterator();
Map.Entry<String,Object> entry; String trafficFloorKey;
while(iter.hasNext()) {
entry = iter.next();
if(!entry.getKey().startsWith(PN.TRAFFIC_FLOOR_KEY_PREFIX)) continue;
trafficFloorKey = (String) entry.getValue();
if(trafficFloorKey == null) continue;
CounterStat cPNStat = new CounterStat (trafficFloorKey, cStat.pnKey);
if (cPNStat.getKey().equals(key)) {
found = true; // This traffic floor still exists in the PN - do nothing
break;
}
}
// Traffic floor doesn't exist in the PN - clean counter
if ( ! found) {
try {
dfAppRoot.countersStatsRepo.deleteRow(key);
counterStats.remove(key);
} catch (Throwable e) {
log.error("Failed to delete counter. " + cStat.pnKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
}
/**
* Periodically drop statistics from detector average to PN repo
* @param param_name param description
* @return return description
* @throws exception_type circumstances description
*/
protected void periodicProcessBaselines () {
if(!fMain.isOpenForBusiness()) return; // Operate only after everything is initialized and is not terminating
/* Set sums into PN repo, and check for attacks */
synchronized ( pnStatSums ) {
Map.Entry<String,PNStatSum> entry;
Iterator<Entry<String, PNStatSum>> pnStatSumIter = pnStatSums.entrySet().iterator();
while(pnStatSumIter.hasNext()) {
entry = pnStatSumIter.next();
String pnKey = entry.getKey();
TrafficTuple average = entry.getValue().average;
if(average.isZero()) continue;
try {
Hashtable<String,Object> pnRow = dfAppRoot.pNsRepo.getRow(pnKey);
if(pnRow == null) continue;
Hashtable<String,Object> pnUpdateCells = new Hashtable<String,Object>();
long currentTime = System.currentTimeMillis() / 1000;
String averageStr = average.serialize();
pnUpdateCells.put(PN.BASELINES, averageStr);
pnUpdateCells.put(PN.BASELINES_TIME, currentTime);
dfAppRoot.pNsRepo.setRow(pnKey, pnUpdateCells);
//fr.logRecord(DFAppRoot.FR_DF_SECURITY,"Baselines for PN " + pnKey + ": " + averageStr);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to update baselines for pnRow. " + pnKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MODERATE_HEALTH_ISSUE);
}
}
}
}
/*
* (non-Javadoc)
* @see org.opendaylight.controlapps.defense4all.core.Detector#getDetectorInfo()
* For spring requirements get should return string
* getter to string is for spring req
*/
public DetectorInfo getDetectorInfo() {return detectorInfo;}
public String getDetectorInfoStr() { return detectorInfo.toString();}
/**
* Compose detector repository row
*/
public Hashtable<String, Object> toRow() {
// First columns are detector info
Hashtable<String, Object> row = detectorInfo.toRow();
row.put(RateBasedDetectorImpl.MOVING_AVERAGE_PERIOD, movingAveragePeriod);
row.put(RateBasedDetectorImpl.GRACE_PERIOD ,gracePeriod);
row.put(RateBasedDetectorImpl.WARMUP_PERIOD ,warmupPeriod);
row.put(RateBasedDetectorImpl.UPPER_DETECTION_DEVIATION_PERCENTAGE ,upperDetectionDeviationPercentage);
row.put(RateBasedDetectorImpl.LOWER_DETECTION_DEVIATION_PERCENTAGE ,lowerDetectionDeviationPercentage);
row.put(RateBasedDetectorImpl.DURATION_OF_DETECTION ,durationOfDetection);
row.put(RateBasedDetectorImpl.BASELINES_PROCESSING_INTERVAL ,baselinesProcessingInterval);
row.put(RateBasedDetectorImpl.PN_SUSPICIONS_THRESHOLD ,pnSuspicionsThreshold);
row.put(RateBasedDetectorImpl.BYTES_IGNORE_THRESHOLD, bytesIgnoreThreshold );
row.put(RateBasedDetectorImpl.PACKETS_IGNORE_THRESHOLD, packetsIgnoreThreshold );
return row;
}
public void fromRow(Hashtable<String, Object> row)
{
this.detectorInfo.fromRow(row);
Object obj = null;
obj = row.get(MOVING_AVERAGE_PERIOD);
if ( obj != null ) movingAveragePeriod = Integer.valueOf(obj.toString());
obj = row.get(GRACE_PERIOD);
if ( obj != null ) gracePeriod = Integer.valueOf(obj.toString());
obj = row.get(WARMUP_PERIOD);
if ( obj != null ) warmupPeriod = Integer.valueOf(obj.toString());
obj = row.get(UPPER_DETECTION_DEVIATION_PERCENTAGE);
if ( obj != null ) upperDetectionDeviationPercentage = Integer.valueOf(obj.toString());
obj = row.get(LOWER_DETECTION_DEVIATION_PERCENTAGE);
if ( obj != null ) lowerDetectionDeviationPercentage = Integer.valueOf(obj.toString());
obj = row.get(DURATION_OF_DETECTION);
if ( obj != null ) durationOfDetection = Integer.valueOf(obj.toString());
obj = row.get(BASELINES_PROCESSING_INTERVAL);
if ( obj != null ) baselinesProcessingInterval = Long.valueOf(obj.toString());
obj = row.get(PN_SUSPICIONS_THRESHOLD);
if ( obj != null ) pnSuspicionsThreshold = Integer.valueOf(obj.toString());
obj = row.get(BYTES_IGNORE_THRESHOLD);
if ( obj != null ) bytesIgnoreThreshold = Float.valueOf(obj.toString());
obj = row.get(PACKETS_IGNORE_THRESHOLD);
if ( obj != null ) packetsIgnoreThreshold = Float.valueOf(obj.toString());
}
@Override
public PNStatReport getLatestPNStatReport(String pnKey) {
if(pnKey == null) return new PNStatReport();
PNStatSum pnStatSum = pnStatSums.get(pnKey);
PNStatReport pnStatReport = new PNStatReport(pnStatSum);
return pnStatReport;
}
@Override
public List<PNStatReport> getLatestPNStatReports() {
List<PNStatReport> pnStatReports = new ArrayList<PNStatReport>();
Iterator<Map.Entry<String,PNStatSum>> iter = pnStatSums.entrySet().iterator();
PNStatReport pnStatReport; PNStatSum pnStatSum;
while(iter.hasNext()) {
pnStatSum = iter.next().getValue();
pnStatReport = new PNStatReport(pnStatSum);
pnStatReports.add(pnStatReport);
}
return pnStatReports;
}
@Override
protected void actionSwitcher(int actionCode, Object param) {
switch(actionCode) {
case ACTION_RESERVED:
break;
case ACTION_PROCESS_STATS:
processStatReports();
break;
case ACTION_PROCESS_BASELINES:
periodicProcessBaselines();
break;
case ACTION_REMOVE_PN:
decoupledRemovePN((String) param);
break;
case ACTION_NOTIFY_END_DETECTION:
decoupledNotifyEndDetection((EndDetectionNotification) param);
break;
default:
}
}
}