CORD-2870: Bypass DHCP leasequery without learning routes.

  Changes:
    * Add configuration flag to disable old leasequery routing/learning flow
    * Route leasequery (v4 and v6) responses to an originator
    * Fix NPE and BufferOverflow exceptions in Dhcp6LeaseQueryOption
    * Make Dhcp4/Dhcp6HandlerUtil classes static
    * Fix codestyle issues

Change-Id: Ic9e527d73a226e7f1f544dab9fb98398b85c5460
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 26643fc0..424b628 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -26,6 +26,7 @@
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -43,6 +44,8 @@
 import org.onlab.packet.dhcp.CircuitId;
 import org.onlab.packet.dhcp.DhcpOption;
 import org.onlab.packet.dhcp.DhcpRelayAgentOption;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.dhcprelay.api.DhcpHandler;
@@ -88,14 +91,16 @@
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketService;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Dictionary;
 import java.util.List;
-import java.util.ArrayList;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -115,7 +120,6 @@
 import static org.onosproject.net.flowobjective.Objective.Operation.ADD;
 import static org.onosproject.net.flowobjective.Objective.Operation.REMOVE;
 
-import org.onosproject.dhcprelay.Dhcp4HandlerUtil.InternalPacket;
 
 @Component
 @Service
@@ -126,6 +130,8 @@
     private static final String BROADCAST_IP = "255.255.255.255";
     private static final int IGNORE_CONTROL_PRIORITY = PacketPriority.CONTROL.priorityValue() + 1000;
 
+    private static final String LQ_ROUTE_PROPERTY_NAME = "learnRouteFromLeasequery";
+
     private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
             .matchEthType(Ethernet.TYPE_IPV4)
             .matchIPProtocol(IPv4.PROTOCOL_UDP)
@@ -173,6 +179,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowObjectiveService flowObjectiveService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
     protected HostProviderService providerService;
     protected ApplicationId appId;
     protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
@@ -180,13 +189,18 @@
 
     private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
     private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
-    private Dhcp4HandlerUtil dhcp4HandlerUtil = new Dhcp4HandlerUtil();
+
+    @Property(name = Dhcp4HandlerImpl.LQ_ROUTE_PROPERTY_NAME, boolValue = false,
+            label = "Enable learning routing information from LQ")
+    private Boolean learnRouteFromLeasequery = Boolean.TRUE;
 
     private Executor hostEventExecutor = newSingleThreadExecutor(
         groupedThreads("dhcp4-event-host", "%d", log));
 
     @Activate
-    protected void activate() {
+    protected void activate(ComponentContext context) {
+        cfgService.registerProperties(getClass());
+        modified(context);
         appId = coreService.registerApplication(DHCP_V4_RELAY_APP);
         hostService.addListener(hostListener);
         providerService = providerRegistry.register(this);
@@ -194,6 +208,7 @@
 
     @Deactivate
     protected void deactivate() {
+        cfgService.unregisterProperties(getClass(), false);
         providerRegistry.unregister(this);
         hostService.removeListener(hostListener);
         defaultServerInfoList.forEach(this::stopMonitoringIps);
@@ -202,6 +217,18 @@
         indirectServerInfoList.clear();
     }
 
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        Boolean flag;
+        flag = Tools.isPropertyEnabled(properties, Dhcp4HandlerImpl.LQ_ROUTE_PROPERTY_NAME);
+        if (flag != null) {
+            learnRouteFromLeasequery = flag;
+            log.info("Learning routes from DHCP leasequery is {}",
+                    learnRouteFromLeasequery ? "enabled" : "disabled");
+        }
+    }
+
     private void stopMonitoringIps(DhcpServerInfo serverInfo) {
         serverInfo.getDhcpGatewayIp4().ifPresent(gatewayIp -> {
             hostService.stopMonitoringIp(gatewayIp);
@@ -302,7 +329,7 @@
         // Create new server info according to the config
         serverInfoList.clear();
         for (DhcpServerConfig serverConfig : configs) {
-            log.info("// Create new server info according to the config");
+            log.debug("Create new server info according to the config");
             DhcpServerInfo newServerInfo = new DhcpServerInfo(serverConfig,
                     DhcpServerInfo.Version.DHCP_V4);
             checkState(newServerInfo.getDhcpServerConnectPoint().isPresent(),
@@ -384,9 +411,9 @@
                 break;
             case DHCPOFFER:
                 //reply to dhcp client.
-                Ethernet ethernetPacketOffer = processDhcpPacketFromServer(context, packet);
+                InternalPacket ethernetPacketOffer = processDhcpPacketFromServer(context, packet);
                 if (ethernetPacketOffer != null) {
-                    writeResponseDhcpRecord(ethernetPacketOffer, dhcpPayload);
+                    writeResponseDhcpRecord(ethernetPacketOffer.getPacket(), dhcpPayload);
                     sendResponseToClient(ethernetPacketOffer, dhcpPayload);
                 }
                 break;
@@ -405,10 +432,10 @@
                 break;
             case DHCPACK:
                 // reply to dhcp client.
-                Ethernet ethernetPacketAck = processDhcpPacketFromServer(context, packet);
+                InternalPacket ethernetPacketAck = processDhcpPacketFromServer(context, packet);
                 if (ethernetPacketAck != null) {
-                    writeResponseDhcpRecord(ethernetPacketAck, dhcpPayload);
-                    handleDhcpAck(ethernetPacketAck, dhcpPayload);
+                    writeResponseDhcpRecord(ethernetPacketAck.getPacket(), dhcpPayload);
+                    handleDhcpAck(ethernetPacketAck.getPacket(), dhcpPayload);
                     sendResponseToClient(ethernetPacketAck, dhcpPayload);
                 }
                 break;
@@ -520,151 +547,171 @@
     private void handleLeaseQueryActivateMsg(Ethernet packet, DHCP dhcpPayload) {
         log.debug("LQ: Got DHCPLEASEACTIVE packet!");
 
-        // TODO: release the ip address from client
-        MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
-        VlanId vlanId = VlanId.vlanId(packet.getVlanID());
-        HostId hostId = HostId.hostId(clientMacAddress, vlanId);
-        DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
+        if (learnRouteFromLeasequery) {
+            // TODO: release the ip address from client
+            MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
+            VlanId vlanId = VlanId.vlanId(packet.getVlanID());
+            HostId hostId = HostId.hostId(clientMacAddress, vlanId);
+            DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
 
-        if (record == null) {
-            log.warn("Can't find record for host {} when processing DHCPLEASEACTIVE", hostId);
-            return;
+            if (record == null) {
+                log.warn("Can't find record for host {} when processing DHCPLEASEACTIVE", hostId);
+                return;
+            }
+
+            // need to update routes
+            log.debug("Lease Query for Client results in DHCPLEASEACTIVE - route needs to be modified");
+            // get current route
+            // find the ip of that client with the DhcpRelay store
+
+            Ip4Address clientIP = record.ip4Address().orElse(null);
+            log.debug("LQ: IP of host is " + clientIP.getIp4Address());
+
+            MacAddress nextHopMac = record.nextHop().orElse(null);
+            log.debug("LQ: MAC of resulting *OLD* NH for that host is " + nextHopMac.toString());
+
+            // find the new NH by looking at the src MAC of the dhcp request
+            // from the LQ store
+            MacAddress newNextHopMac = record.nextHopTemp().orElse(null);
+            log.debug("LQ: MAC of resulting *NEW* NH for that host is " + newNextHopMac.toString());
+
+            log.debug("LQ: updating dhcp relay record with new NH");
+            record.nextHop(newNextHopMac);
+
+            // find the next hop IP from its mac
+            HostId gwHostId = HostId.hostId(newNextHopMac, vlanId);
+            Host gwHost = hostService.getHost(gwHostId);
+
+            if (gwHost == null) {
+                log.warn("Can't find gateway for new NH host " + gwHostId);
+                return;
+            }
+
+            Ip4Address nextHopIp = gwHost.ipAddresses()
+                    .stream()
+                    .filter(IpAddress::isIp4)
+                    .map(IpAddress::getIp4Address)
+                    .findFirst()
+                    .orElse(null);
+
+            if (nextHopIp == null) {
+                log.warn("Can't find IP address of gateway " + gwHost);
+                return;
+            }
+
+            log.debug("LQ: *NEW* NH IP for host is " + nextHopIp.getIp4Address());
+            Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
+            routeStore.updateRoute(route);
         }
 
-        // need to update routes
-        log.debug("Lease Query for Client results in DHCPLEASEACTIVE - route needs to be modified");
-        // get current route
-        // find the ip of that client with the DhcpRelay store
-
-        Ip4Address clientIP = record.ip4Address().orElse(null);
-        log.debug("LQ: IP of host is " + clientIP.getIp4Address());
-
-        MacAddress nextHopMac = record.nextHop().orElse(null);
-        log.debug("LQ: MAC of resulting *OLD* NH for that host is " + nextHopMac.toString());
-
-        // find the new NH by looking at the src MAC of the dhcp request
-        // from the LQ store
-        MacAddress newNextHopMac = record.nextHopTemp().orElse(null);
-        log.debug("LQ: MAC of resulting *NEW* NH for that host is " + newNextHopMac.toString());
-
-        log.debug("LQ: updating dhcp relay record with new NH");
-        record.nextHop(newNextHopMac);
-
-        // find the next hop IP from its mac
-        HostId gwHostId = HostId.hostId(newNextHopMac, vlanId);
-        Host gwHost = hostService.getHost(gwHostId);
-
-        if (gwHost == null) {
-            log.warn("Can't find gateway for new NH host " + gwHostId);
-            return;
-        }
-
-        Ip4Address nextHopIp = gwHost.ipAddresses()
-                .stream()
-                .filter(IpAddress::isIp4)
-                .map(IpAddress::getIp4Address)
-                .findFirst()
-                .orElse(null);
-
-        if (nextHopIp == null) {
-            log.warn("Can't find IP address of gateway " + gwHost);
-            return;
-        }
-
-        log.debug("LQ: *NEW* NH IP for host is " + nextHopIp.getIp4Address());
-        Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
-        routeStore.updateRoute(route);
-
         // and forward to client
-        Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
+        InternalPacket ethernetPacket = processLeaseQueryFromServer(packet);
         if (ethernetPacket != null) {
             sendResponseToClient(ethernetPacket, dhcpPayload);
         }
     }
 
-    /**
-     *
-     */
     private void handleLeaseQueryMsg(PacketContext context, Ethernet packet, DHCP dhcpPayload) {
-        log.debug("LQ: Got DHCPLEASEQUERY packet!");
-        MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
-        log.debug("LQ: got DHCPLEASEQUERY with MAC " + clientMacAddress.toString());
-        // add the client mac (hostid) of this request to a store (the entry will be removed with
-        // the reply sent to the originator)
-        VlanId vlanId = VlanId.vlanId(packet.getVlanID());
-        HostId hId = HostId.hostId(clientMacAddress, vlanId);
-        DhcpRecord record = dhcpRelayStore.getDhcpRecord(hId).orElse(null);
-        if (record != null) {
-            //new NH is to be taken from src mac of LQ packet
-            MacAddress newNextHop = packet.getSourceMAC();
-            record.nextHopTemp(newNextHop);
-            record.ip4Status(dhcpPayload.getPacketType());
-            record.updateLastSeen();
+        // If this flag is enabled we expect that DHCPLEASEQUERY-ies are sent from an access concentrator
+        // where queried client is connected to. Otherwise, DHCPLEASEQUERY source may be a separate connected agent
+        if (learnRouteFromLeasequery) {
+            log.debug("LQ: Got DHCPLEASEQUERY packet!");
+            MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
+            log.debug("LQ: got DHCPLEASEQUERY with MAC " + clientMacAddress.toString());
+            // add the client mac (hostid) of this request to a store (the entry will be removed with
+            // the reply sent to the originator)
+            VlanId vlanId = VlanId.vlanId(packet.getVlanID());
+            HostId hId = HostId.hostId(clientMacAddress, vlanId);
+            DhcpRecord record = dhcpRelayStore.getDhcpRecord(hId).orElse(null);
+            if (record != null) {
+                //new NH is to be taken from src mac of LQ packet
+                MacAddress newNextHop = packet.getSourceMAC();
+                record.nextHopTemp(newNextHop);
+                record.ip4Status(dhcpPayload.getPacketType());
+                record.updateLastSeen();
+
+                // do a basic routing of the packet (this is unicast routing
+                // not a relay operation like for other broadcast dhcp packets
+                List<InternalPacket> ethernetPacketRequest = processLeaseQueryFromAgent(context, packet);
+                // and forward to server
+                for (InternalPacket internalPacket : ethernetPacketRequest) {
+                    log.debug("LeaseQueryMsg forward to server");
+                    forwardPacket(internalPacket);
+                }
+            } else {
+                log.warn("LQ: Error! - DHCP relay record for that client not found - ignoring LQ!");
+            }
+        } else {
+            log.debug("LQ: Got DHCPLEASEQUERY packet!");
+
+            int giaddr = dhcpPayload.getGatewayIPAddress();
+
+            log.debug("DHCPLEASEQUERY giaddr: {} ({}). Originators connectPoint: {}", giaddr,
+                    Ip4Address.valueOf(giaddr), context.inPacket().receivedFrom());
 
             // do a basic routing of the packet (this is unicast routing
             // not a relay operation like for other broadcast dhcp packets
             List<InternalPacket> ethernetPacketRequest = processLeaseQueryFromAgent(context, packet);
             // and forward to server
             for (InternalPacket internalPacket : ethernetPacketRequest) {
-                log.debug("LeaseQueryMsg forward to server");
+                log.trace("LeaseQueryMsg forward to server connected to {}", internalPacket.getDestLocation());
                 forwardPacket(internalPacket);
             }
-        } else {
-            log.warn("LQ: Error! - DHCP relay record for that client not found - ignoring LQ!");
         }
     }
 
     private void handleLeaseQueryUnknown(Ethernet packet, DHCP dhcpPayload) {
         log.debug("Lease Query for Client results in DHCPLEASEUNASSIGNED or " +
                           "DHCPLEASEUNKNOWN - removing route & forwarding reply to originator");
-        MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
-        VlanId vlanId = VlanId.vlanId(packet.getVlanID());
-        HostId hostId = HostId.hostId(clientMacAddress, vlanId);
-        DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
+        if (learnRouteFromLeasequery) {
+            MacAddress clientMacAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
+            VlanId vlanId = VlanId.vlanId(packet.getVlanID());
+            HostId hostId = HostId.hostId(clientMacAddress, vlanId);
+            DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
 
-        if (record == null) {
-            log.warn("Can't find record for host {} when handling LQ UNKNOWN/UNASSIGNED message", hostId);
-            return;
+            if (record == null) {
+                log.warn("Can't find record for host {} when handling LQ UNKNOWN/UNASSIGNED message", hostId);
+                return;
+            }
+
+            Ip4Address clientIP = record.ip4Address().orElse(null);
+            log.debug("LQ: IP of host is " + clientIP.getIp4Address());
+
+            // find the new NH by looking at the src MAC of the dhcp request
+            // from the LQ store
+            MacAddress nextHopMac = record.nextHop().orElse(null);
+            log.debug("LQ: MAC of resulting *Existing* NH for that route is " + nextHopMac.toString());
+
+            // find the next hop IP from its mac
+            HostId gwHostId = HostId.hostId(nextHopMac, vlanId);
+            Host gwHost = hostService.getHost(gwHostId);
+
+            if (gwHost == null) {
+                log.warn("Can't find gateway for new NH host " + gwHostId);
+                return;
+            }
+
+            Ip4Address nextHopIp = gwHost.ipAddresses()
+                    .stream()
+                    .filter(IpAddress::isIp4)
+                    .map(IpAddress::getIp4Address)
+                    .findFirst()
+                    .orElse(null);
+
+            if (nextHopIp == null) {
+                log.warn("Can't find IP address of gateway {}", gwHost);
+                return;
+            }
+
+            log.debug("LQ: *Existing* NH IP for host is " + nextHopIp.getIp4Address() + " removing route for it");
+            Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
+            routeStore.removeRoute(route);
+
+            // remove from temp store
+            dhcpRelayStore.removeDhcpRecord(hostId);
         }
-
-        Ip4Address clientIP = record.ip4Address().orElse(null);
-        log.debug("LQ: IP of host is " + clientIP.getIp4Address());
-
-        // find the new NH by looking at the src MAC of the dhcp request
-        // from the LQ store
-        MacAddress nextHopMac = record.nextHop().orElse(null);
-        log.debug("LQ: MAC of resulting *Existing* NH for that route is " + nextHopMac.toString());
-
-        // find the next hop IP from its mac
-        HostId gwHostId = HostId.hostId(nextHopMac, vlanId);
-        Host gwHost = hostService.getHost(gwHostId);
-
-        if (gwHost == null) {
-            log.warn("Can't find gateway for new NH host " + gwHostId);
-            return;
-        }
-
-        Ip4Address nextHopIp = gwHost.ipAddresses()
-                .stream()
-                .filter(IpAddress::isIp4)
-                .map(IpAddress::getIp4Address)
-                .findFirst()
-                .orElse(null);
-
-        if (nextHopIp == null) {
-            log.warn("Can't find IP address of gateway {}", gwHost);
-            return;
-        }
-
-        log.debug("LQ: *Existing* NH IP for host is " + nextHopIp.getIp4Address() + " removing route for it");
-        Route route = new Route(Route.Source.DHCP, clientIP.toIpPrefix(), nextHopIp);
-        routeStore.removeRoute(route);
-
-        // remove from temp store
-        dhcpRelayStore.removeDhcpRecord(hostId);
-
         // and forward to client
-        Ethernet ethernetPacket = processLeaseQueryFromServer(packet);
+        InternalPacket ethernetPacket = processLeaseQueryFromServer(packet);
         if (ethernetPacket != null) {
             sendResponseToClient(ethernetPacket, dhcpPayload);
         }
@@ -683,7 +730,7 @@
         ConnectPoint receivedFrom = context.inPacket().receivedFrom();
         DeviceId receivedFromDevice = receivedFrom.deviceId();
         Ip4Address relayAgentIp = null;
-        relayAgentIp = dhcp4HandlerUtil.getRelayAgentIPv4Address(clientInterfaces);
+        relayAgentIp = Dhcp4HandlerUtil.getRelayAgentIPv4Address(clientInterfaces);
         MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
         if (relayAgentIp == null || relayAgentMac == null) {
             log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
@@ -724,7 +771,7 @@
         ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
         VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
         Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
-                .stream().filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
+                .stream().filter(iface -> Dhcp4HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
                 .findFirst()
                 .orElse(null);
 
@@ -735,9 +782,9 @@
 
         for (DhcpServerInfo serverInfo : copyServerInfoList) {
             etherReply = (Ethernet) ethernetPacket.clone();
-             ipv4Packet = (IPv4) etherReply.getPayload();
-             udpPacket = (UDP) ipv4Packet.getPayload();
-             dhcpPacket = (DHCP) udpPacket.getPayload();
+            ipv4Packet = (IPv4) etherReply.getPayload();
+            udpPacket = (UDP) ipv4Packet.getPayload();
+            dhcpPacket = (DHCP) udpPacket.getPayload();
             if (!checkDhcpServerConnPt(directConnFlag, serverInfo)) {
                 log.warn("Can't get server connect point, ignore");
                 continue;
@@ -850,8 +897,9 @@
             ipv4Packet.setPayload(udpPacket);
             ipv4Packet.setTtl((byte) 64);
             etherReply.setPayload(ipv4Packet);
-            InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
+            InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
                     serverInfo.getDhcpServerConnectPoint().get());
+
             internalPackets.add(internalPacket);
         }
         return internalPackets;
@@ -898,9 +946,6 @@
         boolean directConnFlag = directlyConnected(dhcpPacket);
 
         // Multi DHCP Start
-        ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
-        VlanId vlanIdInUse = VlanId.vlanId(ethernetPacket.getVlanID());
-
         List<InternalPacket> internalPackets = new ArrayList<>();
         List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
         List<DhcpServerInfo> copyServerInfoList = new ArrayList<>(serverInfoList);
@@ -947,27 +992,28 @@
                 if (newServerInfo.getDhcpConnectVlan().isPresent()) {
                     etherReply.setVlanID(serverInfo.getDhcpConnectVlan().get().toShort());
                 }
-                relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
-                // Sets relay agent IP
-                int effectiveRelayAgentIp = relayAgentIp != null ?
-                        relayAgentIp.toInt() : clientInterfaceIp.toInt();
-                dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
-            } else {
-                if (!newServerInfo.getDhcpServerIp4().isPresent()) {
-                  //do nothing
-                } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
-                    continue;
-                } else {
+                if (learnRouteFromLeasequery) {
                     relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
                     // Sets relay agent IP
                     int effectiveRelayAgentIp = relayAgentIp != null ?
                             relayAgentIp.toInt() : clientInterfaceIp.toInt();
                     dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
+                }
+            } else {
+                if (!newServerInfo.getDhcpServerIp4().isPresent()) {
+                  //do nothing
+                } else if (!newServerInfo.getDhcpConnectMac().isPresent()) {
+                    continue;
+                } else if (learnRouteFromLeasequery) {
+                    relayAgentIp = newServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
+                    // Sets relay agent IP
+                    int effectiveRelayAgentIp = relayAgentIp != null ?
+                            relayAgentIp.toInt() : clientInterfaceIp.toInt();
                     dhcpPacket.setGatewayIPAddress(effectiveRelayAgentIp);
-                    log.info("Relay Agent IP {}", relayAgentIp);
+                    log.debug("Relay Agent IP {}", relayAgentIp);
                 }
 
-                log.info("In Direct");
+                log.trace("Indirect");
             }
 
             // Remove broadcast flag
@@ -981,12 +1027,14 @@
             ipv4Packet.setPayload(udpPacket);
             ipv4Packet.setTtl((byte) 64);
             etherReply.setPayload(ipv4Packet);
+            udpPacket.resetChecksum();
             ////return etherReply;
-            Dhcp4HandlerUtil.InternalPacket internalPacket = new Dhcp4HandlerUtil().new InternalPacket(etherReply,
+            InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
                     newServerInfo.getDhcpServerConnectPoint().get());
+
             internalPackets.add(internalPacket);
         }
-        log.warn("num of processLeaseQueryFromAgent packets to send is{}", internalPackets.size());
+        log.debug("num of processLeaseQueryFromAgent packets to send is: {}", internalPackets.size());
 
         return internalPackets;
     }
@@ -1066,7 +1114,7 @@
      * @param ethernetPacket the original packet comes from server
      * @return new packet which will send to the client
      */
-    private Ethernet processDhcpPacketFromServer(PacketContext context, Ethernet ethernetPacket) {
+    private InternalPacket processDhcpPacketFromServer(PacketContext context, Ethernet ethernetPacket) {
         // get dhcp header.
         Ethernet etherReply = (Ethernet) ethernetPacket.clone();
         IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
@@ -1090,7 +1138,7 @@
             log.warn("Cannot find server info");
             return null;
         } else {
-            if (dhcp4HandlerUtil.isServerIpEmpty(foundServerInfo)) {
+            if (Dhcp4HandlerUtil.isServerIpEmpty(foundServerInfo)) {
                 log.warn("Cannot find server info's ipaddress");
                 return null;
             }
@@ -1160,7 +1208,7 @@
         udpPacket.setPayload(dhcpPayload);
         ipv4Packet.setPayload(udpPacket);
         etherReply.setPayload(ipv4Packet);
-        return etherReply;
+        return InternalPacket.internalPacket(etherReply, clientInterface.connectPoint());
     }
 
     /**
@@ -1169,7 +1217,7 @@
      * @param ethernetPacket the original packet comes from server
      * @return new packet which will send to the client
      */
-    private Ethernet processLeaseQueryFromServer(Ethernet ethernetPacket) {
+    private InternalPacket processLeaseQueryFromServer(Ethernet ethernetPacket) {
         // get dhcp header.
         Ethernet etherReply = (Ethernet) ethernetPacket.clone();
         IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
@@ -1178,7 +1226,33 @@
 
         // determine the vlanId of the client host - note that this vlan id
         // could be different from the vlan in the packet from the server
-        Interface clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
+        Interface clientInterface = null;
+        MacAddress destinationMac = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
+
+        if (!learnRouteFromLeasequery) {
+            int giaddr = ipv4Packet.getDestinationAddress();
+            IpAddress destinationAddress = Ip4Address.valueOf(giaddr);
+            log.debug("DHCPLEASEQUERYRESP giaddr: {}({})", giaddr, destinationAddress);
+
+            Host destinationHost = hostService.getHostsByIp(destinationAddress).stream().findFirst().orElse(null);
+            if (destinationHost != null) {
+                destinationMac = destinationHost.mac();
+                log.trace("DHCPLEASEQUERYRESP destination mac is: {}", destinationMac);
+                ConnectPoint destinationLocation = destinationHost.location();
+                log.trace("Lookup for client interface by destination location {}", destinationLocation);
+                clientInterface = interfaceService.getInterfacesByPort(destinationLocation)
+                        .stream()
+                        .filter(iface -> interfaceContainsVlan(iface, VlanId.vlanId(etherReply.getVlanID())))
+                        .findFirst()
+                        .orElse(null);
+                log.trace("Found Host {} by ip {}", destinationHost, destinationAddress);
+                log.debug("DHCPLEASEQUERYRESP Client interface: {}",
+                        (clientInterface != null ? clientInterface : "not resolved"));
+
+            }
+        } else {
+            clientInterface = getClientInterface(ethernetPacket, dhcpPayload).orElse(null);
+        }
 
         if (clientInterface == null) {
             log.warn("Cannot find the interface for the DHCP {}", dhcpPayload);
@@ -1197,31 +1271,40 @@
         etherReply.setVlanID(vlanId.toShort());
         etherReply.setSourceMACAddress(clientInterface.mac());
 
-        if (!directlyConnected(dhcpPayload)) {
+        if (!directlyConnected(dhcpPayload) && learnRouteFromLeasequery) {
             // if client is indirectly connected, try use next hop mac address
             MacAddress macAddress = MacAddress.valueOf(dhcpPayload.getClientHardwareAddress());
             HostId hostId = HostId.hostId(macAddress, vlanId);
             DhcpRecord record = dhcpRelayStore.getDhcpRecord(hostId).orElse(null);
             if (record != null) {
                 // if next hop can be found, use mac address of next hop
-                record.nextHop().ifPresent(etherReply::setDestinationMACAddress);
+                Optional<MacAddress> nextHop = record.nextHopTemp();
+                if (!nextHop.isPresent()) {
+                    nextHop = record.nextHop();
+                }
+                nextHop.ifPresent(etherReply::setDestinationMACAddress);
             } else {
                 // otherwise, discard the packet
                 log.warn("Can't find record for host id {}, discard packet", hostId);
                 return null;
             }
         } else {
-            etherReply.setDestinationMACAddress(dhcpPayload.getClientHardwareAddress());
+            etherReply.setDestinationMACAddress(destinationMac);
         }
 
-        // default is client port
         udpPacket.setSourcePort(UDP.DHCP_SERVER_PORT);
-        udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
+        if (directlyConnected(dhcpPayload)) {
+            udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
+        } else {
+            udpPacket.setDestinationPort(UDP.DHCP_SERVER_PORT);
+        }
 
         udpPacket.setPayload(dhcpPayload);
         ipv4Packet.setPayload(udpPacket);
         etherReply.setPayload(ipv4Packet);
-        return etherReply;
+        udpPacket.resetChecksum();
+
+        return InternalPacket.internalPacket(etherReply, clientInterface.connectPoint());
     }
     /**
      * Extracts VLAN ID from relay agent option.
@@ -1274,6 +1357,17 @@
         return ethernet;
     }
 
+    private boolean isDhcpPacketLeasequery(DHCP dhcpPacket) {
+        switch (dhcpPacket.getPacketType()) {
+            case DHCPLEASEACTIVE:
+            case DHCPLEASEQUERY:
+            case DHCPLEASEUNASSIGNED:
+            case DHCPLEASEUNKNOWN:
+                return true;
+            default:
+                return false;
+        }
+    }
 
     /**
      * Check if the host is directly connected to the network or not.
@@ -1282,6 +1376,11 @@
      * @return true if the host is directly connected to the network; false otherwise
      */
     private boolean directlyConnected(DHCP dhcpPayload) {
+        // leasequery is always indirect
+        if (isDhcpPacketLeasequery(dhcpPayload)) {
+            return false;
+        }
+
         DhcpRelayAgentOption relayAgentOption =
                 (DhcpRelayAgentOption) dhcpPayload.getOption(OptionCode_CircuitID);
 
@@ -1445,31 +1544,30 @@
     /**
      * Send the response DHCP to the requester host.
      *
-     * @param ethPacket the packet
+     * @param thePacket the packet
      * @param dhcpPayload the DHCP data
      */
-    private void sendResponseToClient(Ethernet ethPacket, DHCP dhcpPayload) {
-        Optional<Interface> outInterface = getClientInterface(ethPacket, dhcpPayload);
+    private void sendResponseToClient(InternalPacket thePacket, DHCP dhcpPayload) {
+        checkNotNull(thePacket, "Nothing to send");
+        checkNotNull(thePacket.getPacket(), "Packet to send must not be empty");
+        checkNotNull(thePacket.getDestLocation(), "Packet destination not be empty");
+
+        Ethernet ethPacket = thePacket.getPacket();
         if (directlyConnected(dhcpPayload)) {
             ethPacket = removeRelayAgentOption(ethPacket);
         }
-        if (!outInterface.isPresent()) {
-            log.warn("Can't find output interface for client, ignore");
-            return;
-        }
-        Interface outIface = outInterface.get();
+
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(outIface.connectPoint().port())
+                .setOutput(thePacket.getDestLocation().port())
                 .build();
         OutboundPacket o = new DefaultOutboundPacket(
-                outIface.connectPoint().deviceId(),
+                thePacket.getDestLocation().deviceId(),
                 treatment,
                 ByteBuffer.wrap(ethPacket.serialize()));
         if (log.isTraceEnabled()) {
-            log.trace("Relaying packet to DHCP client {} via {}, vlan {}",
+            log.trace("Relaying packet to DHCP client {} via {}",
                       ethPacket,
-                      outIface.connectPoint(),
-                      outIface.vlan());
+                      thePacket.getDestLocation());
         }
         packetService.emit(o);
     }
@@ -1798,7 +1896,7 @@
         if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
             serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
                     .stream()
-                    .filter(iface -> dhcp4HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
+                    .filter(iface -> Dhcp4HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
                     .findFirst()
                     .orElse(null);
         } else {
@@ -1812,15 +1910,15 @@
     //forward the packet to ConnectPoint where the DHCP server is attached.
     private void forwardPacket(InternalPacket packet) {
         //send Packetout to dhcp server connectpoint.
-        if (packet.destLocation != null) {
+        if (packet.getDestLocation() != null) {
             TrafficTreatment t = DefaultTrafficTreatment.builder()
-                    .setOutput(packet.destLocation.port()).build();
+                    .setOutput(packet.getDestLocation().port()).build();
             OutboundPacket o = new DefaultOutboundPacket(
-                    packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
+                    packet.getDestLocation().deviceId(), t, ByteBuffer.wrap(packet.getPacket().serialize()));
             if (log.isTraceEnabled()) {
-                log.trace("Relaying packet to destination {}", packet.destLocation);
+                log.trace("Relaying packet to destination {}", packet.getDestLocation());
             }
-            log.debug("packetService.emit(o) to port {}", packet.destLocation);
+            log.debug("packetService.emit(o) to port {}", packet.getDestLocation());
             packetService.emit(o);
         }
     }
@@ -1842,5 +1940,4 @@
         }
         return foundServerInfo;
     }
-
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerUtil.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerUtil.java
index 077c6ca..a88bda8 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerUtil.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerUtil.java
@@ -18,12 +18,8 @@
 
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.VlanId;
-
-import org.onlab.packet.Ethernet;
-
 import org.onlab.util.HexString;
 import org.onosproject.dhcprelay.api.DhcpServerInfo;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.intf.Interface;
 
@@ -31,10 +27,11 @@
 import org.slf4j.LoggerFactory;
 import java.util.Set;
 
-public class Dhcp4HandlerUtil {
+public final class Dhcp4HandlerUtil {
+    private static final Logger log = LoggerFactory.getLogger(Dhcp4HandlerUtil.class);
 
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
+    private Dhcp4HandlerUtil() {
+    }
 
     /**
      * Returns the first v4 interface ip out of a set of interfaces or null.
@@ -42,7 +39,7 @@
      * @param intfs set of interfaces
      * @return Ip4Address / null if not present
      */
-    public Ip4Address getRelayAgentIPv4Address(Set<Interface> intfs) {
+    public static Ip4Address getRelayAgentIPv4Address(Set<Interface> intfs) {
         for (Interface intf : intfs) {
             for (InterfaceIpAddress ip : intf.ipAddressesList()) {
                 Ip4Address relayAgentIp = ip.ipAddress().getIp4Address();
@@ -61,7 +58,7 @@
      * @param vlanId the vlan id
      * @return true if the Interface contains the vlan id
      */
-    public boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
+    public static boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
         if (vlanId.equals(VlanId.NONE)) {
             // untagged packet, check if vlan untagged or vlan native is not NONE
             return !iface.vlanUntagged().equals(VlanId.NONE) ||
@@ -77,7 +74,7 @@
      * @param serverInfo server info to check
      * @return true if server info has v6 ip address; false otherwise
      */
-    public boolean isServerIpEmpty(DhcpServerInfo serverInfo) {
+    public static boolean isServerIpEmpty(DhcpServerInfo serverInfo) {
         if (!serverInfo.getDhcpServerIp4().isPresent()) {
             log.warn("DhcpServerIp not available, use default DhcpServerIp {}",
                     HexString.toHexString(serverInfo.getDhcpServerIp4().get().toOctets()));
@@ -85,20 +82,5 @@
         }
         return false;
     }
-
-
-    /**
-     * the new class the contains Ethernet packet and destination port.
-     */
-    public class InternalPacket {
-        Ethernet packet;
-        ConnectPoint destLocation;
-        public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
-            packet = newPacket;
-            destLocation = newLocation;
-        }
-    }
-
-
 }
 
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 e60cad8..73ddc5f 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -20,11 +20,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Deactivate;
 import com.google.common.collect.Sets;
 import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -54,6 +55,8 @@
 import org.onlab.packet.dhcp.Dhcp6Duid;
 import org.onlab.packet.DHCP6.MsgType;
 import org.onlab.util.HexString;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.dhcprelay.api.DhcpHandler;
@@ -83,7 +86,6 @@
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
-import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.intf.Interface;
@@ -101,17 +103,18 @@
 import org.onosproject.net.packet.OutboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketService;
+import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.dhcprelay.Dhcp6HandlerUtil.InternalPacket;
 import java.nio.ByteBuffer;
-import java.util.List;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Dictionary;
+import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import java.util.ArrayList;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -132,6 +135,7 @@
     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 String gCount = "global";
+    private static final String LQ_ROUTE_PROPERTY_NAME = "learnRouteFromLeasequery";
     private static final TrafficSelector CLIENT_SERVER_SELECTOR = DefaultTrafficSelector.builder()
             .matchEthType(Ethernet.TYPE_IPV6)
             .matchIPProtocol(IPv6.PROTOCOL_UDP)
@@ -194,12 +198,18 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowObjectiveService flowObjectiveService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
+    @Property(name = Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME, boolValue = false,
+            label = "Enable learning routing information from LQ")
+    private Boolean learnRouteFromLeasequery = Boolean.TRUE;
+
     protected HostProviderService providerService;
     protected ApplicationId appId;
     protected Multimap<DeviceId, VlanId> ignoredVlans = Multimaps.synchronizedMultimap(HashMultimap.create());
     private InternalHostListener hostListener = new InternalHostListener();
     private Boolean dhcpFpmEnabled = false;
-    private Dhcp6HandlerUtil dhcp6HandlerUtil = new Dhcp6HandlerUtil();
     private List<DhcpServerInfo> defaultServerInfoList = new CopyOnWriteArrayList<>();
     private List<DhcpServerInfo> indirectServerInfoList = new CopyOnWriteArrayList<>();
 
@@ -236,7 +246,9 @@
                             DHCP6.MsgType.LEASEQUERY_REPLY.value());
 
     @Activate
-    protected void activate() {
+    protected void activate(ComponentContext context) {
+        cfgService.registerProperties(getClass());
+        modified(context);
         appId = coreService.registerApplication(DHCP_V6_RELAY_APP);
         providerService = providerRegistry.register(this);
         hostService.addListener(hostListener);
@@ -244,6 +256,7 @@
 
     @Deactivate
     protected void deactivate() {
+        cfgService.unregisterProperties(getClass(), false);
         providerRegistry.unregister(this);
         hostService.removeListener(hostListener);
         defaultServerInfoList.forEach(this::stopMonitoringIps);
@@ -252,6 +265,18 @@
         indirectServerInfoList.clear();
     }
 
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        Boolean flag;
+        flag = Tools.isPropertyEnabled(properties, Dhcp6HandlerImpl.LQ_ROUTE_PROPERTY_NAME);
+        if (flag != null) {
+            learnRouteFromLeasequery = flag;
+            log.info("Learning routes from DHCP leasequery is {}",
+                    learnRouteFromLeasequery ? "enabled" : "disabled");
+        }
+    }
+
     private void stopMonitoringIps(DhcpServerInfo serverInfo) {
         serverInfo.getDhcpGatewayIp6().ifPresent(gatewayIp -> {
             hostService.stopMonitoringIp(gatewayIp);
@@ -307,7 +332,7 @@
 
     public DhcpRecord getDhcpRelayRecordFor(Ip6Address clientAddress) {
 
-        Collection<DhcpRecord>  records = dhcpRelayStore.getDhcpRecords();
+        Collection<DhcpRecord> records = dhcpRelayStore.getDhcpRecords();
         DhcpRecord dr = null;
         for (DhcpRecord e:records) {
             if (e.ip6Address().isPresent()) {
@@ -478,7 +503,7 @@
         ConnectPoint inPort = context.inPacket().receivedFrom();
 
         if (inPort == null) {
-            log.warn("incomming ConnectPoint is null");
+            log.warn("incoming ConnectPoint is null");
         }
         Set<Interface> receivingInterfaces = interfaceService.getInterfacesByPort(inPort);
         //ignore the packets if dhcp client interface is not configured on onos.
@@ -488,20 +513,21 @@
         }
 
         if (msgTypeVal == DHCP6.MsgType.LEASEQUERY.value()) {
-            List<InternalPacket> ethernetClientPacket =
-                    processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload);
-            for (InternalPacket internalPacket : ethernetClientPacket) {
+            List<InternalPacket> ethernetClientPackets =
+                    learnRouteFromLeasequery ?
+                        processLQ6PacketFromClient(context, receivedPacket, receivingInterfaces, dhcp6Payload) :
+                        processDhcp6ForwardOnly(context, receivedPacket, receivingInterfaces, dhcp6Payload);
+            for (InternalPacket internalPacket : ethernetClientPackets) {
                 forwardPacket(internalPacket);
             }
-        } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value()) {
-
+        } else if (msgTypeVal == DHCP6.MsgType.LEASEQUERY_REPLY.value() && learnRouteFromLeasequery) {
             IPv6 clientIpv6 = (IPv6) receivedPacket.getPayload();
             UDP clientUdp = (UDP) clientIpv6.getPayload();
             DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
-            Interface serverInterface = dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
+            Interface serverInterface = Dhcp6HandlerUtil.directlyConnected(clientDhcp6) ?
                     getServerInterface() : getIndirectServerInterface();
             InternalPacket ethernetPacketReply =
-                    dhcp6HandlerUtil.processLQ6PacketFromServer(
+                    Dhcp6HandlerUtil.processLQ6PacketFromServer(
                             defaultServerInfoList, indirectServerInfoList,
                             serverInterface, interfaceService,
                             hostService,
@@ -510,24 +536,21 @@
                 forwardPacket(ethernetPacketReply);
             }
             handleLeaseQuery6ReplyMsg(context, dhcp6Payload);
-        } else {
-            if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
-
-                List<InternalPacket> ethernetClientPacket =
-                        processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
-                for (InternalPacket internalPacket : ethernetClientPacket) {
-                    forwardPacket(internalPacket);
-                }
-            } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
-                log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}", msgTypeVal);
-                InternalPacket ethernetPacketReply =
-                        processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
-                if (ethernetPacketReply != null) {
-                    forwardPacket(ethernetPacketReply);
-                }
-            } else {
-                log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
+        } else if (MSG_TYPE_FROM_CLIENT.contains(msgTypeVal)) {
+            List<InternalPacket> ethernetClientPacket =
+                    processDhcp6PacketFromClient(context, receivedPacket, receivingInterfaces);
+            for (InternalPacket internalPacket : ethernetClientPacket) {
+                forwardPacket(internalPacket);
             }
+        } else if (MSG_TYPE_FROM_SERVER.contains(msgTypeVal)) {
+            log.debug("calling processDhcp6PacketFromServer with RELAY_REPL {}, {}", receivedPacket, dhcp6Payload);
+            InternalPacket ethernetPacketReply =
+                    processDhcp6PacketFromServer(context, receivedPacket, receivingInterfaces);
+            if (ethernetPacketReply != null) {
+                forwardPacket(ethernetPacketReply);
+            }
+        } else {
+            log.warn("Not so fast, packet type {} not supported yet", msgTypeVal);
         }
     }
 
@@ -553,16 +576,21 @@
     //forward the packet to ConnectPoint where the DHCP server is attached.
     private void forwardPacket(InternalPacket packet) {
         //send Packetout to dhcp server connectpoint.
-        if (packet.destLocation != null) {
+        if (packet.getDestLocation() != null) {
             TrafficTreatment t = DefaultTrafficTreatment.builder()
-                    .setOutput(packet.destLocation.port()).build();
+                    .setOutput(packet.getDestLocation().port()).build();
             OutboundPacket o = new DefaultOutboundPacket(
-                    packet.destLocation.deviceId(), t, ByteBuffer.wrap(packet.packet.serialize()));
-            if (log.isTraceEnabled()) {
-                log.trace("Relaying packet to destination {}", packet.destLocation);
-            }
+                    packet.getDestLocation().deviceId(), t, ByteBuffer.wrap(packet.getPacket().serialize()));
             packetService.emit(o);
-        } // if
+            if (log.isTraceEnabled()) {
+                IPv6 ip6 = (IPv6) packet.getPacket().getPayload();
+                UDP udp = (UDP) ip6.getPayload();
+                DHCP6 dhcp6  = (DHCP6) udp.getPayload();
+                log.trace("Relaying packet to destination {} eth: {} dhcp: {}",
+                        packet.getDestLocation(), packet.getPacket(), dhcp6);
+            }
+
+        }
     }
 
     /**
@@ -683,7 +711,7 @@
                     .findFirst()
                     .orElse(null);
         } else {
-            DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
+            DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Payload);
             clientIdOption = leafDhcp.getOptions()
                     .stream()
                     .filter(opt -> opt instanceof Dhcp6ClientIdOption)
@@ -740,7 +768,7 @@
             record = record.clone();
         }
 
-        Boolean isMsgRelease = dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
+        Boolean isMsgRelease = Dhcp6HandlerUtil.isDhcp6Release(dhcp6Packet);
         IpAddressInfo ipInfo;
         PdPrefixInfo pdInfo = null;
         if (directConnFlag) {
@@ -767,7 +795,7 @@
                 return;
             }
 
-            DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
+            DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(dhcp6Packet);
             ipInfo = extractIpAddress(leafDhcp);
             if (ipInfo == null) {
                 log.debug("ip is null");
@@ -822,7 +850,7 @@
         }
 
         if (record != null) {
-            record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+            record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
             record.addLocation(new HostLocation(location, System.currentTimeMillis()));
             record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
             record.setDirectlyConnected(directConnFlag);
@@ -837,7 +865,7 @@
         try {
             recordSemaphore.acquire();
             try {
-                dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+                dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
             } finally {
                 // calling release() after a successful acquire()
                 recordSemaphore.release();
@@ -856,13 +884,12 @@
      * @param embeddedDhcp6 the dhcp6 payload within relay
      * @param srcMac client gw/host macAddress
      * @param clientInterface client interface
-     * @param dhcpServerIp6Address DHCP server IP
      */
     private void addHostOrRoute(boolean directConnFlag, ConnectPoint location, DHCP6 dhcp6Relay,
                                 DHCP6 embeddedDhcp6, MacAddress srcMac, Interface clientInterface) {
         log.debug("addHostOrRoute entered.");
         VlanId vlanId = clientInterface.vlan();
-        Boolean isMsgReply = dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
+        Boolean isMsgReply = Dhcp6HandlerUtil.isDhcp6Reply(dhcp6Relay);
         MacAddress leafClientMac;
         Byte leafMsgType;
 
@@ -931,7 +958,7 @@
                 return;
             }
 
-            DHCP6 leafDhcp = dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
+            DHCP6 leafDhcp = Dhcp6HandlerUtil.getDhcp6Leaf(embeddedDhcp6);
             ipInfo = extractIpAddress(leafDhcp);
             if (ipInfo == null) {
                 log.debug("ip is null");
@@ -988,7 +1015,7 @@
             }
         }
 
-        record.getV6Counters().incrementCounter(dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+        record.getV6Counters().incrementCounter(Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
         record.ip6Status(DHCP6.MsgType.getType(leafMsgType));
         record.setDirectlyConnected(directConnFlag);
         record.updateLastSeen();
@@ -997,7 +1024,7 @@
         try {
             recordSemaphore.acquire();
             try {
-                dhcpRelayCountersStore.incrementCounter(gCount, dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
+                dhcpRelayCountersStore.incrementCounter(gCount, Dhcp6HandlerUtil.getMsgTypeStr(leafMsgType));
             } finally {
                 // calling release() after a successful acquire()
                 recordSemaphore.release();
@@ -1007,6 +1034,28 @@
         }
     }
 
+    private List<InternalPacket> processDhcp6ForwardOnly(PacketContext context,
+                                                         Ethernet clientPacket,
+                                                         Set<Interface> clientInterfaces,
+                                                         DHCP6 dhcpPacket) {
+        ConnectPoint inPort = context.inPacket().receivedFrom();
+        log.trace("Got DHCPv6 on port {}", inPort);
+        boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcpPacket);
+
+        List<InternalPacket> internalPackets = new ArrayList<>();
+        List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
+
+        for (DhcpServerInfo dhcpServer : serverInfoList) {
+            Ethernet newPacket = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context,
+                    clientPacket, clientInterfaces, dhcpServer, getServerInterface(dhcpServer));
+            log.trace("Built packet for server {} : {}", dhcpServer, newPacket);
+            internalPackets.add(InternalPacket.internalPacket(newPacket,
+                    dhcpServer.getDhcpServerConnectPoint().get()));
+        }
+
+        return internalPackets;
+    }
+
     private List<InternalPacket> processLQ6PacketFromClient(PacketContext context,
                                                               Ethernet clientPacket,
                                                               Set<Interface> clientInterfaces,
@@ -1048,27 +1097,18 @@
         MacAddress potentialNH = packet.getSourceMAC();
         VlanId vlanId = VlanId.vlanId(packet.getVlanID());
         setPotentialNextHopForIp6InRelayStore(clientAddress, vlanId, potentialNH);
-
         // 3. route this LQ6 to all relevant servers
         IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
         UDP clientUdp = (UDP) clientIpv6.getPayload();
         DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
 
-        boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
-
-        ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
-        VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
-        Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
-                .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
-                .findFirst()
-                .orElse(null);
-
+        boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
         List<InternalPacket> internalPackets = new ArrayList<>();
         List<DhcpServerInfo> serverInfoList = findValidServerInfo(directConnFlag);
         List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
 
         for (DhcpServerInfo serverInfo : copyServerInfoList) {
-            if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
+            if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
                 log.warn("Can't get server connect point, ignore");
                 continue;
             }
@@ -1077,14 +1117,11 @@
                 log.warn("Can't get server interface with host info resolved, ignore");
                 continue;
             }
-
             Interface serverInterface = getServerInterface(newServerInfo);
             if (serverInterface == null) {
                 log.warn("Can't get server interface, ignore");
                 continue;
             }
-
-
             Ethernet etherRouted = (Ethernet) clientPacket.clone();
             MacAddress macFacingServer = serverInterface.mac();
             if (macFacingServer == null) {
@@ -1094,7 +1131,7 @@
             etherRouted.setSourceMACAddress(macFacingServer);
             etherRouted.setDestinationMACAddress(newServerInfo.getDhcpConnectMac().get());
             InternalPacket internalPacket =
-                    new Dhcp6HandlerUtil().new InternalPacket(etherRouted,
+                    InternalPacket.internalPacket(etherRouted,
                               serverInfo.getDhcpServerConnectPoint().get());
             internalPackets.add(internalPacket);
             log.debug("Sending LQ to DHCP server {}", newServerInfo.getDhcpServerIp6());
@@ -1115,8 +1152,7 @@
                                                               Ethernet clientPacket,
                                                               Set<Interface> clientInterfaces) {
         ConnectPoint receivedFrom = context.inPacket().receivedFrom();
-        DeviceId receivedFromDevice = receivedFrom.deviceId();
-        Ip6Address relayAgentIp = dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
+        Ip6Address relayAgentIp = Dhcp6HandlerUtil.getRelayAgentIPv6Address(clientInterfaces);
         MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
         if (relayAgentIp == null || relayAgentMac == null) {
             log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
@@ -1130,12 +1166,12 @@
         UDP clientUdp = (UDP) clientIpv6.getPayload();
         DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
 
-        boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(clientDhcp6);
+        boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(clientDhcp6);
 
         ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
         VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
         Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
-                .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
+                .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
                 .findFirst()
                 .orElse(null);
 
@@ -1144,7 +1180,7 @@
         List<DhcpServerInfo> copyServerInfoList = new ArrayList<DhcpServerInfo>(serverInfoList);
 
         for (DhcpServerInfo serverInfo : copyServerInfoList) {
-            if (!dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
+            if (!Dhcp6HandlerUtil.checkDhcpServerConnPt(directConnFlag, serverInfo)) {
                 log.warn("Can't get server connect point, ignore");
                 continue;
             }
@@ -1160,12 +1196,12 @@
                 continue;
             }
 
-            Ethernet etherReply = dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
+            Ethernet etherReply = Dhcp6HandlerUtil.buildDhcp6PacketFromClient(context, clientPacket,
                                                               clientInterfaces, newServerInfo, serverInterface);
             removeHostOrRoute(directConnFlag, clientConnectionPoint, clientDhcp6, clientPacket,
                     clientIpv6, clientInterface);
 
-            InternalPacket internalPacket = new Dhcp6HandlerUtil().new InternalPacket(etherReply,
+            InternalPacket internalPacket = InternalPacket.internalPacket(etherReply,
                     serverInfo.getDhcpServerConnectPoint().get());
             internalPackets.add(internalPacket);
         }
@@ -1189,7 +1225,7 @@
         IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
         UDP udpPacket = (UDP) ipv6Packet.getPayload();
         DHCP6 dhcp6Relay = (DHCP6) udpPacket.getPayload();
-        Boolean directConnFlag = dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
+        Boolean directConnFlag = Dhcp6HandlerUtil.directlyConnected(dhcp6Relay);
 
         DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
                 .filter(opt -> opt instanceof Dhcp6RelayOption)
@@ -1205,7 +1241,7 @@
             dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_INFO);
             return null;
         } else {
-            if (dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
+            if (Dhcp6HandlerUtil.isServerIpEmpty(foundServerInfo)) {
                 log.warn("Cannot find server info's ipaddress");
                 dhcpRelayCountersStore.incrementCounter(gCount, DhcpRelayCounters.NO_SERVER_IP6ADDR);
                 return null;
@@ -1228,7 +1264,7 @@
         ConnectPoint clientConnectionPoint = ConnectPoint.deviceConnectPoint(clientConnectionPointStr);
         VlanId vlanIdInUse = VlanId.vlanId(interfaceIdOption.getVlanId());
         Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
-                .stream().filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
+                .stream().filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, vlanIdInUse))
                 .findFirst().orElse(null);
         if (clientInterface == null) {
             log.warn("Cannot get client interface for from packet, abort... vlan {}", vlanIdInUse.toString());
@@ -1274,30 +1310,23 @@
         } else {
             udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
         }
-        // add host or route
-        addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
-                       clientMac, clientInterface);
 
 
+        boolean hostOrRouteAllowed = learnRouteFromLeasequery ||
+                    Dhcp6HandlerUtil.getDhcp6LeafMessageType(dhcp6Relay) != MsgType.LEASEQUERY_REPLY;
+        log.debug("Can add host or route: {}", hostOrRouteAllowed);
+
+        if (hostOrRouteAllowed) {
+            // add host or route
+            addHostOrRoute(directConnFlag, clientConnectionPoint, dhcp6Relay, embeddedDhcp6,
+                    clientMac, clientInterface);
+        }
+
         udpPacket.setPayload(embeddedDhcp6);
         udpPacket.resetChecksum();
         ipv6Packet.setPayload(udpPacket);
         etherReply.setPayload(ipv6Packet);
-        return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
-    }
-
-    // Returns the first v6 interface ip out of a set of interfaces or null.
-    // Checks all interfaces, and ignores v6 interface ips
-    private Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
-        for (Interface intf : intfs) {
-            for (InterfaceIpAddress ip : intf.ipAddressesList()) {
-                Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
-                if (relayAgentIp != null) {
-                    return relayAgentIp;
-                }
-            }
-        }
-        return null;
+        return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
     }
 
     @Override
@@ -1476,23 +1505,6 @@
         }
     }
 
-    /**
-     * Returns the first interface ip from interface.
-     *
-     * @param iface interface of one connect point
-     * @return the first interface IP; null if not exists an IP address in
-     *         these interfaces
-     */
-    private Ip6Address getFirstIpFromInterface(Interface iface) {
-        checkNotNull(iface, "Interface can't be null");
-        return iface.ipAddressesList().stream()
-                .map(InterfaceIpAddress::ipAddress)
-                .filter(IpAddress::isIp6)
-                .map(IpAddress::getIp6Address)
-                .findFirst()
-                .orElse(null);
-    }
-
    /**
      * Gets Interface facing to the server for default host.
      *
@@ -1516,7 +1528,7 @@
         }
         return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
                 .stream()
-                .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
+                .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
                 .findFirst()
                 .orElse(null);
     }
@@ -1546,7 +1558,7 @@
         }
         return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
                 .stream()
-                .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
+                .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, indirectDhcpConnectVlan))
                 .findFirst()
                 .orElse(null);
     }
@@ -1615,7 +1627,7 @@
         if (dhcpServerConnectPoint != null && dhcpConnectVlan != null) {
         serverInterface = interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
                     .stream()
-                    .filter(iface -> dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
+                    .filter(iface -> Dhcp6HandlerUtil.interfaceContainsVlan(iface, dhcpConnectVlan))
                     .findFirst()
                     .orElse(null);
         } else {
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
index dac5aac..d059158 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerUtil.java
@@ -52,14 +52,16 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+public final class Dhcp6HandlerUtil {
 
+    private static final Logger log = LoggerFactory.getLogger(Dhcp6HandlerUtil.class);
 
-public class Dhcp6HandlerUtil {
+    private Dhcp6HandlerUtil() {
+    }
 
-    private final Logger log = LoggerFactory.getLogger(getClass());
     // Returns the first v6 interface ip out of a set of interfaces or null.
     // Checks all interfaces, and ignores v6 interface ips
-    public Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
+    public static Ip6Address getRelayAgentIPv6Address(Set<Interface> intfs) {
         for (Interface intf : intfs) {
             for (InterfaceIpAddress ip : intf.ipAddressesList()) {
                 Ip6Address relayAgentIp = ip.ipAddress().getIp6Address();
@@ -72,6 +74,22 @@
     }
 
     /**
+     * Returns the first interface ip from interface.
+     *
+     * @param iface interface of one connect point
+     * @return the first interface IP; null if not exists an IP address in
+     *         these interfaces
+     */
+    private static Ip6Address getFirstIpFromInterface(Interface iface) {
+        checkNotNull(iface, "Interface can't be null");
+        return iface.ipAddressesList().stream()
+                .map(InterfaceIpAddress::ipAddress)
+                .filter(IpAddress::isIp6)
+                .map(IpAddress::getIp6Address)
+                .findFirst()
+                .orElse(null);
+    }
+    /**
      *
      * process the LQ reply packet from dhcp server.
      *
@@ -85,7 +103,7 @@
      * @param recevingInterfaces set of server side interfaces
      * @return a packet ready to be sent to relevant output interface
      */
-    public InternalPacket processLQ6PacketFromServer(
+    public static InternalPacket processLQ6PacketFromServer(
             List<DhcpServerInfo> defaultServerInfoList,
             List<DhcpServerInfo> indirectServerInfoList,
             Interface serverInterface,
@@ -141,14 +159,14 @@
         if ((directConnFlag || indirectDhcpServerIp == null)
                 && !inPort.equals(dhcpServerConnectPoint)) {
             log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
-                     inPort, dhcpServerConnectPoint);
+                    inPort, dhcpServerConnectPoint);
             return null;
         }
 
         if (!directConnFlag && indirectDhcpServerIp != null &&
                 !inPort.equals(indirectDhcpServerConnectPoint)) {
             log.warn("Receiving port {} is not the same as server connect point {} for indirect",
-                     inPort, indirectDhcpServerConnectPoint);
+                    inPort, indirectDhcpServerConnectPoint);
             return null;
         }
 
@@ -180,9 +198,6 @@
         etherReply.setSourceMACAddress(iface.mac());
         etherReply.setDestinationMACAddress(host.mac());
 
-
-        // add host or route
-        //addHostOrRoute(directConnFlag, clientConnectionPoint, lq6Reply, embeddedDhcp6, clientMac, clientInterface);
         // workaround for a bug where core sends src port as 547 (server)
         udpPacket.setDestinationPort(UDP.DHCP_V6_SERVER_PORT);
         udpPacket.setPayload(lq6Reply);
@@ -190,23 +205,7 @@
         ipv6Packet.setPayload(udpPacket);
         etherReply.setPayload(ipv6Packet);
 
-        return new Dhcp6HandlerUtil().new InternalPacket(etherReply, clientConnectionPoint);
-    }
-    /**
-     * Returns the first interface ip from interface.
-     *
-     * @param iface interface of one connect point
-     * @return the first interface IP; null if not exists an IP address in
-     *         these interfaces
-     */
-    public Ip6Address getFirstIpFromInterface(Interface iface) {
-        checkNotNull(iface, "Interface can't be null");
-        return iface.ipAddressesList().stream()
-                .map(InterfaceIpAddress::ipAddress)
-                .filter(IpAddress::isIp6)
-                .map(IpAddress::getIp6Address)
-                .findFirst()
-                .orElse(null);
+        return InternalPacket.internalPacket(etherReply, clientConnectionPoint);
     }
 
     /**
@@ -215,7 +214,7 @@
      * @param dhcp6 dhcp6 relay-reply or relay-foward
      * @return dhcp6Packet dhcp6 packet extracted from relay-message
      */
-    public DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
+    public static DHCP6 dhcp6PacketFromRelayPacket(DHCP6 dhcp6) {
 
         // extract the relay message if exist
         DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
@@ -239,7 +238,7 @@
      * @param relayPacket dhcp6 relay packet
      * @return leafPacket non-relay dhcp6 packet
      */
-    public DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
+    public static DHCP6 getDhcp6Leaf(DHCP6 relayPacket) {
         DHCP6 dhcp6Parent = relayPacket;
         DHCP6 dhcp6Child = null;
 
@@ -264,12 +263,24 @@
     }
 
     /**
+     * Determine DHCP message type (direct DHCPv6 or wrapped into relay messages).
+     *
+     * @param relayPacket {@link DHCP6} packet to be parsed
+     * @return {@link DHCP6.MsgType} contained message type of dhcpv6 packet/relay-message
+     */
+    public static DHCP6.MsgType getDhcp6LeafMessageType(DHCP6 relayPacket) {
+        checkNotNull(relayPacket);
+        DHCP6 dhcp6Child = getDhcp6Leaf(relayPacket);
+        return DHCP6.MsgType.getType(dhcp6Child != null ? dhcp6Child.getMsgType() : relayPacket.getMsgType());
+    }
+
+    /**
      * check if DHCP6 relay-reply is reply.
      *
      * @param relayPacket dhcp6 relay-reply
      * @return boolean relay-reply contains ack
      */
-    public boolean isDhcp6Reply(DHCP6 relayPacket) {
+    public static boolean isDhcp6Reply(DHCP6 relayPacket) {
         DHCP6 leafDhcp6 = getDhcp6Leaf(relayPacket);
         if (leafDhcp6 != null) {
             if (leafDhcp6.getMsgType() == DHCP6.MsgType.REPLY.value()) {
@@ -291,7 +302,7 @@
      * @param dhcp6Payload dhcp6 packet
      * @return boolean dhcp6 contains release
      */
-    public boolean isDhcp6Release(DHCP6 dhcp6Payload) {
+    public static  boolean isDhcp6Release(DHCP6 dhcp6Payload) {
         if (dhcp6Payload.getMsgType() ==  DHCP6.MsgType.RELEASE.value()) {
             log.debug("isDhcp6Release  true.");
             return true;  // must be directly connected
@@ -319,7 +330,7 @@
      * @param msgTypeVal msgType byte of dhcp6 packet
      * @return String string value of dhcp6 msg type
      */
-    public String getMsgTypeStr(byte msgTypeVal) {
+    public static String getMsgTypeStr(byte msgTypeVal) {
         MsgType msgType = DHCP6.MsgType.getType(msgTypeVal);
         return DHCP6.MsgType.getMsgTypeStr(msgType);
     }
@@ -331,7 +342,7 @@
      * @param dhcp6Packet dhcp6 packet
      * @return String string value of dhcp6 leaf packet msg type
      */
-    public String findLeafMsgType(boolean directConnFlag, DHCP6  dhcp6Packet) {
+    public static String findLeafMsgType(boolean directConnFlag, DHCP6  dhcp6Packet) {
         if (directConnFlag) {
             return getMsgTypeStr(dhcp6Packet.getMsgType());
         } else {
@@ -351,7 +362,7 @@
      * @param vlanId the vlan id
      * @return true if the Interface contains the vlan id
      */
-    public boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
+    public static boolean interfaceContainsVlan(Interface iface, VlanId vlanId) {
         if (vlanId.equals(VlanId.NONE)) {
             // untagged packet, check if vlan untagged or vlan native is not NONE
             return !iface.vlanUntagged().equals(VlanId.NONE) ||
@@ -362,26 +373,12 @@
     }
 
     /**
-     * the new class the contains Ethernet packet and destination port.
-     */
-    public class InternalPacket {
-        Ethernet packet;
-        ConnectPoint destLocation;
-        public InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
-            packet = newPacket;
-            destLocation = newLocation;
-        }
-        void setLocation(ConnectPoint newLocation) {
-            destLocation = newLocation;
-        }
-    }
-    /**
      * Check if the host is directly connected to the network or not.
      *
      * @param dhcp6Payload the dhcp6 payload
      * @return true if the host is directly connected to the network; false otherwise
      */
-    public boolean directlyConnected(DHCP6 dhcp6Payload) {
+    public static boolean directlyConnected(DHCP6 dhcp6Payload) {
 
         log.debug("directlyConnected enters");
         if (dhcp6Payload.getMsgType() == DHCP6.MsgType.LEASEQUERY.value() ||
@@ -405,7 +402,8 @@
                 return false;
             } else {
                 // relay-reply
-                if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()) {
+                if (dhcp6Payload2.getMsgType() != DHCP6.MsgType.RELAY_REPL.value()
+                        && dhcp6Payload2.getMsgType() != MsgType.LEASEQUERY_REPLY.value()) {
                     log.debug("directlyConnected  true. 2nd MsgType {}", dhcp6Payload2.getMsgType());
                     return true;  // must be directly connected
                 } else {
@@ -425,7 +423,7 @@
      * @param serverInfo server info to check
      * @return true if server info has v6 ip address; false otherwise
      */
-    public boolean isServerIpEmpty(DhcpServerInfo serverInfo) {
+    public static boolean isServerIpEmpty(DhcpServerInfo serverInfo) {
         if (!serverInfo.getDhcpServerIp6().isPresent()) {
             log.warn("DhcpServerIp not available, use default DhcpServerIp {}",
                     HexString.toHexString(serverInfo.getDhcpServerIp6().get().toOctets()));
@@ -434,7 +432,7 @@
         return false;
     }
 
-    private boolean isConnectMacEmpty(DhcpServerInfo serverInfo, Set<Interface> clientInterfaces) {
+    private static boolean isConnectMacEmpty(DhcpServerInfo serverInfo, Set<Interface> clientInterfaces) {
         if (!serverInfo.getDhcpConnectMac().isPresent()) {
             log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
                             + "packet processing from client on port: {}",
@@ -446,7 +444,7 @@
         return false;
     }
 
-    private boolean isRelayAgentIpFromCfgEmpty(DhcpServerInfo serverInfo, DeviceId receivedFromDevice) {
+    private static boolean isRelayAgentIpFromCfgEmpty(DhcpServerInfo serverInfo, DeviceId receivedFromDevice) {
         if (!serverInfo.getRelayAgentIp6(receivedFromDevice).isPresent()) {
             log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic.");
             return true;
@@ -454,7 +452,7 @@
         return false;
     }
 
-    private Dhcp6Option getInterfaceIdIdOption(PacketContext context, Ethernet clientPacket) {
+    private static Dhcp6Option getInterfaceIdIdOption(PacketContext context, Ethernet clientPacket) {
         String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
         Dhcp6Option interfaceId = new Dhcp6Option();
         interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
@@ -482,7 +480,7 @@
         return interfaceId;
     }
 
-    private void addDhcp6OptionsFromClient(List<Dhcp6Option> options, byte[] dhcp6PacketByte,
+    private static void addDhcp6OptionsFromClient(List<Dhcp6Option> options, byte[] dhcp6PacketByte,
                                            PacketContext context, Ethernet clientPacket) {
         Dhcp6Option relayMessage = new Dhcp6Option();
         relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
@@ -504,7 +502,7 @@
      * @param serverInterface target server interface
      * @return ethernet packet with dhcp6 packet info
      */
-    public Ethernet buildDhcp6PacketFromClient(PacketContext context, Ethernet clientPacket,
+    public static Ethernet buildDhcp6PacketFromClient(PacketContext context, Ethernet clientPacket,
                                                Set<Interface> clientInterfaces, DhcpServerInfo serverInfo,
                                                Interface serverInterface) {
         ConnectPoint receivedFrom = context.inPacket().receivedFrom();
@@ -604,7 +602,7 @@
      * @param serverInfo server to check its connect point
      * @return boolean true if serverInfo is found; false otherwise
      */
-    public boolean checkDhcpServerConnPt(boolean directConnFlag,
+    public static boolean checkDhcpServerConnPt(boolean directConnFlag,
                                           DhcpServerInfo serverInfo) {
         if (serverInfo.getDhcpServerConnectPoint() == null) {
             log.warn("DHCP6 server connect point for {} connPt {}",
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 f2c1b87..f2ca330 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
@@ -56,15 +56,15 @@
 import org.onosproject.dhcprelay.api.DhcpRelayService;
 import org.onosproject.dhcprelay.api.DhcpServerInfo;
 import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
-import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
-import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
+import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.dhcprelay.config.EnableDhcpFpmConfig;
+import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
+import org.onosproject.dhcprelay.config.IgnoreDhcpConfig;
 import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
 import org.onosproject.dhcprelay.store.DhcpFpmPrefixStore;
 import org.onosproject.routing.fpm.api.FpmRecord;
 import org.onosproject.net.Device;
-import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.net.Host;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.device.DeviceEvent;
@@ -206,7 +206,6 @@
             label = "Enable DhcpRelay Fpm")
     protected boolean dhcpFpmEnabled = false;
 
-
     private ScheduledExecutorService timerExecutor;
 
     protected DeviceListener deviceListener = new InternalDeviceListener();
@@ -321,7 +320,6 @@
             }
             v6Handler.setDhcpFpmEnabled(dhcpFpmEnabled);
         }
-
     }
 
     private static List<TrafficSelector> buildClientDhcpSelectors() {
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/InternalPacket.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/InternalPacket.java
new file mode 100644
index 0000000..70d4ae2
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/InternalPacket.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.onosproject.dhcprelay;
+
+import org.onlab.packet.Ethernet;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Container for Ethernet packet and destination port.
+ */
+final class InternalPacket {
+    private Ethernet packet;
+    private ConnectPoint destLocation;
+
+    private InternalPacket(Ethernet newPacket, ConnectPoint newLocation) {
+        packet = newPacket;
+        destLocation = newLocation;
+    }
+
+    public Ethernet getPacket() {
+        return packet;
+    }
+
+    public ConnectPoint getDestLocation() {
+        return destLocation;
+    }
+
+    /**
+     * Build {@link InternalPacket} object instance.
+     *
+     * @param newPacket {@link Ethernet} packet to be sent
+     * @param newLocation {@link ConnectPoint} packet destination
+     * @return new instance of {@link InternalPacket} class
+     */
+    public static InternalPacket internalPacket(Ethernet newPacket, ConnectPoint newLocation) {
+        return new InternalPacket(newPacket, newLocation);
+    }
+}
diff --git a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6LeaseQueryOption.java b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6LeaseQueryOption.java
index 5bcc8ba..baa2e6e 100644
--- a/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6LeaseQueryOption.java
+++ b/utils/misc/src/main/java/org/onlab/packet/dhcp/Dhcp6LeaseQueryOption.java
@@ -36,9 +36,11 @@
     //public short QueryType;
     public Ip6Address linkAddress;
     private List<Dhcp6Option> options;
+    public byte queryType;
 
     public Dhcp6LeaseQueryOption(Dhcp6Option dhcp6Option) {
         super(dhcp6Option);
+        options =  Lists.newArrayList();
     }
 
     @Override
@@ -66,10 +68,10 @@
             Dhcp6LeaseQueryOption lQ6Option = new Dhcp6LeaseQueryOption(dhcp6Option);
 
             byte[] optionData = lQ6Option.getData();
-            if (optionData.length >= 61) { // 61 is LQ option length + 4 header
+            if (optionData.length >= DEFAULT_LEN) {
                 ByteBuffer bb = ByteBuffer.wrap(optionData);
                 // fetch the Query type - just pop the byte from the byte buffer for subsequent parsing...
-                bb.get();
+                lQ6Option.queryType = bb.get();
                 byte[] ipv6Addr = new byte[16];
                 bb.get(ipv6Addr);
                 lQ6Option.linkAddress = Ip6Address.valueOf(ipv6Addr);
@@ -108,10 +110,13 @@
 
     @Override
     public byte[] serialize() {
-        ByteBuffer bb = ByteBuffer.allocate(this.getLength() + Dhcp6Option.DEFAULT_LEN);
+        byte[] serializedPayload = payload.serialize();
+
+        ByteBuffer bb = ByteBuffer.allocate(serializedPayload.length + Dhcp6Option.DEFAULT_LEN);
         bb.putShort(getCode());
         bb.putShort(getLength());
-        bb.put(payload.serialize());
+        bb.put(serializedPayload);
+
         return bb.array();
     }