blob: 9a8f21ba8e03c35b82d2618faf2db82ca16dcbb1 [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.core.internal;
2
3import java.io.IOException;
4import java.nio.channels.ClosedChannelException;
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.List;
9import java.util.concurrent.CopyOnWriteArrayList;
10import java.util.concurrent.RejectedExecutionException;
11
12import net.floodlightcontroller.core.FloodlightContext;
13import net.floodlightcontroller.core.IFloodlightProviderService.Role;
14import net.floodlightcontroller.core.IOFSwitch;
15import net.floodlightcontroller.core.IOFSwitch.PortChangeEvent;
16import net.floodlightcontroller.core.annotations.LogMessageDoc;
17import net.floodlightcontroller.core.annotations.LogMessageDocs;
18import net.floodlightcontroller.core.internal.Controller.Counters;
19import net.floodlightcontroller.core.internal.OFChannelHandler.ChannelState.RoleReplyInfo;
20import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
21import net.floodlightcontroller.util.LoadMonitor;
22
23import org.jboss.netty.channel.Channel;
24import org.jboss.netty.channel.ChannelHandlerContext;
25import org.jboss.netty.channel.ChannelStateEvent;
26import org.jboss.netty.channel.Channels;
27import org.jboss.netty.channel.ExceptionEvent;
28import org.jboss.netty.channel.MessageEvent;
29import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
30import org.jboss.netty.handler.timeout.IdleStateEvent;
31import org.jboss.netty.handler.timeout.ReadTimeoutException;
32import org.projectfloodlight.openflow.exceptions.OFParseError;
33import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
34import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
35import org.projectfloodlight.openflow.protocol.OFBarrierReply;
36import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
37import org.projectfloodlight.openflow.protocol.OFControllerRole;
38import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
39import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
40import org.projectfloodlight.openflow.protocol.OFEchoReply;
41import org.projectfloodlight.openflow.protocol.OFEchoRequest;
42import org.projectfloodlight.openflow.protocol.OFErrorMsg;
43import org.projectfloodlight.openflow.protocol.OFErrorType;
44import org.projectfloodlight.openflow.protocol.OFExperimenter;
45import org.projectfloodlight.openflow.protocol.OFFactory;
46import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
47import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
48import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
49import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
50import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
51import org.projectfloodlight.openflow.protocol.OFHello;
52import org.projectfloodlight.openflow.protocol.OFHelloElem;
53import org.projectfloodlight.openflow.protocol.OFMessage;
54import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
55import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
56import org.projectfloodlight.openflow.protocol.OFPacketIn;
57import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
58import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
59import org.projectfloodlight.openflow.protocol.OFPortStatus;
60import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
61import org.projectfloodlight.openflow.protocol.OFRoleReply;
62import org.projectfloodlight.openflow.protocol.OFRoleRequest;
63import org.projectfloodlight.openflow.protocol.OFSetConfig;
64import org.projectfloodlight.openflow.protocol.OFStatsReply;
65import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
66import org.projectfloodlight.openflow.protocol.OFStatsType;
67import org.projectfloodlight.openflow.protocol.OFType;
68import org.projectfloodlight.openflow.protocol.OFVersion;
69import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
70import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
71import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg;
72import org.projectfloodlight.openflow.types.U32;
73import org.projectfloodlight.openflow.types.U64;
74import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070077/**
Saurav Dasfcdad072014-08-13 14:15:21 -070078 * Channel handler deals with the switch connection and dispatches switch
79 * messages to the appropriate locations.
Jonathan Hart6eda2302014-08-14 14:57:03 -070080 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070081 * @author readams, gregor, saurav
82 */
83class OFChannelHandler extends IdleStateAwareChannelHandler {
84
85 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
86
Saurav Das85399942014-09-02 16:11:48 -070087 private static final long DEFAULT_ROLE_TIMEOUT_MS = 3 * 1000; // 3 sec
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070088 private final Controller controller;
89 private final Counters counters;
90 private IOFSwitch sw;
91 private long thisdpid; // channelHandler cached value of connected switch id
92 private Channel channel;
93 // State needs to be volatile because the HandshakeTimeoutHandler
94 // needs to check if the handshake is complete
95 private volatile ChannelState state;
96
Saurav Dasfcdad072014-08-13 14:15:21 -070097 // All role messaging is handled by the roleChanger. The channel state
98 // machine
99 // coordinates between the roleChanger and the
100 // controller-global-registry-service
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700101 // to determine controller roles per switch.
102 private RoleChanger roleChanger;
103 // Used to coordinate between the controller and the cleanup thread(?)
104 // for access to the global registry on a per switch basis.
105 volatile Boolean controlRequested;
106 // When a switch with a duplicate dpid is found (i.e we already have a
107 // connected switch with the same dpid), the new switch is immediately
108 // disconnected. At that point netty callsback channelDisconnected() which
Saurav Dasfcdad072014-08-13 14:15:21 -0700109 // proceeds to cleaup switch state - we need to ensure that it does not
110 // cleanup
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700111 // switch state for the older (still connected) switch
112 private volatile Boolean duplicateDpidFound;
113
114 // Temporary storage for switch-features and port-description
115 private OFFeaturesReply featuresReply;
Sangho Shinda7635e2014-11-03 16:22:11 -0800116 private List<OFPortDescStatsReply> portDescReplies;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700117 // a concurrent ArrayList to temporarily store port status messages
118 // before we are ready to deal with them
119 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
120
Saurav Dasfcdad072014-08-13 14:15:21 -0700121 // Indicates the openflow version used by this switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700122 protected OFVersion ofVersion;
123 protected static OFFactory factory13;
124 protected static OFFactory factory10;
125
Jonathan Hart6eda2302014-08-14 14:57:03 -0700126 // Set to true if the controller should send a 1.0 HELLO instead of a 1.3
127 // HELLO. This is to support older switches that can't support 1.3 HELLO
128 // messages correctly.
129 static boolean useOnly10 = false;
130
Saurav Dasfcdad072014-08-13 14:15:21 -0700131 /**
132 * transaction Ids to use during handshake. Since only one thread calls into
133 * an OFChannelHandler instance, we don't need atomic. We will count down
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700134 */
135 private int handshakeTransactionIds = -1;
136
137 /**
138 * Create a new unconnected OFChannelHandler.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700139 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700140 * @param controller
141 */
142 OFChannelHandler(Controller controller) {
143 this.controller = controller;
144 this.counters = controller.getCounters();
145 this.roleChanger = new RoleChanger(DEFAULT_ROLE_TIMEOUT_MS);
146 this.state = ChannelState.INIT;
147 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
Sangho Shin6ee19c62014-11-04 13:50:13 -0800148 this.portDescReplies = new ArrayList<OFPortDescStatsReply>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700149 factory13 = controller.getOFMessageFactory_13();
150 factory10 = controller.getOFMessageFactory_10();
151 controlRequested = Boolean.FALSE;
152 duplicateDpidFound = Boolean.FALSE;
153 }
154
Saurav Dasfcdad072014-08-13 14:15:21 -0700155 // *******************
156 // Role Handling
157 // *******************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700158
159 /**
160 * When we remove a pending role request we use this enum to indicate how we
161 * arrived at the decision. When we send a role request to the switch, we
Saurav Dasfcdad072014-08-13 14:15:21 -0700162 * also use this enum to indicate what we expect back from the switch, so
163 * the role changer can match the reply to our expectation.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700164 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700165 * @author gregor, saurav
166 */
167 public enum RoleRecvStatus {
Saurav Dasfcdad072014-08-13 14:15:21 -0700168 /**
169 * The switch returned an error indicating that roles are not supported
170 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700171 UNSUPPORTED,
172 /** The request timed out */
173 NO_REPLY,
174 /** The reply was old, there is a newer request pending */
175 OLD_REPLY,
176 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700177 * The reply's role matched the role that this controller set in the
178 * request message - invoked either initially at startup or to reassert
179 * current role
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700180 */
181 MATCHED_CURRENT_ROLE,
182 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700183 * The reply's role matched the role that this controller set in the
184 * request message - this is the result of a callback from the global
185 * registry, followed by a role request sent to the switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700186 */
187 MATCHED_SET_ROLE,
188 /**
189 * The reply's role was a response to the query made by this controller
190 */
191 REPLY_QUERY,
Saurav Dasfcdad072014-08-13 14:15:21 -0700192 /**
193 * We received a role reply message from the switch but the expectation
194 * was unclear, or there was no expectation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700195 */
196 OTHER_EXPECTATION,
197 }
198
199 /**
200 * Forwards to RoleChanger. See there.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700201 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700202 * @param role
203 */
204 public void sendRoleRequest(Role role, RoleRecvStatus expectation) {
205 try {
206 roleChanger.sendRoleRequest(role, expectation);
207 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700208 log.error("Disconnecting switch {} due to IO Error: {}",
209 getSwitchInfoString(), e.getMessage());
210 channel.close();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700211 }
212 }
213
214 // XXX S consider if necessary
215 public void disconnectSwitch() {
216 sw.disconnectSwitch();
217 }
218
219 /**
220 * A utility class to handle role requests and replies for this channel.
221 * After a role request is submitted the role changer keeps track of the
Saurav Dasfcdad072014-08-13 14:15:21 -0700222 * pending request, collects the reply (if any) and times out the request if
223 * necessary.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700224 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700225 * To simplify role handling we only keep track of the /last/ pending role
226 * reply send to the switch. If multiple requests are pending and we receive
227 * replies for earlier requests we ignore them. However, this way of
228 * handling pending requests implies that we could wait forever if a new
229 * request is submitted before the timeout triggers. If necessary we could
230 * work around that though.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700231 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700232 * @author gregor
233 * @author saurav (added support OF1.3 role messages, and expectations)
234 */
235 private class RoleChanger {
236 // indicates that a request is currently pending
237 // needs to be volatile to allow correct double-check idiom
238 private volatile boolean requestPending;
239 // the transaction Id of the pending request
240 private int pendingXid;
241 // the role that's pending
242 private Role pendingRole;
243 // system time in MS when we send the request
244 private long roleSubmitTime;
245 // the timeout to use
246 private final long roleTimeoutMs;
247 // the expectation set by the caller for the returned role
248 private RoleRecvStatus expectation;
249
250 public RoleChanger(long roleTimeoutMs) {
251 this.requestPending = false;
252 this.roleSubmitTime = 0;
253 this.pendingXid = -1;
254 this.pendingRole = null;
255 this.roleTimeoutMs = roleTimeoutMs;
256 this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
257 }
258
259 /**
260 * Send NX role request message to the switch requesting the specified
261 * role.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700262 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700263 * @param sw switch to send the role request message to
264 * @param role role to request
265 */
266 private int sendNxRoleRequest(Role role) throws IOException {
267 // Convert the role enum to the appropriate role to send
268 OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
269 switch (role) {
270 case MASTER:
271 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
272 break;
273 case SLAVE:
274 case EQUAL:
275 default:
276 // ensuring that the only two roles sent to 1.0 switches with
277 // Nicira role support, are MASTER and SLAVE
278 roleToSend = OFNiciraControllerRole.ROLE_SLAVE;
279 log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
280 }
281 int xid = sw.getNextTransactionId();
282 OFExperimenter roleRequest = factory10
283 .buildNiciraControllerRoleRequest()
284 .setXid(xid)
285 .setRole(roleToSend)
286 .build();
287 sw.write(Collections.<OFMessage>singletonList(roleRequest),
Saurav Dasfcdad072014-08-13 14:15:21 -0700288 new FloodlightContext());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700289 return xid;
290 }
291
292 private int sendOF13RoleRequest(Role role) throws IOException {
293 // Convert the role enum to the appropriate role to send
294 OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
295 switch (role) {
296 case EQUAL:
297 roleToSend = OFControllerRole.ROLE_EQUAL;
298 break;
299 case MASTER:
300 roleToSend = OFControllerRole.ROLE_MASTER;
301 break;
302 case SLAVE:
303 roleToSend = OFControllerRole.ROLE_SLAVE;
304 break;
305 default:
306 log.warn("Sending default role.noChange to switch {}."
307 + " Should only be used for queries.", sw);
308 }
309
310 int xid = sw.getNextTransactionId();
311 OFRoleRequest rrm = factory13
312 .buildRoleRequest()
313 .setRole(roleToSend)
314 .setXid(xid)
315 .setGenerationId(sw.getNextGenerationId())
316 .build();
317 sw.write(rrm, null);
318 return xid;
319 }
320
321 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700322 * Send a role request with the given role to the switch and update the
323 * pending request and timestamp. Sends an OFPT_ROLE_REQUEST to an OF1.3
324 * switch, OR Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured
325 * to support it in the IOFSwitch driver. If not supported, this method
326 * sends nothing and returns 'false'. The caller should take appropriate
327 * action.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700328 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700329 * One other optimization we do here is that for OF1.0 switches with
330 * Nicira role message support, we force the Role.EQUAL to become
Saurav Dasfcdad072014-08-13 14:15:21 -0700331 * Role.SLAVE, as there is no defined behavior for the Nicira role
332 * OTHER. We cannot expect it to behave like SLAVE. We don't have this
333 * problem with OF1.3 switches, because Role.EQUAL is well defined and
334 * we can simulate SLAVE behavior by using ASYNC messages.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700335 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700336 * @param role
337 * @throws IOException
Saurav Dasfcdad072014-08-13 14:15:21 -0700338 * @returns false if and only if the switch does not support
339 * role-request messages, according to the switch driver; true
340 * otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700341 */
342 synchronized boolean sendRoleRequest(Role role, RoleRecvStatus expectation)
343 throws IOException {
344 this.expectation = expectation;
345
346 if (ofVersion == OFVersion.OF_10) {
347 Boolean supportsNxRole = (Boolean)
348 sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE);
349 if (!supportsNxRole) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700350 log.debug("Switch driver indicates no support for Nicira "
351 + "role request messages. Not sending ...");
352 state.handleUnsentRoleMessage(OFChannelHandler.this, role,
353 expectation);
354 return false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700355 }
356 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
357 // make Role.EQUAL become Role.SLAVE
358 role = (role == Role.EQUAL) ? Role.SLAVE : role;
359 pendingXid = sendNxRoleRequest(role);
360 pendingRole = role;
361 roleSubmitTime = System.currentTimeMillis();
362 requestPending = true;
363 } else {
364 // OF1.3 switch, use OFPT_ROLE_REQUEST message
365 pendingXid = sendOF13RoleRequest(role);
366 pendingRole = role;
367 roleSubmitTime = System.currentTimeMillis();
368 requestPending = true;
369 }
370 return true;
371 }
372
373 /**
374 * Deliver a received role reply.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700375 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700376 * Check if a request is pending and if the received reply matches the
Saurav Dasfcdad072014-08-13 14:15:21 -0700377 * the expected pending reply (we check both role and xid) we set the
378 * role for the switch/channel.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700379 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700380 * If a request is pending but doesn't match the reply we ignore it, and
381 * return
Jonathan Hart6eda2302014-08-14 14:57:03 -0700382 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700383 * If no request is pending we disconnect with a SwitchStateException
Jonathan Hart6eda2302014-08-14 14:57:03 -0700384 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700385 * @param RoleReplyInfo information about role-reply in format that
Saurav Dasfcdad072014-08-13 14:15:21 -0700386 * controller can understand.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700387 * @throws SwitchStateException if no request is pending
388 */
389 synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
390 throws SwitchStateException {
391 if (!requestPending) {
392 Role currentRole = (sw != null) ? sw.getRole() : null;
393 if (currentRole != null) {
394 if (currentRole == rri.getRole()) {
395 // Don't disconnect if the role reply we received is
396 // for the same role we are already in.
397 log.debug("Received unexpected RoleReply from "
398 + "Switch: {} in State: {}. "
399 + "Role in reply is same as current role of this "
400 + "controller for this sw. Ignoring ...",
401 getSwitchInfoString(), state.toString());
402 return RoleRecvStatus.OTHER_EXPECTATION;
403 } else {
404 String msg = String.format("Switch: [%s], State: [%s], "
405 + "received unexpected RoleReply[%s]. "
406 + "No roles are pending, and this controller's "
407 + "current role:[%s] does not match reply. "
408 + "Disconnecting switch ... ",
409 OFChannelHandler.this.getSwitchInfoString(),
410 OFChannelHandler.this.state.toString(),
411 rri, currentRole);
412 throw new SwitchStateException(msg);
413 }
414 }
415 log.debug("Received unexpected RoleReply {} from "
416 + "Switch: {} in State: {}. "
417 + "This controller has no current role for this sw. "
418 + "Ignoring ...", new Object[] {rri,
Saurav Dasfcdad072014-08-13 14:15:21 -0700419 getSwitchInfoString(), state});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700420 return RoleRecvStatus.OTHER_EXPECTATION;
421 }
422
423 int xid = (int) rri.getXid();
424 Role role = rri.getRole();
Saurav Dasfcdad072014-08-13 14:15:21 -0700425 // XXX S should check generation id meaningfully and other cases of
426 // expectations
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700427 // U64 genId = rri.getGenId();
428
429 if (pendingXid != xid) {
430 log.debug("Received older role reply from " +
431 "switch {} ({}). Ignoring. " +
432 "Waiting for {}, xid={}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700433 new Object[] {getSwitchInfoString(), rri,
434 pendingRole, pendingXid});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700435 return RoleRecvStatus.OLD_REPLY;
436 }
437
438 if (pendingRole == role) {
Saurav Das6de23322014-08-07 18:51:19 -0700439 requestPending = false; // we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700440 log.debug("Received role reply message from {} that matched "
441 + "expected role-reply {} with expectations {}",
442 new Object[] {getSwitchInfoString(), role, expectation});
443 counters.roleReplyReceived.updateCounterWithFlush();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700444 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
445 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
446 return expectation;
447 } else {
448 return RoleRecvStatus.OTHER_EXPECTATION;
449 }
450 }
451
452 // if xids match but role's don't, perhaps its a query (OF1.3)
Saurav Das6de23322014-08-07 18:51:19 -0700453 if (expectation == RoleRecvStatus.REPLY_QUERY) {
454 requestPending = false; // again we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700455 return expectation;
Saurav Das6de23322014-08-07 18:51:19 -0700456 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700457
Saurav Das6de23322014-08-07 18:51:19 -0700458 // It is not clear what this role-reply was about, since it is not
459 // a query and it did not match the pendingRole. But since the xid's
460 // matched, we state that we received what we were waiting for, and
461 // let the caller handle it
462 requestPending = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700463 return RoleRecvStatus.OTHER_EXPECTATION;
464 }
465
466 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700467 * Called if we receive an error message. If the xid matches the pending
468 * request we handle it otherwise we ignore it.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700469 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700470 * Note: since we only keep the last pending request we might get error
471 * messages for earlier role requests that we won't be able to handle
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700472 */
473 synchronized RoleRecvStatus deliverError(OFErrorMsg error)
474 throws SwitchStateException {
475 if (!requestPending) {
476 log.debug("Received an error msg from sw {}, but no pending "
477 + "requests in role-changer; not handling ...",
478 getSwitchInfoString());
479 return RoleRecvStatus.OTHER_EXPECTATION;
480 }
481 if (pendingXid != error.getXid()) {
482 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
483 log.debug("Received an error msg from sw {} for a role request,"
484 + " but not for pending request in role-changer; "
485 + " ignoring error {} ...",
486 getSwitchInfoString(), error);
487 }
488 return RoleRecvStatus.OTHER_EXPECTATION;
489 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700490 // it is an error related to a currently pending role request
491 // message
492 requestPending = false; // we got a response, even though it is an
493 // error
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700494 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
495 counters.roleReplyErrorUnsupported.updateCounterWithFlush();
496 log.error("Received a error msg {} from sw {} in state {} for "
497 + "pending role request {}. Switch driver indicates "
498 + "role-messaging is supported. Possible issues in "
499 + "switch driver configuration?", new Object[] {
Saurav Dasfcdad072014-08-13 14:15:21 -0700500 ((OFBadRequestErrorMsg) error).toString(),
501 getSwitchInfoString(), state, pendingRole
502 });
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700503 return RoleRecvStatus.UNSUPPORTED;
504 }
505
506 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
507 OFRoleRequestFailedErrorMsg rrerr =
508 (OFRoleRequestFailedErrorMsg) error;
509 switch (rrerr.getCode()) {
510 case BAD_ROLE:
511 // switch says that current-role-req has bad role?
512 // for now we disconnect
513 // fall-thru
514 case STALE:
515 // switch says that current-role-req has stale gen-id?
516 // for now we disconnect
517 // fall-thru
518 case UNSUP:
519 // switch says that current-role-req has role that
520 // cannot be supported? for now we disconnect
521 String msgx = String.format("Switch: [%s], State: [%s], "
522 + "received Error to for pending role request [%s]. "
523 + "Error:[%s]. Disconnecting switch ... ",
524 OFChannelHandler.this.getSwitchInfoString(),
525 OFChannelHandler.this.state.toString(),
526 pendingRole, rrerr);
527 throw new SwitchStateException(msgx);
528 default:
529 break;
530 }
531 }
532
Saurav Dasfcdad072014-08-13 14:15:21 -0700533 // This error message was for a role request message but we dont
534 // know
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700535 // how to handle errors for nicira role request messages
536 return RoleRecvStatus.OTHER_EXPECTATION;
537 }
538
539 /**
540 * Check if a pending role request has timed out.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700541 *
Saurav Das6de23322014-08-07 18:51:19 -0700542 * @throws SwitchStateException
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700543 */
Saurav Das6de23322014-08-07 18:51:19 -0700544 void checkTimeout() throws SwitchStateException {
545 if (!requestPending) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700546 return;
Saurav Das6de23322014-08-07 18:51:19 -0700547 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700548 synchronized (this) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700549 if (!requestPending)
550 return;
551 long now = System.currentTimeMillis();
552 if (now - roleSubmitTime > roleTimeoutMs) {
553 // timeout triggered.
554 counters.roleReplyTimeout.updateCounterWithFlush();
Saurav Das6de23322014-08-07 18:51:19 -0700555 state.handleTimedOutRoleReply(OFChannelHandler.this, pendingRole);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700556 }
557 }
558 }
559
560 }
561
Saurav Dasfcdad072014-08-13 14:15:21 -0700562 // *************************
563 // Channel State Machine
564 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700565
566 /**
567 * The state machine for handling the switch/channel state. All state
Saurav Dasfcdad072014-08-13 14:15:21 -0700568 * transitions should happen from within the state machine (and not from
569 * other parts of the code)
Jonathan Hart6eda2302014-08-14 14:57:03 -0700570 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700571 * @author gregor
Saurav Dasfcdad072014-08-13 14:15:21 -0700572 * @author saurav (modified to handle 1.0 & 1.3 switches, EQUAL state,
573 * role-handling )
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700574 */
575 enum ChannelState {
576 /**
577 * Initial state before channel is connected.
578 */
579 INIT(false) {
580 @Override
581 void processOFMessage(OFChannelHandler h, OFMessage m)
582 throws IOException, SwitchStateException {
583 illegalMessageReceived(h, m);
584 }
585
586 @Override
587 void processOFError(OFChannelHandler h, OFErrorMsg m)
588 throws IOException {
589 // need to implement since its abstract but it will never
590 // be called
591 }
592
593 @Override
594 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
595 throws IOException {
596 unhandledMessageReceived(h, m);
597 }
598 },
599
600 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700601 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the
602 * switch. Once we receive the reply, we decide on OF 1.3 or 1.0 switch
603 * - no other protocol version is accepted. We send an OFFeaturesRequest
604 * depending on the protocol version selected Next state is
605 * WAIT_FEATURES_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700606 */
607 WAIT_HELLO(false) {
608 @Override
609 void processOFHello(OFChannelHandler h, OFHello m)
610 throws IOException {
611 // TODO We could check for the optional bitmap, but for now
612 // we are just checking the version number.
613 if (m.getVersion() == OFVersion.OF_13) {
614 log.info("Received {} Hello from {}", m.getVersion(),
615 h.channel.getRemoteAddress());
616 h.ofVersion = OFVersion.OF_13;
617 } else if (m.getVersion() == OFVersion.OF_10) {
618 log.info("Received {} Hello from {} - switching to OF "
619 + "version 1.0", m.getVersion(),
620 h.channel.getRemoteAddress());
621 h.ofVersion = OFVersion.OF_10;
622 } else {
623 log.error("Received Hello of version {} from switch at {}. "
624 + "This controller works with OF1.0 and OF1.3 "
625 + "switches. Disconnecting switch ...",
626 m.getVersion(), h.channel.getRemoteAddress());
627 h.channel.disconnect();
628 return;
629 }
630 h.sendHandshakeFeaturesRequestMessage();
631 h.setState(WAIT_FEATURES_REPLY);
632 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700633
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700634 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700635 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700636 throws IOException, SwitchStateException {
637 illegalMessageReceived(h, m);
638 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700639
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700640 @Override
641 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700642 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700643 throws IOException, SwitchStateException {
644 illegalMessageReceived(h, m);
645 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700646
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700647 @Override
648 void processOFError(OFChannelHandler h, OFErrorMsg m) {
649 logErrorDisconnect(h, m);
650 }
651
652 @Override
653 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
654 throws IOException {
655 unhandledMessageReceived(h, m);
656 }
657 },
658
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700659 /**
660 * We are waiting for a features reply message. Once we receive it, the
Saurav Dasfcdad072014-08-13 14:15:21 -0700661 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0, we
662 * send a SetConfig request, barrier, and GetConfig request and the next
663 * state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700664 * request and the next state is WAIT_PORT_DESC_REPLY.
665 */
666 WAIT_FEATURES_REPLY(false) {
667 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700668 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700669 throws IOException {
670 h.thisdpid = m.getDatapathId().getLong();
671 log.info("Received features reply for switch at {} with dpid {}",
672 h.getSwitchInfoString(), h.thisdpid);
Saurav Dasfcdad072014-08-13 14:15:21 -0700673 // update the controller about this connected switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700674 boolean success = h.controller.addConnectedSwitch(
675 h.thisdpid, h);
676 if (!success) {
677 disconnectDuplicate(h);
678 return;
679 }
680
Saurav Dasfcdad072014-08-13 14:15:21 -0700681 h.featuresReply = m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700682 if (h.ofVersion == OFVersion.OF_10) {
683 h.sendHandshakeSetConfig();
684 h.setState(WAIT_CONFIG_REPLY);
685 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -0700686 // version is 1.3, must get switchport information
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700687 h.sendHandshakeOFPortDescRequest();
688 h.setState(WAIT_PORT_DESC_REPLY);
689 }
690 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700691
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700692 @Override
693 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700694 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700695 throws IOException, SwitchStateException {
696 illegalMessageReceived(h, m);
697 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700698
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700699 @Override
700 void processOFError(OFChannelHandler h, OFErrorMsg m) {
701 logErrorDisconnect(h, m);
702 }
703
704 @Override
705 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
706 throws IOException {
707 unhandledMessageReceived(h, m);
708 }
709 },
710
711 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700712 * We are waiting for a description of the 1.3 switch ports. Once
713 * received, we send a SetConfig request Next State is WAIT_CONFIG_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700714 */
715 WAIT_PORT_DESC_REPLY(false) {
716
717 @Override
718 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
719 throws SwitchStateException {
720 // Read port description
721 if (m.getStatsType() != OFStatsType.PORT_DESC) {
722 log.warn("Expecting port description stats but received stats "
723 + "type {} from {}. Ignoring ...", m.getStatsType(),
724 h.channel.getRemoteAddress());
725 return;
726 }
Sangho Shin6ee19c62014-11-04 13:50:13 -0800727 if (h.portDescReplies.isEmpty()) {
728 //h.portDescReplies = new ArrayList<OFPortDescStatsReply>();
Sangho Shinda7635e2014-11-03 16:22:11 -0800729 h.portDescReplies.add((OFPortDescStatsReply) m);
730 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
731 log.warn("Stats reply indicates more stats from sw {} for "
732 + "port description - not currently handled",
733 h.getSwitchInfoString());
734 return;
735 }
736
737 }
738 else if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700739 log.warn("Stats reply indicates more stats from sw {} for "
740 + "port description - not currently handled",
741 h.getSwitchInfoString());
Sangho Shinda7635e2014-11-03 16:22:11 -0800742 h.portDescReplies.add((OFPortDescStatsReply)m);
743 return;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700744 }
Sangho Shinda7635e2014-11-03 16:22:11 -0800745 else {
746 h.portDescReplies.add((OFPortDescStatsReply)m);
747 }
748 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700749 log.info("Received port desc reply for switch at {}",
750 h.getSwitchInfoString());
751 try {
752 h.sendHandshakeSetConfig();
753 } catch (IOException e) {
754 log.error("Unable to send setConfig after PortDescReply. "
755 + "Error: {}", e.getMessage());
756 }
757 h.setState(WAIT_CONFIG_REPLY);
758 }
759
760 @Override
761 void processOFError(OFChannelHandler h, OFErrorMsg m)
762 throws IOException, SwitchStateException {
763 logErrorDisconnect(h, m);
764
765 }
766
767 @Override
768 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
769 throws IOException, SwitchStateException {
770 unhandledMessageReceived(h, m);
771
772 }
773 },
774
775 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700776 * We are waiting for a config reply message. Once we receive it we send
777 * a DescriptionStatsRequest to the switch. Next state:
778 * WAIT_DESCRIPTION_STAT_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700779 */
780 WAIT_CONFIG_REPLY(false) {
781 @Override
782 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -0700783 @LogMessageDoc(level = "WARN",
784 message = "Config Reply from {switch} has " +
785 "miss length set to {length}",
786 explanation = "The controller requires that the switch " +
787 "use a miss length of 0xffff for correct " +
788 "function",
789 recommendation = "Use a different switch to ensure " +
790 "correct function")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700791 })
792 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
793 throws IOException {
794 if (m.getMissSendLen() == 0xffff) {
795 log.trace("Config Reply from switch {} confirms "
796 + "miss length set to 0xffff",
797 h.getSwitchInfoString());
798 } else {
799 // FIXME: we can't really deal with switches that don't send
800 // full packets. Shouldn't we drop the connection here?
801 log.warn("Config Reply from switch {} has"
802 + "miss length set to {}",
803 h.getSwitchInfoString(),
804 m.getMissSendLen());
805 }
806 h.sendHandshakeDescriptionStatsRequest();
807 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
808 }
809
810 @Override
811 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
812 // do nothing;
813 }
814
815 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700816 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700817 throws IOException, SwitchStateException {
818 illegalMessageReceived(h, m);
819 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700820
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700821 @Override
822 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700823 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700824 throws IOException, SwitchStateException {
825 log.error("Received multipart(stats) message sub-type {}",
826 m.getStatsType());
827 illegalMessageReceived(h, m);
828 }
829
830 @Override
831 void processOFError(OFChannelHandler h, OFErrorMsg m) {
832 logErrorDisconnect(h, m);
833 }
834
835 @Override
836 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
837 throws IOException {
838 h.pendingPortStatusMsg.add(m);
839 }
840 },
841
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700842 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700843 * We are waiting for a OFDescriptionStat message from the switch. Once
844 * we receive any stat message we try to parse it. If it's not a
845 * description stats message we disconnect. If its the expected
846 * description stats message, we: - use the switch driver to bind the
847 * switch and get an IOFSwitch instance - setup the IOFSwitch instance -
848 * add switch to FloodlightProvider(Controller) and send the initial
849 * role request to the switch. Next state: WAIT_INITIAL_ROLE In the
850 * typical case, where switches support role request messages the next
851 * state is where we expect the role reply message. In the special case
852 * that where the switch does not support any kind of role request
853 * messages, we don't send a role message, but we do request mastership
854 * from the registry service. This controller should become master once
855 * we hear back from the registry service. All following states will
856 * have a h.sw instance!
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700857 */
858 WAIT_DESCRIPTION_STAT_REPLY(false) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700859 @LogMessageDoc(message = "Switch {switch info} bound to class " +
860 "{switch driver}, description {switch description}",
861 explanation = "The specified switch has been bound to " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700862 "a switch driver based on the switch description" +
863 "received from the switch")
864 @Override
865 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
866 throws SwitchStateException {
867 // Read description, if it has been updated
868 if (m.getStatsType() != OFStatsType.DESC) {
869 log.warn("Expecting Description stats but received stats "
870 + "type {} from {}. Ignoring ...", m.getStatsType(),
871 h.channel.getRemoteAddress());
872 return;
873 }
874 log.info("Received switch description reply from switch at {}",
875 h.channel.getRemoteAddress());
876 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasfcdad072014-08-13 14:15:21 -0700877 // Here is where we differentiate between different kinds of
878 // switches
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700879 h.sw = h.controller.getOFSwitchInstance(drep, h.ofVersion);
880 // set switch information
881 h.sw.setOFVersion(h.ofVersion);
882 ((OFSwitchImplBase) h.sw).setFeaturesReply(h.featuresReply);
Sangho Shinda7635e2014-11-03 16:22:11 -0800883 ((OFSwitchImplBase) h.sw).setPortDescReplies(h.portDescReplies);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700884 h.sw.setConnected(true);
885 h.sw.setChannel(h.channel);
886 h.sw.setFloodlightProvider(h.controller);
887 h.sw.setThreadPoolService(h.controller.getThreadPoolService());
888 try {
889 h.sw.setDebugCounterService(h.controller.getDebugCounter());
890 } catch (CounterException e) {
891 h.counters.switchCounterRegistrationFailed
892 .updateCounterNoFlush();
893 log.warn("Could not register counters for switch {} ",
Saurav Dasfcdad072014-08-13 14:15:21 -0700894 h.getSwitchInfoString(), e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700895 }
896
897 log.info("Switch {} bound to class {}, description {}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700898 new Object[] {h.sw, h.sw.getClass(), drep});
899 // Put switch in EQUAL mode until we hear back from the global
900 // registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700901 log.debug("Setting new switch {} to EQUAL and sending Role request",
902 h.sw.getStringId());
903 h.setSwitchRole(Role.EQUAL);
904 try {
905 boolean supportsRRMsg = h.roleChanger.sendRoleRequest(Role.EQUAL,
906 RoleRecvStatus.MATCHED_CURRENT_ROLE);
907 if (!supportsRRMsg) {
908 log.warn("Switch {} does not support role request messages "
909 + "of any kind. No role messages were sent. "
910 + "This controller instance SHOULD become MASTER "
911 + "from the registry process. ",
912 h.getSwitchInfoString());
913 }
914 h.setState(WAIT_INITIAL_ROLE);
915 // request control of switch from global registry -
916 // necessary even if this is the only controller the
917 // switch is connected to.
918 h.controller.submitRegistryRequest(h.sw.getId());
919 } catch (IOException e) {
920 log.error("Exception when sending role request: {} ",
921 e.getMessage());
922 // FIXME shouldn't we disconnect?
923 }
924 }
925
926 @Override
927 void processOFError(OFChannelHandler h, OFErrorMsg m) {
928 logErrorDisconnect(h, m);
929 }
930
931 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700932 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700933 throws IOException, SwitchStateException {
934 illegalMessageReceived(h, m);
935 }
936
937 @Override
938 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
939 throws IOException {
940 h.pendingPortStatusMsg.add(m);
941 }
942 },
943
944 /**
945 * We are waiting for a role reply message in response to a role request
946 * sent after hearing back from the registry service -- OR -- we are
947 * just waiting to hear back from the registry service in the case that
948 * the switch does not support role messages. If completed successfully,
Saurav Dasfcdad072014-08-13 14:15:21 -0700949 * the controller's role for this switch will be set here. Before we
950 * move to the state corresponding to the role, we allow the switch
951 * specific driver to complete its configuration. This configuration
952 * typically depends on the role the controller is playing for this
953 * switch. And so we set the switch role (for 'this' controller) before
954 * we start the driver-sub-handshake. Next State:
955 * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700956 */
957 WAIT_INITIAL_ROLE(false) {
958 @Override
959 void processOFError(OFChannelHandler h, OFErrorMsg m)
960 throws SwitchStateException {
961 // role changer will ignore the error if it isn't for it
962 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
963 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
964 logError(h, m);
965 }
966 }
967
968 @Override
969 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
970 throws IOException, SwitchStateException {
971 Role role = extractNiciraRoleReply(h, m);
972 // If role == null it means the vendor (experimenter) message
973 // wasn't really a Nicira role reply. We ignore this case.
974 if (role != null) {
975 RoleReplyInfo rri = new RoleReplyInfo(role, null, m.getXid());
976 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
977 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
978 setRoleAndStartDriverHandshake(h, rri.getRole());
979 } // else do nothing - wait for the correct expected reply
980 } else {
981 unhandledMessageReceived(h, m);
982 }
983 }
984
985 @Override
986 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
987 throws SwitchStateException, IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -0700988 RoleReplyInfo rri = extractOFRoleReply(h, m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700989 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
990 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
991 setRoleAndStartDriverHandshake(h, rri.getRole());
992 } // else do nothing - wait for the correct expected reply
993 }
994
995 @Override
996 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -0700997 RoleRecvStatus expectation) throws IOException {
998 // typically this is triggered for a switch where role messages
999 // are not supported - we confirm that the role being set is
1000 // master and move to the next state
1001 if (expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
1002 if (role == Role.MASTER) {
1003 setRoleAndStartDriverHandshake(h, role);
1004 } else {
1005 log.error("Expected MASTER role from registry for switch "
1006 + "which has no support for role-messages."
1007 + "Received {}. It is possible that this switch "
1008 + "is connected to other controllers, in which "
1009 + "case it should support role messages - not "
1010 + "moving forward.", role);
1011 }
1012 } // else do nothing - wait to hear back from registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001013
1014 }
1015
1016 private void setRoleAndStartDriverHandshake(OFChannelHandler h,
1017 Role role) throws IOException {
1018 h.setSwitchRole(role);
1019 h.sw.startDriverHandshake();
1020 if (h.sw.isDriverHandshakeComplete()) {
1021 Role mySwitchRole = h.sw.getRole();
1022 if (mySwitchRole == Role.MASTER) {
1023 log.info("Switch-driver sub-handshake complete. "
1024 + "Activating switch {} with Role: MASTER",
1025 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001026 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001027 boolean success = h.controller.addActivatedMasterSwitch(
1028 h.sw.getId(), h.sw);
1029 if (!success) {
1030 disconnectDuplicate(h);
1031 return;
1032 }
1033 h.setState(MASTER);
1034 } else {
1035 log.info("Switch-driver sub-handshake complete. "
1036 + "Activating switch {} with Role: EQUAL",
1037 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001038 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001039 boolean success = h.controller.addActivatedEqualSwitch(
1040 h.sw.getId(), h.sw);
1041 if (!success) {
1042 disconnectDuplicate(h);
1043 return;
1044 }
1045 h.setState(EQUAL);
1046 }
1047 } else {
1048 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
1049 }
1050 }
1051
1052 @Override
Saurav Das85399942014-09-02 16:11:48 -07001053 public void handleTimedOutHandshake(OFChannelHandler h,
1054 ChannelHandlerContext ctx) throws IOException {
1055 log.info("Handshake timed out waiting to hear back from registry "
1056 + "service. Moving to Role EQUAL for switch {}",
1057 h.getSwitchInfoString());
1058 setRoleAndStartDriverHandshake(h, Role.EQUAL);
1059 }
1060
1061 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001062 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001063 throws IOException, SwitchStateException {
1064 illegalMessageReceived(h, m);
1065 }
1066
1067 @Override
1068 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1069 throws SwitchStateException {
1070 illegalMessageReceived(h, m);
1071 }
1072
1073 @Override
1074 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1075 throws IOException, SwitchStateException {
1076 h.pendingPortStatusMsg.add(m);
1077
1078 }
1079 },
1080
1081 /**
1082 * We are waiting for the respective switch driver to complete its
Saurav Dasfcdad072014-08-13 14:15:21 -07001083 * configuration. Notice that we do not consider this to be part of the
1084 * main switch-controller handshake. But we do consider it as a step
1085 * that comes before we declare the switch as available to the
1086 * controller. Next State: depends on the role of this controller for
1087 * this switch - either MASTER or EQUAL.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001088 */
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001089 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(false) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001090
1091 @Override
1092 void processOFError(OFChannelHandler h, OFErrorMsg m)
1093 throws IOException {
1094 // will never be called. We override processOFMessage
1095 }
1096
1097 @Override
1098 void processOFMessage(OFChannelHandler h, OFMessage m)
1099 throws IOException {
1100 if (m.getType() == OFType.ECHO_REQUEST)
Saurav Dasfcdad072014-08-13 14:15:21 -07001101 processOFEchoRequest(h, (OFEchoRequest) m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001102 else {
1103 // FIXME: other message to handle here?
1104 h.sw.processDriverHandshakeMessage(m);
1105 if (h.sw.isDriverHandshakeComplete()) {
1106 // consult the h.sw role and goto that state
1107 Role mySwitchRole = h.sw.getRole();
1108 if (mySwitchRole == Role.MASTER) {
1109 log.info("Switch-driver sub-handshake complete. "
1110 + "Activating switch {} with Role: MASTER",
1111 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001112 handlePendingPortStatusMessages(h); // before
1113 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001114 boolean success = h.controller.addActivatedMasterSwitch(
1115 h.sw.getId(), h.sw);
1116 if (!success) {
1117 disconnectDuplicate(h);
1118 return;
1119 }
1120 h.setState(MASTER);
1121 } else {
1122 log.info("Switch-driver sub-handshake complete. "
1123 + "Activating switch {} with Role: EQUAL",
1124 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001125 handlePendingPortStatusMessages(h); // before
1126 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001127 boolean success = h.controller.addActivatedEqualSwitch(
1128 h.sw.getId(), h.sw);
1129 if (!success) {
1130 disconnectDuplicate(h);
1131 return;
1132 }
1133 h.setState(EQUAL);
1134 }
1135 }
1136 }
1137 }
1138
1139 @Override
1140 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1141 throws IOException, SwitchStateException {
1142 h.pendingPortStatusMsg.add(m);
1143 }
1144 },
1145
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001146 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001147 * This controller is in MASTER role for this switch. We enter this
1148 * state after requesting and winning control from the global registry.
1149 * The main handshake as well as the switch-driver sub-handshake is
1150 * complete at this point. // XXX S reconsider below In the (near)
1151 * future we may deterministically assign controllers to switches at
1152 * startup. We only leave this state if the switch disconnects or if we
1153 * send a role request for SLAVE /and/ receive the role reply for SLAVE.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001154 */
1155 MASTER(true) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001156 @LogMessageDoc(level = "WARN",
1157 message = "Received permission error from switch {} while" +
1158 "being master. Reasserting master role.",
1159 explanation = "The switch has denied an operation likely " +
1160 "indicating inconsistent controller roles",
1161 recommendation = "This situation can occurs transiently during role" +
1162 " changes. If, however, the condition persists or happens" +
1163 " frequently this indicates a role inconsistency. " +
1164 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001165 @Override
1166 void processOFError(OFChannelHandler h, OFErrorMsg m)
1167 throws IOException, SwitchStateException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001168 // first check if the error msg is in response to a role-request
1169 // message
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001170 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1171 if (rrstatus != RoleRecvStatus.OTHER_EXPECTATION) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001172 // rolechanger has handled the error message - we are done
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001173 return;
1174 }
1175
1176 // if we get here, then the error message is for something else
1177 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
1178 ((OFBadRequestErrorMsg) m).getCode() ==
Saurav Dasfcdad072014-08-13 14:15:21 -07001179 OFBadRequestCode.EPERM) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001180 // We are the master controller and the switch returned
1181 // a permission error. This is a likely indicator that
1182 // the switch thinks we are slave. Reassert our
1183 // role
1184 // FIXME: this could be really bad during role transitions
1185 // if two controllers are master (even if its only for
1186 // a brief period). We might need to see if these errors
1187 // persist before we reassert
1188 h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush();
1189 log.warn("Received permission error from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001190 "being master. Reasserting master role.",
1191 h.getSwitchInfoString());
1192 // h.controller.reassertRole(h, Role.MASTER);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001193 // XXX S reassert in role changer or reconsider if all this
1194 // stuff is really needed
1195 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
Saurav Dasfcdad072014-08-13 14:15:21 -07001196 ((OFFlowModFailedErrorMsg) m).getCode() ==
1197 OFFlowModFailedCode.ALL_TABLES_FULL) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001198 h.sw.setTableFull(true);
1199 } else {
1200 logError(h, m);
1201 }
1202 h.dispatchMessage(m);
1203 }
1204
1205 @Override
1206 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001207 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001208 h.sw.deliverStatisticsReply(m);
1209 }
1210
1211 @Override
1212 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1213 throws IOException, SwitchStateException {
1214 Role role = extractNiciraRoleReply(h, m);
1215 if (role == null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001216 // The message wasn't really a Nicira role reply. We just
1217 // dispatch it to the OFMessage listeners in this case.
1218 h.dispatchMessage(m);
1219 return;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001220 }
1221
1222 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Saurav Dasfcdad072014-08-13 14:15:21 -07001223 new RoleReplyInfo(role, null, m.getXid()));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001224 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001225 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001226 }
1227 }
1228
1229 @Override
1230 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1231 throws SwitchStateException, IOException {
1232 RoleReplyInfo rri = extractOFRoleReply(h, m);
1233 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1234 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001235 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001236 }
1237 }
1238
1239 @Override
1240 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1241 throws IOException, SwitchStateException {
1242 handlePortStatusMessage(h, m, true);
1243 h.dispatchMessage(m);
1244 }
1245
1246 @Override
1247 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1248 throws IOException {
1249 h.dispatchMessage(m);
1250 }
1251
1252 @Override
1253 void processOFFlowRemoved(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001254 OFFlowRemoved m) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001255 h.dispatchMessage(m);
1256 }
1257
1258 @Override
1259 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1260 throws IOException {
Saurav Dasd84178f2014-09-29 17:48:54 -07001261 h.sw.deliverBarrierReply(m);
Saurav Dasfcdad072014-08-13 14:15:21 -07001262 h.dispatchMessage(m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001263 }
1264
1265 },
1266
1267 /**
1268 * This controller is in EQUAL role for this switch. We enter this state
1269 * after some /other/ controller instance wins mastership-role over this
1270 * switch. The EQUAL role can be considered the same as the SLAVE role
1271 * if this controller does NOT send commands or packets to the switch.
1272 * This should always be true for OF1.0 switches. XXX S need to enforce.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001273 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001274 * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE,
Saurav Dasfcdad072014-08-13 14:15:21 -07001275 * gives us the flexibility that if an app wants to send
1276 * commands/packets to switches, it can, even thought it is running on a
1277 * controller instance that is not in a MASTER role for this switch. Of
1278 * course, it is the job of the app to ensure that commands/packets sent
1279 * by this (EQUAL) controller instance does not clash/conflict with
1280 * commands/packets sent by the MASTER controller for this switch.
1281 * Neither the controller instances, nor the switch provides any kind of
1282 * resolution mechanism should conflicts occur.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001283 */
1284 EQUAL(true) {
1285 @Override
1286 void processOFError(OFChannelHandler h, OFErrorMsg m)
1287 throws IOException, SwitchStateException {
1288 // role changer will ignore the error if it isn't for it
1289 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1290 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
1291 logError(h, m);
1292 h.dispatchMessage(m);
1293 }
1294 }
1295
1296 @Override
1297 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001298 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001299 h.sw.deliverStatisticsReply(m);
1300 }
1301
1302 @Override
1303 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1304 throws IOException, SwitchStateException {
1305 Role role = extractNiciraRoleReply(h, m);
1306 // If role == null it means the message wasn't really a
1307 // Nicira role reply. We ignore it in this state.
1308 if (role != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001309 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001310 new RoleReplyInfo(role, null, m.getXid()));
Saurav Dasfcdad072014-08-13 14:15:21 -07001311 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1312 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001313 }
1314 } else {
1315 unhandledMessageReceived(h, m);
1316 }
1317 }
1318
1319 @Override
1320 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1321 throws SwitchStateException, IOException {
1322 RoleReplyInfo rri = extractOFRoleReply(h, m);
1323 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1324 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001325 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001326 }
1327 }
1328
1329 // XXX S needs more handlers for 1.3 switches in equal role
1330
1331 @Override
1332 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1333 throws IOException, SwitchStateException {
1334 handlePortStatusMessage(h, m, true);
1335 }
1336
1337 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001338 @LogMessageDoc(level = "WARN",
1339 message = "Received PacketIn from switch {} while" +
1340 "being slave. Reasserting slave role.",
1341 explanation = "The switch has receive a PacketIn despite being " +
1342 "in slave role indicating inconsistent controller roles",
1343 recommendation = "This situation can occurs transiently during role" +
1344 " changes. If, however, the condition persists or happens" +
1345 " frequently this indicates a role inconsistency. " +
1346 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001347 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException {
1348 // we don't expect packetIn while slave, reassert we are slave
1349 h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush();
1350 log.warn("Received PacketIn from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001351 "being slave. Reasserting slave role.", h.sw);
1352 // h.controller.reassertRole(h, Role.SLAVE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001353 // XXX reassert in role changer
1354 }
1355 };
1356
1357 private final boolean handshakeComplete;
Saurav Dasfcdad072014-08-13 14:15:21 -07001358
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001359 ChannelState(boolean handshakeComplete) {
1360 this.handshakeComplete = handshakeComplete;
1361 }
1362
1363 /**
1364 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07001365 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001366 * @return true if the handshake is complete
1367 */
1368 public boolean isHandshakeComplete() {
1369 return handshakeComplete;
1370 }
1371
1372 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001373 * Get a string specifying the switch connection, state, and message
1374 * received. To be used as message for SwitchStateException or log
1375 * messages
Jonathan Hart6eda2302014-08-14 14:57:03 -07001376 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001377 * @param h The channel handler (to get switch information_
1378 * @param m The OFMessage that has just been received
Saurav Dasfcdad072014-08-13 14:15:21 -07001379 * @param details A string giving more details about the exact nature of
1380 * the problem.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001381 * @return
1382 */
1383 // needs to be protected because enum members are actually subclasses
1384 protected String getSwitchStateMessage(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001385 OFMessage m,
1386 String details) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001387 return String.format("Switch: [%s], State: [%s], received: [%s]"
Saurav Dasfcdad072014-08-13 14:15:21 -07001388 + ", details: %s",
1389 h.getSwitchInfoString(),
1390 this.toString(),
1391 m.getType().toString(),
1392 details);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001393 }
1394
1395 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001396 * We have an OFMessage we didn't expect given the current state and we
1397 * want to treat this as an error. We currently throw an exception that
1398 * will terminate the connection However, we could be more forgiving
Jonathan Hart6eda2302014-08-14 14:57:03 -07001399 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001400 * @param h the channel handler that received the message
1401 * @param m the message
1402 * @throws SwitchStateException
1403 * @throws SwitchStateExeption we always through the execption
1404 */
1405 // needs to be protected because enum members are acutally subclasses
1406 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
1407 throws SwitchStateException {
1408 String msg = getSwitchStateMessage(h, m,
1409 "Switch should never send this message in the current state");
1410 throw new SwitchStateException(msg);
1411
1412 }
1413
1414 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001415 * We have an OFMessage we didn't expect given the current state and we
1416 * want to ignore the message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001417 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001418 * @param h the channel handler the received the message
1419 * @param m the message
1420 */
1421 protected void unhandledMessageReceived(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001422 OFMessage m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001423 h.counters.unhandledMessage.updateCounterNoFlush();
1424 if (log.isDebugEnabled()) {
1425 String msg = getSwitchStateMessage(h, m,
1426 "Ignoring unexpected message");
1427 log.debug(msg);
1428 }
1429 }
1430
1431 /**
1432 * Log an OpenFlow error message from a switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001433 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001434 * @param sw The switch that sent the error
1435 * @param error The error message
1436 */
Saurav Dasfcdad072014-08-13 14:15:21 -07001437 @LogMessageDoc(level = "ERROR",
1438 message = "Error {error type} {error code} from {switch} " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001439 "in state {state}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001440 explanation = "The switch responded with an unexpected error" +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001441 "to an OpenFlow message from the controller",
Saurav Dasfcdad072014-08-13 14:15:21 -07001442 recommendation = "This could indicate improper network operation. " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001443 "If the problem persists restarting the switch and " +
1444 "controller may help."
1445 )
Saurav Dasfcdad072014-08-13 14:15:21 -07001446 protected void logError(OFChannelHandler h, OFErrorMsg error) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001447 log.error("{} from switch {} in state {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001448 new Object[] {
1449 error,
1450 h.getSwitchInfoString(),
1451 this.toString()});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001452 }
1453
1454 /**
1455 * Log an OpenFlow error message from a switch and disconnect the
1456 * channel
Jonathan Hart6eda2302014-08-14 14:57:03 -07001457 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001458 * @param sw The switch that sent the error
1459 * @param error The error message
1460 */
1461 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
1462 logError(h, error);
1463 h.channel.disconnect();
1464 }
1465
1466 /**
1467 * log an error message for a duplicate dpid and disconnect this channel
1468 */
1469 protected void disconnectDuplicate(OFChannelHandler h) {
1470 log.error("Duplicated dpid or incompleted cleanup - "
1471 + "disconnecting channel {}", h.getSwitchInfoString());
1472 h.duplicateDpidFound = Boolean.TRUE;
1473 h.channel.disconnect();
1474 }
1475
1476 /**
1477 * Extract the role from an OFVendor message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001478 *
Saurav Dasfcdad072014-08-13 14:15:21 -07001479 * Extract the role from an OFVendor message if the message is a Nicira
1480 * role reply. Otherwise return null.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001481 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001482 * @param h The channel handler receiving the message
1483 * @param vendorMessage The vendor message to parse.
1484 * @return The role in the message if the message is a Nicira role
Saurav Dasfcdad072014-08-13 14:15:21 -07001485 * reply, null otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001486 * @throws SwitchStateException If the message is a Nicira role reply
Saurav Dasfcdad072014-08-13 14:15:21 -07001487 * but the numeric role value is unknown.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001488 */
1489 protected Role extractNiciraRoleReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001490 OFExperimenter experimenterMsg) throws SwitchStateException {
1491 int vendor = (int) experimenterMsg.getExperimenter();
1492 if (vendor != 0x2320) // magic number representing nicira
1493 return null;
1494 OFNiciraControllerRoleReply nrr =
1495 (OFNiciraControllerRoleReply) experimenterMsg;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001496
Saurav Dasfcdad072014-08-13 14:15:21 -07001497 Role role = null;
1498 OFNiciraControllerRole ncr = nrr.getRole();
1499 switch (ncr) {
1500 case ROLE_MASTER:
1501 role = Role.MASTER;
1502 break;
1503 case ROLE_OTHER:
1504 role = Role.EQUAL;
1505 break;
1506 case ROLE_SLAVE:
1507 role = Role.SLAVE;
1508 break;
1509 default: // handled below
1510 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001511
Saurav Dasfcdad072014-08-13 14:15:21 -07001512 if (role == null) {
1513 String msg = String.format("Switch: [%s], State: [%s], "
1514 + "received NX_ROLE_REPLY with invalid role "
1515 + "value %d",
1516 h.getSwitchInfoString(),
1517 this.toString(),
1518 nrr.getRole());
1519 throw new SwitchStateException(msg);
1520 }
1521 return role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001522 }
1523
1524 /**
1525 * Helper class returns role reply information in the format understood
1526 * by the controller.
1527 */
1528 protected class RoleReplyInfo {
1529 private Role role;
1530 private U64 genId;
1531 private long xid;
1532
Saurav Dasfcdad072014-08-13 14:15:21 -07001533 RoleReplyInfo(Role role, U64 genId, long xid) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001534 this.role = role;
1535 this.genId = genId;
1536 this.xid = xid;
1537 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001538
1539 public Role getRole() {
1540 return role;
1541 }
1542
1543 public U64 getGenId() {
1544 return genId;
1545 }
1546
1547 public long getXid() {
1548 return xid;
1549 }
1550
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001551 @Override
1552 public String toString() {
1553 return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
1554 }
1555 }
1556
1557 /**
1558 * Extract the role information from an OF1.3 Role Reply Message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001559 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001560 * @param h
1561 * @param rrmsg
1562 * @return RoleReplyInfo object
1563 * @throws SwitchStateException
1564 */
1565 protected RoleReplyInfo extractOFRoleReply(OFChannelHandler h,
1566 OFRoleReply rrmsg) throws SwitchStateException {
1567 OFControllerRole cr = rrmsg.getRole();
1568 Role role = null;
Saurav Dasfcdad072014-08-13 14:15:21 -07001569 switch (cr) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001570 case ROLE_EQUAL:
1571 role = Role.EQUAL;
1572 break;
1573 case ROLE_MASTER:
1574 role = Role.MASTER;
1575 break;
1576 case ROLE_SLAVE:
1577 role = Role.SLAVE;
1578 break;
1579 case ROLE_NOCHANGE: // switch should send current role
1580 default:
1581 String msg = String.format("Unknown controller role {} "
1582 + "received from switch {}", cr, h.sw);
1583 throw new SwitchStateException(msg);
1584 }
1585
1586 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
1587 }
1588
1589 /**
1590 * Handles all pending port status messages before a switch is declared
1591 * activated in MASTER or EQUAL role. Note that since this handling
Saurav Dasfcdad072014-08-13 14:15:21 -07001592 * precedes the activation (and therefore notification to
1593 * IOFSwitchListerners) the changes to ports will already be visible
1594 * once the switch is activated. As a result, no notifications are sent
1595 * out for these pending portStatus messages.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001596 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001597 * @param h
1598 * @throws SwitchStateException
1599 */
1600 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
1601 try {
1602 handlePendingPortStatusMessages(h, 0);
1603 } catch (SwitchStateException e) {
1604 // do nothing - exception msg printed
1605 }
1606 }
1607
1608 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
1609 throws SwitchStateException {
1610 if (h.sw == null) {
1611 String msg = "State machine error: switch is null. Should never " +
1612 "happen";
1613 throw new SwitchStateException(msg);
1614 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001615 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
1616 for (OFPortStatus ps : h.pendingPortStatusMsg) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001617 temp.add(ps);
1618 handlePortStatusMessage(h, ps, false);
1619 }
1620 temp.clear();
1621 // expensive but ok - we don't expect too many port-status messages
1622 // note that we cannot use clear(), because of the reasons below
1623 h.pendingPortStatusMsg.removeAll(temp);
Saurav Dasfcdad072014-08-13 14:15:21 -07001624 // the iterator above takes a snapshot of the list - so while we
1625 // were
1626 // dealing with the pending port-status messages, we could have
1627 // received
1628 // newer ones. Handle them recursively, but break the recursion
1629 // after
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001630 // five steps to avoid an attack.
1631 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1632 handlePendingPortStatusMessages(h, index);
1633 }
1634 }
1635
1636 /**
1637 * Handle a port status message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001638 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001639 * Handle a port status message by updating the port maps in the
Saurav Dasfcdad072014-08-13 14:15:21 -07001640 * IOFSwitch instance and notifying Controller about the change so it
1641 * can dispatch a switch update.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001642 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001643 * @param h The OFChannelHhandler that received the message
1644 * @param m The PortStatus message we received
Saurav Dasfcdad072014-08-13 14:15:21 -07001645 * @param doNotify if true switch port changed events will be dispatched
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001646 * @throws SwitchStateException
Jonathan Hart6eda2302014-08-14 14:57:03 -07001647 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001648 */
1649 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1650 boolean doNotify) throws SwitchStateException {
1651 if (h.sw == null) {
1652 String msg = getSwitchStateMessage(h, m,
1653 "State machine error: switch is null. Should never " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001654 "happen");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001655 throw new SwitchStateException(msg);
1656 }
1657
1658 Collection<PortChangeEvent> changes = h.sw.processOFPortStatus(m);
1659 if (doNotify) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001660 for (PortChangeEvent ev : changes)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001661 h.controller.notifyPortChanged(h.sw.getId(), ev.port, ev.type);
1662 }
1663 }
1664
1665 /**
1666 * Checks if the role received (from the role-reply msg) is different
1667 * from the existing role in the IOFSwitch object for this controller.
Saurav Dasfcdad072014-08-13 14:15:21 -07001668 * If so, it transitions the controller to the new role. Note that the
1669 * caller should have already verified that the role-reply msg received
1670 * was in response to a role-request msg sent out by this controller
1671 * after hearing from the registry service.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001672 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001673 * @param h the ChannelHandler that received the message
1674 * @param role the role in the recieved role reply message
1675 */
1676 protected void checkAndSetRoleTransition(OFChannelHandler h, Role role) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001677 // we received a role-reply in response to a role message
1678 // sent after hearing from the registry service. It is
1679 // possible that the role of this controller instance for
1680 // this switch has changed:
1681 // for 1.0 switch: from MASTER to SLAVE
1682 // for 1.3 switch: from MASTER to EQUAL
1683 if ((h.sw.getRole() == Role.MASTER && role == Role.SLAVE) ||
1684 (h.sw.getRole() == Role.MASTER && role == Role.EQUAL)) {
1685 // the mastership has changed
Saurav Dasd84178f2014-09-29 17:48:54 -07001686 if (role == Role.SLAVE) {
1687 role = Role.EQUAL;
1688 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001689 h.sw.setRole(role);
1690 h.setState(EQUAL);
1691 h.controller.transitionToEqualSwitch(h.sw.getId());
1692 return;
1693 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001694
Saurav Dasfcdad072014-08-13 14:15:21 -07001695 // or for both 1.0 and 1.3 switches from EQUAL to MASTER.
1696 // note that for 1.0, even though we mean SLAVE,
1697 // internally we call the role EQUAL.
1698 if (h.sw.getRole() == Role.EQUAL && role == Role.MASTER) {
1699 // the mastership has changed
1700 h.sw.setRole(role);
1701 h.setState(MASTER);
1702 h.controller.transitionToMasterSwitch(h.sw.getId());
1703 return;
1704 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001705 }
1706
1707 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001708 * Process an OF message received on the channel and update state
1709 * accordingly.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001710 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001711 * The main "event" of the state machine. Process the received message,
1712 * send follow up message if required and update state if required.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001713 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001714 * Switches on the message type and calls more specific event handlers
Saurav Dasfcdad072014-08-13 14:15:21 -07001715 * for each individual OF message type. If we receive a message that is
1716 * supposed to be sent from a controller to a switch we throw a
1717 * SwitchStateExeption.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001718 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001719 * The more specific handlers can also throw SwitchStateExceptions
Jonathan Hart6eda2302014-08-14 14:57:03 -07001720 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001721 * @param h The OFChannelHandler that received the message
1722 * @param m The message we received.
1723 * @throws SwitchStateException
1724 * @throws IOException
1725 */
1726 void processOFMessage(OFChannelHandler h, OFMessage m)
1727 throws IOException, SwitchStateException {
1728 h.roleChanger.checkTimeout();
Saurav Dasfcdad072014-08-13 14:15:21 -07001729 switch (m.getType()) {
1730 case HELLO:
1731 processOFHello(h, (OFHello) m);
1732 break;
1733 case BARRIER_REPLY:
1734 processOFBarrierReply(h, (OFBarrierReply) m);
1735 break;
1736 case ECHO_REPLY:
1737 processOFEchoReply(h, (OFEchoReply) m);
1738 break;
1739 case ECHO_REQUEST:
1740 processOFEchoRequest(h, (OFEchoRequest) m);
1741 break;
1742 case ERROR:
1743 processOFError(h, (OFErrorMsg) m);
1744 break;
1745 case FEATURES_REPLY:
1746 processOFFeaturesReply(h, (OFFeaturesReply) m);
1747 break;
1748 case FLOW_REMOVED:
1749 processOFFlowRemoved(h, (OFFlowRemoved) m);
1750 break;
1751 case GET_CONFIG_REPLY:
1752 processOFGetConfigReply(h, (OFGetConfigReply) m);
1753 break;
1754 case PACKET_IN:
1755 processOFPacketIn(h, (OFPacketIn) m);
1756 break;
1757 case PORT_STATUS:
1758 processOFPortStatus(h, (OFPortStatus) m);
1759 break;
1760 case QUEUE_GET_CONFIG_REPLY:
1761 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1762 break;
1763 case STATS_REPLY: // multipart_reply in 1.3
1764 processOFStatisticsReply(h, (OFStatsReply) m);
1765 break;
1766 case EXPERIMENTER:
1767 processOFExperimenter(h, (OFExperimenter) m);
1768 break;
1769 case ROLE_REPLY:
1770 processOFRoleReply(h, (OFRoleReply) m);
1771 break;
1772 case GET_ASYNC_REPLY:
1773 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1774 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001775
Saurav Dasfcdad072014-08-13 14:15:21 -07001776 // The following messages are sent to switches. The controller
1777 // should never receive them
1778 case SET_CONFIG:
1779 case GET_CONFIG_REQUEST:
1780 case PACKET_OUT:
1781 case PORT_MOD:
1782 case QUEUE_GET_CONFIG_REQUEST:
1783 case BARRIER_REQUEST:
1784 case STATS_REQUEST: // multipart request in 1.3
1785 case FEATURES_REQUEST:
1786 case FLOW_MOD:
1787 case GROUP_MOD:
1788 case TABLE_MOD:
1789 case GET_ASYNC_REQUEST:
1790 case SET_ASYNC:
1791 case METER_MOD:
1792 default:
1793 illegalMessageReceived(h, m);
1794 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001795 }
1796 }
1797
1798 /*-----------------------------------------------------------------
1799 * Default implementation for message handlers in any state.
1800 *
1801 * Individual states must override these if they want a behavior
1802 * that differs from the default.
1803 *
1804 * In general, these handlers simply ignore the message and do
1805 * nothing.
1806 *
1807 * There are some exceptions though, since some messages really
1808 * are handled the same way in every state (e.g., ECHO_REQUST) or
1809 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1810 -----------------------------------------------------------------*/
1811
1812 void processOFHello(OFChannelHandler h, OFHello m)
1813 throws IOException, SwitchStateException {
1814 // we only expect hello in the WAIT_HELLO state
1815 illegalMessageReceived(h, m);
1816 }
1817
1818 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1819 throws IOException {
1820 // Silently ignore.
1821 }
1822
1823 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001824 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001825 if (h.ofVersion == null) {
1826 log.error("No OF version set for {}. Not sending Echo REPLY",
1827 h.channel.getRemoteAddress());
1828 return;
1829 }
1830 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1831 OFEchoReply reply = factory
1832 .buildEchoReply()
1833 .setXid(m.getXid())
1834 .setData(m.getData())
1835 .build();
1836 h.channel.write(Collections.singletonList(reply));
1837 }
1838
1839 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001840 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001841 // Do nothing with EchoReplies !!
1842 }
1843
1844 // no default implementation for OFError
1845 // every state must override it
1846 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1847 throws IOException, SwitchStateException;
1848
Saurav Dasfcdad072014-08-13 14:15:21 -07001849 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001850 throws IOException, SwitchStateException {
1851 unhandledMessageReceived(h, m);
1852 }
1853
1854 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001855 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001856 unhandledMessageReceived(h, m);
1857 }
1858
1859 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1860 throws IOException, SwitchStateException {
1861 // we only expect config replies in the WAIT_CONFIG_REPLY state
1862 illegalMessageReceived(h, m);
1863 }
1864
1865 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1866 throws IOException {
1867 unhandledMessageReceived(h, m);
1868 }
1869
1870 // no default implementation. Every state needs to handle it.
1871 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1872 throws IOException, SwitchStateException;
1873
1874 void processOFQueueGetConfigReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001875 OFQueueGetConfigReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001876 throws IOException {
1877 unhandledMessageReceived(h, m);
1878 }
1879
1880 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1881 throws IOException, SwitchStateException {
1882 unhandledMessageReceived(h, m);
1883 }
1884
1885 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1886 throws IOException, SwitchStateException {
1887 // TODO: it might make sense to parse the vendor message here
1888 // into the known vendor messages we support and then call more
1889 // specific event handlers
1890 unhandledMessageReceived(h, m);
1891 }
1892
1893 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1894 throws SwitchStateException, IOException {
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001895 // A role reply message received by the default handler implies
1896 // that the channel state-machine is in a state where it does not
1897 // expect a role message. That in turn implies that role-request was
1898 // sent out by this controller, as a result of a callback
1899 // from the registry service, because the chosen master (some other
1900 // instance) died, while this controller instance has not completed
1901 // handshake yet. Best to disconnect switch and start over.
1902 illegalMessageReceived(h, m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001903 }
1904
1905 void processOFGetAsyncReply(OFChannelHandler h,
1906 OFAsyncGetReply m) {
1907 unhandledMessageReceived(h, m);
1908 }
1909
1910 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -07001911 RoleRecvStatus expectation) throws IOException {
1912 // do nothing in most states
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001913 }
Saurav Das6de23322014-08-07 18:51:19 -07001914
1915 /**
1916 * Handles role request messages that have timed out.
1917 * <p>
1918 * Role request messages that don't get role-replies (or errors related
1919 * to the request) time out after DEFAULT_ROLE_TIMEOUT_MS secs, at which
1920 * time the controller state-machine disconnects the switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001921 *
Saurav Das6de23322014-08-07 18:51:19 -07001922 * @param h the channel handler for this switch
1923 * @param pendingRole the role for which no reply was received
1924 * @throws SwitchStateException
1925 */
1926 public void handleTimedOutRoleReply(OFChannelHandler h,
1927 Role pendingRole) throws SwitchStateException {
1928 String msg = String.format("Switch: [%s] State: [%s] did not "
1929 + "reply to role-msg for pending role %s. Disconnecting ...",
1930 h.getSwitchInfoString(), this.toString(), pendingRole);
1931 throw new SwitchStateException(msg);
1932 }
Saurav Das85399942014-09-02 16:11:48 -07001933
1934 /**
1935 * Handles switch handshake timeout.
1936 * <p>
1937 * If the handshake times-out while the switch is in any state other
1938 * than WAIT_INITIAL_ROLE, then the switch is disconnected.
1939 * <p>
1940 * If the switch is in WAIT_INITIAL_ROLE state, and a pending role reply
1941 * is not received, it would trigger the role reply timeout, which would
1942 * be handled by handleTimedOutRoleReply (which would disconnect the
1943 * switch).
1944 * <p>
1945 * If the switch is in WAIT_INITIAL_ROLE state, when the handshake
1946 * timeout is triggered, then it's because we have not heard back from
1947 * the registry service regarding switch mastership. In this case, we
1948 * move to EQUAL (or SLAVE) state. See override for this method in
1949 * WAIT_INITIAL_ROLE state.
1950 * <p>
1951 * XXX: This is required today as the registry service does not reply
1952 * with role.slave to a mastership request, i.e it only replies to the
1953 * controller that wins mastership. Once the registry API changes to
1954 * reply to every request, we would not need to wait for a timeout to
1955 * move to Role.EQUAL (or SLAVE).
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001956 *
Saurav Das85399942014-09-02 16:11:48 -07001957 * @param h the channel handler for this switch
1958 * @param ctx the netty channel handler context for the channel 'h'
1959 * @throws IOException
1960 */
1961 public void handleTimedOutHandshake(OFChannelHandler h,
1962 ChannelHandlerContext ctx) throws IOException {
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001963 log.error("Disconnecting switch {}: failed to complete handshake " +
1964 "in state {} (with driverState: {})",
1965 h.getSwitchInfoString(), h.getStateForTesting(),
1966 h.sw.getSwitchDriverState());
Saurav Das85399942014-09-02 16:11:48 -07001967 h.counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush();
1968 ctx.getChannel().close();
1969 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001970 }
1971
Saurav Dasfcdad072014-08-13 14:15:21 -07001972 // *************************
1973 // Channel handler methods
1974 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001975
1976 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001977 @LogMessageDoc(message = "New switch connection from {ip address}",
1978 explanation = "A new switch has connected from the " +
1979 "specified IP address")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001980 public void channelConnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001981 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001982 counters.switchConnected.updateCounterWithFlush();
1983 channel = e.getChannel();
1984 log.info("New switch connection from {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001985 channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001986 sendHandshakeHelloMessage();
1987 setState(ChannelState.WAIT_HELLO);
1988 }
1989
1990 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001991 @LogMessageDoc(message = "Disconnected switch {switch information}",
1992 explanation = "The specified switch has disconnected.")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001993 public void channelDisconnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001994 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001995 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Saurav Dasfcdad072014-08-13 14:15:21 -07001996 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001997 if (thisdpid != 0) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001998 if (duplicateDpidFound != Boolean.TRUE) {
1999 // if the disconnected switch (on this ChannelHandler)
2000 // was not one with a duplicate-dpid, it is safe to remove all
2001 // state for it at the controller. Notice that if the
2002 // disconnected
2003 // switch was a duplicate-dpid, calling the method below would
2004 // clear
2005 // all state for the original switch (with the same dpid),
2006 // which we obviously don't want.
2007 controller.removeConnectedSwitch(thisdpid);
2008 } else {
2009 // A duplicate was disconnected on this ChannelHandler,
2010 // this is the same switch reconnecting, but the original state
2011 // was
2012 // not cleaned up - XXX check liveness of original
2013 // ChannelHandler
2014 duplicateDpidFound = Boolean.FALSE;
2015 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002016 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002017 log.warn("no dpid in channelHandler registered for "
2018 + "disconnected switch {}", getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002019 }
2020 }
2021
2022 @Override
2023 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -07002024 @LogMessageDoc(level = "ERROR",
2025 message = "Disconnecting switch {switch} due to read timeout",
2026 explanation = "The connected switch has failed to send any " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002027 "messages or respond to echo requests",
Saurav Dasfcdad072014-08-13 14:15:21 -07002028 recommendation = LogMessageDoc.CHECK_SWITCH),
2029 @LogMessageDoc(level = "ERROR",
2030 message = "Disconnecting switch {switch}: failed to " +
2031 "complete handshake",
2032 explanation = "The switch did not respond correctly " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002033 "to handshake messages",
Saurav Dasfcdad072014-08-13 14:15:21 -07002034 recommendation = LogMessageDoc.CHECK_SWITCH),
2035 @LogMessageDoc(level = "ERROR",
2036 message = "Disconnecting switch {switch} due to IO Error: {}",
2037 explanation = "There was an error communicating with the switch",
2038 recommendation = LogMessageDoc.CHECK_SWITCH),
2039 @LogMessageDoc(level = "ERROR",
2040 message = "Disconnecting switch {switch} due to switch " +
2041 "state error: {error}",
2042 explanation = "The switch sent an unexpected message",
2043 recommendation = LogMessageDoc.CHECK_SWITCH),
2044 @LogMessageDoc(level = "ERROR",
2045 message = "Disconnecting switch {switch} due to " +
2046 "message parse failure",
2047 explanation = "Could not parse a message from the switch",
2048 recommendation = LogMessageDoc.CHECK_SWITCH),
2049 @LogMessageDoc(level = "ERROR",
2050 message = "Terminating controller due to storage exception",
2051 explanation = Controller.ERROR_DATABASE,
2052 recommendation = LogMessageDoc.CHECK_CONTROLLER),
2053 @LogMessageDoc(level = "ERROR",
2054 message = "Could not process message: queue full",
2055 explanation = "OpenFlow messages are arriving faster than " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002056 " the controller can process them.",
Saurav Dasfcdad072014-08-13 14:15:21 -07002057 recommendation = LogMessageDoc.CHECK_CONTROLLER),
2058 @LogMessageDoc(level = "ERROR",
2059 message = "Error while processing message " +
2060 "from switch {switch} {cause}",
2061 explanation = "An error occurred processing the switch message",
2062 recommendation = LogMessageDoc.GENERIC_ACTION)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002063 })
2064 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
2065 throws Exception {
2066 if (e.getCause() instanceof ReadTimeoutException) {
2067 // switch timeout
2068 log.error("Disconnecting switch {} due to read timeout",
Saurav Dasfcdad072014-08-13 14:15:21 -07002069 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002070 counters.switchDisconnectReadTimeout.updateCounterWithFlush();
2071 ctx.getChannel().close();
2072 } else if (e.getCause() instanceof HandshakeTimeoutException) {
Saurav Das85399942014-09-02 16:11:48 -07002073 // handle timeout within state-machine - different actions taken
2074 // depending on current state
2075 state.handleTimedOutHandshake(this, ctx);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002076 } else if (e.getCause() instanceof ClosedChannelException) {
2077 log.debug("Channel for sw {} already closed", getSwitchInfoString());
2078 } else if (e.getCause() instanceof IOException) {
2079 log.error("Disconnecting switch {} due to IO Error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002080 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002081 if (log.isDebugEnabled()) {
2082 // still print stack trace if debug is enabled
2083 log.debug("StackTrace for previous Exception: ", e.getCause());
2084 }
2085 counters.switchDisconnectIOError.updateCounterWithFlush();
2086 ctx.getChannel().close();
2087 } else if (e.getCause() instanceof SwitchStateException) {
2088 log.error("Disconnecting switch {} due to switch state error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002089 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002090 if (log.isDebugEnabled()) {
2091 // still print stack trace if debug is enabled
2092 log.debug("StackTrace for previous Exception: ", e.getCause());
2093 }
2094 counters.switchDisconnectSwitchStateException.updateCounterWithFlush();
2095 ctx.getChannel().close();
2096 } else if (e.getCause() instanceof OFParseError) {
Saurav Dascc3e35f2014-10-10 15:33:32 -07002097 log.error("Parse failure in switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002098 + getSwitchInfoString() +
2099 " due to message parse failure",
2100 e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002101 counters.switchDisconnectParseError.updateCounterWithFlush();
Saurav Dascc3e35f2014-10-10 15:33:32 -07002102 // OFParse errors should now be handled in the OFMessageDecoder
2103 // So it should never get here. Nevertheless we should not close
2104 // channels for parse errors.
2105 // ctx.getChannel().close();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002106 } else if (e.getCause() instanceof RejectedExecutionException) {
2107 log.warn("Could not process message: queue full");
2108 counters.rejectedExecutionException.updateCounterWithFlush();
2109 } else {
2110 log.error("Error while processing message from switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002111 + getSwitchInfoString()
2112 + "state " + this.state, e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002113 counters.switchDisconnectOtherException.updateCounterWithFlush();
Saurav Das91890e42014-10-03 15:54:21 -07002114 ctx.getChannel().close(); // NPE's land here.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002115 }
2116 }
2117
2118 @Override
2119 public String toString() {
2120 return getSwitchInfoString();
2121 }
2122
2123 @Override
2124 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
2125 throws Exception {
2126 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2127 OFMessage m = factory.buildEchoRequest().build();
2128 log.info("Sending Echo Request on idle channel: {}",
2129 e.getChannel().getPipeline().getLast().toString());
2130 e.getChannel().write(Collections.singletonList(m));
2131 // XXX S some problems here -- echo request has no transaction id, and
2132 // echo reply is not correlated to the echo request.
2133 }
2134
2135 @Override
2136 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
2137 throws Exception {
2138 if (e.getMessage() instanceof List) {
2139 @SuppressWarnings("unchecked")
Saurav Dasfcdad072014-08-13 14:15:21 -07002140 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002141
2142 LoadMonitor.LoadLevel loadlevel;
2143 int packets_dropped = 0;
2144 int packets_allowed = 0;
2145 int lldps_allowed = 0;
2146
2147 if (this.controller.overload_drop) {
2148 loadlevel = this.controller.loadmonitor.getLoadLevel();
2149 }
2150 else {
2151 loadlevel = LoadMonitor.LoadLevel.OK;
2152 }
2153
2154 for (OFMessage ofm : msglist) {
2155 counters.messageReceived.updateCounterNoFlush();
Saurav Dasfcdad072014-08-13 14:15:21 -07002156 // Per-switch input throttling - placeholder for future
2157 // throttling
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002158 /*if (sw != null && sw.inputThrottled(ofm)) {
2159 counters.messageInputThrottled.updateCounterNoFlush();
2160 continue;
2161 }*/
2162 try {
2163 if (this.controller.overload_drop &&
Saurav Dasfcdad072014-08-13 14:15:21 -07002164 !loadlevel.equals(LoadMonitor.LoadLevel.OK)) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002165 switch (ofm.getType()) {
2166 case PACKET_IN:
2167 switch (loadlevel) {
2168 case VERYHIGH:
2169 // Drop all packet-ins, including LLDP/BDDPs
2170 packets_dropped++;
2171 continue;
2172 case HIGH:
2173 // Drop all packet-ins, except LLDP/BDDPs
Saurav Dasfcdad072014-08-13 14:15:21 -07002174 byte[] data = ((OFPacketIn) ofm).getData();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002175 if (data.length > 14) {
Saurav Dasfcdad072014-08-13 14:15:21 -07002176 if (((data[12] == (byte) 0x88) &&
2177 (data[13] == (byte) 0xcc)) ||
2178 ((data[12] == (byte) 0x89) &&
2179 (data[13] == (byte) 0x42))) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002180 lldps_allowed++;
2181 packets_allowed++;
2182 break;
2183 }
2184 }
2185 packets_dropped++;
2186 continue;
2187 default:
2188 // Load not high, go ahead and process msg
2189 packets_allowed++;
2190 break;
2191 }
2192 break;
2193 default:
2194 // Process all non-packet-ins
2195 packets_allowed++;
2196 break;
2197 }
2198 }
2199
2200 // Do the actual packet processing
2201 state.processOFMessage(this, ofm);
2202
Saurav Dasfcdad072014-08-13 14:15:21 -07002203 } catch (Exception ex) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002204 // We are the last handler in the stream, so run the
2205 // exception through the channel again by passing in
2206 // ctx.getChannel().
2207 Channels.fireExceptionCaught(ctx.getChannel(), ex);
2208 }
2209 }
2210
2211 if (loadlevel != LoadMonitor.LoadLevel.OK) {
2212 if (log.isDebugEnabled()) {
2213 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002214 "Overload: Detected {}, packets dropped={}",
2215 loadlevel.toString(), packets_dropped);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002216 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002217 "Overload: Packets allowed={} (LLDP/BDDPs allowed={})",
2218 packets_allowed, lldps_allowed);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002219 }
2220 }
2221 }
2222 else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002223 // Channels.fireExceptionCaught(ctx.getChannel(),
2224 // new
2225 // AssertionError("Message received from Channel is not a list"));
2226 // TODO: Pankaj: move the counters using ONOS metrics implementation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002227
2228 counters.messageReceived.updateCounterNoFlush();
2229 state.processOFMessage(this, (OFMessage) e.getMessage());
2230 }
2231
2232 // Flush all thread local queues etc. generated by this train
2233 // of messages.
2234 this.controller.flushAll();
2235 }
2236
Saurav Dasfcdad072014-08-13 14:15:21 -07002237 // *************************
2238 // Channel utility methods
2239 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002240
2241 /**
2242 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07002243 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002244 * @return true if the handshake is complete
2245 */
2246 public boolean isHandshakeComplete() {
2247 return this.state.isHandshakeComplete();
2248 }
2249
2250 private void dispatchMessage(OFMessage m) throws IOException {
2251 // handleMessage will count
2252 this.controller.handleMessage(this.sw, m, null);
2253 }
2254
2255 /**
2256 * Return a string describing this switch based on the already available
2257 * information (DPID and/or remote socket)
Jonathan Hart6eda2302014-08-14 14:57:03 -07002258 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002259 * @return
2260 */
2261 private String getSwitchInfoString() {
2262 if (sw != null)
2263 return sw.toString();
2264 String channelString;
2265 if (channel == null || channel.getRemoteAddress() == null) {
2266 channelString = "?";
2267 } else {
2268 channelString = channel.getRemoteAddress().toString();
2269 }
2270 String dpidString;
2271 if (featuresReply == null) {
2272 dpidString = "?";
2273 } else {
2274 dpidString = featuresReply.getDatapathId().toString();
2275 }
2276 return String.format("[%s DPID[%s]]", channelString, dpidString);
2277 }
2278
2279 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002280 * Update the channels state. Only called from the state machine. TODO:
2281 * enforce restricted state transitions
Jonathan Hart6eda2302014-08-14 14:57:03 -07002282 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002283 * @param state
2284 */
2285 private void setState(ChannelState state) {
2286 this.state = state;
2287 }
2288
2289 /**
2290 * Send hello message to the switch using the handshake transactions ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002291 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002292 * @throws IOException
2293 */
2294 private void sendHandshakeHelloMessage() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07002295 // The OF protocol requires us to start things off by sending the
2296 // highest
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002297 // version of the protocol supported.
2298
Saurav Dasfcdad072014-08-13 14:15:21 -07002299 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3
2300 // (ofp_version=0x04)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002301 // see Sec. 7.5.1 of the OF1.3.4 spec
Jonathan Hart6eda2302014-08-14 14:57:03 -07002302 OFHello hello;
2303 if (useOnly10) {
2304 hello = factory10.buildHello().setXid(handshakeTransactionIds--).build();
2305 log.info("Sending OF_10 Hello to {}", channel.getRemoteAddress());
2306 } else {
2307 U32 bitmap = U32.ofRaw(0x00000012);
2308 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
2309 .setBitmaps(Collections.singletonList(bitmap))
2310 .build();
2311 OFHello.Builder mb = factory13.buildHello()
2312 .setXid(this.handshakeTransactionIds--)
2313 .setElements(Collections.singletonList(hem));
2314 hello = mb.build();
2315 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
2316 }
2317
2318 channel.write(Collections.singletonList(hello));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002319 }
2320
2321 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002322 * Send featuresRequest msg to the switch using the handshake transactions
2323 * ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002324 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002325 * @throws IOException
2326 */
2327 private void sendHandshakeFeaturesRequestMessage() throws IOException {
2328 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2329 OFMessage m = factory.buildFeaturesRequest()
2330 .setXid(this.handshakeTransactionIds--)
2331 .build();
2332 channel.write(Collections.singletonList(m));
2333 }
2334
2335 private void setSwitchRole(Role role) {
2336 sw.setRole(role);
2337 }
2338
2339 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002340 * Send the configuration requests to tell the switch we want full packets
Jonathan Hart6eda2302014-08-14 14:57:03 -07002341 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002342 * @throws IOException
2343 */
2344 private void sendHandshakeSetConfig() throws IOException {
2345 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Saurav Dasfcdad072014-08-13 14:15:21 -07002346 // log.debug("Sending CONFIG_REQUEST to {}",
2347 // channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002348 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
2349
2350 // Ensure we receive the full packet via PacketIn
2351 // FIXME: We don't set the reassembly flags.
2352 OFSetConfig sc = factory
2353 .buildSetConfig()
2354 .setMissSendLen((short) 0xffff)
2355 .setXid(this.handshakeTransactionIds--)
2356 .build();
2357 msglist.add(sc);
2358
2359 // Barrier
2360 OFBarrierRequest br = factory
2361 .buildBarrierRequest()
2362 .setXid(this.handshakeTransactionIds--)
2363 .build();
2364 msglist.add(br);
2365
2366 // Verify (need barrier?)
2367 OFGetConfigRequest gcr = factory
2368 .buildGetConfigRequest()
2369 .setXid(this.handshakeTransactionIds--)
2370 .build();
2371 msglist.add(gcr);
2372 channel.write(msglist);
2373 }
2374
2375 /**
2376 * send a description state request
Jonathan Hart6eda2302014-08-14 14:57:03 -07002377 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002378 * @throws IOException
2379 */
2380 private void sendHandshakeDescriptionStatsRequest() throws IOException {
2381 // Get Description to set switch-specific flags
2382 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2383 OFDescStatsRequest dreq = factory
2384 .buildDescStatsRequest()
2385 .setXid(handshakeTransactionIds--)
2386 .build();
2387 channel.write(Collections.singletonList(dreq));
2388 }
2389
2390 private void sendHandshakeOFPortDescRequest() throws IOException {
2391 // Get port description for 1.3 switch
2392 OFPortDescStatsRequest preq = factory13
2393 .buildPortDescStatsRequest()
2394 .setXid(handshakeTransactionIds--)
2395 .build();
2396 channel.write(Collections.singletonList(preq));
2397 }
2398
2399 /**
2400 * Read switch properties from storage and set switch attributes accordingly
2401 */
2402 private void readPropertyFromStorage() {
2403 // XXX This is a placeholder for switch configuration
2404 }
2405
2406 ChannelState getStateForTesting() {
2407 return state;
2408 }
2409
Saurav Dascc3e35f2014-10-10 15:33:32 -07002410 public String getChannelSwitchInfo() {
2411 return sw.getStringId();
2412 }
2413
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002414 void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) {
2415 roleChanger = new RoleChanger(roleTimeoutMs);
2416 }
2417
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002418}