Changes to vRouter to accomodate ofdpa and softrouter pipelines.
Adding arp-spa to flow from vRouter to distinguish between multiple untagged
interfaces with the same macAddress.

Change-Id: Ifd6e00f70c538c780c0f5728d9ba960a4c70b1db
diff --git a/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java b/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
index 2294435..b78b0ff 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/impl/ControlPlaneRedirectManager.java
@@ -22,6 +22,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.EthType;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.incubator.net.intf.Interface;
@@ -40,8 +41,10 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.DefaultNextObjective;
 import org.onosproject.net.flowobjective.FlowObjectiveService;
 import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.routing.RoutingService;
 import org.onosproject.routing.config.RouterConfig;
@@ -110,7 +113,7 @@
                 routingAppId, RoutingService.ROUTER_CONFIG_CLASS);
 
         if (config == null) {
-            log.info("Router config not available");
+            log.warn("Router config not available");
             return;
         }
 
@@ -145,6 +148,23 @@
         PortNumber controlPlanePort = controlPlaneConnectPoint.port();
 
         for (InterfaceIpAddress ip : intf.ipAddresses()) {
+            // create nextObjectives for forwarding to this interface and the
+            // controlPlaneConnectPoint
+            int cpNextId, intfNextId;
+            if (intf.vlan() == VlanId.NONE) {
+                cpNextId = createNextObjective(deviceId, controlPlanePort,
+                               VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
+                               true);
+                intfNextId = createNextObjective(deviceId, intf.connectPoint().port(),
+                               VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
+                               true);
+            } else {
+                cpNextId = createNextObjective(deviceId, controlPlanePort,
+                                               intf.vlan(), false);
+                intfNextId = createNextObjective(deviceId, intf.connectPoint().port(),
+                                                 intf.vlan(), false);
+            }
+
             // IPv4 to router
             TrafficSelector toSelector = DefaultTrafficSelector.builder()
                     .matchInPort(intf.connectPoint().port())
@@ -154,12 +174,8 @@
                     .matchIPDst(ip.ipAddress().toIpPrefix())
                     .build();
 
-            TrafficTreatment toTreatment = DefaultTrafficTreatment.builder()
-                    .setOutput(controlPlanePort)
-                    .build();
-
             flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(toSelector, toTreatment, true));
+                    buildForwardingObjective(toSelector, null, cpNextId, true));
 
             // IPv4 from router
             TrafficSelector fromSelector = DefaultTrafficSelector.builder()
@@ -170,12 +186,8 @@
                     .matchIPSrc(ip.ipAddress().toIpPrefix())
                     .build();
 
-            TrafficTreatment intfTreatment = DefaultTrafficTreatment.builder()
-                    .setOutput(intf.connectPoint().port())
-                    .build();
-
             flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(fromSelector, intfTreatment, true));
+                    buildForwardingObjective(fromSelector, null, intfNextId, true));
 
             // ARP to router
             toSelector = DefaultTrafficSelector.builder()
@@ -184,13 +196,12 @@
                     .matchVlanId(intf.vlan())
                     .build();
 
-            toTreatment = DefaultTrafficTreatment.builder()
-                    .setOutput(controlPlanePort)
+            TrafficTreatment puntTreatment = DefaultTrafficTreatment.builder()
                     .punt()
                     .build();
 
             flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(toSelector, toTreatment, true));
+                    buildForwardingObjective(toSelector, puntTreatment, cpNextId, true));
 
             // ARP from router
             fromSelector = DefaultTrafficSelector.builder()
@@ -198,15 +209,11 @@
                     .matchEthSrc(intf.mac())
                     .matchVlanId(intf.vlan())
                     .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                    .build();
-
-            intfTreatment = DefaultTrafficTreatment.builder()
-                    .setOutput(intf.connectPoint().port())
-                    .punt()
+                    .matchArpSpa(ip.ipAddress().getIp4Address())
                     .build();
 
             flowObjectiveService.forward(deviceId,
-                    buildForwardingObjective(fromSelector, intfTreatment, true));
+            buildForwardingObjective(fromSelector, puntTreatment, intfNextId, true));
         }
     }
 
@@ -219,33 +226,85 @@
                 .matchIPProtocol((byte) OSPF_IP_PROTO)
                 .build();
 
-        TrafficTreatment toTreatment = DefaultTrafficTreatment.builder()
-                .setOutput(controlPlaneConnectPoint.port())
-                .build();
-
+        // create nextObjectives for forwarding to the controlPlaneConnectPoint
+        DeviceId deviceId = controlPlaneConnectPoint.deviceId();
+        PortNumber controlPlanePort = controlPlaneConnectPoint.port();
+        int cpNextId;
+        if (intf.vlan() == VlanId.NONE) {
+            cpNextId = createNextObjective(deviceId, controlPlanePort,
+                           VlanId.vlanId(SingleSwitchFibInstaller.ASSIGNED_VLAN),
+                           true);
+        } else {
+            cpNextId = createNextObjective(deviceId, controlPlanePort,
+                                           intf.vlan(), false);
+        }
+        log.debug("ospf flows intf:{} nextid:{}", intf, cpNextId);
         flowObjectiveService.forward(controlPlaneConnectPoint.deviceId(),
-                buildForwardingObjective(toSelector, toTreatment, ospfEnabled));
+                buildForwardingObjective(toSelector, null, cpNextId, ospfEnabled));
     }
 
     /**
-     * Builds a forwarding objective from the given selector and treatment.
+     * Creates a next objective for forwarding to a port. Handles metadata for
+     * some pipelines that require vlan information for egress port.
+     *
+     * @param deviceId the device on which the next objective is being created
+     * @param portNumber the egress port
+     * @param vlanId vlan information for egress port
+     * @param popVlan if vlan tag should be popped or not
+     * @return nextId of the next objective created
+     */
+    private int createNextObjective(DeviceId deviceId, PortNumber portNumber,
+                                    VlanId vlanId, boolean popVlan) {
+        int nextId = flowObjectiveService.allocateNextId();
+        NextObjective.Builder nextObjBuilder = DefaultNextObjective
+                .builder().withId(nextId)
+                .withType(NextObjective.Type.SIMPLE)
+                .fromApp(appId);
+
+        TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
+        if (popVlan) {
+            ttBuilder.popVlan();
+        }
+        ttBuilder.setOutput(portNumber);
+
+        // setup metadata to pass to nextObjective - indicate the vlan on egress
+        // if needed by the switch pipeline.
+        TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
+        metabuilder.matchVlanId(vlanId);
+
+        nextObjBuilder.withMeta(metabuilder.build());
+        nextObjBuilder.addTreatment(ttBuilder.build());
+        log.debug("Submited next objective {} in device {} for port/vlan {}/{}",
+                nextId, deviceId, portNumber, vlanId);
+        flowObjectiveService.next(deviceId, nextObjBuilder.add());
+        return nextId;
+    }
+
+    /**
+     * Builds a forwarding objective from the given selector, treatment and nextId.
      *
      * @param selector selector
-     * @param treatment treatment
+     * @param treatment treatment to apply to packet, can be null
+     * @param nextId next objective to point to for forwarding packet
      * @param add true to create an add objective, false to create a remove
      *            objective
      * @return forwarding objective
      */
     private ForwardingObjective buildForwardingObjective(TrafficSelector selector,
                                                          TrafficTreatment treatment,
+                                                         int nextId,
                                                          boolean add) {
-
-        ForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder()
-                .withSelector(selector)
-                .withTreatment(treatment)
-                .fromApp(appId)
-                .withPriority(PRIORITY)
-                .withFlag(ForwardingObjective.Flag.VERSATILE);
+        DefaultForwardingObjective.Builder fobBuilder = DefaultForwardingObjective.builder();
+        fobBuilder.withSelector(selector);
+        if (treatment != null) {
+            fobBuilder.withTreatment(treatment);
+        }
+        if (nextId != -1) {
+            fobBuilder.nextStep(nextId);
+        }
+        fobBuilder.fromApp(appId)
+            .withPriority(PRIORITY)
+            .withFlag(ForwardingObjective.Flag.VERSATILE);
 
         return add ? fobBuilder.add() : fobBuilder.remove();
     }