Support Solicit-Map-Request (SMR) message for updated MapRegister

Change-Id: I9d0865d569557c904b161d124b4616111f0f9807
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 ac72d57..1e3be19 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
@@ -120,6 +120,20 @@
                 if (msg instanceof LispEncapsulatedControl) {
                     LispMessage innerMsg = extractLispMessage((LispEncapsulatedControl) msg);
                     if (innerMsg instanceof LispMapRequest) {
+
+                        IpAddress xtrAddress = valueOf(innerMsg.getSender().getAddress());
+
+                        router = routerFactory.getRouterInstance(xtrAddress);
+
+                        if (!router.isConnected()) {
+                            router.setChannel(ctx.channel());
+                            router.connectRouter();
+                        }
+
+                        router.setEidRecords(((LispMapRequest) innerMsg).getEids());
+                        router.setSubscribed(true);
+                        router.handleMessage(innerMsg);
+
                         LispMapResolver mapResolver = LispMapResolver.getInstance();
                         List<LispMessage> lispMessages =
                                 mapResolver.processMapRequest(msg);
@@ -136,8 +150,12 @@
                     LispMapRegister register = (LispMapRegister) msg;
                     IpAddress xtrAddress = valueOf(register.getSender().getAddress());
                     router = routerFactory.getRouterInstance(xtrAddress);
-                    router.setChannel(ctx.channel());
-                    router.connectRouter();
+
+                    if (!router.isConnected()) {
+                        router.setChannel(ctx.channel());
+                        router.connectRouter();
+                    }
+
                     router.handleMessage(register);
 
                     LispMapServer mapServer = LispMapServer.getInstance();
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java
index 95af771..573db1b 100644
--- a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/LispControllerImpl.java
@@ -87,6 +87,11 @@
                     "default value is 1")
     private int lispAuthKeyId = DEFAULT_LISP_AUTH_KEY_ID;
 
+    @Property(name = "enableSmr", boolValue = false,
+            label = "Enable to send SMR(Solicit Map Request) by map server; " +
+                    "By default SMR is not activated")
+    private boolean enableSmr = false;
+
     ExecutorService executorMessages =
             newFixedThreadPool(4, groupedThreads("onos/lisp", "event-stats-%d", log));
 
@@ -169,6 +174,16 @@
             log.info("Configured. LISP authentication method is configured to {}", lispAuthKeyId);
         }
         authConfig.updateLispAuthKeyId(lispAuthKeyId);
+
+        Boolean enableSmr = Tools.isPropertyEnabled(properties, "enableSmr");
+        if (enableSmr == null) {
+            log.info("Enable SMR is not configured, " +
+                    "using current value of {}", this.enableSmr);
+        } else {
+            this.enableSmr = enableSmr;
+            log.info("Configured. Sending SMR through map server is {}",
+                    this.enableSmr ? "enabled" : "disabled");
+        }
     }
 
     @Override
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 5e4d35a..ae766ba 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
@@ -15,12 +15,17 @@
  */
 package org.onosproject.lisp.ctl.impl;
 
+import com.google.common.collect.ImmutableList;
 import org.onlab.packet.IpAddress;
+import org.onosproject.lisp.ctl.LispRouter;
+import org.onosproject.lisp.ctl.LispRouterFactory;
+import org.onosproject.lisp.ctl.impl.util.LispMapUtil;
 import org.onosproject.lisp.msg.authentication.LispAuthenticationConfig;
 import org.onosproject.lisp.msg.protocols.DefaultLispInfoReply.DefaultInfoReplyBuilder;
 import org.onosproject.lisp.msg.protocols.DefaultLispInfoRequest.DefaultInfoRequestBuilder;
 import org.onosproject.lisp.msg.protocols.DefaultLispMapNotify.DefaultNotifyBuilder;
 import org.onosproject.lisp.msg.protocols.DefaultLispMapRegister.DefaultRegisterBuilder;
+import org.onosproject.lisp.msg.protocols.DefaultLispMapRequest.DefaultRequestBuilder;
 import org.onosproject.lisp.msg.protocols.LispEidRecord;
 import org.onosproject.lisp.msg.protocols.LispInfoReply;
 import org.onosproject.lisp.msg.protocols.LispInfoReply.InfoReplyBuilder;
@@ -28,8 +33,11 @@
 import org.onosproject.lisp.msg.protocols.LispInfoRequest.InfoRequestBuilder;
 import org.onosproject.lisp.msg.protocols.LispMapNotify;
 import org.onosproject.lisp.msg.protocols.LispMapNotify.NotifyBuilder;
+import org.onosproject.lisp.msg.protocols.LispMapRecord;
 import org.onosproject.lisp.msg.protocols.LispMapRegister;
 import org.onosproject.lisp.msg.protocols.LispMapRegister.RegisterBuilder;
+import org.onosproject.lisp.msg.protocols.LispMapRequest;
+import org.onosproject.lisp.msg.protocols.LispMapRequest.RequestBuilder;
 import org.onosproject.lisp.msg.protocols.LispMessage;
 import org.onosproject.lisp.msg.types.LispAfiAddress;
 import org.onosproject.lisp.msg.types.LispIpv4Address;
@@ -43,6 +51,8 @@
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 
 import static org.onlab.packet.IpAddress.valueOf;
 import static org.onosproject.lisp.msg.authentication.LispAuthenticationKeyEnum.valueOf;
@@ -63,6 +73,7 @@
     private static final String FAILED_TO_FORMULATE_NAT_MSG =
                                 "Fails during formulate NAT address.";
 
+    private boolean enableSmr = false;
 
     private LispMappingDatabase mapDb = LispMappingDatabase.getInstance();
     private LispAuthenticationConfig authConfig = LispAuthenticationConfig.getInstance();
@@ -76,6 +87,15 @@
     }
 
     /**
+     * Enable LISP Map server sends SMR(Solicit Map Request) message.
+     *
+     * @param enable whether enable or disable sending SMR
+     */
+    public void enableSmr(boolean enable) {
+        this.enableSmr = enable;
+    }
+
+    /**
      * Handles map-register message and replies with map-notify message.
      *
      * @param message map-register message
@@ -95,7 +115,19 @@
                                 new LispEidRecord(mapRecord.getMaskLength(),
                                                   mapRecord.getEidPrefixAfi());
 
-            mapDb.putMapRecord(eidRecord, mapRecord, register.isProxyMapReply());
+            LispMapRecord oldMapRecord = mapDb.getMapRecordByEidRecord(eidRecord,
+                    register.isProxyMapReply());
+            if (oldMapRecord == null) {
+                mapDb.putMapRecord(eidRecord, mapRecord, register.isProxyMapReply());
+            } else {
+                if (oldMapRecord.getMapVersionNumber() <= mapRecord.getMapVersionNumber()) {
+                    mapDb.putMapRecord(eidRecord, mapRecord, register.isProxyMapReply());
+
+                    if (enableSmr) {
+                        sendSmrMessage(eidRecord);
+                    }
+                }
+            }
         });
 
         // we only acknowledge back to ETR when want-map-notify bit is set to true
@@ -220,6 +252,50 @@
     }
 
     /**
+     * Sends SMR (Solicit Map Request) to their subscribers.
+     *
+     * @param eidRecord the updated EID
+     */
+    private void sendSmrMessage(LispEidRecord eidRecord) {
+
+        RequestBuilder builder = new DefaultRequestBuilder();
+
+        LispAfiAddress msAddress = null;
+        try {
+            msAddress = new LispIpv4Address(IpAddress.valueOf(InetAddress.getLocalHost()));
+        } catch (UnknownHostException e) {
+            log.warn("Source EID is not found, {}", e.getMessage());
+        }
+
+        LispMapRequest msg = builder.withIsSmr(true)
+                .withIsSmrInvoked(true)
+                .withIsProbe(false)
+                .withIsPitr(false)
+                .withIsAuthoritative(false)
+                .withIsMapDataPresent(false)
+                .withSourceEid(msAddress)
+                .withEidRecords(ImmutableList.of(eidRecord))
+                .build();
+
+        LispRouterFactory routerFactory = LispRouterFactory.getInstance();
+        Collection<LispRouter> routers = routerFactory.getRouters();
+        routers.forEach(router -> {
+            if (isInEidRecordRange(eidRecord, router.getEidRecords())) {
+                router.sendMessage(msg);
+            }
+        });
+    }
+
+    private boolean isInEidRecordRange(LispEidRecord originalRecord, List<LispEidRecord> records) {
+
+        for (LispEidRecord record : records) {
+            return LispMapUtil.isInRange(record, originalRecord) ||
+                    LispMapUtil.isInRange(originalRecord, record);
+        }
+        return false;
+    }
+
+    /**
      * Prevents object instantiation from external.
      */
     private static final class SingletonHelper {
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 4cfaf12..aff24a2 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
@@ -17,7 +17,6 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
-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;
@@ -31,6 +30,7 @@
 import java.util.Optional;
 
 import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.lisp.ctl.impl.util.LispMapUtil.isInRange;
 
 /**
  * A singleton class that stores EID-RLOC mapping information.
@@ -165,35 +165,6 @@
     }
 
     /**
-     * Generates CIDR style string from EID record.
-     *
-     * @param eidRecord EID record
-     * @return CIDR style string
-     */
-    private String cidrfy(LispEidRecord eidRecord) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(eidRecord.getPrefix().toString());
-        sb.append("/");
-        sb.append(eidRecord.getMaskLength());
-        return sb.toString();
-    }
-
-    /**
-     * Checks whether the EID record is included in the given EID record.
-     *
-     * @param origin  the EID record to be compared
-     * @param compare the EID record to compare
-     * @return boolean result
-     */
-    private boolean isInRange(LispEidRecord origin, LispEidRecord compare) {
-
-        IpPrefix originIpPrefix = IpPrefix.valueOf(cidrfy(origin));
-        IpPrefix compareIpPrefix = IpPrefix.valueOf(cidrfy(compare));
-
-        return originIpPrefix.contains(compareIpPrefix);
-    }
-
-    /**
      * Prevents object instantiation from external.
      */
     private static final class SingletonHelper {
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/util/LispMapUtil.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/util/LispMapUtil.java
new file mode 100644
index 0000000..34b6ff9
--- /dev/null
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/util/LispMapUtil.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-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.ctl.impl.util;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.lisp.msg.protocols.LispEidRecord;
+
+/**
+ * A LISP map utility class includes various useful methods.
+ */
+public final class LispMapUtil {
+
+    /**
+     * Prevents object instantiation from external.
+     */
+    private LispMapUtil() {
+    }
+
+    /**
+     * Generates CIDR style string from EID record.
+     *
+     * @param eidRecord EID record
+     * @return CIDR style string
+     */
+    public static String cidrfy(LispEidRecord eidRecord) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(eidRecord.getPrefix().toString());
+        sb.append("/");
+        sb.append(eidRecord.getMaskLength());
+        return sb.toString();
+    }
+
+    /**
+     * Checks whether the EID record is included in the given EID record.
+     *
+     * @param origin  the EID record to be compared
+     * @param compare the EID record to compare
+     * @return boolean result
+     */
+    public static boolean isInRange(LispEidRecord origin, LispEidRecord compare) {
+
+        IpPrefix originIpPrefix = IpPrefix.valueOf(cidrfy(origin));
+        IpPrefix compareIpPrefix = IpPrefix.valueOf(cidrfy(compare));
+
+        return originIpPrefix.contains(compareIpPrefix);
+    }
+}
\ No newline at end of file
diff --git a/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/util/package-info.java b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/util/package-info.java
new file mode 100644
index 0000000..0957591
--- /dev/null
+++ b/protocols/lisp/ctl/src/main/java/org/onosproject/lisp/ctl/impl/util/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017-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.
+ */
+/**
+ * LISP map utility package.
+ */
+package org.onosproject.lisp.ctl.impl.util;
\ No newline at end of file