2 * Copyright 2014-2015 Open Networking Laboratory
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 package org.onosproject.openflow.controller.impl;
20 import java.io.IOException;
21 import java.nio.channels.ClosedChannelException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.concurrent.CopyOnWriteArrayList;
26 import java.util.concurrent.RejectedExecutionException;
28 import org.jboss.netty.channel.Channel;
29 import org.jboss.netty.channel.ChannelHandlerContext;
30 import org.jboss.netty.channel.ChannelStateEvent;
31 import org.jboss.netty.channel.ExceptionEvent;
32 import org.jboss.netty.channel.MessageEvent;
33 import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
34 import org.jboss.netty.handler.timeout.IdleStateEvent;
35 import org.jboss.netty.handler.timeout.ReadTimeoutException;
36 import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
37 import org.onosproject.openflow.controller.driver.SwitchStateException;
38 import org.projectfloodlight.openflow.exceptions.OFParseError;
39 import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
40 import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
41 import org.projectfloodlight.openflow.protocol.OFBarrierReply;
42 import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
43 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
44 import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
45 import org.projectfloodlight.openflow.protocol.OFEchoReply;
46 import org.projectfloodlight.openflow.protocol.OFEchoRequest;
47 import org.projectfloodlight.openflow.protocol.OFErrorMsg;
48 import org.projectfloodlight.openflow.protocol.OFErrorType;
49 import org.projectfloodlight.openflow.protocol.OFExperimenter;
50 import org.projectfloodlight.openflow.protocol.OFFactory;
51 import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
52 import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
53 import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
54 import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
55 import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
56 import org.projectfloodlight.openflow.protocol.OFHello;
57 import org.projectfloodlight.openflow.protocol.OFHelloElem;
58 import org.projectfloodlight.openflow.protocol.OFMessage;
59 import org.projectfloodlight.openflow.protocol.OFPacketIn;
60 import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
61 import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
62 import org.projectfloodlight.openflow.protocol.OFPortStatus;
63 import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
64 import org.projectfloodlight.openflow.protocol.OFRoleReply;
65 import org.projectfloodlight.openflow.protocol.OFSetConfig;
66 import org.projectfloodlight.openflow.protocol.OFStatsReply;
67 import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
68 import org.projectfloodlight.openflow.protocol.OFStatsType;
69 import org.projectfloodlight.openflow.protocol.OFType;
70 import org.projectfloodlight.openflow.protocol.OFVersion;
71 import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
72 import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
73 import org.projectfloodlight.openflow.types.U32;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
78 * Channel handler deals with the switch connection and dispatches
79 * switch messages to the appropriate locations.
81 class OFChannelHandler extends IdleStateAwareChannelHandler {
82 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
84 private static final String RESET_BY_PEER = "Connection reset by peer";
85 private static final String BROKEN_PIPE = "Broken pipe";
87 private final Controller controller;
88 private OpenFlowSwitchDriver sw;
89 private long thisdpid; // channelHandler cached value of connected switch id
90 private Channel channel;
91 // State needs to be volatile because the HandshakeTimeoutHandler
92 // needs to check if the handshake is complete
93 private volatile ChannelState state;
95 // When a switch with a duplicate dpid is found (i.e we already have a
96 // connected switch with the same dpid), the new switch is immediately
97 // disconnected. At that point netty callsback channelDisconnected() which
98 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
99 // switch state for the older (still connected) switch
100 private volatile Boolean duplicateDpidFound;
102 // Temporary storage for switch-features and port-description
103 private OFFeaturesReply featuresReply;
104 private List<OFPortDescStatsReply> portDescReplies;
105 //private OFPortDescStatsReply portDescReply;
106 // a concurrent ArrayList to temporarily store port status messages
107 // before we are ready to deal with them
108 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
110 //Indicates the openflow version used by this switch
111 protected OFVersion ofVersion;
112 protected OFFactory factory13;
113 protected OFFactory factory10;
115 /** transaction Ids to use during handshake. Since only one thread
116 * calls into an OFChannelHandler instance, we don't need atomic.
119 private int handshakeTransactionIds = -1;
122 * Create a new unconnected OFChannelHandler.
123 * @param controller parent controller
125 OFChannelHandler(Controller controller) {
126 this.controller = controller;
127 this.state = ChannelState.INIT;
128 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
129 this.portDescReplies = new ArrayList<OFPortDescStatsReply>();
130 factory13 = controller.getOFMessageFactory13();
131 factory10 = controller.getOFMessageFactory10();
132 duplicateDpidFound = Boolean.FALSE;
137 // XXX S consider if necessary
138 public void disconnectSwitch() {
139 sw.disconnectSwitch();
144 //*************************
145 // Channel State Machine
146 //*************************
149 * The state machine for handling the switch/channel state. All state
150 * transitions should happen from within the state machine (and not from other
155 * Initial state before channel is connected.
159 void processOFMessage(OFChannelHandler h, OFMessage m)
160 throws IOException, SwitchStateException {
161 illegalMessageReceived(h, m);
165 void processOFError(OFChannelHandler h, OFErrorMsg m)
167 // need to implement since its abstract but it will never
172 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
174 unhandledMessageReceived(h, m);
179 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
180 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
181 * protocol version is accepted.
182 * We send an OFFeaturesRequest depending on the protocol version selected
183 * Next state is WAIT_FEATURES_REPLY
187 void processOFHello(OFChannelHandler h, OFHello m)
189 // TODO We could check for the optional bitmap, but for now
190 // we are just checking the version number.
191 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
192 log.debug("Received {} Hello from {} - switching to OF "
193 + "version 1.3", m.getVersion(),
194 h.channel.getRemoteAddress());
195 h.sendHandshakeHelloMessage();
196 h.ofVersion = OFVersion.OF_13;
197 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
198 log.debug("Received {} Hello from {} - switching to OF "
199 + "version 1.0", m.getVersion(),
200 h.channel.getRemoteAddress());
202 h.factory10.buildHello()
203 .setXid(h.handshakeTransactionIds--)
205 h.channel.write(Collections.singletonList(hi));
206 h.ofVersion = OFVersion.OF_10;
208 log.error("Received Hello of version {} from switch at {}. "
209 + "This controller works with OF1.0 and OF1.3 "
210 + "switches. Disconnecting switch ...",
211 m.getVersion(), h.channel.getRemoteAddress());
212 h.channel.disconnect();
215 h.sendHandshakeFeaturesRequestMessage();
216 h.setState(WAIT_FEATURES_REPLY);
219 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
220 throws IOException, SwitchStateException {
221 illegalMessageReceived(h, m);
224 void processOFStatisticsReply(OFChannelHandler h,
226 throws IOException, SwitchStateException {
227 illegalMessageReceived(h, m);
230 void processOFError(OFChannelHandler h, OFErrorMsg m) {
231 logErrorDisconnect(h, m);
235 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
237 unhandledMessageReceived(h, m);
243 * We are waiting for a features reply message. Once we receive it, the
244 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
245 * we send a SetConfig request, barrier, and GetConfig request and the
246 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
247 * request and the next state is WAIT_PORT_DESC_REPLY.
249 WAIT_FEATURES_REPLY(false) {
251 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
253 h.thisdpid = m.getDatapathId().getLong();
254 log.debug("Received features reply for switch at {} with dpid {}",
255 h.getSwitchInfoString(), h.thisdpid);
257 h.featuresReply = m; //temp store
258 if (h.ofVersion == OFVersion.OF_10) {
259 h.sendHandshakeSetConfig();
260 h.setState(WAIT_CONFIG_REPLY);
262 //version is 1.3, must get switchport information
263 h.sendHandshakeOFPortDescRequest();
264 h.setState(WAIT_PORT_DESC_REPLY);
268 void processOFStatisticsReply(OFChannelHandler h,
270 throws IOException, SwitchStateException {
271 illegalMessageReceived(h, m);
274 void processOFError(OFChannelHandler h, OFErrorMsg m) {
275 logErrorDisconnect(h, m);
279 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
281 h.pendingPortStatusMsg.add(m);
286 * We are waiting for a description of the 1.3 switch ports.
287 * Once received, we send a SetConfig request
288 * Next State is WAIT_CONFIG_REPLY
290 WAIT_PORT_DESC_REPLY(false) {
293 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
294 throws SwitchStateException {
295 // Read port description
296 if (m.getStatsType() != OFStatsType.PORT_DESC) {
297 log.warn("Expecting port description stats but received stats "
298 + "type {} from {}. Ignoring ...", m.getStatsType(),
299 h.channel.getRemoteAddress());
302 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
303 log.debug("Stats reply indicates more stats from sw {} for "
304 + "port description",
305 h.getSwitchInfoString());
306 h.portDescReplies.add((OFPortDescStatsReply)m);
310 h.portDescReplies.add((OFPortDescStatsReply)m);
312 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
313 log.info("Received port desc reply for switch at {}",
314 h.getSwitchInfoString());
316 h.sendHandshakeSetConfig();
317 } catch (IOException e) {
318 log.error("Unable to send setConfig after PortDescReply. "
319 + "Error: {}", e.getMessage());
321 h.setState(WAIT_CONFIG_REPLY);
325 void processOFError(OFChannelHandler h, OFErrorMsg m)
326 throws IOException, SwitchStateException {
327 logErrorDisconnect(h, m);
332 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
333 throws IOException, SwitchStateException {
334 h.pendingPortStatusMsg.add(m);
340 * We are waiting for a config reply message. Once we receive it
341 * we send a DescriptionStatsRequest to the switch.
342 * Next state: WAIT_DESCRIPTION_STAT_REPLY
344 WAIT_CONFIG_REPLY(false) {
346 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
348 if (m.getMissSendLen() == 0xffff) {
349 log.trace("Config Reply from switch {} confirms "
350 + "miss length set to 0xffff",
351 h.getSwitchInfoString());
353 // FIXME: we can't really deal with switches that don't send
354 // full packets. Shouldn't we drop the connection here?
355 log.warn("Config Reply from switch {} has"
356 + "miss length set to {}",
357 h.getSwitchInfoString(),
360 h.sendHandshakeDescriptionStatsRequest();
361 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
365 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
370 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
371 throws IOException, SwitchStateException {
372 illegalMessageReceived(h, m);
375 void processOFStatisticsReply(OFChannelHandler h,
377 throws IOException, SwitchStateException {
378 log.error("Received multipart(stats) message sub-type {}",
380 illegalMessageReceived(h, m);
384 void processOFError(OFChannelHandler h, OFErrorMsg m) {
385 logErrorDisconnect(h, m);
389 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
391 h.pendingPortStatusMsg.add(m);
397 * We are waiting for a OFDescriptionStat message from the switch.
398 * Once we receive any stat message we try to parse it. If it's not
399 * a description stats message we disconnect. If its the expected
400 * description stats message, we:
401 * - use the switch driver to bind the switch and get an IOFSwitch instance
402 * - setup the IOFSwitch instance
403 * - add switch controller and send the initial role
404 * request to the switch.
405 * Next state: WAIT_INITIAL_ROLE
406 * In the typical case, where switches support role request messages
407 * the next state is where we expect the role reply message.
408 * In the special case that where the switch does not support any kind
409 * of role request messages, we don't send a role message, but we do
410 * request mastership from the registry service. This controller
411 * should become master once we hear back from the registry service.
412 * All following states will have a h.sw instance!
414 WAIT_DESCRIPTION_STAT_REPLY(false) {
416 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
417 throws SwitchStateException {
418 // Read description, if it has been updated
419 if (m.getStatsType() != OFStatsType.DESC) {
420 log.warn("Expecting Description stats but received stats "
421 + "type {} from {}. Ignoring ...", m.getStatsType(),
422 h.channel.getRemoteAddress());
425 OFDescStatsReply drep = (OFDescStatsReply) m;
426 log.info("Received switch description reply {} from switch at {}",
427 drep, h.channel.getRemoteAddress());
428 // Here is where we differentiate between different kinds of switches
429 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
431 h.sw.setOFVersion(h.ofVersion);
432 h.sw.setFeaturesReply(h.featuresReply);
433 //h.sw.setPortDescReply(h.portDescReply);
434 h.sw.setPortDescReplies(h.portDescReplies);
435 h.sw.setConnected(true);
436 h.sw.setChannel(h.channel);
437 // boolean success = h.sw.connectSwitch();
440 // disconnectDuplicate(h);
443 // set switch information
447 log.debug("Switch {} bound to class {}, description {}",
448 new Object[] {h.sw, h.sw.getClass(), drep });
449 //Put switch in EQUAL mode until we hear back from the global registry
450 //log.debug("Setting new switch {} to EQUAL and sending Role request",
451 // h.sw.getStringId());
452 //h.sw.activateEqualSwitch();
453 //h.setSwitchRole(RoleState.EQUAL);
455 h.sw.startDriverHandshake();
456 if (h.sw.isDriverHandshakeComplete()) {
457 if (!h.sw.connectSwitch()) {
458 disconnectDuplicate(h);
460 handlePendingPortStatusMessages(h);
463 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
469 void processOFError(OFChannelHandler h, OFErrorMsg m) {
470 logErrorDisconnect(h, m);
474 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
475 throws IOException, SwitchStateException {
476 illegalMessageReceived(h, m);
480 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
482 h.pendingPortStatusMsg.add(m);
488 * We are waiting for the respective switch driver to complete its
489 * configuration. Notice that we do not consider this to be part of the main
490 * switch-controller handshake. But we do consider it as a step that comes
491 * before we declare the switch as available to the controller.
492 * Next State: depends on the role of this controller for this switch - either
495 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
498 void processOFError(OFChannelHandler h, OFErrorMsg m)
500 // will never be called. We override processOFMessage
506 void processOFMessage(OFChannelHandler h, OFMessage m)
507 throws IOException, SwitchStateException {
509 if (h.sw.isDriverHandshakeComplete()) {
511 h.state.processOFMessage(h, m);
516 if (m.getType() == OFType.ECHO_REQUEST) {
517 processOFEchoRequest(h, (OFEchoRequest) m);
518 } else if (m.getType() == OFType.ECHO_REPLY) {
519 processOFEchoReply(h, (OFEchoReply) m);
520 } else if (m.getType() == OFType.ROLE_REPLY) {
522 } else if (m.getType() == OFType.ERROR) {
523 if (!h.sw.handleRoleError((OFErrorMsg)m)) {
524 h.sw.processDriverHandshakeMessage(m);
525 if (h.sw.isDriverHandshakeComplete()) {
530 if (m.getType() == OFType.EXPERIMENTER &&
531 ((OFExperimenter) m).getExperimenter() ==
532 RoleManager.NICIRA_EXPERIMENTER) {
533 h.sw.handleNiciraRole(m);
535 h.sw.processDriverHandshakeMessage(m);
536 if (h.sw.isDriverHandshakeComplete()) {
544 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
545 throws IOException, SwitchStateException {
546 h.pendingPortStatusMsg.add(m);
549 private void moveToActive(OFChannelHandler h) {
550 boolean success = h.sw.connectSwitch();
551 handlePendingPortStatusMessages(h);
554 disconnectDuplicate(h);
562 * This controller is in MASTER role for this switch. We enter this state
563 * after requesting and winning control from the global registry.
564 * The main handshake as well as the switch-driver sub-handshake
565 * is complete at this point.
566 * // XXX S reconsider below
567 * In the (near) future we may deterministically assign controllers to
568 * switches at startup.
569 * We only leave this state if the switch disconnects or
570 * if we send a role request for SLAVE /and/ receive the role reply for
575 void processOFError(OFChannelHandler h, OFErrorMsg m)
576 throws IOException, SwitchStateException {
577 // if we get here, then the error message is for something else
578 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
579 ((OFBadRequestErrorMsg) m).getCode() ==
580 OFBadRequestCode.EPERM) {
581 // We are the master controller and the switch returned
582 // a permission error. This is a likely indicator that
583 // the switch thinks we are slave. Reassert our
585 // FIXME: this could be really bad during role transitions
586 // if two controllers are master (even if its only for
587 // a brief period). We might need to see if these errors
588 // persist before we reassert
591 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
592 ((OFFlowModFailedErrorMsg) m).getCode() ==
593 OFFlowModFailedCode.ALL_TABLES_FULL) {
594 h.sw.setTableFull(true);
598 h.dispatchMessage(m);
602 void processOFStatisticsReply(OFChannelHandler h,
604 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
605 h.sw.setPortDescReply((OFPortDescStatsReply) m);
607 h.dispatchMessage(m);
611 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
612 throws SwitchStateException {
613 h.sw.handleNiciraRole(m);
617 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
618 throws SwitchStateException {
623 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
624 throws SwitchStateException {
625 handlePortStatusMessage(h, m, true);
626 //h.dispatchMessage(m);
630 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
632 // h.sw.factory().buildPacketOut()
633 // .setXid(m.getXid())
634 // .setBufferId(m.getBufferId()).build();
635 // h.sw.sendMsg(out);
636 h.dispatchMessage(m);
640 void processOFFlowRemoved(OFChannelHandler h,
642 h.dispatchMessage(m);
646 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
647 h.dispatchMessage(m);
651 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
652 h.sw.setFeaturesReply(m);
653 h.dispatchMessage(m);
658 private final boolean handshakeComplete;
659 ChannelState(boolean handshakeComplete) {
660 this.handshakeComplete = handshakeComplete;
664 * Is this a state in which the handshake has completed?
665 * @return true if the handshake is complete
667 public boolean isHandshakeComplete() {
668 return handshakeComplete;
672 * Get a string specifying the switch connection, state, and
673 * message received. To be used as message for SwitchStateException
675 * @param h The channel handler (to get switch information_
676 * @param m The OFMessage that has just been received
677 * @param details A string giving more details about the exact nature
679 * @return display string
681 // needs to be protected because enum members are actually subclasses
682 protected String getSwitchStateMessage(OFChannelHandler h,
685 return String.format("Switch: [%s], State: [%s], received: [%s]"
687 h.getSwitchInfoString(),
689 m.getType().toString(),
694 * We have an OFMessage we didn't expect given the current state and
695 * we want to treat this as an error.
696 * We currently throw an exception that will terminate the connection
697 * However, we could be more forgiving
698 * @param h the channel handler that received the message
699 * @param m the message
700 * @throws SwitchStateException we always throw the exception
702 // needs to be protected because enum members are actually subclasses
703 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
704 throws SwitchStateException {
705 String msg = getSwitchStateMessage(h, m,
706 "Switch should never send this message in the current state");
707 throw new SwitchStateException(msg);
712 * We have an OFMessage we didn't expect given the current state and
713 * we want to ignore the message.
714 * @param h the channel handler the received the message
715 * @param m the message
717 protected void unhandledMessageReceived(OFChannelHandler h,
719 if (log.isDebugEnabled()) {
720 String msg = getSwitchStateMessage(h, m,
721 "Ignoring unexpected message");
727 * Log an OpenFlow error message from a switch.
728 * @param h The switch that sent the error
729 * @param error The error message
731 protected void logError(OFChannelHandler h, OFErrorMsg error) {
732 log.error("{} from switch {} in state {}",
735 h.getSwitchInfoString(),
740 * Log an OpenFlow error message from a switch and disconnect the
743 * @param h the IO channel for this switch.
744 * @param error The error message
746 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
748 h.channel.disconnect();
752 * log an error message for a duplicate dpid and disconnect this channel.
753 * @param h the IO channel for this switch.
755 protected void disconnectDuplicate(OFChannelHandler h) {
756 log.error("Duplicated dpid or incompleted cleanup - "
757 + "disconnecting channel {}", h.getSwitchInfoString());
758 h.duplicateDpidFound = Boolean.TRUE;
759 h.channel.disconnect();
765 * Handles all pending port status messages before a switch is declared
766 * activated in MASTER or EQUAL role. Note that since this handling
767 * precedes the activation (and therefore notification to IOFSwitchListerners)
768 * the changes to ports will already be visible once the switch is
769 * activated. As a result, no notifications are sent out for these
770 * pending portStatus messages.
772 * @param h the channel handler that received the message
774 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
776 handlePendingPortStatusMessages(h, 0);
777 } catch (SwitchStateException e) {
778 log.error(e.getMessage());
782 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
783 throws SwitchStateException {
785 String msg = "State machine error: switch is null. Should never " +
787 throw new SwitchStateException(msg);
789 log.info("Processing {} pending port status messages for {}",
790 h.pendingPortStatusMsg.size(), h.sw.getStringId());
792 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
793 for (OFPortStatus ps: h.pendingPortStatusMsg) {
795 handlePortStatusMessage(h, ps, false);
797 // expensive but ok - we don't expect too many port-status messages
798 // note that we cannot use clear(), because of the reasons below
799 h.pendingPortStatusMsg.removeAll(temp);
801 // the iterator above takes a snapshot of the list - so while we were
802 // dealing with the pending port-status messages, we could have received
803 // newer ones. Handle them recursively, but break the recursion after
804 // five steps to avoid an attack.
805 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
806 handlePendingPortStatusMessages(h, index);
811 * Handle a port status message.
813 * Handle a port status message by updating the port maps in the
814 * IOFSwitch instance and notifying Controller about the change so
815 * it can dispatch a switch update.
817 * @param h The OFChannelHhandler that received the message
818 * @param m The PortStatus message we received
819 * @param doNotify if true switch port changed events will be
821 * @throws SwitchStateException if the switch is not bound to the channel
824 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
825 boolean doNotify) throws SwitchStateException {
827 String msg = getSwitchStateMessage(h, m,
828 "State machine error: switch is null. Should never " +
830 throw new SwitchStateException(msg);
833 h.sw.handleMessage(m);
838 * Process an OF message received on the channel and
839 * update state accordingly.
841 * The main "event" of the state machine. Process the received message,
842 * send follow up message if required and update state if required.
844 * Switches on the message type and calls more specific event handlers
845 * for each individual OF message type. If we receive a message that
846 * is supposed to be sent from a controller to a switch we throw
847 * a SwitchStateExeption.
849 * The more specific handlers can also throw SwitchStateExceptions
851 * @param h The OFChannelHandler that received the message
852 * @param m The message we received.
853 * @throws SwitchStateException if the switch is not bound to the channel
854 * @throws IOException if unable to send message back to the switch
856 void processOFMessage(OFChannelHandler h, OFMessage m)
857 throws IOException, SwitchStateException {
858 switch(m.getType()) {
860 processOFHello(h, (OFHello) m);
863 processOFBarrierReply(h, (OFBarrierReply) m);
866 processOFEchoReply(h, (OFEchoReply) m);
869 processOFEchoRequest(h, (OFEchoRequest) m);
872 processOFError(h, (OFErrorMsg) m);
875 processOFFeaturesReply(h, (OFFeaturesReply) m);
878 processOFFlowRemoved(h, (OFFlowRemoved) m);
880 case GET_CONFIG_REPLY:
881 processOFGetConfigReply(h, (OFGetConfigReply) m);
884 processOFPacketIn(h, (OFPacketIn) m);
887 processOFPortStatus(h, (OFPortStatus) m);
889 case QUEUE_GET_CONFIG_REPLY:
890 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
892 case STATS_REPLY: // multipart_reply in 1.3
893 processOFStatisticsReply(h, (OFStatsReply) m);
896 processOFExperimenter(h, (OFExperimenter) m);
899 processOFRoleReply(h, (OFRoleReply) m);
901 case GET_ASYNC_REPLY:
902 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
905 // The following messages are sent to switches. The controller
906 // should never receive them
908 case GET_CONFIG_REQUEST:
911 case QUEUE_GET_CONFIG_REQUEST:
912 case BARRIER_REQUEST:
913 case STATS_REQUEST: // multipart request in 1.3
914 case FEATURES_REQUEST:
918 case GET_ASYNC_REQUEST:
922 illegalMessageReceived(h, m);
927 /*-----------------------------------------------------------------
928 * Default implementation for message handlers in any state.
930 * Individual states must override these if they want a behavior
931 * that differs from the default.
933 * In general, these handlers simply ignore the message and do
936 * There are some exceptions though, since some messages really
937 * are handled the same way in every state (e.g., ECHO_REQUST) or
938 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
939 -----------------------------------------------------------------*/
941 void processOFHello(OFChannelHandler h, OFHello m)
942 throws IOException, SwitchStateException {
943 // we only expect hello in the WAIT_HELLO state
944 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
945 h.channel.getRemoteAddress());
948 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
953 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
955 if (h.ofVersion == null) {
956 log.error("No OF version set for {}. Not sending Echo REPLY",
957 h.channel.getRemoteAddress());
960 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
961 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
962 OFEchoReply reply = factory
965 .setData(m.getData())
967 h.channel.write(Collections.singletonList(reply));
970 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
972 // Do nothing with EchoReplies !!
975 // no default implementation for OFError
976 // every state must override it
977 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
978 throws IOException, SwitchStateException;
981 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
982 throws IOException, SwitchStateException {
983 unhandledMessageReceived(h, m);
986 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
988 unhandledMessageReceived(h, m);
991 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
992 throws IOException, SwitchStateException {
993 // we only expect config replies in the WAIT_CONFIG_REPLY state
994 illegalMessageReceived(h, m);
997 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
999 unhandledMessageReceived(h, m);
1002 // no default implementation. Every state needs to handle it.
1003 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1004 throws IOException, SwitchStateException;
1006 void processOFQueueGetConfigReply(OFChannelHandler h,
1007 OFQueueGetConfigReply m)
1008 throws IOException {
1009 unhandledMessageReceived(h, m);
1012 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1013 throws IOException, SwitchStateException {
1014 unhandledMessageReceived(h, m);
1017 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1018 throws IOException, SwitchStateException {
1019 // TODO: it might make sense to parse the vendor message here
1020 // into the known vendor messages we support and then call more
1021 // specific event handlers
1022 unhandledMessageReceived(h, m);
1025 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1026 throws SwitchStateException, IOException {
1027 unhandledMessageReceived(h, m);
1030 void processOFGetAsyncReply(OFChannelHandler h,
1031 OFAsyncGetReply m) {
1032 unhandledMessageReceived(h, m);
1039 //*************************
1040 // Channel handler methods
1041 //*************************
1044 public void channelConnected(ChannelHandlerContext ctx,
1045 ChannelStateEvent e) throws Exception {
1046 channel = e.getChannel();
1047 log.info("New switch connection from {}",
1048 channel.getRemoteAddress());
1050 hack to wait for the switch to tell us what it's
1051 max version is. This is not spec compliant and should
1052 be removed as soon as switches behave better.
1054 //sendHandshakeHelloMessage();
1055 setState(ChannelState.WAIT_HELLO);
1059 public void channelDisconnected(ChannelHandlerContext ctx,
1060 ChannelStateEvent e) throws Exception {
1061 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1062 getSwitchInfoString());
1063 if (thisdpid != 0) {
1064 if (!duplicateDpidFound) {
1065 // if the disconnected switch (on this ChannelHandler)
1066 // was not one with a duplicate-dpid, it is safe to remove all
1067 // state for it at the controller. Notice that if the disconnected
1068 // switch was a duplicate-dpid, calling the method below would clear
1069 // all state for the original switch (with the same dpid),
1070 // which we obviously don't want.
1071 log.info("{}:removal called", getSwitchInfoString());
1073 sw.removeConnectedSwitch();
1076 // A duplicate was disconnected on this ChannelHandler,
1077 // this is the same switch reconnecting, but the original state was
1078 // not cleaned up - XXX check liveness of original ChannelHandler
1079 log.info("{}:duplicate found", getSwitchInfoString());
1080 duplicateDpidFound = Boolean.FALSE;
1083 log.warn("no dpid in channelHandler registered for "
1084 + "disconnected switch {}", getSwitchInfoString());
1089 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1091 if (e.getCause() instanceof ReadTimeoutException) {
1093 log.error("Disconnecting switch {} due to read timeout",
1094 getSwitchInfoString());
1095 ctx.getChannel().close();
1096 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1097 log.error("Disconnecting switch {}: failed to complete handshake",
1098 getSwitchInfoString());
1099 ctx.getChannel().close();
1100 } else if (e.getCause() instanceof ClosedChannelException) {
1101 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1102 } else if (e.getCause() instanceof IOException) {
1103 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1104 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1105 log.error("Disconnecting switch {} due to IO Error: {}",
1106 getSwitchInfoString(), e.getCause().getMessage());
1107 if (log.isDebugEnabled()) {
1108 // still print stack trace if debug is enabled
1109 log.debug("StackTrace for previous Exception: ", e.getCause());
1112 ctx.getChannel().close();
1113 } else if (e.getCause() instanceof SwitchStateException) {
1114 log.error("Disconnecting switch {} due to switch state error: {}",
1115 getSwitchInfoString(), e.getCause().getMessage());
1116 if (log.isDebugEnabled()) {
1117 // still print stack trace if debug is enabled
1118 log.debug("StackTrace for previous Exception: ", e.getCause());
1120 ctx.getChannel().close();
1121 } else if (e.getCause() instanceof OFParseError) {
1122 log.error("Disconnecting switch "
1123 + getSwitchInfoString() +
1124 " due to message parse failure",
1126 ctx.getChannel().close();
1127 } else if (e.getCause() instanceof RejectedExecutionException) {
1128 log.warn("Could not process message: queue full");
1130 log.error("Error while processing message from switch "
1131 + getSwitchInfoString()
1132 + "state " + this.state, e.getCause());
1133 ctx.getChannel().close();
1138 public String toString() {
1139 return getSwitchInfoString();
1143 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1145 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1146 OFMessage m = factory.buildEchoRequest().build();
1147 log.debug("Sending Echo Request on idle channel: {}",
1148 e.getChannel().getPipeline().getLast().toString());
1149 e.getChannel().write(Collections.singletonList(m));
1150 // XXX S some problems here -- echo request has no transaction id, and
1151 // echo reply is not correlated to the echo request.
1155 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1157 if (e.getMessage() instanceof List) {
1158 @SuppressWarnings("unchecked")
1159 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1162 for (OFMessage ofm : msglist) {
1163 // Do the actual packet processing
1164 state.processOFMessage(this, ofm);
1167 state.processOFMessage(this, (OFMessage) e.getMessage());
1173 //*************************
1174 // Channel utility methods
1175 //*************************
1178 * Is this a state in which the handshake has completed?
1179 * @return true if the handshake is complete
1181 public boolean isHandshakeComplete() {
1182 return this.state.isHandshakeComplete();
1185 private void dispatchMessage(OFMessage m) {
1186 sw.handleMessage(m);
1190 * Return a string describing this switch based on the already available
1191 * information (DPID and/or remote socket).
1192 * @return display string
1194 private String getSwitchInfoString() {
1196 return sw.toString();
1198 String channelString;
1199 if (channel == null || channel.getRemoteAddress() == null) {
1200 channelString = "?";
1202 channelString = channel.getRemoteAddress().toString();
1205 if (featuresReply == null) {
1208 dpidString = featuresReply.getDatapathId().toString();
1210 return String.format("[%s DPID[%s]]", channelString, dpidString);
1214 * Update the channels state. Only called from the state machine.
1215 * TODO: enforce restricted state transitions
1218 private void setState(ChannelState state) {
1223 * Send hello message to the switch using the handshake transactions ids.
1224 * @throws IOException
1226 private void sendHandshakeHelloMessage() throws IOException {
1227 // The OF protocol requires us to start things off by sending the highest
1228 // version of the protocol supported.
1230 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1231 // see Sec. 7.5.1 of the OF1.3.4 spec
1232 U32 bitmap = U32.ofRaw(0x00000012);
1233 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1234 .setBitmaps(Collections.singletonList(bitmap))
1236 OFMessage.Builder mb = factory13.buildHello()
1237 .setXid(this.handshakeTransactionIds--)
1238 .setElements(Collections.singletonList(hem));
1239 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1240 channel.write(Collections.singletonList(mb.build()));
1244 * Send featuresRequest msg to the switch using the handshake transactions ids.
1245 * @throws IOException
1247 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1248 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1249 OFMessage m = factory.buildFeaturesRequest()
1250 .setXid(this.handshakeTransactionIds--)
1252 channel.write(Collections.singletonList(m));
1256 * Send the configuration requests to tell the switch we want full
1258 * @throws IOException
1260 private void sendHandshakeSetConfig() throws IOException {
1261 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1262 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1263 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1265 // Ensure we receive the full packet via PacketIn
1266 // FIXME: We don't set the reassembly flags.
1267 // Only send config to switches to send full packets, if they have a buffer.
1268 // Saves a packet & OFSetConfig can't be handled by certain switches.
1269 if(this.featuresReply.getNBuffers() > 0) {
1270 OFSetConfig sc = factory
1272 .setMissSendLen((short) 0xffff)
1273 .setXid(this.handshakeTransactionIds--)
1279 OFBarrierRequest br = factory
1280 .buildBarrierRequest()
1281 .setXid(this.handshakeTransactionIds--)
1285 // Verify (need barrier?)
1286 OFGetConfigRequest gcr = factory
1287 .buildGetConfigRequest()
1288 .setXid(this.handshakeTransactionIds--)
1291 channel.write(msglist);
1295 * send a description state request.
1296 * @throws IOException
1298 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1299 // Get Description to set switch-specific flags
1300 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1301 OFDescStatsRequest dreq = factory
1302 .buildDescStatsRequest()
1303 .setXid(handshakeTransactionIds--)
1305 channel.write(Collections.singletonList(dreq));
1308 private void sendHandshakeOFPortDescRequest() throws IOException {
1309 // Get port description for 1.3 switch
1310 OFPortDescStatsRequest preq = factory13
1311 .buildPortDescStatsRequest()
1312 .setXid(handshakeTransactionIds--)
1314 channel.write(Collections.singletonList(preq));
1317 ChannelState getStateForTesting() {