Adding netcfg listener to disconnect switches when certificate is
updated or removed.

Change-Id: I04b170aec328b4c91a6d699ff128347d9a148736
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
index 92358c4..70de681 100644
--- a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
@@ -50,19 +50,23 @@
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManagerFactory;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.security.DigestInputStream;
 import java.security.KeyManagementException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
+import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Dictionary;
@@ -272,13 +276,25 @@
         return oldValue != this.workerThreads; // restart if number of threads has changed
     }
 
-    private static class TlsParams {
-        TlsMode mode;
-        String ksLocation;
-        String tsLocation;
-        String ksPwd;
-        String tsPwd;
-        //TODO add the hash of the keystore file contents, so that we restart if the keystore has changed
+    static class TlsParams {
+        final TlsMode mode;
+        final String ksLocation;
+        final String tsLocation;
+        final String ksPwd;
+        final String tsPwd;
+        final byte[] ksSignature;
+        final byte[] tsSignature;
+
+        TlsParams(TlsMode mode, String ksLocation, String tsLocation,
+                  String ksPwd, String tsPwd) {
+            this.mode = mode;
+            this.ksLocation = ksLocation;
+            this.tsLocation = tsLocation;
+            this.ksPwd = ksPwd;
+            this.tsPwd = tsPwd;
+            this.ksSignature = getSha1Checksum(ksLocation);
+            this.tsSignature = getSha1Checksum(tsLocation);
+        }
 
         public char[] ksPwd() {
             return ksPwd.toCharArray();
@@ -292,9 +308,34 @@
             return TLS_ENABLED.contains(mode);
         }
 
+        public byte[] getSha1Checksum(String filepath) {
+            if (filepath == null) {
+                return new byte[0];
+            }
+            try {
+                MessageDigest digest = MessageDigest.getInstance("SHA1");
+                File f = new File(filepath);
+                FileInputStream is = new FileInputStream(f);
+                DigestInputStream dis = new DigestInputStream(is, digest);
+                byte[] buffer = new byte[1024];
+                while (dis.read(buffer) > 0) {
+                    // nothing to do :)
+                }
+                dis.getMessageDigest().digest();
+            } catch (NoSuchAlgorithmException ignored) {
+            } catch (IOException e) {
+                log.info("Error reading file file: {}", filepath);
+            }
+            return new byte[0];
+        }
+
         @Override
         public int hashCode() {
-            return 1; //TODO
+            if (mode == TlsMode.DISABLED) {
+                return Objects.hash(mode);
+            }
+            return Objects.hash(mode, ksLocation, tsLocation,
+                    ksPwd, tsPwd, ksSignature, tsSignature);
         }
 
         @Override
@@ -314,7 +355,9 @@
                         Objects.equals(this.ksLocation, that.ksLocation) &&
                         Objects.equals(this.tsLocation, that.tsLocation) &&
                         Objects.equals(this.ksPwd, that.ksPwd) &&
-                        Objects.equals(this.tsPwd, that.tsPwd);
+                        Objects.equals(this.tsPwd, that.tsPwd) &&
+                        Arrays.equals(this.ksSignature, that.ksSignature) &&
+                        Arrays.equals(this.tsSignature, that.tsSignature);
             }
             return false;
         }
@@ -339,67 +382,68 @@
     private boolean setTlsParameters(Dictionary<?, ?> properties) {
         TlsParams oldParams = this.tlsParams;
 
-        TlsParams newParams = new TlsParams();
+        TlsMode mode = null;
         String tlsString = get(properties, "tlsMode");
         if (!Strings.isNullOrEmpty(tlsString)) {
             try {
-                newParams.mode = TlsMode.valueOf(tlsString.toUpperCase());
+                mode = TlsMode.valueOf(tlsString.toUpperCase());
             } catch (IllegalArgumentException e) {
                 log.info("Invalid TLS mode {}. TLS is disabled.", tlsString);
-                newParams.mode = TlsMode.DISABLED;
+                mode = TlsMode.DISABLED;
             }
         } else {
             // Fallback to system properties
             // TODO this method of configuring TLS is deprecated and should be removed eventually
             tlsString = System.getProperty("enableOFTLS");
-            newParams.mode = !Strings.isNullOrEmpty(tlsString) && Boolean.parseBoolean(tlsString) ?
+            mode = !Strings.isNullOrEmpty(tlsString) && Boolean.parseBoolean(tlsString) ?
                     TlsMode.ENABLED : TlsMode.DISABLED;
         }
 
-        if (newParams.isTlsEnabled()) {
-            newParams.ksLocation = get(properties, "keyStore");
-            if (Strings.isNullOrEmpty(newParams.ksLocation)) {
+        String ksLocation = null, tsLocation = null, ksPwd = null, tsPwd = null;
+        if (TLS_ENABLED.contains(mode)) {
+            ksLocation = get(properties, "keyStore");
+            if (Strings.isNullOrEmpty(ksLocation)) {
                 // Fallback to system properties
                 // TODO remove this eventually
-                newParams.ksLocation = System.getProperty("javax.net.ssl.keyStore");
+                ksLocation = System.getProperty("javax.net.ssl.keyStore");
             }
-            if (Strings.isNullOrEmpty(newParams.ksLocation)) {
-                newParams.mode = TlsMode.DISABLED;
+            if (Strings.isNullOrEmpty(ksLocation)) {
+                mode = TlsMode.DISABLED;
             }
 
-            newParams.tsLocation = get(properties, "trustStore");
-            if (Strings.isNullOrEmpty(newParams.tsLocation)) {
+            tsLocation = get(properties, "trustStore");
+            if (Strings.isNullOrEmpty(tsLocation)) {
                 // Fallback to system properties
                 // TODO remove this eventually
-                newParams.tsLocation = System.getProperty("javax.net.ssl.trustStore");
+                tsLocation = System.getProperty("javax.net.ssl.trustStore");
             }
-            if (Strings.isNullOrEmpty(newParams.tsLocation)) {
-                newParams.mode = TlsMode.DISABLED;
+            if (Strings.isNullOrEmpty(tsLocation)) {
+                mode = TlsMode.DISABLED;
             }
 
-            newParams.ksPwd = get(properties, "keyStorePassword");
-            if (Strings.isNullOrEmpty(newParams.ksPwd)) {
+            ksPwd = get(properties, "keyStorePassword");
+            if (Strings.isNullOrEmpty(ksPwd)) {
                 // Fallback to system properties
                 // TODO remove this eventually
-                newParams.ksPwd = System.getProperty("javax.net.ssl.keyStorePassword");
+                ksPwd = System.getProperty("javax.net.ssl.keyStorePassword");
             }
-            if (Strings.isNullOrEmpty(newParams.ksPwd) || MIN_KS_LENGTH > newParams.ksPwd.length()) {
-                newParams.mode = TlsMode.DISABLED;
+            if (Strings.isNullOrEmpty(ksPwd) || MIN_KS_LENGTH > ksPwd.length()) {
+                mode = TlsMode.DISABLED;
             }
 
-            newParams.tsPwd = get(properties, "trustStorePassword");
-            if (Strings.isNullOrEmpty(newParams.tsPwd)) {
+            tsPwd = get(properties, "trustStorePassword");
+            if (Strings.isNullOrEmpty(tsPwd)) {
                 // Fallback to system properties
                 // TODO remove this eventually
-                newParams.tsPwd = System.getProperty("javax.net.ssl.trustStorePassword");
+                tsPwd = System.getProperty("javax.net.ssl.trustStorePassword");
             }
-            if (Strings.isNullOrEmpty(newParams.tsPwd) || MIN_KS_LENGTH > newParams.tsPwd.length()) {
-                newParams.mode = TlsMode.DISABLED;
+            if (Strings.isNullOrEmpty(tsPwd) || MIN_KS_LENGTH > tsPwd.length()) {
+                mode = TlsMode.DISABLED;
             }
         }
-        this.tlsParams = newParams;
+        this.tlsParams = new TlsParams(mode, ksLocation, tsLocation, ksPwd, tsPwd);
         log.info("OpenFlow TLS Params: {}", tlsParams);
-        return !Objects.equals(newParams, oldParams); // restart if TLS params change
+        return !Objects.equals(this.tlsParams, oldParams); // restart if TLS params change
     }
 
     /**
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
index 6224842..bcf9edf 100644
--- a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
@@ -30,6 +30,8 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
 import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.config.basics.SubjectFactories;
 import org.onosproject.net.driver.DriverService;
@@ -79,6 +81,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
@@ -213,14 +216,56 @@
 
     private final Controller ctrl = new Controller();
 
+    private final NetworkConfigListener netCfgListener = new NetworkConfigListener() {
+        @Override
+        public boolean isRelevant(NetworkConfigEvent event) {
+            return OpenFlowDeviceConfig.class.equals(event.configClass());
+        }
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            // We only receive NetworkConfigEvents
+            OpenFlowDeviceConfig prevConfig = null;
+            if (event.prevConfig().isPresent()) {
+                prevConfig = (OpenFlowDeviceConfig) event.prevConfig().get();
+            }
+
+            OpenFlowDeviceConfig newConfig = null;
+            if (event.config().isPresent()) {
+                newConfig = (OpenFlowDeviceConfig) event.config().get();
+            }
+
+            boolean closeConnection = false;
+            if (prevConfig != null && newConfig != null) {
+                if (!Objects.equals(prevConfig.keyAlias(), newConfig.keyAlias())) {
+                    closeConnection = true;
+                }
+            } else if (prevConfig != null) {
+                // config was removed
+                closeConnection = true;
+            }
+            if (closeConnection) {
+                if (event.subject() instanceof DeviceId) {
+                    DeviceId deviceId = (DeviceId) event.subject();
+                    Dpid dpid = Dpid.dpid(deviceId.uri());
+                    OpenFlowSwitch sw = getSwitch(dpid);
+                    if (sw != null && ctrl.tlsParams.mode == Controller.TlsMode.STRICT) {
+                        sw.disconnectSwitch();
+                        log.info("Disconnecting switch {} because key has been updated or removed", dpid);
+                    }
+                }
+            }
+        }
+    };
+
     @Activate
     public void activate(ComponentContext context) {
         coreService.registerApplication(APP_ID, this::cleanup);
         cfgService.registerProperties(getClass());
         netCfgService.registerConfigFactory(factory);
+        netCfgService.addListener(netCfgListener);
         ctrl.setConfigParams(context.getProperties());
         ctrl.start(agent, driverService, netCfgService);
-        // TODO register a netcfg listener that disconnects switches when the keyAlias changes
     }
 
     private void cleanup() {
@@ -237,14 +282,13 @@
     public void deactivate() {
         cleanup();
         cfgService.unregisterProperties(getClass(), false);
+        netCfgService.removeListener(netCfgListener);
         netCfgService.unregisterConfigFactory(factory);
     }
 
     @Modified
     public void modified(ComponentContext context) {
-        //ctrl.stop();
         ctrl.setConfigParams(context.getProperties());
-        //ctrl.start(agent, driverService, netCfgService);
     }
 
     @Override