Adding a higher priority flow to trap dhcp messages from server at dhcpServerConnectPoint (agg-switch uplink)

 - The flows added previously were being ignored due to the vlan-crossconnect functionality
   in the agg-switch. The priority of those flows were too low.
 - Also added a component config to ensure that ONOS core HostLocationProvider learns
   IP addresses of hosts from dhcp ACK messages.

Change-Id: Ic4377e5ff5642bd05117d9ad286f50f94cc51ec5
diff --git a/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java b/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
index a577ae8..9f68554 100755
--- a/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
+++ b/src/main/java/org/opencord/dhcpl2relay/DhcpL2Relay.java
@@ -40,6 +40,7 @@
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.DHCP;
 import org.onlab.packet.DHCPPacketType;
+import org.onlab.packet.EthType.EtherType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
@@ -74,6 +75,9 @@
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
@@ -103,6 +107,8 @@
         implements DhcpL2RelayService {
 
     public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
+    private static final String HOST_LOC_PROVIDER =
+            "org.onosproject.provider.host.impl.HostLocationProvider";
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final InternalConfigListener cfgListener =
             new InternalConfigListener();
@@ -142,6 +148,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MastershipService mastershipService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
     @Property(name = "option82", boolValue = true,
             label = "Add option 82 to relayed packets")
     protected boolean option82 = true;
@@ -171,6 +180,9 @@
     protected void activate(ComponentContext context) {
         //start the dhcp relay agent
         appId = coreService.registerApplication(DHCP_L2RELAY_APP);
+        // ensure that host-learning via dhcp includes IP addresses
+        componentConfigService.preSetProperty(HOST_LOC_PROVIDER,
+                                              "useDhcp", Boolean.TRUE.toString());
         componentConfigService.registerProperties(getClass());
         eventDispatcher.addSink(DhcpL2RelayEvent.class, listenerRegistry);
 
@@ -184,7 +196,6 @@
         //add the packet services.
         packetService.addProcessor(dhcpRelayPacketProcessor,
                 PacketProcessor.director(0));
-        requestDhcpPackets();
         if (context != null) {
             modified(context);
         }
@@ -197,7 +208,7 @@
         cfgService.removeListener(cfgListener);
         factories.forEach(cfgService::unregisterConfigFactory);
         packetService.removeProcessor(dhcpRelayPacketProcessor);
-        cancelDhcpPackets();
+        cancelDhcpPktsFromServer();
 
         componentConfigService.unregisterProperties(getClass(), false);
         deviceService.removeListener(deviceListener);
@@ -273,13 +284,95 @@
 
         dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
         modifyClientPktsSrcDstMac = cfg.getModifySrcDstMacAddresses();
+        boolean prevUseOltUplink = useOltUplink;
         useOltUplink = cfg.getUseOltUplinkForServerPktInOut();
 
-        if (!useOltUplink) {
+        if (useOltUplink) {
+            for (ConnectPoint cp : getUplinkPortsOfOlts()) {
+                log.debug("requestDhcpPackets: ConnectPoint: {}", cp);
+                requestDhcpPacketsFromConnectPoint(cp);
+            }
+            // check if previous config was different and so trap flows may
+            // need to be removed from other places
+            if (!prevUseOltUplink) {
+                if (dhcpServerConnectPoint.get() == null) {
+                    log.warn("No dhcpServer connectPoint found .. cannot remove"
+                            + " previously installed trap flows");
+                    return;
+                }
+                // XXX change to use packet service when priority can be set
+                DefaultForwardingObjective.Builder bldr =
+                        trapDhcpPktsFromServer(dhcpServerConnectPoint.get().deviceId(),
+                                               dhcpServerConnectPoint.get().port());
+                flowObjectiveService.forward(dhcpServerConnectPoint.get().deviceId(),
+                                             bldr.remove());
+            }
+
+        } else {
             selectServerConnectPoint();
+            log.info("dhcp server connect point: " + dhcpServerConnectPoint);
+            // create flow to trap packets coming from the server to this connectpoint
+            // which is typically not on the OLT (otherwise use the OLT's uplink)
+            if (dhcpServerConnectPoint.get() == null) {
+                log.warn("No dhcpServer connectPoint found, cannot install dhcp"
+                        + " trap flows");
+                return;
+            }
+            // XXX change to use packet service when priority can be set
+            DefaultForwardingObjective.Builder bldr =
+                    trapDhcpPktsFromServer(dhcpServerConnectPoint.get().deviceId(),
+                                           dhcpServerConnectPoint.get().port());
+            flowObjectiveService.forward(dhcpServerConnectPoint.get().deviceId(),
+                                         bldr.add());
+        }
+    }
+
+    private void cancelDhcpPktsFromServer() {
+        if (useOltUplink) {
+            for (ConnectPoint cp : getUplinkPortsOfOlts()) {
+                log.debug("cancelDhcpPackets: ConnectPoint: {}", cp);
+                cancelDhcpPacketsFromConnectPoint(cp);
+            }
+        } else {
+            // delete flow to trap packets coming from the server to this connectpoint
+            // which is typically not on the OLT (otherwise use the OLT's uplink)
+            if (dhcpServerConnectPoint.get() == null) {
+                log.warn("No dhcpServer connectPoint found.. cannot remove dhcp"
+                        + " trap flows");
+                return;
+            }
+            // XXX change to use packet service when priority can be set
+            DefaultForwardingObjective.Builder bldr =
+                    trapDhcpPktsFromServer(dhcpServerConnectPoint.get().deviceId(),
+                                           dhcpServerConnectPoint.get().port());
+            flowObjectiveService.forward(dhcpServerConnectPoint.get().deviceId(),
+                                         bldr.remove());
         }
 
-        log.info("dhcp server connect point: " + dhcpServerConnectPoint);
+    }
+
+    private DefaultForwardingObjective.Builder trapDhcpPktsFromServer(DeviceId devId,
+                                                                      PortNumber port) {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .punt()
+                .wipeDeferred()
+                .build();
+        TrafficSelector downstream = DefaultTrafficSelector.builder()
+                .matchInPort(port)
+                .matchEthType(EtherType.IPV4.ethType().toShort())
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpSrc(TpPort.tpPort(67))
+                .matchUdpDst(TpPort.tpPort(68))
+                .build();
+
+        DefaultForwardingObjective.Builder bldr = DefaultForwardingObjective.builder()
+                .withPriority(61000) // XXX packet service does not allow setting this
+                .withSelector(downstream)
+                .fromApp(appId)
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withTreatment(treatment)
+                .makePermanent();
+        return bldr;
     }
 
     /**
@@ -382,58 +475,6 @@
                 PacketPriority.CONTROL, appId, Optional.of(cp.deviceId()));
     }
 
-    /**
-     * Request DHCP packet in via PacketService.
-     */
-    private void requestDhcpPackets() {
-        if (!useOltUplink) {
-            TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
-                    .matchEthType(Ethernet.TYPE_IPV4)
-                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                    .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
-            packetService.requestPackets(selectorServer.build(),
-                    PacketPriority.CONTROL, appId);
-
-            TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
-                    .matchEthType(Ethernet.TYPE_IPV4)
-                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                    .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
-            packetService.requestPackets(selectorClient.build(),
-                    PacketPriority.CONTROL, appId);
-        } else {
-            for (ConnectPoint cp: getUplinkPortsOfOlts()) {
-                log.debug("requestDhcpPackets: ConnectPoint: {}", cp);
-                requestDhcpPacketsFromConnectPoint(cp);
-            }
-        }
-
-    }
-
-    /**
-     * Cancel requested DHCP packets in via packet service.
-     */
-    private void cancelDhcpPackets() {
-        if (!useOltUplink) {
-            TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
-                    .matchEthType(Ethernet.TYPE_IPV4)
-                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                    .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
-            packetService.cancelPackets(selectorServer.build(),
-                    PacketPriority.CONTROL, appId);
-
-            TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
-                    .matchEthType(Ethernet.TYPE_IPV4)
-                    .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                    .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
-            packetService.cancelPackets(selectorClient.build(),
-                    PacketPriority.CONTROL, appId);
-        } else {
-            for (ConnectPoint cp: getUplinkPortsOfOlts()) {
-                cancelDhcpPacketsFromConnectPoint(cp);
-            }
-        }
-    }
-
     public static Map<String, DhcpAllocationInfo> allocationMap() {
         return allocationMap;
     }
@@ -923,7 +964,7 @@
             } else {
                 switch (event.type()) {
                     case PORT_ADDED:
-                        if (isUplinkPortOfOlt(event.subject().id(), event.port())) {
+                        if (useOltUplink && isUplinkPortOfOlt(event.subject().id(), event.port())) {
                             requestDhcpPacketsFromConnectPoint(new ConnectPoint(event.subject().id(),
                                     event.port().number()));
                         }