[CORD-2066] Router to indirect IPv6 addresses are unresolvable

Change-Id: I8a6cbade1d995692de40a4b5418b8fa6c60f10af
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 d816a96..abcdcb6 100644
--- a/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
+++ b/apps/dhcprelay/src/main/java/org/onosproject/dhcprelay/Dhcp6HandlerImpl.java
@@ -631,6 +631,10 @@
                                    Ethernet clientPacket, IPv6 clientIpv6,
                                    Interface clientInterface) {
         log.debug("extractPrefix  enters {}", dhcp6Packet);
+        VlanId vlanId = clientInterface.vlan();
+        MacAddress clientMac = clientPacket.getSourceMAC();
+        log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
+
         // add host or route
         if (isDhcp6Release(dhcp6Packet)) {
             IpAddress ip = null;
@@ -638,13 +642,9 @@
                 // Add to host store if it is connected to network directly
                 ip = extractIpAddress(dhcp6Packet);
                 if (ip != null) {
-                    VlanId vlanId = clientInterface.vlan();
-                    MacAddress clientMac = clientPacket.getSourceMAC();
+
                     HostId hostId = HostId.hostId(clientMac, vlanId);
                     log.debug("remove Host {} ip for directly connected.", hostId.toString());
-
-                    log.debug("client mac {} client vlan {}", HexString.toHexString(clientMac.toBytes(), ":"), vlanId);
-
                     // Remove host's ip of  when dhcp release msg is received
                     providerService.removeIpFromHost(hostId, ip);
                 } else {
@@ -652,7 +652,13 @@
                 }
             } else {
                 // Remove from route store if it is not connected to network directly
-                IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, clientIpv6.getSourceAddress());
+                // pick out the first link-local ip address
+                IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
+                if (nextHopIp == null) {
+                    log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
+                            clientMac, vlanId);
+                    return;
+                }
 
                 DHCP6 leafDhcp = getDhcp6Leaf(dhcp6Packet);
                 ip = extractIpAddress(leafDhcp);
@@ -695,6 +701,7 @@
                                    MacAddress clientMac,
                                    Interface clientInterface) {
         log.debug("addHostOrRoute entered.");
+        VlanId vlanId = clientInterface.vlan();
         // add host or route
         if (isDhcp6Reply(dhcp6Relay)) {
             IpAddress ip = null;
@@ -705,7 +712,6 @@
                     Set<IpAddress> ips = Sets.newHashSet(ip);
 
                     // FIXME: we should use vlan id from original packet (solicit, request)
-                    VlanId vlanId = clientInterface.vlan();
                     HostId hostId = HostId.hostId(clientMac, vlanId);
                     Host host = hostService.getHost(hostId);
                     HostLocation hostLocation = new HostLocation(clientInterface.connectPoint(),
@@ -732,7 +738,13 @@
                 }
             } else {
                 // Add to route store if it does not connect to network directly
-                IpAddress nextHopIp = IpAddress.valueOf(IpAddress.Version.INET6, dhcp6Relay.getPeerAddress());
+                // pick out the first link-local ip address
+                IpAddress nextHopIp = getFirstIpByHost(clientMac, vlanId);
+                if (nextHopIp == null) {
+                    log.warn("Can't find link-local IP address of gateway mac {} vlanId {}",
+                            clientMac, vlanId);
+                    return;
+                }
 
                 DHCP6 leafDhcp = getDhcp6Leaf(embeddedDhcp6);
                 ip = extractIpAddress(leafDhcp);
@@ -809,69 +821,44 @@
          if (this.dhcpServerConnectPoint == null) {
              log.warn("DHCP6 server connection point direct {} directConn {} indirectConn {} is not set up yet",
                      directConnFlag, this.dhcpServerConnectPoint, this.indirectDhcpServerConnectPoint);
-             return null;
-         }
+            return null;
+        }
 
-         etherReply.setDestinationMACAddress(this.dhcpConnectMac);
-         etherReply.setVlanID(this.dhcpConnectVlan.toShort());
+        etherReply.setDestinationMACAddress(this.dhcpConnectMac);
+        etherReply.setVlanID(this.dhcpConnectVlan.toShort());
 
-         IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
-         Ip6Address peerAddress = null;
-         if (directConnFlag) {
-             peerAddress = Ip6Address.valueOf(ipv6Packet.getSourceAddress());
-         } else {
-             MacAddress gwOrClientMac = MacAddress.valueOf(clientPacket.getSourceMACAddress());
-             VlanId vlanId = VlanId.vlanId(clientPacket.getVlanID());
-             HostId gwOrClientHostId = HostId.hostId(gwOrClientMac, vlanId);
-             Host gwOrClientHost = hostService.getHost(gwOrClientHostId);
+        IPv6 ipv6Packet = (IPv6) etherReply.getPayload();
+        byte[] peerAddress = clientIpv6.getSourceAddress();
+        ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
+        ipv6Packet.setDestinationAddress(this.dhcpServerIp.toOctets());
 
-             if (gwOrClientHost == null) {
-                 log.warn("Can't find client gateway/server host {}", gwOrClientHostId);
-                 return null;
-             }
-             // pick out the first gloabl ip address
-             peerAddress = gwOrClientHost.ipAddresses().stream()
-                     .filter(IpAddress::isIp6).filter(ip6 -> !ip6.isLinkLocal())
-                     .map(IpAddress::getIp6Address).findFirst().orElse(null);
+        UDP udpPacket = (UDP) ipv6Packet.getPayload();
+        udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
+        DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
+        byte[] dhcp6PacketByte = dhcp6Packet.serialize();
 
-             if (peerAddress == null) {
-                 log.warn("Can't find client gateway/server for mac {} ip {}", gwOrClientMac,
-                         HexString.toHexString(ipv6Packet.getSourceAddress()));
-                 log.warn("Can't find IP address of client gateway/ClienHost address {} for peerAddress",
-                         gwOrClientHost);
-                 return null;
-             }
-         }
-         ipv6Packet.setSourceAddress(ipFacingServer.toOctets());
-         ipv6Packet.setDestinationAddress(this.dhcpServerIp.toOctets());
+        // notify onos and quagga to release PD
+        //releasePD(dhcp6Packet);
+        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);
 
-         UDP udpPacket = (UDP) ipv6Packet.getPayload();
-         udpPacket.setSourcePort(UDP.DHCP_V6_SERVER_PORT);
-         DHCP6 dhcp6Packet = (DHCP6) udpPacket.getPayload();
-         byte[] dhcp6PacketByte = dhcp6Packet.serialize();
+        removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
 
-         // notify onos and quagga to release PD
-         //releasePD(dhcp6Packet);
-         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);
+        DHCP6 dhcp6Relay = new DHCP6();
+        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(), ":"));
 
-         removeHostOrRoute(directConnFlag, dhcp6Packet, clientPacket, clientIpv6, clientInterface);
-
-         DHCP6 dhcp6Relay = new DHCP6();
-         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(), ":"));
-
-         } 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
@@ -893,76 +880,76 @@
                  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.
-         dhcp6Relay.setPeerAddress(peerAddress.toOctets());
-         List<Dhcp6Option> options = new ArrayList<Dhcp6Option>();
+        // 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));
-         }
+        // 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 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);
+        // 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);
+        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);
+        interfaceId.setData(interfaceIdBytes);
+        interfaceId.setLength((short) interfaceIdBytes.length);
 
-         options.add(interfaceId);
+        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);
+        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);
-             }
-         }
+        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);
+            }
+        }
     }
 
     /**
@@ -1514,4 +1501,30 @@
             flowObjectiveService.apply(deviceId, fwd);
         });
     }
+
+    /**
+     * Find first ipaddress for a given Host info i.e.  mac and vlan.
+     *
+     * @param clientMac client mac
+     * @param vlanId  packet's vlan
+     * @return next-hop link-local ipaddress for a given host
+     */
+    private IpAddress getFirstIpByHost(MacAddress clientMac, VlanId vlanId) {
+        IpAddress nextHopIp;
+        // pick out the first link-local ip address
+        HostId gwHostId = HostId.hostId(clientMac, vlanId);
+        Host gwHost = hostService.getHost(gwHostId);
+        if (gwHost == null) {
+            log.warn("Can't find gateway host for hostId {}", gwHostId);
+            return null;
+        }
+        nextHopIp = gwHost.ipAddresses()
+                .stream()
+                .filter(IpAddress::isIp6)
+                .filter(ip6 -> ip6.isLinkLocal())
+                .map(IpAddress::getIp6Address)
+                .findFirst()
+                .orElse(null);
+        return nextHopIp;
+    }
 }
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 eb24ae2..7efed1a 100644
--- a/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
+++ b/apps/dhcprelay/src/test/java/org/onosproject/dhcprelay/DhcpRelayManagerTest.java
@@ -178,11 +178,13 @@
                                                                        CLIENT2_VLAN_NATIVE);
     private static final VlanId CLIENT_BOGUS_VLAN = VlanId.vlanId("108");
 
-
     // Outer relay information
-    private static final Ip4Address OUTER_RELAY_IP = Ip4Address.valueOf("10.0.5.253");
+    private static final Ip4Address OUTER_RELAY_IP = Ip4Address.valueOf("10.0.6.253");
     private static final Ip6Address OUTER_RELAY_IP_V6 = Ip6Address.valueOf("2001:db8:1::4");
-    private static final Set<IpAddress> OUTER_RELAY_IPS = ImmutableSet.of(OUTER_RELAY_IP, OUTER_RELAY_IP_V6);
+    private static final Ip6Address OUTER_RELAY_LL_IP_V6 = Ip6Address.valueOf("fe80::200:0102:0304:0501");
+    private static final Set<IpAddress> OUTER_RELAY_IPS = ImmutableSet.of(OUTER_RELAY_IP,
+            OUTER_RELAY_IP_V6,
+            OUTER_RELAY_LL_IP_V6);
     private static final MacAddress OUTER_RELAY_MAC = MacAddress.valueOf("00:01:02:03:04:05");
     private static final VlanId OUTER_RELAY_VLAN = VlanId.NONE;
     private static final ConnectPoint OUTER_RELAY_CP = ConnectPoint.deviceConnectPoint("of:0000000000000001/2");
@@ -286,7 +288,20 @@
                 .andReturn(APP_ID).anyTimes();
 
         manager.hostService = createNiceMock(HostService.class);
-        expect(manager.hostService.getHostsByIp(anyObject())).andReturn(ImmutableSet.of(SERVER_HOST)).anyTimes();
+
+        expect(manager.hostService.getHostsByIp(OUTER_RELAY_IP_V6))
+                .andReturn(ImmutableSet.of(OUTER_RELAY_HOST)).anyTimes();
+        expect(manager.hostService.getHostsByIp(SERVER_IP))
+                .andReturn(ImmutableSet.of(SERVER_HOST)).anyTimes();
+        expect(manager.hostService.getHostsByIp(SERVER_IP_V6))
+                .andReturn(ImmutableSet.of(SERVER_HOST)).anyTimes();
+        expect(manager.hostService.getHostsByIp(GATEWAY_IP))
+                .andReturn(ImmutableSet.of(SERVER_HOST)).anyTimes();
+        expect(manager.hostService.getHostsByIp(GATEWAY_IP_V6))
+                .andReturn(ImmutableSet.of(SERVER_HOST)).anyTimes();
+        expect(manager.hostService.getHostsByIp(CLIENT_LL_IP_V6))
+                .andReturn(ImmutableSet.of(EXISTS_HOST)).anyTimes();
+
         expect(manager.hostService.getHost(OUTER_RELAY_HOST_ID)).andReturn(OUTER_RELAY_HOST).anyTimes();
 
         packetService = new MockPacketService();
@@ -641,7 +656,7 @@
                                                                     CLIENT_CP, CLIENT_MAC,
                                                                     CLIENT_VLAN,
                                                                     INTERFACE_IP_V6.ipAddress().getIp6Address(),
-                                                                    0));
+                                                                    0, false, CLIENT_VLAN));
         verify(mockHostProviderService);
         assertEquals(0, mockRouteStore.routes.size());
 
@@ -685,7 +700,7 @@
                 CLIENT2_MAC,
                 CLIENT2_VLAN,
                 OUTER_RELAY_IP_V6,
-                1));
+                1, false, CLIENT2_VLAN));
 
         // won't trigger the host provider service
         verify(mockHostProviderService);
@@ -750,9 +765,11 @@
         packetService.processPacket(new TestDhcp6ReplyPacketContext(DHCP6.MsgType.REPLY.value(),
                 CLIENT2_CP,
                 CLIENT2_MAC,
-                CLIENT_BOGUS_VLAN,  // mismatch
+                CLIENT2_VLAN,
                 INTERFACE_IP_V6.ipAddress().getIp6Address(),
-                1));
+                1, true,
+                CLIENT_BOGUS_VLAN // mismatch
+        ));
 
         // won't trigger the host provider service
         verify(mockHostProviderService);
@@ -789,7 +806,7 @@
                 new TestDhcp6ReplyPacketContext(DHCP6.MsgType.REPLY.value(),
                                                 CLIENT_DH_CP, CLIENT_MAC, CLIENT_VLAN,
                                                 INTERFACE_IP_V6.ipAddress().getIp6Address(),
-                                                0);
+                                                0, false, CLIENT_VLAN);
         reset(manager.hostService);
         expect(manager.hostService.getHostsByIp(CLIENT_LL_IP_V6)).andReturn(ImmutableSet.of(EXISTS_HOST)).anyTimes();
 
@@ -1377,7 +1394,7 @@
             Ethernet eth = new Ethernet();
             if (relayLevel > 0) {
                 eth.setEtherType(Ethernet.TYPE_IPV6)
-                        .setVlanID(vlanId.toShort())
+                        .setVlanID(OUTER_RELAY_VLAN.toShort())
                         .setSourceMACAddress(OUTER_RELAY_MAC)
                         .setDestinationMACAddress(MacAddress.valueOf("33:33:00:01:00:02"))
                         .setPayload(ipv6);
@@ -1407,7 +1424,7 @@
 
         public TestDhcp6ReplyPacketContext(byte msgType, ConnectPoint clientCp, MacAddress clientMac,
                                         VlanId clientVlan, Ip6Address clientGwAddr,
-                                        int relayLevel) {
+                                        int relayLevel, boolean overWriteFlag, VlanId overWriteVlan) {
             super(0, null, null, false);
 
 
@@ -1415,7 +1432,14 @@
             buildDhcp6Packet(dhcp6Payload, msgType,
                     IP_FOR_CLIENT_V6,
                     PREFIX_FOR_CLIENT_V6);
-            byte[] interfaceId = buildInterfaceId(clientMac, clientVlan.toShort(), clientCp);
+            byte[] interfaceId = null;
+            if (relayLevel > 0) {
+                interfaceId = buildInterfaceId(OUTER_RELAY_MAC,
+                                               overWriteFlag ? overWriteVlan.toShort() : OUTER_RELAY_VLAN.toShort(),
+                                                OUTER_RELAY_CP);
+            } else {
+                interfaceId = buildInterfaceId(clientMac, clientVlan.toShort(), clientCp);
+            }
             DHCP6 dhcp6 = new DHCP6();
             buildRelayMsg(dhcp6, DHCP6.MsgType.RELAY_REPL.value(),
                           INTERFACE_IP_V6.ipAddress().getIp6Address(),