Fix: process ICMP packets in controller

Change-Id: I03ccd687cc524fa442ad322c8d8c5ba202ffede4
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index 18bad9b..b59307e 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -66,6 +66,7 @@
     public static final int PRIORITY_FLOATING_EXTERNAL = 41000;
     public static final int PRIORITY_STATEFUL_SNAT_RULE = 40500;
     public static final int PRIORITY_ICMP_RULE = 43000;
+    public static final int PRIORITY_ICMP_REQUEST_RULE = 40800;
     public static final int PRIORITY_INTERNAL_ROUTING_RULE = 28000;
     public static final int PRIORITY_EXTERNAL_ROUTING_RULE = 25000;
     public static final int PRIORITY_EXTERNAL_FLOATING_ROUTING_RULE = 27000;
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
index 0e2b46b..222ce3e 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -18,6 +18,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
@@ -70,6 +71,7 @@
 import static org.onosproject.openstacknetworking.api.Constants.ARP_BROADCAST_MODE;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ADMIN_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_REQUEST_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_INTERNAL_ROUTING_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE;
@@ -251,14 +253,12 @@
                         netType,
                         install));
 
-        if (!getStatefulSnatFlag()) {
-            // install rules to punt ICMP packets to controller at gateway node
-            // this rule is only valid for stateless ICMP SNAT case
-            osNodeService.completeNodes(GATEWAY).forEach(gNode ->
-                    setReactiveGatewayIcmpRule(
-                            IpAddress.valueOf(osSubnet.getGateway()),
-                            gNode.intgBridge(), install));
-        }
+        // install rules to punt ICMP packets to controller at gateway node
+        // this rule is only valid for stateless ICMP SNAT case
+        osNodeService.completeNodes(GATEWAY).forEach(gNode ->
+                setReactiveGatewayIcmpRule(
+                        IpAddress.valueOf(osSubnet.getGateway()),
+                        gNode.intgBridge(), install));
 
         final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
         log.debug(updateStr + "IP to {}", osSubnet.getGateway());
@@ -336,11 +336,21 @@
     }
 
     private void setReactiveGatewayIcmpRule(IpAddress gatewayIp, DeviceId deviceId, boolean install) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPProtocol(IPv4.PROTOCOL_ICMP)
-                .matchIPDst(gatewayIp.getIp4Address().toIpPrefix())
-                .build();
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        int icmpRulePriority;
+
+        if (getStatefulSnatFlag()) {
+            sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+                    .matchIcmpType(ICMP.TYPE_ECHO_REQUEST);
+            icmpRulePriority = PRIORITY_ICMP_REQUEST_RULE;
+        } else {
+            sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+                    .matchIPDst(gatewayIp.getIp4Address().toIpPrefix());
+            icmpRulePriority = PRIORITY_ICMP_RULE;
+        }
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
                 .punt()
@@ -349,9 +359,9 @@
         osFlowRuleService.setRule(
                 appId,
                 deviceId,
-                selector,
+                sBuilder.build(),
                 treatment,
-                PRIORITY_ICMP_RULE,
+                icmpRulePriority,
                 Constants.GW_COMMON_TABLE,
                 install);
     }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
index 55b1a41..d74e276 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -17,6 +17,7 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.onlab.packet.Ethernet;
@@ -111,6 +112,7 @@
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SNAT_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
+import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.FLAT;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VLAN;
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_STATEFUL_SNAT;
@@ -831,10 +833,17 @@
             return;
         }
 
+        String netId = osNetworkAdminService.subnet(routerIface.getSubnetId()).getNetworkId();
+
         Map<OpenstackNode, PortRange> gwPortRangeMap = getAssignedPortsForGateway(
                 ImmutableList.copyOf(osNodeService.nodes(GATEWAY)));
 
         osNodeService.completeNodes(GATEWAY).forEach(gwNode -> {
+            instancePortService.instancePorts(netId)
+                    .stream()
+                    .filter(port -> port.state() == ACTIVE)
+                    .forEach(port -> setGatewayToInstanceDownstreamRule(
+                            gwNode, port, install));
             if (install) {
                 PortRange gwPortRange = gwPortRangeMap.get(gwNode);
 
@@ -1005,10 +1014,18 @@
                                                IpPrefix gatewayIp,
                                                boolean install) {
 
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(gatewayIp)
-                .build();
+        Set<TrafficSelector> selectors = Sets.newConcurrentHashSet();
+
+        ImmutableSet<Byte> ipv4Proto = ImmutableSet.of(IPv4.PROTOCOL_TCP, IPv4.PROTOCOL_UDP);
+
+        ipv4Proto.forEach(proto -> {
+            TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+            sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPDst(gatewayIp)
+                    .matchIPProtocol(proto);
+            selectors.add(sBuilder.build());
+        });
+
 
         ExtensionTreatment natTreatment = RulePopulatorUtil
                 .niciraConnTrackTreatmentBuilder(driverService, deviceId)
@@ -1021,14 +1038,16 @@
                 .extension(natTreatment, deviceId)
                 .build();
 
-        osFlowRuleService.setRule(
-                appId,
-                deviceId,
-                selector,
-                treatment,
-                PRIORITY_STATEFUL_SNAT_RULE,
-                GW_COMMON_TABLE,
-                install);
+        selectors.forEach(s -> {
+            osFlowRuleService.setRule(
+                    appId,
+                    deviceId,
+                    s,
+                    treatment,
+                    PRIORITY_STATEFUL_SNAT_RULE,
+                    GW_COMMON_TABLE,
+                    install);
+        });
     }
 
     private void setStatefulSnatUpstreamRule(OpenstackNode gwNode,
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatIcmpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatIcmpHandler.java
index 1e4d002..117312a 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatIcmpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatIcmpHandler.java
@@ -190,7 +190,7 @@
     private class InternalNodeEventListener implements OpenstackNodeListener {
         @Override
         public boolean isRelevant(OpenstackNodeEvent event) {
-            return event.subject().type() == GATEWAY && !getStatefulSnatFlag();
+            return event.subject().type() == GATEWAY;
         }
 
         private boolean isRelevantHelper() {
@@ -265,10 +265,6 @@
                     return;
                 }
 
-                if (getStatefulSnatFlag()) {
-                    return;
-                }
-
                 InboundPacket pkt = context.inPacket();
                 Ethernet ethernet = pkt.parsed();
                 if (ethernet == null || ethernet.getEtherType() != Ethernet.TYPE_IPV4) {