/**
* 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.mitigationdriver.local;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import org.opendaylight.defense4all.core.AMS;
import org.opendaylight.defense4all.core.AMSConnection;
import org.opendaylight.defense4all.core.Attack;
import org.opendaylight.defense4all.core.CounterStat;
import org.opendaylight.defense4all.core.DFAppModule;
import org.opendaylight.defense4all.core.DFAppRoot;
import org.opendaylight.defense4all.core.DFAppRoot.HealthStatus;
import org.opendaylight.defense4all.core.NetNode;
import org.opendaylight.defense4all.core.DvsnInfo;
import org.opendaylight.defense4all.core.MitigationDriver;
import org.opendaylight.defense4all.core.Mitigation;
import org.opendaylight.defense4all.core.PN;
import org.opendaylight.defense4all.core.Traffic.TrafficDirection;
import org.opendaylight.defense4all.core.TrafficFloor;
import org.opendaylight.defense4all.core.TrafficTuple;
import org.opendaylight.defense4all.core.DvsnInfo.AMSDvsnInfo;
import org.opendaylight.defense4all.framework.core.ExceptionControlApp;
import org.opendaylight.defense4all.framework.core.FrameworkMain.ResetLevel;
import org.opendaylight.defense4all.framework.core.FMHolder;
import org.opendaylight.defense4all.framework.core.HealthTracker;
public class MitigationDriverLocal extends DFAppModule implements MitigationDriver {
protected class MitigationResponse {
String mitigationKey; boolean mitigating;
MitigationResponse(String mitigationKey, boolean mitigating) {
this.mitigationKey = mitigationKey; this.mitigating = mitigating;
}
}
protected class MitigationParam {
String mitigationKey; List<DvsnInfo> netNodeDvsnInfo;
MitigationParam(String mitigationKey, List<DvsnInfo> netNodeDvsnInfo) {
this.mitigationKey = mitigationKey; this.netNodeDvsnInfo = netNodeDvsnInfo;
}
}
protected class NetNodeAmsConnectivitySet {
Set<String> netNodeLabels;
Set<String> amsLabels;
NetNodeAmsConnectivitySet() {
netNodeLabels = new HashSet<String>();
amsLabels = new HashSet<String>();
}
}
/**
* 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_MITIGATE = 1;
protected static final int ACTION_END_MITIGATION = 2;
protected static final int ACTION_RESPOND_MITIGATION = 3;
protected static final int ACTION_ADD_NETNODE = 4;
private static final int ACTION_ADD_PN = 5;
private static final int ACTION_REMOVE_PN = 6;
private static final int ACTION_NETNODE_DOWNED = 7;
private List<NetNodeAmsConnectivitySet> netNodeSets = null;
String label = null;
/* Constructor for Spring */
public MitigationDriverLocal() {
super();
}
/* Setters for Spring */
public void setLabel(String label) {this.label = label;}
@Override
public String getLabel() {return label;}
/** Post-constructor initialization */
public void init() throws ExceptionControlApp {
super.init();
netNodeSets = new ArrayList<NetNodeAmsConnectivitySet>();
}
/** Pre-shutdown cleanup */
public void finit() {
super.finit();
}
/** Reset
* @throws ExceptionControlApp */
public void reset(ResetLevel resetLevel) throws ExceptionControlApp {
super.reset(resetLevel);
netNodeSets.clear();
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
public void addPN(String pnKey) throws ExceptionControlApp {
try {
invokeDecoupledSerially(ACTION_ADD_PN, pnKey);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerially. "+ACTION_ADD_PN+" "+pnKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Excepted trying to invokeDecoupledSerially. "+ACTION_ADD_PN+" "+pnKey, e);
}
}
protected void decoupledAddPN(String pnKey) {
try {
dfAppRoot.getAMSRep().addPN(pnKey);
} catch (Throwable e) {
log.error("Excepted trying to add PN to AMSRep "+pnKey);
}
}
/**
* #### 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 invokeDecoupledSerially. "+ACTION_REMOVE_PN+" "+pnKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Excepted trying to invokeDecoupledSerially. "+ACTION_REMOVE_PN+" "+pnKey, e);
}
}
protected void decoupledRemovePN(String pnKey) {
try {
dfAppRoot.getAMSRep().removePN(pnKey);
} catch (Throwable e) {
log.error("Excepted trying to remove PN from AMSRep "+pnKey);
}
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws exception_type circumstances description
*/
@Override
public void addNetNode(String netNodeKey) {
try {
invokeDecoupledSerially(ACTION_ADD_NETNODE, netNodeKey);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerially." + ACTION_ADD_NETNODE, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
/* Check if netnode sets should be updated */
protected void decoupledAddNetNode(String netNodeKey) {
Hashtable<String, Object> netNodeRow;
try {
netNodeRow = dfAppRoot.netNodesRepo.getRow(netNodeKey);
} catch (Throwable e) {
log.error("Failed to get NetNode row from netNodesRepo : "+netNodeKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
NetNode netNode;
try {
netNode = new NetNode(netNodeRow);
} catch (Throwable e) {
log.error("Failed to inflate NetNode row from netNodesRepo : "+netNodeKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
/* Construct a list of all amsLabels this netNode is connected to. */
Set<String> amsLabels = new HashSet<String>(); String amsLabel;
Set<Entry<String, AMSConnection>> amsConnectionsSet = netNode.amsConnections.entrySet();
if (amsConnectionsSet == null ) return;
Iterator<Map.Entry<String,AMSConnection>> iter = amsConnectionsSet.iterator();
while(iter.hasNext()) {
amsLabel = iter.next().getValue().amsLabel;
amsLabels.add(amsLabel);
}
/* If the set of AMSs connected to this netNode is identical to the set of AMSs connected to netNodes
* in some set, then add this netNode to that set. */
for(NetNodeAmsConnectivitySet netNodeSet : netNodeSets) {
if(netNodeSet.amsLabels.equals(amsLabels)) {
netNodeSet.netNodeLabels.add(netNode.label);
return;
}
}
/* Not found any existing matching set, so create a new one and add to the sets of netNodes. */
NetNodeAmsConnectivitySet netNodeSet = new NetNodeAmsConnectivitySet();
netNodeSet.netNodeLabels.add(netNode.label);
netNodeSet.amsLabels.addAll(amsLabels);
netNodeSets.add(netNodeSet);
}
/* Allocate AMS(s) for diversion for each of the netNodes through which attacked PN traffic flows.
* Ensure that netNodes in a set are assigned the same AMS(s). */
protected List<DvsnInfo> allocateAMSForDiversion(String mitigationKey) throws ExceptionControlApp {
log.info("Looking for candidate AMSs to divert traffic through per mitigation " + mitigationKey);
/* First get all AMSs and make sure there are any */
List<String> amsKeys;
try {
amsKeys = dfAppRoot.amsRepo.getKeys();
} catch (Throwable e) {
log.error("Failed to obtain amsKeys from amsRepo",e);
FMHolder.get().getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Failed to obtain amsKeys from amsRepo.", e);
}
if(amsKeys == null) {
fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY, "No AMS installed on DefenseFlow.");
return null;
}
List<DvsnInfo> dvsnInfos = new ArrayList<DvsnInfo>();
String pnKey;
Hashtable<String, Object> pnRow;
try {
String attackKey = (String) dfAppRoot.mitigationsRepo.getCellValue(mitigationKey, Mitigation.ATTACK_KEY);
pnKey = (String) dfAppRoot.attacksRepo.getCellValue(attackKey, Attack.PNKEY);
pnRow = dfAppRoot.pNsRepo.getRow(pnKey);
} catch (Throwable e) {
log.error("Failed to obtain attack and/or pnRow relevant for mitigation "+mitigationKey, e);
FMHolder.get().getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Failed to obtain attack and/or pnRow relevant for mitigation "+mitigationKey, e);
}
PN pn;
try {
pn = new PN(pnRow);
} catch (Throwable e) {
log.error("Excepted trying to inflate PN from row. "+ pnKey, e);
FMHolder.get().getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Excepted trying to inflate PN from row. "+ pnKey, e);
}
DvsnInfo netNodeDvsnInfo;
/* For each netNode assign ams device(s) to divert traffic to, such that all netNodes in the same
* netNodeDPConnectivitySet are assigned the same ams device(s).
* Try/catch for extra safety in list processing functions */
StringBuilder sb = new StringBuilder();
try {
for (String netNodeLabel : pn.netNodeLabels) {
netNodeDvsnInfo = getAssignedAmsForNetNodeSet(dvsnInfos, netNodeLabel);
if(netNodeDvsnInfo == null) // AMS(s) have not yet been assigned for this netNode set
netNodeDvsnInfo = allocateDvsnAMSForNetNode(mitigationKey, pnKey, netNodeLabel);
sb.setLength(0);
if(netNodeDvsnInfo != null) {
sb.append("Allocated for NetNode "); sb.append(netNodeLabel); sb.append(", amsLabels: ");
for(AMSDvsnInfo amsDvsnInfo : netNodeDvsnInfo.amsDvsnInfos) {
sb.append(amsDvsnInfo.label); sb.append(", ");
}
sb.setLength(sb.length() - 2); // Remove last ", "
dvsnInfos.add(netNodeDvsnInfo);
} else {
sb.append("No AMSs were allocated for diversion from " + netNodeLabel);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "No AMS allocated for diversion from PO "+PN.getPrintableKey(pn.label));
}
log.info(sb.toString());
}
} catch (Exception e) {
log.error("Excepted in processing DvsnInfo for PN "+pnKey, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "No AMS allocated for diversion from PO "+PN.getPrintableKey(pn.label));
FMHolder.get().getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Excepted in processing DvsnInfo for PN "+pnKey, e);
}
if(dvsnInfos.isEmpty()) return null;
return dvsnInfos;
}
/* Return the assigned AMS(s) for netNodes in the set containing the netNodeLabel. If no AMS(s) was/were
* assigned yet return null. */
protected DvsnInfo getAssignedAmsForNetNodeSet(List<DvsnInfo> dvsnInfos, String netNodeLabel) {
for(DvsnInfo netNodeDvsnInfo : dvsnInfos) {
if(inSameNetNodeSet(netNodeDvsnInfo.netNodeLabel, netNodeLabel)) {
DvsnInfo result = new DvsnInfo(netNodeDvsnInfo);
result.netNodeLabel = netNodeLabel;
return result;
}
}
return null;
}
/* Check if two netNodes are in the same netNodeSet. */
protected boolean inSameNetNodeSet(String netNodeLabel1, String netNodeLabel2) {
/* Loop through all netNodeSets to check if netNodeLabel1 is in one of them. In case a set, containing
* netNodeLabel1 is found, and that set also contains netNodeLabel2 - return true. Otherwise false */
for(NetNodeAmsConnectivitySet netNodeSet : netNodeSets) {
if(netNodeSet.netNodeLabels.contains(netNodeLabel1)) {
if(netNodeSet.netNodeLabels.contains(netNodeLabel2))
return true;
else
return false;
}
}
return false;
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
protected DvsnInfo allocateDvsnAMSForNetNode(String mitigationKey, String pnKey, String netNodeLabel)
throws ExceptionControlApp {
List<String> amsKeys;
try {
amsKeys = dfAppRoot.amsRepo.getKeys();
} catch (Throwable e) {
log.error("Failed to obtain AmsKeys from amsRepo",e);
FMHolder.get().getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Failed to obtain AmsKeys from amsRepo.", e);
}
if(amsKeys == null) return null;
Properties dvsnProps = null; AMSDvsnInfo possibleAmsDvsnInfo;
List<AMSDvsnInfo> possibleAmsDvsnInfos = new ArrayList<AMSDvsnInfo>();
boolean up;
for(String amsKey : amsKeys) {
try {
up = (Boolean) dfAppRoot.amsRepo.getCellValue(amsKey, AMS.HEALTH_STATUS);
if(!up) continue; // This AMS is dead / not healthy. Choose from others.
} catch (Throwable e) {
String msg = "Failed to read from AMS Repo " + amsKey;
log.error(msg,e);
FMHolder.get().getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
continue;
}
dvsnProps = dfAppRoot.getDvsnRep().getDvsnProps(pnKey, netNodeLabel, amsKey);
if (dvsnProps != null) { // Diversion to this ams (amsKey) is possible
possibleAmsDvsnInfo = new AMSDvsnInfo();
possibleAmsDvsnInfo.label = amsKey;
possibleAmsDvsnInfo.amsDvsnProps = dvsnProps;
possibleAmsDvsnInfos.add(possibleAmsDvsnInfo);
break;
}
}
if(dvsnProps == null) return null;
/* Select AMSs to divert traffic to. Currently assume a single AMS for NodeNode diversion. */
List<AMSDvsnInfo> selectedAmsDvsnInfos = selectDvsnAMS(possibleAmsDvsnInfos);
if(selectedAmsDvsnInfos == null || selectedAmsDvsnInfos.isEmpty()) return null;
DvsnInfo dvsnInfo = new DvsnInfo(netNodeLabel, null, mitigationKey, selectedAmsDvsnInfos, null);
return dvsnInfo;
}
/* Select AMSs for diversion. Right now we select only one - the first one in the list. */
protected List<AMSDvsnInfo> selectDvsnAMS(List<AMSDvsnInfo> possibleAmsDvsnInfos) {
if(possibleAmsDvsnInfos == null || possibleAmsDvsnInfos.isEmpty()) return null;
List<AMSDvsnInfo> selectedDvsnAMS = new ArrayList<AMSDvsnInfo>();
for(AMSDvsnInfo amsDvsnInfo : possibleAmsDvsnInfos) {
if(AMS.isUp(amsDvsnInfo.label)) {
selectedDvsnAMS.add(amsDvsnInfo);
break; // Currently select only one. Can select multiple for large attacks (elastic scale) in the future.
}
}
return selectedDvsnAMS;
}
@Override
public synchronized void mitigate(String mitigationKey) throws ExceptionControlApp {
try {
invokeDecoupledSerially(ACTION_MITIGATE, mitigationKey);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerially. "+ACTION_MITIGATE+" "+mitigationKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Excepted trying to invokeDecoupledSerially. "+ACTION_MITIGATE+" "+mitigationKey, e);
}
}
protected void decoupledMitigate(String mitigationKey) {
MitigationResponse mitigationResponse = new MitigationResponse(mitigationKey, false);
log.info("MitigationDriverLocal attempts to mitigate " + mitigationKey);
/* Select a AMS for diversion and update the mitigation record */
List<DvsnInfo> dvsnInfos;
try {
log.info("MitigationDriverLocal is checking diversion options for "+ mitigationKey);
dvsnInfos = allocateAMSForDiversion(mitigationKey);
} catch (Throwable e) {
log.error("Failed to allocate AMS for mitigation " + mitigationKey );
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to mitigate "+Mitigation.getPrintableMitigationTarget(mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
dvsnInfos = null;
}
if (dvsnInfos == null) { // Notify Mitigation manager - not mitigating
log.info( "MitigationDriverLocal will not mitigate "+mitigationKey);
try {
invokeDecoupledSerially(ACTION_RESPOND_MITIGATION, mitigationResponse);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerially. "+ACTION_RESPOND_MITIGATION+" "+mitigationKey, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to mitigate "+Mitigation.getPrintableMitigationTarget(mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
return;
}
List<String> tFloorKeys = new ArrayList<String>();
String pnkey;
try {
pnkey = (String) dfAppRoot.mitigationsRepo.getCellValue(mitigationKey, Mitigation.PNKEY);
} catch (Throwable e1) {
log.error("Failed to obtain PN for mitigation. "+mitigationKey, e1);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to mitigate "+Mitigation.getPrintableMitigationTarget(mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
try {
invokeDecoupledSerially(ACTION_RESPOND_MITIGATION, mitigationResponse);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerially. "+ACTION_RESPOND_MITIGATION+" "+mitigationKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
return;
}
log.info("MitigationDriverLocal has found diversion options for "
+ mitigationKey + ", and is attempting to divert traffic from all NetNodes through which the " +
"subject traffic flows.");
for(DvsnInfo dvsnInfo : dvsnInfos) {
mitigatePerDvsnInfo(dvsnInfo, pnkey, mitigationKey, tFloorKeys);
}
Boolean isMitigationRepoError = false; Boolean isMonitoringError = false;
if(!tFloorKeys.isEmpty()) { // Diversion succeeded at least on some netNodes " */
try {
dfAppRoot.mitigationsRepo.setCell(mitigationKey, Mitigation.STATUS, Mitigation.Status.ACTIVE.name());
} catch (ExceptionControlApp e) {
log.error("Failed to update mitigation status in mitigationsRepo "+mitigationKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
isMitigationRepoError = true;
}
if ( ! isMitigationRepoError ) {
try {
log.info("MitigationDriverLocal is instructing AMSRep to start "
+ "monitoring from AMS(s) for diverted traffic according to mitigation " + mitigationKey);
dfAppRoot.getAMSRep().startMonitoring(mitigationKey); //Notify amsRep -can trigger attack monitoring by AMS
} catch (ExceptionControlApp e) {
log.error("Excepted in startMonitoring invocation", e);
log.info("MitigationDriverLocal failed to instruct AMSRep to "
+ "start monitoring from AMS(s) for diverted traffic according to mitigation "+mitigationKey);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
isMonitoringError = true;
}
}
if ( isMitigationRepoError || isMonitoringError ) {
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to mitigate "+Mitigation.getPrintableMitigationTarget(mitigationKey));
failedMitigationCleanup(pnkey, mitigationKey, dvsnInfos, tFloorKeys );
} else {
mitigationResponse.mitigating = true;
}
}
decoupledRespondMitigation(mitigationResponse);
}
protected void mitigatePerDvsnInfo(DvsnInfo dvsnInfo, String pnkey, String mitigationKey, List<String> tFloorKeys) {
/* Set the traffic bandwidth of the peacetime floor in this netNode location. */
String bandwidth = retrieveBandwidth(dvsnInfo.netNodeLabel, pnkey);
dvsnInfo.configProps.setProperty(DvsnInfo.INBOUND_BANDWIDTH, bandwidth);
boolean setDvsnInfoRow = false;
StringBuilder sb = new StringBuilder();
StringBuilder sbNetNodeAMSs = new StringBuilder();
String tFloorKey = null;
/* Record netNodeDvsnInfo in its repo and record the record key in relevant mitigations repo. */
try {
dfAppRoot.dvsnInfosRepo.setRow(dvsnInfo.key, dvsnInfo.toRow());
setDvsnInfoRow = true;
dfAppRoot.mitigationsRepo.setCell(dvsnInfo.mitigationKey, Mitigation.DVSN_INFO_KEY_PREFIX + dvsnInfo.key, dvsnInfo.key);
} catch (ExceptionControlApp e) {
log.error("Failed to persist dvsnInfo in dvsnInfosRepo and/or mitigationsRepo "+dvsnInfo.key, e );
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE,"Failed to divert "
+ Mitigation.getPrintableMitigationTarget(dvsnInfo.mitigationKey ));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
try {
if(setDvsnInfoRow) dfAppRoot.dvsnInfosRepo.deleteRow(dvsnInfo.key);
} catch (Throwable e1) {/* Ignore */}
return;
}
/* Configure AMSs that will mitigate this attack with relevant bandwidth.
* Divert traffic to mitigating AMSs. */
try {
sb.setLength(0);
sbNetNodeAMSs.setLength(0);
sb.append("MitigationDriverLocal is adding security configuration to AMSs to receive traffic from NetNode.");
sbNetNodeAMSs.append("NetNode="); sbNetNodeAMSs.append(dvsnInfo.netNodeLabel);
sbNetNodeAMSs.append(". AMS(s) is/are: ");
for(AMSDvsnInfo amsDvsnInfo : dvsnInfo.amsDvsnInfos) {
sbNetNodeAMSs.append(amsDvsnInfo.label); sb.append(", ");
}
sbNetNodeAMSs.setLength(sb.length() - 2); // Remove the last ", "
log.info(sb.toString() + sbNetNodeAMSs.toString());
dfAppRoot.getAMSRep().addSecurityConfiguration(dvsnInfo.key);
} catch (ExceptionControlApp e) {
log.error("Failed to set AMS Security configuration for diversion "+dvsnInfo.key, e );
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE,"Failed to add security configuration for diversion "
+Mitigation.getPrintableMitigationTarget(mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
tFloorKey = reigniteDvsn(dvsnInfo, pnkey);
if (tFloorKey != null )
tFloorKeys.add(tFloorKey);
}
protected String reigniteDvsn(DvsnInfo dvsnInfo, String pnkey) {
String tFloorKey = null;
int retry = 3; boolean printed = false;
fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY, "Reigniting traffic diversion for PO "+
PN.getPrintableKey(pnkey));
while ( retry-- > 0 ) {
try {
// dvsnInfo = dfAppRoot.getDvsnRep().prepareForDvsn(dvsnInfo.mitigationKey, dvsnInfo.key);
dfAppRoot.getDvsnRep().prepareForDvsn(dvsnInfo.mitigationKey, dvsnInfo.key);
if( dvsnInfo != null ) {
tFloorKey = dfAppRoot.getDvsnRep().divert(dvsnInfo.mitigationKey, dvsnInfo.key);
dvsnInfo.tFloorKey = tFloorKey;
}
break;
} catch (Throwable e) {
if ( ! printed ) {
log.error("Failed to reignite diverson "+dvsnInfo.key, e);
printed = true;
}
continue;
}
}
/* If succeeded creating the traffic floor - record it in PNs record and in mitigation record. */
if(tFloorKey != null) {
try {
dfAppRoot.pNsRepo.setCell(pnkey, PN.TRAFFIC_FLOOR_KEY_PREFIX + tFloorKey, tFloorKey);
dfAppRoot.mitigationsRepo.setCell(dvsnInfo.mitigationKey,Mitigation.TRAFFIC_FLOOR_KEY_PREFIX +
tFloorKey,tFloorKey);
dvsnInfo.trafficDiverted = true;
dfAppRoot.dvsnInfosRepo.setRow(dvsnInfo.key, dvsnInfo.toRow()); // Record trafficDiverted and tFloor.
} catch (ExceptionControlApp e) {
log.error("Failed to persist traffic floor in pNsRepo and/or mitigationsRepo "+dvsnInfo.key, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY,"Internal memory error. Canceling TCP traffic diversion for PO "
+ PN.getPrintableKey(pnkey));
endDvsnPerDvsnInfo(dvsnInfo);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
return tFloorKey;
}
protected void failedMitigationCleanup(String pnkey, String mitigationKey, List<DvsnInfo> dvsnInfos,
List<String> tFloorKeys) {
// delete all diversions from failed mitigation
if ( dvsnInfos != null ) {
for ( DvsnInfo dvsnInfo:dvsnInfos ) {
try {
endDvsnPerDvsnInfo(dvsnInfo);
} catch (Throwable e) {};
try {
dfAppRoot.getAMSRep().removeSecurityConfiguration(dvsnInfo.key);
} catch (Throwable e) {};
try {
dfAppRoot.dvsnInfosRepo.deleteRow(dvsnInfo.key);
dfAppRoot.mitigationsRepo.deleteCell(dvsnInfo.mitigationKey, Mitigation.DVSN_INFO_KEY_PREFIX + dvsnInfo.key);
} catch (Throwable e) {};
try {
dfAppRoot.mitigationsRepo.setCell(dvsnInfo.mitigationKey, Mitigation.STATUS, Mitigation.Status.INVALID.name());
} catch (Throwable e) {};
}
}
// revert operations from mitigation
try {
dfAppRoot.getAMSRep().stopMonitoring(mitigationKey);
} catch (Throwable e) {};
try {
dfAppRoot.mitigationsRepo.setCell(mitigationKey, Mitigation.STATUS, Mitigation.Status.INVALID.name());
} catch(Throwable e) {};
}
// protected void failedMitigationCleanup(String pnkey, String mitigationKey, List<DvsnInfo> dvsnInfos,
// List<String> tFloorKeys) {
//
// // delete all traffic floors from failed mitigation
// if ( tFloorKeys != null ) {
// for ( String tFloorKey:tFloorKeys )
// failedTrafficFloorCleanup(pnkey, mitigationKey, tFloorKey);
// }
//
// // delete all diversions from failed mitigation
// if ( dvsnInfos != null ) {
// for ( DvsnInfo dvsnInfo:dvsnInfos ) {
// try {
// dfAppRoot.getAMSRep().removeSecurityConfiguration(dvsnInfo.key);
// } catch (Throwable e) {};
// try {
// dfAppRoot.dvsnInfosRepo.deleteRow(dvsnInfo.key);
// dfAppRoot.mitigationsRepo.deleteCell(dvsnInfo.mitigationKey, Mitigation.DVSN_INFO_KEY_PREFIX + dvsnInfo.key);
// } catch (Throwable e) {};
// try {
// dfAppRoot.mitigationsRepo.setCell(dvsnInfo.mitigationKey, Mitigation.STATUS, Mitigation.Status.INVALID.name());
// } catch (Throwable e) {};
// }
// }
//
// // revert operations from mitigation
// try {
// dfAppRoot.getAMSRep().stopMonitoring(mitigationKey);
// } catch (Throwable e) {};
//
// try {
// dfAppRoot.mitigationsRepo.setCell(mitigationKey, Mitigation.STATUS, Mitigation.Status.INVALID.name());
// } catch(Throwable e) {};
// }
protected String retrieveBandwidth(String netNodeLabel, String pnkey) {
String peaceFloorKey = TrafficFloor.generatePeacetimeFloorKey(netNodeLabel, pnkey);
String counterStatKey = CounterStat.generateKey(peaceFloorKey);
TrafficTuple trafficTuple = null;
try {
String trafficTupleStr = (String) dfAppRoot.countersStatsRepo.getCellValue(counterStatKey, CounterStat.MOVING_AVERAGE);
trafficTuple = new TrafficTuple(trafficTupleStr);
} catch (Throwable e) {
log.error("Failed to obtain bandwidth for NetNode "+netNodeLabel+" PN "+pnkey, e );
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
trafficTuple = new TrafficTuple();
}
String bandwidth = trafficTuple.getBandwidth(TrafficDirection.INBOUND);
return bandwidth;
}
protected void decoupledRespondMitigation(MitigationResponse mitigationResponse) {
try {
dfAppRoot.getMitigationMgr().handleMitigationResponse(mitigationResponse.mitigationKey, mitigationResponse.mitigating);
} catch (Throwable e) {
log.error("Excepted trying to process mitigation response "+mitigationResponse.mitigationKey+" mitigating : "+mitigationResponse.mitigating );
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
}
@Override
public void endMitigation(String mitigationKey) throws ExceptionControlApp {
try {
invokeDecoupledSerially(ACTION_END_MITIGATION, mitigationKey);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to invokeDecoupledSerialiy " + ACTION_END_MITIGATION + " " + mitigationKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp("Excepted trying to invokeDecoupledSerially. "+ ACTION_END_MITIGATION + " " + mitigationKey, e);
}
}
protected synchronized void decoupledEndMitigation(String mitigationKey) {
fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY, "Ending mitigation of " + Mitigation.getPrintableMitigationTarget(mitigationKey));
/* Notify amsRep to stop monitoring for mitigation of this attack. */
try {
String msg = "MitigationDriverLocal instructs AMSRep to stop monitoring for traffic per " + mitigationKey;
log.info(msg);
dfAppRoot.getAMSRep().stopMonitoring(mitigationKey);
} catch (Throwable e) {
log.error("Excepted in stopMonitoring invocation", e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MODERATE_HEALTH_ISSUE);
}
Mitigation mitigation;
try {
mitigation = Mitigation.getMitigation(mitigationKey);
} catch (Throwable e) {
log.error("Failed to inflate Mitigation from row ", e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE,"Failed to end mitigation of "+Mitigation.getPrintableMitigationTarget(mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
/* End the diversion itself per each dvsnInfo in this mitigation, and remove the trafficFloor from pn's repo */
if(mitigation.dvsnInfoKeys == null || mitigation.dvsnInfoKeys.isEmpty()) return;
//fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY,"Ending diversions for "+Mitigation.getPrintableMitigationTarget(mitigationKey));
DvsnInfo dvsnInfo;
for(String dvsnInfoKey : mitigation.dvsnInfoKeys) {
try {
dvsnInfo = DvsnInfo.getDvsnInfo(dvsnInfoKey);
} catch (Throwable e) {
log.error("Failed to inflate dvsnInfo record " + dvsnInfoKey, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to end mitigation of " + Mitigation.getPrintableMitigationTarget(mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
continue;
}
if(dvsnInfo != null) {
// endDvsnPerDvsnInfo(dvsnInfo);//I moved it below to a new finally.
/* Remove the security configuration from all mitigating AMSs. */
try {
dfAppRoot.getAMSRep().removeSecurityConfiguration(dvsnInfoKey);
} catch (Throwable e) {
log.error("Failed to removeSecurityConfiguration " + dvsnInfoKey, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to end mitigation of " + Mitigation.getPrintableMitigationTarget(mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
finally
{
endDvsnPerDvsnInfo(dvsnInfo);
}
}
/* Remove the mitigation record from mitigations repo, and remove dvsnInfos records from dvsnInfos repo. */
try {
dfAppRoot.dvsnInfosRepo.deleteRow(dvsnInfoKey);
} catch (Throwable e) {
log.error("Failed to delete dvsnInfosRepo row "+dvsnInfoKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
}
@Override
public void netNodeStatusDowned(String netNodeLabel, HealthStatus healthStatus) {
try {
invokeDecoupledSerially(ACTION_NETNODE_DOWNED, netNodeLabel);
} catch (Throwable e) {
log.error("Failed to invoke handling of status change of netNode " + netNodeLabel, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
protected void decoupledNetNodeDowned(String netNodeLabel) {
Hashtable<String,Object> dvsnInfoRow;
DvsnInfo dvsnInfo = null;
try {
Hashtable<String,Hashtable<String,Object>> dvsnInfoTable = dfAppRoot.dvsnInfosRepo.getTable();
Iterator<Map.Entry<String,Hashtable<String,Object>>> iter = dvsnInfoTable.entrySet().iterator();
String currentNetNodeLabel; boolean hasActiveDvsnsForMitigation;
while(iter.hasNext()) {
dvsnInfoRow = iter.next().getValue();
currentNetNodeLabel = (String) dvsnInfoRow.get(DvsnInfo.NETNODE_LABEL);
if(! currentNetNodeLabel.equals(netNodeLabel)) continue; // Some other netNode
/* End this particular diversion from the netNode. */
dvsnInfo = new DvsnInfo(dvsnInfoRow);
endDvsnPerDvsnInfo(dvsnInfo);
/* Check if there are any active diversions left for this mitigation. If not notify not mitigating. */
hasActiveDvsnsForMitigation = Mitigation.hasActiveDvsns(dvsnInfo.mitigationKey);
if(!hasActiveDvsnsForMitigation) {
decoupledEndMitigation(dvsnInfo.mitigationKey); // Remove all configurations related to this mitigation
MitigationResponse mitigationResponse = new MitigationResponse(dvsnInfo.mitigationKey, false);
fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY, "Ending mitigation of "+ Mitigation.getPrintableMitigationTarget(dvsnInfo.mitigationKey)
+ " - no live netNodes left to divert traffic from.");
invokeDecoupledSerially(ACTION_RESPOND_MITIGATION, mitigationResponse);
}
}
} catch (Throwable e) {
String msg = "Failed to properly handle NetNodeDowned";
log.error(msg, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
protected void endDvsnPerDvsnInfo(DvsnInfo dvsnInfo) {
/* End the diversion itself and remove the trafficFloor from pn's repo */
if(dvsnInfo.tFloorKey != null) {
fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY,"Ending diversion of "+Mitigation.getPrintableMitigationTarget(dvsnInfo.mitigationKey));
try {
dfAppRoot.getDvsnRep().endDvsn(dvsnInfo.mitigationKey, dvsnInfo.tFloorKey);
} catch (Throwable e) {
String msg = "Failed to end diversion for ";
log.error("Failed to end diverson from " + dvsnInfo.tFloorKey + " for " + dvsnInfo.mitigationKey, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE,msg + Mitigation.getPrintableMitigationTarget(dvsnInfo.mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
String pnKey = null;
try {
pnKey = (String) dfAppRoot.mitigationsRepo.getCellValue(dvsnInfo.mitigationKey, Mitigation.PNKEY);
dfAppRoot.pNsRepo.deleteCell(pnKey, PN.TRAFFIC_FLOOR_KEY_PREFIX + dvsnInfo.tFloorKey);
dfAppRoot.mitigationsRepo.deleteCell(dvsnInfo.mitigationKey, Mitigation.TRAFFIC_FLOOR_KEY_PREFIX +
dvsnInfo.tFloorKey);
dvsnInfo.tFloorKey = null;
} catch (Throwable e) {
log.error("Failed to delete traffic floor cell from PN row "+pnKey+" "+dvsnInfo.tFloorKey, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
/* Remove topological preparations for this diversion. */
try {
String msgTrying = "MitigationDriverLocal is removing topological preparation as well as security configuration for diversion ";
log.info(msgTrying + dvsnInfo.key);
dvsnInfo = dfAppRoot.getDvsnRep().unprepareForDvsn(dvsnInfo.mitigationKey, dvsnInfo.key);
} catch (Throwable e) {
String msgFailed = "MitigationDriverLocal failed removing topological preparation as well as security configuration for diversion ";
log.error(msgFailed + dvsnInfo.key, e);
//fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE,msgFailed + dvsnInfo.key);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
if ( dvsnInfo == null ) {
String msgFailed = "MitigationDriverLocal failed removing topological preparation as well as security configuration for diversion ";
log.error(msgFailed);
//fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE,msgFailed);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
return;
}
/* Mark trafficDiverted as false. */
dvsnInfo.trafficDiverted = false;
try {
dfAppRoot.dvsnInfosRepo.setRow(dvsnInfo.key, dvsnInfo.toRow());
} catch (Throwable e) {
log.error("Failed to record in DvsnInfo that traffic is no longer diverted " + dvsnInfo.key, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to end diversion of " + Mitigation.getPrintableMitigationTarget(dvsnInfo.mitigationKey));
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
@Override
public void handleFailedAMSs(List<String> failedAMSs) {
Hashtable<String,Object> dvsnInfoRow;
DvsnInfo dvsnInfo = null;
try {
Hashtable<String,Hashtable<String,Object>> dvsnInfoTable = dfAppRoot.dvsnInfosRepo.getTable();
Iterator<Map.Entry<String,Hashtable<String,Object>>> iter = dvsnInfoTable.entrySet().iterator();
boolean dvsnInfoAffected; boolean hasActiveDvsnsForMitigation;
while(iter.hasNext()) {
dvsnInfoRow = iter.next().getValue();
dvsnInfo = new DvsnInfo(dvsnInfoRow);
dvsnInfoAffected = dvsnInfo.containsDvsnToAMSs(failedAMSs);
if(!dvsnInfoAffected) continue;
endDvsnPerDvsnInfo(dvsnInfo);
/* Check if there are any active diversions left for this mitigation. If not notify not mitigating. */
hasActiveDvsnsForMitigation = Mitigation.hasActiveDvsns(dvsnInfo.mitigationKey);
if(!hasActiveDvsnsForMitigation) {
decoupledEndMitigation(dvsnInfo.mitigationKey); // Remove all configurations related to this mitigation
MitigationResponse mitigationResponse = new MitigationResponse(dvsnInfo.mitigationKey, false);
String message = "Ending mitigation of " + Mitigation.getPrintableMitigationTarget(dvsnInfo.mitigationKey)
+ " - no live netNodes left to divert traffic from.";
fMain.getFR().logRecord(DFAppRoot.FR_DF_SECURITY, message);
log.debug(message);
invokeDecoupledSerially(ACTION_RESPOND_MITIGATION, mitigationResponse);
}
}
} catch (Throwable e) {
String msg = "Failed to properly handle handleFailedAMSs";
log.error(msg, e);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
}
@Override
protected void actionSwitcher(int actionCode, Object param) {
switch(actionCode) {
case ACTION_RESERVED:
break;
case ACTION_MITIGATE:
decoupledMitigate((String) param);
break;
case ACTION_END_MITIGATION:
decoupledEndMitigation((String) param);
break;
case ACTION_RESPOND_MITIGATION:
decoupledRespondMitigation((MitigationResponse) param);
break;
case ACTION_ADD_NETNODE:
decoupledAddNetNode((String) param);
break;
case ACTION_ADD_PN:
decoupledAddPN((String) param);
break;
case ACTION_REMOVE_PN:
decoupledRemovePN((String) param);
break;
case ACTION_NETNODE_DOWNED:
decoupledNetNodeDowned((String) param);
break;
default:
break;
}
}
}