[ONOS-5710] Process Map-Request msg when proxy bit is set to false

Change-Id: I0ceef00c67ab266b01dec121fe6a4fe1f3dca3e5
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispChannelHandler.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispChannelHandler.java
index a36031e..b5e0105 100644
--- a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispChannelHandler.java
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispChannelHandler.java
@@ -30,6 +30,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
+
 /**
  * Channel handler deals with the xTR connection and dispatches xTR messages
  * to the appropriate locations.
@@ -42,18 +44,22 @@
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
 
         try {
-            // first we need to check whether this is an ECM
+
+            // process map-request message that is encapsulated in ECM
             if (msg instanceof LispEncapsulatedControl) {
                 LispMessage innerMsg = extractLispMessage((LispEncapsulatedControl) msg);
                 if (innerMsg instanceof LispMapRequest) {
                     LispMapResolver mapResolver = LispMapResolver.getInstance();
-                    LispMessage lispMessage =
+                    List<LispMessage> lispMessages =
                             mapResolver.processMapRequest((LispEncapsulatedControl) msg);
 
-                    ctx.writeAndFlush(lispMessage);
+                    if (lispMessages != null) {
+                        lispMessages.forEach(ctx::writeAndFlush);
+                    }
                 }
             }
 
+            // process map-register message
             if (msg instanceof LispMapRegister) {
                 LispMapServer mapServer = LispMapServer.getInstance();
                 LispMapNotify mapNotify =
@@ -64,11 +70,14 @@
                 }
             }
 
+            // process info-request message
             if (msg instanceof LispInfoRequest) {
                 LispMapServer mapServer = LispMapServer.getInstance();
                 LispInfoReply infoReply = mapServer.processInfoRequest((LispInfoRequest) msg);
 
-                ctx.writeAndFlush(infoReply);
+                if (infoReply != null) {
+                    ctx.writeAndFlush(infoReply);
+                }
             }
         } finally {
             // try to remove the received message form the buffer
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapResolver.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapResolver.java
index 5784a07..fc22f80 100644
--- a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapResolver.java
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapResolver.java
@@ -15,14 +15,19 @@
  */
 package org.onosproject.lisp.ctl.impl;
 
+import com.google.common.collect.Lists;
+import org.onosproject.lisp.msg.protocols.DefaultLispEncapsulatedControl.DefaultEcmBuilder;
 import org.onosproject.lisp.msg.protocols.DefaultLispMapReply.DefaultReplyBuilder;
 import org.onosproject.lisp.msg.protocols.LispEncapsulatedControl;
+import org.onosproject.lisp.msg.protocols.LispEncapsulatedControl.EcmBuilder;
+import org.onosproject.lisp.msg.protocols.LispLocatorRecord;
 import org.onosproject.lisp.msg.protocols.LispMapRecord;
-import org.onosproject.lisp.msg.protocols.LispMapReply;
 import org.onosproject.lisp.msg.protocols.LispMapReply.ReplyBuilder;
-import org.onosproject.lisp.msg.protocols.LispMessage;
-import org.onosproject.lisp.msg.types.LispIpAddress;
 import org.onosproject.lisp.msg.protocols.LispMapRequest;
+import org.onosproject.lisp.msg.protocols.LispMapReply;
+import org.onosproject.lisp.msg.protocols.LispMessage;
+import org.onosproject.lisp.msg.types.LispAfiAddress;
+import org.onosproject.lisp.msg.types.LispIpAddress;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,13 +43,20 @@
 
     private static final Logger log = LoggerFactory.getLogger(LispMapResolver.class);
 
+    private static final int ECM_DST_PORT = 4342;
+    private static final String NO_ITR_RLOCS_MSG =
+                                "No ITR RLOC is found, cannot respond to ITR.";
+    private static final String NO_ETR_RLOCS_MSG =
+                                "No ETR RLOC is found, cannot relay to ETR.";
+    private static final String NO_MAP_INFO_MSG  = "Map information is not found.";
+
     private LispMappingDatabase mapDb = LispMappingDatabase.getInstance();
 
     // non-instantiable (except for our Singleton)
     private LispMapResolver() {
     }
 
-    public static LispMapResolver getInstance() {
+    static LispMapResolver getInstance() {
         return SingletonHelper.INSTANCE;
     }
 
@@ -54,38 +66,145 @@
      * @param message encapsulated control message
      * @return map-reply message
      */
-    public LispMessage processMapRequest(LispMessage message) {
+    List<LispMessage> processMapRequest(LispMessage message) {
 
         LispEncapsulatedControl ecm = (LispEncapsulatedControl) message;
         LispMapRequest request = (LispMapRequest) ecm.getControlMessage();
 
-        // build map-reply message
+        List<LispMapRecord> mapReplyRecords =
+                mapDb.getMapRecordByEidRecords(request.getEids(), true);
+
+        List<LispMapRecord> mapRequestRecords =
+                mapDb.getMapRecordByEidRecords(request.getEids(), false);
+
+        if (mapReplyRecords.size() + mapRequestRecords.size() == 0) {
+
+            // TODO: need to generate map-reply with configured native-forward action
+            log.warn(NO_MAP_INFO_MSG);
+
+        } else {
+
+            if (mapReplyRecords.size() > 0) {
+
+                List<LispMessage> mapReplies = Lists.newArrayList();
+
+                // build map-reply message based on map-request from ITR
+                ReplyBuilder replyBuilder = initMapReplyBuilder(request);
+                replyBuilder.withMapRecords(mapReplyRecords);
+
+                List<InetSocketAddress> addresses =
+                                        getItrAddresses(request.getItrRlocs(),
+                                                ecm.innerUdp().getSourcePort());
+
+                addresses.forEach(address -> {
+                    if (address != null) {
+                        LispMapReply reply = replyBuilder.build();
+                        reply.configSender(address);
+                        mapReplies.add(reply);
+                    } else {
+                        log.warn(NO_ITR_RLOCS_MSG);
+                    }
+                });
+
+                return mapReplies;
+            }
+
+            if (mapRequestRecords.size() > 0) {
+
+                List<LispMessage> ecms = Lists.newArrayList();
+
+                // re-encapsulate encapsulated control message from ITR
+                List<InetSocketAddress> addresses =
+                                getEtrAddresses(mapRequestRecords, ECM_DST_PORT);
+
+                addresses.forEach(address -> {
+                    if (address != null) {
+                        LispEncapsulatedControl reencapEcm = cloneEcm(ecm);
+                        reencapEcm.configSender(address);
+                        ecms.add(reencapEcm);
+                    } else {
+                        log.warn(NO_ETR_RLOCS_MSG);
+                    }
+                });
+
+                return ecms;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Initializes MapReply builder without specifying map records.
+     *
+     * @param request received map request from ITR
+     * @return initialized MapReply builder
+     */
+    private ReplyBuilder initMapReplyBuilder(LispMapRequest request) {
         ReplyBuilder replyBuilder = new DefaultReplyBuilder();
         replyBuilder.withNonce(request.getNonce());
         replyBuilder.withIsEtr(false);
         replyBuilder.withIsSecurity(false);
         replyBuilder.withIsProbe(request.isProbe());
 
-        List<LispMapRecord> mapRecords = mapDb.getMapRecordByEidRecords(request.getEids());
+        return replyBuilder;
+    }
 
-        if (mapRecords.size() == 0) {
-            log.warn("Map information is not found.");
-        } else {
-            replyBuilder.withMapRecords(mapRecords);
+    /**
+     * Clones ECM from original ECM.
+     *
+     * @param ecm original ECM
+     * @return cloned ECM
+     */
+    private LispEncapsulatedControl cloneEcm(LispEncapsulatedControl ecm) {
+        EcmBuilder ecmBuilder = new DefaultEcmBuilder();
+        ecmBuilder.innerLispMessage(ecm.getControlMessage());
+        ecmBuilder.isSecurity(ecm.isSecurity());
+        ecmBuilder.innerIpHeader(ecm.innerIpHeader());
+        ecmBuilder.innerUdpHeader(ecm.innerUdp());
+
+        return ecmBuilder.build();
+    }
+
+    /**
+     * Obtains a collection of valid ITR addresses with a port number specified.
+     * These addresses will be used to acknowledge map-reply to ITR.
+     *
+     * @param itrRlocs a collection of ITR RLOCs
+     * @param port     port number
+     * @return a collection of valid ITR addresses with a port number specified
+     */
+    private List<InetSocketAddress> getItrAddresses(List<LispAfiAddress> itrRlocs,
+                                                    int port) {
+        List<InetSocketAddress> addresses = Lists.newArrayList();
+        for (LispAfiAddress itrRloc : itrRlocs) {
+            addresses.add(new InetSocketAddress(((LispIpAddress)
+                    itrRloc).getAddress().toInetAddress(), port));
         }
+        return addresses;
+    }
 
-        LispMapReply reply = replyBuilder.build();
+    /**
+     * Obtains a collection of valid ETR addresses with a port number specified.
+     * These addresses will be used to relay map-request to ETR.
+     *
+     * @param mapRecords a collection of map records
+     * @param port       port number
+     * @return a collection of valid ETR addresses with a port number specified
+     */
+    private List<InetSocketAddress> getEtrAddresses(List<LispMapRecord> mapRecords,
+                                                    int port) {
+        List<InetSocketAddress> addresses = Lists.newArrayList();
+        for (LispMapRecord mapRecord : mapRecords) {
 
-        if (request.getItrRlocs() != null && request.getItrRlocs().size() > 0) {
-            LispIpAddress itr = (LispIpAddress) request.getItrRlocs().get(0);
-            InetSocketAddress address = new InetSocketAddress(itr.getAddress().toInetAddress(),
-                    ecm.innerUdp().getSourcePort());
-            reply.configSender(address);
-        } else {
-            log.warn("No ITR RLOC is found, cannot respond back to ITR.");
+            // we only select the first locator record in all cases...
+            LispLocatorRecord locatorRecord = mapRecord.getLocators().get(0);
+            if (locatorRecord != null) {
+                addresses.add(new InetSocketAddress(((LispIpAddress)
+                                locatorRecord.getLocatorAfi()).getAddress()
+                                                    .toInetAddress(), port));
+            }
         }
-
-        return reply;
+        return addresses;
     }
 
     /**
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapServer.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapServer.java
index 58792a2..faac48d 100644
--- a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapServer.java
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMapServer.java
@@ -44,6 +44,7 @@
 import java.net.UnknownHostException;
 import java.util.Arrays;
 
+import static org.onlab.packet.IpAddress.valueOf;
 import static org.onosproject.lisp.msg.authentication.LispAuthenticationKeyEnum.valueOf;
 
 /**
@@ -52,10 +53,16 @@
  */
 public final class LispMapServer {
 
+    private static final Logger log = LoggerFactory.getLogger(LispMapServer.class);
+
     private static final int MAP_NOTIFY_PORT = 4342;
     private static final int INFO_REPLY_PORT = 4342;
 
-    private static final Logger log = LoggerFactory.getLogger(LispMapServer.class);
+    private static final String INVALID_AUTHENTICATION_DATA_MSG =
+                                "Unmatched authentication data of {}.";
+    private static final String FAILED_TO_FORMULATE_NAT_MSG =
+                                "Fails during formulate NAT address.";
+
 
     private LispMappingDatabase mapDb = LispMappingDatabase.getInstance();
     private LispAuthenticationConfig authConfig = LispAuthenticationConfig.getInstance();
@@ -64,7 +71,7 @@
     private LispMapServer() {
     }
 
-    public static LispMapServer getInstance() {
+    static LispMapServer getInstance() {
         return SingletonHelper.INSTANCE;
     }
 
@@ -74,21 +81,21 @@
      * @param message map-register message
      * @return map-notify message
      */
-    public LispMapNotify processMapRegister(LispMessage message) {
+    LispMapNotify processMapRegister(LispMessage message) {
 
         LispMapRegister register = (LispMapRegister) message;
 
         if (!checkMapRegisterAuthData(register)) {
-            log.warn("Unmatched authentication data of Map-Register");
+            log.warn(INVALID_AUTHENTICATION_DATA_MSG, "Map-Register");
             return null;
         }
 
         register.getMapRecords().forEach(mapRecord -> {
             LispEidRecord eidRecord =
-                    new LispEidRecord(mapRecord.getMaskLength(),
-                                      mapRecord.getEidPrefixAfi());
+                                new LispEidRecord(mapRecord.getMaskLength(),
+                                                  mapRecord.getEidPrefixAfi());
 
-            mapDb.putMapRecord(eidRecord, mapRecord);
+            mapDb.putMapRecord(eidRecord, mapRecord, register.isProxyMapReply());
         });
 
         // we only acknowledge back to ETR when want-map-notify bit is set to true
@@ -119,23 +126,23 @@
      * @param message info-request message
      * @return info-reply message
      */
-    public LispInfoReply processInfoRequest(LispMessage message) {
+    LispInfoReply processInfoRequest(LispMessage message) {
         LispInfoRequest request = (LispInfoRequest) message;
 
         if (!checkInfoRequestAuthData(request)) {
-            log.warn("Unmatched authentication data of Info-Request");
+            log.warn(INVALID_AUTHENTICATION_DATA_MSG, "Info-Request");
             return null;
         }
 
         NatAddressBuilder natBuilder = new NatAddressBuilder();
         try {
             LispAfiAddress msAddress =
-                    new LispIpv4Address(IpAddress.valueOf(InetAddress.getLocalHost()));
+                        new LispIpv4Address(valueOf(InetAddress.getLocalHost()));
             natBuilder.withMsRlocAddress(msAddress);
             natBuilder.withMsUdpPortNumber((short) INFO_REPLY_PORT);
 
             // try to extract global ETR RLOC address from info-request
-            IpAddress globalRlocIp = IpAddress.valueOf(request.getSender().getAddress());
+            IpAddress globalRlocIp = valueOf(request.getSender().getAddress());
             LispAfiAddress globalRlocAddress;
             if (globalRlocIp.isIp4()) {
                 globalRlocAddress = new LispIpv4Address(globalRlocIp);
@@ -149,7 +156,7 @@
             // TODO: need to specify RTR addresses
 
         } catch (UnknownHostException e) {
-            log.warn("Fails during formulate NAT address", e);
+            log.warn(FAILED_TO_FORMULATE_NAT_MSG, e);
         }
 
         InfoReplyBuilder replyBuilder = new DefaultInfoReplyBuilder();
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMappingDatabase.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMappingDatabase.java
index f4f421b..4545542 100644
--- a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMappingDatabase.java
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispMappingDatabase.java
@@ -20,8 +20,10 @@
 import org.onlab.packet.IpPrefix;
 import org.onosproject.lisp.ctl.impl.map.ExpireMap;
 import org.onosproject.lisp.ctl.impl.map.ExpireHashMap;
+import org.onosproject.lisp.msg.protocols.DefaultLispProxyMapRecord.DefaultMapWithProxyBuilder;
 import org.onosproject.lisp.msg.protocols.LispEidRecord;
 import org.onosproject.lisp.msg.protocols.LispMapRecord;
+import org.onosproject.lisp.msg.protocols.LispProxyMapRecord;
 import org.onosproject.lisp.msg.types.LispAfiAddress;
 
 import java.util.List;
@@ -34,7 +36,7 @@
 
     private static final long MINUTE_TO_MS_UNIT = 60 * 1000;
 
-    private ExpireMap<LispEidRecord, LispMapRecord> map = new ExpireHashMap<>();
+    private ExpireMap<LispEidRecord, LispProxyMapRecord> map = new ExpireHashMap<>();
 
     /**
      * Prevents object instantiation from external.
@@ -54,11 +56,17 @@
     /**
      * Inserts a new EID-RLOC mapping record.
      *
-     * @param eid  endpoint identifier
-     * @param rloc route locator record
+     * @param eid           endpoint identifier
+     * @param rloc          route locator record
+     * @param proxyMapReply proxy map reply flag
      */
-    public void putMapRecord(LispEidRecord eid, LispMapRecord rloc) {
-        map.put(eid, rloc, rloc.getRecordTtl() * MINUTE_TO_MS_UNIT);
+    public void putMapRecord(LispEidRecord eid, LispMapRecord rloc,
+                             boolean proxyMapReply) {
+        LispProxyMapRecord mapWithProxy = new DefaultMapWithProxyBuilder()
+                .withMapRecord(rloc)
+                .withIsProxyMapReply(proxyMapReply)
+                .build();
+        map.put(eid, mapWithProxy, rloc.getRecordTtl() * MINUTE_TO_MS_UNIT);
     }
 
     /**
@@ -81,16 +89,19 @@
     }
 
     /**
-     * Obtains an EID-RLOC mapping record with given EID record.
+     * Obtains an EID-RLOC mapping record in accordance with the proxy map reply
+     * flag bit and EID record.
      *
-     * @param eid endpoint identifier record
+     * @param eid           endpoint identifier record
+     * @param proxyMapReply proxy map reply flag
      * @return an EID-RLOC mapping record
      */
-    public LispMapRecord getMapRecordByEidRecord(LispEidRecord eid) {
+    public LispMapRecord getMapRecordByEidRecord(LispEidRecord eid, boolean proxyMapReply) {
 
         for (LispEidRecord key : map.keySet()) {
-            if (isInRange(key, eid)) {
-                return map.get(key);
+            if (isInRange(key, eid) && map.get(key) != null
+                    && map.get(key).isProxyMapReply() == proxyMapReply) {
+                return map.get(key).getMapRecord();
             }
         }
 
@@ -98,15 +109,18 @@
     }
 
     /**
-     * Obtains a collection of EID-RLOC mapping records with given EID records.
+     * Obtains a collection of EID-RLOC mapping record in accordance with the
+     * proxy map reply flag bit and EID record.
      *
-     * @param eids endpoint identifier records
+     * @param eids          endpoint identifier records
+     * @param proxyMapReply proxy map reply flag
      * @return a collection of EID-RLOC mapping records
      */
-    public List<LispMapRecord> getMapRecordByEidRecords(List<LispEidRecord> eids) {
+    public List<LispMapRecord> getMapRecordByEidRecords(List<LispEidRecord> eids,
+                                                        boolean proxyMapReply) {
         List<LispMapRecord> mapRecords = Lists.newArrayList();
         eids.forEach(eidRecord -> {
-            LispMapRecord mapRecord = getMapRecordByEidRecord(eidRecord);
+            LispMapRecord mapRecord = getMapRecordByEidRecord(eidRecord, proxyMapReply);
             if (mapRecord != null) {
                 mapRecords.add(mapRecord);
             }
@@ -123,7 +137,8 @@
     public LispMapRecord getMapRecordByEidAddress(LispAfiAddress address) {
         Optional<LispEidRecord> eidRecord =
                 map.keySet().stream().filter(k -> k.getPrefix().equals(address)).findFirst();
-        return eidRecord.map(lispEidRecord -> map.get(lispEidRecord)).orElse(null);
+        return eidRecord.map(lispEidRecord ->
+                map.get(lispEidRecord).getMapRecord()).orElse(null);
     }
 
     /**
diff --git a/protocols/lisp/ctl/src/test/java/org/onosproject/lisp/ctl/impl/LispMappingDatabaseTest.java b/protocols/lisp/ctl/src/test/java/org/onosproject/lisp/ctl/impl/LispMappingDatabaseTest.java
index 2c2142a..6f6c603 100644
--- a/protocols/lisp/ctl/src/test/java/org/onosproject/lisp/ctl/impl/LispMappingDatabaseTest.java
+++ b/protocols/lisp/ctl/src/test/java/org/onosproject/lisp/ctl/impl/LispMappingDatabaseTest.java
@@ -106,9 +106,9 @@
         builder3.withRecordTtl(60);
         LispMapRecord mapRecord3 = builder3.build();
 
-        mapDb.putMapRecord(eidRecord1, mapRecord1);
-        mapDb.putMapRecord(eidRecord2, mapRecord2);
-        mapDb.putMapRecord(eidRecord3, mapRecord3);
+        mapDb.putMapRecord(eidRecord1, mapRecord1, true);
+        mapDb.putMapRecord(eidRecord2, mapRecord2, true);
+        mapDb.putMapRecord(eidRecord3, mapRecord3, true);
     }
 
     @Test
@@ -116,7 +116,7 @@
         byte cidr32 = (byte) 32;
         LispIpv4Address eid = new LispIpv4Address(IpAddress.valueOf("10.1.1.1"));
         LispEidRecord record = new LispEidRecord(cidr32, eid);
-        LispMapRecord mapRecord = mapDb.getMapRecordByEidRecord(record);
+        LispMapRecord mapRecord = mapDb.getMapRecordByEidRecord(record, true);
 
         assertThat("Failed to fetch the RLOCs with /32 EID record",
                     mapRecord.getLocatorCount(), is(3));
@@ -127,12 +127,12 @@
         byte cidr32 = (byte) 32;
         LispIpv4Address eid = new LispIpv4Address(IpAddress.valueOf("10.1.2.1"));
         LispEidRecord record32 = new LispEidRecord(cidr32, eid);
-        LispMapRecord mapRecord32 = mapDb.getMapRecordByEidRecord(record32);
+        LispMapRecord mapRecord32 = mapDb.getMapRecordByEidRecord(record32, true);
 
         byte cidr24 = (byte) 24;
         LispIpv4Address eid24 = new LispIpv4Address(IpAddress.valueOf("10.1.2.0"));
         LispEidRecord record24 = new LispEidRecord(cidr24, eid24);
-        LispMapRecord mapRecord24 = mapDb.getMapRecordByEidRecord(record24);
+        LispMapRecord mapRecord24 = mapDb.getMapRecordByEidRecord(record24, true);
 
         assertThat("Failed to fetch the RLOCs with /32 EID record",
                     mapRecord32.getLocatorCount(), is(2));
@@ -145,17 +145,17 @@
         byte cidr32 = (byte) 32;
         LispIpv4Address eid = new LispIpv4Address(IpAddress.valueOf("10.2.1.1"));
         LispEidRecord record32 = new LispEidRecord(cidr32, eid);
-        LispMapRecord mapRecord32 = mapDb.getMapRecordByEidRecord(record32);
+        LispMapRecord mapRecord32 = mapDb.getMapRecordByEidRecord(record32, true);
 
         byte cidr24 = (byte) 24;
         LispIpv4Address eid24 = new LispIpv4Address(IpAddress.valueOf("10.2.1.0"));
         LispEidRecord record24 = new LispEidRecord(cidr24, eid24);
-        LispMapRecord mapRecord24 = mapDb.getMapRecordByEidRecord(record24);
+        LispMapRecord mapRecord24 = mapDb.getMapRecordByEidRecord(record24, true);
 
         byte cidr16 = (byte) 16;
         LispIpv4Address eid16 = new LispIpv4Address(IpAddress.valueOf("10.2.0.0"));
         LispEidRecord record16 = new LispEidRecord(cidr16, eid16);
-        LispMapRecord mapRecord16 = mapDb.getMapRecordByEidRecord(record16);
+        LispMapRecord mapRecord16 = mapDb.getMapRecordByEidRecord(record16, true);
 
         assertThat("Failed to fetch the RLOCs with /32 EID record",
                     mapRecord32.getLocatorCount(), is(1));
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispProxyMapRecord.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispProxyMapRecord.java
new file mode 100644
index 0000000..ac67130
--- /dev/null
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispProxyMapRecord.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.lisp.msg.protocols;
+
+/**
+ * A default implementation class of LispMapWithProxy interface.
+ */
+public final class DefaultLispProxyMapRecord implements LispProxyMapRecord {
+
+    private final LispMapRecord mapRecord;
+    private final boolean proxyMapReply;
+
+    private DefaultLispProxyMapRecord(LispMapRecord mapRecord, boolean proxyMapReply) {
+        this.mapRecord = mapRecord;
+        this.proxyMapReply = proxyMapReply;
+    }
+
+    @Override
+    public LispMapRecord getMapRecord() {
+        return mapRecord;
+    }
+
+    @Override
+    public boolean isProxyMapReply() {
+        return proxyMapReply;
+    }
+
+    /**
+     * A default builder class that builds MapWithProxy object.
+     */
+    public static final class DefaultMapWithProxyBuilder implements MapWithProxyBuilder {
+
+        private LispMapRecord mapRecord;
+        private boolean proxyMapReply;
+
+        @Override
+        public MapWithProxyBuilder withMapRecord(LispMapRecord mapRecord) {
+            this.mapRecord = mapRecord;
+            return this;
+        }
+
+        @Override
+        public MapWithProxyBuilder withIsProxyMapReply(boolean proxyMapReply) {
+            this.proxyMapReply = proxyMapReply;
+            return this;
+        }
+
+        @Override
+        public LispProxyMapRecord build() {
+            return new DefaultLispProxyMapRecord(mapRecord, proxyMapReply);
+        }
+    }
+}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispProxyMapRecord.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispProxyMapRecord.java
new file mode 100644
index 0000000..913f7c6
--- /dev/null
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispProxyMapRecord.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.lisp.msg.protocols;
+
+/**
+ * An interface that wraps LispMapRecord with proxy-bit flag.
+ */
+public interface LispProxyMapRecord {
+
+    /**
+     * Obtains LISP map record.
+     *
+     * @return LISP map record
+     */
+    LispMapRecord getMapRecord();
+
+    /**
+     * Obtains proxy-map-reply flag.
+     *
+     * @return proxy-map-reply flag
+     */
+    boolean isProxyMapReply();
+
+    /**
+     * A builder of LISP map with proxy flag internal data structure.
+     */
+    interface MapWithProxyBuilder {
+
+        /**
+         * Sets LISP map record.
+         *
+         * @param mapRecord map record
+         * @return MapWithProxyBuilder object
+         */
+        MapWithProxyBuilder withMapRecord(LispMapRecord mapRecord);
+
+        /**
+         * Sets isProxyMapReply flag.
+         *
+         * @param isProxyMapReply isProxyMapReply flag
+         * @return MapWithProxyBuilder object
+         */
+        MapWithProxyBuilder withIsProxyMapReply(boolean isProxyMapReply);
+
+        /**
+         * Builds LISP map with proxy data object.
+         *
+         * @return LISP map with proxy data object
+         */
+        LispProxyMapRecord build();
+    }
+}