/*
* Copyright (c) 2013 Pantheon Technologies s.r.o. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.openflowjava.protocol.impl.core;
import io.netty.channel.Channel;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLEngine;
import org.opendaylight.openflowjava.protocol.impl.core.connection.ConnectionAdapterFactory;
import org.opendaylight.openflowjava.protocol.impl.core.connection.ConnectionAdapterFactoryImpl;
import org.opendaylight.openflowjava.protocol.impl.core.connection.ConnectionFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Initializes TCP / TLS channel.
* @author michal.polkorab
*/
public class TcpChannelInitializer extends ProtocolChannelInitializer<SocketChannel> {
private static final Logger LOG = LoggerFactory.getLogger(TcpChannelInitializer.class);
private final DefaultChannelGroup allChannels;
private final ConnectionAdapterFactory connectionAdapterFactory;
/**
* Default constructor.
*/
public TcpChannelInitializer() {
this( new DefaultChannelGroup("netty-receiver", null), new ConnectionAdapterFactoryImpl() );
}
/**
* Testing constructor.
*/
protected TcpChannelInitializer( final DefaultChannelGroup channelGroup, final ConnectionAdapterFactory connAdaptorFactory ) {
allChannels = channelGroup ;
connectionAdapterFactory = connAdaptorFactory ;
}
@Override
protected void initChannel(final SocketChannel ch) {
if (ch.remoteAddress() != null) {
final InetAddress switchAddress = ch.remoteAddress().getAddress();
final int port = ch.localAddress().getPort();
final int remotePort = ch.remoteAddress().getPort();
LOG.debug("Incoming connection from (remote address): {}:{} --> :{}",
switchAddress.toString(), remotePort, port);
if (!getSwitchConnectionHandler().accept(switchAddress)) {
ch.disconnect();
LOG.debug("Incoming connection rejected");
return;
}
}
LOG.debug("Incoming connection accepted - building pipeline");
allChannels.add(ch);
ConnectionFacade connectionFacade = null;
connectionFacade = connectionAdapterFactory.createConnectionFacade(ch, null, useBarrier());
try {
LOG.debug("Calling OF plugin: {}", getSwitchConnectionHandler());
getSwitchConnectionHandler().onSwitchConnected(connectionFacade);
connectionFacade.checkListeners();
ch.pipeline().addLast(PipelineHandlers.IDLE_HANDLER.name(),
new IdleHandler(getSwitchIdleTimeout(), TimeUnit.MILLISECONDS));
boolean tlsPresent = false;
// If this channel is configured to support SSL it will only support SSL
if (getTlsConfiguration() != null) {
tlsPresent = true;
final SslContextFactory sslFactory = new SslContextFactory(getTlsConfiguration());
final SSLEngine engine = sslFactory.getServerContext().createSSLEngine();
engine.setNeedClientAuth(true);
engine.setUseClientMode(false);
List<String> suitesList = getTlsConfiguration().getCipherSuites();
if (suitesList != null && !suitesList.isEmpty()) {
LOG.debug("Requested Cipher Suites are: {}", suitesList);
String[] suites = suitesList.toArray(new String[suitesList.size()]);
engine.setEnabledCipherSuites(suites);
LOG.debug("Cipher suites enabled in SSLEngine are: {}", engine.getEnabledCipherSuites().toString());
}
final SslHandler ssl = new SslHandler(engine);
final Future<Channel> handshakeFuture = ssl.handshakeFuture();
final ConnectionFacade finalConnectionFacade = connectionFacade;
handshakeFuture.addListener(new GenericFutureListener<Future<? super Channel>>() {
@Override
public void operationComplete(final Future<? super Channel> future) throws Exception {
finalConnectionFacade.fireConnectionReadyNotification();
}
});
ch.pipeline().addLast(PipelineHandlers.SSL_HANDLER.name(), ssl);
}
ch.pipeline().addLast(PipelineHandlers.OF_FRAME_DECODER.name(),
new OFFrameDecoder(connectionFacade, tlsPresent));
ch.pipeline().addLast(PipelineHandlers.OF_VERSION_DETECTOR.name(), new OFVersionDetector());
final OFDecoder ofDecoder = new OFDecoder();
ofDecoder.setDeserializationFactory(getDeserializationFactory());
ch.pipeline().addLast(PipelineHandlers.OF_DECODER.name(), ofDecoder);
final OFEncoder ofEncoder = new OFEncoder();
ofEncoder.setSerializationFactory(getSerializationFactory());
ch.pipeline().addLast(PipelineHandlers.OF_ENCODER.name(), ofEncoder);
ch.pipeline().addLast(PipelineHandlers.DELEGATING_INBOUND_HANDLER.name(),
new DelegatingInboundHandler(connectionFacade));
if (!tlsPresent) {
connectionFacade.fireConnectionReadyNotification();
}
} catch (final Exception e) {
LOG.warn("Failed to initialize channel", e);
ch.close();
}
}
/**
* @return iterator through active connections
*/
public Iterator<Channel> getConnectionIterator() {
return allChannels.iterator();
}
/**
* @return amount of active channels
*/
public int size() {
return allChannels.size();
}
}