[CORD-46] Implement L2 switching in Segment Routing

DONE
- Update SpringOpenTTP to support bridging table emulation
- Populate low priority subnet broadcast entry for bridging table
- Move IP entry population to host event handler as well
- Update ArpHandler to handle intra-rack ARP forwarding/flooding
- Move TTL_OUT action from IP table to corresponding group
    Since hardware does not support TTL_OUT in the IP table
- Populate entries to bridging table (MAC learning)
- Emulate src mac table

Not in this submission
- Emulate src-mac table behavior
- Pop vlan in the group instead of the flow

Change-Id: Ib69357c1889ccddaa4daa272d9f5843790ee1a3c
diff --git a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index f827403..bc3ce8c 100644
--- a/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -55,7 +55,6 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 public class RoutingRulePopulator {
-
     private static final Logger log = LoggerFactory
             .getLogger(RoutingRulePopulator.class);
 
@@ -105,13 +104,45 @@
      */
     public void populateIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
                                       MacAddress hostMac, PortNumber outPort) {
-        MacAddress deviceMac;
+        log.debug("Populate IP table entry for host {} at {}:{}",
+                hostIp, deviceId, outPort);
+        ForwardingObjective.Builder fwdBuilder;
         try {
-            deviceMac = config.getDeviceMac(deviceId);
+            fwdBuilder = getForwardingObjectiveBuilder(
+                    deviceId, hostIp, hostMac, outPort);
         } catch (DeviceConfigNotFoundException e) {
             log.warn(e.getMessage() + " Aborting populateIpRuleForHost.");
             return;
         }
+        srManager.flowObjectiveService.
+            forward(deviceId, fwdBuilder.add(new SRObjectiveContext(deviceId,
+                    SRObjectiveContext.ObjectiveType.FORWARDING)));
+        rulePopulationCounter.incrementAndGet();
+    }
+
+    public void revokeIpRuleForHost(DeviceId deviceId, Ip4Address hostIp,
+            MacAddress hostMac, PortNumber outPort) {
+        log.debug("Revoke IP table entry for host {} at {}:{}",
+                hostIp, deviceId, outPort);
+        ForwardingObjective.Builder fwdBuilder;
+        try {
+            fwdBuilder = getForwardingObjectiveBuilder(
+                    deviceId, hostIp, hostMac, outPort);
+        } catch (DeviceConfigNotFoundException e) {
+            log.warn(e.getMessage() + " Aborting revokeIpRuleForHost.");
+            return;
+        }
+        srManager.flowObjectiveService.
+                forward(deviceId, fwdBuilder.remove(new SRObjectiveContext(deviceId,
+                        SRObjectiveContext.ObjectiveType.FORWARDING)));
+    }
+
+    private ForwardingObjective.Builder getForwardingObjectiveBuilder(
+            DeviceId deviceId, Ip4Address hostIp,
+            MacAddress hostMac, PortNumber outPort)
+            throws DeviceConfigNotFoundException {
+        MacAddress deviceMac;
+        deviceMac = config.getDeviceMac(deviceId);
 
         TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
         TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
@@ -127,19 +158,10 @@
         TrafficTreatment treatment = tbuilder.build();
         TrafficSelector selector = sbuilder.build();
 
-        ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                .builder().fromApp(srManager.appId).makePermanent()
+        return DefaultForwardingObjective.builder()
+                .fromApp(srManager.appId).makePermanent()
                 .withSelector(selector).withTreatment(treatment)
                 .withPriority(100).withFlag(ForwardingObjective.Flag.SPECIFIC);
-
-        log.debug("Installing IPv4 forwarding objective "
-                + "for host {} in switch {}", hostIp, deviceId);
-        srManager.flowObjectiveService.
-            forward(deviceId,
-                    fwdBuilder.
-                    add(new SRObjectiveContext(deviceId,
-                                           SRObjectiveContext.ObjectiveType.FORWARDING)));
-        rulePopulationCounter.incrementAndGet();
     }
 
     /**
@@ -186,26 +208,25 @@
         }
 
         TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-
         sbuilder.matchIPDst(ipPrefix);
         sbuilder.matchEthType(Ethernet.TYPE_IPV4);
+        TrafficSelector selector = sbuilder.build();
 
-        NeighborSet ns = null;
+        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+        NeighborSet ns;
+        TrafficTreatment treatment;
 
         // If the next hop is the same as the final destination, then MPLS label
         // is not set.
         if (nextHops.size() == 1 && nextHops.toArray()[0].equals(destSw)) {
-            tbuilder.deferred().decNwTtl();
+            tbuilder.immediate().decNwTtl();
             ns = new NeighborSet(nextHops);
+            treatment = tbuilder.build();
         } else {
-            tbuilder.deferred().copyTtlOut();
             ns = new NeighborSet(nextHops, segmentId);
+            treatment = null;
         }
 
-        TrafficTreatment treatment = tbuilder.build();
-        TrafficSelector selector = sbuilder.build();
-
         if (srManager.getNextObjectiveId(deviceId, ns) <= 0) {
             log.warn("No next objective in {} for ns: {}", deviceId, ns);
             return false;
@@ -216,10 +237,12 @@
                 .fromApp(srManager.appId)
                 .makePermanent()
                 .nextStep(srManager.getNextObjectiveId(deviceId, ns))
-                .withTreatment(treatment)
                 .withSelector(selector)
                 .withPriority(100)
                 .withFlag(ForwardingObjective.Flag.SPECIFIC);
+        if (treatment != null) {
+            fwdBuilder.withTreatment(treatment);
+        }
         log.debug("Installing IPv4 forwarding objective "
                         + "for router IP/subnet {} in switch {}",
                 ipPrefix,
@@ -423,8 +446,6 @@
                         ? VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET)
                         : srManager.getSubnetAssignedVlanId(deviceId, portSubnet);
 
-
-
                 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
                 fob.withKey(Criteria.matchInPort(port.number()))
                 .addCondition(Criteria.matchEthDst(deviceMac))
@@ -469,14 +490,14 @@
         Set<Ip4Address> allIps = new HashSet<Ip4Address>(config.getPortIPs(deviceId));
         allIps.add(routerIp);
         for (Ip4Address ipaddr : allIps) {
-            TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-            TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-            selector.matchEthType(Ethernet.TYPE_IPV4);
-            selector.matchIPDst(IpPrefix.valueOf(ipaddr,
+            TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+            TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+            sbuilder.matchEthType(Ethernet.TYPE_IPV4);
+            sbuilder.matchIPDst(IpPrefix.valueOf(ipaddr,
                                                  IpPrefix.MAX_INET_MASK_LENGTH));
-            treatment.setOutput(PortNumber.CONTROLLER);
-            puntIp.withSelector(selector.build());
-            puntIp.withTreatment(treatment.build());
+            tbuilder.setOutput(PortNumber.CONTROLLER);
+            puntIp.withSelector(sbuilder.build());
+            puntIp.withTreatment(tbuilder.build());
             puntIp.withFlag(Flag.VERSATILE)
                 .withPriority(HIGHEST_PRIORITY)
                 .makePermanent()
@@ -489,6 +510,48 @@
         }
     }
 
+    /**
+     * 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.
+     *
+     * Note: We assume that packets sending from the edge switches to the hosts
+     * have untagged VLAN.
+     * The VLAN tag will be popped later in the flooding group.
+     *
+     * @param deviceId switch ID to set the rules
+     */
+    public void populateSubnetBroadcastRule(DeviceId deviceId) {
+        config.getSubnets(deviceId).forEach(subnet -> {
+            int nextId = srManager.getSubnetNextObjectiveId(deviceId, subnet);
+            VlanId vlanId = srManager.getSubnetAssignedVlanId(deviceId, subnet);
+
+            /* Driver should treat objective with MacAddress.NONE as the
+             * subnet broadcast rule
+             */
+            TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+            sbuilder.matchVlanId(vlanId);
+            sbuilder.matchEthDst(MacAddress.NONE);
+
+            ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
+            fob.withFlag(Flag.SPECIFIC)
+                    .withSelector(sbuilder.build())
+                    .nextStep(nextId)
+                    .withPriority(5)
+                    .fromApp(srManager.appId)
+                    .makePermanent();
+
+            srManager.flowObjectiveService.forward(
+                    deviceId,
+                    fob.add(new SRObjectiveContext(
+                                    deviceId,
+                                    SRObjectiveContext.ObjectiveType.FORWARDING)
+                    )
+            );
+        });
+    }
+
+
     private PortNumber selectOnePort(DeviceId srcId, Set<DeviceId> destIds) {
 
         Set<Link> links = srManager.linkService.getDeviceLinks(srcId);