[CORD-2097] DHCP V6 Relay incorrect src ip and src mac set toward server

Change-Id: Ib2bd641d5be37763e83e5a056e50125c4e7e4f84
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 719bbc5..d070308 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -316,7 +316,7 @@
                     return true;  // must be directly connected
                 } else {
                     log.debug("directlyConnected  false. 1st relay-reply, 2nd relay-reply MsgType {}",
-                               dhcp6Payload2.getMsgType());
+                              dhcp6Payload2.getMsgType());
                     return false;  // must be indirectly connected
                 }
             }
@@ -337,11 +337,11 @@
 
         // extract the relay message if exist
         DHCP6 dhcp6Payload = dhcp6.getOptions().stream()
-                    .filter(opt -> opt instanceof Dhcp6RelayOption)
-                    .map(BasePacket::getPayload)
-                    .map(pld -> (DHCP6) pld)
-                    .findFirst()
-                    .orElse(null);
+                .filter(opt -> opt instanceof Dhcp6RelayOption)
+                .map(BasePacket::getPayload)
+                .map(pld -> (DHCP6) pld)
+                .findFirst()
+                .orElse(null);
 
 
         if (dhcp6Payload == null) {
@@ -584,7 +584,7 @@
                     Route routeForIP = new Route(Route.Source.STATIC, ip.toIpPrefix(), nextHopIp);
                     log.debug("removing route of 128 address for indirectly connected.");
                     log.debug("128 ip {}, nexthop {}", HexString.toHexString(ip.toOctets(), ":"),
-                            HexString.toHexString(nextHopIp.toOctets(), ":"));
+                              HexString.toHexString(nextHopIp.toOctets(), ":"));
                     routeStore.removeRoute(routeForIP);
                 }
 
@@ -595,7 +595,7 @@
                     Route routeForPrefix = new Route(Route.Source.STATIC, ipPrefix, nextHopIp);
                     log.debug("removing route of PD for indirectly connected.");
                     log.debug("pd ip {}, nexthop {}", HexString.toHexString(ipPrefix.address().toOctets(), ":"),
-                            HexString.toHexString(nextHopIp.toOctets(), ":"));
+                              HexString.toHexString(nextHopIp.toOctets(), ":"));
 
                     routeStore.removeRoute(routeForPrefix);
                 }
@@ -613,9 +613,9 @@
      * @param clientInterface client interface
      */
     private void addHostOrRoute(boolean directConnFlag, DHCP6 dhcp6Relay,
-                                   DHCP6 embeddedDhcp6,
-                                   MacAddress clientMac,
-                                   Interface clientInterface) {
+                                DHCP6 embeddedDhcp6,
+                                MacAddress clientMac,
+                                Interface clientInterface) {
         log.debug("addHostOrRoute entered.");
         // add host or route
         if (isDhcp6Reply(dhcp6Relay)) {
@@ -644,8 +644,8 @@
                                                                       false);
                     log.debug("adding Host for directly connected.");
                     log.debug("client mac {} client vlan {} hostlocation {}",
-                            HexString.toHexString(clientMac.toBytes(), ":"),
-                            vlanId, hostLocation.toString());
+                              HexString.toHexString(clientMac.toBytes(), ":"),
+                              vlanId, hostLocation.toString());
 
                     // Replace the ip when dhcp server give the host new ip address
                     providerService.hostDetected(hostId, desc, false);
@@ -686,14 +686,14 @@
      * @param clientPacket client ethernet packet
      * @param clientInterfaces set of client side interfaces
      */
-     private InternalPacket processDhcp6PacketFromClient(PacketContext context,
+    private InternalPacket processDhcp6PacketFromClient(PacketContext context,
                                                         Ethernet clientPacket, Set<Interface> clientInterfaces) {
         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;
         }
 
@@ -704,25 +704,36 @@
         DHCP6 clientDhcp6 = (DHCP6) clientUdp.getPayload();
 
         boolean directConnFlag = directlyConnected(clientDhcp6);
+        Interface serverInterface = directConnFlag ? getServerInterface() : getIndirectServerInterface();
+        if (serverInterface == null) {
+            log.warn("Can't get {} server interface, ignore", directConnFlag ? "direct" : "indirect");
+            return null;
+        }
+        Ip6Address ipFacingServer = getFirstIpFromInterface(serverInterface);
+        MacAddress macFacingServer = serverInterface.mac();
+        if (ipFacingServer == null || macFacingServer == null) {
+            log.warn("No IP v6 address for server Interface {}", serverInterface);
+            return null;
+        }
 
         Ethernet etherReply = (Ethernet) clientPacket.clone();
-        etherReply.setSourceMACAddress(relayAgentMac);
+        etherReply.setSourceMACAddress(macFacingServer);
 
         if ((directConnFlag && this.dhcpConnectMac == null)  ||
                 !directConnFlag && this.indirectDhcpConnectMac == null && this.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());
+                             + "packet processing from client on port: {}",
+                     (this.dhcpGatewayIp == null) ? "server IP " + this.dhcpServerIp
+                             : "gateway IP " + this.dhcpGatewayIp,
+                     clientInterfaces.iterator().next().connectPoint());
 
             return null;
         }
 
         if (this.dhcpServerConnectPoint == null) {
             log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
-                    directConnFlag, this.dhcpServerConnectPoint, this.indirectDhcpServerConnectPoint);
+                     directConnFlag, this.dhcpServerConnectPoint, this.indirectDhcpServerConnectPoint);
             return null;
         }
 
@@ -731,7 +742,8 @@
 
         IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
         byte[] peerAddress = clientIpv6.getSourceAddress();
-        ipv6Packet.setSourceAddress(relayAgentIp.toOctets());
+        ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
+
         ipv6Packet.setDestinationAddress(this.dhcpServerIp.toOctets());
 
         UDP udpPacket = (UDP) ipv6Packet.getPayload();
@@ -744,10 +756,10 @@
         ConnectPoint clientConnectionPoint = context.inPacket().receivedFrom();
         VlanId vlanIdInUse = VlanId.vlanId(clientPacket.getVlanID());
         Interface clientInterface = interfaceService.getInterfacesByPort(clientConnectionPoint)
-                 .stream()
-                 .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
-                 .findFirst()
-                 .orElse(null);
+                .stream()
+                .filter(iface -> interfaceContainsVlan(iface, vlanIdInUse))
+                .findFirst()
+                .orElse(null);
 
         removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
 
@@ -755,42 +767,42 @@
         dhcp6Relay.setMsgType(DHCP6.MsgType.RELAY_FORW.value());
         // link address: server uses the address to identify the link on which the client
         // is located.
-         if (directConnFlag) {
-             dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
-             log.debug("direct connection: relayAgentIp obtained dynamically {}",
-                     HexString.toHexString(relayAgentIp.toOctets(), ":"));
+        if (directConnFlag) {
+            dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
+            log.debug("direct connection: relayAgentIp obtained dynamically {}",
+                      HexString.toHexString(relayAgentIp.toOctets(), ":"));
 
-         } else {
-             if (this.indirectDhcpServerIp == null) {
-                 log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
+        } else {
+            if (this.indirectDhcpServerIp == null) {
+                log.warn("indirect DhcpServerIp not available, use default DhcpServerIp {}",
                          HexString.toHexString(this.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) {
-                     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,
+            } else {
+                // Indirect case, replace destination to indirect dhcp server if exist
+                // Check if mac is obtained for valid server ip
+                if (this.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());
-                     return null;
-                 }
-                 etherReply.setDestinationMACAddress(this.indirectDhcpConnectMac);
-                 etherReply.setVlanID(this.indirectDhcpConnectVlan.toShort());
-                 ipv6Packet.setDestinationAddress(this.indirectDhcpServerIp.toOctets());
+                    return null;
+                }
+                etherReply.setDestinationMACAddress(this.indirectDhcpConnectMac);
+                etherReply.setVlanID(this.indirectDhcpConnectVlan.toShort());
+                ipv6Packet.setDestinationAddress(this.indirectDhcpServerIp.toOctets());
 
-             }
-             if (this.indirectRelayAgentIpFromCfg == null) {
-                 dhcp6Relay.setLinkAddress(relayAgentIp.toOctets());
-                 log.warn("indirect connection: relayAgentIp NOT availale from config file! Use dynamic. {}",
+            }
+            if (this.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(), ":"));
-             }
-         }
+            } 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.
@@ -822,15 +834,15 @@
         vlanIdBytes[0] = (byte) (clientPacket.getVlanID() & 0xff);
         vlanIdBytes[1] = (byte) ((clientPacket.getVlanID() >> 8) & 0xff);
         byte[] interfaceIdBytes = new byte[clientSoureMacBytes.length +
-                                           inPortStringBytes.length + vlanIdBytes.length];
+                inPortStringBytes.length + vlanIdBytes.length];
         log.debug("Length: interfaceIdBytes  {} clientSoureMacBytes {} inPortStringBytes {} vlan {}",
-                interfaceIdBytes.length, clientSoureMacBytes.length, inPortStringBytes.length,
-                vlanIdBytes.length);
+                  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);
+                         vlanIdBytes.length);
 
         interfaceId.setData(interfaceIdBytes);
         interfaceId.setLength((short) interfaceIdBytes.length);
@@ -876,16 +888,16 @@
         Boolean directConnFlag = directlyConnected(dhcp6Relay);
         ConnectPoint inPort = context.inPacket().receivedFrom();
         if ((directConnFlag || (!directConnFlag && this.indirectDhcpServerIp == null))
-             && !inPort.equals(this.dhcpServerConnectPoint)) {
+                && !inPort.equals(this.dhcpServerConnectPoint)) {
             log.warn("Receiving port {} is not the same as server connect point {} for direct or indirect-null",
-                    inPort, this.dhcpServerConnectPoint);
+                     inPort, this.dhcpServerConnectPoint);
             return null;
         }
 
         if (!directConnFlag && this.indirectDhcpServerIp != null &&
-                                !inPort.equals(this.indirectDhcpServerConnectPoint)) {
+                !inPort.equals(this.indirectDhcpServerConnectPoint)) {
             log.warn("Receiving port {} is not the same as server connect point {} for indirect",
-                    inPort, this.indirectDhcpServerConnectPoint);
+                     inPort, this.indirectDhcpServerConnectPoint);
             return null;
         }
 
@@ -928,7 +940,7 @@
         Set<Host> clients = hostService.getHostsByIp(peerAddress);
         if (clients.isEmpty()) {
             log.warn("There's no host found for this address {}",
-                    HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
+                     HexString.toHexString(dhcp6Relay.getPeerAddress(), ":"));
             log.warn("Let's look up interfaceId {}", HexString.toHexString(peerMac.toBytes(), ":"));
             clientMac = peerMac;
         } else {
@@ -954,11 +966,11 @@
         }
 
         DHCP6 embeddedDhcp6 = dhcp6Relay.getOptions().stream()
-                    .filter(opt -> opt instanceof Dhcp6RelayOption)
-                    .map(BasePacket::getPayload)
-                    .map(pld -> (DHCP6) pld)
-                    .findFirst()
-                    .orElse(null);
+                .filter(opt -> opt instanceof Dhcp6RelayOption)
+                .map(BasePacket::getPayload)
+                .map(pld -> (DHCP6) pld)
+                .findFirst()
+                .orElse(null);
 
 
         // add host or route
@@ -1271,6 +1283,57 @@
             this.indirectRelayAgentIpFromCfg = serverInfo.getRelayAgentIp6().orElse(null);
         }
     }
+
+    /**
+     * 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.
+     *
+     * @return the Interface facing to the server; null if not found
+     */
+    private Interface getServerInterface() {
+        if (dhcpServerConnectPoint == null || dhcpConnectVlan == null) {
+            return null;
+        }
+        return interfaceService.getInterfacesByPort(dhcpServerConnectPoint)
+                .stream()
+                .filter(iface -> interfaceContainsVlan(iface, dhcpConnectVlan))
+                .findFirst()
+                .orElse(null);
+    }
+
+    /**
+     * Gets Interface facing to the server for indirect hosts.
+     * Use default server Interface if indirect server not configured.
+     *
+     * @return the Interface facing to the server; null if not found
+     */
+    private Interface getIndirectServerInterface() {
+        if (indirectDhcpServerConnectPoint == null || indirectDhcpConnectVlan == null) {
+            return getServerInterface();
+        }
+        return interfaceService.getInterfacesByPort(indirectDhcpServerConnectPoint)
+                .stream()
+                .filter(iface -> interfaceContainsVlan(iface, indirectDhcpConnectVlan))
+                .findFirst()
+                .orElse(null);
+    }
+
     /**
      * Determind if an Interface contains a vlan id.
      *