Split tunneling behavior into a separated tunnel bridge

Change-Id: I4de5f21ac9eaf286479f619c960a2319b597a819
diff --git a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
index 503ab08..3588414 100644
--- a/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
+++ b/apps/k8s-networking/api/src/main/java/org/onosproject/k8snetworking/api/Constants.java
@@ -95,6 +95,7 @@
     public static final int PRIORITY_NODE_PORT_RULE = 42000;
     public static final int PRIORITY_NODE_PORT_REMOTE_RULE = 41500;
     public static final int PRIORITY_NODE_PORT_INTER_RULE = 40000;
+    public static final int PRIORITY_DEFAULT_RULE = 0;
 
     // flow table index
     public static final int STAT_INGRESS_TABLE = 0;
@@ -125,6 +126,8 @@
 
     public static final int LOCAL_ENTRY_TABLE = 0;
 
+    public static final int TUN_ENTRY_TABLE = 0;
+
     // CLI item length
     public static final int CLI_ID_LENGTH = 30;
     public static final int CLI_NAME_LENGTH = 30;
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java
index 73ce2ef..0234613 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sFlowRuleManager.java
@@ -70,6 +70,7 @@
 import static org.onosproject.k8snetworking.api.Constants.ROUTING_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.STAT_EGRESS_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.STAT_INGRESS_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.VTAP_EGRESS_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.VTAP_INGRESS_TABLE;
@@ -317,16 +318,32 @@
                 }
                 tBuilder.transition(STAT_EGRESS_TABLE);
             } else {
+                K8sNode localNode = k8sNodeService.node(k8sNetwork.name());
+                tBuilder.setOutput(node.intgToTunPortNum());
+
+                // install flows into tunnel bridge
                 PortNumber portNum = tunnelPortNumByNetId(k8sNetwork.networkId(),
                         k8sNetworkService, node);
-                K8sNode localNode = k8sNodeService.node(k8sNetwork.name());
+                TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                        .extension(buildExtension(
+                                deviceService,
+                                node.tunBridge(),
+                                localNode.dataIp().getIp4Address()),
+                                node.tunBridge())
+                        .setTunnelId(Long.valueOf(k8sNetwork.segmentId()))
+                        .setOutput(portNum)
+                        .build();
 
-                tBuilder.extension(buildExtension(
-                        deviceService,
-                        node.intgBridge(),
-                        localNode.dataIp().getIp4Address()),
-                        node.intgBridge())
-                        .setOutput(portNum);
+                FlowRule remoteFlowRule = DefaultFlowRule.builder()
+                        .forDevice(node.tunBridge())
+                        .withSelector(sBuilder.build())
+                        .withTreatment(treatmentToRemote)
+                        .withPriority(PRIORITY_CIDR_RULE)
+                        .fromApp(appId)
+                        .makePermanent()
+                        .forTable(TUN_ENTRY_TABLE)
+                        .build();
+                applyRule(remoteFlowRule, true);
             }
 
             FlowRule flowRule = DefaultFlowRule.builder()
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java
index 18b9a11..fd0dff0 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sServiceHandler.java
@@ -111,6 +111,7 @@
 import static org.onosproject.k8snetworking.api.Constants.SHIFTED_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.SRC;
 import static org.onosproject.k8snetworking.api.Constants.STAT_EGRESS_TABLE;
+import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.SERVICE_CIDR;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.SERVICE_IP_CIDR_DEFAULT;
 import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.SERVICE_IP_NAT_MODE;
@@ -645,16 +646,33 @@
                 }
                 tBuilder.transition(STAT_EGRESS_TABLE);
             } else {
-                PortNumber portNum = tunnelPortNumByNetId(network.networkId(),
-                        k8sNetworkService, n);
                 K8sNode localNode = k8sNodeService.node(network.name());
 
-                tBuilder.extension(buildExtension(
-                        deviceService,
-                        n.intgBridge(),
-                        localNode.dataIp().getIp4Address()),
-                        n.intgBridge())
-                        .setOutput(portNum);
+                tBuilder.setOutput(n.intgToTunPortNum());
+
+                PortNumber portNum = tunnelPortNumByNetId(network.networkId(),
+                        k8sNetworkService, n);
+
+                // install rules into tunnel bridge
+                TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                        .extension(buildExtension(
+                                deviceService,
+                                n.tunBridge(),
+                                localNode.dataIp().getIp4Address()),
+                                n.tunBridge())
+                        .setTunnelId(Long.valueOf(network.segmentId()))
+                        .setOutput(portNum)
+                        .build();
+
+                k8sFlowRuleService.setRule(
+                        appId,
+                        n.tunBridge(),
+                        sBuilder.build(),
+                        treatmentToRemote,
+                        PRIORITY_CIDR_RULE,
+                        TUN_ENTRY_TABLE,
+                        install
+                );
             }
 
             k8sFlowRuleService.setRule(
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
index 2f994da..03a953e 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
@@ -67,6 +67,7 @@
 import static org.onosproject.k8snetworking.api.Constants.SHIFTED_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.SHIFTED_LOCAL_IP_PREFIX;
 import static org.onosproject.k8snetworking.api.Constants.SRC;
+import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.shiftIpDomain;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
 import static org.onosproject.k8snetworking.util.RulePopulatorUtil.buildExtension;
@@ -159,16 +160,31 @@
                 tBuilder.setEthDst(node.intgBridgeMac())
                         .setOutput(PortNumber.LOCAL);
             } else {
-                PortNumber portNum = tunnelPortNumByNetId(k8sNetwork.networkId(),
-                        k8sNetworkService, node);
                 K8sNode localNode = k8sNodeService.node(k8sNetwork.name());
 
-                tBuilder.extension(buildExtension(
-                        deviceService,
-                        node.intgBridge(),
-                        localNode.dataIp().getIp4Address()),
-                        node.intgBridge())
-                        .setOutput(portNum);
+                tBuilder.setOutput(node.intgToTunPortNum());
+
+                // install flows into tunnel bridge
+                PortNumber portNum = tunnelPortNumByNetId(k8sNetwork.networkId(),
+                        k8sNetworkService, node);
+                TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                        .extension(buildExtension(
+                                deviceService,
+                                node.tunBridge(),
+                                localNode.dataIp().getIp4Address()),
+                                node.tunBridge())
+                        .setTunnelId(Long.valueOf(k8sNetwork.segmentId()))
+                        .setOutput(portNum)
+                        .build();
+
+                k8sFlowRuleService.setRule(
+                        appId,
+                        node.tunBridge(),
+                        sBuilder.build(),
+                        treatmentToRemote,
+                        PRIORITY_GATEWAY_RULE,
+                        TUN_ENTRY_TABLE,
+                        install);
             }
 
             k8sFlowRuleService.setRule(
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
index 56c4398..0165975 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingHandler.java
@@ -61,8 +61,10 @@
 import static org.onosproject.k8snetworking.api.Constants.FORWARDING_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.JUMP_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
+import static org.onosproject.k8snetworking.api.Constants.PRIORITY_DEFAULT_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_SWITCHING_RULE;
 import static org.onosproject.k8snetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
+import static org.onosproject.k8snetworking.api.Constants.TUN_ENTRY_TABLE;
 import static org.onosproject.k8snetworking.api.Constants.VTAG_TABLE;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
 import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.tunnelPortNumByNetId;
@@ -150,7 +152,7 @@
 
     /**
      * Configures the flow rules which are used for L2 packet switching.
-     * Note that these rules will be inserted in switching table (table 5).
+     * Note that these rules will be inserted in switching table (table 80).
      *
      * @param port      kubernetes port object
      * @param install   install flag, add the rule if true, remove it otherwise
@@ -161,7 +163,7 @@
                 // TODO: need to handle IPv6 in near future
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPDst(port.ipAddress().toIpPrefix())
-                .matchTunnelId(getVni(port))
+                // .matchTunnelId(getVni(port))
                 .build();
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
@@ -188,28 +190,86 @@
         k8sNodeService.completeNodes().stream()
                 .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
                 .forEach(remoteNode -> {
-                    PortNumber portNum = tunnelPortNumByNetId(port.networkId(),
-                            k8sNetworkService, remoteNode);
-                    TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                            .extension(buildExtension(
-                                    deviceService,
-                                    remoteNode.intgBridge(),
-                                    localNode.dataIp().getIp4Address()),
-                                    remoteNode.intgBridge())
-                            .setOutput(portNum)
+                    TrafficTreatment treatmentToTunnel = DefaultTrafficTreatment.builder()
+                            .setOutput(remoteNode.intgToTunPortNum())
                             .build();
 
                     k8sFlowRuleService.setRule(
                             appId,
                             remoteNode.intgBridge(),
                             selector,
-                            treatmentToRemote,
+                            treatmentToTunnel,
                             PRIORITY_SWITCHING_RULE,
                             FORWARDING_TABLE,
                             install);
+
+                    PortNumber portNum = tunnelPortNumByNetId(port.networkId(),
+                            k8sNetworkService, remoteNode);
+
+                    TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                            .extension(buildExtension(
+                                    deviceService,
+                                    remoteNode.tunBridge(),
+                                    localNode.dataIp().getIp4Address()),
+                                    remoteNode.tunBridge())
+                            .setTunnelId(getVni(port))
+                            .setOutput(portNum)
+                            .build();
+
+                    k8sFlowRuleService.setRule(
+                            appId,
+                            remoteNode.tunBridge(),
+                            selector,
+                            treatmentToRemote,
+                            PRIORITY_DEFAULT_RULE,
+                            TUN_ENTRY_TABLE,
+                            install);
                 });
     }
 
+    private void setRulesForTunnelBridge(K8sNode node, boolean install) {
+        setRulesForTunnelBridgeByType(node, K8sNetwork.Type.VXLAN, install);
+        setRulesForTunnelBridgeByType(node, K8sNetwork.Type.GRE, install);
+        setRulesForTunnelBridgeByType(node, K8sNetwork.Type.GENEVE, install);
+    }
+
+    private void setRulesForTunnelBridgeByType(K8sNode node, K8sNetwork.Type type, boolean install) {
+
+        PortNumber portNum;
+
+        switch (type) {
+            case VXLAN:
+                portNum = node.vxlanPortNum();
+                break;
+            case GRE:
+                portNum = node.grePortNum();
+                break;
+            case GENEVE:
+                portNum = node.genevePortNum();
+                break;
+            default:
+                return;
+        }
+
+        TrafficSelector inboundSelector = DefaultTrafficSelector.builder()
+                .matchInPort(portNum)
+                .build();
+
+        TrafficTreatment inboundTreatment = DefaultTrafficTreatment.builder()
+                .setOutput(node.tunToIntgPortNum())
+                .build();
+
+        k8sFlowRuleService.setRule(
+                appId,
+                node.tunBridge(),
+                inboundSelector,
+                inboundTreatment,
+                PRIORITY_DEFAULT_RULE,
+                TUN_ENTRY_TABLE,
+                install);
+    }
+
+
     private void setTunnelTagArpFlowRules(K8sPort port, boolean install) {
         setTunnelTagFlowRules(port, Ethernet.TYPE_ARP, install);
     }
@@ -439,6 +499,7 @@
 
             setExtToIntgTunnelTagFlowRules(k8sNode, true);
             setLocalTunnelTagFlowRules(k8sNode, true);
+            setRulesForTunnelBridge(k8sNode, true);
         }
     }
 }
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
index 56039b5..4e430a3 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
@@ -396,6 +396,16 @@
     }
 
     @Override
+    public PortNumber intgToTunPortNum() {
+        return portNumber(intgBridge, intgToTunPatchPortName());
+    }
+
+    @Override
+    public PortNumber tunToIntgPortNum() {
+        return portNumber(tunBridge, tunToIntgPatchPortName());
+    }
+
+    @Override
     public PortNumber extBridgePortNum() {
         if (this.extIntf == null) {
             return null;
@@ -519,6 +529,15 @@
     }
 
     @Override
+    public String tunBridgePortName() {
+        if (mode == PASSTHROUGH) {
+            return TUNNEL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return TUNNEL_BRIDGE;
+        }
+    }
+
+    @Override
     public String intgToExtPatchPortName() {
         if (mode == PASSTHROUGH) {
             return INTEGRATION_TO_EXTERNAL_BRIDGE + "-" + uniqueString(5);
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
index 8931b45..e443880 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
@@ -287,6 +287,13 @@
     String localBridgePortName();
 
     /**
+     * Returns tunnel bridge port name.
+     *
+     * @return tunnel bridge port name
+     */
+    String tunBridgePortName();
+
+    /**
      * Returns integration to external patch port name.
      *
      * @return integration to external patch port name
@@ -385,6 +392,20 @@
     PortNumber extToIntgPatchPortNum();
 
     /**
+     * Returns the integration to tunnel patch port number.
+     *
+     * @return patch port number
+     */
+    PortNumber intgToTunPortNum();
+
+    /**
+     * Returns the tunnel to integration patch port number.
+     *
+     * @return patch port number
+     */
+    PortNumber tunToIntgPortNum();
+
+    /**
      * Returns the external bridge to router port number.
      *
      * @return port number, null if the port does not exist
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java
index 2060712..de8f38c 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java
@@ -68,11 +68,6 @@
             printPortState(deviceService, node.intgBridge(), node.intgBridgePortName());
             printPortState(deviceService, node.intgBridge(), node.intgToExtPatchPortName());
             printPortState(deviceService, node.intgBridge(), node.intgToLocalPatchPortName());
-            if (node.dataIp() != null) {
-                printPortState(deviceService, node.intgBridge(), node.vxlanPortName());
-                printPortState(deviceService, node.intgBridge(), node.grePortName());
-                printPortState(deviceService, node.intgBridge(), node.genevePortName());
-            }
         } else {
             print("%s %s=%s is not available",
                     MSG_ERROR,
@@ -80,6 +75,7 @@
                     node.intgBridge());
         }
 
+        print("");
         print("[External Bridge Status]");
         Device extBridge = deviceService.getDevice(node.extBridge());
         if (extBridge != null) {
@@ -89,7 +85,6 @@
                     extBridge.id(),
                     deviceService.isAvailable(extBridge.id()),
                     extBridge.annotations());
-            printPortState(deviceService, node.extBridge(), node.extBridgePortName());
             printPortState(deviceService, node.extBridge(), node.extToIntgPatchPortName());
         } else {
             print("%s %s=%s is not available",
@@ -98,6 +93,7 @@
                     node.extBridge());
         }
 
+        print("");
         print("[Local Bridge Status]");
         Device localBridge = deviceService.getDevice(node.localBridge());
         if (localBridge != null) {
@@ -107,9 +103,27 @@
                     localBridge.id(),
                     deviceService.isAvailable(localBridge.id()),
                     localBridge.annotations());
-            printPortState(deviceService, node.localBridge(), node.localBridgePortName());
             printPortState(deviceService, node.localBridge(), node.localToIntgPatchPortName());
         }
+
+        print("");
+        print("[Tunnel Bridge Status]");
+        Device tunBridge = deviceService.getDevice(node.tunBridge());
+        if (tunBridge != null) {
+            print("%s %s=%s available=%s %s",
+                    deviceService.isAvailable(tunBridge.id()) ? MSG_OK : MSG_ERROR,
+                    node.tunBridgeName(),
+                    tunBridge.id(),
+                    deviceService.isAvailable(tunBridge.id()),
+                    tunBridge.annotations());
+            printPortState(deviceService, node.tunBridge(), node.tunToIntgPatchPortName());
+
+            if (node.dataIp() != null) {
+                printPortState(deviceService, node.tunBridge(), node.vxlanPortName());
+                printPortState(deviceService, node.tunBridge(), node.grePortName());
+                printPortState(deviceService, node.tunBridge(), node.genevePortName());
+            }
+        }
     }
 
     private void printPortState(DeviceService deviceService,