Adding route blackhole

Change-Id: I70212b0ab91e628e8528bca896c3aecc499e31b1
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/AppConfigHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/AppConfigHandler.java
index f54ee9e..034ddfe 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/AppConfigHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/AppConfigHandler.java
@@ -16,6 +16,8 @@
 package org.onosproject.segmentrouting;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
@@ -62,6 +64,9 @@
         SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
         deviceService.getAvailableDevices().forEach(device -> {
             populateVRouter(device.id(), getMacAddresses(config));
+            config.blackholeIPs().forEach(ipPrefix -> {
+                srManager.routingRulePopulator.populateDefaultRouteBlackhole(device.id(), ipPrefix);
+            });
         });
     }
 
@@ -86,6 +91,14 @@
 
             revokeVRouter(device.id(), prevMacAddresses);
             populateVRouter(device.id(), macAddresses);
+            Set<IpPrefix> toRemove = Sets.difference(prevConfig.blackholeIPs(), config.blackholeIPs());
+            toRemove.forEach(ipPrefix -> {
+                srManager.routingRulePopulator.removeDefaultRouteBlackhole(device.id(), ipPrefix);
+            });
+            Set<IpPrefix> toAdd = Sets.difference(config.blackholeIPs(), prevConfig.blackholeIPs());
+            toAdd.forEach(ipPrefix -> {
+                srManager.routingRulePopulator.populateDefaultRouteBlackhole(device.id(), ipPrefix);
+            });
         });
 
     }
@@ -100,11 +113,14 @@
         SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
         deviceService.getAvailableDevices().forEach(device -> {
             revokeVRouter(device.id(), getMacAddresses(prevConfig));
+            prevConfig.blackholeIPs().forEach(ipPrefix -> {
+                srManager.routingRulePopulator.removeDefaultRouteBlackhole(device.id(), ipPrefix);
+            });
         });
     }
 
     /**
-     * Populates initial vRouter rules.
+     * Populates initial vRouter and blackhole rules.
      *
      * @param deviceId device ID
      */
@@ -112,6 +128,9 @@
         SegmentRoutingAppConfig config =
                 srManager.cfgService.getConfig(srManager.appId, SegmentRoutingAppConfig.class);
         populateVRouter(deviceId, getMacAddresses(config));
+        config.blackholeIPs().forEach(ipPrefix -> {
+            srManager.routingRulePopulator.populateDefaultRouteBlackhole(deviceId, ipPrefix);
+        });
     }
 
     private void populateVRouter(DeviceId deviceId, Set<MacAddress> pendingAdd) {
@@ -166,7 +185,9 @@
             if (srManager.deviceConfiguration.isEdgeDevice(deviceId)) {
                 return true;
             }
-        } catch (DeviceConfigNotFoundException e) { }
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn("Device configuration for {} is not present.", deviceId);
+        }
         return false;
     }
 }
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 094d287..64fe2fc 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -1378,6 +1378,75 @@
      * priority Bridging Table entries to a group that contains all ports of
      * its subnet.
      *
+     * @param address the address to block
+     * @param deviceId switch ID to set the rules
+     */
+    void populateDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address) {
+        updateDefaultRouteBlackhole(deviceId, address, true);
+    }
+
+    /**
+     * Populates a forwarding objective to send packets that miss other high
+     * priority Bridging Table entries to a group that contains all ports of
+     * its subnet.
+     *
+     * @param address the address to block
+     * @param deviceId switch ID to set the rules
+     */
+    void removeDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address) {
+        updateDefaultRouteBlackhole(deviceId, address, false);
+    }
+
+    private void updateDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address, boolean install) {
+        try {
+            if (srManager.deviceConfiguration.isEdgeDevice(deviceId)) {
+
+                TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+                if (address.isIp4()) {
+                    sbuilder.matchIPDst(address);
+                    sbuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
+                } else {
+                    sbuilder.matchIPv6Dst(address);
+                    sbuilder.matchEthType(EthType.EtherType.IPV6.ethType().toShort());
+                }
+
+                TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+                tBuilder.transition(60);
+                tBuilder.wipeDeferred();
+
+                ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
+                fob.withFlag(Flag.SPECIFIC)
+                        .withSelector(sbuilder.build())
+                        .withTreatment(tBuilder.build())
+                        .withPriority(getPriorityFromPrefix(address))
+                        .fromApp(srManager.appId)
+                        .makePermanent();
+
+                log.debug("{} blackhole forwarding objectives for dev: {}",
+                        install ? "Installing" : "Removing", deviceId);
+                ObjectiveContext context = new DefaultObjectiveContext(
+                        (objective) -> log.debug("Forward for {} {}", deviceId,
+                                install ? "installed" : "removed"),
+                        (objective, error) -> log.warn("Failed to {} forward for {}: {}",
+                                install ? "install" : "remove", deviceId, error));
+                if (install) {
+                    srManager.flowObjectiveService.forward(deviceId, fob.add(context));
+                } else {
+                    srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
+                }
+            }
+        } catch (DeviceConfigNotFoundException e) {
+            log.info("Not populating blackhole for un-configured device {}", deviceId);
+        }
+
+    }
+
+    /**
+     * Populates a forwarding objective to send packets that miss other high
+     * priority Bridging Table entries to a group that contains all ports of
+     * its subnet.
+     *
      * @param deviceId switch ID to set the rules
      */
     void populateSubnetBroadcastRule(DeviceId deviceId) {
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
index 3e4d17c..9741f78 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
@@ -19,6 +19,7 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.ConnectPoint;
@@ -38,14 +39,68 @@
     // TODO We might want to move SUPPRESS_HOST_BY_PROVIDER to Component Config
     private static final String SUPPRESS_HOST_BY_PROVIDER = "suppressHostByProvider";
     private static final String MPLS_ECMP = "MPLS-ECMP";
+    private static final String BLACKHOLE_IPS = "blackholeIps";
 
     @Override
     public boolean isValid() {
         return hasOnlyFields(VROUTER_MACS, SUPPRESS_SUBNET,
-                SUPPRESS_HOST_BY_PORT, SUPPRESS_HOST_BY_PROVIDER, MPLS_ECMP) &&
+                SUPPRESS_HOST_BY_PORT, SUPPRESS_HOST_BY_PROVIDER, MPLS_ECMP, BLACKHOLE_IPS) &&
                 vRouterMacs() != null &&
                 suppressSubnet() != null && suppressHostByPort() != null &&
-                suppressHostByProvider() != null;
+                suppressHostByProvider() != null &&
+                blackholeIPs() != null;
+    }
+
+    /**
+     * Gets ips to blackhole from the config.
+     *
+     * @return Set of ips to blackhole, empty is not specified,
+     *         or null if not valid
+     */
+    public Set<IpPrefix> blackholeIPs() {
+        if (!object.has(BLACKHOLE_IPS)) {
+            return ImmutableSet.of();
+        }
+
+        ImmutableSet.Builder<IpPrefix> builder = ImmutableSet.builder();
+        ArrayNode arrayNode = (ArrayNode) object.path(BLACKHOLE_IPS);
+        for (JsonNode jsonNode : arrayNode) {
+            IpPrefix address;
+
+            String addrStr = jsonNode.asText(null);
+            if (addrStr == null) {
+                return null;
+            }
+            try {
+                address = IpPrefix.valueOf(addrStr);
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+
+            builder.add(address);
+        }
+        return builder.build();
+    }
+
+    /**
+     * Sets ips to blackhole to the config.
+     *
+     * @param blackholeIps a set of ips to blackhole
+     * @return this {@link SegmentRoutingAppConfig}
+     */
+    public SegmentRoutingAppConfig setBalckholeIps(Set<IpPrefix> blackholeIps) {
+        if (blackholeIps == null) {
+            object.remove(BLACKHOLE_IPS);
+        } else {
+            ArrayNode arrayNode = mapper.createArrayNode();
+
+            blackholeIps.forEach(ip -> {
+                arrayNode.add(ip.toString());
+            });
+
+            object.set(BLACKHOLE_IPS, arrayNode);
+        }
+        return this;
     }
 
     /**
@@ -262,6 +317,7 @@
                 .add("suppressHostByPort", suppressHostByPort())
                 .add("suppressHostByProvider", suppressHostByProvider())
                 .add("mplsEcmp", mplsEcmp())
+                .add("blackholeIps", blackholeIPs())
                 .toString();
     }
 }
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
index 72b1ef5..2a10f56 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.ImmutableSet;
 import org.junit.Before;
 import org.junit.Test;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.TestApplicationId;
@@ -55,6 +56,8 @@
     private static final String PROVIDER_1 = "org.onosproject.provider.host";
     private static final String PROVIDER_2 = "org.onosproject.netcfghost";
     private static final String PROVIDER_3 = "org.onosproject.anotherprovider";
+    private static final IpPrefix BLACKHOLE_IP = IpPrefix.valueOf("10.0.0.0/8");
+    private static final IpPrefix BLACKHOLE_IP_2 = IpPrefix.valueOf("20.0.0.0/8");
 
     /**
      * Initialize test related variables.
@@ -263,6 +266,34 @@
         assertTrue(supprsuppressHostByProvider.contains(PROVIDER_3));
     }
 
+    /**
+     * Tests BlackHoleIps getter.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testBlackHoleIps() throws Exception {
+        Set<IpPrefix> blackHoleIps = config.blackholeIPs();
+        assertNotNull("BlackHoleIps should not be null", blackHoleIps);
+        assertThat(blackHoleIps.size(), is(1));
+        assertTrue(blackHoleIps.contains(BLACKHOLE_IP));
+    }
+
+    /**
+     * Tests BlackHoleIps setter.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testSetBlackHoleIps() throws Exception {
+
+        config.setBalckholeIps(ImmutableSet.of(BLACKHOLE_IP_2));
+
+        Set<IpPrefix> blackHoleIps = config.blackholeIPs();
+        assertThat(blackHoleIps.size(), is(1));
+        assertTrue(blackHoleIps.contains(BLACKHOLE_IP_2));
+    }
+
     private class MockDelegate implements ConfigApplyDelegate {
         @Override
         public void onApply(Config config) {
diff --git a/apps/segmentrouting/app/src/test/resources/app.json b/apps/segmentrouting/app/src/test/resources/app.json
index dab6384..2e59b0d 100644
--- a/apps/segmentrouting/app/src/test/resources/app.json
+++ b/apps/segmentrouting/app/src/test/resources/app.json
@@ -14,5 +14,8 @@
   "suppressHostByProvider" : [
       "org.onosproject.provider.host",
       "org.onosproject.netcfghost"
+  ],
+  "blackholeIps": [
+    "10.0.0.0/8"
   ]
 }
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
index 04d581a..d834b48 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa2Pipeline.java
@@ -284,6 +284,15 @@
         return false;
     }
 
+    /**
+     * Determines whether this driver supports installing a clearDeferred action on table 30.
+     *
+     * @return true if required
+     */
+    protected boolean supportsUnicastBlackHole() {
+        return true;
+    }
+
     //////////////////////////////////////
     //  Flow Objectives
     //////////////////////////////////////
@@ -1462,6 +1471,15 @@
             tb.transition(ACL_TABLE);
         }
 
+        if (fwd.treatment() != null && fwd.treatment().clearedDeferred()) {
+            if (supportsUnicastBlackHole()) {
+                tb.wipeDeferred();
+            } else {
+                log.warn("Clear Deferred is not supported Unicast Routing Table on device {}", deviceId);
+                return Collections.emptySet();
+            }
+        }
+
         FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
                 .fromApp(fwd.appId())
                 .withPriority(fwd.priority())
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java
index 7377a20..13fe2b1 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/Ofdpa3Pipeline.java
@@ -65,7 +65,10 @@
 
 import static org.onlab.packet.MacAddress.NONE;
 import static org.onosproject.driver.extensions.Ofdpa3MplsType.VPWS;
-import static org.onosproject.net.flow.criteria.Criterion.Type.*;
+import static org.onosproject.net.flow.criteria.Criterion.Type.INNER_VLAN_VID;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
+import static org.onosproject.net.flow.criteria.Criterion.Type.TUNNEL_ID;
+import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
 import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -100,6 +103,11 @@
     }
 
     @Override
+    protected boolean supportsUnicastBlackHole() {
+        return true;
+    }
+
+    @Override
     protected void processFilter(FilteringObjective filteringObjective,
                                  boolean install,
                                  ApplicationId applicationId) {
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OvsOfdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OvsOfdpa2Pipeline.java
index 013b98e..3aa8865 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OvsOfdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/ofdpa/OvsOfdpa2Pipeline.java
@@ -50,4 +50,9 @@
     protected boolean supportPuntGroup() {
         return true;
     }
+
+    @Override
+    protected boolean supportsUnicastBlackHole() {
+        return true;
+    }
 }