[CORD-1735] Add "relayAgentIps" option to DHCP relay application config

Change-Id: I8ea5a144694f510c93530c9107573b1cb1f5703b
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 7379e5f..e1b3933 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp4HandlerImpl.java
@@ -631,12 +631,16 @@
      */
     private Ethernet processDhcpPacketFromClient(PacketContext context,
                                                  Ethernet ethernetPacket) {
+        ConnectPoint receivedFrom = context.inPacket().receivedFrom();
+        DeviceId receivedFromDevice = receivedFrom.deviceId();
+
         // get dhcp header.
         Ethernet etherReply = ethernetPacket.duplicate();
         IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
         UDP udpPacket = (UDP) ipv4Packet.getPayload();
         DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
 
+        // TODO: refactor
         VlanId dhcpConnectVlan = null;
         MacAddress dhcpConnectMac = null;
         Ip4Address dhcpServerIp = null;
@@ -652,7 +656,7 @@
             dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
             dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
             dhcpServerIp = serverInfo.getDhcpServerIp4().orElse(null);
-            relayAgentIp = serverInfo.getRelayAgentIp4().orElse(null);
+            relayAgentIp = serverInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
         }
 
         if (!indirectServerInfoList.isEmpty()) {
@@ -660,7 +664,7 @@
             indirectDhcpConnectVlan = indirectServerInfo.getDhcpConnectVlan().orElse(null);
             indirectDhcpConnectMac = indirectServerInfo.getDhcpConnectMac().orElse(null);
             indirectDhcpServerIp = indirectServerInfo.getDhcpServerIp4().orElse(null);
-            indirectRelayAgentIp = indirectServerInfo.getRelayAgentIp4().orElse(null);
+            indirectRelayAgentIp = indirectServerInfo.getRelayAgentIp4(receivedFromDevice).orElse(null);
         }
 
         Ip4Address clientInterfaceIp =
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 d9af816..d809d53 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -171,24 +171,6 @@
     protected Multimap<DeviceId, VlanId> ignoredVlans = HashMultimap.create();
     private InternalHostListener hostListener = new InternalHostListener();
 
-    private Ip6Address dhcpServerIp = null;
-    // dhcp server may be connected directly to the SDN network or
-    // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
-    // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
-    // to the gateway.
-    private ConnectPoint dhcpServerConnectPoint = null;
-    private MacAddress dhcpConnectMac = null;
-    private VlanId dhcpConnectVlan = null;
-    private Ip6Address dhcpGatewayIp = null;
-    private Ip6Address relayAgentIpFromCfg = null;
-
-    private Ip6Address indirectDhcpServerIp = null;
-    private ConnectPoint indirectDhcpServerConnectPoint = null;
-    private MacAddress indirectDhcpConnectMac = null;
-    private VlanId indirectDhcpConnectVlan = null;
-    private Ip6Address indirectDhcpGatewayIp = null;
-    private Ip6Address indirectRelayAgentIpFromCfg = null;
-
     private List<DhcpServerInfo> defaultServerInfoList = Lists.newArrayList();
     private List<DhcpServerInfo> indirectServerInfoList = Lists.newArrayList();
 
@@ -769,29 +751,58 @@
     }
 
     /**
-     *
-     * build the DHCP6 solicit/request packet with gatewayip.
+     * Build the DHCP6 solicit/request packet with gatewayip.
+     * TODO: method too long, need to be refactored.
      *
      * @param context packet context
      * @param clientPacket client ethernet packet
      * @param clientInterfaces set of client side interfaces
      */
      private InternalPacket processDhcp6PacketFromClient(PacketContext context,
-                                                        Ethernet clientPacket, Set<Interface> clientInterfaces) {
+                                                         Ethernet clientPacket, Set<Interface> clientInterfaces) {
+         ConnectPoint receivedFrom = context.inPacket().receivedFrom();
+         DeviceId receivedFromDevice = receivedFrom.deviceId();
+         DhcpServerInfo serverInfo;
+         Ip6Address dhcpServerIp = null;
+         ConnectPoint dhcpServerConnectPoint = null;
+         MacAddress dhcpConnectMac = null;
+         VlanId dhcpConnectVlan = null;
+         Ip6Address dhcpGatewayIp = null;
+         Ip6Address indirectDhcpServerIp = null;
+         ConnectPoint indirectDhcpServerConnectPoint = null;
+         MacAddress indirectDhcpConnectMac = null;
+         VlanId indirectDhcpConnectVlan = null;
+         Ip6Address indirectDhcpGatewayIp = null;
+         Ip6Address indirectRelayAgentIpFromCfg = null;
+         if (!defaultServerInfoList.isEmpty()) {
+             serverInfo = defaultServerInfoList.get(0);
+             dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
+             dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
+             dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
+             dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+             dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+         }
+         if (!indirectServerInfoList.isEmpty()) {
+             serverInfo = indirectServerInfoList.get(0);
+             indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
+             indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
+             indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
+             indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+             indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+             indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
+         }
          Ip6Address relayAgentIp = getRelayAgentIPv6Address(clientInterfaces);
          MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
          if (relayAgentIp == null || relayAgentMac == null) {
              log.warn("Missing DHCP relay agent interface Ipv6 addr config for "
-                             + "packet from client on port: {}. Aborting packet processing",
-                     clientInterfaces.iterator().next().connectPoint());
+                              + "packet from client on port: {}. Aborting packet processing",
+                      clientInterfaces.iterator().next().connectPoint());
              return null;
          }
-
          // get dhcp6 header.
          IPv6 clientIpv6 = (IPv6) clientPacket.getPayload();
          UDP clientUdp = (UDP) clientIpv6.getPayload();
          DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
-
          boolean directConnFlag = directlyConnected(clientDhcp6);
          Interface serverInterface = directConnFlag ? getServerInterface() : getIndirectServerInterface();
          if (serverInterface == null) {
@@ -804,33 +815,30 @@
              log.warn("No IP v6 address for server Interface {}", serverInterface);
              return null;
          }
-
          Ethernet etherReply = clientPacket.duplicate();
          etherReply.setSourceMACAddress(macFacingServer);
-
-         if ((directConnFlag && this.dhcpConnectMac == null)  ||
-                 !directConnFlag && this.indirectDhcpConnectMac == null && this.dhcpConnectMac == null)   {
+         if ((directConnFlag && dhcpConnectMac == null)  ||
+                 !directConnFlag && indirectDhcpConnectMac == null && dhcpConnectMac == null)   {
              log.warn("Packet received from {} connected client.", directConnFlag ? "directly" : "indirectly");
              log.warn("DHCP6 {} not yet resolved .. Aborting DHCP packet processing from client on port: {}",
-                     (this.dhcpGatewayIp == null) ? "server IP " + this.dhcpServerIp
-                             : "gateway IP " + this.dhcpGatewayIp,
-                     clientInterfaces.iterator().next().connectPoint());
+                      (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
+                              : "gateway IP " + dhcpGatewayIp,
+                      clientInterfaces.iterator().next().connectPoint());
              return null;
          }
-
-         if (this.dhcpServerConnectPoint == null) {
+         if (dhcpServerConnectPoint == null) {
              log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
-                     directConnFlag, this.dhcpServerConnectPoint, this.indirectDhcpServerConnectPoint);
+                     directConnFlag, dhcpServerConnectPoint, indirectDhcpServerConnectPoint);
             return null;
         }
 
-        etherReply.setDestinationMACAddress(this.dhcpConnectMac);
-        etherReply.setVlanID(this.dhcpConnectVlan.toShort());
+        etherReply.setDestinationMACAddress(dhcpConnectMac);
+        etherReply.setVlanID(dhcpConnectVlan.toShort());
 
         IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
         byte[] peerAddress = clientIpv6.getSourceAddress();
         ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
-        ipv6Packet.setDestinationAddress(this.dhcpServerIp.toOctets());
+        ipv6Packet.setDestinationAddress(dhcpServerIp.toOctets());
 
         UDP udpPacket = (UDP) ipv6Packet.getPayload();
         udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
@@ -857,100 +865,86 @@
                       HexString.toHexString(relayAgentIp.toOctets(), ":"));
 
         } else {
-            if (this.indirectDhcpServerIp == null) {
+            if (indirectDhcpServerIp == null) {
                 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
-                         HexString.toHexString(this.dhcpServerIp.toOctets()));
+                         HexString.toHexString(dhcpServerIp.toOctets()));
              } else {
                  // Indirect case, replace destination to indirect dhcp server if exist
                  // Check if mac is obtained for valid server ip
-                 if (this.indirectDhcpConnectMac == null) {
+                 if (indirectDhcpConnectMac == null) {
                      log.warn("DHCP6 {} not yet resolved .. Aborting DHCP "
-                            + "packet processing from client on port: {}",
-                            (this.indirectDhcpGatewayIp == null) ? "server IP " + this.indirectDhcpServerIp
-                                                                   : "gateway IP " + this.indirectDhcpGatewayIp,
-                             clientInterfaces.iterator().next().connectPoint());
+                                      + "packet processing from client on port: {}",
+                              (indirectDhcpGatewayIp == null) ? "server IP " + indirectDhcpServerIp
+                                      : "gateway IP " + indirectDhcpGatewayIp,
+                              clientInterfaces.iterator().next().connectPoint());
                      return null;
                  }
-                 etherReply.setDestinationMACAddress(this.indirectDhcpConnectMac);
-                 etherReply.setVlanID(this.indirectDhcpConnectVlan.toShort());
-                 ipv6Packet.setDestinationAddress(this.indirectDhcpServerIp.toOctets());
+                 etherReply.setDestinationMACAddress(indirectDhcpConnectMac);
+                 etherReply.setVlanID(indirectDhcpConnectVlan.toShort());
+                 ipv6Packet.setDestinationAddress(indirectDhcpServerIp.toOctets());
 
              }
-             if (this.indirectRelayAgentIpFromCfg == null) {
+             if (indirectRelayAgentIpFromCfg == null) {
                  dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
                  log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
-                         HexString.toHexString(relayAgentIp.toOctets(), ":"));
-            } else {
-                dhcp6Relay.setLinkAddress(this.indirectRelayAgentIpFromCfg.toOctets());
-                log.debug("indirect connection: relayAgentIp from config file is available! {}",
-                          HexString.toHexString(this.indirectRelayAgentIpFromCfg.toOctets(), ":"));
-            }
-        }
-
-        // peer address:  address of the client or relay agent from which
-        // the message to be relayed was received.
-        dhcp6Relay.setPeerAddress(peerAddress);
-        List<Dhcp6Option> options = new ArrayList<Dhcp6Option>();
-
-        // directly connected case, hop count is zero
-        // otherwise, hop count + 1
-        if (directConnFlag) {
-            dhcp6Relay.setHopCount((byte) 0);
-        } else {
-            dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
-        }
-
-        // create relay message option
-        Dhcp6Option relayMessage = new Dhcp6Option();
-        relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
-        relayMessage.setLength((short) dhcp6PacketByte.length);
-        relayMessage.setData(dhcp6PacketByte);
-        options.add(relayMessage);
-
-        // create interfaceId option
-        String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
-        Dhcp6Option interfaceId = new Dhcp6Option();
-        interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
-        byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
-        byte[] inPortStringBytes = inPortString.getBytes();
-        byte[] vlanIdBytes = new byte[2];
-        vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
-        vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
-        byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
-                inPortStringBytes.length + vlanIdBytes.length];
-        log.debug("Length: interfaceIdBytes  {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
-                  interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
-                  vlanIdBytes.length);
-
-        System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
-        System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
-        System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
-                         vlanIdBytes.length);
-
-        interfaceId.setData(interfaceIdBytes);
-        interfaceId.setLength((short) interfaceIdBytes.length);
-
-        options.add(interfaceId);
-
-        log.debug("interfaceId write srcMac {} portString {}",
-                  HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
-        dhcp6Relay.setOptions(options);
-        udpPacket.setPayload(dhcp6Relay);
-        udpPacket.resetChecksum();
-        ipv6Packet.setPayload(udpPacket);
-        ipv6Packet.setHopLimit((byte) 64);
-        etherReply.setPayload(ipv6Packet);
-
-        if (directConnFlag) {
-            return new InternalPacket(etherReply, this.dhcpServerConnectPoint);
-        } else {
-            if (this.indirectDhcpServerIp == null) {
-                return new InternalPacket(etherReply, this.dhcpServerConnectPoint);
-            } else {
-                return new InternalPacket(etherReply, this.indirectDhcpServerConnectPoint);
-            }
-        }
-    }
+                          HexString.toHexString(relayAgentIp.toOctets(), ":"));
+             } else {
+                 dhcp6Relay.setLinkAddress(indirectRelayAgentIpFromCfg.toOctets());
+                 log.debug("indirect connection: relayAgentIp from config file is available! {}",
+                           HexString.toHexString(indirectRelayAgentIpFromCfg.toOctets(), ":"));
+             }
+         }
+         // peer address: address of the client or relay agent from which
+         // the message to be relayed was received.
+         dhcp6Relay.setPeerAddress(peerAddress);
+         List<Dhcp6Option> options = new ArrayList<>();
+         // directly connected case, hop count is zero; otherwise, hop count + 1
+         if (directConnFlag) {
+             dhcp6Relay.setHopCount((byte) 0);
+         } else {
+             dhcp6Relay.setHopCount((byte) (dhcp6Packet.getHopCount() + 1));
+         }
+         // create relay message option
+         Dhcp6Option relayMessage = new Dhcp6Option();
+         relayMessage.setCode(DHCP6.OptionCode.RELAY_MSG.value());
+         relayMessage.setLength((short) dhcp6PacketByte.length);
+         relayMessage.setData(dhcp6PacketByte);
+         options.add(relayMessage);
+         // create interfaceId option
+         String inPortString = "-" + context.inPacket().receivedFrom().toString() + ":";
+         Dhcp6Option interfaceId = new Dhcp6Option();
+         interfaceId.setCode(DHCP6.OptionCode.INTERFACE_ID.value());
+         byte[] clientSoureMacBytes = clientPacket.getSourceMACAddress();
+         byte[] inPortStringBytes = inPortString.getBytes();
+         byte[] vlanIdBytes = new byte[2];
+         vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
+         vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
+         byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
+                 inPortStringBytes.length + vlanIdBytes.length];
+         log.debug("Length: interfaceIdBytes  {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
+                   interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
+                   vlanIdBytes.length);
+         System.arraycopy(clientSoureMacBytes, 0, interfaceIdBytes, 0, clientSoureMacBytes.length);
+         System.arraycopy(inPortStringBytes, 0, interfaceIdBytes, clientSoureMacBytes.length, inPortStringBytes.length);
+         System.arraycopy(vlanIdBytes, 0, interfaceIdBytes, clientSoureMacBytes.length + inPortStringBytes.length,
+                          vlanIdBytes.length);
+         interfaceId.setData(interfaceIdBytes);
+         interfaceId.setLength((short) interfaceIdBytes.length);
+         options.add(interfaceId);
+         log.debug("interfaceId write srcMac {} portString {}",
+                   HexString.toHexString(clientSoureMacBytes, ":"), inPortString);
+         dhcp6Relay.setOptions(options);
+         udpPacket.setPayload(dhcp6Relay);
+         udpPacket.resetChecksum();
+         ipv6Packet.setPayload(udpPacket);
+         ipv6Packet.setHopLimit((byte) 64);
+         etherReply.setPayload(ipv6Packet);
+         if (directConnFlag || indirectDhcpServerIp == null) {
+             return new InternalPacket(etherReply, dhcpServerConnectPoint);
+         } else {
+             return new InternalPacket(etherReply, indirectDhcpServerConnectPoint);
+         }
+     }
 
     /**
      *
@@ -962,6 +956,44 @@
      */
     private InternalPacket processDhcp6PacketFromServer(PacketContext context,
                                                         Ethernet receivedPacket, Set<Interface> recevingInterfaces) {
+
+        ConnectPoint receivedFrom = context.inPacket().receivedFrom();
+        DeviceId receivedFromDevice = receivedFrom.deviceId();
+
+        // TODO: refactor
+        DhcpServerInfo serverInfo;
+        Ip6Address dhcpServerIp = null;
+        ConnectPoint dhcpServerConnectPoint = null;
+        MacAddress dhcpConnectMac = null;
+        VlanId dhcpConnectVlan = null;
+        Ip6Address dhcpGatewayIp = null;
+
+        Ip6Address indirectDhcpServerIp = null;
+        ConnectPoint indirectDhcpServerConnectPoint = null;
+        MacAddress indirectDhcpConnectMac = null;
+        VlanId indirectDhcpConnectVlan = null;
+        Ip6Address indirectDhcpGatewayIp = null;
+        Ip6Address indirectRelayAgentIpFromCfg = null;
+
+        if (!defaultServerInfoList.isEmpty()) {
+            serverInfo = defaultServerInfoList.get(0);
+            dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
+            dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
+            dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
+            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+            dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+        }
+
+        if (!indirectServerInfoList.isEmpty()) {
+            serverInfo = indirectServerInfoList.get(0);
+            indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
+            indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
+            indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
+            indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+            indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+            indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6(receivedFromDevice).orElse(null);
+        }
+
         // get dhcp6 header.
         Ethernet etherReply = receivedPacket.duplicate();
         IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
@@ -970,17 +1002,17 @@
 
         Boolean directConnFlag = directlyConnected(dhcp6Relay);
         ConnectPoint inPort = context.inPacket().receivedFrom();
-        if ((directConnFlag || (!directConnFlag && this.indirectDhcpServerIp == null))
-             && !inPort.equals(this.dhcpServerConnectPoint)) {
+        if ((directConnFlag || (!directConnFlag && indirectDhcpServerIp == null))
+             && !inPort.equals(dhcpServerConnectPoint)) {
             log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
-                    inPort, this.dhcpServerConnectPoint);
+                    inPort, dhcpServerConnectPoint);
             return null;
         }
 
-        if (!directConnFlag && this.indirectDhcpServerIp != null &&
-                                !inPort.equals(this.indirectDhcpServerConnectPoint)) {
+        if (!directConnFlag && indirectDhcpServerIp != null &&
+                                !inPort.equals(indirectDhcpServerConnectPoint)) {
             log.warn("Receiving port {} is not the same as server connect point {} for indirect",
-                    inPort, this.indirectDhcpServerConnectPoint);
+                    inPort, indirectDhcpServerConnectPoint);
             return null;
         }
 
@@ -1084,13 +1116,11 @@
     @Override
     public void setDefaultDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
         setDhcpServerConfigs(configs, defaultServerInfoList);
-        reloadServerSettings();
     }
 
     @Override
     public void setIndirectDhcpServerConfigs(Collection<DhcpServerConfig> configs) {
         setDhcpServerConfigs(configs, indirectServerInfoList);
-        reloadServerSettings();
     }
 
     public void setDhcpServerConfigs(Collection<DhcpServerConfig> configs, List<DhcpServerInfo> serverInfoList) {
@@ -1186,7 +1216,6 @@
     private void hostUpdated(Host host) {
         hostUpdated(host, defaultServerInfoList);
         hostUpdated(host, indirectServerInfoList);
-        reloadServerSettings();
     }
 
     private void hostUpdated(Host host, List<DhcpServerInfo> serverInfoList) {
@@ -1220,7 +1249,6 @@
     private void hostRemoved(Host host) {
         hostRemoved(host, defaultServerInfoList);
         hostRemoved(host, indirectServerInfoList);
-        reloadServerSettings();
     }
 
     private void hostRemoved(Host host, List<DhcpServerInfo> serverInfoList) {
@@ -1246,28 +1274,6 @@
         }
     }
 
-    private void reloadServerSettings() {
-        DhcpServerInfo serverInfo;
-        if (!defaultServerInfoList.isEmpty()) {
-            serverInfo = defaultServerInfoList.get(0);
-            this.dhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
-            this.dhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            this.dhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            this.dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            this.dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
-        }
-
-        if (!indirectServerInfoList.isEmpty()) {
-            serverInfo = indirectServerInfoList.get(0);
-            this.indirectDhcpConnectMac = serverInfo.getDhcpConnectMac().orElse(null);
-            this.indirectDhcpGatewayIp = serverInfo.getDhcpGatewayIp6().orElse(null);
-            this.indirectDhcpServerIp = serverInfo.getDhcpServerIp6().orElse(null);
-            this.indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
-            this.indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
-            this.indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6().orElse(null);
-        }
-    }
-
     /**
      * Returns the first interface ip from interface.
      *
@@ -1291,9 +1297,18 @@
      * @return the Interface facing to the server; null if not found
      */
     private Interface getServerInterface() {
-        if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
+        DhcpServerInfo serverInfo;
+        ConnectPoint dhcpServerConnectPoint;
+        VlanId dhcpConnectVlan;
+
+        if (!defaultServerInfoList.isEmpty()) {
+            serverInfo = defaultServerInfoList.get(0);
+            dhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+            dhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+        } else {
             return null;
         }
+
         return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
                 .stream()
                 .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
@@ -1308,9 +1323,19 @@
      * @return the Interface facing to the server; null if not found
      */
     private Interface getIndirectServerInterface() {
-        if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
+        DhcpServerInfo serverInfo;
+
+        ConnectPoint indirectDhcpServerConnectPoint;
+        VlanId indirectDhcpConnectVlan;
+
+        if (!indirectServerInfoList.isEmpty()) {
+            serverInfo = indirectServerInfoList.get(0);
+            indirectDhcpServerConnectPoint = serverInfo.getDhcpServerConnectPoint().orElse(null);
+            indirectDhcpConnectVlan = serverInfo.getDhcpConnectVlan().orElse(null);
+        } else {
             return getServerInterface();
         }
+
         return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
                 .stream()
                 .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpServerInfo.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpServerInfo.java
index 81a2411..74328ec 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpServerInfo.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/api/DhcpServerInfo.java
@@ -18,6 +18,7 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.dhcprelay.config.DhcpServerConfig;
@@ -43,6 +44,7 @@
      * @param version DHCP version for the server
      */
     public DhcpServerInfo(DhcpServerConfig config, Version version) {
+        this.relayAgentIps = Maps.newHashMap(config.getRelayAgentIps());
         this.connectPoint = config.getDhcpServerConnectPoint().orElse(null);
         this.version = version;
 
@@ -50,12 +52,10 @@
             case DHCP_V4:
                 this.serverIp4Addr = config.getDhcpServerIp4().orElse(null);
                 this.gatewayIp4Addr = config.getDhcpGatewayIp4().orElse(null);
-                this.relayAgentIp4Addr = config.getRelayAgentIp4().orElse(null);
                 break;
             case DHCP_V6:
                 this.serverIp6Addr = config.getDhcpServerIp6().orElse(null);
                 this.gatewayIp6Addr = config.getDhcpGatewayIp6().orElse(null);
-                this.relayAgentIp6Addr = config.getRelayAgentIp6().orElse(null);
                 break;
             default:
                 break;
@@ -119,14 +119,12 @@
             case DHCP_V4:
                 toStringHelper
                         .add("serverIp4Addr", serverIp4Addr)
-                        .add("gatewayIp4Addr", gatewayIp4Addr)
-                        .add("relayAgentIp4Addr", relayAgentIp4Addr);
+                        .add("gatewayIp4Addr", gatewayIp4Addr);
                 break;
             case DHCP_V6:
                 toStringHelper
                         .add("serverIp6Addr", serverIp6Addr)
-                        .add("gatewayIp6Addr", gatewayIp6Addr)
-                        .add("relayAgentIp6Addr", relayAgentIp6Addr);
+                        .add("gatewayIp6Addr", gatewayIp6Addr);
                 break;
             default:
                 break;
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 acf8f79..a54f54b 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
@@ -99,7 +99,6 @@
                     .map(Object::toString).orElse(NA);
             String gatewayAddress;
             String serverIp;
-            String relayAgentIp;
 
             switch (dhcpServerInfo.getVersion()) {
                 case DHCP_V4:
@@ -107,24 +106,20 @@
                             .map(Object::toString).orElse(null);
                     serverIp = dhcpServerInfo.getDhcpServerIp4()
                             .map(Object::toString).orElse(NA);
-                    relayAgentIp = dhcpServerInfo.getRelayAgentIp4()
-                            .map(Object::toString).orElse(NA);
                     break;
                 case DHCP_V6:
                     gatewayAddress = dhcpServerInfo.getDhcpGatewayIp6()
                             .map(Object::toString).orElse(null);
                     serverIp = dhcpServerInfo.getDhcpServerIp6()
                             .map(Object::toString).orElse(NA);
-                    relayAgentIp = dhcpServerInfo.getRelayAgentIp6()
-                            .map(Object::toString).orElse(NA);
                     break;
                 default:
                     return;
             }
             if (gatewayAddress != null) {
-                print(DHCP_SERVER_GW, connectPoint, serverIp, gatewayAddress, serverMac, relayAgentIp);
+                print(DHCP_SERVER_GW, connectPoint, serverIp, gatewayAddress, serverMac);
             } else {
-                print(DHCP_SERVER, connectPoint, serverIp, serverMac, relayAgentIp);
+                print(DHCP_SERVER, connectPoint, serverIp, serverMac);
             }
         });
     }
diff --git a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java
index 39683cd..c8c0f55 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/config/DhcpServerConfig.java
@@ -20,11 +20,15 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
 
+import java.util.Map;
 import java.util.Optional;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -38,14 +42,15 @@
     private static final String DHCP_SERVER_IP = "serverIps";
     private static final String DHCP_GATEWAY_IP = "gatewayIps";
     private static final String RELAY_AGENT_IP = "relayAgentIps";
+    private static final String IPV4 = "ipv4";
+    private static final String IPV6 = "ipv6";
 
     protected ConnectPoint connectPoint;
     protected Ip4Address serverIp4Addr;
     protected Ip4Address gatewayIp4Addr;
-    protected Ip4Address relayAgentIp4Addr;
     protected Ip6Address serverIp6Addr;
     protected Ip6Address gatewayIp6Addr;
-    protected Ip6Address relayAgentIp6Addr;
+    protected Map<DeviceId, Pair<Ip4Address, Ip6Address>> relayAgentIps = Maps.newHashMap();
 
     protected DhcpServerConfig() {
         // empty config not allowed here
@@ -90,17 +95,19 @@
             });
         }
         if (config.has(RELAY_AGENT_IP)) {
-            ArrayNode relayAgentIps = (ArrayNode) config.path(RELAY_AGENT_IP);
-            relayAgentIps.forEach(node -> {
-                if (node.isTextual()) {
-                    IpAddress ip = IpAddress.valueOf(node.asText());
-                    if (ip.isIp4() && relayAgentIp4Addr == null) {
-                        relayAgentIp4Addr = ip.getIp4Address();
-                    }
-                    if (ip.isIp6() && relayAgentIp6Addr == null) {
-                        relayAgentIp6Addr = ip.getIp6Address();
-                    }
+            JsonNode relayAgentIpsNode = config.path(RELAY_AGENT_IP);
+            relayAgentIpsNode.fields().forEachRemaining(e -> {
+                DeviceId deviceId = DeviceId.deviceId(e.getKey());
+                JsonNode ips = e.getValue();
+                Ip4Address ipv4 = null;
+                Ip6Address ipv6 = null;
+                if (ips.has(IPV4)) {
+                    ipv4 = Ip4Address.valueOf(ips.get(IPV4).asText());
                 }
+                if (ips.has(IPV6)) {
+                    ipv6 = Ip6Address.valueOf(ips.get(IPV6).asText());
+                }
+                relayAgentIps.put(deviceId, Pair.of(ipv4, ipv6));
             });
         }
 
@@ -172,25 +179,46 @@
     }
 
     /**
-     * Returns the optional IPv4 address for relay agent, if configured.
+     * Returns the optional IPv4 address for relay agent for given device,
+     * if configured.
      * This option is used if we want to replace the giaddr field in DHCPv4
      * payload.
      *
+     * @param deviceId the device
      * @return the giaddr; empty value if not set
      */
-    public Optional<Ip4Address> getRelayAgentIp4() {
-        return Optional.ofNullable(relayAgentIp4Addr);
+    public Optional<Ip4Address> getRelayAgentIp4(DeviceId deviceId) {
+        Pair<Ip4Address, Ip6Address> relayAgentIp = relayAgentIps.get(deviceId);
+        if (relayAgentIp == null) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(relayAgentIp.getLeft());
     }
 
     /**
-     * Returns the optional IPv6 address for relay agent, if configured.
+     * Returns the optional IPv6 address for relay agent for given device,
+     * if configured.
      * This option is used if we want to replace the link-address field in DHCPv6
      * payload.
      *
-     * @return the giaddr; empty value if not set
+     * @param deviceId the device
+     * @return the link-addr; empty value if not set
      */
-    public Optional<Ip6Address> getRelayAgentIp6() {
-        return Optional.ofNullable(relayAgentIp6Addr);
+    public Optional<Ip6Address> getRelayAgentIp6(DeviceId deviceId) {
+        Pair<Ip4Address, Ip6Address> relayAgentIp = relayAgentIps.get(deviceId);
+        if (relayAgentIp == null) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(relayAgentIp.getRight());
+    }
+
+    /**
+     * Gets all relay agent ips and device mapping.
+     *
+     * @return the mapping
+     */
+    public Map<DeviceId, Pair<Ip4Address, Ip6Address>> getRelayAgentIps() {
+        return relayAgentIps;
     }
 
     @Override
@@ -205,16 +233,14 @@
         return Objects.equal(connectPoint, that.connectPoint) &&
                 Objects.equal(serverIp4Addr, that.serverIp4Addr) &&
                 Objects.equal(gatewayIp4Addr, that.gatewayIp4Addr) &&
-                Objects.equal(relayAgentIp4Addr, that.relayAgentIp4Addr) &&
                 Objects.equal(serverIp6Addr, that.serverIp6Addr) &&
                 Objects.equal(gatewayIp6Addr, that.gatewayIp6Addr) &&
-                Objects.equal(relayAgentIp6Addr, that.relayAgentIp6Addr);
+                Objects.equal(relayAgentIps, that.relayAgentIps);
     }
 
     @Override
     public int hashCode() {
         return Objects.hashCode(connectPoint, serverIp4Addr, gatewayIp4Addr,
-                                relayAgentIp4Addr, serverIp6Addr, gatewayIp6Addr,
-                                relayAgentIp6Addr);
+                                serverIp6Addr, gatewayIp6Addr, relayAgentIps);
     }
 }
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 7efed1a..6e920cd 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.Streams;
 import com.google.common.io.Resources;
 import org.apache.commons.io.Charsets;
+import org.apache.commons.lang3.tuple.Pair;
 import org.easymock.Capture;
 import org.easymock.CaptureType;
 import org.easymock.EasyMock;
@@ -871,11 +872,13 @@
          */
         public MockDhcpServerConfig(Ip4Address relayAgentIp) {
             this.relayAgentIp = relayAgentIp;
+            this.relayAgentIps.put(DEV_1_ID, Pair.of(relayAgentIp, null));
+            this.relayAgentIps.put(DEV_2_ID, Pair.of(relayAgentIp, null));
         }
 
         @Override
-        public Optional<Ip4Address> getRelayAgentIp4() {
-            return Optional.ofNullable(relayAgentIp);
+        public Optional<Ip4Address> getRelayAgentIp4(DeviceId deviceId) {
+            return Optional.ofNullable(this.relayAgentIps.get(deviceId).getLeft());
         }
 
         @Override
diff --git a/apps/dhcprelay/src/test/resources/dhcp-relay.json b/apps/dhcprelay/src/test/resources/dhcp-relay.json
index e77d8a7..fe5da9c 100644
--- a/apps/dhcprelay/src/test/resources/dhcp-relay.json
+++ b/apps/dhcprelay/src/test/resources/dhcp-relay.json
@@ -6,14 +6,28 @@
           "dhcpServerConnectPoint": "of:0000000000000002/2",
           "serverIps": ["172.168.10.2", "2000::200:1"],
           "gatewayIps": ["192.168.10.254", "1000::100:1"],
-          "relayAgentIps": ["10.0.0.1", "1000:100::100:1"]
+          "relayAgentIps": {
+            "of:0000000000000001": {
+              "ipv4": "10.0.0.10",
+              "ipv6": "2000::10"
+            },
+            "of:0000000000000002": {
+              "ipv4": "10.0.1.10",
+              "ipv6": "2000::1:10"
+            }
+          }
         }
       ],
       "indirect": [
         {
           "dhcpServerConnectPoint": "of:0000000000000002/3",
           "serverIps": ["172.168.10.3"],
-          "relayAgentIps": ["10.0.1.1"]
+          "relayAgentIps": {
+            "of:0000000000000001": {
+              "ipv4": "10.0.0.10",
+              "ipv6": "2000::10"
+            }
+          }
         }
       ],
       "ignoreDhcp": [