9d3551562a73acbd3db46075f57e45230c5c7c29
[onosfw.git] /
1 /*
2  * Copyright 2014-2015 Open Networking Laboratory
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onosproject.openflow.controller.impl;
18
19 import com.google.common.base.Strings;
20 import com.google.common.collect.ImmutableList;
21 import org.jboss.netty.bootstrap.ServerBootstrap;
22 import org.jboss.netty.channel.ChannelPipelineFactory;
23 import org.jboss.netty.channel.group.ChannelGroup;
24 import org.jboss.netty.channel.group.DefaultChannelGroup;
25 import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
26 import org.onlab.util.ItemNotFoundException;
27 import org.onosproject.net.DeviceId;
28 import org.onosproject.net.driver.DefaultDriverData;
29 import org.onosproject.net.driver.DefaultDriverHandler;
30 import org.onosproject.net.driver.Driver;
31 import org.onosproject.net.driver.DriverService;
32 import org.onosproject.openflow.controller.Dpid;
33 import org.onosproject.openflow.controller.driver.OpenFlowAgent;
34 import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
35 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
36 import org.projectfloodlight.openflow.protocol.OFFactories;
37 import org.projectfloodlight.openflow.protocol.OFFactory;
38 import org.projectfloodlight.openflow.protocol.OFVersion;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import javax.net.ssl.KeyManagerFactory;
43 import javax.net.ssl.SSLContext;
44 import javax.net.ssl.SSLEngine;
45 import javax.net.ssl.TrustManagerFactory;
46 import java.io.FileInputStream;
47 import java.lang.management.ManagementFactory;
48 import java.lang.management.RuntimeMXBean;
49 import java.net.InetSocketAddress;
50 import java.security.KeyStore;
51 import java.util.Dictionary;
52 import java.util.HashMap;
53 import java.util.List;
54 import java.util.Map;
55 import java.util.concurrent.Executors;
56 import java.util.stream.Collectors;
57 import java.util.stream.Stream;
58
59 import static org.onlab.util.Tools.get;
60 import static org.onlab.util.Tools.groupedThreads;
61 import static org.onosproject.net.DeviceId.deviceId;
62 import static org.onosproject.openflow.controller.Dpid.uri;
63
64
65 /**
66  * The main controller class.  Handles all setup and network listeners
67  * - Distributed ownership control of switch through IControllerRegistryService
68  */
69 public class Controller {
70
71     protected static final Logger log = LoggerFactory.getLogger(Controller.class);
72
73     protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13);
74     protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10);
75     private static final boolean TLS_DISABLED = false;
76     private static final short MIN_KS_LENGTH = 6;
77
78     protected HashMap<String, String> controllerNodeIPsCache;
79
80     private ChannelGroup cg;
81
82     // Configuration options
83     protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653);
84     protected int workerThreads = 16;
85
86     // Start time of the controller
87     protected long systemStartTime;
88
89     private OpenFlowAgent agent;
90
91     private NioServerSocketChannelFactory execFactory;
92
93     protected String ksLocation;
94     protected String tsLocation;
95     protected char[] ksPwd;
96     protected char[] tsPwd;
97     private SSLEngine serverSSLEngine;
98
99     // Perf. related configuration
100     protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
101     private DriverService driverService;
102     private boolean enableOFTLS = TLS_DISABLED;
103
104     // ***************
105     // Getters/Setters
106     // ***************
107
108     public OFFactory getOFMessageFactory10() {
109         return FACTORY10;
110     }
111
112
113     public OFFactory getOFMessageFactory13() {
114         return FACTORY13;
115     }
116
117
118     public Map<String, String> getControllerNodeIPs() {
119         // We return a copy of the mapping so we can guarantee that
120         // the mapping return is the same as one that will be (or was)
121         // dispatched to IHAListeners
122         HashMap<String, String> retval = new HashMap<>();
123         synchronized (controllerNodeIPsCache) {
124             retval.putAll(controllerNodeIPsCache);
125         }
126         return retval;
127     }
128
129
130     public long getSystemStartTime() {
131         return (this.systemStartTime);
132     }
133
134     // **************
135     // Initialization
136     // **************
137
138     /**
139      * Tell controller that we're ready to accept switches loop.
140      */
141     public void run() {
142
143         try {
144             final ServerBootstrap bootstrap = createServerBootStrap();
145
146             bootstrap.setOption("reuseAddr", true);
147             bootstrap.setOption("child.keepAlive", true);
148             bootstrap.setOption("child.tcpNoDelay", true);
149             bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
150
151             ChannelPipelineFactory pfact =
152                     new OpenflowPipelineFactory(this, null, serverSSLEngine);
153             bootstrap.setPipelineFactory(pfact);
154             cg = new DefaultChannelGroup();
155             openFlowPorts.forEach(port -> {
156                 InetSocketAddress sa = new InetSocketAddress(port);
157                 cg.add(bootstrap.bind(sa));
158                 log.info("Listening for switch connections on {}", sa);
159             });
160
161         } catch (Exception e) {
162             throw new RuntimeException(e);
163         }
164
165     }
166
167     private ServerBootstrap createServerBootStrap() {
168
169         if (workerThreads == 0) {
170             execFactory = new NioServerSocketChannelFactory(
171                     Executors.newCachedThreadPool(groupedThreads("onos/of", "boss-%d")),
172                     Executors.newCachedThreadPool(groupedThreads("onos/of", "worker-%d")));
173             return new ServerBootstrap(execFactory);
174         } else {
175             execFactory = new NioServerSocketChannelFactory(
176                     Executors.newCachedThreadPool(groupedThreads("onos/of", "boss-%d")),
177                     Executors.newCachedThreadPool(groupedThreads("onos/of", "worker-%d")), workerThreads);
178             return new ServerBootstrap(execFactory);
179         }
180     }
181
182     public void setConfigParams(Dictionary<?, ?> properties) {
183         String ports = get(properties, "openflowPorts");
184         if (!Strings.isNullOrEmpty(ports)) {
185             this.openFlowPorts = Stream.of(ports.split(","))
186                                        .map(s -> Integer.parseInt(s))
187                                        .collect(Collectors.toList());
188         }
189         log.debug("OpenFlow ports set to {}", this.openFlowPorts);
190
191         String threads = get(properties, "workerThreads");
192         if (!Strings.isNullOrEmpty(threads)) {
193             this.workerThreads = Integer.parseInt(threads);
194         }
195         log.debug("Number of worker threads set to {}", this.workerThreads);
196     }
197
198     /**
199      * Initialize internal data structures.
200      */
201     public void init() {
202         // These data structures are initialized here because other
203         // module's startUp() might be called before ours
204         this.controllerNodeIPsCache = new HashMap<>();
205
206         this.systemStartTime = System.currentTimeMillis();
207
208         try {
209             getTLSParameters();
210             if (enableOFTLS) {
211                 initSSL();
212             }
213         } catch (Exception ex) {
214             log.error("SSL init failed: {}", ex.getMessage());
215         }
216
217     }
218
219     private void getTLSParameters() {
220         String tempString = System.getProperty("enableOFTLS");
221         enableOFTLS = Strings.isNullOrEmpty(tempString) ? TLS_DISABLED : Boolean.parseBoolean(tempString);
222         log.info("OpenFlow Security is {}", enableOFTLS ? "enabled" : "disabled");
223         if (enableOFTLS) {
224             ksLocation = System.getProperty("javax.net.ssl.keyStore");
225             if (Strings.isNullOrEmpty(ksLocation)) {
226                 enableOFTLS = TLS_DISABLED;
227                 return;
228             }
229             tsLocation = System.getProperty("javax.net.ssl.trustStore");
230             if (Strings.isNullOrEmpty(tsLocation)) {
231                 enableOFTLS = TLS_DISABLED;
232                 return;
233             }
234             ksPwd = System.getProperty("javax.net.ssl.keyStorePassword").toCharArray();
235             if (MIN_KS_LENGTH > ksPwd.length) {
236                 enableOFTLS = TLS_DISABLED;
237                 return;
238             }
239             tsPwd = System.getProperty("javax.net.ssl.trustStorePassword").toCharArray();
240             if (MIN_KS_LENGTH > tsPwd.length) {
241                 enableOFTLS = TLS_DISABLED;
242                 return;
243             }
244         }
245     }
246
247     private void initSSL() throws Exception {
248
249         TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
250         KeyStore ts = KeyStore.getInstance("JKS");
251         ts.load(new FileInputStream(tsLocation), tsPwd);
252         tmFactory.init(ts);
253
254         KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
255         KeyStore ks = KeyStore.getInstance("JKS");
256         ks.load(new FileInputStream(ksLocation), ksPwd);
257         kmf.init(ks, ksPwd);
258
259         SSLContext serverContext = SSLContext.getInstance("TLS");
260         serverContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
261
262         serverSSLEngine = serverContext.createSSLEngine();
263
264         serverSSLEngine.setNeedClientAuth(true);
265         serverSSLEngine.setUseClientMode(false);
266         serverSSLEngine.setEnabledProtocols(serverSSLEngine.getSupportedProtocols());
267         serverSSLEngine.setEnabledCipherSuites(serverSSLEngine.getSupportedCipherSuites());
268         serverSSLEngine.setEnableSessionCreation(true);
269     }
270
271     // **************
272     // Utility methods
273     // **************
274
275     public Map<String, Long> getMemory() {
276         Map<String, Long> m = new HashMap<>();
277         Runtime runtime = Runtime.getRuntime();
278         m.put("total", runtime.totalMemory());
279         m.put("free", runtime.freeMemory());
280         return m;
281     }
282
283
284     public Long getUptime() {
285         RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
286         return rb.getUptime();
287     }
288
289     /**
290      * Forward to the driver-manager to get an IOFSwitch instance.
291      *
292      * @param dpid data path id
293      * @param desc switch description
294      * @param ofv  OpenFlow version
295      * @return switch instance
296      */
297     protected OpenFlowSwitchDriver getOFSwitchInstance(long dpid,
298                                                        OFDescStatsReply desc,
299                                                        OFVersion ofv) {
300         Dpid dpidObj = new Dpid(dpid);
301
302         Driver driver;
303         try {
304             driver = driverService.getDriver(DeviceId.deviceId(Dpid.uri(dpidObj)));
305         } catch (ItemNotFoundException e) {
306             driver = driverService.getDriver(desc.getMfrDesc(), desc.getHwDesc(), desc.getSwDesc());
307         }
308
309         if (driver != null && driver.hasBehaviour(OpenFlowSwitchDriver.class)) {
310             Dpid did = new Dpid(dpid);
311             DefaultDriverHandler handler =
312                     new DefaultDriverHandler(new DefaultDriverData(driver, deviceId(uri(did))));
313             OpenFlowSwitchDriver ofSwitchDriver =
314                     driver.createBehaviour(handler, OpenFlowSwitchDriver.class);
315             ofSwitchDriver.init(did, desc, ofv);
316             ofSwitchDriver.setAgent(agent);
317             ofSwitchDriver.setRoleHandler(new RoleManager(ofSwitchDriver));
318             log.info("OpenFlow handshaker found for device {}: {}", dpid, ofSwitchDriver);
319             return ofSwitchDriver;
320         }
321         log.error("No OpenFlow driver for {} : {}", dpid, desc);
322         return null;
323
324     }
325
326     public void start(OpenFlowAgent ag, DriverService driverService) {
327         log.info("Starting OpenFlow IO");
328         this.agent = ag;
329         this.driverService = driverService;
330         this.init();
331         this.run();
332     }
333
334
335     public void stop() {
336         log.info("Stopping OpenFlow IO");
337         cg.close();
338         execFactory.shutdown();
339     }
340
341 }