blob: 9d3551562a73acbd3db46075f57e45230c5c7c29 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
tom7ef8ff92014-09-17 13:08:06 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
tom7ef8ff92014-09-17 13:08:06 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
tom7ef8ff92014-09-17 13:08:06 -070016
Brian O'Connorabafb502014-12-02 22:26:20 -080017package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070018
Brian O'Connorff278502015-09-22 14:49:52 -070019import com.google.common.base.Strings;
20import com.google.common.collect.ImmutableList;
tom7ef8ff92014-09-17 13:08:06 -070021import org.jboss.netty.bootstrap.ServerBootstrap;
22import org.jboss.netty.channel.ChannelPipelineFactory;
23import org.jboss.netty.channel.group.ChannelGroup;
24import org.jboss.netty.channel.group.DefaultChannelGroup;
25import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
Jonathan Harta0d12492015-07-16 12:03:41 -070026import org.onlab.util.ItemNotFoundException;
27import org.onosproject.net.DeviceId;
alshabibb452fd72015-04-22 20:46:20 -070028import org.onosproject.net.driver.DefaultDriverData;
29import org.onosproject.net.driver.DefaultDriverHandler;
30import org.onosproject.net.driver.Driver;
31import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.openflow.controller.Dpid;
33import org.onosproject.openflow.controller.driver.OpenFlowAgent;
34import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
tom7ef8ff92014-09-17 13:08:06 -070035import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
36import org.projectfloodlight.openflow.protocol.OFFactories;
37import org.projectfloodlight.openflow.protocol.OFFactory;
38import org.projectfloodlight.openflow.protocol.OFVersion;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
alshabib9b6c19c2015-09-26 12:19:27 -070042import javax.net.ssl.KeyManagerFactory;
43import javax.net.ssl.SSLContext;
44import javax.net.ssl.SSLEngine;
45import javax.net.ssl.TrustManagerFactory;
46import java.io.FileInputStream;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080047import java.lang.management.ManagementFactory;
48import java.lang.management.RuntimeMXBean;
49import java.net.InetSocketAddress;
alshabib9b6c19c2015-09-26 12:19:27 -070050import java.security.KeyStore;
Brian O'Connorff278502015-09-22 14:49:52 -070051import java.util.Dictionary;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080052import java.util.HashMap;
Brian O'Connorff278502015-09-22 14:49:52 -070053import java.util.List;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080054import java.util.Map;
55import java.util.concurrent.Executors;
Brian O'Connorff278502015-09-22 14:49:52 -070056import java.util.stream.Collectors;
57import java.util.stream.Stream;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080058
Brian O'Connorff278502015-09-22 14:49:52 -070059import static org.onlab.util.Tools.get;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080060import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska80b0a802015-07-17 08:43:30 -070061import static org.onosproject.net.DeviceId.deviceId;
62import static org.onosproject.openflow.controller.Dpid.uri;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080063
tom7ef8ff92014-09-17 13:08:06 -070064
65/**
66 * The main controller class. Handles all setup and network listeners
67 * - Distributed ownership control of switch through IControllerRegistryService
68 */
69public class Controller {
70
71 protected static final Logger log = LoggerFactory.getLogger(Controller.class);
alshabib9eab22f2014-10-20 17:17:31 -070072
tom7ef8ff92014-09-17 13:08:06 -070073 protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13);
74 protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10);
alshabib9b6c19c2015-09-26 12:19:27 -070075 private static final boolean TLS_DISABLED = false;
76 private static final short MIN_KS_LENGTH = 6;
tom7ef8ff92014-09-17 13:08:06 -070077
tom7ef8ff92014-09-17 13:08:06 -070078 protected HashMap<String, String> controllerNodeIPsCache;
79
80 private ChannelGroup cg;
81
82 // Configuration options
Brian O'Connorff278502015-09-22 14:49:52 -070083 protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653);
84 protected int workerThreads = 16;
tom7ef8ff92014-09-17 13:08:06 -070085
86 // Start time of the controller
87 protected long systemStartTime;
88
89 private OpenFlowAgent agent;
90
91 private NioServerSocketChannelFactory execFactory;
92
alshabib9b6c19c2015-09-26 12:19:27 -070093 protected String ksLocation;
94 protected String tsLocation;
95 protected char[] ksPwd;
96 protected char[] tsPwd;
97 private SSLEngine serverSSLEngine;
98
tom7ef8ff92014-09-17 13:08:06 -070099 // Perf. related configuration
100 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
alshabibb452fd72015-04-22 20:46:20 -0700101 private DriverService driverService;
alshabib9b6c19c2015-09-26 12:19:27 -0700102 private boolean enableOFTLS = TLS_DISABLED;
tom7ef8ff92014-09-17 13:08:06 -0700103
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
tom7ef8ff92014-09-17 13:08:06 -0700118 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
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800122 HashMap<String, String> retval = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700123 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 =
alshabib9b6c19c2015-09-26 12:19:27 -0700152 new OpenflowPipelineFactory(this, null, serverSSLEngine);
tom7ef8ff92014-09-17 13:08:06 -0700153 bootstrap.setPipelineFactory(pfact);
tom7ef8ff92014-09-17 13:08:06 -0700154 cg = new DefaultChannelGroup();
Brian O'Connorff278502015-09-22 14:49:52 -0700155 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 });
tom7ef8ff92014-09-17 13:08:06 -0700160
tom7ef8ff92014-09-17 13:08:06 -0700161 } catch (Exception e) {
162 throw new RuntimeException(e);
163 }
164
165 }
166
167 private ServerBootstrap createServerBootStrap() {
168
169 if (workerThreads == 0) {
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800170 execFactory = new NioServerSocketChannelFactory(
171 Executors.newCachedThreadPool(groupedThreads("onos/of", "boss-%d")),
172 Executors.newCachedThreadPool(groupedThreads("onos/of", "worker-%d")));
tom7ef8ff92014-09-17 13:08:06 -0700173 return new ServerBootstrap(execFactory);
174 } else {
175 execFactory = new NioServerSocketChannelFactory(
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800176 Executors.newCachedThreadPool(groupedThreads("onos/of", "boss-%d")),
177 Executors.newCachedThreadPool(groupedThreads("onos/of", "worker-%d")), workerThreads);
tom7ef8ff92014-09-17 13:08:06 -0700178 return new ServerBootstrap(execFactory);
179 }
180 }
181
Brian O'Connorff278502015-09-22 14:49:52 -0700182 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());
tom7ef8ff92014-09-17 13:08:06 -0700188 }
Brian O'Connorff278502015-09-22 14:49:52 -0700189 log.debug("OpenFlow ports set to {}", this.openFlowPorts);
Charles Chan45624b82015-08-24 00:29:20 +0800190
Brian O'Connorff278502015-09-22 14:49:52 -0700191 String threads = get(properties, "workerThreads");
192 if (!Strings.isNullOrEmpty(threads)) {
193 this.workerThreads = Integer.parseInt(threads);
194 }
tom7ef8ff92014-09-17 13:08:06 -0700195 log.debug("Number of worker threads set to {}", this.workerThreads);
196 }
197
tom7ef8ff92014-09-17 13:08:06 -0700198 /**
199 * Initialize internal data structures.
200 */
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800201 public void init() {
tom7ef8ff92014-09-17 13:08:06 -0700202 // These data structures are initialized here because other
203 // module's startUp() might be called before ours
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800204 this.controllerNodeIPsCache = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700205
tom7ef8ff92014-09-17 13:08:06 -0700206 this.systemStartTime = System.currentTimeMillis();
alshabib9b6c19c2015-09-26 12:19:27 -0700207
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);
tom7ef8ff92014-09-17 13:08:06 -0700269 }
270
271 // **************
272 // Utility methods
273 // **************
274
275 public Map<String, Long> getMemory() {
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800276 Map<String, Long> m = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700277 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.
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800291 *
Yuta HIGUCHI5c947272014-11-03 21:39:21 -0800292 * @param dpid data path id
293 * @param desc switch description
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800294 * @param ofv OpenFlow version
tom7ef8ff92014-09-17 13:08:06 -0700295 * @return switch instance
296 */
297 protected OpenFlowSwitchDriver getOFSwitchInstance(long dpid,
alshabibb452fd72015-04-22 20:46:20 -0700298 OFDescStatsReply desc,
299 OFVersion ofv) {
Jonathan Harta0d12492015-07-16 12:03:41 -0700300 Dpid dpidObj = new Dpid(dpid);
301
302 Driver driver;
303 try {
Sho SHIMIZUbc82ebb2015-08-25 10:15:21 -0700304 driver = driverService.getDriver(DeviceId.deviceId(Dpid.uri(dpidObj)));
Jonathan Harta0d12492015-07-16 12:03:41 -0700305 } catch (ItemNotFoundException e) {
306 driver = driverService.getDriver(desc.getMfrDesc(), desc.getHwDesc(), desc.getSwDesc());
307 }
alshabibb452fd72015-04-22 20:46:20 -0700308
Thomas Vachuska3358af22015-05-19 18:40:34 -0700309 if (driver != null && driver.hasBehaviour(OpenFlowSwitchDriver.class)) {
Thomas Vachuska80b0a802015-07-17 08:43:30 -0700310 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);
alshabibb452fd72015-04-22 20:46:20 -0700316 ofSwitchDriver.setAgent(agent);
317 ofSwitchDriver.setRoleHandler(new RoleManager(ofSwitchDriver));
Saurav Das100e3b82015-04-30 11:12:10 -0700318 log.info("OpenFlow handshaker found for device {}: {}", dpid, ofSwitchDriver);
alshabibb452fd72015-04-22 20:46:20 -0700319 return ofSwitchDriver;
320 }
321 log.error("No OpenFlow driver for {} : {}", dpid, desc);
322 return null;
323
tom7ef8ff92014-09-17 13:08:06 -0700324 }
325
alshabibb452fd72015-04-22 20:46:20 -0700326 public void start(OpenFlowAgent ag, DriverService driverService) {
tom7ef8ff92014-09-17 13:08:06 -0700327 log.info("Starting OpenFlow IO");
328 this.agent = ag;
alshabibb452fd72015-04-22 20:46:20 -0700329 this.driverService = driverService;
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800330 this.init();
tom7ef8ff92014-09-17 13:08:06 -0700331 this.run();
332 }
333
334
335 public void stop() {
336 log.info("Stopping OpenFlow IO");
tom7ef8ff92014-09-17 13:08:06 -0700337 cg.close();
Thomas Vachuska1c681d72015-05-18 14:58:53 -0700338 execFactory.shutdown();
tom7ef8ff92014-09-17 13:08:06 -0700339 }
340
341}