[CORD-1751] Preventing attacks on DHCP-Relay

Change-Id: I46f7ba2490994e71c9f7d881cbe44785720f1e37
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
index 0c031fd..210d0c9 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -17,7 +17,10 @@
 
 package org.onosproject.dhcprelay;
 
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -33,16 +36,32 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
 import org.onlab.packet.VlanId;
 import org.onlab.packet.dhcp.CircuitId;
 import org.onlab.packet.dhcp.DhcpOption;
 import org.onlab.packet.dhcp.DhcpRelayAgentOption;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
 import org.onosproject.dhcprelay.api.DhcpHandler;
 import org.onosproject.dhcprelay.api.DhcpServerInfo;
 import org.onosproject.dhcprelay.config.DhcpServerConfig;
+import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
 import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostProvider;
@@ -50,6 +69,7 @@
 import org.onosproject.net.host.HostProviderService;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceService;
+import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.routeservice.Route;
 import org.onosproject.routeservice.RouteStore;
@@ -76,6 +96,7 @@
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -85,6 +106,8 @@
 import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
 import static org.onlab.packet.MacAddress.valueOf;
 import static org.onlab.packet.dhcp.DhcpRelayAgentOption.RelayAgentInfoOptions.CIRCUIT_ID;
+import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
+import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
 
 @Component
 @Service
@@ -92,6 +115,27 @@
 public class Dhcp4HandlerImpl implements DhcpHandler, HostProvider {
     public static final String DHCP_V4_RELAY_APP = "org.onosproject.Dhcp4HandlerImpl";
     public static final ProviderId PROVIDER_ID = new ProviderId("dhcp4", DHCP_V4_RELAY_APP);
+    private static final String BROADCAST_IP = "255.255.255.255";
+    private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
+
+    private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
+            .matchEthType(Ethernet.TYPE_IPV4)
+            .matchIPProtocol(IPv4.PROTOCOL_UDP)
+            .matchIPSrc(Ip4Address.ZERO.toIpPrefix())
+            .matchIPDst(Ip4Address.valueOf(BROADCAST_IP).toIpPrefix())
+            .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
+            .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+            .build();
+    private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
+            .matchEthType(Ethernet.TYPE_IPV4)
+            .matchIPProtocol(IPv4.PROTOCOL_UDP)
+            .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+            .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+            .build();
+    static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
+            CLIENT_SERVER_SELECTOR,
+            SERVER_RELAY_SELECTOR
+    );
     private static Logger log = LoggerFactory.getLogger(Dhcp4HandlerImpl.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -112,7 +156,18 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostProviderRegistry providerRegistry;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
     protected HostProviderService providerService;
+    protected ApplicationId appId;
+    protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
     private InternalHostListener hostListener = new InternalHostListener();
 
     private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
@@ -120,6 +175,7 @@
 
     @Activate
     protected void activate() {
+        appId = coreService.registerApplication(DHCP_V4_RELAY_APP);
         hostService.addListener(hostListener);
         providerService = providerRegistry.register(this);
     }
@@ -163,6 +219,30 @@
         return indirectServerInfoList;
     }
 
+    @Override
+    public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
+        if (config == null) {
+            ignoredVlans.forEach(((deviceId, vlanId) -> {
+                processIgnoreVlanRule(deviceId, vlanId, REMOVE);
+            }));
+            return;
+        }
+        config.ignoredVlans().forEach((deviceId, vlanId) -> {
+            if (ignoredVlans.get(deviceId).contains(vlanId)) {
+                // don't need to process if it already ignored
+                return;
+            }
+            processIgnoreVlanRule(deviceId, vlanId, ADD);
+        });
+
+        ignoredVlans.forEach((deviceId, vlanId) -> {
+            if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
+                // not contains in new config, remove it
+                processIgnoreVlanRule(deviceId, vlanId, REMOVE);
+            }
+        });
+    }
+
     public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
         if (configs.size() == 0) {
             // no config to update
@@ -188,6 +268,7 @@
             });
             oldServerInfo.getDhcpServerIp4().ifPresent(serverIp -> {
                 hostService.stopMonitoringIp(serverIp);
+                cancelDhcpPacket(serverIp);
             });
         }
 
@@ -202,7 +283,8 @@
         log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
         log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp4().orElse(null));
 
-        IpAddress ipToProbe;
+        Ip4Address serverIp = newServerInfo.getDhcpServerIp4().get();
+        Ip4Address ipToProbe;
         if (newServerInfo.getDhcpGatewayIp4().isPresent()) {
             ipToProbe = newServerInfo.getDhcpGatewayIp4().get();
         } else {
@@ -228,6 +310,7 @@
         nonDupServerInfoList.addAll(serverInfoList);
         serverInfoList.clear();
         serverInfoList.addAll(nonDupServerInfoList);
+        requestDhcpPacket(serverIp);
     }
 
     @Override
@@ -414,7 +497,6 @@
             indirectRelayAgentIp = indirectServerInfo.getRelayAgentIp4().orElse(null);
         }
 
-
         Ip4Address clientInterfaceIp =
                 interfaceService.getInterfacesByPort(context.inPacket().receivedFrom())
                         .stream()
@@ -982,9 +1064,6 @@
                 case HOST_REMOVED:
                     hostRemoved(event.subject());
                     break;
-                case HOST_MOVED:
-                    hostMoved(event.subject());
-                    break;
                 default:
                     break;
             }
@@ -992,114 +1071,39 @@
     }
 
     /**
-     * Handle host move.
-     * If the host DHCP server or gateway and it moved to the location different
-     * to user configured, unsets the connect mac and vlan
-     *
-     * @param host the host
-     */
-    private void hostMoved(Host host) {
-        Set<ConnectPoint> hostConnectPoints = host.locations().stream()
-                .map(hl -> new ConnectPoint(hl.elementId(), hl.port()))
-                .collect(Collectors.toSet());
-        DhcpServerInfo serverInfo;
-        ConnectPoint dhcpServerConnectPoint;
-        Ip4Address dhcpGatewayIp;
-        Ip4Address dhcpServerIp;
-
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp4().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-        }
-
-        if (!indirectServerInfoList.isEmpty()) {
-            // Indirect server
-            serverInfo = indirectServerInfoList.get(0);
-            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp4().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-        }
-    }
-
-    /**
      * Handle host updated.
      * If the host is DHCP server or gateway, update connect mac and vlan.
      *
      * @param host the host
      */
     private void hostUpdated(Host host) {
-        DhcpServerInfo serverInfo;
-        Ip4Address dhcpGatewayIp;
-        Ip4Address dhcpServerIp;
-
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp4().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-        }
-
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp4().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-        }
-
+        hostUpdated(host, defaultServerInfoList);
+        hostUpdated(host, indirectServerInfoList);
     }
 
+    private void hostUpdated(Host host, List<DhcpServerInfo> srverInfoList) {
+        DhcpServerInfo serverInfo;
+        Ip4Address targetIp;
+        if (!srverInfoList.isEmpty()) {
+            serverInfo = srverInfoList.get(0);
+            targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
+            Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
+
+            if (targetIp == null) {
+                targetIp = serverIp;
+            }
+
+            if (targetIp != null) {
+                if (host.ipAddresses().contains(targetIp)) {
+                    serverInfo.setDhcpConnectMac(host.mac());
+                    serverInfo.setDhcpConnectVlan(host.vlan());
+                    requestDhcpPacket(serverIp);
+                }
+            }
+        }
+    }
+
+
     /**
      * Handle host removed.
      * If the host is DHCP server or gateway, unset connect mac and vlan.
@@ -1107,45 +1111,165 @@
      * @param host the host
      */
     private void hostRemoved(Host host) {
+        hostRemoved(host, defaultServerInfoList);
+        hostRemoved(host, indirectServerInfoList);
+    }
+
+    private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
         DhcpServerInfo serverInfo;
-        Ip4Address dhcpGatewayIp;
-        Ip4Address dhcpServerIp;
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp4().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
+        Ip4Address targetIp;
+        if (!serverInfoList.isEmpty()) {
+            serverInfo = serverInfoList.get(0);
+            Ip4Address serverIp = serverInfo.getDhcpServerIp4().orElse(null);
+            targetIp = serverInfo.getDhcpGatewayIp4().orElse(null);
 
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
+            if (targetIp == null) {
+                targetIp = serverIp;
             }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
+
+            if (targetIp != null) {
+                if (host.ipAddresses().contains(targetIp)) {
                     serverInfo.setDhcpConnectVlan(null);
                     serverInfo.setDhcpConnectMac(null);
+                    cancelDhcpPacket(serverIp);
                 }
             }
         }
+    }
 
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp4().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
+    private void requestDhcpPacket(Ip4Address serverIp) {
+        requestServerDhcpPacket(serverIp);
+        requestClientDhcpPacket(serverIp);
+    }
 
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
+    private void cancelDhcpPacket(Ip4Address serverIp) {
+        cancelServerDhcpPacket(serverIp);
+        cancelClientDhcpPacket(serverIp);
+    }
+
+    private void cancelServerDhcpPacket(Ip4Address serverIp) {
+        TrafficSelector serverSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPSrc(serverIp.toIpPrefix())
+                        .build();
+        packetService.cancelPackets(serverSelector,
+                                    PacketPriority.CONTROL,
+                                    appId);
+    }
+
+    private void requestServerDhcpPacket(Ip4Address serverIp) {
+        TrafficSelector serverSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPSrc(serverIp.toIpPrefix())
+                        .build();
+        packetService.requestPackets(serverSelector,
+                                     PacketPriority.CONTROL,
+                                     appId);
+    }
+
+    private void cancelClientDhcpPacket(Ip4Address serverIp) {
+        // Packet comes from relay
+        TrafficSelector indirectClientSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPDst(serverIp.toIpPrefix())
+                        .build();
+        packetService.cancelPackets(indirectClientSelector,
+                                    PacketPriority.CONTROL,
+                                    appId);
+
+        // Packet comes from client
+        packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
+                                    PacketPriority.CONTROL,
+                                    appId);
+    }
+
+    private void requestClientDhcpPacket(Ip4Address serverIp) {
+        // Packet comes from relay
+        TrafficSelector indirectClientSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPDst(serverIp.toIpPrefix())
+                        .build();
+        packetService.requestPackets(indirectClientSelector,
+                                     PacketPriority.CONTROL,
+                                     appId);
+
+        // Packet comes from client
+        packetService.requestPackets(CLIENT_SERVER_SELECTOR,
+                                     PacketPriority.CONTROL,
+                                     appId);
+    }
+
+    /**
+     * Process the ignore rules.
+     *
+     * @param deviceId the device id
+     * @param vlanId the vlan to be ignored
+     * @param op the operation, ADD to install; REMOVE to uninstall rules
+     */
+    private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
+        TrafficTreatment dropTreatment = DefaultTrafficTreatment.builder().wipeDeferred().build();
+        AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
+        DHCP_SELECTORS.forEach(trafficSelector -> {
+            TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
+                    .matchVlanId(vlanId)
+                    .build();
+
+            ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withSelector(selector)
+                    .withPriority(IGNORE_CONTROL_PRIORITY)
+                    .withTreatment(dropTreatment)
+                    .fromApp(appId);
+
+
+            ObjectiveContext objectiveContext = new ObjectiveContext() {
+                @Override
+                public void onSuccess(Objective objective) {
+                    log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
+                             op, vlanId, deviceId, selector);
+                    int countDown = installedCount.decrementAndGet();
+                    if (countDown != 0) {
+                        return;
+                    }
+                    switch (op) {
+                        case ADD:
+                            ignoredVlans.put(deviceId, vlanId);
+                            break;
+                        case REMOVE:
+                            ignoredVlans.remove(deviceId, vlanId);
+                            break;
+                        default:
+                            log.warn("Unsupported objective operation {}", op);
+                            break;
+                    }
                 }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
+
+                @Override
+                public void onError(Objective objective, ObjectiveError error) {
+                    log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
+                             op, vlanId, selector, deviceId, error);
                 }
+            };
+
+            ForwardingObjective fwd;
+            switch (op) {
+                case ADD:
+                    fwd = builder.add(objectiveContext);
+                    break;
+                case REMOVE:
+                    fwd = builder.remove(objectiveContext);
+                    break;
+                default:
+                    log.warn("Unsupported objective operation {}", op);
+                    return;
             }
-        }
+
+            Device device = deviceService.getDevice(deviceId);
+            if (device == null || !device.is(Pipeliner.class)) {
+                log.warn("Device {} is not available now, wait until device is available", deviceId);
+                return;
+            }
+            flowObjectiveService.apply(deviceId, fwd);
+        });
     }
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
index 3e6ce74..b4d2176 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -17,7 +17,9 @@
 
 package org.onosproject.dhcprelay;
 
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Deactivate;
 import com.google.common.collect.Sets;
@@ -35,6 +37,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
 import org.onlab.packet.VlanId;
 import org.onlab.packet.dhcp.Dhcp6RelayOption;
@@ -46,9 +49,24 @@
 import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
 import org.onlab.packet.dhcp.Dhcp6IaPrefixOption;
 import org.onlab.util.HexString;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
 import org.onosproject.dhcprelay.api.DhcpHandler;
 import org.onosproject.dhcprelay.api.DhcpServerInfo;
+import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
 import org.onosproject.net.host.HostProvider;
 import org.onosproject.net.host.HostProviderRegistry;
 import org.onosproject.net.host.HostProviderService;
@@ -60,6 +78,7 @@
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceService;
+import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.provider.ProviderId;
 import org.onosproject.routeservice.Route;
 import org.onosproject.routeservice.RouteStore;
@@ -84,11 +103,13 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.ArrayList;
-import java.util.stream.Collectors;
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
+import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
+import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
 
 @Component
 @Service
@@ -96,6 +117,26 @@
 public class Dhcp6HandlerImpl implements DhcpHandler, HostProvider {
     public static final String DHCP_V6_RELAY_APP = "org.onosproject.Dhcp6HandlerImpl";
     public static final ProviderId PROVIDER_ID = new ProviderId("dhcp6", DHCP_V6_RELAY_APP);
+    private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
+
+    private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
+            .matchEthType(Ethernet.TYPE_IPV6)
+            .matchIPProtocol(IPv6.PROTOCOL_UDP)
+            .matchIPv6Src(IpPrefix.IPV6_LINK_LOCAL_PREFIX)
+            .matchIPv6Dst(Ip6Address.ALL_DHCP_RELAY_AGENTS_AND_SERVERS.toIpPrefix())
+            .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
+            .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
+            .build();
+    private static final TrafficSelector SERVER_RELAY_SELECTOR = DefaultTrafficSelector.builder()
+            .matchEthType(Ethernet.TYPE_IPV6)
+            .matchIPProtocol(IPv6.PROTOCOL_UDP)
+            .matchUdpSrc(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
+            .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
+            .build();
+    static final Set<TrafficSelector> DHCP_SELECTORS = ImmutableSet.of(
+            CLIENT_SERVER_SELECTOR,
+            SERVER_RELAY_SELECTOR
+    );
     private static Logger log = LoggerFactory.getLogger(Dhcp6HandlerImpl.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -116,8 +157,20 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostProviderRegistry providerRegistry;
 
-    private InternalHostListener hostListener = new InternalHostListener();
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
     protected HostProviderService providerService;
+    protected ApplicationId appId;
+    protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
+    private InternalHostListener hostListener = new InternalHostListener();
+
     private Ip6Address dhcpServerIp = null;
     // dhcp server may be connected directly to the SDN network or
     // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
@@ -156,6 +209,7 @@
 
     @Activate
     protected void activate() {
+        appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
         providerService = providerRegistry.register(this);
         hostService.addListener(hostListener);
     }
@@ -190,6 +244,30 @@
     }
 
     @Override
+    public void updateIgnoreVlanConfig(IgnoreDhcpConfig config) {
+        if (config == null) {
+            ignoredVlans.forEach(((deviceId, vlanId) -> {
+                processIgnoreVlanRule(deviceId, vlanId, REMOVE);
+            }));
+            return;
+        }
+        config.ignoredVlans().forEach((deviceId, vlanId) -> {
+            if (ignoredVlans.get(deviceId).contains(vlanId)) {
+                // don't need to process if it already ignored
+                return;
+            }
+            processIgnoreVlanRule(deviceId, vlanId, ADD);
+        });
+
+        ignoredVlans.forEach((deviceId, vlanId) -> {
+            if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
+                // not contains in new config, remove it
+                processIgnoreVlanRule(deviceId, vlanId, REMOVE);
+            }
+        });
+    }
+
+    @Override
     public void processDhcpPacket(PacketContext context, BasePacket payload) {
         checkNotNull(payload, "DHCP6 payload can't be null");
         checkState(payload instanceof DHCP6, "Payload is not a DHCP6");
@@ -1053,6 +1131,7 @@
             });
             oldServerInfo.getDhcpServerIp6().ifPresent(serverIp -> {
                 hostService.stopMonitoringIp(serverIp);
+                cancelDhcpPacket(serverIp);
             });
         }
 
@@ -1067,7 +1146,8 @@
         log.debug("DHCP server connect point: {}", newServerInfo.getDhcpServerConnectPoint().orElse(null));
         log.debug("DHCP server IP: {}", newServerInfo.getDhcpServerIp6().orElse(null));
 
-        IpAddress ipToProbe;
+        Ip6Address serverIp = newServerInfo.getDhcpServerIp6().get();
+        Ip6Address ipToProbe;
         if (newServerInfo.getDhcpGatewayIp6().isPresent()) {
             ipToProbe = newServerInfo.getDhcpGatewayIp6().get();
         } else {
@@ -1093,6 +1173,7 @@
         nonDupServerInfoList.addAll(serverInfoList);
         serverInfoList.clear();
         serverInfoList.addAll(nonDupServerInfoList);
+        requestDhcpPacket(serverIp);
     }
 
     class InternalHostListener implements HostListener {
@@ -1106,9 +1187,6 @@
                 case HOST_REMOVED:
                     hostRemoved(event.subject());
                     break;
-                case HOST_MOVED:
-                    hostMoved(event.subject());
-                    break;
                 default:
                     break;
             }
@@ -1116,119 +1194,39 @@
     }
 
     /**
-     * Handle host move.
-     * If the host DHCP server or gateway and it moved to the location different
-     * to user configured, unsets the connect mac and vlan
-     *
-     * @param host the host
-     */
-    private void hostMoved(Host host) {
-        Set<ConnectPoint> hostConnectPoints = host.locations().stream()
-                .map(hl -> new ConnectPoint(hl.elementId(), hl.port()))
-                .collect(Collectors.toSet());
-        DhcpServerInfo serverInfo;
-        ConnectPoint dhcpServerConnectPoint;
-        Ip6Address dhcpGatewayIp;
-        Ip6Address dhcpServerIp;
-
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            if (dhcpServerConnectPoint == null) {
-                return;
-            }
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-        }
-
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            if (dhcpServerConnectPoint == null) {
-                return;
-            }
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp) &&
-                        !hostConnectPoints.contains(dhcpServerConnectPoint)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-        }
-        reloadServerSettings();
-    }
-
-    /**
      * Handle host updated.
      * If the host is DHCP server or gateway, update connect mac and vlan.
      *
      * @param host the host
      */
     private void hostUpdated(Host host) {
-        DhcpServerInfo serverInfo;
-        Ip6Address dhcpGatewayIp;
-        Ip6Address dhcpServerIp;
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-        }
-
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
-                    serverInfo.setDhcpConnectMac(host.mac());
-                    serverInfo.setDhcpConnectVlan(host.vlan());
-                }
-            }
-        }
+        hostUpdated(host, defaultServerInfoList);
+        hostUpdated(host, indirectServerInfoList);
         reloadServerSettings();
     }
 
+    private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
+        DhcpServerInfo serverInfo;
+        Ip6Address targetIp;
+        if (!serverInfoList.isEmpty()) {
+            serverInfo = serverInfoList.get(0);
+            Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
+            targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
+
+            if (targetIp == null) {
+                targetIp = serverIp;
+            }
+
+            if (targetIp != null) {
+                if (host.ipAddresses().contains(targetIp)) {
+                    serverInfo.setDhcpConnectMac(host.mac());
+                    serverInfo.setDhcpConnectVlan(host.vlan());
+                    requestDhcpPacket(serverIp);
+                }
+            }
+        }
+    }
+
     /**
      * Handle host removed.
      * If the host is DHCP server or gateway, unset connect mac and vlan.
@@ -1236,50 +1234,34 @@
      * @param host the host
      */
     private void hostRemoved(Host host) {
-        DhcpServerInfo serverInfo;
-        Ip6Address dhcpGatewayIp;
-        Ip6Address dhcpServerIp;
-
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-        }
-
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-
-            if (dhcpGatewayIp != null) {
-                if (host.ipAddresses().contains(dhcpGatewayIp)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-            if (dhcpServerIp != null) {
-                if (host.ipAddresses().contains(dhcpServerIp)) {
-                    serverInfo.setDhcpConnectVlan(null);
-                    serverInfo.setDhcpConnectMac(null);
-                }
-            }
-        }
+        hostRemoved(host, defaultServerInfoList);
+        hostRemoved(host, indirectServerInfoList);
         reloadServerSettings();
     }
 
+    private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
+        DhcpServerInfo serverInfo;
+        Ip6Address targetIp;
+
+        if (!serverInfoList.isEmpty()) {
+            serverInfo = serverInfoList.get(0);
+            Ip6Address serverIp = serverInfo.getDhcpServerIp6().orElse(null);
+            targetIp = serverInfo.getDhcpGatewayIp6().orElse(null);
+
+            if (targetIp == null) {
+                targetIp = serverIp;
+            }
+
+            if (targetIp != null) {
+                if (host.ipAddresses().contains(targetIp)) {
+                    serverInfo.setDhcpConnectVlan(null);
+                    serverInfo.setDhcpConnectMac(null);
+                    cancelDhcpPacket(serverIp);
+                }
+            }
+        }
+    }
+
     private void reloadServerSettings() {
         DhcpServerInfo serverInfo;
         if (!defaultServerInfoList.isEmpty()) {
@@ -1369,4 +1351,139 @@
         return iface.vlanTagged().contains(vlanId);
     }
 
+    private void requestDhcpPacket(Ip6Address serverIp) {
+        requestServerDhcpPacket(serverIp);
+        requestClientDhcpPacket(serverIp);
+    }
+
+    private void cancelDhcpPacket(Ip6Address serverIp) {
+        cancelServerDhcpPacket(serverIp);
+        cancelClientDhcpPacket(serverIp);
+    }
+
+    private void cancelServerDhcpPacket(Ip6Address serverIp) {
+        TrafficSelector serverSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPv6Src(serverIp.toIpPrefix())
+                        .build();
+        packetService.cancelPackets(serverSelector,
+                                    PacketPriority.CONTROL,
+                                    appId);
+    }
+
+    private void requestServerDhcpPacket(Ip6Address serverIp) {
+        TrafficSelector serverSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPv6Src(serverIp.toIpPrefix())
+                        .build();
+        packetService.requestPackets(serverSelector,
+                                     PacketPriority.CONTROL,
+                                     appId);
+    }
+
+    private void cancelClientDhcpPacket(Ip6Address serverIp) {
+        // Packet comes from relay
+        TrafficSelector indirectClientSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPv6Dst(serverIp.toIpPrefix())
+                        .build();
+        packetService.cancelPackets(indirectClientSelector,
+                                    PacketPriority.CONTROL,
+                                    appId);
+
+        // Packet comes from client
+        packetService.cancelPackets(CLIENT_SERVER_SELECTOR,
+                                    PacketPriority.CONTROL,
+                                    appId);
+    }
+
+    private void requestClientDhcpPacket(Ip6Address serverIp) {
+        // Packet comes from relay
+        TrafficSelector indirectClientSelector =
+                DefaultTrafficSelector.builder(SERVER_RELAY_SELECTOR)
+                        .matchIPv6Dst(serverIp.toIpPrefix())
+                        .build();
+        packetService.requestPackets(indirectClientSelector,
+                                     PacketPriority.CONTROL,
+                                     appId);
+
+        // Packet comes from client
+        packetService.requestPackets(CLIENT_SERVER_SELECTOR,
+                                     PacketPriority.CONTROL,
+                                     appId);
+    }
+
+    /**
+     * Process the ignore rules.
+     *
+     * @param deviceId the device id
+     * @param vlanId the vlan to be ignored
+     * @param op the operation, ADD to install; REMOVE to uninstall rules
+     */
+    private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
+        TrafficTreatment dropTreatment = DefaultTrafficTreatment.builder().wipeDeferred().build();
+        AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
+        DHCP_SELECTORS.forEach(trafficSelector -> {
+            TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
+                    .matchVlanId(vlanId)
+                    .build();
+
+            ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withSelector(selector)
+                    .withPriority(IGNORE_CONTROL_PRIORITY)
+                    .withTreatment(dropTreatment)
+                    .fromApp(appId);
+
+
+            ObjectiveContext objectiveContext = new ObjectiveContext() {
+                @Override
+                public void onSuccess(Objective objective) {
+                    log.info("Ignore rule {} (Vlan id {}, device {}, selector {})",
+                             op, vlanId, deviceId, selector);
+                    int countDown = installedCount.decrementAndGet();
+                    if (countDown != 0) {
+                        return;
+                    }
+                    switch (op) {
+                        case ADD:
+                            ignoredVlans.put(deviceId, vlanId);
+                            break;
+                        case REMOVE:
+                            ignoredVlans.remove(deviceId, vlanId);
+                            break;
+                        default:
+                            log.warn("Unsupported objective operation {}", op);
+                            break;
+                    }
+                }
+
+                @Override
+                public void onError(Objective objective, ObjectiveError error) {
+                    log.warn("Can't {} ignore rule (vlan id {}, selector {}, device {}) due to {}",
+                             op, vlanId, selector, deviceId, error);
+                }
+            };
+
+            ForwardingObjective fwd;
+            switch (op) {
+                case ADD:
+                    fwd = builder.add(objectiveContext);
+                    break;
+                case REMOVE:
+                    fwd = builder.remove(objectiveContext);
+                    break;
+                default:
+                    log.warn("Unsupported objective operation {}", op);
+                    return;
+            }
+
+            Device device = deviceService.getDevice(deviceId);
+            if (device == null || !device.is(Pipeliner.class)) {
+                log.warn("Device {} is not available now, wait until device is available", deviceId);
+                return;
+            }
+            flowObjectiveService.apply(deviceId, fwd);
+        });
+    }
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
index e1f9939..15026eb 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
@@ -23,12 +23,11 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.Streams;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -46,7 +45,6 @@
 import org.onlab.packet.IPv4;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
-import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
 import org.onlab.packet.VlanId;
 import org.onlab.util.Tools;
@@ -62,22 +60,12 @@
 import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
 import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
 import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.net.Host;
-import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.UdpPortCriterion;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.flowobjective.ObjectiveError;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
@@ -104,8 +92,6 @@
 import com.google.common.collect.ImmutableSet;
 
 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
-import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
-import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
 
 /**
  * DHCP Relay Agent Application Component.
@@ -116,36 +102,10 @@
     public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
     public static final String ROUTE_STORE_IMPL =
             "org.onosproject.routeservice.store.RouteStoreImpl";
-    private static final TrafficSelector DHCP_SERVER_SELECTOR = DefaultTrafficSelector.builder()
-            .matchEthType(Ethernet.TYPE_IPV4)
-            .matchIPProtocol(IPv4.PROTOCOL_UDP)
-            .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
-            .build();
-    private static final TrafficSelector DHCP_CLIENT_SELECTOR = DefaultTrafficSelector.builder()
-            .matchEthType(Ethernet.TYPE_IPV4)
-            .matchIPProtocol(IPv4.PROTOCOL_UDP)
-            .matchUdpDst(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
-            .build();
-    private static final TrafficSelector DHCP6_SERVER_SELECTOR = DefaultTrafficSelector.builder()
-            .matchEthType(Ethernet.TYPE_IPV6)
-            .matchIPProtocol(IPv4.PROTOCOL_UDP)
-            .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_SERVER_PORT))
-            .build();
-    private static final TrafficSelector DHCP6_CLIENT_SELECTOR = DefaultTrafficSelector.builder()
-            .matchEthType(Ethernet.TYPE_IPV6)
-            .matchIPProtocol(IPv4.PROTOCOL_UDP)
-            .matchUdpDst(TpPort.tpPort(UDP.DHCP_V6_CLIENT_PORT))
-            .build();
-    static final List<TrafficSelector> DHCP_SELECTORS = ImmutableList.of(
-            DHCP_SERVER_SELECTOR,
-            DHCP_CLIENT_SELECTOR,
-            DHCP6_SERVER_SELECTOR,
-            DHCP6_CLIENT_SELECTOR
-    );
+
     private static final TrafficSelector ARP_SELECTOR = DefaultTrafficSelector.builder()
             .matchEthType(Ethernet.TYPE_ARP)
             .build();
-    private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final InternalConfigListener cfgListener = new InternalConfigListener();
 
@@ -201,9 +161,6 @@
     protected ComponentConfigService compCfgService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY,
@@ -218,7 +175,6 @@
             label = "Enable Address resolution protocol")
     protected boolean arpEnabled = true;
 
-    protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
     protected DeviceListener deviceListener = new InternalDeviceListener();
     private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
     private ApplicationId appId;
@@ -236,8 +192,6 @@
         //add the packet processor
         packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
 
-        // listen host event for dhcp server or the gateway
-        requestDhcpPackets();
         modified(context);
 
         // Enable distribute route store
@@ -254,7 +208,6 @@
         cfgService.removeListener(cfgListener);
         factories.forEach(cfgService::unregisterConfigFactory);
         packetService.removeProcessor(dhcpRelayPacketProcessor);
-        cancelDhcpPackets();
         cancelArpPackets();
         compCfgService.unregisterProperties(getClass(), false);
         deviceService.removeListener(deviceListener);
@@ -280,6 +233,12 @@
         }
     }
 
+    private static List<TrafficSelector> buildClientDhcpSelectors() {
+        return Streams.concat(Dhcp4HandlerImpl.DHCP_SELECTORS.stream(),
+                              Dhcp6HandlerImpl.DHCP_SELECTORS.stream())
+                .collect(Collectors.toList());
+    }
+
     /**
      * Updates DHCP relay app configuration.
      */
@@ -318,7 +277,8 @@
             v6Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
         }
         if (config instanceof IgnoreDhcpConfig) {
-            updateIgnoreVlanRules((IgnoreDhcpConfig) config);
+            v4Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
+            v6Handler.updateIgnoreVlanConfig((IgnoreDhcpConfig) config);
         }
     }
 
@@ -331,125 +291,11 @@
             v6Handler.setDefaultDhcpServerConfigs(Collections.emptyList());
         }
         if (config instanceof IgnoreDhcpConfig) {
-            ignoredVlans.forEach(((deviceId, vlanId) -> {
-                processIgnoreVlanRule(deviceId, vlanId, REMOVE);
-            }));
+            v4Handler.updateIgnoreVlanConfig(null);
+            v6Handler.updateIgnoreVlanConfig(null);
         }
     }
 
-    private void updateIgnoreVlanRules(IgnoreDhcpConfig config) {
-        config.ignoredVlans().forEach((deviceId, vlanId) -> {
-            if (ignoredVlans.get(deviceId).contains(vlanId)) {
-                // don't need to process if it already ignored
-                return;
-            }
-            processIgnoreVlanRule(deviceId, vlanId, ADD);
-        });
-
-        ignoredVlans.forEach((deviceId, vlanId) -> {
-            if (!config.ignoredVlans().get(deviceId).contains(vlanId)) {
-                // not contains in new config, remove it
-                processIgnoreVlanRule(deviceId, vlanId, REMOVE);
-            }
-        });
-    }
-
-    /**
-     * Process the ignore rules.
-     *
-     * @param deviceId the device id
-     * @param vlanId the vlan to be ignored
-     * @param op the operation, ADD to install; REMOVE to uninstall rules
-     */
-    private void processIgnoreVlanRule(DeviceId deviceId, VlanId vlanId, Objective.Operation op) {
-        TrafficTreatment dropTreatment = DefaultTrafficTreatment.emptyTreatment();
-        dropTreatment.clearedDeferred();
-        AtomicInteger installedCount = new AtomicInteger(DHCP_SELECTORS.size());
-        DHCP_SELECTORS.forEach(trafficSelector -> {
-            UdpPortCriterion udpDst = (UdpPortCriterion) trafficSelector.getCriterion(Criterion.Type.UDP_DST);
-            int udpDstPort = udpDst.udpPort().toInt();
-            TrafficSelector selector = DefaultTrafficSelector.builder(trafficSelector)
-                    .matchVlanId(vlanId)
-                    .build();
-
-            ForwardingObjective.Builder builder = DefaultForwardingObjective.builder()
-                    .withFlag(ForwardingObjective.Flag.VERSATILE)
-                    .withSelector(selector)
-                    .withPriority(IGNORE_CONTROL_PRIORITY)
-                    .withTreatment(dropTreatment)
-                    .fromApp(appId);
-
-
-            ObjectiveContext objectiveContext = new ObjectiveContext() {
-                @Override
-                public void onSuccess(Objective objective) {
-                    log.info("Ignore rule {} (Vlan id {}, device {}, UDP dst {})",
-                             op, vlanId, deviceId, udpDstPort);
-                    int countDown = installedCount.decrementAndGet();
-                    if (countDown != 0) {
-                        return;
-                    }
-                    switch (op) {
-                        case ADD:
-
-                            ignoredVlans.put(deviceId, vlanId);
-                            break;
-                        case REMOVE:
-                            ignoredVlans.remove(deviceId, vlanId);
-                            break;
-                        default:
-                            log.warn("Unsupported objective operation {}", op);
-                            break;
-                    }
-                }
-
-                @Override
-                public void onError(Objective objective, ObjectiveError error) {
-                    log.warn("Can't {} ignore rule (vlan id {}, udp dst {}, device {}) due to {}",
-                             op, vlanId, udpDstPort, deviceId, error);
-                }
-            };
-
-            ForwardingObjective fwd;
-            switch (op) {
-                case ADD:
-                    fwd = builder.add(objectiveContext);
-                    break;
-                case REMOVE:
-                    fwd = builder.remove(objectiveContext);
-                    break;
-                default:
-                    log.warn("Unsupported objective operation {}", op);
-                    return;
-            }
-
-            Device device = deviceService.getDevice(deviceId);
-            if (device == null || !device.is(Pipeliner.class)) {
-                log.warn("Device {} is not available now, wait until device is available", deviceId);
-                return;
-            }
-            flowObjectiveService.apply(deviceId, fwd);
-        });
-    }
-
-    /**
-     * Request DHCP packet in via PacketService.
-     */
-    private void requestDhcpPackets() {
-        DHCP_SELECTORS.forEach(trafficSelector -> {
-            packetService.requestPackets(trafficSelector, PacketPriority.CONTROL, appId);
-        });
-    }
-
-    /**
-     * Cancel requested DHCP packets in via packet service.
-     */
-    private void cancelDhcpPackets() {
-        DHCP_SELECTORS.forEach(trafficSelector -> {
-            packetService.cancelPackets(trafficSelector, PacketPriority.CONTROL, appId);
-        });
-    }
-
     /**
      * Request ARP packet in via PacketService.
      */
@@ -663,14 +509,10 @@
             Device device = event.subject();
             switch (event.type()) {
                 case DEVICE_ADDED:
-                    deviceAdd(device.id());
-                    break;
-                case DEVICE_REMOVED:
-                    ignoredVlans.removeAll(device.id());
+                    updateIgnoreVlanConfigs();
                     break;
                 case DEVICE_AVAILABILITY_CHANGED:
                     deviceAvailabilityChanged(device);
-
                 default:
                     break;
             }
@@ -678,23 +520,14 @@
 
         private void deviceAvailabilityChanged(Device device) {
             if (deviceService.isAvailable(device.id())) {
-                deviceAdd(device.id());
-            } else {
-                ignoredVlans.removeAll(device.id());
+                updateIgnoreVlanConfigs();
             }
         }
 
-        private void deviceAdd(DeviceId deviceId) {
+        private void updateIgnoreVlanConfigs() {
             IgnoreDhcpConfig config = cfgService.getConfig(appId, IgnoreDhcpConfig.class);
-            if (config == null) {
-                log.debug("No ignoreVlan config found for {}. Do nothing.", deviceId);
-                return;
-            }
-
-            Collection<VlanId> vlanIds = config.ignoredVlans().get(deviceId);
-            vlanIds.forEach(vlanId -> {
-                processIgnoreVlanRule(deviceId, vlanId, ADD);
-            });
+            v4Handler.updateIgnoreVlanConfig(config);
+            v6Handler.updateIgnoreVlanConfig(config);
         }
     }
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
index 2efeca4..5397d18 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpHandler.java
@@ -22,6 +22,7 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.dhcprelay.config.DhcpServerConfig;
+import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.packet.PacketContext;
 
@@ -161,4 +162,11 @@
      * @param configs the config
      */
     void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs);
+
+    /**
+     * Push IgnoreDhcpConfig to the handler.
+     *
+     * @param config the config
+     */
+    void updateIgnoreVlanConfig(IgnoreDhcpConfig config);
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java
index 19d8ed2..39683cd 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java
@@ -27,6 +27,9 @@
 
 import java.util.Optional;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
 /**
  * DHCP server configuration.
  */
@@ -100,6 +103,11 @@
                 }
             });
         }
+
+        checkNotNull(connectPoint, "Connect point of DHCP server can't be null");
+        checkState(serverIp4Addr != null || serverIp6Addr != null,
+                   "Should exist at least one server IP for DHCPv4 or DHCPv6");
+
     }
 
     /**
diff --git a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
index 0de95f0..eb24ae2 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
 import com.google.common.io.Resources;
 import org.apache.commons.io.Charsets;
 import org.easymock.Capture;
@@ -116,7 +117,6 @@
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.*;
 import static org.onosproject.dhcprelay.DhcpRelayManager.DHCP_RELAY_APP;
-import static org.onosproject.dhcprelay.DhcpRelayManager.DHCP_SELECTORS;
 
 public class DhcpRelayManagerTest {
     private static final short VLAN_LEN = 2;
@@ -227,6 +227,8 @@
     // Relay agent config
     private static final Ip4Address RELAY_AGENT_IP = Ip4Address.valueOf("10.0.4.254");
 
+    private static final List<TrafficSelector> DHCP_SELECTORS = buildClientDhcpSelectors();
+
     // Components
     private static final ApplicationId APP_ID = TestApplicationId.create(DhcpRelayManager.DHCP_RELAY_APP);
     private static final DefaultDhcpRelayConfig CONFIG = new MockDefaultDhcpRelayConfig();
@@ -246,6 +248,10 @@
     private MockRouteStore mockRouteStore;
     private MockDhcpRelayStore mockDhcpRelayStore;
     private HostProviderService mockHostProviderService;
+    private FlowObjectiveService flowObjectiveService;
+    private DeviceService deviceService;
+    private Dhcp4HandlerImpl v4Handler;
+    private Dhcp6HandlerImpl v6Handler;
 
     private static Interface createInterface(String name, ConnectPoint connectPoint,
                                              List<InterfaceIpAddress> interfaceIps,
@@ -286,38 +292,47 @@
         packetService = new MockPacketService();
         manager.packetService = packetService;
         manager.compCfgService = createNiceMock(ComponentConfigService.class);
-        manager.deviceService = createNiceMock(DeviceService.class);
+        deviceService = createNiceMock(DeviceService.class);
 
         Device device = createNiceMock(Device.class);
         expect(device.is(Pipeliner.class)).andReturn(true).anyTimes();
 
-        expect(manager.deviceService.getDevice(DEV_1_ID)).andReturn(device).anyTimes();
-        expect(manager.deviceService.getDevice(DEV_2_ID)).andReturn(device).anyTimes();
-        replay(manager.deviceService, device);
+        expect(deviceService.getDevice(DEV_1_ID)).andReturn(device).anyTimes();
+        expect(deviceService.getDevice(DEV_2_ID)).andReturn(device).anyTimes();
+        replay(deviceService, device);
 
         mockRouteStore = new MockRouteStore();
         mockDhcpRelayStore = new MockDhcpRelayStore();
         manager.dhcpRelayStore = mockDhcpRelayStore;
+        manager.deviceService = deviceService;
 
         manager.interfaceService = new MockInterfaceService();
-        manager.flowObjectiveService = EasyMock.niceMock(FlowObjectiveService.class);
+        flowObjectiveService = EasyMock.niceMock(FlowObjectiveService.class);
         mockHostProviderService = createNiceMock(HostProviderService.class);
-        Dhcp4HandlerImpl v4Handler = new Dhcp4HandlerImpl();
+        v4Handler = new Dhcp4HandlerImpl();
         v4Handler.providerService = mockHostProviderService;
         v4Handler.dhcpRelayStore = mockDhcpRelayStore;
         v4Handler.hostService = manager.hostService;
         v4Handler.interfaceService = manager.interfaceService;
         v4Handler.packetService = manager.packetService;
         v4Handler.routeStore = mockRouteStore;
+        v4Handler.coreService = createNiceMock(CoreService.class);
+        v4Handler.flowObjectiveService = flowObjectiveService;
+        v4Handler.appId = TestApplicationId.create(Dhcp4HandlerImpl.DHCP_V4_RELAY_APP);
+        v4Handler.deviceService = deviceService;
         manager.v4Handler = v4Handler;
 
-        Dhcp6HandlerImpl v6Handler = new Dhcp6HandlerImpl();
+        v6Handler = new Dhcp6HandlerImpl();
         v6Handler.dhcpRelayStore = mockDhcpRelayStore;
         v6Handler.hostService = manager.hostService;
         v6Handler.interfaceService = manager.interfaceService;
         v6Handler.packetService = manager.packetService;
         v6Handler.routeStore = mockRouteStore;
         v6Handler.providerService = mockHostProviderService;
+        v6Handler.coreService = createNiceMock(CoreService.class);
+        v6Handler.flowObjectiveService = flowObjectiveService;
+        v6Handler.appId = TestApplicationId.create(Dhcp6HandlerImpl.DHCP_V6_RELAY_APP);
+        v6Handler.deviceService = deviceService;
         manager.v6Handler = v6Handler;
 
         // properties
@@ -454,26 +469,25 @@
         config.init(APP_ID, IgnoreDhcpConfig.KEY, json, om, null);
 
         Capture<Objective> capturedFromDev1 = newCapture(CaptureType.ALL);
-        manager.flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
+        flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
         expectLastCall().times(DHCP_SELECTORS.size());
         Capture<Objective> capturedFromDev2 = newCapture(CaptureType.ALL);
-        manager.flowObjectiveService.apply(eq(DEV_2_ID), capture(capturedFromDev2));
+        flowObjectiveService.apply(eq(DEV_2_ID), capture(capturedFromDev2));
         expectLastCall().times(DHCP_SELECTORS.size());
-        replay(manager.flowObjectiveService);
+        replay(flowObjectiveService);
         manager.updateConfig(config);
-        verify(manager.flowObjectiveService);
+        verify(flowObjectiveService);
 
         List<Objective> objectivesFromDev1 = capturedFromDev1.getValues();
         List<Objective> objectivesFromDev2 = capturedFromDev2.getValues();
 
         assertTrue(objectivesFromDev1.containsAll(objectivesFromDev2));
         assertTrue(objectivesFromDev2.containsAll(objectivesFromDev1));
-        TrafficTreatment dropTreatment = DefaultTrafficTreatment.emptyTreatment();
-        dropTreatment.clearedDeferred();
+        TrafficTreatment dropTreatment = DefaultTrafficTreatment.builder().wipeDeferred().build();
 
         for (int index = 0; index < objectivesFromDev1.size(); index++) {
             TrafficSelector selector =
-                    DefaultTrafficSelector.builder(DhcpRelayManager.DHCP_SELECTORS.get(index))
+                    DefaultTrafficSelector.builder(DHCP_SELECTORS.get(index))
                     .matchVlanId(IGNORED_VLAN)
                     .build();
             ForwardingObjective fwd = (ForwardingObjective) objectivesFromDev1.get(index);
@@ -487,7 +501,8 @@
             });
         }
         objectivesFromDev2.forEach(obj -> obj.context().ifPresent(ctx -> ctx.onSuccess(obj)));
-        assertEquals(2, manager.ignoredVlans.size());
+        assertEquals(2, v4Handler.ignoredVlans.size());
+        assertEquals(2, v6Handler.ignoredVlans.size());
     }
 
     /**
@@ -495,31 +510,32 @@
      */
     @Test
     public void testRemoveIgnoreVlan() {
-        manager.ignoredVlans.put(DEV_1_ID, IGNORED_VLAN);
-        manager.ignoredVlans.put(DEV_2_ID, IGNORED_VLAN);
+        v4Handler.ignoredVlans.put(DEV_1_ID, IGNORED_VLAN);
+        v4Handler.ignoredVlans.put(DEV_2_ID, IGNORED_VLAN);
+        v6Handler.ignoredVlans.put(DEV_1_ID, IGNORED_VLAN);
+        v6Handler.ignoredVlans.put(DEV_2_ID, IGNORED_VLAN);
         IgnoreDhcpConfig config = new IgnoreDhcpConfig();
 
         Capture<Objective> capturedFromDev1 = newCapture(CaptureType.ALL);
-        manager.flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
+        flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
         expectLastCall().times(DHCP_SELECTORS.size());
         Capture<Objective> capturedFromDev2 = newCapture(CaptureType.ALL);
-        manager.flowObjectiveService.apply(eq(DEV_2_ID), capture(capturedFromDev2));
+        flowObjectiveService.apply(eq(DEV_2_ID), capture(capturedFromDev2));
         expectLastCall().times(DHCP_SELECTORS.size());
-        replay(manager.flowObjectiveService);
+        replay(flowObjectiveService);
         manager.removeConfig(config);
-        verify(manager.flowObjectiveService);
+        verify(flowObjectiveService);
 
         List<Objective> objectivesFromDev1 = capturedFromDev1.getValues();
         List<Objective> objectivesFromDev2 = capturedFromDev2.getValues();
 
         assertTrue(objectivesFromDev1.containsAll(objectivesFromDev2));
         assertTrue(objectivesFromDev2.containsAll(objectivesFromDev1));
-        TrafficTreatment dropTreatment = DefaultTrafficTreatment.emptyTreatment();
-        dropTreatment.clearedDeferred();
+        TrafficTreatment dropTreatment = DefaultTrafficTreatment.builder().wipeDeferred().build();
 
         for (int index = 0; index < objectivesFromDev1.size(); index++) {
             TrafficSelector selector =
-                    DefaultTrafficSelector.builder(DhcpRelayManager.DHCP_SELECTORS.get(index))
+                    DefaultTrafficSelector.builder(DHCP_SELECTORS.get(index))
                     .matchVlanId(IGNORED_VLAN)
                     .build();
             ForwardingObjective fwd = (ForwardingObjective) objectivesFromDev1.get(index);
@@ -533,7 +549,8 @@
             });
         }
         objectivesFromDev2.forEach(obj -> obj.context().ifPresent(ctx -> ctx.onSuccess(obj)));
-        assertEquals(0, manager.ignoredVlans.size());
+        assertEquals(0, v4Handler.ignoredVlans.size());
+        assertEquals(0, v6Handler.ignoredVlans.size());
     }
 
     /**
@@ -555,14 +572,15 @@
         config.init(APP_ID, IgnoreDhcpConfig.KEY, json, om, null);
 
         Capture<Objective> capturedFromDev1 = newCapture(CaptureType.ALL);
-        manager.flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
+        flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
         expectLastCall().times(DHCP_SELECTORS.size());
-        replay(manager.flowObjectiveService, manager.deviceService, device);
+        replay(flowObjectiveService, manager.deviceService, device);
 
         manager.updateConfig(config);
         capturedFromDev1.getValues().forEach(obj -> obj.context().ifPresent(ctx -> ctx.onSuccess(obj)));
 
-        assertEquals(1, manager.ignoredVlans.size());
+        assertEquals(1, v4Handler.ignoredVlans.size());
+        assertEquals(1, v6Handler.ignoredVlans.size());
     }
 
     /**
@@ -576,7 +594,7 @@
         json = json.path("apps").path(DHCP_RELAY_APP).path(IgnoreDhcpConfig.KEY);
         config.init(APP_ID, IgnoreDhcpConfig.KEY, json, om, null);
 
-        reset(manager.cfgService, manager.flowObjectiveService, manager.deviceService);
+        reset(manager.cfgService, flowObjectiveService, manager.deviceService);
         expect(manager.cfgService.getConfig(APP_ID, IgnoreDhcpConfig.class))
                 .andReturn(config).anyTimes();
 
@@ -586,13 +604,13 @@
         expect(manager.deviceService.getDevice(DEV_1_ID)).andReturn(device).anyTimes();
         DeviceEvent event = new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, device);
         Capture<Objective> capturedFromDev1 = newCapture(CaptureType.ALL);
-        manager.flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
+        flowObjectiveService.apply(eq(DEV_1_ID), capture(capturedFromDev1));
         expectLastCall().times(DHCP_SELECTORS.size());
-        replay(manager.cfgService, manager.flowObjectiveService, manager.deviceService, device);
-        assertEquals(0, manager.ignoredVlans.size());
+        replay(manager.cfgService, flowObjectiveService, manager.deviceService, device);
         manager.deviceListener.event(event);
         capturedFromDev1.getValues().forEach(obj -> obj.context().ifPresent(ctx -> ctx.onSuccess(obj)));
-        assertEquals(1, manager.ignoredVlans.size());
+        assertEquals(1, v4Handler.ignoredVlans.size());
+        assertEquals(1, v6Handler.ignoredVlans.size());
     }
 
     /**
@@ -1296,6 +1314,12 @@
         return interfaceIdBytes;
     }
 
+    private static List<TrafficSelector> buildClientDhcpSelectors() {
+        return Streams.concat(Dhcp4HandlerImpl.DHCP_SELECTORS.stream(),
+                              Dhcp6HandlerImpl.DHCP_SELECTORS.stream())
+                .collect(Collectors.toList());
+    }
+
     private class TestDhcp6RequestPacketContext extends PacketContextAdapter {
 
 
diff --git a/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java b/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
index 54e0925..ecddc3a 100644
--- a/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
+++ b/utils/misc/src/main/java/org/onlab/packet/Ip6Address.java
@@ -32,6 +32,19 @@
     public static final int BIT_LENGTH = IpAddress.INET6_BIT_LENGTH;
 
     /**
+     * A link-scoped multicast address used by a DHCP client to communicate with
+     * neighboring DHCP relay agents and servers. (RFC 3315)
+     */
+    public static final Ip6Address ALL_DHCP_RELAY_AGENTS_AND_SERVERS =
+            Ip6Address.valueOf("ff02::1:2");
+    /**
+     * A site-scoped multicast address used by a DHCP relay agent to
+     * communicate with DHCP servers. (RFC 3315)
+     */
+    public static final Ip6Address ALL_DHCP_SERVERS =
+            Ip6Address.valueOf("ff05::1:3");
+
+    /**
      * All-zero unspecified IPv6 address.
      */
     public static final Ip6Address ZERO = Ip6Address.valueOf("::");