- If a port is already under OF control and then hw LLDP transmit is enabled on it...
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / core / internal / SecureMessageReadWriteService.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
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
8  */
9
10 package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
11
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;
33
34 /**
35  * This class implements methods to read/write messages over an established
36  * socket channel. The data exchange is encrypted/decrypted by SSLEngine.
37  */
38 public class SecureMessageReadWriteService implements IMessageReadWrite {
39     private static final Logger logger = LoggerFactory
40             .getLogger(SecureMessageReadWriteService.class);
41
42     private Selector selector;
43     private SelectionKey clientSelectionKey;
44     private SocketChannel socket;
45     private BasicFactory factory;
46
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
53
54     public SecureMessageReadWriteService(SocketChannel socket, Selector selector) throws Exception {
55         this.socket = socket;
56         this.selector = selector;
57         this.factory = new BasicFactory();
58
59         createSecureChannel(socket);
60         createBuffers(sslEngine);
61     }
62
63         /**
64          * Bring up secure channel using SSL Engine
65          * 
66          * @param socket TCP socket channel
67          * @throws Exception
68          */
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");
74
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());
82         tmf.init(ts);
83
84         SecureRandom random = new SecureRandom();
85         random.nextInt();
86
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);
92         
93         // Do initial handshake
94         doHandshake(socket, sslEngine);
95         
96         this.clientSelectionKey = this.socket.register(this.selector,
97                 SelectionKey.OP_READ);
98     }
99
100         /**
101          * Sends the OF message out over the socket channel. The message is
102          * encrypted by SSL Engine.
103          * 
104          * @param msg OF message to be sent
105          * @throws Exception
106          */
107     @Override
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
114                                         .capacity()
115                                         + msgLen);
116                         myAppData.flip();
117                         newBuffer.put(myAppData);
118                         myAppData = newBuffer;
119                 }
120                 msg.writeTo(myAppData);
121                 myAppData.flip();
122                 sslEngineResult = sslEngine.wrap(myAppData, myNetData);
123                 logger.trace("asyncSend sslEngine wrap: {}", sslEngineResult);
124                 runDelegatedTasks(sslEngineResult, sslEngine);
125
126                 if (!socket.isOpen()) {
127                         return;
128                 }
129
130                 myNetData.flip();
131                 socket.write(myNetData);
132                 if (myNetData.hasRemaining()) {
133                         myNetData.compact();
134                 } else {
135                         myNetData.clear();
136                 }
137
138                 if (myAppData.hasRemaining()) {
139                         myAppData.compact();
140                         this.clientSelectionKey = this.socket.register(
141                                         this.selector, SelectionKey.OP_WRITE, this);
142                 } else {
143                         myAppData.clear();
144                         this.clientSelectionKey = this.socket.register(
145                                         this.selector, SelectionKey.OP_READ, this);
146                 }
147
148                 logger.trace("Message sent: {}", msg.toString());
149         }
150     }
151
152         /**
153          * Resumes sending the remaining messages in the outgoing buffer
154          * @throws Exception
155          */
156     @Override
157     public void resumeSend() throws Exception {
158                 synchronized (myAppData) {
159                         myAppData.flip();
160                         sslEngineResult = sslEngine.wrap(myAppData, myNetData);
161                         logger.trace("resumeSend sslEngine wrap: {}", sslEngineResult);
162                         runDelegatedTasks(sslEngineResult, sslEngine);
163
164                         if (!socket.isOpen()) {
165                                 return;
166                         }
167
168                         myNetData.flip();
169                         socket.write(myNetData);
170                         if (myNetData.hasRemaining()) {
171                                 myNetData.compact();
172                         } else {
173                                 myNetData.clear();
174                         }
175
176                         if (myAppData.hasRemaining()) {
177                                 myAppData.compact();
178                                 this.clientSelectionKey = this.socket.register(this.selector,
179                                                 SelectionKey.OP_WRITE, this);
180                         } else {
181                                 myAppData.clear();
182                                 this.clientSelectionKey = this.socket.register(this.selector,
183                                                 SelectionKey.OP_READ, this);
184                         }
185                 }
186     }
187
188         /**
189          * Reads the incoming network data from the socket, decryptes them and then
190          * retrieves the OF messages.
191          * 
192          * @return list of OF messages
193          * @throws Exception
194          */
195     @Override
196     public List<OFMessage> readMessages() throws Exception {
197                 if (!socket.isOpen()) {
198                         return null;
199                 }
200
201                 List<OFMessage> msgs = null;
202         int bytesRead = -1;
203         int countDown = 50;             
204
205         bytesRead = socket.read(peerNetData);
206         if (bytesRead < 0) {
207                 logger.debug("Message read operation failed");
208                         throw new AsynchronousCloseException();
209         }
210
211         do {                    
212                 peerNetData.flip();
213                 sslEngineResult = sslEngine.unwrap(peerNetData, peerAppData);
214                 if (peerNetData.hasRemaining()) {
215                         peerNetData.compact();
216                 } else {
217                         peerNetData.clear();
218                 }
219                 logger.trace("sslEngine unwrap result: {}", sslEngineResult);
220                 runDelegatedTasks(sslEngineResult, sslEngine);
221         } while ((sslEngineResult.getStatus() == SSLEngineResult.Status.OK) &&
222                           peerNetData.hasRemaining() && (--countDown > 0));
223         
224         if (countDown == 0) {
225                 logger.trace("countDown reaches 0. peerNetData pos {} lim {}", peerNetData.position(), peerNetData.limit());
226         }
227
228         peerAppData.flip();
229         msgs = factory.parseMessages(peerAppData);
230         if (peerAppData.hasRemaining()) {
231                 peerAppData.compact();
232         } else {
233                 peerAppData.clear();
234         }
235
236         this.clientSelectionKey = this.socket.register(
237                         this.selector, SelectionKey.OP_READ, this);
238         
239         return msgs;
240     }
241
242     /**
243      *  If the result indicates that we have outstanding tasks to do,
244      *  go ahead and run them in this thread.
245      */
246     private void runDelegatedTasks(SSLEngineResult result,
247                 SSLEngine engine) throws Exception {
248
249         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
250                 Runnable runnable;
251                 while ((runnable = engine.getDelegatedTask()) != null) {
252                         logger.debug("\trunning delegated task...");
253                         runnable.run();
254                 }
255                 HandshakeStatus hsStatus = engine.getHandshakeStatus();
256                 if (hsStatus == HandshakeStatus.NEED_TASK) {
257                         throw new Exception(
258                                         "handshake shouldn't need additional tasks");
259                 }
260                 logger.debug("\tnew HandshakeStatus: {}", hsStatus);
261         }
262     }
263
264     private void doHandshake(SocketChannel socket, SSLEngine engine) throws Exception {
265         SSLSession session = engine.getSession();
266         ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
267         ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
268         ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
269         ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
270
271         // Begin handshake
272         engine.beginHandshake();
273         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
274
275         // Process handshaking message
276         while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
277                    hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
278                 switch (hs) {
279                 case NEED_UNWRAP:
280                         // Receive handshaking data from peer
281                         if (socket.read(peerNetData) < 0) {
282                                 throw new AsynchronousCloseException();
283                         }
284
285                         // Process incoming handshaking data
286                         peerNetData.flip();
287                         SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
288                         peerNetData.compact();
289                         hs = res.getHandshakeStatus();
290
291                         // Check status
292                         switch (res.getStatus()) {
293                         case OK :
294                                 // Handle OK status
295                                 break;
296                         }
297                         break;
298
299                 case NEED_WRAP :
300                         // Empty the local network packet buffer.
301                         myNetData.clear();
302
303                         // Generate handshaking data
304                         res = engine.wrap(myAppData, myNetData);
305                         hs = res.getHandshakeStatus();
306
307                         // Check status
308                         switch (res.getStatus()) {
309                         case OK :
310                                 myNetData.flip();
311
312                                 // Send the handshaking data to peer
313                                 while (myNetData.hasRemaining()) {
314                                         if (socket.write(myNetData) < 0) {
315                                         throw new AsynchronousCloseException();
316                                         }
317                                 }
318                                 break;
319                         }
320                         break;
321
322                 case NEED_TASK :
323                         // Handle blocking tasks
324                         Runnable runnable;
325                         while ((runnable = engine.getDelegatedTask()) != null) {
326                                 logger.debug("\trunning delegated task...");
327                                 runnable.run();
328                         }
329                         hs = engine.getHandshakeStatus();
330                         if (hs == HandshakeStatus.NEED_TASK) {
331                                 throw new Exception(
332                                                 "handshake shouldn't need additional tasks");
333                         }
334                         logger.debug("\tnew HandshakeStatus: {}", hs);
335                         break;
336                 }
337         }
338     }
339     
340     private void createBuffers(SSLEngine engine) {
341         SSLSession session = engine.getSession();
342         this.myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
343         this.peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
344         this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
345         this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
346     }
347 }