3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
12 import java.io.FileInputStream;
13 import java.nio.ByteBuffer;
14 import java.nio.channels.AsynchronousCloseException;
15 import java.nio.channels.SelectionKey;
16 import java.nio.channels.Selector;
17 import java.nio.channels.SocketChannel;
18 import java.security.KeyStore;
19 import java.security.SecureRandom;
20 import java.util.List;
21 import javax.net.ssl.KeyManagerFactory;
22 import javax.net.ssl.SSLContext;
23 import javax.net.ssl.SSLEngine;
24 import javax.net.ssl.SSLEngineResult;
25 import javax.net.ssl.SSLSession;
26 import javax.net.ssl.TrustManagerFactory;
27 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
28 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageReadWrite;
29 import org.openflow.protocol.OFMessage;
30 import org.openflow.protocol.factory.BasicFactory;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
35 * This class implements methods to read/write messages over an established
36 * socket channel. The data exchange is encrypted/decrypted by SSLEngine.
38 public class SecureMessageReadWriteService implements IMessageReadWrite {
39 private static final Logger logger = LoggerFactory
40 .getLogger(SecureMessageReadWriteService.class);
42 private Selector selector;
43 private SelectionKey clientSelectionKey;
44 private SocketChannel socket;
45 private BasicFactory factory;
47 private SSLEngine sslEngine;
48 private SSLEngineResult sslEngineResult; // results from sslEngine last operation
49 private ByteBuffer myAppData; // clear text message to be sent
50 private ByteBuffer myNetData; // encrypted message to be sent
51 private ByteBuffer peerAppData; // clear text message received from the switch
52 private ByteBuffer peerNetData; // encrypted message from the switch
54 public SecureMessageReadWriteService(SocketChannel socket, Selector selector) throws Exception {
56 this.selector = selector;
57 this.factory = new BasicFactory();
59 createSecureChannel(socket);
60 createBuffers(sslEngine);
64 * Bring up secure channel using SSL Engine
66 * @param socket TCP socket channel
69 private void createSecureChannel(SocketChannel socket) throws Exception {
70 String keyStoreFile = System.getProperty("controllerKeyStore");
71 String keyStorePassword = System.getProperty("controllerKeyStorePassword");
72 String trustStoreFile = System.getProperty("controllerTrustStore");;
73 String trustStorePassword = System.getProperty("controllerTrustStorePassword");;
75 KeyStore ks = KeyStore.getInstance("JKS");
76 KeyStore ts = KeyStore.getInstance("JKS");
77 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
78 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
79 ks.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray());
80 ts.load(new FileInputStream(trustStoreFile), trustStorePassword.toCharArray());
81 kmf.init(ks, keyStorePassword.toCharArray());
84 SecureRandom random = new SecureRandom();
87 SSLContext sslContext = SSLContext.getInstance("TLS");
88 sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), random);
89 sslEngine = sslContext.createSSLEngine();
90 sslEngine.setUseClientMode(false);
91 sslEngine.setNeedClientAuth(true);
93 // Do initial handshake
94 doHandshake(socket, sslEngine);
96 this.clientSelectionKey = this.socket.register(this.selector,
97 SelectionKey.OP_READ);
101 * Sends the OF message out over the socket channel. The message is
102 * encrypted by SSL Engine.
104 * @param msg OF message to be sent
108 public void asyncSend(OFMessage msg) throws Exception {
109 synchronized (myAppData) {
110 int msgLen = msg.getLengthU();
111 if (myAppData.remaining() < msgLen) {
112 // increase the buffer size so that it can contain this message
113 ByteBuffer newBuffer = ByteBuffer.allocateDirect(myAppData
117 newBuffer.put(myAppData);
118 myAppData = newBuffer;
120 msg.writeTo(myAppData);
122 sslEngineResult = sslEngine.wrap(myAppData, myNetData);
123 logger.trace("asyncSend sslEngine wrap: {}", sslEngineResult);
124 runDelegatedTasks(sslEngineResult, sslEngine);
126 if (!socket.isOpen()) {
131 socket.write(myNetData);
132 if (myNetData.hasRemaining()) {
138 if (myAppData.hasRemaining()) {
140 this.clientSelectionKey = this.socket.register(
141 this.selector, SelectionKey.OP_WRITE, this);
144 this.clientSelectionKey = this.socket.register(
145 this.selector, SelectionKey.OP_READ, this);
148 logger.trace("Message sent: {}", msg.toString());
153 * Resumes sending the remaining messages in the outgoing buffer
157 public void resumeSend() throws Exception {
158 synchronized (myAppData) {
160 sslEngineResult = sslEngine.wrap(myAppData, myNetData);
161 logger.trace("resumeSend sslEngine wrap: {}", sslEngineResult);
162 runDelegatedTasks(sslEngineResult, sslEngine);
164 if (!socket.isOpen()) {
169 socket.write(myNetData);
170 if (myNetData.hasRemaining()) {
176 if (myAppData.hasRemaining()) {
178 this.clientSelectionKey = this.socket.register(this.selector,
179 SelectionKey.OP_WRITE, this);
182 this.clientSelectionKey = this.socket.register(this.selector,
183 SelectionKey.OP_READ, this);
189 * Reads the incoming network data from the socket, decryptes them and then
190 * retrieves the OF messages.
192 * @return list of OF messages
196 public List<OFMessage> readMessages() throws Exception {
197 if (!socket.isOpen()) {
201 List<OFMessage> msgs = null;
205 bytesRead = socket.read(peerNetData);
207 throw new AsynchronousCloseException();
212 sslEngineResult = sslEngine.unwrap(peerNetData, peerAppData);
213 if (peerNetData.hasRemaining()) {
214 peerNetData.compact();
218 logger.trace("sslEngine unwrap result: {}", sslEngineResult);
219 runDelegatedTasks(sslEngineResult, sslEngine);
220 } while ((sslEngineResult.getStatus() == SSLEngineResult.Status.OK) &&
221 peerNetData.hasRemaining() && (--countDown > 0));
223 if (countDown == 0) {
224 logger.trace("countDown reaches 0. peerNetData pos {} lim {}", peerNetData.position(), peerNetData.limit());
228 msgs = factory.parseMessages(peerAppData);
229 if (peerAppData.hasRemaining()) {
230 peerAppData.compact();
235 this.clientSelectionKey = this.socket.register(
236 this.selector, SelectionKey.OP_READ, this);
242 * If the result indicates that we have outstanding tasks to do,
243 * go ahead and run them in this thread.
245 private void runDelegatedTasks(SSLEngineResult result,
246 SSLEngine engine) throws Exception {
248 if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
250 while ((runnable = engine.getDelegatedTask()) != null) {
251 logger.debug("\trunning delegated task...");
254 HandshakeStatus hsStatus = engine.getHandshakeStatus();
255 if (hsStatus == HandshakeStatus.NEED_TASK) {
257 "handshake shouldn't need additional tasks");
259 logger.debug("\tnew HandshakeStatus: {}", hsStatus);
263 private void doHandshake(SocketChannel socket, SSLEngine engine) throws Exception {
264 SSLSession session = engine.getSession();
265 ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
266 ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
267 ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
268 ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
271 engine.beginHandshake();
272 SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
274 // Process handshaking message
275 while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
276 hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
279 // Receive handshaking data from peer
280 if (socket.read(peerNetData) < 0) {
281 throw new AsynchronousCloseException();
284 // Process incoming handshaking data
286 SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
287 peerNetData.compact();
288 hs = res.getHandshakeStatus();
291 switch (res.getStatus()) {
299 // Empty the local network packet buffer.
302 // Generate handshaking data
303 res = engine.wrap(myAppData, myNetData);
304 hs = res.getHandshakeStatus();
307 switch (res.getStatus()) {
311 // Send the handshaking data to peer
312 while (myNetData.hasRemaining()) {
313 if (socket.write(myNetData) < 0) {
314 throw new AsynchronousCloseException();
322 // Handle blocking tasks
324 while ((runnable = engine.getDelegatedTask()) != null) {
325 logger.debug("\trunning delegated task...");
328 hs = engine.getHandshakeStatus();
329 if (hs == HandshakeStatus.NEED_TASK) {
331 "handshake shouldn't need additional tasks");
333 logger.debug("\tnew HandshakeStatus: {}", hs);
339 private void createBuffers(SSLEngine engine) {
340 SSLSession session = engine.getSession();
341 this.myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
342 this.peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
343 this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
344 this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());