[CORD-1628] DHCP relay app configuration change for multiple servers

Change-Id: I13747d8e6451658dfd9344c428b6aad11bf5af62
(cherry picked from commit 483ac6f0689220425061056e748230efe6d8a1b2)
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 0305cd8..deeaa36 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -17,8 +17,11 @@
 
 package org.onosproject.dhcprelay;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.collect.Sets;
+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.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -36,8 +39,11 @@
 import org.onlab.packet.dhcp.DhcpOption;
 import org.onlab.packet.dhcp.DhcpRelayAgentOption;
 import org.onosproject.dhcprelay.api.DhcpHandler;
+import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceService;
 import org.onosproject.routeservice.Route;
@@ -100,6 +106,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
+    private InternalHostListener hostListener = new InternalHostListener();
+
     private Ip4Address dhcpServerIp = null;
     // dhcp server may be connected directly to the SDN network or
     // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
@@ -110,6 +118,18 @@
     private VlanId dhcpConnectVlan = null;
     private Ip4Address dhcpGatewayIp = null;
 
+    @Activate
+    protected void activate() {
+        hostService.addListener(hostListener);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        hostService.removeListener(hostListener);
+        this.dhcpConnectMac = null;
+        this.dhcpConnectVlan = null;
+    }
+
     @Override
     public void setDhcpServerIp(IpAddress dhcpServerIp) {
         checkNotNull(dhcpServerIp, "DHCP server IP can't be null");
@@ -160,6 +180,64 @@
     }
 
     @Override
+    public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
+        if (configs.size() == 0) {
+            // no config to update
+            return;
+        }
+
+        // TODO: currently we pick up first DHCP server config.
+        // Will use other server configs in the future for HA.
+        DhcpServerConfig serverConfig = configs.iterator().next();
+        checkState(serverConfig.getDhcpServerConnectPoint().isPresent(),
+                   "Connect point not exists");
+        checkState(serverConfig.getDhcpServerIp4().isPresent(),
+                   "IP of DHCP server not exists");
+        Ip4Address oldServerIp = this.dhcpServerIp;
+        Ip4Address oldGatewayIp = this.dhcpGatewayIp;
+
+        // stop monitoring gateway or server
+        if (oldGatewayIp != null) {
+            hostService.stopMonitoringIp(oldGatewayIp);
+        } else if (oldServerIp != null) {
+            hostService.stopMonitoringIp(oldServerIp);
+        }
+
+        this.dhcpServerConnectPoint = serverConfig.getDhcpServerConnectPoint().get();
+        this.dhcpServerIp = serverConfig.getDhcpServerIp4().get();
+        this.dhcpGatewayIp = serverConfig.getDhcpGatewayIp4().orElse(null);
+
+        // reset server mac and vlan
+        this.dhcpConnectMac = null;
+        this.dhcpConnectVlan = null;
+
+        log.info("DHCP server connect point: " + this.dhcpServerConnectPoint);
+        log.info("DHCP server IP: " + this.dhcpServerIp);
+
+        IpAddress ipToProbe = MoreObjects.firstNonNull(this.dhcpGatewayIp, this.dhcpServerIp);
+        String hostToProbe = this.dhcpGatewayIp != null ? "gateway" : "DHCP server";
+
+        if (ipToProbe == null) {
+            log.warn("Server IP not set, can't probe it");
+            return;
+        }
+
+        log.info("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
+        hostService.startMonitoringIp(ipToProbe);
+
+        Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
+        if (!hosts.isEmpty()) {
+            Host host = hosts.iterator().next();
+            this.dhcpConnectVlan = host.vlan();
+            this.dhcpConnectMac = host.mac();
+        }
+    }
+
+    @Override
+    public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
+        log.warn("Indirect config feature for DHCPv4 handler not implement yet");
+    }
+
     public void processDhcpPacket(PacketContext context, BasePacket payload) {
         checkNotNull(payload, "DHCP payload can't be null");
         checkState(payload instanceof DHCP, "Payload is not a DHCP");
@@ -813,4 +891,96 @@
         }
         packetService.emit(o);
     }
+
+    class InternalHostListener implements HostListener {
+        @Override
+        public void event(HostEvent event) {
+            switch (event.type()) {
+                case HOST_ADDED:
+                case HOST_UPDATED:
+                    hostUpdated(event.subject());
+                    break;
+                case HOST_REMOVED:
+                    hostRemoved(event.subject());
+                    break;
+                case HOST_MOVED:
+                    hostMoved(event.subject());
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * 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) {
+        if (this.dhcpServerConnectPoint == null) {
+            return;
+        }
+        if (this.dhcpGatewayIp != null) {
+            if (host.ipAddresses().contains(this.dhcpGatewayIp) &&
+                    !host.locations().contains(this.dhcpServerConnectPoint)) {
+                this.dhcpConnectMac = null;
+                this.dhcpConnectVlan = null;
+            }
+            return;
+        }
+        if (this.dhcpServerIp != null) {
+            if (host.ipAddresses().contains(this.dhcpServerIp) &&
+                    !host.locations().contains(this.dhcpServerConnectPoint)) {
+                this.dhcpConnectMac = null;
+                this.dhcpConnectVlan = 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) {
+        if (this.dhcpGatewayIp != null) {
+            if (host.ipAddresses().contains(this.dhcpGatewayIp)) {
+                this.dhcpConnectMac = host.mac();
+                this.dhcpConnectVlan = host.vlan();
+            }
+            return;
+        }
+        if (this.dhcpServerIp != null) {
+            if (host.ipAddresses().contains(this.dhcpServerIp)) {
+                this.dhcpConnectMac = host.mac();
+                this.dhcpConnectVlan = host.vlan();
+            }
+        }
+    }
+
+    /**
+     * Handle host removed.
+     * If the host is DHCP server or gateway, unset connect mac and vlan.
+     *
+     * @param host the host
+     */
+    private void hostRemoved(Host host) {
+        if (this.dhcpGatewayIp != null) {
+            if (host.ipAddresses().contains(this.dhcpGatewayIp)) {
+                this.dhcpConnectMac = null;
+                this.dhcpConnectVlan = null;
+            }
+            return;
+        }
+        if (this.dhcpServerIp != null) {
+            if (host.ipAddresses().contains(this.dhcpServerIp)) {
+                this.dhcpConnectMac = null;
+                this.dhcpConnectVlan = null;
+            }
+        }
+    }
 }
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 f5375bf..19123dc 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -25,9 +25,11 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.dhcprelay.api.DhcpHandler;
+import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.packet.PacketContext;
 
+import java.util.Collection;
 import java.util.Optional;
 
 @Component
@@ -79,4 +81,14 @@
     public void setDhcpServerIp(IpAddress dhcpServerIp) {
 
     }
+
+    @Override
+    public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
+
+    }
+
+    @Override
+    public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
+
+    }
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
deleted file mode 100644
index 0ed8411..0000000
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayConfig.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2016-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.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.config.Config;
-
-import static org.onosproject.net.config.Config.FieldPresence.MANDATORY;
-import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL;
-import org.onlab.packet.Ip4Address;
-/**
- * DHCP Relay Config class.
- */
-public class DhcpRelayConfig extends Config<ApplicationId> {
-
-    private static final String DHCP_CONNECT_POINT = "dhcpserverConnectPoint";
-    private static final String DHCP_SERVER_IP = "serverip";
-    private static final String DHCP_GATEWAY_IP = "gatewayip";
-
-    @Override
-    public boolean isValid() {
-        return hasOnlyFields(DHCP_CONNECT_POINT, DHCP_SERVER_IP, DHCP_GATEWAY_IP) &&
-                isConnectPoint(DHCP_CONNECT_POINT, MANDATORY) &&
-                isIpAddress(DHCP_SERVER_IP, MANDATORY) &&
-                isIpAddress(DHCP_GATEWAY_IP, OPTIONAL);
-    }
-
-    /**
-     * Returns the dhcp server connect point.
-     *
-     * @return dhcp server connect point
-     */
-    public ConnectPoint getDhcpServerConnectPoint() {
-        return ConnectPoint.deviceConnectPoint(object.path(DHCP_CONNECT_POINT).asText());
-    }
-
-    /**
-     * Returns the dhcp server ip.
-     *
-     * @return ip address or null if not set
-     */
-    public Ip4Address getDhcpServerIp() {
-        String ip = get(DHCP_SERVER_IP, null);
-        return ip != null ? Ip4Address.valueOf(ip) : null;
-    }
-
-    /**
-     * Returns the optional dhcp gateway ip, if configured. This option is
-     * typically used if the dhcp server is not directly attached to a switch;
-     * For example, the dhcp server may be reached via an external gateway connected
-     * to the dhcpserverConnectPoint.
-     *
-     * @return gateway ip or null if not set
-     */
-    public Ip4Address getDhcpGatewayIp() {
-        String gip = get(DHCP_GATEWAY_IP, null);
-        return gip != null ? Ip4Address.valueOf(gip) : null;
-    }
-}
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 afab19a..ba19fa7 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/DhcpRelayManager.java
@@ -38,7 +38,6 @@
 import org.onlab.packet.IPv6;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
-import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.TpPort;
 import org.onlab.packet.UDP;
@@ -49,12 +48,14 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.dhcprelay.api.DhcpHandler;
 import org.onosproject.dhcprelay.api.DhcpRelayService;
+import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
+import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
 import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
+import org.onosproject.net.config.Config;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
@@ -64,8 +65,6 @@
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
@@ -87,7 +86,7 @@
 @Component(immediate = true)
 @Service
 public class DhcpRelayManager implements DhcpRelayService {
-    public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
+    public static final String DHCP_RELAY_APP = "org.onosproject.dhcprelay";
     public static final ProviderId PROVIDER_ID = new ProviderId("host", DHCP_RELAY_APP);
     public static final String HOST_LOCATION_PROVIDER =
             "org.onosproject.provider.host.impl.HostLocationProvider";
@@ -95,12 +94,22 @@
     private final InternalConfigListener cfgListener = new InternalConfigListener();
 
     private final Set<ConfigFactory> factories = ImmutableSet.of(
-            new ConfigFactory<ApplicationId, DhcpRelayConfig>(APP_SUBJECT_FACTORY,
-                    DhcpRelayConfig.class,
-                    "dhcprelay") {
+            new ConfigFactory<ApplicationId, DefaultDhcpRelayConfig>(APP_SUBJECT_FACTORY,
+                                                                     DefaultDhcpRelayConfig.class,
+                                                                     DefaultDhcpRelayConfig.KEY,
+                                                                     true) {
                 @Override
-                public DhcpRelayConfig createConfig() {
-                    return new DhcpRelayConfig();
+                public DefaultDhcpRelayConfig createConfig() {
+                    return new DefaultDhcpRelayConfig();
+                }
+            },
+            new ConfigFactory<ApplicationId, IndirectDhcpRelayConfig>(APP_SUBJECT_FACTORY,
+                                                                      IndirectDhcpRelayConfig.class,
+                                                                      IndirectDhcpRelayConfig.KEY,
+                                                                      true) {
+                @Override
+                public IndirectDhcpRelayConfig createConfig() {
+                    return new IndirectDhcpRelayConfig();
                 }
             }
     );
@@ -139,7 +148,6 @@
     protected boolean arpEnabled = true;
 
     private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
-    private InternalHostListener hostListener = new InternalHostListener();
     private ApplicationId appId;
 
     @Activate
@@ -156,7 +164,6 @@
         packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
 
         // listen host event for dhcp server or the gateway
-        hostService.addListener(hostListener);
         requestDhcpPackets();
         modified(context);
 
@@ -172,7 +179,6 @@
         cfgService.removeListener(cfgListener);
         factories.forEach(cfgService::unregisterConfigFactory);
         packetService.removeProcessor(dhcpRelayPacketProcessor);
-        hostService.removeListener(hostListener);
         cancelDhcpPackets();
         cancelArpPackets();
         v4Handler.getDhcpGatewayIp().ifPresent(hostService::stopMonitoringIp);
@@ -202,74 +208,44 @@
         }
     }
 
+    /**
+     * Updates DHCP relay app configuration.
+     */
     private void updateConfig() {
-        DhcpRelayConfig cfg = cfgService.getConfig(appId, DhcpRelayConfig.class);
-        if (cfg == null) {
-            log.warn("Dhcp Server info not available");
+        DefaultDhcpRelayConfig defaultConfig =
+                cfgService.getConfig(appId, DefaultDhcpRelayConfig.class);
+        IndirectDhcpRelayConfig indirectConfig =
+                cfgService.getConfig(appId, IndirectDhcpRelayConfig.class);
+
+        if (defaultConfig != null) {
+            updateConfig(defaultConfig);
+        }
+
+        if (indirectConfig != null) {
+            updateConfig(indirectConfig);
+        }
+    }
+
+    /**
+     * Updates DHCP relay app configuration with given configuration.
+     *
+     * @param config the configuration ot update
+     */
+    private void updateConfig(Config config) {
+        if (config == null) {
+            // Ignore if config is not present
             return;
         }
-        Optional<IpAddress> oldDhcpServerIp = v4Handler.getDhcpServerIp();
-        Optional<IpAddress> oldDhcpGatewayIp = v4Handler.getDhcpGatewayIp();
-        v4Handler.setDhcpServerConnectPoint(cfg.getDhcpServerConnectPoint());
-        v4Handler.setDhcpServerIp(cfg.getDhcpServerIp());
-        v4Handler.setDhcpGatewayIp(cfg.getDhcpGatewayIp());
-        v4Handler.setDhcpConnectMac(null);
-        v4Handler.setDhcpConnectVlan(null);
-
-        log.info("DHCP server connect point: " + cfg.getDhcpServerConnectPoint());
-        log.info("DHCP server ipaddress " + cfg.getDhcpServerIp());
-
-        IpAddress ipToProbe = v4Handler.getDhcpGatewayIp().isPresent() ? cfg.getDhcpGatewayIp() :
-                                                                         cfg.getDhcpServerIp();
-        String hostToProbe = v4Handler.getDhcpGatewayIp().isPresent() ? "gateway" : "DHCP server";
-
-        // TODO: DHCPv6 server config
-        Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
-        if (hosts.isEmpty()) {
-            log.info("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
-            oldDhcpGatewayIp.ifPresent(hostService::stopMonitoringIp);
-            oldDhcpServerIp.ifPresent(hostService::stopMonitoringIp);
-            hostService.startMonitoringIp(ipToProbe);
-        } else {
-            // Probe target is known; There should be only 1 host with this ip
-            hostUpdated(hosts.iterator().next());
+        if (config instanceof DefaultDhcpRelayConfig) {
+            DefaultDhcpRelayConfig defaultConfig = (DefaultDhcpRelayConfig) config;
+            v4Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
+            v6Handler.setDefaultDhcpServerConfigs(defaultConfig.dhcpServerConfigs());
         }
-    }
-
-    private void hostRemoved(Host host) {
-        v4Handler.getDhcpServerIp().ifPresent(ip -> {
-            if (host.ipAddresses().contains(ip)) {
-                log.warn("DHCP server {} removed", ip);
-                v4Handler.setDhcpConnectMac(null);
-                v4Handler.setDhcpConnectVlan(null);
-            }
-        });
-        v4Handler.getDhcpGatewayIp().ifPresent(ip -> {
-            if (host.ipAddresses().contains(ip)) {
-                log.warn("DHCP gateway {} removed", ip);
-                v4Handler.setDhcpConnectMac(null);
-                v4Handler.setDhcpConnectVlan(null);
-            }
-        });
-        // TODO: v6 handler
-    }
-
-    private void hostUpdated(Host host) {
-        v4Handler.getDhcpGatewayIp().ifPresent(ip -> {
-            if (host.ipAddresses().contains(ip)) {
-                log.warn("DHCP gateway {} removed", ip);
-                v4Handler.setDhcpConnectMac(host.mac());
-                v4Handler.setDhcpConnectVlan(host.vlan());
-            }
-        });
-        v4Handler.getDhcpServerIp().ifPresent(ip -> {
-            if (host.ipAddresses().contains(ip)) {
-                log.warn("DHCP server {} removed", ip);
-                v4Handler.setDhcpConnectMac(host.mac());
-                v4Handler.setDhcpConnectVlan(host.vlan());
-            }
-        });
-        // TODO: v6 handler
+        if (config instanceof IndirectDhcpRelayConfig) {
+            IndirectDhcpRelayConfig indirectConfig = (IndirectDhcpRelayConfig) config;
+            v4Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
+            v6Handler.setIndirectDhcpServerConfigs(indirectConfig.dhcpServerConfigs());
+        }
     }
 
     /**
@@ -471,35 +447,20 @@
     private class InternalConfigListener implements NetworkConfigListener {
         @Override
         public void event(NetworkConfigEvent event) {
-            if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
-                    event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
-                    event.configClass().equals(DhcpRelayConfig.class)) {
-                updateConfig();
+            if (event.type() != NetworkConfigEvent.Type.CONFIG_ADDED &&
+                    event.type() != NetworkConfigEvent.Type.CONFIG_UPDATED) {
+                // Ignore unhandled event type
+                return;
+            }
+            if (!event.configClass().equals(DefaultDhcpRelayConfig.class) &&
+                    !event.configClass().equals(IndirectDhcpRelayConfig.class)) {
+                // Ignore unhandled config type
+                return;
+            }
+            event.config().ifPresent(config -> {
+                updateConfig(config);
                 log.info("Reconfigured");
-            }
-        }
-    }
-
-    /**
-     * Internal listener for host events.
-     */
-    private class InternalHostListener implements HostListener {
-        @Override
-        public void event(HostEvent event) {
-            switch (event.type()) {
-            case HOST_ADDED:
-            case HOST_UPDATED:
-                hostUpdated(event.subject());
-                break;
-            case HOST_REMOVED:
-                hostRemoved(event.subject());
-                break;
-            case HOST_MOVED:
-                // XXX todo -- moving dhcp server
-                break;
-            default:
-                break;
-            }
+            });
         }
     }
 }
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 d015532..a821be5 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
@@ -21,9 +21,11 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.packet.PacketContext;
 
+import java.util.Collection;
 import java.util.Optional;
 
 /**
@@ -42,6 +44,7 @@
      * Gets DHCP server IP.
      *
      * @return IP address of DHCP server; empty value if not exist
+     * @deprecated 1.12 get the address from config service
      */
     Optional<IpAddress> getDhcpServerIp();
 
@@ -49,48 +52,76 @@
      * Gets DHCP gateway IP.
      *
      * @return IP address of DHCP gateway; empty value if not exist
+     * @deprecated 1.12 get the address from config service
      */
+    @Deprecated
     Optional<IpAddress> getDhcpGatewayIp();
 
     /**
      * Gets DHCP connect Mac address.
      *
      * @return the connect Mac address of server or gateway
+     * @deprecated 1.12 get host mac from host server
      */
+    @Deprecated
     Optional<MacAddress> getDhcpConnectMac();
 
     /**
      * Sets DHCP gateway IP.
      *
      * @param dhcpGatewayIp the DHCP gateway IP
+     * @deprecated 1.12 use setDefaultDhcpServerConfigs or setindirectDhcpServerConfigs
      */
+    @Deprecated
     void setDhcpGatewayIp(IpAddress dhcpGatewayIp);
 
     /**
      * Sets DHCP connect vlan.
      *
      * @param dhcpConnectVlan the DHCP connect vlan
+     * @deprecated 1.12 use setDefaultDhcpServerConfigs or setindirectDhcpServerConfigs
      */
+    @Deprecated
     void setDhcpConnectVlan(VlanId dhcpConnectVlan);
 
     /**
      * Sets DHCP connect Mac address.
      *
      * @param dhcpConnectMac the connect Mac address
+     * @deprecated 1.12 use setDefaultDhcpServerConfigs or setindirectDhcpServerConfigs
      */
+    @Deprecated
     void setDhcpConnectMac(MacAddress dhcpConnectMac);
 
     /**
      * Sets DHCP server connect point.
      *
      * @param dhcpServerConnectPoint the server connect point
+     * @deprecated 1.12 use setDefaultDhcpServerConfigs or setindirectDhcpServerConfigs
      */
+    @Deprecated
     void setDhcpServerConnectPoint(ConnectPoint dhcpServerConnectPoint);
 
     /**
      * Sets DHCP server IP.
      *
      * @param dhcpServerIp the DHCP server IP
+     * @deprecated 1.12 use setDefaultDhcpServerConfigs or setindirectDhcpServerConfigs
      */
+    @Deprecated
     void setDhcpServerIp(IpAddress dhcpServerIp);
+
+    /**
+     * Sets DHCP server config for default case.
+     *
+     * @param configs the config
+     */
+    void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs);
+
+    /**
+     * Sets DHCP server config for indirect case.
+     *
+     * @param configs the config
+     */
+    void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs);
 }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
index 089b8ba..5a4ecde 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/cli/DhcpRelayCommand.java
@@ -25,9 +25,10 @@
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.dhcprelay.DhcpRelayConfig;
+import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
 import org.onosproject.dhcprelay.DhcpRelayManager;
 import org.onosproject.dhcprelay.api.DhcpRelayService;
+import org.onosproject.dhcprelay.config.DhcpServerConfig;
 import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Host;
@@ -65,16 +66,20 @@
 
     @Override
     protected void execute() {
-        DhcpRelayConfig cfg = CFG_SERVICE.getConfig(APP_ID, DhcpRelayConfig.class);
-        if (cfg == null) {
+        DefaultDhcpRelayConfig cfg = CFG_SERVICE.getConfig(APP_ID, DefaultDhcpRelayConfig.class);
+        if (cfg == null || cfg.dhcpServerConfigs().size() == 0) {
             print(MISSING_SERVER_CFG);
             return;
         }
 
         // DHCP server information
-        ConnectPoint connectPoint = cfg.getDhcpServerConnectPoint();
-        Ip4Address gatewayAddress = cfg.getDhcpGatewayIp();
-        Ip4Address serverIp = cfg.getDhcpServerIp();
+        // TODO: currently we pick up first DHCP server config.
+        // Will use other server configs in the future.
+        DhcpServerConfig serverConfig = cfg.dhcpServerConfigs().get(0);
+
+        ConnectPoint connectPoint = serverConfig.getDhcpServerConnectPoint().orElse(null);
+        Ip4Address gatewayAddress = serverConfig.getDhcpGatewayIp4().orElse(null);
+        Ip4Address serverIp = serverConfig.getDhcpServerIp4().orElse(null);
         String serverMac = DHCP_RELAY_SERVICE.getDhcpServerMacAddress()
                 .map(MacAddress::toString).orElse(NA);
         if (gatewayAddress != null) {
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DefaultDhcpRelayConfig.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DefaultDhcpRelayConfig.java
new file mode 100644
index 0000000..959c01b
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DefaultDhcpRelayConfig.java
@@ -0,0 +1,47 @@
+/*
+ * 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.config;
+
+import com.google.common.collect.Lists;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * DHCP Relay Config class for default use case (directly connected hosts).
+ */
+public class DefaultDhcpRelayConfig extends Config<ApplicationId> {
+    public static final String KEY = "default";
+
+
+
+    @Override
+    public boolean isValid() {
+        // check if all configs are valid
+        AtomicBoolean valid = new AtomicBoolean(true);
+        array.forEach(config -> valid.compareAndSet(true, DhcpServerConfig.isValid(config)));
+        return valid.get();
+    }
+
+    public List<DhcpServerConfig> dhcpServerConfigs() {
+        List<DhcpServerConfig> configs = Lists.newArrayList();
+        array.forEach(node -> configs.add(new DhcpServerConfig(node)));
+        return configs;
+    }
+}
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
new file mode 100644
index 0000000..2451a7a
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java
@@ -0,0 +1,149 @@
+/*
+ * 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.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.ConnectPoint;
+
+import java.util.Optional;
+
+/**
+ * DHCP server configuration.
+ */
+public class DhcpServerConfig {
+    private static final String DHCP_CONNECT_POINT = "dhcpServerConnectPoint";
+    private static final String DHCP_SERVER_IP = "serverIps";
+    private static final String DHCP_GATEWAY_IP = "gatewayIps";
+
+    private ConnectPoint connectPoint;
+    private Ip4Address serverIp4Addr;
+    private Ip4Address gatewayIp4Addr;
+    private Ip6Address serverIp6Addr;
+    private Ip6Address gatewayIp6Addr;
+
+    protected DhcpServerConfig() {
+        // empty config not allowed here
+    }
+
+    public DhcpServerConfig(JsonNode config) {
+        if (!config.has(DHCP_CONNECT_POINT)) {
+            // connect point doesn't exist
+            throw new IllegalArgumentException("Missing " + DHCP_CONNECT_POINT);
+        }
+        connectPoint = ConnectPoint.deviceConnectPoint(config.path(DHCP_CONNECT_POINT).asText());
+
+        if (!config.has(DHCP_SERVER_IP)) {
+            // server ip doesn't exist
+            throw new IllegalArgumentException("Missing " + DHCP_SERVER_IP);
+        }
+        ArrayNode serverIps = (ArrayNode) config.path(DHCP_SERVER_IP);
+        serverIps.forEach(node -> {
+            if (node.isTextual()) {
+                IpAddress ip = IpAddress.valueOf(node.asText());
+                if (ip.isIp4() && serverIp4Addr == null) {
+                    serverIp4Addr = ip.getIp4Address();
+                }
+                if (ip.isIp6() && serverIp6Addr == null) {
+                    serverIp6Addr = ip.getIp6Address();
+                }
+            }
+        });
+
+        if (!config.has(DHCP_GATEWAY_IP)) {
+            // gateway ip doesn't exist, ignore the gateway
+            return;
+        }
+        ArrayNode gatewayIps = (ArrayNode) config.path(DHCP_GATEWAY_IP);
+        gatewayIps.forEach(node -> {
+            if (node.isTextual()) {
+                IpAddress ip = IpAddress.valueOf(node.asText());
+                if (ip.isIp4() && gatewayIp4Addr == null) {
+                    gatewayIp4Addr = ip.getIp4Address();
+                }
+                if (ip.isIp6() && gatewayIp6Addr == null) {
+                    gatewayIp6Addr = ip.getIp6Address();
+                }
+            }
+        });
+    }
+
+    /**
+     * Verify a json config is a valid DHCP server config.
+     *
+     * @param jsonConfig the json config
+     * @return true if valid; false otherwise
+     */
+    public static boolean isValid(JsonNode jsonConfig) {
+        return jsonConfig.has(DHCP_CONNECT_POINT) && jsonConfig.has(DHCP_SERVER_IP);
+    }
+
+    /**
+     * Returns the dhcp server connect point.
+     *
+     * @return dhcp server connect point
+     */
+    public Optional<ConnectPoint> getDhcpServerConnectPoint() {
+        return Optional.ofNullable(connectPoint);
+    }
+
+    /**
+     * Returns the IPv4 address of DHCP server.
+     *
+     * @return IPv4 address of server; empty value if not set
+     */
+    public Optional<Ip4Address> getDhcpServerIp4() {
+        return Optional.ofNullable(serverIp4Addr);
+    }
+
+    /**
+     * Returns the optional IPv4 address of dhcp gateway, if configured.
+     * This option is typically used if the dhcp server is not directly attached
+     * to a switch; For example, the dhcp server may be reached via an external
+     * gateway connected to the dhcpserverConnectPoint.
+     *
+     * @return IPv4 address of gateway; empty value if not set
+     */
+    public Optional<Ip4Address> getDhcpGatewayIp4() {
+        return Optional.ofNullable(gatewayIp4Addr);
+    }
+
+    /**
+     * Returns the IPv6 address of DHCP server.
+     *
+     * @return IPv6 address of server ; empty value if not set
+     */
+    public Optional<Ip6Address> getDhcpServerIp6() {
+        return Optional.ofNullable(serverIp6Addr);
+    }
+
+    /**
+     * Returns the optional IPv6 address of dhcp gateway, if configured.
+     * This option is typically used if the dhcp server is not directly attached
+     * to a switch; For example, the dhcp server may be reached via an external
+     * gateway connected to the dhcpserverConnectPoint.
+     *
+     * @return IPv6 address of gateway; empty value if not set
+     */
+    public Optional<Ip6Address> getDhcpGatewayIp6() {
+        return Optional.ofNullable(gatewayIp6Addr);
+    }
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/IndirectDhcpRelayConfig.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/IndirectDhcpRelayConfig.java
new file mode 100644
index 0000000..02e76b2
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/IndirectDhcpRelayConfig.java
@@ -0,0 +1,25 @@
+/*
+ * 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.config;
+
+/**
+ * DHCP Relay Config class for indirect use case (indirectly connected hosts).
+ */
+public class IndirectDhcpRelayConfig extends DefaultDhcpRelayConfig {
+    public static final String KEY = "indirect";
+}
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/package-info.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/package-info.java
new file mode 100644
index 0000000..b9ca580
--- /dev/null
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ *
+ */
+
+/**
+ * Configuration utility for DHCP relay app.
+ */
+package org.onosproject.dhcprelay.config;
\ No newline at end of file
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java
index 60e04f9..1e87ba6 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/store/DistributedDhcpRelayStore.java
@@ -81,7 +81,7 @@
 
     @Override
     public void setDelegate(StoreDelegate<DhcpRelayStoreEvent> delegate) {
-        checkNotNull("Delegate can't be null", delegate);
+        checkNotNull(delegate, "Delegate can't be null");
         this.delegate = delegate;
     }
 
diff --git a/apps/dhcprelay/src/main/resources/dhcp-relay.json b/apps/dhcprelay/src/main/resources/dhcp-relay.json
deleted file mode 100644
index be8b062..0000000
--- a/apps/dhcprelay/src/main/resources/dhcp-relay.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "apps": {
-    "org.onosproject.dhcp-relay" : {
-      "dhcprelay" : {
-        "dhcpserverConnectPoint": "of:0000000000000002/2",
-        "serverip": "172.168.10.2",
-        "gatewayip": "192.168.10.254"
-      }
-    }
-  }
-}
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 8c57c9f..8a47cf8 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -40,6 +40,10 @@
 import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.dhcprelay.api.DhcpHandler;
+import org.onosproject.dhcprelay.config.DefaultDhcpRelayConfig;
+import org.onosproject.dhcprelay.config.DhcpServerConfig;
+import org.onosproject.dhcprelay.config.IndirectDhcpRelayConfig;
 import org.onosproject.dhcprelay.store.DhcpRecord;
 import org.onosproject.dhcprelay.store.DhcpRelayStore;
 import org.onosproject.dhcprelay.store.DhcpRelayStoreEvent;
@@ -149,7 +153,7 @@
 
     // Components
     private static final ApplicationId APP_ID = TestApplicationId.create(DhcpRelayManager.DHCP_RELAY_APP);
-    private static final DhcpRelayConfig CONFIG = new MockDhcpRelayConfig();
+    private static final DefaultDhcpRelayConfig CONFIG = new MockDefaultDhcpRelayConfig();
     private static final Set<Interface> INTERFACES = ImmutableSet.of(
             CLIENT_INTERFACE,
             CLIENT2_INTERFACE,
@@ -167,10 +171,15 @@
         manager = new DhcpRelayManager();
         manager.cfgService = createNiceMock(NetworkConfigRegistry.class);
 
-        expect(manager.cfgService.getConfig(anyObject(), anyObject()))
+        expect(manager.cfgService.getConfig(APP_ID, DefaultDhcpRelayConfig.class))
                 .andReturn(CONFIG)
                 .anyTimes();
 
+        // TODO: add indirect test
+        expect(manager.cfgService.getConfig(APP_ID, IndirectDhcpRelayConfig.class))
+                .andReturn(null)
+                .anyTimes();
+
         manager.coreService = createNiceMock(CoreService.class);
         expect(manager.coreService.registerApplication(anyString()))
                 .andReturn(APP_ID).anyTimes();
@@ -200,6 +209,9 @@
         manager.v4Handler = v4Handler;
 
         // TODO: initialize v6 handler.
+        DhcpHandler v6Handler = createNiceMock(DhcpHandler.class);
+        manager.v6Handler = v6Handler;
+
         // properties
         Dictionary<String, Object> dictionary = createNiceMock(Dictionary.class);
         expect(dictionary.get("arpEnabled")).andReturn(true).anyTimes();
@@ -297,23 +309,27 @@
         assertArrayEquals(arp.getSenderHardwareAddress(), CLIENT_INTERFACE.mac().toBytes());
     }
 
-    private static class MockDhcpRelayConfig extends DhcpRelayConfig {
+    private static class MockDefaultDhcpRelayConfig extends DefaultDhcpRelayConfig {
         @Override
         public boolean isValid() {
             return true;
         }
 
         @Override
-        public ConnectPoint getDhcpServerConnectPoint() {
-            return SERVER_CONNECT_POINT;
+        public List<DhcpServerConfig> dhcpServerConfigs() {
+            return ImmutableList.of(new MockDhcpServerConfig());
+        }
+    }
+
+    private static class MockDhcpServerConfig extends DhcpServerConfig {
+        @Override
+        public Optional<ConnectPoint> getDhcpServerConnectPoint() {
+            return Optional.of(SERVER_CONNECT_POINT);
         }
 
-        public Ip4Address getDhcpServerIp() {
-            return SERVER_IP;
-        }
-
-        public Ip4Address getDhcpGatewayIp() {
-            return null;
+        @Override
+        public Optional<Ip4Address> getDhcpServerIp4() {
+            return Optional.of(SERVER_IP);
         }
     }
 
diff --git a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/config/DhcpRelayConfigTest.java b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/config/DhcpRelayConfigTest.java
new file mode 100644
index 0000000..4160e06
--- /dev/null
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/config/DhcpRelayConfigTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.io.Resources;
+import org.junit.Test;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+import static org.onosproject.dhcprelay.DhcpRelayManager.DHCP_RELAY_APP;
+
+/**
+ * Tests for DHCP relay app configuration.
+ */
+public class DhcpRelayConfigTest {
+    private static final String CONFIG_FILE_PATH = "dhcp-relay.json";
+    private static final String INVALID_CONFIG_FILE_PATH = "invalid-dhcp-relay.json";
+    private static final ApplicationId APP_ID = new TestApplicationId("DhcpRelayTest");
+    private static final ConnectPoint DEFAULT_CONNECT_POINT = ConnectPoint.deviceConnectPoint("of:0000000000000002/2");
+    private static final Ip4Address DEFAULT_SERVER_IP = Ip4Address.valueOf("172.168.10.2");
+    private static final Ip4Address DEFAULT_GATEWAY_IP = Ip4Address.valueOf("192.168.10.254");
+    private static final Ip6Address DEFAULT_SERVER_IP_V6 = Ip6Address.valueOf("2000::200:1");
+    private static final Ip6Address DEFAULT_GATEWAY_IP_V6 = Ip6Address.valueOf("1000::100:1");
+
+    private static final ConnectPoint INDIRECT_CONNECT_POINT = ConnectPoint.deviceConnectPoint("of:0000000000000002/3");
+    private static final Ip4Address INDIRECT_SERVER_IP = Ip4Address.valueOf("172.168.10.3");
+
+    @Test
+    public void testDefaultConfig() throws IOException {
+        ObjectMapper om = new ObjectMapper();
+        JsonNode json = om.readTree(Resources.getResource(CONFIG_FILE_PATH));
+        DefaultDhcpRelayConfig config = new DefaultDhcpRelayConfig();
+        json = json.path("apps").path(DHCP_RELAY_APP).path(DefaultDhcpRelayConfig.KEY);
+        config.init(APP_ID, DefaultDhcpRelayConfig.KEY, json, om, null);
+
+        assertEquals(1, config.dhcpServerConfigs().size());
+        DhcpServerConfig serverConfig = config.dhcpServerConfigs().get(0);
+        assertEquals(DEFAULT_CONNECT_POINT, serverConfig.getDhcpServerConnectPoint().orElse(null));
+        assertEquals(DEFAULT_SERVER_IP, serverConfig.getDhcpServerIp4().orElse(null));
+        assertEquals(DEFAULT_GATEWAY_IP, serverConfig.getDhcpGatewayIp4().orElse(null));
+        assertEquals(DEFAULT_SERVER_IP_V6, serverConfig.getDhcpServerIp6().orElse(null));
+        assertEquals(DEFAULT_GATEWAY_IP_V6, serverConfig.getDhcpGatewayIp6().orElse(null));
+    }
+
+    @Test
+    public void testIndirectConfig() throws IOException {
+        ObjectMapper om = new ObjectMapper();
+        JsonNode json = om.readTree(Resources.getResource(CONFIG_FILE_PATH));
+        IndirectDhcpRelayConfig config = new IndirectDhcpRelayConfig();
+        json = json.path("apps").path(DHCP_RELAY_APP).path(IndirectDhcpRelayConfig.KEY);
+        config.init(APP_ID, IndirectDhcpRelayConfig.KEY, json, om, null);
+
+        assertEquals(1, config.dhcpServerConfigs().size());
+        DhcpServerConfig serverConfig = config.dhcpServerConfigs().get(0);
+        assertEquals(INDIRECT_CONNECT_POINT, serverConfig.getDhcpServerConnectPoint().orElse(null));
+        assertEquals(INDIRECT_SERVER_IP, serverConfig.getDhcpServerIp4().orElse(null));
+        assertNull(serverConfig.getDhcpGatewayIp4().orElse(null));
+        assertNull(serverConfig.getDhcpServerIp6().orElse(null));
+        assertNull(serverConfig.getDhcpGatewayIp6().orElse(null));
+    }
+
+    @Test
+    public void testInvalidConfig() throws IOException {
+        ObjectMapper om = new ObjectMapper();
+        JsonNode json = om.readTree(Resources.getResource(INVALID_CONFIG_FILE_PATH));
+        DefaultDhcpRelayConfig config = new DefaultDhcpRelayConfig();
+        json = json.path("apps").path(DHCP_RELAY_APP).path(DefaultDhcpRelayConfig.KEY);
+        config.init(APP_ID, DefaultDhcpRelayConfig.KEY, json, om, null);
+        assertFalse(config.isValid());
+    }
+}
diff --git a/apps/dhcprelay/src/test/resources/dhcp-relay.json b/apps/dhcprelay/src/test/resources/dhcp-relay.json
new file mode 100644
index 0000000..bd90b7a
--- /dev/null
+++ b/apps/dhcprelay/src/test/resources/dhcp-relay.json
@@ -0,0 +1,19 @@
+{
+  "apps": {
+    "org.onosproject.dhcprelay" : {
+      "default": [
+        {
+          "dhcpServerConnectPoint": "of:0000000000000002/2",
+          "serverIps": ["172.168.10.2", "2000::200:1"],
+          "gatewayIps": ["192.168.10.254", "1000::100:1"]
+        }
+      ],
+      "indirect": [
+        {
+          "dhcpServerConnectPoint": "of:0000000000000002/3",
+          "serverIps": ["172.168.10.3"]
+        }
+      ]
+    }
+  }
+}
diff --git a/apps/dhcprelay/src/test/resources/invalid-dhcp-relay.json b/apps/dhcprelay/src/test/resources/invalid-dhcp-relay.json
new file mode 100644
index 0000000..2c67eaa
--- /dev/null
+++ b/apps/dhcprelay/src/test/resources/invalid-dhcp-relay.json
@@ -0,0 +1,12 @@
+{
+  "apps": {
+    "org.onosproject.dhcprelay" : {
+      "default": [
+        {
+          "dhcpServerConnectPoint": "of:0000000000000002/2",
+          "gatewayIps": ["192.168.10.254", "1000::100:1"]
+        }
+      ]
+    }
+  }
+}