blob: 578dae028de12ad2ec292ba544e92f8fe2a8c974 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
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;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070021import io.netty.bootstrap.ServerBootstrap;
22import io.netty.channel.ChannelOption;
23import io.netty.channel.EventLoopGroup;
24import io.netty.channel.epoll.EpollEventLoopGroup;
25import io.netty.channel.epoll.EpollServerSocketChannel;
26import io.netty.channel.group.ChannelGroup;
27import io.netty.channel.group.DefaultChannelGroup;
28import io.netty.channel.nio.NioEventLoopGroup;
29import io.netty.channel.socket.nio.NioServerSocketChannel;
30import io.netty.util.concurrent.GlobalEventExecutor;
Jonathan Harta0d12492015-07-16 12:03:41 -070031import org.onlab.util.ItemNotFoundException;
32import org.onosproject.net.DeviceId;
alshabibb452fd72015-04-22 20:46:20 -070033import org.onosproject.net.driver.DefaultDriverData;
34import org.onosproject.net.driver.DefaultDriverHandler;
35import org.onosproject.net.driver.Driver;
36import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.openflow.controller.Dpid;
38import org.onosproject.openflow.controller.driver.OpenFlowAgent;
39import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
tom7ef8ff92014-09-17 13:08:06 -070040import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
41import org.projectfloodlight.openflow.protocol.OFFactories;
42import org.projectfloodlight.openflow.protocol.OFFactory;
43import org.projectfloodlight.openflow.protocol.OFVersion;
44import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
alshabib9b6c19c2015-09-26 12:19:27 -070047import javax.net.ssl.KeyManagerFactory;
48import javax.net.ssl.SSLContext;
alshabib9b6c19c2015-09-26 12:19:27 -070049import javax.net.ssl.TrustManagerFactory;
50import java.io.FileInputStream;
Ray Milkey986a47a2018-01-25 11:38:51 -080051import java.io.IOException;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080052import java.lang.management.ManagementFactory;
53import java.lang.management.RuntimeMXBean;
Ray Milkey986a47a2018-01-25 11:38:51 -080054import java.security.KeyManagementException;
alshabib9b6c19c2015-09-26 12:19:27 -070055import java.security.KeyStore;
Ray Milkey986a47a2018-01-25 11:38:51 -080056import java.security.KeyStoreException;
57import java.security.NoSuchAlgorithmException;
58import java.security.UnrecoverableKeyException;
59import java.security.cert.CertificateException;
Brian O'Connorff278502015-09-22 14:49:52 -070060import java.util.Dictionary;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080061import java.util.HashMap;
Brian O'Connorff278502015-09-22 14:49:52 -070062import java.util.List;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080063import java.util.Map;
Brian O'Connorff278502015-09-22 14:49:52 -070064import java.util.stream.Collectors;
65import java.util.stream.Stream;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080066
Brian O'Connorff278502015-09-22 14:49:52 -070067import static org.onlab.util.Tools.get;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080068import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska80b0a802015-07-17 08:43:30 -070069import static org.onosproject.net.DeviceId.deviceId;
70import static org.onosproject.openflow.controller.Dpid.uri;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080071
tom7ef8ff92014-09-17 13:08:06 -070072
73/**
74 * The main controller class. Handles all setup and network listeners
tom7ef8ff92014-09-17 13:08:06 -070075 */
76public class Controller {
77
Ray Milkey9c9cde42018-01-12 14:22:06 -080078 private static final Logger log = LoggerFactory.getLogger(Controller.class);
alshabib9eab22f2014-10-20 17:17:31 -070079
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080080 /**
81 * @deprecated in 1.10.0
82 */
83 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -070084 protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13);
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080085 /**
86 * @deprecated in 1.10.0
87 */
88 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -070089 protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10);
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080090
alshabib9b6c19c2015-09-26 12:19:27 -070091 private static final boolean TLS_DISABLED = false;
92 private static final short MIN_KS_LENGTH = 6;
tom7ef8ff92014-09-17 13:08:06 -070093
tom7ef8ff92014-09-17 13:08:06 -070094 protected HashMap<String, String> controllerNodeIPsCache;
95
96 private ChannelGroup cg;
97
98 // Configuration options
Brian O'Connorff278502015-09-22 14:49:52 -070099 protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653);
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700100 protected int workerThreads = 0;
tom7ef8ff92014-09-17 13:08:06 -0700101
102 // Start time of the controller
103 protected long systemStartTime;
104
105 private OpenFlowAgent agent;
106
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700107 private EventLoopGroup bossGroup;
108 private EventLoopGroup workerGroup;
tom7ef8ff92014-09-17 13:08:06 -0700109
alshabib9b6c19c2015-09-26 12:19:27 -0700110 protected String ksLocation;
111 protected String tsLocation;
112 protected char[] ksPwd;
113 protected char[] tsPwd;
alshabib5162a9d2015-12-07 17:49:37 -0800114 protected SSLContext sslContext;
alshabib9b6c19c2015-09-26 12:19:27 -0700115
tom7ef8ff92014-09-17 13:08:06 -0700116 // Perf. related configuration
117 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800118
alshabibb452fd72015-04-22 20:46:20 -0700119 private DriverService driverService;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800120 private boolean enableOfTls = TLS_DISABLED;
tom7ef8ff92014-09-17 13:08:06 -0700121
122 // ***************
123 // Getters/Setters
124 // ***************
125
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800126 /**
127 * @return OF1.0 factory
128 * @deprecated in 1.10.0
129 */
130 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700131 public OFFactory getOFMessageFactory10() {
132 return FACTORY10;
133 }
134
135
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800136 /**
137 * @return OF1.3 factory
138 * @deprecated in 1.10.0
139 */
140 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700141 public OFFactory getOFMessageFactory13() {
142 return FACTORY13;
143 }
144
tom7ef8ff92014-09-17 13:08:06 -0700145 // **************
146 // Initialization
147 // **************
148
149 /**
150 * Tell controller that we're ready to accept switches loop.
151 */
152 public void run() {
153
tom7ef8ff92014-09-17 13:08:06 -0700154 final ServerBootstrap bootstrap = createServerBootStrap();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700155 bootstrap.option(ChannelOption.SO_REUSEADDR, true);
156 bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
157 bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
158 bootstrap.childOption(ChannelOption.SO_SNDBUF, Controller.SEND_BUFFER_SIZE);
159// bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
160// new WriteBufferWaterMark(8 * 1024, 32 * 1024));
tom7ef8ff92014-09-17 13:08:06 -0700161
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700162 bootstrap.childHandler(new OFChannelInitializer(this, null, sslContext));
tom7ef8ff92014-09-17 13:08:06 -0700163
Brian O'Connorff278502015-09-22 14:49:52 -0700164 openFlowPorts.forEach(port -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700165 // TODO revisit if this is best way to listen to multiple ports
166 cg.add(bootstrap.bind(port).syncUninterruptibly().channel());
167 log.info("Listening for switch connections on {}", port);
Brian O'Connorff278502015-09-22 14:49:52 -0700168 });
tom7ef8ff92014-09-17 13:08:06 -0700169
tom7ef8ff92014-09-17 13:08:06 -0700170 }
171
172 private ServerBootstrap createServerBootStrap() {
173
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700174 int bossThreads = Math.max(1, openFlowPorts.size());
175 try {
176 bossGroup = new EpollEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
177 workerGroup = new EpollEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
178 ServerBootstrap bs = new ServerBootstrap()
179 .group(bossGroup, workerGroup)
180 .channel(EpollServerSocketChannel.class);
181 log.info("Using Epoll transport");
182 return bs;
183 } catch (Throwable e) {
184 log.debug("Failed to initialize native (epoll) transport: {}", e.getMessage());
tom7ef8ff92014-09-17 13:08:06 -0700185 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700186
187// Requires 4.1.11 or later
188// try {
189// bossGroup = new KQueueEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
190// workerGroup = new KQueueEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
191// ServerBootstrap bs = new ServerBootstrap()
192// .group(bossGroup, workerGroup)
193// .channel(KQueueServerSocketChannel.class);
194// log.info("Using Kqueue transport");
195// return bs;
196// } catch (Throwable e) {
197// log.debug("Failed to initialize native (kqueue) transport. ", e.getMessage());
198// }
199
200 bossGroup = new NioEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
201 workerGroup = new NioEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
202 log.info("Using Nio transport");
203 return new ServerBootstrap()
204 .group(bossGroup, workerGroup)
205 .channel(NioServerSocketChannel.class);
tom7ef8ff92014-09-17 13:08:06 -0700206 }
207
Brian O'Connorff278502015-09-22 14:49:52 -0700208 public void setConfigParams(Dictionary<?, ?> properties) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700209 // TODO should be possible to reconfigure ports without restart,
210 // by updating ChannelGroup
Brian O'Connorff278502015-09-22 14:49:52 -0700211 String ports = get(properties, "openflowPorts");
212 if (!Strings.isNullOrEmpty(ports)) {
213 this.openFlowPorts = Stream.of(ports.split(","))
214 .map(s -> Integer.parseInt(s))
215 .collect(Collectors.toList());
tom7ef8ff92014-09-17 13:08:06 -0700216 }
Brian O'Connorff278502015-09-22 14:49:52 -0700217 log.debug("OpenFlow ports set to {}", this.openFlowPorts);
Charles Chan45624b82015-08-24 00:29:20 +0800218
Brian O'Connorff278502015-09-22 14:49:52 -0700219 String threads = get(properties, "workerThreads");
220 if (!Strings.isNullOrEmpty(threads)) {
221 this.workerThreads = Integer.parseInt(threads);
222 }
tom7ef8ff92014-09-17 13:08:06 -0700223 log.debug("Number of worker threads set to {}", this.workerThreads);
224 }
225
tom7ef8ff92014-09-17 13:08:06 -0700226 /**
227 * Initialize internal data structures.
228 */
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800229 public void init() {
tom7ef8ff92014-09-17 13:08:06 -0700230 // These data structures are initialized here because other
231 // module's startUp() might be called before ours
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800232 this.controllerNodeIPsCache = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700233
tom7ef8ff92014-09-17 13:08:06 -0700234 this.systemStartTime = System.currentTimeMillis();
alshabib9b6c19c2015-09-26 12:19:27 -0700235
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700236 cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
237
Ray Milkey986a47a2018-01-25 11:38:51 -0800238 getTlsParameters();
239 if (enableOfTls) {
240 initSsl();
alshabib9b6c19c2015-09-26 12:19:27 -0700241 }
alshabib9b6c19c2015-09-26 12:19:27 -0700242 }
243
Jonathan Hartb35540a2015-11-17 09:30:56 -0800244 private void getTlsParameters() {
alshabib9b6c19c2015-09-26 12:19:27 -0700245 String tempString = System.getProperty("enableOFTLS");
Jonathan Hartb35540a2015-11-17 09:30:56 -0800246 enableOfTls = Strings.isNullOrEmpty(tempString) ? TLS_DISABLED : Boolean.parseBoolean(tempString);
247 log.info("OpenFlow Security is {}", enableOfTls ? "enabled" : "disabled");
248 if (enableOfTls) {
alshabib9b6c19c2015-09-26 12:19:27 -0700249 ksLocation = System.getProperty("javax.net.ssl.keyStore");
250 if (Strings.isNullOrEmpty(ksLocation)) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800251 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700252 return;
253 }
254 tsLocation = System.getProperty("javax.net.ssl.trustStore");
255 if (Strings.isNullOrEmpty(tsLocation)) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800256 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700257 return;
258 }
259 ksPwd = System.getProperty("javax.net.ssl.keyStorePassword").toCharArray();
260 if (MIN_KS_LENGTH > ksPwd.length) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800261 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700262 return;
263 }
264 tsPwd = System.getProperty("javax.net.ssl.trustStorePassword").toCharArray();
265 if (MIN_KS_LENGTH > tsPwd.length) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800266 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700267 return;
268 }
269 }
270 }
271
Ray Milkey986a47a2018-01-25 11:38:51 -0800272 private void initSsl() {
273 try {
274 TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
275 KeyStore ts = KeyStore.getInstance("JKS");
276 ts.load(new FileInputStream(tsLocation), tsPwd);
277 tmFactory.init(ts);
alshabib9b6c19c2015-09-26 12:19:27 -0700278
Ray Milkey986a47a2018-01-25 11:38:51 -0800279 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
280 KeyStore ks = KeyStore.getInstance("JKS");
281 ks.load(new FileInputStream(ksLocation), ksPwd);
282 kmf.init(ks, ksPwd);
alshabib9b6c19c2015-09-26 12:19:27 -0700283
Ray Milkey986a47a2018-01-25 11:38:51 -0800284 sslContext = SSLContext.getInstance("TLS");
285 sslContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
286 } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException |
287 IOException | KeyManagementException | UnrecoverableKeyException ex) {
288 log.error("SSL init failed: {}", ex.getMessage());
289 }
tom7ef8ff92014-09-17 13:08:06 -0700290 }
291
292 // **************
293 // Utility methods
294 // **************
295
296 public Map<String, Long> getMemory() {
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800297 Map<String, Long> m = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700298 Runtime runtime = Runtime.getRuntime();
299 m.put("total", runtime.totalMemory());
300 m.put("free", runtime.freeMemory());
301 return m;
302 }
303
304
Ray Milkeydc0ff192015-11-04 13:49:52 -0800305 public Long getSystemUptime() {
tom7ef8ff92014-09-17 13:08:06 -0700306 RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
307 return rb.getUptime();
308 }
309
Ray Milkeydc0ff192015-11-04 13:49:52 -0800310 public long getSystemStartTime() {
311 return (this.systemStartTime);
312 }
313
tom7ef8ff92014-09-17 13:08:06 -0700314 /**
315 * Forward to the driver-manager to get an IOFSwitch instance.
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800316 *
Yuta HIGUCHI5c947272014-11-03 21:39:21 -0800317 * @param dpid data path id
318 * @param desc switch description
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800319 * @param ofv OpenFlow version
tom7ef8ff92014-09-17 13:08:06 -0700320 * @return switch instance
321 */
322 protected OpenFlowSwitchDriver getOFSwitchInstance(long dpid,
alshabibb452fd72015-04-22 20:46:20 -0700323 OFDescStatsReply desc,
324 OFVersion ofv) {
Jonathan Harta0d12492015-07-16 12:03:41 -0700325 Dpid dpidObj = new Dpid(dpid);
326
327 Driver driver;
328 try {
Sho SHIMIZUbc82ebb2015-08-25 10:15:21 -0700329 driver = driverService.getDriver(DeviceId.deviceId(Dpid.uri(dpidObj)));
Jonathan Harta0d12492015-07-16 12:03:41 -0700330 } catch (ItemNotFoundException e) {
331 driver = driverService.getDriver(desc.getMfrDesc(), desc.getHwDesc(), desc.getSwDesc());
332 }
alshabibb452fd72015-04-22 20:46:20 -0700333
Jonathan Harta33134e2016-07-27 17:33:35 -0700334 if (driver == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700335 log.error("No OpenFlow driver for {} : {}", dpidObj, desc);
Jonathan Harta33134e2016-07-27 17:33:35 -0700336 return null;
alshabibb452fd72015-04-22 20:46:20 -0700337 }
alshabibb452fd72015-04-22 20:46:20 -0700338
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700339 log.info("Driver '{}' assigned to device {}", driver.name(), dpidObj);
Jonathan Harta33134e2016-07-27 17:33:35 -0700340
341 if (!driver.hasBehaviour(OpenFlowSwitchDriver.class)) {
342 log.error("Driver {} does not support OpenFlowSwitchDriver behaviour", driver.name());
343 return null;
344 }
345
346 DefaultDriverHandler handler =
347 new DefaultDriverHandler(new DefaultDriverData(driver, deviceId(uri(dpidObj))));
348 OpenFlowSwitchDriver ofSwitchDriver =
349 driver.createBehaviour(handler, OpenFlowSwitchDriver.class);
350 ofSwitchDriver.init(dpidObj, desc, ofv);
351 ofSwitchDriver.setAgent(agent);
352 ofSwitchDriver.setRoleHandler(new RoleManager(ofSwitchDriver));
Jonathan Harta33134e2016-07-27 17:33:35 -0700353 return ofSwitchDriver;
tom7ef8ff92014-09-17 13:08:06 -0700354 }
355
alshabibb452fd72015-04-22 20:46:20 -0700356 public void start(OpenFlowAgent ag, DriverService driverService) {
tom7ef8ff92014-09-17 13:08:06 -0700357 log.info("Starting OpenFlow IO");
358 this.agent = ag;
alshabibb452fd72015-04-22 20:46:20 -0700359 this.driverService = driverService;
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800360 this.init();
tom7ef8ff92014-09-17 13:08:06 -0700361 this.run();
362 }
363
364
365 public void stop() {
366 log.info("Stopping OpenFlow IO");
tom7ef8ff92014-09-17 13:08:06 -0700367 cg.close();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700368
369 // Shut down all event loops to terminate all threads.
370 bossGroup.shutdownGracefully();
371 workerGroup.shutdownGracefully();
372
373 // Wait until all threads are terminated.
374 try {
375 bossGroup.terminationFuture().sync();
376 workerGroup.terminationFuture().sync();
377 } catch (InterruptedException e) {
378 log.warn("Interrupted while stopping", e);
379 Thread.currentThread().interrupt();
380 }
tom7ef8ff92014-09-17 13:08:06 -0700381 }
382
383}