From fbfd230558d9c43581f8b5d08eb51d0bbde02a4f Mon Sep 17 00:00:00 2001 From: Michal Polkorab Date: Thu, 7 Aug 2014 11:54:00 +0200 Subject: [PATCH] UDP support implementation - unit tests will be added in another change Change-Id: I8b6fba59a82febf60c0ddfb97ef8e2e9267aff56 Signed-off-by: Michal Polkorab --- .../src/main/yang/openflow-configuration.yang | 17 ++ .../impl/connection/ChannelOutboundQueue.java | 14 +- .../connection/ConnectionAdapterFactory.java | 6 +- .../ConnectionAdapterFactoryImpl.java | 8 +- .../connection/ConnectionAdapterImpl.java | 11 +- .../impl/connection/ServerFacade.java | 8 +- .../SwitchConnectionProviderImpl.java | 19 +- .../connection/UdpMessageListenerWrapper.java | 45 ++++ ...ry.java => ChannelInitializerFactory.java} | 22 +- .../impl/core/OFDatagramPacketDecoder.java | 67 ++++++ .../impl/core/OFDatagramPacketEncoder.java | 56 +++++ .../impl/core/OFDatagramPacketHandler.java | 105 +++++++++ .../protocol/impl/core/PipelineHandlers.java | 63 ++++++ .../impl/core/ProtocolChannelInitializer.java | 101 +++++++++ .../core/PublishingChannelInitializer.java | 200 ------------------ .../impl/core/TcpChannelInitializer.java | 116 ++++++++++ .../protocol/impl/core/TcpHandler.java | 12 +- .../impl/core/UdpChannelInitializer.java | 32 +++ .../protocol/impl/core/UdpConnectionMap.java | 48 +++++ .../protocol/impl/core/UdpHandler.java | 151 +++++++++++++ .../impl/core/VersionMessageUdpWrapper.java | 40 ++++ .../SwitchConnectionProviderModule.java | 7 +- ...nflow-switch-connection-provider-impl.yang | 5 + ...blishingChannelInitializerFactoryTest.java | 8 +- .../PublishingChannelInitializerTest.java | 23 +- .../protocol/impl/core/TcpHandlerTest.java | 2 +- .../ConnectionConfigurationImpl.java | 10 + .../it/integration/IntegrationTest.java | 113 ++++++---- .../protocol/impl/clients/OFClient.java | 40 ++++ .../protocol/impl/clients/SimpleClient.java | 27 +-- .../impl/clients/SimpleClientInitializer.java | 6 +- .../impl/clients/UdpSimpleClient.java | 149 +++++++++++++ .../impl/clients/UdpSimpleClientFramer.java | 68 ++++++ .../clients/UdpSimpleClientInitializer.java | 50 +++++ 34 files changed, 1341 insertions(+), 308 deletions(-) create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/UdpMessageListenerWrapper.java rename openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/{PublishingChannelInitializerFactory.java => ChannelInitializerFactory.java} (74%) create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketDecoder.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketEncoder.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketHandler.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PipelineHandlers.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ProtocolChannelInitializer.java delete mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializer.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpChannelInitializer.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpChannelInitializer.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpConnectionMap.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpHandler.java create mode 100644 openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/VersionMessageUdpWrapper.java create mode 100644 simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/OFClient.java create mode 100644 simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClient.java create mode 100644 simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientFramer.java create mode 100644 simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientInitializer.java diff --git a/openflow-protocol-api/src/main/yang/openflow-configuration.yang b/openflow-protocol-api/src/main/yang/openflow-configuration.yang index 36b163cb..8a1f8bbc 100644 --- a/openflow-protocol-api/src/main/yang/openflow-configuration.yang +++ b/openflow-protocol-api/src/main/yang/openflow-configuration.yang @@ -39,4 +39,21 @@ } } } + + typedef transport-protocol { + type enumeration { + enum TCP { + value 0; + description "Communication over TCP protocol."; + } + enum TLS { + value 1; + description "Communication over TLS protocol."; + } + enum UDP { + value 2; + description "Communication over UDP protocol."; + } + } + } } \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ChannelOutboundQueue.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ChannelOutboundQueue.java index 40af2791..6937a1fb 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ChannelOutboundQueue.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ChannelOutboundQueue.java @@ -16,6 +16,7 @@ import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; +import java.net.InetSocketAddress; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; @@ -95,6 +96,7 @@ final class ChannelOutboundQueue extends ChannelInboundHandlerAdapter { private final Queue> queue; private final long maxWorkTime; private final Channel channel; + private InetSocketAddress address; public ChannelOutboundQueue(final Channel channel, final int queueDepth) { Preconditions.checkArgument(queueDepth > 0, "Queue depth has to be positive"); @@ -181,7 +183,13 @@ final class ChannelOutboundQueue extends ChannelInboundHandlerAdapter { } final GenericFutureListener> l = h.takeListener(); - final ChannelFuture p = channel.write(new MessageListenerWrapper(h.takeMessage(), l)); + + final ChannelFuture p; + if (address == null) { + p = channel.write(new MessageListenerWrapper(h.takeMessage(), l)); + } else { + p = channel.write(new UdpMessageListenerWrapper(h.takeMessage(), l, address)); + } if (l != null) { p.addListener(l); } @@ -268,4 +276,8 @@ final class ChannelOutboundQueue extends ChannelInboundHandlerAdapter { public String toString() { return String.format("Channel %s queue [%s messages flushing=%s]", channel, queue.size(), flushScheduled); } + + public void setAddress(InetSocketAddress address) { + this.address = address; + } } diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactory.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactory.java index 3070aba8..dba298b7 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactory.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactory.java @@ -9,7 +9,9 @@ package org.opendaylight.openflowjava.protocol.impl.connection; -import io.netty.channel.socket.SocketChannel; +import java.net.InetSocketAddress; + +import io.netty.channel.Channel; /** * @author mirehak @@ -21,6 +23,6 @@ public interface ConnectionAdapterFactory { * @param ch * @return connection adapter tcp-implementation */ - public ConnectionFacade createConnectionFacade(SocketChannel ch) ; + public ConnectionFacade createConnectionFacade(Channel ch, InetSocketAddress address) ; } diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactoryImpl.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactoryImpl.java index fda222ce..259dd6f5 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactoryImpl.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterFactoryImpl.java @@ -9,7 +9,9 @@ package org.opendaylight.openflowjava.protocol.impl.connection; -import io.netty.channel.socket.SocketChannel; +import java.net.InetSocketAddress; + +import io.netty.channel.Channel; /** * @author mirehak @@ -22,8 +24,8 @@ public class ConnectionAdapterFactoryImpl implements ConnectionAdapterFactory { * @return connection adapter tcp-implementation */ @Override - public ConnectionFacade createConnectionFacade(SocketChannel ch) { - return new ConnectionAdapterImpl(ch); + public ConnectionFacade createConnectionFacade(Channel ch, InetSocketAddress address) { + return new ConnectionAdapterImpl(ch, address); } } diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterImpl.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterImpl.java index 44595d7c..9fe040fc 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterImpl.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ConnectionAdapterImpl.java @@ -9,8 +9,8 @@ package org.opendaylight.openflowjava.protocol.impl.connection; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.channel.socket.SocketChannel; import io.netty.util.concurrent.GenericFutureListener; import java.net.InetSocketAddress; @@ -111,7 +111,7 @@ public class ConnectionAdapterImpl implements ConnectionFacade { private final Cache> responseCache; private final ChannelOutboundQueue output; - private final SocketChannel channel; + private final Channel channel; private ConnectionReadyListener connectionReadyListener; private OpenflowProtocolListener messageListener; @@ -121,14 +121,17 @@ public class ConnectionAdapterImpl implements ConnectionFacade { /** * default ctor * @param channel the channel to be set - used for communication + * @param address client address (used only in case of UDP communication, + * as there is no need to store address over tcp (stable channel)) */ - public ConnectionAdapterImpl(final SocketChannel channel) { + public ConnectionAdapterImpl(final Channel channel, final InetSocketAddress address) { responseCache = CacheBuilder.newBuilder() .concurrencyLevel(1) .expireAfterWrite(RPC_RESPONSE_EXPIRATION, TimeUnit.MINUTES) .removalListener(REMOVAL_LISTENER).build(); this.channel = Preconditions.checkNotNull(channel); this.output = new ChannelOutboundQueue(channel, DEFAULT_QUEUE_DEPTH); + output.setAddress(address); channel.pipeline().addLast(output); LOG.debug("ConnectionAdapter created"); } @@ -471,6 +474,6 @@ public class ConnectionAdapterImpl implements ConnectionFacade { @Override public InetSocketAddress getRemoteAddress() { - return channel.remoteAddress(); + return (InetSocketAddress) channel.remoteAddress(); } } diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ServerFacade.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ServerFacade.java index 84c84df7..39a6eb7e 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ServerFacade.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/ServerFacade.java @@ -9,10 +9,16 @@ package org.opendaylight.openflowjava.protocol.impl.connection; +import org.opendaylight.openflowjava.protocol.api.connection.ThreadConfiguration; + /** * @author mirehak */ public interface ServerFacade extends ShutdownProvider, OnlineProvider, Runnable { - // empty unifying superinterface + /** + * Sets thread configuration + * @param threadConfig desired thread configuration + */ + public void setThreadConfig(ThreadConfiguration threadConfig); } diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/SwitchConnectionProviderImpl.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/SwitchConnectionProviderImpl.java index b3785de6..d8c38cd5 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/SwitchConnectionProviderImpl.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/SwitchConnectionProviderImpl.java @@ -25,13 +25,15 @@ import org.opendaylight.openflowjava.protocol.api.keys.experimenter.Experimenter import org.opendaylight.openflowjava.protocol.api.keys.experimenter.ExperimenterInstructionDeserializerKey; import org.opendaylight.openflowjava.protocol.api.keys.experimenter.ExperimenterInstructionSerializerKey; import org.opendaylight.openflowjava.protocol.api.keys.experimenter.ExperimenterSerializerKey; -import org.opendaylight.openflowjava.protocol.impl.core.PublishingChannelInitializerFactory; +import org.opendaylight.openflowjava.protocol.impl.core.ChannelInitializerFactory; import org.opendaylight.openflowjava.protocol.impl.core.TcpHandler; +import org.opendaylight.openflowjava.protocol.impl.core.UdpHandler; import org.opendaylight.openflowjava.protocol.impl.deserialization.DeserializationFactory; import org.opendaylight.openflowjava.protocol.impl.deserialization.DeserializerRegistryImpl; import org.opendaylight.openflowjava.protocol.impl.serialization.SerializationFactory; import org.opendaylight.openflowjava.protocol.impl.serialization.SerializerRegistryImpl; import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider; +import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.TransportProtocol; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.MatchField; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev130731.OxmClassBase; import org.slf4j.Logger; @@ -118,16 +120,23 @@ public class SwitchConnectionProviderImpl implements SwitchConnectionProvider { /** * @return */ - private TcpHandler createAndConfigureServer() { + private ServerFacade createAndConfigureServer() { LOGGER.debug("Configuring .."); - TcpHandler server = new TcpHandler(connConfig.getAddress(), connConfig.getPort()); - PublishingChannelInitializerFactory factory = new PublishingChannelInitializerFactory(); + ServerFacade server = null; + ChannelInitializerFactory factory = new ChannelInitializerFactory(); factory.setSwitchConnectionHandler(switchConnectionHandler); factory.setSwitchIdleTimeout(connConfig.getSwitchIdleTimeout()); factory.setTlsConfig(connConfig.getTlsConfiguration()); factory.setSerializationFactory(serializationFactory); factory.setDeserializationFactory(deserializationFactory); - server.setChannelInitializer(factory.createPublishingChannelInitializer()); + TransportProtocol transportProtocol = (TransportProtocol) connConfig.getTransferProtocol(); + if (transportProtocol.equals(TransportProtocol.TCP) || transportProtocol.equals(TransportProtocol.TLS)) { + server = new TcpHandler(connConfig.getAddress(), connConfig.getPort()); + ((TcpHandler) server).setChannelInitializer(factory.createPublishingChannelInitializer()); + } else { + server = new UdpHandler(connConfig.getAddress(), connConfig.getPort()); + ((UdpHandler) server).setChannelInitializer(factory.createUdpChannelInitializer()); + } server.setThreadConfig(connConfig.getThreadConfiguration()); return server; } diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/UdpMessageListenerWrapper.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/UdpMessageListenerWrapper.java new file mode 100644 index 00000000..84bf0b19 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/connection/UdpMessageListenerWrapper.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014 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.connection; + +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; + +import java.net.InetSocketAddress; + +/** + * Wraps outgoing message and includes listener attached to this message. This object + * is sent to OFEncoder. When OFEncoder fails to serialize the message, + * listener is filled with exception. The exception is then delegated to upper ODL layers. + * This object is used for UDP communication - it also carries recipient address + + * @author michal.polkorab + */ +public class UdpMessageListenerWrapper extends MessageListenerWrapper { + + private InetSocketAddress address; + + /** + * @param msg message to be sent + * @param listener listener attached to channel.write(msg) Future + * @param address recipient's address + */ + public UdpMessageListenerWrapper(Object msg, GenericFutureListener> listener, + InetSocketAddress address) { + super(msg, listener); + this.address = address; + } + + /** + * @return recipient address + */ + public InetSocketAddress getAddress() { + return address; + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerFactory.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ChannelInitializerFactory.java similarity index 74% rename from openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerFactory.java rename to openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ChannelInitializerFactory.java index 90351dc1..c453b0b0 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerFactory.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ChannelInitializerFactory.java @@ -17,7 +17,7 @@ import org.opendaylight.openflowjava.protocol.impl.serialization.SerializationFa * @author michal.polkorab * */ -public class PublishingChannelInitializerFactory { +public class ChannelInitializerFactory { private long switchIdleTimeOut; private DeserializationFactory deserializationFactory; @@ -28,8 +28,8 @@ public class PublishingChannelInitializerFactory { /** * @return PublishingChannelInitializer that initializes new channels */ - public PublishingChannelInitializer createPublishingChannelInitializer() { - PublishingChannelInitializer initializer = new PublishingChannelInitializer(); + public TcpChannelInitializer createPublishingChannelInitializer() { + TcpChannelInitializer initializer = new TcpChannelInitializer(); initializer.setSwitchIdleTimeout(switchIdleTimeOut); initializer.setDeserializationFactory(deserializationFactory); initializer.setSerializationFactory(serializationFactory); @@ -38,6 +38,18 @@ public class PublishingChannelInitializerFactory { return initializer; } + /** + * @return PublishingChannelInitializer that initializes new channels + */ + public UdpChannelInitializer createUdpChannelInitializer() { + UdpChannelInitializer initializer = new UdpChannelInitializer(); + initializer.setSwitchIdleTimeout(switchIdleTimeOut); + initializer.setDeserializationFactory(deserializationFactory); + initializer.setSerializationFactory(serializationFactory); + initializer.setSwitchConnectionHandler(switchConnectionHandler); + return initializer; + } + /** * @param switchIdleTimeOut */ @@ -72,6 +84,4 @@ public class PublishingChannelInitializerFactory { public void setSwitchConnectionHandler(SwitchConnectionHandler switchConnectionHandler) { this.switchConnectionHandler = switchConnectionHandler; } - - -} +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketDecoder.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketDecoder.java new file mode 100644 index 00000000..7b4c3186 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketDecoder.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 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.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; + +import java.util.List; + +import org.opendaylight.openflowjava.protocol.impl.connection.MessageConsumer; +import org.opendaylight.openflowjava.protocol.impl.deserialization.DeserializationFactory; +import org.opendaylight.openflowjava.util.ByteBufUtils; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author michal.polkorab + * + */ +public class OFDatagramPacketDecoder extends MessageToMessageDecoder{ + + private static final Logger LOGGER = LoggerFactory.getLogger(OFDatagramPacketDecoder.class); + private DeserializationFactory deserializationFactory; + + @Override + protected void decode(ChannelHandlerContext ctx, + VersionMessageUdpWrapper msg, List out) throws Exception { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("UdpVersionMessageWrapper received"); + LOGGER.debug("<< " + ByteBufUtils.byteBufToHexString(msg.getMessageBuffer())); + } + + DataObject dataObject = null; + try { + dataObject = deserializationFactory.deserialize(msg.getMessageBuffer(), + msg.getVersion()); + if (dataObject == null) { + LOGGER.warn("Translated POJO is null"); + } else { + MessageConsumer consumer = UdpConnectionMap.getMessageConsumer(msg.getAddress()); + consumer.consume(dataObject); + } + } catch(Exception e) { + LOGGER.error("Message deserialization failed"); + LOGGER.error(e.getMessage(), e); + // TODO: delegate exception to allow easier deserialization + // debugging / deserialization problem awareness + } finally { + msg.getMessageBuffer().release(); + } + + } + + /** + * @param deserializationFactory + */ + public void setDeserializationFactory(DeserializationFactory deserializationFactory) { + this.deserializationFactory = deserializationFactory; + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketEncoder.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketEncoder.java new file mode 100644 index 00000000..631509d0 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketEncoder.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014 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 java.util.List; + +import org.opendaylight.openflowjava.protocol.impl.connection.UdpMessageListenerWrapper; +import org.opendaylight.openflowjava.protocol.impl.serialization.SerializationFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.DatagramPacket; +import io.netty.handler.codec.MessageToMessageEncoder; +import io.netty.util.concurrent.Future; + +/** + * @author michal.polkorab + * + */ +public class OFDatagramPacketEncoder extends MessageToMessageEncoder { + + private static final Logger LOGGER = LoggerFactory.getLogger(OFDatagramPacketEncoder.class); + private SerializationFactory serializationFactory; + + @Override + protected void encode(ChannelHandlerContext ctx, + UdpMessageListenerWrapper wrapper, List out) throws Exception { + LOGGER.trace("Encoding"); + try { + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(); + serializationFactory.messageToBuffer(wrapper.getMsg().getVersion(), buffer, wrapper.getMsg()); + out.add(new DatagramPacket(buffer, wrapper.getAddress())); + } catch(Exception e) { + LOGGER.warn("Message serialization failed: {}", e.getMessage()); + Future newFailedFuture = ctx.newFailedFuture(e); + wrapper.getListener().operationComplete(newFailedFuture); + return; + } + } + + /** + * @param serializationFactory + */ + public void setSerializationFactory(SerializationFactory serializationFactory) { + this.serializationFactory = serializationFactory; + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketHandler.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketHandler.java new file mode 100644 index 00000000..0f1b8236 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/OFDatagramPacketHandler.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014 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.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.DatagramPacket; +import io.netty.handler.codec.MessageToMessageDecoder; + +import java.util.List; + +import org.opendaylight.openflowjava.protocol.api.connection.SwitchConnectionHandler; +import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants; +import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionAdapterFactory; +import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionAdapterFactoryImpl; +import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionFacade; +import org.opendaylight.openflowjava.protocol.impl.connection.MessageConsumer; +import org.opendaylight.openflowjava.util.ByteBufUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author michal.polkorab + * + */ +public class OFDatagramPacketHandler extends MessageToMessageDecoder { + + private static final Logger LOGGER = LoggerFactory.getLogger(OFDatagramPacketHandler.class); + + /** Length of OpenFlow 1.3 header */ + public static final byte LENGTH_OF_HEADER = 8; + private static final byte LENGTH_INDEX_IN_HEADER = 2; + private ConnectionAdapterFactory adapterFactory = new ConnectionAdapterFactoryImpl(); + private SwitchConnectionHandler connectionHandler; + + /** + * Default constructor + * @param sch the switchConnectionHandler that decides + * what to do with incomming message / channel + */ + public OFDatagramPacketHandler(SwitchConnectionHandler sch) { + this.connectionHandler = sch; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + LOGGER.warn("Unexpected exception from downstream.", cause); + LOGGER.warn("Closing connection."); + ctx.close(); + } + + @Override + protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, + List out) throws Exception { + LOGGER.debug("OFDatagramPacketFramer"); + MessageConsumer consumer = UdpConnectionMap.getMessageConsumer(msg.sender()); + if (consumer == null) { + ConnectionFacade connectionFacade = + adapterFactory.createConnectionFacade(ctx.channel(), msg.sender()); + connectionHandler.onSwitchConnected(connectionFacade); + connectionFacade.checkListeners(); + UdpConnectionMap.addConnection(msg.sender(), connectionFacade); + } + ByteBuf bb = msg.content(); + int readableBytes = bb.readableBytes(); + if (readableBytes < LENGTH_OF_HEADER) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("skipping bytebuf - too few bytes for header: " + readableBytes + " < " + LENGTH_OF_HEADER ); + LOGGER.debug("bb: " + ByteBufUtils.byteBufToHexString(bb)); + } + return; + } + + int length = bb.getUnsignedShort(bb.readerIndex() + LENGTH_INDEX_IN_HEADER); + LOGGER.debug("length of actual message: {}", length); + + if (readableBytes < length) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("skipping bytebuf - too few bytes for msg: " + + readableBytes + " < " + length); + LOGGER.debug("bytebuffer: " + ByteBufUtils.byteBufToHexString(bb)); + } + return; + } + LOGGER.debug("OF Protocol message received, type:{}", bb.getByte(bb.readerIndex() + 1)); + + + byte version = bb.readByte(); + if ((version == EncodeConstants.OF13_VERSION_ID) || (version == EncodeConstants.OF10_VERSION_ID)) { + LOGGER.debug("detected version: " + version); + ByteBuf messageBuffer = bb.slice(); + out.add(new VersionMessageUdpWrapper(version, messageBuffer, msg.sender())); + messageBuffer.retain(); + } else { + LOGGER.warn("detected version: " + version + " - currently not supported"); + } + bb.skipBytes(bb.readableBytes()); + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PipelineHandlers.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PipelineHandlers.java new file mode 100644 index 00000000..9a9bf3c4 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PipelineHandlers.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014 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; + +/** + * Stores names of handlers used in pipeline. + * + * @author michal.polkorab + */ +public enum PipelineHandlers { + + /** + * Detects switch idle state + */ + IDLE_HANDLER, + /** + * Component for handling TLS frames + */ + SSL_HANDLER, + /** + * Decodes incoming messages into message frames + */ + OF_FRAME_DECODER, + /** + * Detects version of incoming OpenFlow Protocol message + */ + OF_VERSION_DETECTOR, + /** + * Transforms OpenFlow Protocol byte messages into POJOs + */ + OF_DECODER, + /** + * Transforms POJOs into OpenFlow Protocol byte messages + */ + OF_ENCODER, + /** + * Delegates translated POJOs into MessageConsumer + */ + DELEGATING_INBOUND_HANDLER, + /** + * Performs efficient flushing + */ + CHANNEL_OUTBOUNF_QUEUE, + /** + * Decodes incoming messages into message frames + * and filters them based on version supported + */ + OF_DATAGRAMPACKET_HANDLER, + /** + * Transforms OpenFlow Protocol datagram messages into POJOs + */ + OF_DATAGRAMPACKET_DECODER, + /** + * Transforms POJOs into OpenFlow Protocol datagrams + */ + OF_DATAGRAMPACKET_ENCODER +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ProtocolChannelInitializer.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ProtocolChannelInitializer.java new file mode 100644 index 00000000..2c486f00 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/ProtocolChannelInitializer.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2014 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.ChannelInitializer; + +import org.opendaylight.openflowjava.protocol.api.connection.SwitchConnectionHandler; +import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration; +import org.opendaylight.openflowjava.protocol.impl.deserialization.DeserializationFactory; +import org.opendaylight.openflowjava.protocol.impl.serialization.SerializationFactory; + +/** + * @param Channel type + * @author michal.polkorab + */ +public abstract class ProtocolChannelInitializer + extends ChannelInitializer { + + private SwitchConnectionHandler switchConnectionHandler; + private long switchIdleTimeout; + private SerializationFactory serializationFactory; + private DeserializationFactory deserializationFactory; + private TlsConfiguration tlsConfiguration; + + /** + * @param switchConnectionHandler the switchConnectionHandler to set + */ + public void setSwitchConnectionHandler(final SwitchConnectionHandler switchConnectionHandler) { + this.switchConnectionHandler = switchConnectionHandler; + } + + /** + * @param switchIdleTimeout the switchIdleTimeout to set + */ + public void setSwitchIdleTimeout(final long switchIdleTimeout) { + this.switchIdleTimeout = switchIdleTimeout; + } + + /** + * @param serializationFactory + */ + public void setSerializationFactory(final SerializationFactory serializationFactory) { + this.serializationFactory = serializationFactory; + } + + /** + * @param deserializationFactory + */ + public void setDeserializationFactory(final DeserializationFactory deserializationFactory) { + this.deserializationFactory = deserializationFactory; + } + + /** + * @param tlsConfiguration + */ + public void setTlsConfiguration(TlsConfiguration tlsConfiguration) { + this.tlsConfiguration = tlsConfiguration; + } + + /** + * @return switch connection handler + */ + public SwitchConnectionHandler getSwitchConnectionHandler() { + return switchConnectionHandler; + } + + /** + * @return switch idle timeout + */ + public long getSwitchIdleTimeout() { + return switchIdleTimeout; + } + + /** + * @return serialization factory + */ + public SerializationFactory getSerializationFactory() { + return serializationFactory; + } + + /** + * @return deserialization factory + */ + public DeserializationFactory getDeserializationFactory() { + return deserializationFactory; + } + + /** + * @return TLS configuration + */ + public TlsConfiguration getTlsConfiguration() { + return tlsConfiguration; + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializer.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializer.java deleted file mode 100644 index 5d29e4be..00000000 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializer.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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.ChannelInitializer; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.ssl.SslHandler; - -import java.net.InetAddress; -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -import javax.net.ssl.SSLEngine; - -import org.opendaylight.openflowjava.protocol.api.connection.SwitchConnectionHandler; -import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration; -import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionAdapterFactory; -import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionAdapterFactoryImpl; -import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionFacade; -import org.opendaylight.openflowjava.protocol.impl.deserialization.DeserializationFactory; -import org.opendaylight.openflowjava.protocol.impl.serialization.SerializationFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Initializes the channel - * @author michal.polkorab - */ -public class PublishingChannelInitializer extends ChannelInitializer { - - /** - * Enum used for storing names of used components (in pipeline). - */ - public static enum COMPONENT_NAMES { - - /** - * Detects switch idle state - */ - IDLE_HANDLER, - /** - * Detects TLS connections - */ - TLS_DETECTOR, - /** - * Component for handling TLS frames - */ - SSL_HANDLER, - /** - * Decodes incoming messages into message frames - */ - OF_FRAME_DECODER, - /** - * Detects version of incoming OpenFlow Protocol message - */ - OF_VERSION_DETECTOR, - /** - * Transforms OpenFlow Protocol byte messages into POJOs - */ - OF_DECODER, - /** - * Transforms POJOs into OpenFlow Protocol byte messages - */ - OF_ENCODER, - /** - * Delegates translated POJOs into MessageConsumer - */ - DELEGATING_INBOUND_HANDLER, - } - - private static final Logger LOGGER = LoggerFactory - .getLogger(PublishingChannelInitializer.class); - private final DefaultChannelGroup allChannels; - private SwitchConnectionHandler switchConnectionHandler; - private long switchIdleTimeout; - private SerializationFactory serializationFactory; - private DeserializationFactory deserializationFactory; - private ConnectionAdapterFactory connectionAdapterFactory; - private TlsConfiguration tlsConfiguration ; - - /** - * default ctor - */ - public PublishingChannelInitializer() { - this( new DefaultChannelGroup("netty-receiver", null), new ConnectionAdapterFactoryImpl() ); - } - - /** - * Testing Constructor - * - */ - protected PublishingChannelInitializer( DefaultChannelGroup channelGroup, ConnectionAdapterFactory connAdaptorFactory ) { - allChannels = channelGroup ; - connectionAdapterFactory = connAdaptorFactory ; - } - - @Override - protected void initChannel(final SocketChannel ch) { - InetAddress switchAddress = ch.remoteAddress().getAddress(); - int port = ch.localAddress().getPort(); - int remotePort = ch.remoteAddress().getPort(); - LOGGER.info("Incoming connection from (remote address): " + switchAddress.toString() - + ":" + remotePort + " --> :" + port); - if (!switchConnectionHandler.accept(switchAddress)) { - ch.disconnect(); - LOGGER.info("Incoming connection rejected"); - return; - } - LOGGER.info("Incoming connection accepted - building pipeline"); - allChannels.add(ch); - ConnectionFacade connectionFacade = null; - connectionFacade = connectionAdapterFactory.createConnectionFacade(ch); - try { - LOGGER.debug("calling plugin: " + switchConnectionHandler); - switchConnectionHandler.onSwitchConnected(connectionFacade); - connectionFacade.checkListeners(); - ch.pipeline().addLast(COMPONENT_NAMES.IDLE_HANDLER.name(), new IdleHandler(switchIdleTimeout, TimeUnit.MILLISECONDS)); - - // If this channel is configured to support SSL it will only support SSL - if (tlsConfiguration != null) { - SslContextFactory sslFactory = new SslContextFactory(tlsConfiguration); - SSLEngine engine = sslFactory.getServerContext().createSSLEngine(); - engine.setNeedClientAuth(true); - engine.setUseClientMode(false); - ch.pipeline().addLast(COMPONENT_NAMES.SSL_HANDLER.name(), new SslHandler(engine)); - } - ch.pipeline().addLast(COMPONENT_NAMES.OF_FRAME_DECODER.name(), new OFFrameDecoder(connectionFacade)); - ch.pipeline().addLast(COMPONENT_NAMES.OF_VERSION_DETECTOR.name(), new OFVersionDetector()); - OFDecoder ofDecoder = new OFDecoder(); - ofDecoder.setDeserializationFactory(deserializationFactory); - ch.pipeline().addLast(COMPONENT_NAMES.OF_DECODER.name(), ofDecoder); - OFEncoder ofEncoder = new OFEncoder(); - ofEncoder.setSerializationFactory(serializationFactory); - ch.pipeline().addLast(COMPONENT_NAMES.OF_ENCODER.name(), ofEncoder); - ch.pipeline().addLast(COMPONENT_NAMES.DELEGATING_INBOUND_HANDLER.name(), new DelegatingInboundHandler(connectionFacade)); - if (tlsConfiguration == null) { - connectionFacade.fireConnectionReadyNotification(); - } - } catch (Exception e) { - LOGGER.error("Failed to initialize channel", e); - ch.close(); - } - } - - /** - * @return iterator through active connections - */ - public Iterator getConnectionIterator() { - return allChannels.iterator(); - } - - /** - * @return amount of active channels - */ - public int size() { - return allChannels.size(); - } - - /** - * @param switchConnectionHandler the switchConnectionHandler to set - */ - public void setSwitchConnectionHandler(final SwitchConnectionHandler switchConnectionHandler) { - this.switchConnectionHandler = switchConnectionHandler; - } - - /** - * @param switchIdleTimeout the switchIdleTimeout to set - */ - public void setSwitchIdleTimeout(final long switchIdleTimeout) { - this.switchIdleTimeout = switchIdleTimeout; - } - - /** - * @param serializationFactory - */ - public void setSerializationFactory(final SerializationFactory serializationFactory) { - this.serializationFactory = serializationFactory; - } - - /** - * @param deserializationFactory - */ - public void setDeserializationFactory(final DeserializationFactory deserializationFactory) { - this.deserializationFactory = deserializationFactory; - } - - /** - * @param tlsConfiguration - */ - public void setTlsConfiguration(TlsConfiguration tlsConfiguration) { - this.tlsConfiguration = tlsConfiguration; - } -} diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpChannelInitializer.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpChannelInitializer.java new file mode 100644 index 00000000..d84126b8 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpChannelInitializer.java @@ -0,0 +1,116 @@ +/* + * 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 java.net.InetAddress; +import java.util.Iterator; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLEngine; + +import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionAdapterFactory; +import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionAdapterFactoryImpl; +import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionFacade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Initializes TCP / TLS channel + * @author michal.polkorab + */ +public class TcpChannelInitializer extends ProtocolChannelInitializer { + + private static final Logger LOGGER = LoggerFactory + .getLogger(TcpChannelInitializer.class); + private final DefaultChannelGroup allChannels; + private ConnectionAdapterFactory connectionAdapterFactory; + + /** + * default ctor + */ + public TcpChannelInitializer() { + this( new DefaultChannelGroup("netty-receiver", null), new ConnectionAdapterFactoryImpl() ); + } + + /** + * Testing Constructor + * + */ + protected TcpChannelInitializer( DefaultChannelGroup channelGroup, ConnectionAdapterFactory connAdaptorFactory ) { + allChannels = channelGroup ; + connectionAdapterFactory = connAdaptorFactory ; + } + + @Override + protected void initChannel(final SocketChannel ch) { + InetAddress switchAddress = ch.remoteAddress().getAddress(); + int port = ch.localAddress().getPort(); + int remotePort = ch.remoteAddress().getPort(); + LOGGER.info("Incoming connection from (remote address): " + switchAddress.toString() + + ":" + remotePort + " --> :" + port); + if (!getSwitchConnectionHandler().accept(switchAddress)) { + ch.disconnect(); + LOGGER.info("Incoming connection rejected"); + return; + } + LOGGER.info("Incoming connection accepted - building pipeline"); + allChannels.add(ch); + ConnectionFacade connectionFacade = null; + connectionFacade = connectionAdapterFactory.createConnectionFacade(ch, null); + try { + LOGGER.debug("calling plugin: " + getSwitchConnectionHandler()); + getSwitchConnectionHandler().onSwitchConnected(connectionFacade); + connectionFacade.checkListeners(); + ch.pipeline().addLast(PipelineHandlers.IDLE_HANDLER.name(), new IdleHandler(getSwitchIdleTimeout(), TimeUnit.MILLISECONDS)); + + // If this channel is configured to support SSL it will only support SSL + if (getTlsConfiguration() != null) { + SslContextFactory sslFactory = new SslContextFactory(getTlsConfiguration()); + SSLEngine engine = sslFactory.getServerContext().createSSLEngine(); + engine.setNeedClientAuth(true); + engine.setUseClientMode(false); + ch.pipeline().addLast(PipelineHandlers.SSL_HANDLER.name(), new SslHandler(engine)); + } + ch.pipeline().addLast(PipelineHandlers.OF_FRAME_DECODER.name(), new OFFrameDecoder(connectionFacade)); + ch.pipeline().addLast(PipelineHandlers.OF_VERSION_DETECTOR.name(), new OFVersionDetector()); + OFDecoder ofDecoder = new OFDecoder(); + ofDecoder.setDeserializationFactory(getDeserializationFactory()); + ch.pipeline().addLast(PipelineHandlers.OF_DECODER.name(), ofDecoder); + 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 (getTlsConfiguration() == null) { + connectionFacade.fireConnectionReadyNotification(); + } + } catch (Exception e) { + LOGGER.error("Failed to initialize channel", e); + ch.close(); + } + } + + /** + * @return iterator through active connections + */ + public Iterator getConnectionIterator() { + return allChannels.iterator(); + } + + /** + * @return amount of active channels + */ + public int size() { + return allChannels.size(); + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandler.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandler.java index 72c5d07d..59675b27 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandler.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandler.java @@ -30,7 +30,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; /** - * Class implementing server over TCP for handling incoming connections. + * Class implementing server over TCP / TLS for handling incoming connections. * * @author michal.polkorab */ @@ -56,7 +56,7 @@ public class TcpHandler implements ServerFacade { private final SettableFuture isOnlineFuture; private ThreadConfiguration threadConfig; - private PublishingChannelInitializer channelInitializer; + private TcpChannelInitializer channelInitializer; /** * Constructor of TCPHandler that listens on selected port. @@ -135,7 +135,7 @@ public class TcpHandler implements ServerFacade { LOGGER.debug("address from tcphandler: {}", address); isOnlineFuture.set(true); - LOGGER.info("Switch listener started and ready to accept incoming connections on port: {}", port); + LOGGER.info("Switch listener started and ready to accept incoming tcp/tls connections on port: {}", port); f.channel().closeFuture().sync(); } catch (InterruptedException e) { LOGGER.error("Interrupted while waiting for port {} shutdown", port, e); @@ -197,13 +197,11 @@ public class TcpHandler implements ServerFacade { /** * @param channelInitializer */ - public void setChannelInitializer(PublishingChannelInitializer channelInitializer) { + public void setChannelInitializer(TcpChannelInitializer channelInitializer) { this.channelInitializer = channelInitializer; } - /** - * @param threadConfig EventLoopGroup configuration - */ + @Override public void setThreadConfig(ThreadConfiguration threadConfig) { this.threadConfig = threadConfig; } diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpChannelInitializer.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpChannelInitializer.java new file mode 100644 index 00000000..51871440 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpChannelInitializer.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 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.socket.nio.NioDatagramChannel; + +/** + * @author michal.polkorab + * + */ +public class UdpChannelInitializer extends ProtocolChannelInitializer { + + @Override + protected void initChannel(NioDatagramChannel ch) throws Exception { + ch.pipeline().addLast(PipelineHandlers.OF_DATAGRAMPACKET_HANDLER.name(), + new OFDatagramPacketHandler(getSwitchConnectionHandler())); + OFDatagramPacketDecoder ofDatagramPacketDecoder = new OFDatagramPacketDecoder(); + ofDatagramPacketDecoder.setDeserializationFactory(getDeserializationFactory()); + ch.pipeline().addLast(PipelineHandlers.OF_DATAGRAMPACKET_DECODER.name(), + ofDatagramPacketDecoder); + OFDatagramPacketEncoder ofDatagramPacketEncoder = new OFDatagramPacketEncoder(); + ofDatagramPacketEncoder.setSerializationFactory(getSerializationFactory()); + ch.pipeline().addLast(PipelineHandlers.OF_ENCODER.name(), ofDatagramPacketEncoder); +// connectionFacade.fireConnectionReadyNotification(); + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpConnectionMap.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpConnectionMap.java new file mode 100644 index 00000000..ce6d17f0 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpConnectionMap.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014 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 java.net.InetSocketAddress; +import java.util.HashMap; + +import org.opendaylight.openflowjava.protocol.impl.connection.MessageConsumer; + +/** + * As UDP communication is handled only by one channel, it is needed + * to store MessageConsumers, so that we know which consumer handles which channel + + * @author michal.polkorab + */ +public class UdpConnectionMap { + + private static HashMap connectionMap = new HashMap<>(); + + /** + * @param address sender's address + * @return corresponding MessageConsumer + */ + public static MessageConsumer getMessageConsumer(InetSocketAddress address) { + return connectionMap.get(address); + } + + /** + * @param address sender's address + * @param consumer MessageConsumer to be added / paired with specified address + */ + public static void addConnection(InetSocketAddress address, MessageConsumer consumer) { + connectionMap.put(address, consumer); + } + + /** + * @param address sender's address + */ + public static void removeConnection(InetSocketAddress address) { + connectionMap.remove(address); + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpHandler.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpHandler.java new file mode 100644 index 00000000..c0f958d2 --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/UdpHandler.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014 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.bootstrap.Bootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioDatagramChannel; +import io.netty.util.concurrent.GenericFutureListener; + +import java.net.InetAddress; +import java.net.InetSocketAddress; + +import org.opendaylight.openflowjava.protocol.api.connection.ThreadConfiguration; +import org.opendaylight.openflowjava.protocol.impl.connection.ServerFacade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; + +/** + * Class implementing server over UDP for handling incoming connections. + * + * @author michal.polkorab + */ +public final class UdpHandler implements ServerFacade { + + private static final Logger LOGGER = LoggerFactory + .getLogger(UdpHandler.class); + private int port; + private String address; + private EventLoopGroup group; + private final InetAddress startupAddress; + private final SettableFuture isOnlineFuture; + private UdpChannelInitializer channelInitializer; + private ThreadConfiguration threadConfig; + + /** + * Constructor of UdpHandler that listens on selected port. + * + * @param port listening port of UdpHandler server + */ + public UdpHandler(final int port) { + this(null, port); + } + + /** + * Constructor of UdpHandler that listens on selected address and port. + * @param address listening address of UdpHandler server + * @param port listening port of UdpHandler server + */ + public UdpHandler(final InetAddress address, final int port) { + this.port = port; + this.startupAddress = address; + isOnlineFuture = SettableFuture.create(); + } + + @Override + public void run() { + if (threadConfig != null) { + group = new NioEventLoopGroup(threadConfig.getWorkerThreadCount()); + } else { + group = new NioEventLoopGroup(); + } + final ChannelFuture f; + try { + Bootstrap b = new Bootstrap(); + b.group(group) + .channel(NioDatagramChannel.class) + .option(ChannelOption.SO_BROADCAST, false) + .handler(channelInitializer); + + if (startupAddress != null) { + f = b.bind(startupAddress.getHostAddress(), port).sync(); + } else { + f = b.bind(port).sync(); + } + } catch (InterruptedException e) { + LOGGER.error("Interrupted while binding port {}", port, e); + return; + } + + try { + InetSocketAddress isa = (InetSocketAddress) f.channel().localAddress(); + this.address = isa.getHostString(); + + // Update port, as it may have been specified as 0 + this.port = isa.getPort(); + + LOGGER.debug("Address from udpHandler: {}", address); + isOnlineFuture.set(true); + LOGGER.info("Switch listener started and ready to accept incoming udp connections on port: {}", port); + f.channel().closeFuture().sync(); + } catch (InterruptedException e) { + LOGGER.error("Interrupted while waiting for port {} shutdown", port, e); + } finally { + shutdown(); + } + } + + @Override + public ListenableFuture shutdown() { + final SettableFuture result = SettableFuture.create(); + group.shutdownGracefully().addListener(new GenericFutureListener>() { + + @Override + public void operationComplete( + final io.netty.util.concurrent.Future downResult) throws Exception { + result.set(downResult.isSuccess()); + if (downResult.cause() != null) { + result.setException(downResult.cause()); + } + } + + }); + return result; + } + + @Override + public ListenableFuture getIsOnlineFuture() { + return isOnlineFuture; + } + + /** + * @return the port + */ + public int getPort() { + return port; + } + + /** + * @param channelInitializer + */ + public void setChannelInitializer(UdpChannelInitializer channelInitializer) { + this.channelInitializer = channelInitializer; + } + + @Override + public void setThreadConfig(ThreadConfiguration threadConfig) { + this.threadConfig = threadConfig; + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/VersionMessageUdpWrapper.java b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/VersionMessageUdpWrapper.java new file mode 100644 index 00000000..087bcb6b --- /dev/null +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/openflowjava/protocol/impl/core/VersionMessageUdpWrapper.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 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.buffer.ByteBuf; + +import java.net.InetSocketAddress; + +/** + * Wraps received messages (includes version) and sender address + + * @author michal.polkorab + */ +public class VersionMessageUdpWrapper extends VersionMessageWrapper { + + private InetSocketAddress address; + + /** + * @param version Openflow wire version + * @param messageBuffer ByteBuf containing binary message + * @param address sender address + */ + public VersionMessageUdpWrapper(short version, ByteBuf messageBuffer, InetSocketAddress address) { + super(version, messageBuffer); + this.address = address; + } + + /** + * @return sender address + */ + public InetSocketAddress getAddress() { + return address; + } +} \ No newline at end of file diff --git a/openflow-protocol-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/_switch/connection/provider/impl/rev140328/SwitchConnectionProviderModule.java b/openflow-protocol-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/_switch/connection/provider/impl/rev140328/SwitchConnectionProviderModule.java index 1a22723a..b3cbeb29 100644 --- a/openflow-protocol-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/_switch/connection/provider/impl/rev140328/SwitchConnectionProviderModule.java +++ b/openflow-protocol-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/openflow/_switch/connection/provider/impl/rev140328/SwitchConnectionProviderModule.java @@ -18,6 +18,7 @@ import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration; import org.opendaylight.openflowjava.protocol.impl.connection.SwitchConnectionProviderImpl; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.KeystoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.TransportProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,6 +80,7 @@ public final class SwitchConnectionProviderModule extends org.opendaylight.yang. final long switchIdleTimeout = getSwitchIdleTimeout(); final Tls tlsConfig = getTls(); final Threads threads = getThreads(); + final TransportProtocol transportProtocol = getTransportProtocol(); return new ConnectionConfiguration() { @Override @@ -91,12 +93,11 @@ public final class SwitchConnectionProviderModule extends org.opendaylight.yang. } @Override public Object getTransferProtocol() { - // TODO Auto-generated method stub - return null; + return transportProtocol; } @Override public TlsConfiguration getTlsConfiguration() { - if (tlsConfig == null) { + if (tlsConfig == null || !(TransportProtocol.TLS.equals(transportProtocol))) { return null; } return new TlsConfiguration() { diff --git a/openflow-protocol-impl/src/main/yang/openflow-switch-connection-provider-impl.yang b/openflow-protocol-impl/src/main/yang/openflow-switch-connection-provider-impl.yang index dbc48866..6afd5c60 100644 --- a/openflow-protocol-impl/src/main/yang/openflow-switch-connection-provider-impl.yang +++ b/openflow-protocol-impl/src/main/yang/openflow-switch-connection-provider-impl.yang @@ -36,6 +36,11 @@ module openflow-switch-connection-provider-impl { description "address of local listening interface"; type ietf-inet:ip-address; } + leaf transport-protocol { + description "Transport protocol used for communication."; + type of-config:transport-protocol; + mandatory true; + } leaf switch-idle-timeout { description "idle timeout in [ms]"; type uint32; diff --git a/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerFactoryTest.java b/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerFactoryTest.java index b24192a1..6001e7f8 100644 --- a/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerFactoryTest.java +++ b/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerFactoryTest.java @@ -29,7 +29,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.P public class PublishingChannelInitializerFactoryTest { TlsConfiguration tlsConfiguration ; - PublishingChannelInitializerFactory factory; + ChannelInitializerFactory factory; private final long switchIdleTimeOut = 60; @Mock SwitchConnectionHandler switchConnectionHandler ; @Mock SerializationFactory serializationFactory; @@ -41,7 +41,7 @@ public class PublishingChannelInitializerFactoryTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - factory = new PublishingChannelInitializerFactory(); + factory = new ChannelInitializerFactory(); tlsConfiguration = new TlsConfigurationImpl(KeystoreType.JKS, "/exemplary-ctlTrustStore", PathType.CLASSPATH, KeystoreType.JKS, "/exemplary-ctlKeystore", PathType.CLASSPATH); factory.setDeserializationFactory(deserializationFactory); @@ -52,11 +52,11 @@ public class PublishingChannelInitializerFactoryTest { } /** - * Test {@link PublishingChannelInitializer} creation + * Test {@link TcpChannelInitializer} creation */ @Test public void testCreatePublishingChannelInitializer() { - PublishingChannelInitializer initializer = factory.createPublishingChannelInitializer() ; + TcpChannelInitializer initializer = factory.createPublishingChannelInitializer() ; assertNotNull( initializer ); } } \ No newline at end of file diff --git a/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerTest.java b/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerTest.java index 6666da3d..2a503dd2 100644 --- a/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerTest.java +++ b/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/PublishingChannelInitializerTest.java @@ -36,7 +36,6 @@ import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration; import org.opendaylight.openflowjava.protocol.api.connection.TlsConfigurationImpl; import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionAdapterFactory; import org.opendaylight.openflowjava.protocol.impl.connection.ConnectionFacade; -import org.opendaylight.openflowjava.protocol.impl.core.PublishingChannelInitializer.COMPONENT_NAMES; import org.opendaylight.openflowjava.protocol.impl.deserialization.DeserializationFactory; import org.opendaylight.openflowjava.protocol.impl.serialization.SerializationFactory; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.KeystoreType; @@ -63,7 +62,7 @@ public class PublishingChannelInitializerTest { TlsConfiguration tlsConfiguration ; InetSocketAddress inetSockAddr; - PublishingChannelInitializer pubChInitializer ; + TcpChannelInitializer pubChInitializer ; /** * Sets up test environment @@ -72,7 +71,7 @@ public class PublishingChannelInitializerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - pubChInitializer= new PublishingChannelInitializer(mockChGrp, mockConnAdaptorFactory) ; + pubChInitializer= new TcpChannelInitializer(mockChGrp, mockConnAdaptorFactory) ; pubChInitializer.setSerializationFactory(mockSerializationFactory); pubChInitializer.setDeserializationFactory(mockDeserializationFactory); pubChInitializer.setSwitchIdleTimeout(1) ; @@ -83,7 +82,7 @@ public class PublishingChannelInitializerTest { inetSockAddr = new InetSocketAddress(InetAddress.getLocalHost(), 8675 ) ; - when(mockConnAdaptorFactory.createConnectionFacade(mockSocketCh)) + when(mockConnAdaptorFactory.createConnectionFacade(mockSocketCh, null)) .thenReturn(mockConnFacade); when(mockSocketCh.remoteAddress()).thenReturn(inetSockAddr) ; when(mockSocketCh.localAddress()).thenReturn(inetSockAddr) ; @@ -105,7 +104,7 @@ public class PublishingChannelInitializerTest { pubChInitializer.initChannel(mockSocketCh) ; verifyCommonHandlers(); - verify(mockChPipeline, times(1)).addLast(eq(COMPONENT_NAMES.SSL_HANDLER.name()),any(SslHandler.class)) ; + verify(mockChPipeline, times(1)).addLast(eq(PipelineHandlers.SSL_HANDLER.name()),any(SslHandler.class)) ; } /** @@ -117,7 +116,7 @@ public class PublishingChannelInitializerTest { pubChInitializer.initChannel(mockSocketCh) ; verifyCommonHandlers(); - verify(mockChPipeline, times(0)).addLast(eq(COMPONENT_NAMES.SSL_HANDLER.name()),any(SslHandler.class)) ; + verify(mockChPipeline, times(0)).addLast(eq(PipelineHandlers.SSL_HANDLER.name()),any(SslHandler.class)) ; } /** @@ -160,12 +159,12 @@ public class PublishingChannelInitializerTest { * All paths should install these six handlers: */ private void verifyCommonHandlers() { - verify(mockChPipeline, times(1)).addLast(eq(COMPONENT_NAMES.IDLE_HANDLER.name()),any(IdleHandler.class)) ; - verify(mockChPipeline, times(1)).addLast(eq(COMPONENT_NAMES.OF_DECODER.name()),any(OFDecoder.class)) ; - verify(mockChPipeline, times(1)).addLast(eq(COMPONENT_NAMES.OF_ENCODER.name()),any(OFEncoder.class)) ; - verify(mockChPipeline, times(1)).addLast(eq(COMPONENT_NAMES.OF_FRAME_DECODER.name()),any(OFFrameDecoder.class)) ; - verify(mockChPipeline, times(1)).addLast(eq(COMPONENT_NAMES.OF_VERSION_DETECTOR.name()),any(OFVersionDetector.class)) ; - verify(mockChPipeline, times(1)).addLast(eq(COMPONENT_NAMES.DELEGATING_INBOUND_HANDLER.name()),any(DelegatingInboundHandler.class)); + verify(mockChPipeline, times(1)).addLast(eq(PipelineHandlers.IDLE_HANDLER.name()),any(IdleHandler.class)) ; + verify(mockChPipeline, times(1)).addLast(eq(PipelineHandlers.OF_DECODER.name()),any(OFDecoder.class)) ; + verify(mockChPipeline, times(1)).addLast(eq(PipelineHandlers.OF_ENCODER.name()),any(OFEncoder.class)) ; + verify(mockChPipeline, times(1)).addLast(eq(PipelineHandlers.OF_FRAME_DECODER.name()),any(OFFrameDecoder.class)) ; + verify(mockChPipeline, times(1)).addLast(eq(PipelineHandlers.OF_VERSION_DETECTOR.name()),any(OFVersionDetector.class)) ; + verify(mockChPipeline, times(1)).addLast(eq(PipelineHandlers.DELEGATING_INBOUND_HANDLER.name()),any(DelegatingInboundHandler.class)); assertEquals(1, pubChInitializer.size()) ; } } diff --git a/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandlerTest.java b/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandlerTest.java index 7092922f..129a8668 100644 --- a/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandlerTest.java +++ b/openflow-protocol-impl/src/test/java/org/opendaylight/openflowjava/protocol/impl/core/TcpHandlerTest.java @@ -36,7 +36,7 @@ public class TcpHandlerTest { private InetAddress serverAddress = InetAddress.getLoopbackAddress() ; @Mock ChannelHandlerContext mockChHndlrCtx ; - @Mock PublishingChannelInitializer mockChannelInitializer; + @Mock TcpChannelInitializer mockChannelInitializer; @Mock SwitchConnectionHandler mockSwitchConnHndler ; @Mock SerializationFactory mockSerializationFactory ; @Mock DeserializationFactory mockDeserializationFactory ; diff --git a/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/ConnectionConfigurationImpl.java b/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/ConnectionConfigurationImpl.java index d0a76c8f..47c1889b 100644 --- a/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/ConnectionConfigurationImpl.java +++ b/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/ConnectionConfigurationImpl.java @@ -13,6 +13,7 @@ import java.net.InetAddress; import org.opendaylight.openflowjava.protocol.api.connection.ConnectionConfiguration; import org.opendaylight.openflowjava.protocol.api.connection.ThreadConfiguration; import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration; +import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.TransportProtocol; /** * @author michal.polkorab @@ -26,6 +27,7 @@ public class ConnectionConfigurationImpl implements ConnectionConfiguration { private TlsConfiguration tlsConfig; private long switchIdleTimeout; private ThreadConfiguration threadConfig; + private TransportProtocol protocol; /** * Creates {@link ConnectionConfigurationImpl} @@ -56,6 +58,14 @@ public class ConnectionConfigurationImpl implements ConnectionConfiguration { return transferProtocol; } + /** + * Used for testing - sets transport protocol + * @param protocol + */ + public void setTransferProtocol(TransportProtocol protocol) { + this.transferProtocol = protocol; + } + @Override public long getSwitchIdleTimeout() { return switchIdleTimeout; diff --git a/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/IntegrationTest.java b/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/IntegrationTest.java index c12ec55b..8a94cd05 100644 --- a/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/IntegrationTest.java +++ b/openflow-protocol-it/src/test/java/org/opendaylight/openflowjava/protocol/it/integration/IntegrationTest.java @@ -20,17 +20,21 @@ import org.junit.Test; import org.opendaylight.openflowjava.protocol.api.connection.TlsConfiguration; import org.opendaylight.openflowjava.protocol.api.connection.TlsConfigurationImpl; import org.opendaylight.openflowjava.protocol.impl.clients.ClientEvent; +import org.opendaylight.openflowjava.protocol.impl.clients.OFClient; import org.opendaylight.openflowjava.protocol.impl.clients.ScenarioFactory; import org.opendaylight.openflowjava.protocol.impl.clients.ScenarioHandler; import org.opendaylight.openflowjava.protocol.impl.clients.SendEvent; import org.opendaylight.openflowjava.protocol.impl.clients.SimpleClient; import org.opendaylight.openflowjava.protocol.impl.clients.SleepEvent; +import org.opendaylight.openflowjava.protocol.impl.clients.UdpSimpleClient; import org.opendaylight.openflowjava.protocol.impl.clients.WaitForMessageEvent; import org.opendaylight.openflowjava.protocol.impl.connection.SwitchConnectionProviderImpl; import org.opendaylight.openflowjava.protocol.impl.core.TcpHandler; +import org.opendaylight.openflowjava.protocol.impl.core.UdpHandler; import org.opendaylight.openflowjava.util.ByteBufUtils; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.KeystoreType; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.PathType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.config.rev140630.TransportProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +48,7 @@ public class IntegrationTest { .getLogger(IntegrationTest.class); private static int port; - private TlsConfiguration tlsConfiguration ; + private TlsConfiguration tlsConfiguration; private static final int SWITCH_IDLE_TIMEOUT = 2000; private static final long CONNECTION_TIMEOUT = 2000; private InetAddress startupAddress; @@ -53,31 +57,36 @@ public class IntegrationTest { private ConnectionConfigurationImpl connConfig; /** - * @param secured true if an encrypted connection should be used + * @param protocol communication protocol to be used during test * @throws Exception */ - public void setUp(boolean secured) throws Exception { + public void setUp(TransportProtocol protocol) throws Exception { LOGGER.debug("\n starting test -------------------------------"); String currentDir = System.getProperty("user.dir"); LOGGER.debug("Current dir using System:" +currentDir); startupAddress = InetAddress.getLocalHost(); - if (secured) { + tlsConfiguration = null; + if (protocol.equals(TransportProtocol.TLS)) { tlsConfiguration = new TlsConfigurationImpl(KeystoreType.JKS, "/selfSignedSwitch", PathType.CLASSPATH, KeystoreType.JKS, "/selfSignedController", PathType.CLASSPATH) ; - connConfig = new ConnectionConfigurationImpl(startupAddress, 0, tlsConfiguration, SWITCH_IDLE_TIMEOUT); - } else { - connConfig = new ConnectionConfigurationImpl(startupAddress, 0, null, SWITCH_IDLE_TIMEOUT); } + connConfig = new ConnectionConfigurationImpl(startupAddress, 0, tlsConfiguration, SWITCH_IDLE_TIMEOUT); + connConfig.setTransferProtocol(protocol); mockPlugin = new MockPlugin(); switchConnectionProvider = new SwitchConnectionProviderImpl(); switchConnectionProvider.setSwitchConnectionHandler(mockPlugin); switchConnectionProvider.setConfiguration(connConfig); switchConnectionProvider.startup().get(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); - TcpHandler tcpHandler = (TcpHandler) switchConnectionProvider.getServerFacade(); - port = tcpHandler.getPort(); + if (protocol.equals(TransportProtocol.TCP) || protocol.equals(TransportProtocol.TLS)) { + TcpHandler tcpHandler = (TcpHandler) switchConnectionProvider.getServerFacade(); + port = tcpHandler.getPort(); + } else { + UdpHandler udpHandler = (UdpHandler) switchConnectionProvider.getServerFacade(); + port = udpHandler.getPort(); + } } /** @@ -87,7 +96,6 @@ public class IntegrationTest { public void tearDown() throws Exception { switchConnectionProvider.close(); LOGGER.debug("\n ending test -------------------------------"); - } /** @@ -96,12 +104,12 @@ public class IntegrationTest { */ @Test public void testHandshake() throws Exception { - setUp(false); + setUp(TransportProtocol.TCP); int amountOfCLients = 1; Stack scenario = ScenarioFactory.createHandshakeScenario(); ScenarioHandler handler = new ScenarioHandler(scenario); - List clients = createAndStartClient(amountOfCLients, handler, false); - SimpleClient firstClient = clients.get(0); + List clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TCP); + OFClient firstClient = clients.get(0); firstClient.getScenarioDone().get(); Thread.sleep(1000); @@ -109,17 +117,17 @@ public class IntegrationTest { } /** - * Library integration and communication test with handshake + * Library integration and secured communication test with handshake * @throws Exception */ @Test public void testTlsHandshake() throws Exception { - setUp(true); + setUp(TransportProtocol.TLS); int amountOfCLients = 1; Stack scenario = ScenarioFactory.createHandshakeScenario(); ScenarioHandler handler = new ScenarioHandler(scenario); - List clients = createAndStartClient(amountOfCLients, handler, true); - SimpleClient firstClient = clients.get(0); + List clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TLS); + OFClient firstClient = clients.get(0); firstClient.getScenarioDone().get(); Thread.sleep(1000); @@ -132,7 +140,7 @@ public class IntegrationTest { */ @Test public void testHandshakeAndEcho() throws Exception { - setUp(false); + setUp(TransportProtocol.TCP); int amountOfCLients = 1; Stack scenario = ScenarioFactory.createHandshakeScenario(); scenario.add(0, new SleepEvent(1000)); @@ -140,20 +148,20 @@ public class IntegrationTest { scenario.add(0, new SleepEvent(1000)); scenario.add(0, new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 03 00 08 00 00 00 04"))); ScenarioHandler handler = new ScenarioHandler(scenario); - List clients = createAndStartClient(amountOfCLients, handler, false); - SimpleClient firstClient = clients.get(0); + List clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TCP); + OFClient firstClient = clients.get(0); firstClient.getScenarioDone().get(); LOGGER.debug("testHandshakeAndEcho() Finished") ; } /** - * Library integration and communication test with handshake + echo exchange + * Library integration and secured communication test with handshake + echo exchange * @throws Exception */ @Test public void testTlsHandshakeAndEcho() throws Exception { - setUp(true); + setUp(TransportProtocol.TLS); int amountOfCLients = 1; Stack scenario = ScenarioFactory.createHandshakeScenario(); scenario.add(0, new SleepEvent(1000)); @@ -161,40 +169,69 @@ public class IntegrationTest { scenario.add(0, new SleepEvent(1000)); scenario.add(0, new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 03 00 08 00 00 00 04"))); ScenarioHandler handler = new ScenarioHandler(scenario); - List clients = createAndStartClient(amountOfCLients, handler, true); - SimpleClient firstClient = clients.get(0); + List clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.TLS); + OFClient firstClient = clients.get(0); firstClient.getScenarioDone().get(); LOGGER.debug("testTlsHandshakeAndEcho() Finished") ; } /** - * Library integration and communication test (with virtual machine) - * @throws Exception - */ - //@Test - public void testCommunicationWithVM() throws Exception { - mockPlugin.getFinishedFuture().get(); + * Library udp integration and communication test with handshake + echo exchange + * @throws Exception + */ + @Test + public void testUdpHandshakeAndEcho() throws Exception { + setUp(TransportProtocol.UDP); + int amountOfCLients = 1; + Stack scenario = ScenarioFactory.createHandshakeScenario(); + scenario.add(0, new SleepEvent(1000)); + scenario.add(0, new SendEvent(ByteBufUtils.hexStringToBytes("04 02 00 08 00 00 00 04"))); + scenario.add(0, new SleepEvent(1000)); + scenario.add(0, new WaitForMessageEvent(ByteBufUtils.hexStringToBytes("04 03 00 08 00 00 00 04"))); + ScenarioHandler handler = new ScenarioHandler(scenario); + List clients = createAndStartClient(amountOfCLients, handler, TransportProtocol.UDP); + OFClient firstClient = clients.get(0); + firstClient.getScenarioDone().get(); + + LOGGER.debug("testUdpHandshakeAndEcho() Finished") ; + } + + /** + * Library integration and communication test (with virtual machine) + * @throws Exception + */ + //@Test + public void testCommunicationWithVM() throws Exception { + mockPlugin.getFinishedFuture().get(); } /** * @param amountOfCLients - * @param secured true if encrypted connection should be used + * @param protocol true if encrypted connection should be used * @return new clients up and running * @throws ExecutionException if some client could not start */ - private List createAndStartClient(int amountOfCLients, ScenarioHandler scenarioHandler, - boolean secured) throws ExecutionException { - List clientsHorde = new ArrayList<>(); + private List createAndStartClient(int amountOfCLients, ScenarioHandler scenarioHandler, + TransportProtocol protocol) throws ExecutionException { + List clientsHorde = new ArrayList<>(); for (int i = 0; i < amountOfCLients; i++) { LOGGER.debug("startup address in createclient: " + startupAddress.getHostAddress()); - SimpleClient sc = new SimpleClient(startupAddress.getHostAddress(), port); - sc.setSecuredClient(secured); + OFClient sc = null; + if (protocol.equals(TransportProtocol.TCP)) { + sc = new SimpleClient(startupAddress.getHostAddress(), port); + sc.setSecuredClient(false); + } else if (protocol.equals(TransportProtocol.TLS)) { + sc = new SimpleClient(startupAddress.getHostAddress(), port); + sc.setSecuredClient(true); + } else { + sc = new UdpSimpleClient(startupAddress.getHostAddress(), port); + } sc.setScenarioHandler(scenarioHandler); clientsHorde.add(sc); - sc.start(); + sc.run(); } - for (SimpleClient sc : clientsHorde) { + for (OFClient sc : clientsHorde) { try { sc.getIsOnlineFuture().get(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS); } catch (Exception e) { diff --git a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/OFClient.java b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/OFClient.java new file mode 100644 index 00000000..f7b4bafa --- /dev/null +++ b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/OFClient.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 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.clients; + +import com.google.common.util.concurrent.SettableFuture; + +/** + * Unifying interface for simple clients / switch simulators + * + * @author michal.polkorab + */ +public interface OFClient extends Runnable { + + /** + * @return the isOnlineFuture which is set when client is started + */ + public SettableFuture getIsOnlineFuture(); + + /** + * @return the scenarioDone when scenario is successfully finished + */ + public SettableFuture getScenarioDone(); + + /** + * @param scenario list of desired actions + */ + public void setScenarioHandler(ScenarioHandler scenario); + + /** + * @param securedClient true is client should use encrypted communication, + * false otherwise + */ + public void setSecuredClient(boolean securedClient); +} \ No newline at end of file diff --git a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClient.java b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClient.java index 72ef9d43..e31c3674 100644 --- a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClient.java +++ b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClient.java @@ -27,7 +27,7 @@ import com.google.common.util.concurrent.SettableFuture; * * @author michal.polkorab */ -public class SimpleClient extends Thread { +public class SimpleClient implements OFClient { private static final Logger LOGGER = LoggerFactory.getLogger(SimpleClient.class); private final String host; @@ -98,9 +98,7 @@ public class SimpleClient extends Thread { return group.shutdownGracefully(); } - /** - * @param securedClient - */ + @Override public void setSecuredClient(boolean securedClient) { this.securedClient = securedClient; } @@ -131,27 +129,20 @@ public class SimpleClient extends Thread { sc = new SimpleClient(host, port); sc.setSecuredClient(Boolean.parseBoolean(args[2])); } - sc.start(); - + sc.run(); } - - /** - * @return the isOnlineFuture - */ + + @Override public SettableFuture getIsOnlineFuture() { return isOnlineFuture; } - - /** - * @return the scenarioDone - */ + + @Override public SettableFuture getScenarioDone() { return scenarioDone; } - - /** - * @param scenario list of wanted actions - */ + + @Override public void setScenarioHandler(ScenarioHandler scenario) { this.scenarioHandler = scenario; } diff --git a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClientInitializer.java b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClientInitializer.java index 1d74bc0d..c44a660c 100644 --- a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClientInitializer.java +++ b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/SimpleClientInitializer.java @@ -11,7 +11,7 @@ package org.opendaylight.openflowjava.protocol.impl.clients; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; -import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.ssl.SslHandler; import javax.net.ssl.SSLEngine; @@ -22,7 +22,7 @@ import com.google.common.util.concurrent.SettableFuture; * * @author michal.polkorab */ -public class SimpleClientInitializer extends ChannelInitializer { +public class SimpleClientInitializer extends ChannelInitializer { private SettableFuture isOnlineFuture; private boolean secured; @@ -38,7 +38,7 @@ public class SimpleClientInitializer extends ChannelInitializer { } @Override - public void initChannel(SocketChannel ch) throws Exception { + public void initChannel(NioSocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (secured) { SSLEngine engine = ClientSslContextFactory.getClientContext() diff --git a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClient.java b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClient.java new file mode 100644 index 00000000..685dbd2a --- /dev/null +++ b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClient.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2014 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.clients; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioDatagramChannel; +import io.netty.util.concurrent.Future; + +import java.net.InetAddress; +import java.util.concurrent.ExecutionException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.util.concurrent.SettableFuture; + +/** + * Simple client for testing purposes + * + * @author michal.polkorab + */ +public class UdpSimpleClient implements OFClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(UdpSimpleClient.class); + private final String host; + private final int port; + private EventLoopGroup group; + private SettableFuture isOnlineFuture; + private SettableFuture scenarioDone; + private UdpSimpleClientInitializer clientInitializer; + private ScenarioHandler scenarioHandler; + + /** + * Constructor of class + * + * @param host address of host + * @param port host listening port + */ + public UdpSimpleClient(String host, int port) { + this.host = host; + this.port = port; + init(); + } + + private void init() { + isOnlineFuture = SettableFuture.create(); + scenarioDone = SettableFuture.create(); + } + + /** + * Starting class of {@link UdpSimpleClient} + */ + @Override + public void run() { + group = new NioEventLoopGroup(); + clientInitializer = new UdpSimpleClientInitializer(isOnlineFuture); + clientInitializer.setScenario(scenarioHandler); + try { + Bootstrap b = new Bootstrap(); + b.group(group) + .channel(NioDatagramChannel.class) + .option(ChannelOption.SO_BROADCAST, false) + .handler(clientInitializer); + + b.connect(host, port).sync(); + + synchronized (scenarioHandler) { + LOGGER.debug("WAITING FOR SCENARIO"); + scenarioHandler.wait(); + } + } catch (Exception ex) { + LOGGER.error(ex.getMessage(), ex); + } finally { + LOGGER.debug("shutting down"); + try { + group.shutdownGracefully().get(); + LOGGER.debug("shutdown succesful"); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error(e.getMessage(), e); + } + } + scenarioDone.set(true); + } + + /** + * @return close future + */ + public Future disconnect() { + LOGGER.debug("disconnecting client"); + return group.shutdownGracefully(); + } + + /** + * Sets up {@link UdpSimpleClient} and fires run() + * + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + String host; + int port; + UdpSimpleClient sc; + if (args.length != 2) { + LOGGER.error("Usage: " + UdpSimpleClient.class.getSimpleName() + + " "); + LOGGER.error("Trying to use default setting."); + InetAddress ia = InetAddress.getLocalHost(); + InetAddress[] all = InetAddress.getAllByName(ia.getHostName()); + host = all[0].getHostAddress(); + port = 6633; + sc = new UdpSimpleClient(host, port); + } else { + host = args[0]; + port = Integer.parseInt(args[1]); + sc = new UdpSimpleClient(host, port); + } + sc.run(); + + } + + @Override + public SettableFuture getIsOnlineFuture() { + return isOnlineFuture; + } + + @Override + public SettableFuture getScenarioDone() { + return scenarioDone; + } + + @Override + public void setScenarioHandler(ScenarioHandler scenario) { + this.scenarioHandler = scenario; + } + + @Override + public void setSecuredClient(boolean securedClient) { + // TODO: Finish implementation when DTLS is supported + } +} \ No newline at end of file diff --git a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientFramer.java b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientFramer.java new file mode 100644 index 00000000..d5f8e7be --- /dev/null +++ b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientFramer.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014 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.clients; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.DatagramPacket; +import io.netty.handler.codec.MessageToMessageDecoder; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class for decoding incoming udp messages into message frames. + * + * @author michal.polkorab + */ +public class UdpSimpleClientFramer extends MessageToMessageDecoder { + + /** Length of OpenFlow 1.3 header */ + public static final byte LENGTH_OF_HEADER = 8; + private static final byte LENGTH_INDEX_IN_HEADER = 2; + private static final Logger LOGGER = LoggerFactory.getLogger(UdpSimpleClientFramer.class); + + /** + * Constructor of class. + */ + public UdpSimpleClientFramer() { + LOGGER.trace("Creating OFFrameDecoder"); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + LOGGER.warn("Unexpected exception from downstream.", cause); + ctx.close(); + } + + @Override + protected void decode(ChannelHandlerContext chc, DatagramPacket msg, List list) throws Exception { + ByteBuf bb = msg.content(); + if (bb.readableBytes() < LENGTH_OF_HEADER) { + LOGGER.debug("skipping bb - too few data for header: " + bb.readableBytes()); + return; + } + + int length = bb.getUnsignedShort(bb.readerIndex() + LENGTH_INDEX_IN_HEADER); + if (bb.readableBytes() < length) { + LOGGER.debug("skipping bb - too few data for msg: " + + bb.readableBytes() + " < " + length); + return; + } + LOGGER.debug("OF Protocol message received, type:{}", bb.getByte(bb.readerIndex() + 1)); + + ByteBuf messageBuffer = bb.slice(bb.readerIndex(), length); + list.add(messageBuffer); + messageBuffer.retain(); + bb.skipBytes(length); + } +} \ No newline at end of file diff --git a/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientInitializer.java b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientInitializer.java new file mode 100644 index 00000000..cd7a8a8a --- /dev/null +++ b/simple-client/src/main/java/org/opendaylight/openflowjava/protocol/impl/clients/UdpSimpleClientInitializer.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 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.clients; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.nio.NioDatagramChannel; + +import com.google.common.util.concurrent.SettableFuture; + +/** Initializes udp pipeline + * + * @author michal.polkorab + */ +public class UdpSimpleClientInitializer extends ChannelInitializer { + + private SettableFuture isOnlineFuture; + private ScenarioHandler scenarioHandler; + + /** + * @param isOnlineFuture future notifier of connected channel + */ + public UdpSimpleClientInitializer(SettableFuture isOnlineFuture) { + this.isOnlineFuture = isOnlineFuture; + } + + @Override + public void initChannel(NioDatagramChannel ch) throws Exception { + ChannelPipeline pipeline = ch.pipeline(); + SimpleClientHandler simpleClientHandler = new SimpleClientHandler(isOnlineFuture, scenarioHandler); + simpleClientHandler.setScenario(scenarioHandler); + pipeline.addLast("framer", new UdpSimpleClientFramer()); + pipeline.addLast("handler", simpleClientHandler); + isOnlineFuture = null; + } + + /** + * @param scenarioHandler handler of scenario events + */ + public void setScenario(ScenarioHandler scenarioHandler) { + this.scenarioHandler = scenarioHandler; + } +} \ No newline at end of file -- 2.36.6