blob: f328ae7b177583c323a278875525acae953ded61 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070021
22import io.netty.bootstrap.ServerBootstrap;
23import io.netty.channel.ChannelOption;
24import io.netty.channel.EventLoopGroup;
25import io.netty.channel.epoll.EpollEventLoopGroup;
26import io.netty.channel.epoll.EpollServerSocketChannel;
27import io.netty.channel.group.ChannelGroup;
28import io.netty.channel.group.DefaultChannelGroup;
29import io.netty.channel.nio.NioEventLoopGroup;
30import io.netty.channel.socket.nio.NioServerSocketChannel;
31import io.netty.util.concurrent.GlobalEventExecutor;
alshabib5162a9d2015-12-07 17:49:37 -080032
Jonathan Harta0d12492015-07-16 12:03:41 -070033import org.onlab.util.ItemNotFoundException;
34import org.onosproject.net.DeviceId;
alshabibb452fd72015-04-22 20:46:20 -070035import org.onosproject.net.driver.DefaultDriverData;
36import org.onosproject.net.driver.DefaultDriverHandler;
37import org.onosproject.net.driver.Driver;
38import org.onosproject.net.driver.DriverService;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.openflow.controller.Dpid;
40import org.onosproject.openflow.controller.driver.OpenFlowAgent;
41import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
tom7ef8ff92014-09-17 13:08:06 -070042import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
43import org.projectfloodlight.openflow.protocol.OFFactories;
44import org.projectfloodlight.openflow.protocol.OFFactory;
45import org.projectfloodlight.openflow.protocol.OFVersion;
46import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
48
alshabib9b6c19c2015-09-26 12:19:27 -070049import javax.net.ssl.KeyManagerFactory;
50import javax.net.ssl.SSLContext;
alshabib9b6c19c2015-09-26 12:19:27 -070051import javax.net.ssl.TrustManagerFactory;
52import java.io.FileInputStream;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080053import java.lang.management.ManagementFactory;
54import java.lang.management.RuntimeMXBean;
alshabib9b6c19c2015-09-26 12:19:27 -070055import java.security.KeyStore;
Brian O'Connorff278502015-09-22 14:49:52 -070056import java.util.Dictionary;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080057import java.util.HashMap;
Brian O'Connorff278502015-09-22 14:49:52 -070058import java.util.List;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080059import java.util.Map;
Brian O'Connorff278502015-09-22 14:49:52 -070060import java.util.stream.Collectors;
61import java.util.stream.Stream;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080062
Brian O'Connorff278502015-09-22 14:49:52 -070063import static org.onlab.util.Tools.get;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080064import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska80b0a802015-07-17 08:43:30 -070065import static org.onosproject.net.DeviceId.deviceId;
66import static org.onosproject.openflow.controller.Dpid.uri;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080067
tom7ef8ff92014-09-17 13:08:06 -070068
69/**
70 * The main controller class. Handles all setup and network listeners
tom7ef8ff92014-09-17 13:08:06 -070071 */
72public class Controller {
73
74 protected static final Logger log = LoggerFactory.getLogger(Controller.class);
alshabib9eab22f2014-10-20 17:17:31 -070075
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080076 /**
77 * @deprecated in 1.10.0
78 */
79 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -070080 protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13);
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080081 /**
82 * @deprecated in 1.10.0
83 */
84 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -070085 protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10);
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080086
alshabib9b6c19c2015-09-26 12:19:27 -070087 private static final boolean TLS_DISABLED = false;
88 private static final short MIN_KS_LENGTH = 6;
tom7ef8ff92014-09-17 13:08:06 -070089
tom7ef8ff92014-09-17 13:08:06 -070090 protected HashMap<String, String> controllerNodeIPsCache;
91
92 private ChannelGroup cg;
93
94 // Configuration options
Brian O'Connorff278502015-09-22 14:49:52 -070095 protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653);
Yuta HIGUCHI8552b172016-07-25 12:10:08 -070096 protected int workerThreads = 0;
tom7ef8ff92014-09-17 13:08:06 -070097
98 // Start time of the controller
99 protected long systemStartTime;
100
101 private OpenFlowAgent agent;
102
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700103 private EventLoopGroup bossGroup;
104 private EventLoopGroup workerGroup;
tom7ef8ff92014-09-17 13:08:06 -0700105
alshabib9b6c19c2015-09-26 12:19:27 -0700106 protected String ksLocation;
107 protected String tsLocation;
108 protected char[] ksPwd;
109 protected char[] tsPwd;
alshabib5162a9d2015-12-07 17:49:37 -0800110 protected SSLContext sslContext;
alshabib9b6c19c2015-09-26 12:19:27 -0700111
tom7ef8ff92014-09-17 13:08:06 -0700112 // Perf. related configuration
113 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800114
alshabibb452fd72015-04-22 20:46:20 -0700115 private DriverService driverService;
Jonathan Hartb35540a2015-11-17 09:30:56 -0800116 private boolean enableOfTls = TLS_DISABLED;
tom7ef8ff92014-09-17 13:08:06 -0700117
118 // ***************
119 // Getters/Setters
120 // ***************
121
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800122 /**
123 * @return OF1.0 factory
124 * @deprecated in 1.10.0
125 */
126 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700127 public OFFactory getOFMessageFactory10() {
128 return FACTORY10;
129 }
130
131
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800132 /**
133 * @return OF1.3 factory
134 * @deprecated in 1.10.0
135 */
136 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700137 public OFFactory getOFMessageFactory13() {
138 return FACTORY13;
139 }
140
tom7ef8ff92014-09-17 13:08:06 -0700141 // **************
142 // Initialization
143 // **************
144
145 /**
146 * Tell controller that we're ready to accept switches loop.
147 */
148 public void run() {
149
150 try {
151 final ServerBootstrap bootstrap = createServerBootStrap();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700152 bootstrap.option(ChannelOption.SO_REUSEADDR, true);
153 bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
154 bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
155 bootstrap.childOption(ChannelOption.SO_SNDBUF, Controller.SEND_BUFFER_SIZE);
156// bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
157// new WriteBufferWaterMark(8 * 1024, 32 * 1024));
tom7ef8ff92014-09-17 13:08:06 -0700158
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700159 bootstrap.childHandler(new OFChannelInitializer(this, null, sslContext));
tom7ef8ff92014-09-17 13:08:06 -0700160
Brian O'Connorff278502015-09-22 14:49:52 -0700161 openFlowPorts.forEach(port -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700162 // TODO revisit if this is best way to listen to multiple ports
163 cg.add(bootstrap.bind(port).syncUninterruptibly().channel());
164 log.info("Listening for switch connections on {}", port);
Brian O'Connorff278502015-09-22 14:49:52 -0700165 });
tom7ef8ff92014-09-17 13:08:06 -0700166
tom7ef8ff92014-09-17 13:08:06 -0700167 } catch (Exception e) {
168 throw new RuntimeException(e);
169 }
170
171 }
172
173 private ServerBootstrap createServerBootStrap() {
174
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700175 int bossThreads = Math.max(1, openFlowPorts.size());
176 try {
177 bossGroup = new EpollEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
178 workerGroup = new EpollEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
179 ServerBootstrap bs = new ServerBootstrap()
180 .group(bossGroup, workerGroup)
181 .channel(EpollServerSocketChannel.class);
182 log.info("Using Epoll transport");
183 return bs;
184 } catch (Throwable e) {
185 log.debug("Failed to initialize native (epoll) transport: {}", e.getMessage());
tom7ef8ff92014-09-17 13:08:06 -0700186 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700187
188// Requires 4.1.11 or later
189// try {
190// bossGroup = new KQueueEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
191// workerGroup = new KQueueEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
192// ServerBootstrap bs = new ServerBootstrap()
193// .group(bossGroup, workerGroup)
194// .channel(KQueueServerSocketChannel.class);
195// log.info("Using Kqueue transport");
196// return bs;
197// } catch (Throwable e) {
198// log.debug("Failed to initialize native (kqueue) transport. ", e.getMessage());
199// }
200
201 bossGroup = new NioEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
202 workerGroup = new NioEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
203 log.info("Using Nio transport");
204 return new ServerBootstrap()
205 .group(bossGroup, workerGroup)
206 .channel(NioServerSocketChannel.class);
tom7ef8ff92014-09-17 13:08:06 -0700207 }
208
Brian O'Connorff278502015-09-22 14:49:52 -0700209 public void setConfigParams(Dictionary<?, ?> properties) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700210 // TODO should be possible to reconfigure ports without restart,
211 // by updating ChannelGroup
Brian O'Connorff278502015-09-22 14:49:52 -0700212 String ports = get(properties, "openflowPorts");
213 if (!Strings.isNullOrEmpty(ports)) {
214 this.openFlowPorts = Stream.of(ports.split(","))
215 .map(s -> Integer.parseInt(s))
216 .collect(Collectors.toList());
tom7ef8ff92014-09-17 13:08:06 -0700217 }
Brian O'Connorff278502015-09-22 14:49:52 -0700218 log.debug("OpenFlow ports set to {}", this.openFlowPorts);
Charles Chan45624b82015-08-24 00:29:20 +0800219
Brian O'Connorff278502015-09-22 14:49:52 -0700220 String threads = get(properties, "workerThreads");
221 if (!Strings.isNullOrEmpty(threads)) {
222 this.workerThreads = Integer.parseInt(threads);
223 }
tom7ef8ff92014-09-17 13:08:06 -0700224 log.debug("Number of worker threads set to {}", this.workerThreads);
225 }
226
tom7ef8ff92014-09-17 13:08:06 -0700227 /**
228 * Initialize internal data structures.
229 */
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800230 public void init() {
tom7ef8ff92014-09-17 13:08:06 -0700231 // These data structures are initialized here because other
232 // module's startUp() might be called before ours
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800233 this.controllerNodeIPsCache = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700234
tom7ef8ff92014-09-17 13:08:06 -0700235 this.systemStartTime = System.currentTimeMillis();
alshabib9b6c19c2015-09-26 12:19:27 -0700236
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700237 cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
238
alshabib9b6c19c2015-09-26 12:19:27 -0700239 try {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800240 getTlsParameters();
241 if (enableOfTls) {
242 initSsl();
alshabib9b6c19c2015-09-26 12:19:27 -0700243 }
244 } catch (Exception ex) {
245 log.error("SSL init failed: {}", ex.getMessage());
246 }
alshabib9b6c19c2015-09-26 12:19:27 -0700247 }
248
Jonathan Hartb35540a2015-11-17 09:30:56 -0800249 private void getTlsParameters() {
alshabib9b6c19c2015-09-26 12:19:27 -0700250 String tempString = System.getProperty("enableOFTLS");
Jonathan Hartb35540a2015-11-17 09:30:56 -0800251 enableOfTls = Strings.isNullOrEmpty(tempString) ? TLS_DISABLED : Boolean.parseBoolean(tempString);
252 log.info("OpenFlow Security is {}", enableOfTls ? "enabled" : "disabled");
253 if (enableOfTls) {
alshabib9b6c19c2015-09-26 12:19:27 -0700254 ksLocation = System.getProperty("javax.net.ssl.keyStore");
255 if (Strings.isNullOrEmpty(ksLocation)) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800256 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700257 return;
258 }
259 tsLocation = System.getProperty("javax.net.ssl.trustStore");
260 if (Strings.isNullOrEmpty(tsLocation)) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800261 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700262 return;
263 }
264 ksPwd = System.getProperty("javax.net.ssl.keyStorePassword").toCharArray();
265 if (MIN_KS_LENGTH > ksPwd.length) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800266 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700267 return;
268 }
269 tsPwd = System.getProperty("javax.net.ssl.trustStorePassword").toCharArray();
270 if (MIN_KS_LENGTH > tsPwd.length) {
Jonathan Hartb35540a2015-11-17 09:30:56 -0800271 enableOfTls = TLS_DISABLED;
alshabib9b6c19c2015-09-26 12:19:27 -0700272 return;
273 }
274 }
275 }
276
Jonathan Hartb35540a2015-11-17 09:30:56 -0800277 private void initSsl() throws Exception {
alshabib9b6c19c2015-09-26 12:19:27 -0700278 TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
279 KeyStore ts = KeyStore.getInstance("JKS");
280 ts.load(new FileInputStream(tsLocation), tsPwd);
281 tmFactory.init(ts);
282
283 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
284 KeyStore ks = KeyStore.getInstance("JKS");
285 ks.load(new FileInputStream(ksLocation), ksPwd);
286 kmf.init(ks, ksPwd);
287
alshabib5162a9d2015-12-07 17:49:37 -0800288 sslContext = SSLContext.getInstance("TLS");
289 sslContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
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}