blob: cbf356942a7a21cc05a6ce72f0d689041b377414 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.ofagent.impl;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.ReadTimeoutException;
import org.onosproject.ofagent.api.OFSwitch;
import org.projectfloodlight.openflow.protocol.OFErrorMsg;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.RejectedExecutionException;
import static org.onosproject.ofagent.impl.OFChannelHandler.ChannelState.INIT;
/**
* Implementation of OpenFlow channel handler.
* It processes OpenFlow message according to the channel state.
*/
public final class OFChannelHandler extends ChannelDuplexHandler {
private static final String MSG_CHANNEL_STATE = "Set channel(%s) state: %s";
private final Logger log = LoggerFactory.getLogger(getClass());
private final OFSwitch ofSwitch;
private Channel channel;
private ChannelState state;
enum ChannelState {
INIT {
@Override
void processOFMessage(final OFChannelHandler handler,
final OFMessage msg) {
logProcessOFMessageDetails(handler, msg, this);
// TODO implement
}
},
WAIT_HELLO {
@Override
void processOFMessage(final OFChannelHandler handler,
final OFMessage msg) {
logProcessOFMessageDetails(handler, msg, this);
switch (msg.getType()) {
case HELLO:
handler.setState(ChannelState.WAIT_FEATURE_REQUEST);
break;
default:
handler.illegalMessageReceived(msg);
break;
}
}
},
WAIT_FEATURE_REQUEST {
@Override
void processOFMessage(final OFChannelHandler handler,
final OFMessage msg) {
logProcessOFMessageDetails(handler, msg, this);
switch (msg.getType()) {
case FEATURES_REQUEST:
handler.ofSwitch.processFeaturesRequest(handler.channel, msg);
handler.setState(ChannelState.ESTABLISHED);
break;
case ECHO_REQUEST:
handler.ofSwitch.processEchoRequest(handler.channel, msg);
break;
case ERROR:
handler.logErrorClose((OFErrorMsg) msg);
break;
default:
handler.illegalMessageReceived(msg);
break;
}
}
},
ESTABLISHED {
@Override
void processOFMessage(final OFChannelHandler handler,
final OFMessage msg) {
logProcessOFMessageDetails(handler, msg, this);
// TODO implement
// TODO add this channel to ofSwitch role service
switch (msg.getType()) {
case STATS_REQUEST:
//TODO implement
//TODO: use vNetService to build OFPortDesc.
handler.ofSwitch.processStatsRequest(handler.channel, msg);
break;
case SET_CONFIG:
//TODO implement
handler.ofSwitch.processSetConfigMessage(handler.channel, msg);
break;
case GET_CONFIG_REQUEST:
//TODO implement
handler.ofSwitch.processGetConfigRequest(handler.channel, msg);
break;
case BARRIER_REQUEST:
//TODO implement
handler.ofSwitch.processBarrierRequest(handler.channel, msg);
break;
case ECHO_REQUEST:
handler.ofSwitch.processEchoRequest(handler.channel, msg);
break;
case ROLE_REQUEST:
handler.ofSwitch.processRoleRequest(handler.channel, msg);
break;
case ERROR:
handler.logErrorClose((OFErrorMsg) msg);
break;
default:
handler.unhandledMessageReceived(msg);
break;
}
}
};
abstract void processOFMessage(final OFChannelHandler handler, final OFMessage msg);
private static void logProcessOFMessageDetails(final OFChannelHandler handler,
final OFMessage msg, ChannelState chnState) {
handler.log.trace("Channel state: {} dpid: {} processOFMessage type: {} nsg: {}",
chnState, handler.ofSwitch.dpid(), msg.getType(), msg);
}
}
/**
* Default constructor.
*
* @param ofSwitch openflow switch that owns this channel
*/
public OFChannelHandler(OFSwitch ofSwitch) {
super();
this.ofSwitch = ofSwitch;
setState(INIT);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
this.channel = ctx.channel();
// FIXME move this to channel handler and add channel when OF handshake is done
ofSwitch.addControllerChannel(channel);
try {
ofSwitch.sendOfHello(channel);
log.trace("Send OF_13 Hello to {}", channel.remoteAddress());
setState(ChannelState.WAIT_HELLO);
} catch (Exception ex) {
log.error("Failed sending OF_13 Hello to {} for {}", channel.remoteAddress(), ex.getMessage());
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
ofSwitch.deleteControllerChannel(channel);
log.info("Device {} disconnected from controller {}", ofSwitch.dpid(), channel.remoteAddress());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
state.processOFMessage(this, (OFMessage) msg);
} catch (Exception ex) {
ctx.fireExceptionCaught(ex);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof ReadTimeoutException) {
log.error("Connection closed because of ReadTimeoutException {}", cause.getMessage());
} else if (cause instanceof ClosedChannelException) {
log.error("ClosedChannelException occurred");
return;
} else if (cause instanceof RejectedExecutionException) {
log.error("Could not process message: queue full");
} else if (cause instanceof IOException) {
log.error("IOException occurred");
} else {
log.error("Error while processing message from switch {}", cause.getMessage());
}
channel.close();
}
private void setState(ChannelState state) {
this.state = state;
if (state != INIT) {
log.debug(String.format(MSG_CHANNEL_STATE, channel.remoteAddress(), state.name()));
}
}
private void logErrorClose(OFErrorMsg errorMsg) {
log.error("{} from switch {} in state {}",
errorMsg,
ofSwitch.dpid(),
state);
channel.close();
}
private void illegalMessageReceived(OFMessage ofMessage) {
log.warn("Controller should never send message {} to switch {} in current state {}",
ofMessage.getType(), ofSwitch.dpid(), state);
}
private void unhandledMessageReceived(OFMessage ofMessage) {
log.warn("Unexpected message {} received for switch {} in state {}",
ofMessage.getType(), ofSwitch.dpid(), state);
}
}