blob: 92358c414e41fed5092a9910e395ebfad2957e83 [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'Connorf7215b82018-05-10 19:12:44 -070019import com.google.common.base.MoreObjects;
Brian O'Connorff278502015-09-22 14:49:52 -070020import com.google.common.base.Strings;
21import com.google.common.collect.ImmutableList;
Brian O'Connorf7215b82018-05-10 19:12:44 -070022import com.google.common.collect.Sets;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070023import io.netty.bootstrap.ServerBootstrap;
Brian O'Connorf7215b82018-05-10 19:12:44 -070024import io.netty.channel.Channel;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070025import io.netty.channel.ChannelOption;
26import io.netty.channel.EventLoopGroup;
27import io.netty.channel.epoll.EpollEventLoopGroup;
28import io.netty.channel.epoll.EpollServerSocketChannel;
29import io.netty.channel.group.ChannelGroup;
30import io.netty.channel.group.DefaultChannelGroup;
31import io.netty.channel.nio.NioEventLoopGroup;
32import io.netty.channel.socket.nio.NioServerSocketChannel;
33import io.netty.util.concurrent.GlobalEventExecutor;
Jonathan Harta0d12492015-07-16 12:03:41 -070034import org.onlab.util.ItemNotFoundException;
35import org.onosproject.net.DeviceId;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070036import org.onosproject.net.config.NetworkConfigRegistry;
alshabibb452fd72015-04-22 20:46:20 -070037import org.onosproject.net.driver.DefaultDriverData;
38import org.onosproject.net.driver.DefaultDriverHandler;
39import org.onosproject.net.driver.Driver;
40import org.onosproject.net.driver.DriverService;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070041import org.onosproject.openflow.config.OpenFlowDeviceConfig;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.openflow.controller.Dpid;
43import org.onosproject.openflow.controller.driver.OpenFlowAgent;
44import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
tom7ef8ff92014-09-17 13:08:06 -070045import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
tom7ef8ff92014-09-17 13:08:06 -070046import org.projectfloodlight.openflow.protocol.OFVersion;
47import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
49
alshabib9b6c19c2015-09-26 12:19:27 -070050import javax.net.ssl.KeyManagerFactory;
51import javax.net.ssl.SSLContext;
alshabib9b6c19c2015-09-26 12:19:27 -070052import javax.net.ssl.TrustManagerFactory;
53import java.io.FileInputStream;
Ray Milkey986a47a2018-01-25 11:38:51 -080054import java.io.IOException;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080055import java.lang.management.ManagementFactory;
56import java.lang.management.RuntimeMXBean;
Brian O'Connorf7215b82018-05-10 19:12:44 -070057import java.net.InetSocketAddress;
58import java.net.SocketAddress;
Ray Milkey986a47a2018-01-25 11:38:51 -080059import java.security.KeyManagementException;
alshabib9b6c19c2015-09-26 12:19:27 -070060import java.security.KeyStore;
Ray Milkey986a47a2018-01-25 11:38:51 -080061import java.security.KeyStoreException;
62import java.security.NoSuchAlgorithmException;
63import java.security.UnrecoverableKeyException;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070064import java.security.cert.Certificate;
Ray Milkey986a47a2018-01-25 11:38:51 -080065import java.security.cert.CertificateException;
Brian O'Connorf7215b82018-05-10 19:12:44 -070066import java.util.Collection;
67import java.util.Collections;
Brian O'Connorff278502015-09-22 14:49:52 -070068import java.util.Dictionary;
Brian O'Connorf7215b82018-05-10 19:12:44 -070069import java.util.EnumSet;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080070import java.util.HashMap;
Brian O'Connorf7215b82018-05-10 19:12:44 -070071import java.util.Iterator;
Brian O'Connorff278502015-09-22 14:49:52 -070072import java.util.List;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080073import java.util.Map;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070074import java.util.Objects;
75import java.util.Optional;
Brian O'Connorf7215b82018-05-10 19:12:44 -070076import java.util.Set;
Brian O'Connorff278502015-09-22 14:49:52 -070077import java.util.stream.Collectors;
78import java.util.stream.Stream;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080079
Brian O'Connorff278502015-09-22 14:49:52 -070080import static org.onlab.util.Tools.get;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080081import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuska80b0a802015-07-17 08:43:30 -070082import static org.onosproject.net.DeviceId.deviceId;
83import static org.onosproject.openflow.controller.Dpid.uri;
Thomas Vachuska6f94ded2015-02-21 14:02:38 -080084
tom7ef8ff92014-09-17 13:08:06 -070085
86/**
87 * The main controller class. Handles all setup and network listeners
tom7ef8ff92014-09-17 13:08:06 -070088 */
89public class Controller {
90
Ray Milkey9c9cde42018-01-12 14:22:06 -080091 private static final Logger log = LoggerFactory.getLogger(Controller.class);
alshabib9eab22f2014-10-20 17:17:31 -070092
alshabib9b6c19c2015-09-26 12:19:27 -070093 private static final short MIN_KS_LENGTH = 6;
tom7ef8ff92014-09-17 13:08:06 -070094
tom7ef8ff92014-09-17 13:08:06 -070095 protected HashMap<String, String> controllerNodeIPsCache;
96
97 private ChannelGroup cg;
98
99 // Configuration options
Brian O'Connorff278502015-09-22 14:49:52 -0700100 protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653);
Yuta HIGUCHI8552b172016-07-25 12:10:08 -0700101 protected int workerThreads = 0;
tom7ef8ff92014-09-17 13:08:06 -0700102
103 // Start time of the controller
104 protected long systemStartTime;
105
106 private OpenFlowAgent agent;
107
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700108 private EventLoopGroup bossGroup;
109 private EventLoopGroup workerGroup;
tom7ef8ff92014-09-17 13:08:06 -0700110
Brian O'Connorf7215b82018-05-10 19:12:44 -0700111 enum TlsMode {
112 DISABLED, // TLS is not used for OpenFlow connections
113 ENABLED, // Clients are required use TLS and present a client certificate
114 STRICT, // Clients must use TLS, and certificate must match the one specified in netcfg
115 }
116 private static final EnumSet<TlsMode> TLS_ENABLED = EnumSet.of(TlsMode.ENABLED, TlsMode.STRICT);
117
118 protected TlsParams tlsParams;
alshabib5162a9d2015-12-07 17:49:37 -0800119 protected SSLContext sslContext;
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700120 protected KeyStore keyStore;
alshabib9b6c19c2015-09-26 12:19:27 -0700121
tom7ef8ff92014-09-17 13:08:06 -0700122 // Perf. related configuration
123 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800124
alshabibb452fd72015-04-22 20:46:20 -0700125 private DriverService driverService;
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700126 private NetworkConfigRegistry netCfgService;
tom7ef8ff92014-09-17 13:08:06 -0700127
Brian O'Connorf7215b82018-05-10 19:12:44 -0700128
129
tom7ef8ff92014-09-17 13:08:06 -0700130 // **************
131 // Initialization
132 // **************
133
Brian O'Connorf7215b82018-05-10 19:12:44 -0700134 private void addListeningPorts(Collection<Integer> ports) {
135 if (cg == null) {
136 return;
137 }
138 final ServerBootstrap bootstrap = createServerBootStrap();
139 bootstrap.option(ChannelOption.SO_REUSEADDR, true);
140 bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
141 bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
142 bootstrap.childOption(ChannelOption.SO_SNDBUF, Controller.SEND_BUFFER_SIZE);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700143// bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK,
144// new WriteBufferWaterMark(8 * 1024, 32 * 1024));
tom7ef8ff92014-09-17 13:08:06 -0700145
Brian O'Connorf7215b82018-05-10 19:12:44 -0700146 bootstrap.childHandler(new OFChannelInitializer(this, null, sslContext));
tom7ef8ff92014-09-17 13:08:06 -0700147
Brian O'Connorf7215b82018-05-10 19:12:44 -0700148 Set<Integer> existingPorts = cg.stream()
149 .map(Channel::localAddress)
150 .filter(InetSocketAddress.class::isInstance)
151 .map(InetSocketAddress.class::cast)
152 .map(InetSocketAddress::getPort)
153 .collect(Collectors.toSet());
154 ports.removeAll(existingPorts);
tom7ef8ff92014-09-17 13:08:06 -0700155
Brian O'Connorf7215b82018-05-10 19:12:44 -0700156 ports.forEach(port -> {
157 // TODO revisit if this is best way to listen to multiple ports
158 cg.add(bootstrap.bind(port).syncUninterruptibly().channel());
159 log.info("Listening for OF switch connections on {}", port);
160 });
161 }
162
163 private void removeListeningPorts(Collection<Integer> ports) {
164 if (cg == null) {
165 return;
166 }
167 Iterator<Channel> itr = cg.iterator();
168 while (itr.hasNext()) {
169 Channel c = itr.next();
170 SocketAddress addr = c.localAddress();
171 if (addr instanceof InetSocketAddress) {
172 InetSocketAddress inetAddr = (InetSocketAddress) addr;
173 Integer port = inetAddr.getPort();
174 if (ports.contains(port)) {
175 log.info("No longer listening for OF switch connections on {}", port);
176 c.close();
177 itr.remove();
178 }
179 }
180
181 }
tom7ef8ff92014-09-17 13:08:06 -0700182 }
183
184 private ServerBootstrap createServerBootStrap() {
185
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700186 int bossThreads = Math.max(1, openFlowPorts.size());
187 try {
188 bossGroup = new EpollEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
189 workerGroup = new EpollEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
190 ServerBootstrap bs = new ServerBootstrap()
191 .group(bossGroup, workerGroup)
192 .channel(EpollServerSocketChannel.class);
193 log.info("Using Epoll transport");
194 return bs;
195 } catch (Throwable e) {
196 log.debug("Failed to initialize native (epoll) transport: {}", e.getMessage());
tom7ef8ff92014-09-17 13:08:06 -0700197 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700198
199// Requires 4.1.11 or later
200// try {
201// bossGroup = new KQueueEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
202// workerGroup = new KQueueEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
203// ServerBootstrap bs = new ServerBootstrap()
204// .group(bossGroup, workerGroup)
205// .channel(KQueueServerSocketChannel.class);
206// log.info("Using Kqueue transport");
207// return bs;
208// } catch (Throwable e) {
209// log.debug("Failed to initialize native (kqueue) transport. ", e.getMessage());
210// }
211
212 bossGroup = new NioEventLoopGroup(bossThreads, groupedThreads("onos/of", "boss-%d", log));
213 workerGroup = new NioEventLoopGroup(workerThreads, groupedThreads("onos/of", "worker-%d", log));
214 log.info("Using Nio transport");
215 return new ServerBootstrap()
216 .group(bossGroup, workerGroup)
217 .channel(NioServerSocketChannel.class);
tom7ef8ff92014-09-17 13:08:06 -0700218 }
219
Brian O'Connorff278502015-09-22 14:49:52 -0700220 public void setConfigParams(Dictionary<?, ?> properties) {
Brian O'Connorf7215b82018-05-10 19:12:44 -0700221 boolean restartRequired = setOpenFlowPorts(properties);
222 restartRequired |= setWorkerThreads(properties);
223 restartRequired |= setTlsParameters(properties);
224 if (restartRequired) {
225 restart();
tom7ef8ff92014-09-17 13:08:06 -0700226 }
Brian O'Connorf7215b82018-05-10 19:12:44 -0700227 }
228
229 /**
230 * Gets the list of listening ports from property dict.
231 *
232 * @param properties dictionary
233 * @return true if restart is required
234 */
235 private boolean setWorkerThreads(Dictionary<?, ?> properties) {
236 List<Integer> oldPorts = this.openFlowPorts;
237 String ports = get(properties, "openflowPorts");
238 List<Integer> newPorts = Collections.emptyList();
239 if (!Strings.isNullOrEmpty(ports)) {
240 newPorts = Stream.of(ports.split(","))
241 .map(s -> Integer.parseInt(s))
242 .collect(Collectors.toList());
243 }
244
245 Set<Integer> portsToAdd = Sets.newHashSet(newPorts);
246 portsToAdd.removeAll(oldPorts);
247 addListeningPorts(portsToAdd);
248
249 Set<Integer> portsToRemove = Sets.newHashSet(oldPorts);
250 portsToRemove.removeAll(newPorts);
251 removeListeningPorts(portsToRemove);
252
253 this.openFlowPorts = newPorts;
Brian O'Connorff278502015-09-22 14:49:52 -0700254 log.debug("OpenFlow ports set to {}", this.openFlowPorts);
Brian O'Connorf7215b82018-05-10 19:12:44 -0700255 return false; // restart is never required
256 }
257
258 /**
259 * Gets the number of worker threads from property dict.
260 *
261 * @param properties dictionary
262 * @return true if restart is required
263 */
264 private boolean setOpenFlowPorts(Dictionary<?, ?> properties) {
265 Integer oldValue = this.workerThreads;
Charles Chan45624b82015-08-24 00:29:20 +0800266
Brian O'Connorff278502015-09-22 14:49:52 -0700267 String threads = get(properties, "workerThreads");
268 if (!Strings.isNullOrEmpty(threads)) {
269 this.workerThreads = Integer.parseInt(threads);
270 }
tom7ef8ff92014-09-17 13:08:06 -0700271 log.debug("Number of worker threads set to {}", this.workerThreads);
Brian O'Connorf7215b82018-05-10 19:12:44 -0700272 return oldValue != this.workerThreads; // restart if number of threads has changed
273 }
274
275 private static class TlsParams {
276 TlsMode mode;
277 String ksLocation;
278 String tsLocation;
279 String ksPwd;
280 String tsPwd;
281 //TODO add the hash of the keystore file contents, so that we restart if the keystore has changed
282
283 public char[] ksPwd() {
284 return ksPwd.toCharArray();
285 }
286
287 public char[] tsPwd() {
288 return tsPwd.toCharArray();
289 }
290
291 public boolean isTlsEnabled() {
292 return TLS_ENABLED.contains(mode);
293 }
294
295 @Override
296 public int hashCode() {
297 return 1; //TODO
298 }
299
300 @Override
301 public boolean equals(Object obj) {
302 if (this == obj) {
303 return true;
304 }
305 if (obj instanceof TlsParams) {
306 final TlsParams that = (TlsParams) obj;
307 if (this.getClass() != that.getClass()) {
308 return false;
309 } else if (this.mode == that.mode && this.mode == TlsMode.DISABLED) {
310 // All disabled objects should be equal regardless of other params
311 return true;
312 }
313 return this.mode == that.mode &&
314 Objects.equals(this.ksLocation, that.ksLocation) &&
315 Objects.equals(this.tsLocation, that.tsLocation) &&
316 Objects.equals(this.ksPwd, that.ksPwd) &&
317 Objects.equals(this.tsPwd, that.tsPwd);
318 }
319 return false;
320 }
321
322 @Override
323 public String toString() {
324 return MoreObjects.toStringHelper(this)
325 .add("tlsMode", mode.toString().toLowerCase())
326 .add("ksLocation", ksLocation)
327 .add("tsLocation", tsLocation)
328 .toString();
329 }
330 }
331
332 /**
333 * Gets the TLS parameters from the properties dict, but fallback to the old approach of
334 * system properties if the parameters are missing from the dict.
335 *
336 * @param properties dictionary
337 * @return true if restart is required
338 */
339 private boolean setTlsParameters(Dictionary<?, ?> properties) {
340 TlsParams oldParams = this.tlsParams;
341
342 TlsParams newParams = new TlsParams();
343 String tlsString = get(properties, "tlsMode");
344 if (!Strings.isNullOrEmpty(tlsString)) {
345 try {
346 newParams.mode = TlsMode.valueOf(tlsString.toUpperCase());
347 } catch (IllegalArgumentException e) {
348 log.info("Invalid TLS mode {}. TLS is disabled.", tlsString);
349 newParams.mode = TlsMode.DISABLED;
350 }
351 } else {
352 // Fallback to system properties
353 // TODO this method of configuring TLS is deprecated and should be removed eventually
354 tlsString = System.getProperty("enableOFTLS");
355 newParams.mode = !Strings.isNullOrEmpty(tlsString) && Boolean.parseBoolean(tlsString) ?
356 TlsMode.ENABLED : TlsMode.DISABLED;
357 }
358
359 if (newParams.isTlsEnabled()) {
360 newParams.ksLocation = get(properties, "keyStore");
361 if (Strings.isNullOrEmpty(newParams.ksLocation)) {
362 // Fallback to system properties
363 // TODO remove this eventually
364 newParams.ksLocation = System.getProperty("javax.net.ssl.keyStore");
365 }
366 if (Strings.isNullOrEmpty(newParams.ksLocation)) {
367 newParams.mode = TlsMode.DISABLED;
368 }
369
370 newParams.tsLocation = get(properties, "trustStore");
371 if (Strings.isNullOrEmpty(newParams.tsLocation)) {
372 // Fallback to system properties
373 // TODO remove this eventually
374 newParams.tsLocation = System.getProperty("javax.net.ssl.trustStore");
375 }
376 if (Strings.isNullOrEmpty(newParams.tsLocation)) {
377 newParams.mode = TlsMode.DISABLED;
378 }
379
380 newParams.ksPwd = get(properties, "keyStorePassword");
381 if (Strings.isNullOrEmpty(newParams.ksPwd)) {
382 // Fallback to system properties
383 // TODO remove this eventually
384 newParams.ksPwd = System.getProperty("javax.net.ssl.keyStorePassword");
385 }
386 if (Strings.isNullOrEmpty(newParams.ksPwd) || MIN_KS_LENGTH > newParams.ksPwd.length()) {
387 newParams.mode = TlsMode.DISABLED;
388 }
389
390 newParams.tsPwd = get(properties, "trustStorePassword");
391 if (Strings.isNullOrEmpty(newParams.tsPwd)) {
392 // Fallback to system properties
393 // TODO remove this eventually
394 newParams.tsPwd = System.getProperty("javax.net.ssl.trustStorePassword");
395 }
396 if (Strings.isNullOrEmpty(newParams.tsPwd) || MIN_KS_LENGTH > newParams.tsPwd.length()) {
397 newParams.mode = TlsMode.DISABLED;
398 }
399 }
400 this.tlsParams = newParams;
401 log.info("OpenFlow TLS Params: {}", tlsParams);
402 return !Objects.equals(newParams, oldParams); // restart if TLS params change
tom7ef8ff92014-09-17 13:08:06 -0700403 }
404
tom7ef8ff92014-09-17 13:08:06 -0700405 /**
406 * Initialize internal data structures.
407 */
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800408 public void init() {
tom7ef8ff92014-09-17 13:08:06 -0700409 // These data structures are initialized here because other
410 // module's startUp() might be called before ours
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800411 this.controllerNodeIPsCache = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700412
tom7ef8ff92014-09-17 13:08:06 -0700413 this.systemStartTime = System.currentTimeMillis();
alshabib9b6c19c2015-09-26 12:19:27 -0700414
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700415 cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
416
Brian O'Connorf7215b82018-05-10 19:12:44 -0700417 if (tlsParams.isTlsEnabled()) {
Ray Milkey986a47a2018-01-25 11:38:51 -0800418 initSsl();
alshabib9b6c19c2015-09-26 12:19:27 -0700419 }
alshabib9b6c19c2015-09-26 12:19:27 -0700420 }
421
Ray Milkey986a47a2018-01-25 11:38:51 -0800422 private void initSsl() {
423 try {
424 TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
425 KeyStore ts = KeyStore.getInstance("JKS");
Brian O'Connorf7215b82018-05-10 19:12:44 -0700426 ts.load(new FileInputStream(tlsParams.tsLocation), tlsParams.tsPwd());
Ray Milkey986a47a2018-01-25 11:38:51 -0800427 tmFactory.init(ts);
alshabib9b6c19c2015-09-26 12:19:27 -0700428
Ray Milkey986a47a2018-01-25 11:38:51 -0800429 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700430 keyStore = KeyStore.getInstance("JKS");
Brian O'Connorf7215b82018-05-10 19:12:44 -0700431 keyStore.load(new FileInputStream(tlsParams.ksLocation), tlsParams.ksPwd());
432 kmf.init(keyStore, tlsParams.ksPwd());
alshabib9b6c19c2015-09-26 12:19:27 -0700433
Ray Milkey986a47a2018-01-25 11:38:51 -0800434 sslContext = SSLContext.getInstance("TLS");
435 sslContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
436 } catch (NoSuchAlgorithmException | KeyStoreException | CertificateException |
437 IOException | KeyManagementException | UnrecoverableKeyException ex) {
438 log.error("SSL init failed: {}", ex.getMessage());
439 }
tom7ef8ff92014-09-17 13:08:06 -0700440 }
441
442 // **************
443 // Utility methods
444 // **************
445
446 public Map<String, Long> getMemory() {
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800447 Map<String, Long> m = new HashMap<>();
tom7ef8ff92014-09-17 13:08:06 -0700448 Runtime runtime = Runtime.getRuntime();
449 m.put("total", runtime.totalMemory());
450 m.put("free", runtime.freeMemory());
451 return m;
452 }
453
454
Ray Milkeydc0ff192015-11-04 13:49:52 -0800455 public Long getSystemUptime() {
tom7ef8ff92014-09-17 13:08:06 -0700456 RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
457 return rb.getUptime();
458 }
459
Ray Milkeydc0ff192015-11-04 13:49:52 -0800460 public long getSystemStartTime() {
461 return (this.systemStartTime);
462 }
463
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700464 public boolean isValidCertificate(Long dpid, Certificate peerCert) {
Brian O'Connorf7215b82018-05-10 19:12:44 -0700465 if (!tlsParams.isTlsEnabled()) {
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700466 return true;
467 }
468
Brian O'Connorf7215b82018-05-10 19:12:44 -0700469 if (netCfgService == null) {
470 // netcfg service not available; accept any cert if not in strict mode
471 return tlsParams.mode == TlsMode.ENABLED;
472 }
473
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700474 DeviceId deviceId = DeviceId.deviceId(Dpid.uri(new Dpid(dpid)));
475 OpenFlowDeviceConfig config =
476 netCfgService.getConfig(deviceId, OpenFlowDeviceConfig.class);
477 if (config == null) {
Brian O'Connorf7215b82018-05-10 19:12:44 -0700478 // Config not set for device, accept any cert if not in strict mode
479 return tlsParams.mode == TlsMode.ENABLED;
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700480 }
481
482 Optional<String> alias = config.keyAlias();
483 if (!alias.isPresent()) {
Brian O'Connorf7215b82018-05-10 19:12:44 -0700484 // Config for device does not specify a certificate chain, accept any cert if not in strict mode
485 return tlsParams.mode == TlsMode.ENABLED;
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700486 }
487
488 try {
489 Certificate configuredCert = keyStore.getCertificate(alias.get());
Brian O'Connorf7215b82018-05-10 19:12:44 -0700490 //TODO there's probably a better way to compare these
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700491 return Objects.deepEquals(peerCert, configuredCert);
492 } catch (KeyStoreException e) {
493 log.info("failed to load key", e);
494 }
495 return false;
496 }
497
tom7ef8ff92014-09-17 13:08:06 -0700498 /**
499 * Forward to the driver-manager to get an IOFSwitch instance.
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800500 *
Yuta HIGUCHI5c947272014-11-03 21:39:21 -0800501 * @param dpid data path id
502 * @param desc switch description
Thomas Vachuska6f94ded2015-02-21 14:02:38 -0800503 * @param ofv OpenFlow version
tom7ef8ff92014-09-17 13:08:06 -0700504 * @return switch instance
505 */
506 protected OpenFlowSwitchDriver getOFSwitchInstance(long dpid,
alshabibb452fd72015-04-22 20:46:20 -0700507 OFDescStatsReply desc,
508 OFVersion ofv) {
Jonathan Harta0d12492015-07-16 12:03:41 -0700509 Dpid dpidObj = new Dpid(dpid);
510
511 Driver driver;
512 try {
Sho SHIMIZUbc82ebb2015-08-25 10:15:21 -0700513 driver = driverService.getDriver(DeviceId.deviceId(Dpid.uri(dpidObj)));
Jonathan Harta0d12492015-07-16 12:03:41 -0700514 } catch (ItemNotFoundException e) {
515 driver = driverService.getDriver(desc.getMfrDesc(), desc.getHwDesc(), desc.getSwDesc());
516 }
alshabibb452fd72015-04-22 20:46:20 -0700517
Jonathan Harta33134e2016-07-27 17:33:35 -0700518 if (driver == null) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700519 log.error("No OpenFlow driver for {} : {}", dpidObj, desc);
Jonathan Harta33134e2016-07-27 17:33:35 -0700520 return null;
alshabibb452fd72015-04-22 20:46:20 -0700521 }
alshabibb452fd72015-04-22 20:46:20 -0700522
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700523 log.info("Driver '{}' assigned to device {}", driver.name(), dpidObj);
Jonathan Harta33134e2016-07-27 17:33:35 -0700524
525 if (!driver.hasBehaviour(OpenFlowSwitchDriver.class)) {
526 log.error("Driver {} does not support OpenFlowSwitchDriver behaviour", driver.name());
527 return null;
528 }
529
530 DefaultDriverHandler handler =
531 new DefaultDriverHandler(new DefaultDriverData(driver, deviceId(uri(dpidObj))));
532 OpenFlowSwitchDriver ofSwitchDriver =
533 driver.createBehaviour(handler, OpenFlowSwitchDriver.class);
534 ofSwitchDriver.init(dpidObj, desc, ofv);
535 ofSwitchDriver.setAgent(agent);
536 ofSwitchDriver.setRoleHandler(new RoleManager(ofSwitchDriver));
Jonathan Harta33134e2016-07-27 17:33:35 -0700537 return ofSwitchDriver;
tom7ef8ff92014-09-17 13:08:06 -0700538 }
539
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700540 @Deprecated
alshabibb452fd72015-04-22 20:46:20 -0700541 public void start(OpenFlowAgent ag, DriverService driverService) {
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700542 start(ag, driverService, null);
543 }
544
545 public void start(OpenFlowAgent ag, DriverService driverService,
546 NetworkConfigRegistry netCfgService) {
tom7ef8ff92014-09-17 13:08:06 -0700547 log.info("Starting OpenFlow IO");
548 this.agent = ag;
alshabibb452fd72015-04-22 20:46:20 -0700549 this.driverService = driverService;
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700550 this.netCfgService = netCfgService;
Jonathan Hartbbd91d42015-02-27 11:18:04 -0800551 this.init();
Brian O'Connorf7215b82018-05-10 19:12:44 -0700552 this.addListeningPorts(this.openFlowPorts);
tom7ef8ff92014-09-17 13:08:06 -0700553 }
554
555
556 public void stop() {
557 log.info("Stopping OpenFlow IO");
Brian O'Connorf7215b82018-05-10 19:12:44 -0700558 if (cg != null) {
559 cg.close();
560 cg = null;
561 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700562
563 // Shut down all event loops to terminate all threads.
564 bossGroup.shutdownGracefully();
565 workerGroup.shutdownGracefully();
566
567 // Wait until all threads are terminated.
568 try {
569 bossGroup.terminationFuture().sync();
570 workerGroup.terminationFuture().sync();
571 } catch (InterruptedException e) {
572 log.warn("Interrupted while stopping", e);
573 Thread.currentThread().interrupt();
574 }
tom7ef8ff92014-09-17 13:08:06 -0700575 }
576
Brian O'Connorf7215b82018-05-10 19:12:44 -0700577 private void restart() {
578 // only restart if we are already running
579 if (cg != null) {
580 stop();
581 start(this.agent, this.driverService, this.netCfgService);
582 }
583 }
584
tom7ef8ff92014-09-17 13:08:06 -0700585}