blob: 2dd458b77dfe92ef589d436c8db7d15acbd2fb5b [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>();
148 factory13 = controller.getOFMessageFactory_13();
149 factory10 = controller.getOFMessageFactory_10();
150 controlRequested = Boolean.FALSE;
151 duplicateDpidFound = Boolean.FALSE;
152 }
153
Saurav Dasfcdad072014-08-13 14:15:21 -0700154 // *******************
155 // Role Handling
156 // *******************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700157
158 /**
159 * When we remove a pending role request we use this enum to indicate how we
160 * arrived at the decision. When we send a role request to the switch, we
Saurav Dasfcdad072014-08-13 14:15:21 -0700161 * also use this enum to indicate what we expect back from the switch, so
162 * the role changer can match the reply to our expectation.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700163 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700164 * @author gregor, saurav
165 */
166 public enum RoleRecvStatus {
Saurav Dasfcdad072014-08-13 14:15:21 -0700167 /**
168 * The switch returned an error indicating that roles are not supported
169 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700170 UNSUPPORTED,
171 /** The request timed out */
172 NO_REPLY,
173 /** The reply was old, there is a newer request pending */
174 OLD_REPLY,
175 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700176 * The reply's role matched the role that this controller set in the
177 * request message - invoked either initially at startup or to reassert
178 * current role
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700179 */
180 MATCHED_CURRENT_ROLE,
181 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700182 * The reply's role matched the role that this controller set in the
183 * request message - this is the result of a callback from the global
184 * registry, followed by a role request sent to the switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700185 */
186 MATCHED_SET_ROLE,
187 /**
188 * The reply's role was a response to the query made by this controller
189 */
190 REPLY_QUERY,
Saurav Dasfcdad072014-08-13 14:15:21 -0700191 /**
192 * We received a role reply message from the switch but the expectation
193 * was unclear, or there was no expectation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700194 */
195 OTHER_EXPECTATION,
196 }
197
198 /**
199 * Forwards to RoleChanger. See there.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700200 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700201 * @param role
202 */
203 public void sendRoleRequest(Role role, RoleRecvStatus expectation) {
204 try {
205 roleChanger.sendRoleRequest(role, expectation);
206 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700207 log.error("Disconnecting switch {} due to IO Error: {}",
208 getSwitchInfoString(), e.getMessage());
209 channel.close();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700210 }
211 }
212
213 // XXX S consider if necessary
214 public void disconnectSwitch() {
215 sw.disconnectSwitch();
216 }
217
218 /**
219 * A utility class to handle role requests and replies for this channel.
220 * After a role request is submitted the role changer keeps track of the
Saurav Dasfcdad072014-08-13 14:15:21 -0700221 * pending request, collects the reply (if any) and times out the request if
222 * necessary.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700223 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700224 * To simplify role handling we only keep track of the /last/ pending role
225 * reply send to the switch. If multiple requests are pending and we receive
226 * replies for earlier requests we ignore them. However, this way of
227 * handling pending requests implies that we could wait forever if a new
228 * request is submitted before the timeout triggers. If necessary we could
229 * work around that though.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700230 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700231 * @author gregor
232 * @author saurav (added support OF1.3 role messages, and expectations)
233 */
234 private class RoleChanger {
235 // indicates that a request is currently pending
236 // needs to be volatile to allow correct double-check idiom
237 private volatile boolean requestPending;
238 // the transaction Id of the pending request
239 private int pendingXid;
240 // the role that's pending
241 private Role pendingRole;
242 // system time in MS when we send the request
243 private long roleSubmitTime;
244 // the timeout to use
245 private final long roleTimeoutMs;
246 // the expectation set by the caller for the returned role
247 private RoleRecvStatus expectation;
248
249 public RoleChanger(long roleTimeoutMs) {
250 this.requestPending = false;
251 this.roleSubmitTime = 0;
252 this.pendingXid = -1;
253 this.pendingRole = null;
254 this.roleTimeoutMs = roleTimeoutMs;
255 this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
256 }
257
258 /**
259 * Send NX role request message to the switch requesting the specified
260 * role.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700261 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700262 * @param sw switch to send the role request message to
263 * @param role role to request
264 */
265 private int sendNxRoleRequest(Role role) throws IOException {
266 // Convert the role enum to the appropriate role to send
267 OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
268 switch (role) {
269 case MASTER:
270 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
271 break;
272 case SLAVE:
273 case EQUAL:
274 default:
275 // ensuring that the only two roles sent to 1.0 switches with
276 // Nicira role support, are MASTER and SLAVE
277 roleToSend = OFNiciraControllerRole.ROLE_SLAVE;
278 log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
279 }
280 int xid = sw.getNextTransactionId();
281 OFExperimenter roleRequest = factory10
282 .buildNiciraControllerRoleRequest()
283 .setXid(xid)
284 .setRole(roleToSend)
285 .build();
286 sw.write(Collections.<OFMessage>singletonList(roleRequest),
Saurav Dasfcdad072014-08-13 14:15:21 -0700287 new FloodlightContext());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700288 return xid;
289 }
290
291 private int sendOF13RoleRequest(Role role) throws IOException {
292 // Convert the role enum to the appropriate role to send
293 OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
294 switch (role) {
295 case EQUAL:
296 roleToSend = OFControllerRole.ROLE_EQUAL;
297 break;
298 case MASTER:
299 roleToSend = OFControllerRole.ROLE_MASTER;
300 break;
301 case SLAVE:
302 roleToSend = OFControllerRole.ROLE_SLAVE;
303 break;
304 default:
305 log.warn("Sending default role.noChange to switch {}."
306 + " Should only be used for queries.", sw);
307 }
308
309 int xid = sw.getNextTransactionId();
310 OFRoleRequest rrm = factory13
311 .buildRoleRequest()
312 .setRole(roleToSend)
313 .setXid(xid)
314 .setGenerationId(sw.getNextGenerationId())
315 .build();
316 sw.write(rrm, null);
317 return xid;
318 }
319
320 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700321 * Send a role request with the given role to the switch and update the
322 * pending request and timestamp. Sends an OFPT_ROLE_REQUEST to an OF1.3
323 * switch, OR Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured
324 * to support it in the IOFSwitch driver. If not supported, this method
325 * sends nothing and returns 'false'. The caller should take appropriate
326 * action.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700327 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700328 * One other optimization we do here is that for OF1.0 switches with
329 * Nicira role message support, we force the Role.EQUAL to become
Saurav Dasfcdad072014-08-13 14:15:21 -0700330 * Role.SLAVE, as there is no defined behavior for the Nicira role
331 * OTHER. We cannot expect it to behave like SLAVE. We don't have this
332 * problem with OF1.3 switches, because Role.EQUAL is well defined and
333 * we can simulate SLAVE behavior by using ASYNC messages.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700334 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700335 * @param role
336 * @throws IOException
Saurav Dasfcdad072014-08-13 14:15:21 -0700337 * @returns false if and only if the switch does not support
338 * role-request messages, according to the switch driver; true
339 * otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700340 */
341 synchronized boolean sendRoleRequest(Role role, RoleRecvStatus expectation)
342 throws IOException {
343 this.expectation = expectation;
344
345 if (ofVersion == OFVersion.OF_10) {
346 Boolean supportsNxRole = (Boolean)
347 sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE);
348 if (!supportsNxRole) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700349 log.debug("Switch driver indicates no support for Nicira "
350 + "role request messages. Not sending ...");
351 state.handleUnsentRoleMessage(OFChannelHandler.this, role,
352 expectation);
353 return false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700354 }
355 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
356 // make Role.EQUAL become Role.SLAVE
357 role = (role == Role.EQUAL) ? Role.SLAVE : role;
358 pendingXid = sendNxRoleRequest(role);
359 pendingRole = role;
360 roleSubmitTime = System.currentTimeMillis();
361 requestPending = true;
362 } else {
363 // OF1.3 switch, use OFPT_ROLE_REQUEST message
364 pendingXid = sendOF13RoleRequest(role);
365 pendingRole = role;
366 roleSubmitTime = System.currentTimeMillis();
367 requestPending = true;
368 }
369 return true;
370 }
371
372 /**
373 * Deliver a received role reply.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700374 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700375 * Check if a request is pending and if the received reply matches the
Saurav Dasfcdad072014-08-13 14:15:21 -0700376 * the expected pending reply (we check both role and xid) we set the
377 * role for the switch/channel.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700378 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700379 * If a request is pending but doesn't match the reply we ignore it, and
380 * return
Jonathan Hart6eda2302014-08-14 14:57:03 -0700381 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700382 * If no request is pending we disconnect with a SwitchStateException
Jonathan Hart6eda2302014-08-14 14:57:03 -0700383 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700384 * @param RoleReplyInfo information about role-reply in format that
Saurav Dasfcdad072014-08-13 14:15:21 -0700385 * controller can understand.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700386 * @throws SwitchStateException if no request is pending
387 */
388 synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
389 throws SwitchStateException {
390 if (!requestPending) {
391 Role currentRole = (sw != null) ? sw.getRole() : null;
392 if (currentRole != null) {
393 if (currentRole == rri.getRole()) {
394 // Don't disconnect if the role reply we received is
395 // for the same role we are already in.
396 log.debug("Received unexpected RoleReply from "
397 + "Switch: {} in State: {}. "
398 + "Role in reply is same as current role of this "
399 + "controller for this sw. Ignoring ...",
400 getSwitchInfoString(), state.toString());
401 return RoleRecvStatus.OTHER_EXPECTATION;
402 } else {
403 String msg = String.format("Switch: [%s], State: [%s], "
404 + "received unexpected RoleReply[%s]. "
405 + "No roles are pending, and this controller's "
406 + "current role:[%s] does not match reply. "
407 + "Disconnecting switch ... ",
408 OFChannelHandler.this.getSwitchInfoString(),
409 OFChannelHandler.this.state.toString(),
410 rri, currentRole);
411 throw new SwitchStateException(msg);
412 }
413 }
414 log.debug("Received unexpected RoleReply {} from "
415 + "Switch: {} in State: {}. "
416 + "This controller has no current role for this sw. "
417 + "Ignoring ...", new Object[] {rri,
Saurav Dasfcdad072014-08-13 14:15:21 -0700418 getSwitchInfoString(), state});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700419 return RoleRecvStatus.OTHER_EXPECTATION;
420 }
421
422 int xid = (int) rri.getXid();
423 Role role = rri.getRole();
Saurav Dasfcdad072014-08-13 14:15:21 -0700424 // XXX S should check generation id meaningfully and other cases of
425 // expectations
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700426 // U64 genId = rri.getGenId();
427
428 if (pendingXid != xid) {
429 log.debug("Received older role reply from " +
430 "switch {} ({}). Ignoring. " +
431 "Waiting for {}, xid={}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700432 new Object[] {getSwitchInfoString(), rri,
433 pendingRole, pendingXid});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700434 return RoleRecvStatus.OLD_REPLY;
435 }
436
437 if (pendingRole == role) {
Saurav Das6de23322014-08-07 18:51:19 -0700438 requestPending = false; // we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700439 log.debug("Received role reply message from {} that matched "
440 + "expected role-reply {} with expectations {}",
441 new Object[] {getSwitchInfoString(), role, expectation});
442 counters.roleReplyReceived.updateCounterWithFlush();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700443 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
444 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
445 return expectation;
446 } else {
447 return RoleRecvStatus.OTHER_EXPECTATION;
448 }
449 }
450
451 // if xids match but role's don't, perhaps its a query (OF1.3)
Saurav Das6de23322014-08-07 18:51:19 -0700452 if (expectation == RoleRecvStatus.REPLY_QUERY) {
453 requestPending = false; // again we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700454 return expectation;
Saurav Das6de23322014-08-07 18:51:19 -0700455 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700456
Saurav Das6de23322014-08-07 18:51:19 -0700457 // It is not clear what this role-reply was about, since it is not
458 // a query and it did not match the pendingRole. But since the xid's
459 // matched, we state that we received what we were waiting for, and
460 // let the caller handle it
461 requestPending = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700462 return RoleRecvStatus.OTHER_EXPECTATION;
463 }
464
465 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700466 * Called if we receive an error message. If the xid matches the pending
467 * request we handle it otherwise we ignore it.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700468 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700469 * Note: since we only keep the last pending request we might get error
470 * messages for earlier role requests that we won't be able to handle
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700471 */
472 synchronized RoleRecvStatus deliverError(OFErrorMsg error)
473 throws SwitchStateException {
474 if (!requestPending) {
475 log.debug("Received an error msg from sw {}, but no pending "
476 + "requests in role-changer; not handling ...",
477 getSwitchInfoString());
478 return RoleRecvStatus.OTHER_EXPECTATION;
479 }
480 if (pendingXid != error.getXid()) {
481 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
482 log.debug("Received an error msg from sw {} for a role request,"
483 + " but not for pending request in role-changer; "
484 + " ignoring error {} ...",
485 getSwitchInfoString(), error);
486 }
487 return RoleRecvStatus.OTHER_EXPECTATION;
488 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700489 // it is an error related to a currently pending role request
490 // message
491 requestPending = false; // we got a response, even though it is an
492 // error
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700493 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
494 counters.roleReplyErrorUnsupported.updateCounterWithFlush();
495 log.error("Received a error msg {} from sw {} in state {} for "
496 + "pending role request {}. Switch driver indicates "
497 + "role-messaging is supported. Possible issues in "
498 + "switch driver configuration?", new Object[] {
Saurav Dasfcdad072014-08-13 14:15:21 -0700499 ((OFBadRequestErrorMsg) error).toString(),
500 getSwitchInfoString(), state, pendingRole
501 });
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700502 return RoleRecvStatus.UNSUPPORTED;
503 }
504
505 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
506 OFRoleRequestFailedErrorMsg rrerr =
507 (OFRoleRequestFailedErrorMsg) error;
508 switch (rrerr.getCode()) {
509 case BAD_ROLE:
510 // switch says that current-role-req has bad role?
511 // for now we disconnect
512 // fall-thru
513 case STALE:
514 // switch says that current-role-req has stale gen-id?
515 // for now we disconnect
516 // fall-thru
517 case UNSUP:
518 // switch says that current-role-req has role that
519 // cannot be supported? for now we disconnect
520 String msgx = String.format("Switch: [%s], State: [%s], "
521 + "received Error to for pending role request [%s]. "
522 + "Error:[%s]. Disconnecting switch ... ",
523 OFChannelHandler.this.getSwitchInfoString(),
524 OFChannelHandler.this.state.toString(),
525 pendingRole, rrerr);
526 throw new SwitchStateException(msgx);
527 default:
528 break;
529 }
530 }
531
Saurav Dasfcdad072014-08-13 14:15:21 -0700532 // This error message was for a role request message but we dont
533 // know
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700534 // how to handle errors for nicira role request messages
535 return RoleRecvStatus.OTHER_EXPECTATION;
536 }
537
538 /**
539 * Check if a pending role request has timed out.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700540 *
Saurav Das6de23322014-08-07 18:51:19 -0700541 * @throws SwitchStateException
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700542 */
Saurav Das6de23322014-08-07 18:51:19 -0700543 void checkTimeout() throws SwitchStateException {
544 if (!requestPending) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700545 return;
Saurav Das6de23322014-08-07 18:51:19 -0700546 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700547 synchronized (this) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700548 if (!requestPending)
549 return;
550 long now = System.currentTimeMillis();
551 if (now - roleSubmitTime > roleTimeoutMs) {
552 // timeout triggered.
553 counters.roleReplyTimeout.updateCounterWithFlush();
Saurav Das6de23322014-08-07 18:51:19 -0700554 state.handleTimedOutRoleReply(OFChannelHandler.this, pendingRole);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700555 }
556 }
557 }
558
559 }
560
Saurav Dasfcdad072014-08-13 14:15:21 -0700561 // *************************
562 // Channel State Machine
563 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700564
565 /**
566 * The state machine for handling the switch/channel state. All state
Saurav Dasfcdad072014-08-13 14:15:21 -0700567 * transitions should happen from within the state machine (and not from
568 * other parts of the code)
Jonathan Hart6eda2302014-08-14 14:57:03 -0700569 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700570 * @author gregor
Saurav Dasfcdad072014-08-13 14:15:21 -0700571 * @author saurav (modified to handle 1.0 & 1.3 switches, EQUAL state,
572 * role-handling )
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700573 */
574 enum ChannelState {
575 /**
576 * Initial state before channel is connected.
577 */
578 INIT(false) {
579 @Override
580 void processOFMessage(OFChannelHandler h, OFMessage m)
581 throws IOException, SwitchStateException {
582 illegalMessageReceived(h, m);
583 }
584
585 @Override
586 void processOFError(OFChannelHandler h, OFErrorMsg m)
587 throws IOException {
588 // need to implement since its abstract but it will never
589 // be called
590 }
591
592 @Override
593 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
594 throws IOException {
595 unhandledMessageReceived(h, m);
596 }
597 },
598
599 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700600 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the
601 * switch. Once we receive the reply, we decide on OF 1.3 or 1.0 switch
602 * - no other protocol version is accepted. We send an OFFeaturesRequest
603 * depending on the protocol version selected Next state is
604 * WAIT_FEATURES_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700605 */
606 WAIT_HELLO(false) {
607 @Override
608 void processOFHello(OFChannelHandler h, OFHello m)
609 throws IOException {
610 // TODO We could check for the optional bitmap, but for now
611 // we are just checking the version number.
612 if (m.getVersion() == OFVersion.OF_13) {
613 log.info("Received {} Hello from {}", m.getVersion(),
614 h.channel.getRemoteAddress());
615 h.ofVersion = OFVersion.OF_13;
616 } else if (m.getVersion() == OFVersion.OF_10) {
617 log.info("Received {} Hello from {} - switching to OF "
618 + "version 1.0", m.getVersion(),
619 h.channel.getRemoteAddress());
620 h.ofVersion = OFVersion.OF_10;
621 } else {
622 log.error("Received Hello of version {} from switch at {}. "
623 + "This controller works with OF1.0 and OF1.3 "
624 + "switches. Disconnecting switch ...",
625 m.getVersion(), h.channel.getRemoteAddress());
626 h.channel.disconnect();
627 return;
628 }
629 h.sendHandshakeFeaturesRequestMessage();
630 h.setState(WAIT_FEATURES_REPLY);
631 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700632
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700633 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700634 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700635 throws IOException, SwitchStateException {
636 illegalMessageReceived(h, m);
637 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700638
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700639 @Override
640 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700641 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700642 throws IOException, SwitchStateException {
643 illegalMessageReceived(h, m);
644 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700645
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700646 @Override
647 void processOFError(OFChannelHandler h, OFErrorMsg m) {
648 logErrorDisconnect(h, m);
649 }
650
651 @Override
652 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
653 throws IOException {
654 unhandledMessageReceived(h, m);
655 }
656 },
657
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700658 /**
659 * We are waiting for a features reply message. Once we receive it, the
Saurav Dasfcdad072014-08-13 14:15:21 -0700660 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0, we
661 * send a SetConfig request, barrier, and GetConfig request and the next
662 * state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700663 * request and the next state is WAIT_PORT_DESC_REPLY.
664 */
665 WAIT_FEATURES_REPLY(false) {
666 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700667 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700668 throws IOException {
669 h.thisdpid = m.getDatapathId().getLong();
670 log.info("Received features reply for switch at {} with dpid {}",
671 h.getSwitchInfoString(), h.thisdpid);
Saurav Dasfcdad072014-08-13 14:15:21 -0700672 // update the controller about this connected switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700673 boolean success = h.controller.addConnectedSwitch(
674 h.thisdpid, h);
675 if (!success) {
676 disconnectDuplicate(h);
677 return;
678 }
679
Saurav Dasfcdad072014-08-13 14:15:21 -0700680 h.featuresReply = m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700681 if (h.ofVersion == OFVersion.OF_10) {
682 h.sendHandshakeSetConfig();
683 h.setState(WAIT_CONFIG_REPLY);
684 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -0700685 // version is 1.3, must get switchport information
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700686 h.sendHandshakeOFPortDescRequest();
687 h.setState(WAIT_PORT_DESC_REPLY);
688 }
689 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700690
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700691 @Override
692 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700693 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700694 throws IOException, SwitchStateException {
695 illegalMessageReceived(h, m);
696 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700697
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700698 @Override
699 void processOFError(OFChannelHandler h, OFErrorMsg m) {
700 logErrorDisconnect(h, m);
701 }
702
703 @Override
704 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
705 throws IOException {
706 unhandledMessageReceived(h, m);
707 }
708 },
709
710 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700711 * We are waiting for a description of the 1.3 switch ports. Once
712 * received, we send a SetConfig request Next State is WAIT_CONFIG_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700713 */
714 WAIT_PORT_DESC_REPLY(false) {
715
716 @Override
717 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
718 throws SwitchStateException {
719 // Read port description
720 if (m.getStatsType() != OFStatsType.PORT_DESC) {
721 log.warn("Expecting port description stats but received stats "
722 + "type {} from {}. Ignoring ...", m.getStatsType(),
723 h.channel.getRemoteAddress());
724 return;
725 }
Sangho Shinda7635e2014-11-03 16:22:11 -0800726 if (h.portDescReplies == null) {
727 h.portDescReplies = new ArrayList<OFPortDescStatsReply>();
728 h.portDescReplies.add((OFPortDescStatsReply) m);
729 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
730 log.warn("Stats reply indicates more stats from sw {} for "
731 + "port description - not currently handled",
732 h.getSwitchInfoString());
733 return;
734 }
735
736 }
737 else if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700738 log.warn("Stats reply indicates more stats from sw {} for "
739 + "port description - not currently handled",
740 h.getSwitchInfoString());
Sangho Shinda7635e2014-11-03 16:22:11 -0800741 h.portDescReplies.add((OFPortDescStatsReply)m);
742 return;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700743 }
Sangho Shinda7635e2014-11-03 16:22:11 -0800744 else {
745 h.portDescReplies.add((OFPortDescStatsReply)m);
746 }
747 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700748 log.info("Received port desc reply for switch at {}",
749 h.getSwitchInfoString());
750 try {
751 h.sendHandshakeSetConfig();
752 } catch (IOException e) {
753 log.error("Unable to send setConfig after PortDescReply. "
754 + "Error: {}", e.getMessage());
755 }
756 h.setState(WAIT_CONFIG_REPLY);
757 }
758
759 @Override
760 void processOFError(OFChannelHandler h, OFErrorMsg m)
761 throws IOException, SwitchStateException {
762 logErrorDisconnect(h, m);
763
764 }
765
766 @Override
767 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
768 throws IOException, SwitchStateException {
769 unhandledMessageReceived(h, m);
770
771 }
772 },
773
774 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700775 * We are waiting for a config reply message. Once we receive it we send
776 * a DescriptionStatsRequest to the switch. Next state:
777 * WAIT_DESCRIPTION_STAT_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700778 */
779 WAIT_CONFIG_REPLY(false) {
780 @Override
781 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -0700782 @LogMessageDoc(level = "WARN",
783 message = "Config Reply from {switch} has " +
784 "miss length set to {length}",
785 explanation = "The controller requires that the switch " +
786 "use a miss length of 0xffff for correct " +
787 "function",
788 recommendation = "Use a different switch to ensure " +
789 "correct function")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700790 })
791 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
792 throws IOException {
793 if (m.getMissSendLen() == 0xffff) {
794 log.trace("Config Reply from switch {} confirms "
795 + "miss length set to 0xffff",
796 h.getSwitchInfoString());
797 } else {
798 // FIXME: we can't really deal with switches that don't send
799 // full packets. Shouldn't we drop the connection here?
800 log.warn("Config Reply from switch {} has"
801 + "miss length set to {}",
802 h.getSwitchInfoString(),
803 m.getMissSendLen());
804 }
805 h.sendHandshakeDescriptionStatsRequest();
806 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
807 }
808
809 @Override
810 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
811 // do nothing;
812 }
813
814 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700815 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700816 throws IOException, SwitchStateException {
817 illegalMessageReceived(h, m);
818 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700819
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700820 @Override
821 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700822 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700823 throws IOException, SwitchStateException {
824 log.error("Received multipart(stats) message sub-type {}",
825 m.getStatsType());
826 illegalMessageReceived(h, m);
827 }
828
829 @Override
830 void processOFError(OFChannelHandler h, OFErrorMsg m) {
831 logErrorDisconnect(h, m);
832 }
833
834 @Override
835 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
836 throws IOException {
837 h.pendingPortStatusMsg.add(m);
838 }
839 },
840
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700841 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700842 * We are waiting for a OFDescriptionStat message from the switch. Once
843 * we receive any stat message we try to parse it. If it's not a
844 * description stats message we disconnect. If its the expected
845 * description stats message, we: - use the switch driver to bind the
846 * switch and get an IOFSwitch instance - setup the IOFSwitch instance -
847 * add switch to FloodlightProvider(Controller) and send the initial
848 * role request to the switch. Next state: WAIT_INITIAL_ROLE In the
849 * typical case, where switches support role request messages the next
850 * state is where we expect the role reply message. In the special case
851 * that where the switch does not support any kind of role request
852 * messages, we don't send a role message, but we do request mastership
853 * from the registry service. This controller should become master once
854 * we hear back from the registry service. All following states will
855 * have a h.sw instance!
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700856 */
857 WAIT_DESCRIPTION_STAT_REPLY(false) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700858 @LogMessageDoc(message = "Switch {switch info} bound to class " +
859 "{switch driver}, description {switch description}",
860 explanation = "The specified switch has been bound to " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700861 "a switch driver based on the switch description" +
862 "received from the switch")
863 @Override
864 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
865 throws SwitchStateException {
866 // Read description, if it has been updated
867 if (m.getStatsType() != OFStatsType.DESC) {
868 log.warn("Expecting Description stats but received stats "
869 + "type {} from {}. Ignoring ...", m.getStatsType(),
870 h.channel.getRemoteAddress());
871 return;
872 }
873 log.info("Received switch description reply from switch at {}",
874 h.channel.getRemoteAddress());
875 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasfcdad072014-08-13 14:15:21 -0700876 // Here is where we differentiate between different kinds of
877 // switches
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700878 h.sw = h.controller.getOFSwitchInstance(drep, h.ofVersion);
879 // set switch information
880 h.sw.setOFVersion(h.ofVersion);
881 ((OFSwitchImplBase) h.sw).setFeaturesReply(h.featuresReply);
Sangho Shinda7635e2014-11-03 16:22:11 -0800882 ((OFSwitchImplBase) h.sw).setPortDescReplies(h.portDescReplies);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700883 h.sw.setConnected(true);
884 h.sw.setChannel(h.channel);
885 h.sw.setFloodlightProvider(h.controller);
886 h.sw.setThreadPoolService(h.controller.getThreadPoolService());
887 try {
888 h.sw.setDebugCounterService(h.controller.getDebugCounter());
889 } catch (CounterException e) {
890 h.counters.switchCounterRegistrationFailed
891 .updateCounterNoFlush();
892 log.warn("Could not register counters for switch {} ",
Saurav Dasfcdad072014-08-13 14:15:21 -0700893 h.getSwitchInfoString(), e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700894 }
895
896 log.info("Switch {} bound to class {}, description {}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700897 new Object[] {h.sw, h.sw.getClass(), drep});
898 // Put switch in EQUAL mode until we hear back from the global
899 // registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700900 log.debug("Setting new switch {} to EQUAL and sending Role request",
901 h.sw.getStringId());
902 h.setSwitchRole(Role.EQUAL);
903 try {
904 boolean supportsRRMsg = h.roleChanger.sendRoleRequest(Role.EQUAL,
905 RoleRecvStatus.MATCHED_CURRENT_ROLE);
906 if (!supportsRRMsg) {
907 log.warn("Switch {} does not support role request messages "
908 + "of any kind. No role messages were sent. "
909 + "This controller instance SHOULD become MASTER "
910 + "from the registry process. ",
911 h.getSwitchInfoString());
912 }
913 h.setState(WAIT_INITIAL_ROLE);
914 // request control of switch from global registry -
915 // necessary even if this is the only controller the
916 // switch is connected to.
917 h.controller.submitRegistryRequest(h.sw.getId());
918 } catch (IOException e) {
919 log.error("Exception when sending role request: {} ",
920 e.getMessage());
921 // FIXME shouldn't we disconnect?
922 }
923 }
924
925 @Override
926 void processOFError(OFChannelHandler h, OFErrorMsg m) {
927 logErrorDisconnect(h, m);
928 }
929
930 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700931 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700932 throws IOException, SwitchStateException {
933 illegalMessageReceived(h, m);
934 }
935
936 @Override
937 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
938 throws IOException {
939 h.pendingPortStatusMsg.add(m);
940 }
941 },
942
943 /**
944 * We are waiting for a role reply message in response to a role request
945 * sent after hearing back from the registry service -- OR -- we are
946 * just waiting to hear back from the registry service in the case that
947 * the switch does not support role messages. If completed successfully,
Saurav Dasfcdad072014-08-13 14:15:21 -0700948 * the controller's role for this switch will be set here. Before we
949 * move to the state corresponding to the role, we allow the switch
950 * specific driver to complete its configuration. This configuration
951 * typically depends on the role the controller is playing for this
952 * switch. And so we set the switch role (for 'this' controller) before
953 * we start the driver-sub-handshake. Next State:
954 * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700955 */
956 WAIT_INITIAL_ROLE(false) {
957 @Override
958 void processOFError(OFChannelHandler h, OFErrorMsg m)
959 throws SwitchStateException {
960 // role changer will ignore the error if it isn't for it
961 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
962 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
963 logError(h, m);
964 }
965 }
966
967 @Override
968 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
969 throws IOException, SwitchStateException {
970 Role role = extractNiciraRoleReply(h, m);
971 // If role == null it means the vendor (experimenter) message
972 // wasn't really a Nicira role reply. We ignore this case.
973 if (role != null) {
974 RoleReplyInfo rri = new RoleReplyInfo(role, null, m.getXid());
975 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
976 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
977 setRoleAndStartDriverHandshake(h, rri.getRole());
978 } // else do nothing - wait for the correct expected reply
979 } else {
980 unhandledMessageReceived(h, m);
981 }
982 }
983
984 @Override
985 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
986 throws SwitchStateException, IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -0700987 RoleReplyInfo rri = extractOFRoleReply(h, m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700988 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
989 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
990 setRoleAndStartDriverHandshake(h, rri.getRole());
991 } // else do nothing - wait for the correct expected reply
992 }
993
994 @Override
995 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -0700996 RoleRecvStatus expectation) throws IOException {
997 // typically this is triggered for a switch where role messages
998 // are not supported - we confirm that the role being set is
999 // master and move to the next state
1000 if (expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
1001 if (role == Role.MASTER) {
1002 setRoleAndStartDriverHandshake(h, role);
1003 } else {
1004 log.error("Expected MASTER role from registry for switch "
1005 + "which has no support for role-messages."
1006 + "Received {}. It is possible that this switch "
1007 + "is connected to other controllers, in which "
1008 + "case it should support role messages - not "
1009 + "moving forward.", role);
1010 }
1011 } // else do nothing - wait to hear back from registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001012
1013 }
1014
1015 private void setRoleAndStartDriverHandshake(OFChannelHandler h,
1016 Role role) throws IOException {
1017 h.setSwitchRole(role);
1018 h.sw.startDriverHandshake();
1019 if (h.sw.isDriverHandshakeComplete()) {
1020 Role mySwitchRole = h.sw.getRole();
1021 if (mySwitchRole == Role.MASTER) {
1022 log.info("Switch-driver sub-handshake complete. "
1023 + "Activating switch {} with Role: MASTER",
1024 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001025 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001026 boolean success = h.controller.addActivatedMasterSwitch(
1027 h.sw.getId(), h.sw);
1028 if (!success) {
1029 disconnectDuplicate(h);
1030 return;
1031 }
1032 h.setState(MASTER);
1033 } else {
1034 log.info("Switch-driver sub-handshake complete. "
1035 + "Activating switch {} with Role: EQUAL",
1036 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001037 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001038 boolean success = h.controller.addActivatedEqualSwitch(
1039 h.sw.getId(), h.sw);
1040 if (!success) {
1041 disconnectDuplicate(h);
1042 return;
1043 }
1044 h.setState(EQUAL);
1045 }
1046 } else {
1047 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
1048 }
1049 }
1050
1051 @Override
Saurav Das85399942014-09-02 16:11:48 -07001052 public void handleTimedOutHandshake(OFChannelHandler h,
1053 ChannelHandlerContext ctx) throws IOException {
1054 log.info("Handshake timed out waiting to hear back from registry "
1055 + "service. Moving to Role EQUAL for switch {}",
1056 h.getSwitchInfoString());
1057 setRoleAndStartDriverHandshake(h, Role.EQUAL);
1058 }
1059
1060 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001061 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001062 throws IOException, SwitchStateException {
1063 illegalMessageReceived(h, m);
1064 }
1065
1066 @Override
1067 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1068 throws SwitchStateException {
1069 illegalMessageReceived(h, m);
1070 }
1071
1072 @Override
1073 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1074 throws IOException, SwitchStateException {
1075 h.pendingPortStatusMsg.add(m);
1076
1077 }
1078 },
1079
1080 /**
1081 * We are waiting for the respective switch driver to complete its
Saurav Dasfcdad072014-08-13 14:15:21 -07001082 * configuration. Notice that we do not consider this to be part of the
1083 * main switch-controller handshake. But we do consider it as a step
1084 * that comes before we declare the switch as available to the
1085 * controller. Next State: depends on the role of this controller for
1086 * this switch - either MASTER or EQUAL.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001087 */
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001088 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(false) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001089
1090 @Override
1091 void processOFError(OFChannelHandler h, OFErrorMsg m)
1092 throws IOException {
1093 // will never be called. We override processOFMessage
1094 }
1095
1096 @Override
1097 void processOFMessage(OFChannelHandler h, OFMessage m)
1098 throws IOException {
1099 if (m.getType() == OFType.ECHO_REQUEST)
Saurav Dasfcdad072014-08-13 14:15:21 -07001100 processOFEchoRequest(h, (OFEchoRequest) m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001101 else {
1102 // FIXME: other message to handle here?
1103 h.sw.processDriverHandshakeMessage(m);
1104 if (h.sw.isDriverHandshakeComplete()) {
1105 // consult the h.sw role and goto that state
1106 Role mySwitchRole = h.sw.getRole();
1107 if (mySwitchRole == Role.MASTER) {
1108 log.info("Switch-driver sub-handshake complete. "
1109 + "Activating switch {} with Role: MASTER",
1110 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001111 handlePendingPortStatusMessages(h); // before
1112 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001113 boolean success = h.controller.addActivatedMasterSwitch(
1114 h.sw.getId(), h.sw);
1115 if (!success) {
1116 disconnectDuplicate(h);
1117 return;
1118 }
1119 h.setState(MASTER);
1120 } else {
1121 log.info("Switch-driver sub-handshake complete. "
1122 + "Activating switch {} with Role: EQUAL",
1123 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001124 handlePendingPortStatusMessages(h); // before
1125 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001126 boolean success = h.controller.addActivatedEqualSwitch(
1127 h.sw.getId(), h.sw);
1128 if (!success) {
1129 disconnectDuplicate(h);
1130 return;
1131 }
1132 h.setState(EQUAL);
1133 }
1134 }
1135 }
1136 }
1137
1138 @Override
1139 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1140 throws IOException, SwitchStateException {
1141 h.pendingPortStatusMsg.add(m);
1142 }
1143 },
1144
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001145 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001146 * This controller is in MASTER role for this switch. We enter this
1147 * state after requesting and winning control from the global registry.
1148 * The main handshake as well as the switch-driver sub-handshake is
1149 * complete at this point. // XXX S reconsider below In the (near)
1150 * future we may deterministically assign controllers to switches at
1151 * startup. We only leave this state if the switch disconnects or if we
1152 * send a role request for SLAVE /and/ receive the role reply for SLAVE.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001153 */
1154 MASTER(true) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001155 @LogMessageDoc(level = "WARN",
1156 message = "Received permission error from switch {} while" +
1157 "being master. Reasserting master role.",
1158 explanation = "The switch has denied an operation likely " +
1159 "indicating inconsistent controller roles",
1160 recommendation = "This situation can occurs transiently during role" +
1161 " changes. If, however, the condition persists or happens" +
1162 " frequently this indicates a role inconsistency. " +
1163 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001164 @Override
1165 void processOFError(OFChannelHandler h, OFErrorMsg m)
1166 throws IOException, SwitchStateException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001167 // first check if the error msg is in response to a role-request
1168 // message
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001169 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1170 if (rrstatus != RoleRecvStatus.OTHER_EXPECTATION) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001171 // rolechanger has handled the error message - we are done
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001172 return;
1173 }
1174
1175 // if we get here, then the error message is for something else
1176 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
1177 ((OFBadRequestErrorMsg) m).getCode() ==
Saurav Dasfcdad072014-08-13 14:15:21 -07001178 OFBadRequestCode.EPERM) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001179 // We are the master controller and the switch returned
1180 // a permission error. This is a likely indicator that
1181 // the switch thinks we are slave. Reassert our
1182 // role
1183 // FIXME: this could be really bad during role transitions
1184 // if two controllers are master (even if its only for
1185 // a brief period). We might need to see if these errors
1186 // persist before we reassert
1187 h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush();
1188 log.warn("Received permission error from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001189 "being master. Reasserting master role.",
1190 h.getSwitchInfoString());
1191 // h.controller.reassertRole(h, Role.MASTER);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001192 // XXX S reassert in role changer or reconsider if all this
1193 // stuff is really needed
1194 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
Saurav Dasfcdad072014-08-13 14:15:21 -07001195 ((OFFlowModFailedErrorMsg) m).getCode() ==
1196 OFFlowModFailedCode.ALL_TABLES_FULL) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001197 h.sw.setTableFull(true);
1198 } else {
1199 logError(h, m);
1200 }
1201 h.dispatchMessage(m);
1202 }
1203
1204 @Override
1205 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001206 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001207 h.sw.deliverStatisticsReply(m);
1208 }
1209
1210 @Override
1211 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1212 throws IOException, SwitchStateException {
1213 Role role = extractNiciraRoleReply(h, m);
1214 if (role == null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001215 // The message wasn't really a Nicira role reply. We just
1216 // dispatch it to the OFMessage listeners in this case.
1217 h.dispatchMessage(m);
1218 return;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001219 }
1220
1221 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Saurav Dasfcdad072014-08-13 14:15:21 -07001222 new RoleReplyInfo(role, null, m.getXid()));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001223 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001224 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001225 }
1226 }
1227
1228 @Override
1229 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1230 throws SwitchStateException, IOException {
1231 RoleReplyInfo rri = extractOFRoleReply(h, m);
1232 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1233 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001234 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001235 }
1236 }
1237
1238 @Override
1239 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1240 throws IOException, SwitchStateException {
1241 handlePortStatusMessage(h, m, true);
1242 h.dispatchMessage(m);
1243 }
1244
1245 @Override
1246 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1247 throws IOException {
1248 h.dispatchMessage(m);
1249 }
1250
1251 @Override
1252 void processOFFlowRemoved(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001253 OFFlowRemoved m) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001254 h.dispatchMessage(m);
1255 }
1256
1257 @Override
1258 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1259 throws IOException {
Saurav Dasd84178f2014-09-29 17:48:54 -07001260 h.sw.deliverBarrierReply(m);
Saurav Dasfcdad072014-08-13 14:15:21 -07001261 h.dispatchMessage(m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001262 }
1263
1264 },
1265
1266 /**
1267 * This controller is in EQUAL role for this switch. We enter this state
1268 * after some /other/ controller instance wins mastership-role over this
1269 * switch. The EQUAL role can be considered the same as the SLAVE role
1270 * if this controller does NOT send commands or packets to the switch.
1271 * This should always be true for OF1.0 switches. XXX S need to enforce.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001272 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001273 * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE,
Saurav Dasfcdad072014-08-13 14:15:21 -07001274 * gives us the flexibility that if an app wants to send
1275 * commands/packets to switches, it can, even thought it is running on a
1276 * controller instance that is not in a MASTER role for this switch. Of
1277 * course, it is the job of the app to ensure that commands/packets sent
1278 * by this (EQUAL) controller instance does not clash/conflict with
1279 * commands/packets sent by the MASTER controller for this switch.
1280 * Neither the controller instances, nor the switch provides any kind of
1281 * resolution mechanism should conflicts occur.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001282 */
1283 EQUAL(true) {
1284 @Override
1285 void processOFError(OFChannelHandler h, OFErrorMsg m)
1286 throws IOException, SwitchStateException {
1287 // role changer will ignore the error if it isn't for it
1288 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1289 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
1290 logError(h, m);
1291 h.dispatchMessage(m);
1292 }
1293 }
1294
1295 @Override
1296 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001297 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001298 h.sw.deliverStatisticsReply(m);
1299 }
1300
1301 @Override
1302 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1303 throws IOException, SwitchStateException {
1304 Role role = extractNiciraRoleReply(h, m);
1305 // If role == null it means the message wasn't really a
1306 // Nicira role reply. We ignore it in this state.
1307 if (role != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001308 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001309 new RoleReplyInfo(role, null, m.getXid()));
Saurav Dasfcdad072014-08-13 14:15:21 -07001310 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1311 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001312 }
1313 } else {
1314 unhandledMessageReceived(h, m);
1315 }
1316 }
1317
1318 @Override
1319 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1320 throws SwitchStateException, IOException {
1321 RoleReplyInfo rri = extractOFRoleReply(h, m);
1322 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1323 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001324 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001325 }
1326 }
1327
1328 // XXX S needs more handlers for 1.3 switches in equal role
1329
1330 @Override
1331 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1332 throws IOException, SwitchStateException {
1333 handlePortStatusMessage(h, m, true);
1334 }
1335
1336 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001337 @LogMessageDoc(level = "WARN",
1338 message = "Received PacketIn from switch {} while" +
1339 "being slave. Reasserting slave role.",
1340 explanation = "The switch has receive a PacketIn despite being " +
1341 "in slave role indicating inconsistent controller roles",
1342 recommendation = "This situation can occurs transiently during role" +
1343 " changes. If, however, the condition persists or happens" +
1344 " frequently this indicates a role inconsistency. " +
1345 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001346 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException {
1347 // we don't expect packetIn while slave, reassert we are slave
1348 h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush();
1349 log.warn("Received PacketIn from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001350 "being slave. Reasserting slave role.", h.sw);
1351 // h.controller.reassertRole(h, Role.SLAVE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001352 // XXX reassert in role changer
1353 }
1354 };
1355
1356 private final boolean handshakeComplete;
Saurav Dasfcdad072014-08-13 14:15:21 -07001357
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001358 ChannelState(boolean handshakeComplete) {
1359 this.handshakeComplete = handshakeComplete;
1360 }
1361
1362 /**
1363 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07001364 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001365 * @return true if the handshake is complete
1366 */
1367 public boolean isHandshakeComplete() {
1368 return handshakeComplete;
1369 }
1370
1371 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001372 * Get a string specifying the switch connection, state, and message
1373 * received. To be used as message for SwitchStateException or log
1374 * messages
Jonathan Hart6eda2302014-08-14 14:57:03 -07001375 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001376 * @param h The channel handler (to get switch information_
1377 * @param m The OFMessage that has just been received
Saurav Dasfcdad072014-08-13 14:15:21 -07001378 * @param details A string giving more details about the exact nature of
1379 * the problem.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001380 * @return
1381 */
1382 // needs to be protected because enum members are actually subclasses
1383 protected String getSwitchStateMessage(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001384 OFMessage m,
1385 String details) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001386 return String.format("Switch: [%s], State: [%s], received: [%s]"
Saurav Dasfcdad072014-08-13 14:15:21 -07001387 + ", details: %s",
1388 h.getSwitchInfoString(),
1389 this.toString(),
1390 m.getType().toString(),
1391 details);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001392 }
1393
1394 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001395 * We have an OFMessage we didn't expect given the current state and we
1396 * want to treat this as an error. We currently throw an exception that
1397 * will terminate the connection However, we could be more forgiving
Jonathan Hart6eda2302014-08-14 14:57:03 -07001398 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001399 * @param h the channel handler that received the message
1400 * @param m the message
1401 * @throws SwitchStateException
1402 * @throws SwitchStateExeption we always through the execption
1403 */
1404 // needs to be protected because enum members are acutally subclasses
1405 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
1406 throws SwitchStateException {
1407 String msg = getSwitchStateMessage(h, m,
1408 "Switch should never send this message in the current state");
1409 throw new SwitchStateException(msg);
1410
1411 }
1412
1413 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001414 * We have an OFMessage we didn't expect given the current state and we
1415 * want to ignore the message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001416 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001417 * @param h the channel handler the received the message
1418 * @param m the message
1419 */
1420 protected void unhandledMessageReceived(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001421 OFMessage m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001422 h.counters.unhandledMessage.updateCounterNoFlush();
1423 if (log.isDebugEnabled()) {
1424 String msg = getSwitchStateMessage(h, m,
1425 "Ignoring unexpected message");
1426 log.debug(msg);
1427 }
1428 }
1429
1430 /**
1431 * Log an OpenFlow error message from a switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001432 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001433 * @param sw The switch that sent the error
1434 * @param error The error message
1435 */
Saurav Dasfcdad072014-08-13 14:15:21 -07001436 @LogMessageDoc(level = "ERROR",
1437 message = "Error {error type} {error code} from {switch} " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001438 "in state {state}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001439 explanation = "The switch responded with an unexpected error" +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001440 "to an OpenFlow message from the controller",
Saurav Dasfcdad072014-08-13 14:15:21 -07001441 recommendation = "This could indicate improper network operation. " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001442 "If the problem persists restarting the switch and " +
1443 "controller may help."
1444 )
Saurav Dasfcdad072014-08-13 14:15:21 -07001445 protected void logError(OFChannelHandler h, OFErrorMsg error) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001446 log.error("{} from switch {} in state {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001447 new Object[] {
1448 error,
1449 h.getSwitchInfoString(),
1450 this.toString()});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001451 }
1452
1453 /**
1454 * Log an OpenFlow error message from a switch and disconnect the
1455 * channel
Jonathan Hart6eda2302014-08-14 14:57:03 -07001456 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001457 * @param sw The switch that sent the error
1458 * @param error The error message
1459 */
1460 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
1461 logError(h, error);
1462 h.channel.disconnect();
1463 }
1464
1465 /**
1466 * log an error message for a duplicate dpid and disconnect this channel
1467 */
1468 protected void disconnectDuplicate(OFChannelHandler h) {
1469 log.error("Duplicated dpid or incompleted cleanup - "
1470 + "disconnecting channel {}", h.getSwitchInfoString());
1471 h.duplicateDpidFound = Boolean.TRUE;
1472 h.channel.disconnect();
1473 }
1474
1475 /**
1476 * Extract the role from an OFVendor message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001477 *
Saurav Dasfcdad072014-08-13 14:15:21 -07001478 * Extract the role from an OFVendor message if the message is a Nicira
1479 * role reply. Otherwise return null.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001480 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001481 * @param h The channel handler receiving the message
1482 * @param vendorMessage The vendor message to parse.
1483 * @return The role in the message if the message is a Nicira role
Saurav Dasfcdad072014-08-13 14:15:21 -07001484 * reply, null otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001485 * @throws SwitchStateException If the message is a Nicira role reply
Saurav Dasfcdad072014-08-13 14:15:21 -07001486 * but the numeric role value is unknown.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001487 */
1488 protected Role extractNiciraRoleReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001489 OFExperimenter experimenterMsg) throws SwitchStateException {
1490 int vendor = (int) experimenterMsg.getExperimenter();
1491 if (vendor != 0x2320) // magic number representing nicira
1492 return null;
1493 OFNiciraControllerRoleReply nrr =
1494 (OFNiciraControllerRoleReply) experimenterMsg;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001495
Saurav Dasfcdad072014-08-13 14:15:21 -07001496 Role role = null;
1497 OFNiciraControllerRole ncr = nrr.getRole();
1498 switch (ncr) {
1499 case ROLE_MASTER:
1500 role = Role.MASTER;
1501 break;
1502 case ROLE_OTHER:
1503 role = Role.EQUAL;
1504 break;
1505 case ROLE_SLAVE:
1506 role = Role.SLAVE;
1507 break;
1508 default: // handled below
1509 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001510
Saurav Dasfcdad072014-08-13 14:15:21 -07001511 if (role == null) {
1512 String msg = String.format("Switch: [%s], State: [%s], "
1513 + "received NX_ROLE_REPLY with invalid role "
1514 + "value %d",
1515 h.getSwitchInfoString(),
1516 this.toString(),
1517 nrr.getRole());
1518 throw new SwitchStateException(msg);
1519 }
1520 return role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001521 }
1522
1523 /**
1524 * Helper class returns role reply information in the format understood
1525 * by the controller.
1526 */
1527 protected class RoleReplyInfo {
1528 private Role role;
1529 private U64 genId;
1530 private long xid;
1531
Saurav Dasfcdad072014-08-13 14:15:21 -07001532 RoleReplyInfo(Role role, U64 genId, long xid) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001533 this.role = role;
1534 this.genId = genId;
1535 this.xid = xid;
1536 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001537
1538 public Role getRole() {
1539 return role;
1540 }
1541
1542 public U64 getGenId() {
1543 return genId;
1544 }
1545
1546 public long getXid() {
1547 return xid;
1548 }
1549
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001550 @Override
1551 public String toString() {
1552 return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
1553 }
1554 }
1555
1556 /**
1557 * Extract the role information from an OF1.3 Role Reply Message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001558 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001559 * @param h
1560 * @param rrmsg
1561 * @return RoleReplyInfo object
1562 * @throws SwitchStateException
1563 */
1564 protected RoleReplyInfo extractOFRoleReply(OFChannelHandler h,
1565 OFRoleReply rrmsg) throws SwitchStateException {
1566 OFControllerRole cr = rrmsg.getRole();
1567 Role role = null;
Saurav Dasfcdad072014-08-13 14:15:21 -07001568 switch (cr) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001569 case ROLE_EQUAL:
1570 role = Role.EQUAL;
1571 break;
1572 case ROLE_MASTER:
1573 role = Role.MASTER;
1574 break;
1575 case ROLE_SLAVE:
1576 role = Role.SLAVE;
1577 break;
1578 case ROLE_NOCHANGE: // switch should send current role
1579 default:
1580 String msg = String.format("Unknown controller role {} "
1581 + "received from switch {}", cr, h.sw);
1582 throw new SwitchStateException(msg);
1583 }
1584
1585 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
1586 }
1587
1588 /**
1589 * Handles all pending port status messages before a switch is declared
1590 * activated in MASTER or EQUAL role. Note that since this handling
Saurav Dasfcdad072014-08-13 14:15:21 -07001591 * precedes the activation (and therefore notification to
1592 * IOFSwitchListerners) the changes to ports will already be visible
1593 * once the switch is activated. As a result, no notifications are sent
1594 * out for these pending portStatus messages.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001595 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001596 * @param h
1597 * @throws SwitchStateException
1598 */
1599 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
1600 try {
1601 handlePendingPortStatusMessages(h, 0);
1602 } catch (SwitchStateException e) {
1603 // do nothing - exception msg printed
1604 }
1605 }
1606
1607 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
1608 throws SwitchStateException {
1609 if (h.sw == null) {
1610 String msg = "State machine error: switch is null. Should never " +
1611 "happen";
1612 throw new SwitchStateException(msg);
1613 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001614 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
1615 for (OFPortStatus ps : h.pendingPortStatusMsg) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001616 temp.add(ps);
1617 handlePortStatusMessage(h, ps, false);
1618 }
1619 temp.clear();
1620 // expensive but ok - we don't expect too many port-status messages
1621 // note that we cannot use clear(), because of the reasons below
1622 h.pendingPortStatusMsg.removeAll(temp);
Saurav Dasfcdad072014-08-13 14:15:21 -07001623 // the iterator above takes a snapshot of the list - so while we
1624 // were
1625 // dealing with the pending port-status messages, we could have
1626 // received
1627 // newer ones. Handle them recursively, but break the recursion
1628 // after
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001629 // five steps to avoid an attack.
1630 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1631 handlePendingPortStatusMessages(h, index);
1632 }
1633 }
1634
1635 /**
1636 * Handle a port status message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001637 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001638 * Handle a port status message by updating the port maps in the
Saurav Dasfcdad072014-08-13 14:15:21 -07001639 * IOFSwitch instance and notifying Controller about the change so it
1640 * can dispatch a switch update.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001641 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001642 * @param h The OFChannelHhandler that received the message
1643 * @param m The PortStatus message we received
Saurav Dasfcdad072014-08-13 14:15:21 -07001644 * @param doNotify if true switch port changed events will be dispatched
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001645 * @throws SwitchStateException
Jonathan Hart6eda2302014-08-14 14:57:03 -07001646 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001647 */
1648 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1649 boolean doNotify) throws SwitchStateException {
1650 if (h.sw == null) {
1651 String msg = getSwitchStateMessage(h, m,
1652 "State machine error: switch is null. Should never " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001653 "happen");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001654 throw new SwitchStateException(msg);
1655 }
1656
1657 Collection<PortChangeEvent> changes = h.sw.processOFPortStatus(m);
1658 if (doNotify) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001659 for (PortChangeEvent ev : changes)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001660 h.controller.notifyPortChanged(h.sw.getId(), ev.port, ev.type);
1661 }
1662 }
1663
1664 /**
1665 * Checks if the role received (from the role-reply msg) is different
1666 * from the existing role in the IOFSwitch object for this controller.
Saurav Dasfcdad072014-08-13 14:15:21 -07001667 * If so, it transitions the controller to the new role. Note that the
1668 * caller should have already verified that the role-reply msg received
1669 * was in response to a role-request msg sent out by this controller
1670 * after hearing from the registry service.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001671 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001672 * @param h the ChannelHandler that received the message
1673 * @param role the role in the recieved role reply message
1674 */
1675 protected void checkAndSetRoleTransition(OFChannelHandler h, Role role) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001676 // we received a role-reply in response to a role message
1677 // sent after hearing from the registry service. It is
1678 // possible that the role of this controller instance for
1679 // this switch has changed:
1680 // for 1.0 switch: from MASTER to SLAVE
1681 // for 1.3 switch: from MASTER to EQUAL
1682 if ((h.sw.getRole() == Role.MASTER && role == Role.SLAVE) ||
1683 (h.sw.getRole() == Role.MASTER && role == Role.EQUAL)) {
1684 // the mastership has changed
Saurav Dasd84178f2014-09-29 17:48:54 -07001685 if (role == Role.SLAVE) {
1686 role = Role.EQUAL;
1687 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001688 h.sw.setRole(role);
1689 h.setState(EQUAL);
1690 h.controller.transitionToEqualSwitch(h.sw.getId());
1691 return;
1692 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001693
Saurav Dasfcdad072014-08-13 14:15:21 -07001694 // or for both 1.0 and 1.3 switches from EQUAL to MASTER.
1695 // note that for 1.0, even though we mean SLAVE,
1696 // internally we call the role EQUAL.
1697 if (h.sw.getRole() == Role.EQUAL && role == Role.MASTER) {
1698 // the mastership has changed
1699 h.sw.setRole(role);
1700 h.setState(MASTER);
1701 h.controller.transitionToMasterSwitch(h.sw.getId());
1702 return;
1703 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001704 }
1705
1706 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001707 * Process an OF message received on the channel and update state
1708 * accordingly.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001709 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001710 * The main "event" of the state machine. Process the received message,
1711 * send follow up message if required and update state if required.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001712 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001713 * Switches on the message type and calls more specific event handlers
Saurav Dasfcdad072014-08-13 14:15:21 -07001714 * for each individual OF message type. If we receive a message that is
1715 * supposed to be sent from a controller to a switch we throw a
1716 * SwitchStateExeption.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001717 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001718 * The more specific handlers can also throw SwitchStateExceptions
Jonathan Hart6eda2302014-08-14 14:57:03 -07001719 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001720 * @param h The OFChannelHandler that received the message
1721 * @param m The message we received.
1722 * @throws SwitchStateException
1723 * @throws IOException
1724 */
1725 void processOFMessage(OFChannelHandler h, OFMessage m)
1726 throws IOException, SwitchStateException {
1727 h.roleChanger.checkTimeout();
Saurav Dasfcdad072014-08-13 14:15:21 -07001728 switch (m.getType()) {
1729 case HELLO:
1730 processOFHello(h, (OFHello) m);
1731 break;
1732 case BARRIER_REPLY:
1733 processOFBarrierReply(h, (OFBarrierReply) m);
1734 break;
1735 case ECHO_REPLY:
1736 processOFEchoReply(h, (OFEchoReply) m);
1737 break;
1738 case ECHO_REQUEST:
1739 processOFEchoRequest(h, (OFEchoRequest) m);
1740 break;
1741 case ERROR:
1742 processOFError(h, (OFErrorMsg) m);
1743 break;
1744 case FEATURES_REPLY:
1745 processOFFeaturesReply(h, (OFFeaturesReply) m);
1746 break;
1747 case FLOW_REMOVED:
1748 processOFFlowRemoved(h, (OFFlowRemoved) m);
1749 break;
1750 case GET_CONFIG_REPLY:
1751 processOFGetConfigReply(h, (OFGetConfigReply) m);
1752 break;
1753 case PACKET_IN:
1754 processOFPacketIn(h, (OFPacketIn) m);
1755 break;
1756 case PORT_STATUS:
1757 processOFPortStatus(h, (OFPortStatus) m);
1758 break;
1759 case QUEUE_GET_CONFIG_REPLY:
1760 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1761 break;
1762 case STATS_REPLY: // multipart_reply in 1.3
1763 processOFStatisticsReply(h, (OFStatsReply) m);
1764 break;
1765 case EXPERIMENTER:
1766 processOFExperimenter(h, (OFExperimenter) m);
1767 break;
1768 case ROLE_REPLY:
1769 processOFRoleReply(h, (OFRoleReply) m);
1770 break;
1771 case GET_ASYNC_REPLY:
1772 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1773 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001774
Saurav Dasfcdad072014-08-13 14:15:21 -07001775 // The following messages are sent to switches. The controller
1776 // should never receive them
1777 case SET_CONFIG:
1778 case GET_CONFIG_REQUEST:
1779 case PACKET_OUT:
1780 case PORT_MOD:
1781 case QUEUE_GET_CONFIG_REQUEST:
1782 case BARRIER_REQUEST:
1783 case STATS_REQUEST: // multipart request in 1.3
1784 case FEATURES_REQUEST:
1785 case FLOW_MOD:
1786 case GROUP_MOD:
1787 case TABLE_MOD:
1788 case GET_ASYNC_REQUEST:
1789 case SET_ASYNC:
1790 case METER_MOD:
1791 default:
1792 illegalMessageReceived(h, m);
1793 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001794 }
1795 }
1796
1797 /*-----------------------------------------------------------------
1798 * Default implementation for message handlers in any state.
1799 *
1800 * Individual states must override these if they want a behavior
1801 * that differs from the default.
1802 *
1803 * In general, these handlers simply ignore the message and do
1804 * nothing.
1805 *
1806 * There are some exceptions though, since some messages really
1807 * are handled the same way in every state (e.g., ECHO_REQUST) or
1808 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1809 -----------------------------------------------------------------*/
1810
1811 void processOFHello(OFChannelHandler h, OFHello m)
1812 throws IOException, SwitchStateException {
1813 // we only expect hello in the WAIT_HELLO state
1814 illegalMessageReceived(h, m);
1815 }
1816
1817 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1818 throws IOException {
1819 // Silently ignore.
1820 }
1821
1822 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001823 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001824 if (h.ofVersion == null) {
1825 log.error("No OF version set for {}. Not sending Echo REPLY",
1826 h.channel.getRemoteAddress());
1827 return;
1828 }
1829 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1830 OFEchoReply reply = factory
1831 .buildEchoReply()
1832 .setXid(m.getXid())
1833 .setData(m.getData())
1834 .build();
1835 h.channel.write(Collections.singletonList(reply));
1836 }
1837
1838 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001839 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001840 // Do nothing with EchoReplies !!
1841 }
1842
1843 // no default implementation for OFError
1844 // every state must override it
1845 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1846 throws IOException, SwitchStateException;
1847
Saurav Dasfcdad072014-08-13 14:15:21 -07001848 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001849 throws IOException, SwitchStateException {
1850 unhandledMessageReceived(h, m);
1851 }
1852
1853 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001854 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001855 unhandledMessageReceived(h, m);
1856 }
1857
1858 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1859 throws IOException, SwitchStateException {
1860 // we only expect config replies in the WAIT_CONFIG_REPLY state
1861 illegalMessageReceived(h, m);
1862 }
1863
1864 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1865 throws IOException {
1866 unhandledMessageReceived(h, m);
1867 }
1868
1869 // no default implementation. Every state needs to handle it.
1870 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1871 throws IOException, SwitchStateException;
1872
1873 void processOFQueueGetConfigReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001874 OFQueueGetConfigReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001875 throws IOException {
1876 unhandledMessageReceived(h, m);
1877 }
1878
1879 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1880 throws IOException, SwitchStateException {
1881 unhandledMessageReceived(h, m);
1882 }
1883
1884 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1885 throws IOException, SwitchStateException {
1886 // TODO: it might make sense to parse the vendor message here
1887 // into the known vendor messages we support and then call more
1888 // specific event handlers
1889 unhandledMessageReceived(h, m);
1890 }
1891
1892 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1893 throws SwitchStateException, IOException {
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001894 // A role reply message received by the default handler implies
1895 // that the channel state-machine is in a state where it does not
1896 // expect a role message. That in turn implies that role-request was
1897 // sent out by this controller, as a result of a callback
1898 // from the registry service, because the chosen master (some other
1899 // instance) died, while this controller instance has not completed
1900 // handshake yet. Best to disconnect switch and start over.
1901 illegalMessageReceived(h, m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001902 }
1903
1904 void processOFGetAsyncReply(OFChannelHandler h,
1905 OFAsyncGetReply m) {
1906 unhandledMessageReceived(h, m);
1907 }
1908
1909 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -07001910 RoleRecvStatus expectation) throws IOException {
1911 // do nothing in most states
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001912 }
Saurav Das6de23322014-08-07 18:51:19 -07001913
1914 /**
1915 * Handles role request messages that have timed out.
1916 * <p>
1917 * Role request messages that don't get role-replies (or errors related
1918 * to the request) time out after DEFAULT_ROLE_TIMEOUT_MS secs, at which
1919 * time the controller state-machine disconnects the switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001920 *
Saurav Das6de23322014-08-07 18:51:19 -07001921 * @param h the channel handler for this switch
1922 * @param pendingRole the role for which no reply was received
1923 * @throws SwitchStateException
1924 */
1925 public void handleTimedOutRoleReply(OFChannelHandler h,
1926 Role pendingRole) throws SwitchStateException {
1927 String msg = String.format("Switch: [%s] State: [%s] did not "
1928 + "reply to role-msg for pending role %s. Disconnecting ...",
1929 h.getSwitchInfoString(), this.toString(), pendingRole);
1930 throw new SwitchStateException(msg);
1931 }
Saurav Das85399942014-09-02 16:11:48 -07001932
1933 /**
1934 * Handles switch handshake timeout.
1935 * <p>
1936 * If the handshake times-out while the switch is in any state other
1937 * than WAIT_INITIAL_ROLE, then the switch is disconnected.
1938 * <p>
1939 * If the switch is in WAIT_INITIAL_ROLE state, and a pending role reply
1940 * is not received, it would trigger the role reply timeout, which would
1941 * be handled by handleTimedOutRoleReply (which would disconnect the
1942 * switch).
1943 * <p>
1944 * If the switch is in WAIT_INITIAL_ROLE state, when the handshake
1945 * timeout is triggered, then it's because we have not heard back from
1946 * the registry service regarding switch mastership. In this case, we
1947 * move to EQUAL (or SLAVE) state. See override for this method in
1948 * WAIT_INITIAL_ROLE state.
1949 * <p>
1950 * XXX: This is required today as the registry service does not reply
1951 * with role.slave to a mastership request, i.e it only replies to the
1952 * controller that wins mastership. Once the registry API changes to
1953 * reply to every request, we would not need to wait for a timeout to
1954 * move to Role.EQUAL (or SLAVE).
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001955 *
Saurav Das85399942014-09-02 16:11:48 -07001956 * @param h the channel handler for this switch
1957 * @param ctx the netty channel handler context for the channel 'h'
1958 * @throws IOException
1959 */
1960 public void handleTimedOutHandshake(OFChannelHandler h,
1961 ChannelHandlerContext ctx) throws IOException {
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001962 log.error("Disconnecting switch {}: failed to complete handshake " +
1963 "in state {} (with driverState: {})",
1964 h.getSwitchInfoString(), h.getStateForTesting(),
1965 h.sw.getSwitchDriverState());
Saurav Das85399942014-09-02 16:11:48 -07001966 h.counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush();
1967 ctx.getChannel().close();
1968 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001969 }
1970
Saurav Dasfcdad072014-08-13 14:15:21 -07001971 // *************************
1972 // Channel handler methods
1973 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001974
1975 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001976 @LogMessageDoc(message = "New switch connection from {ip address}",
1977 explanation = "A new switch has connected from the " +
1978 "specified IP address")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001979 public void channelConnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001980 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001981 counters.switchConnected.updateCounterWithFlush();
1982 channel = e.getChannel();
1983 log.info("New switch connection from {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001984 channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001985 sendHandshakeHelloMessage();
1986 setState(ChannelState.WAIT_HELLO);
1987 }
1988
1989 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001990 @LogMessageDoc(message = "Disconnected switch {switch information}",
1991 explanation = "The specified switch has disconnected.")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001992 public void channelDisconnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001993 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001994 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Saurav Dasfcdad072014-08-13 14:15:21 -07001995 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001996 if (thisdpid != 0) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001997 if (duplicateDpidFound != Boolean.TRUE) {
1998 // if the disconnected switch (on this ChannelHandler)
1999 // was not one with a duplicate-dpid, it is safe to remove all
2000 // state for it at the controller. Notice that if the
2001 // disconnected
2002 // switch was a duplicate-dpid, calling the method below would
2003 // clear
2004 // all state for the original switch (with the same dpid),
2005 // which we obviously don't want.
2006 controller.removeConnectedSwitch(thisdpid);
2007 } else {
2008 // A duplicate was disconnected on this ChannelHandler,
2009 // this is the same switch reconnecting, but the original state
2010 // was
2011 // not cleaned up - XXX check liveness of original
2012 // ChannelHandler
2013 duplicateDpidFound = Boolean.FALSE;
2014 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002015 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002016 log.warn("no dpid in channelHandler registered for "
2017 + "disconnected switch {}", getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002018 }
2019 }
2020
2021 @Override
2022 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -07002023 @LogMessageDoc(level = "ERROR",
2024 message = "Disconnecting switch {switch} due to read timeout",
2025 explanation = "The connected switch has failed to send any " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002026 "messages or respond to echo requests",
Saurav Dasfcdad072014-08-13 14:15:21 -07002027 recommendation = LogMessageDoc.CHECK_SWITCH),
2028 @LogMessageDoc(level = "ERROR",
2029 message = "Disconnecting switch {switch}: failed to " +
2030 "complete handshake",
2031 explanation = "The switch did not respond correctly " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002032 "to handshake messages",
Saurav Dasfcdad072014-08-13 14:15:21 -07002033 recommendation = LogMessageDoc.CHECK_SWITCH),
2034 @LogMessageDoc(level = "ERROR",
2035 message = "Disconnecting switch {switch} due to IO Error: {}",
2036 explanation = "There was an error communicating with the switch",
2037 recommendation = LogMessageDoc.CHECK_SWITCH),
2038 @LogMessageDoc(level = "ERROR",
2039 message = "Disconnecting switch {switch} due to switch " +
2040 "state error: {error}",
2041 explanation = "The switch sent an unexpected message",
2042 recommendation = LogMessageDoc.CHECK_SWITCH),
2043 @LogMessageDoc(level = "ERROR",
2044 message = "Disconnecting switch {switch} due to " +
2045 "message parse failure",
2046 explanation = "Could not parse a message from the switch",
2047 recommendation = LogMessageDoc.CHECK_SWITCH),
2048 @LogMessageDoc(level = "ERROR",
2049 message = "Terminating controller due to storage exception",
2050 explanation = Controller.ERROR_DATABASE,
2051 recommendation = LogMessageDoc.CHECK_CONTROLLER),
2052 @LogMessageDoc(level = "ERROR",
2053 message = "Could not process message: queue full",
2054 explanation = "OpenFlow messages are arriving faster than " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002055 " the controller can process them.",
Saurav Dasfcdad072014-08-13 14:15:21 -07002056 recommendation = LogMessageDoc.CHECK_CONTROLLER),
2057 @LogMessageDoc(level = "ERROR",
2058 message = "Error while processing message " +
2059 "from switch {switch} {cause}",
2060 explanation = "An error occurred processing the switch message",
2061 recommendation = LogMessageDoc.GENERIC_ACTION)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002062 })
2063 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
2064 throws Exception {
2065 if (e.getCause() instanceof ReadTimeoutException) {
2066 // switch timeout
2067 log.error("Disconnecting switch {} due to read timeout",
Saurav Dasfcdad072014-08-13 14:15:21 -07002068 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002069 counters.switchDisconnectReadTimeout.updateCounterWithFlush();
2070 ctx.getChannel().close();
2071 } else if (e.getCause() instanceof HandshakeTimeoutException) {
Saurav Das85399942014-09-02 16:11:48 -07002072 // handle timeout within state-machine - different actions taken
2073 // depending on current state
2074 state.handleTimedOutHandshake(this, ctx);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002075 } else if (e.getCause() instanceof ClosedChannelException) {
2076 log.debug("Channel for sw {} already closed", getSwitchInfoString());
2077 } else if (e.getCause() instanceof IOException) {
2078 log.error("Disconnecting switch {} due to IO Error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002079 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002080 if (log.isDebugEnabled()) {
2081 // still print stack trace if debug is enabled
2082 log.debug("StackTrace for previous Exception: ", e.getCause());
2083 }
2084 counters.switchDisconnectIOError.updateCounterWithFlush();
2085 ctx.getChannel().close();
2086 } else if (e.getCause() instanceof SwitchStateException) {
2087 log.error("Disconnecting switch {} due to switch state error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002088 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002089 if (log.isDebugEnabled()) {
2090 // still print stack trace if debug is enabled
2091 log.debug("StackTrace for previous Exception: ", e.getCause());
2092 }
2093 counters.switchDisconnectSwitchStateException.updateCounterWithFlush();
2094 ctx.getChannel().close();
2095 } else if (e.getCause() instanceof OFParseError) {
Saurav Dascc3e35f2014-10-10 15:33:32 -07002096 log.error("Parse failure in switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002097 + getSwitchInfoString() +
2098 " due to message parse failure",
2099 e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002100 counters.switchDisconnectParseError.updateCounterWithFlush();
Saurav Dascc3e35f2014-10-10 15:33:32 -07002101 // OFParse errors should now be handled in the OFMessageDecoder
2102 // So it should never get here. Nevertheless we should not close
2103 // channels for parse errors.
2104 // ctx.getChannel().close();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002105 } else if (e.getCause() instanceof RejectedExecutionException) {
2106 log.warn("Could not process message: queue full");
2107 counters.rejectedExecutionException.updateCounterWithFlush();
2108 } else {
2109 log.error("Error while processing message from switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002110 + getSwitchInfoString()
2111 + "state " + this.state, e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002112 counters.switchDisconnectOtherException.updateCounterWithFlush();
Saurav Das91890e42014-10-03 15:54:21 -07002113 ctx.getChannel().close(); // NPE's land here.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002114 }
2115 }
2116
2117 @Override
2118 public String toString() {
2119 return getSwitchInfoString();
2120 }
2121
2122 @Override
2123 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
2124 throws Exception {
2125 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2126 OFMessage m = factory.buildEchoRequest().build();
2127 log.info("Sending Echo Request on idle channel: {}",
2128 e.getChannel().getPipeline().getLast().toString());
2129 e.getChannel().write(Collections.singletonList(m));
2130 // XXX S some problems here -- echo request has no transaction id, and
2131 // echo reply is not correlated to the echo request.
2132 }
2133
2134 @Override
2135 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
2136 throws Exception {
2137 if (e.getMessage() instanceof List) {
2138 @SuppressWarnings("unchecked")
Saurav Dasfcdad072014-08-13 14:15:21 -07002139 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002140
2141 LoadMonitor.LoadLevel loadlevel;
2142 int packets_dropped = 0;
2143 int packets_allowed = 0;
2144 int lldps_allowed = 0;
2145
2146 if (this.controller.overload_drop) {
2147 loadlevel = this.controller.loadmonitor.getLoadLevel();
2148 }
2149 else {
2150 loadlevel = LoadMonitor.LoadLevel.OK;
2151 }
2152
2153 for (OFMessage ofm : msglist) {
2154 counters.messageReceived.updateCounterNoFlush();
Saurav Dasfcdad072014-08-13 14:15:21 -07002155 // Per-switch input throttling - placeholder for future
2156 // throttling
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002157 /*if (sw != null && sw.inputThrottled(ofm)) {
2158 counters.messageInputThrottled.updateCounterNoFlush();
2159 continue;
2160 }*/
2161 try {
2162 if (this.controller.overload_drop &&
Saurav Dasfcdad072014-08-13 14:15:21 -07002163 !loadlevel.equals(LoadMonitor.LoadLevel.OK)) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002164 switch (ofm.getType()) {
2165 case PACKET_IN:
2166 switch (loadlevel) {
2167 case VERYHIGH:
2168 // Drop all packet-ins, including LLDP/BDDPs
2169 packets_dropped++;
2170 continue;
2171 case HIGH:
2172 // Drop all packet-ins, except LLDP/BDDPs
Saurav Dasfcdad072014-08-13 14:15:21 -07002173 byte[] data = ((OFPacketIn) ofm).getData();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002174 if (data.length > 14) {
Saurav Dasfcdad072014-08-13 14:15:21 -07002175 if (((data[12] == (byte) 0x88) &&
2176 (data[13] == (byte) 0xcc)) ||
2177 ((data[12] == (byte) 0x89) &&
2178 (data[13] == (byte) 0x42))) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002179 lldps_allowed++;
2180 packets_allowed++;
2181 break;
2182 }
2183 }
2184 packets_dropped++;
2185 continue;
2186 default:
2187 // Load not high, go ahead and process msg
2188 packets_allowed++;
2189 break;
2190 }
2191 break;
2192 default:
2193 // Process all non-packet-ins
2194 packets_allowed++;
2195 break;
2196 }
2197 }
2198
2199 // Do the actual packet processing
2200 state.processOFMessage(this, ofm);
2201
Saurav Dasfcdad072014-08-13 14:15:21 -07002202 } catch (Exception ex) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002203 // We are the last handler in the stream, so run the
2204 // exception through the channel again by passing in
2205 // ctx.getChannel().
2206 Channels.fireExceptionCaught(ctx.getChannel(), ex);
2207 }
2208 }
2209
2210 if (loadlevel != LoadMonitor.LoadLevel.OK) {
2211 if (log.isDebugEnabled()) {
2212 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002213 "Overload: Detected {}, packets dropped={}",
2214 loadlevel.toString(), packets_dropped);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002215 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002216 "Overload: Packets allowed={} (LLDP/BDDPs allowed={})",
2217 packets_allowed, lldps_allowed);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002218 }
2219 }
2220 }
2221 else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002222 // Channels.fireExceptionCaught(ctx.getChannel(),
2223 // new
2224 // AssertionError("Message received from Channel is not a list"));
2225 // TODO: Pankaj: move the counters using ONOS metrics implementation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002226
2227 counters.messageReceived.updateCounterNoFlush();
2228 state.processOFMessage(this, (OFMessage) e.getMessage());
2229 }
2230
2231 // Flush all thread local queues etc. generated by this train
2232 // of messages.
2233 this.controller.flushAll();
2234 }
2235
Saurav Dasfcdad072014-08-13 14:15:21 -07002236 // *************************
2237 // Channel utility methods
2238 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002239
2240 /**
2241 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07002242 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002243 * @return true if the handshake is complete
2244 */
2245 public boolean isHandshakeComplete() {
2246 return this.state.isHandshakeComplete();
2247 }
2248
2249 private void dispatchMessage(OFMessage m) throws IOException {
2250 // handleMessage will count
2251 this.controller.handleMessage(this.sw, m, null);
2252 }
2253
2254 /**
2255 * Return a string describing this switch based on the already available
2256 * information (DPID and/or remote socket)
Jonathan Hart6eda2302014-08-14 14:57:03 -07002257 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002258 * @return
2259 */
2260 private String getSwitchInfoString() {
2261 if (sw != null)
2262 return sw.toString();
2263 String channelString;
2264 if (channel == null || channel.getRemoteAddress() == null) {
2265 channelString = "?";
2266 } else {
2267 channelString = channel.getRemoteAddress().toString();
2268 }
2269 String dpidString;
2270 if (featuresReply == null) {
2271 dpidString = "?";
2272 } else {
2273 dpidString = featuresReply.getDatapathId().toString();
2274 }
2275 return String.format("[%s DPID[%s]]", channelString, dpidString);
2276 }
2277
2278 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002279 * Update the channels state. Only called from the state machine. TODO:
2280 * enforce restricted state transitions
Jonathan Hart6eda2302014-08-14 14:57:03 -07002281 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002282 * @param state
2283 */
2284 private void setState(ChannelState state) {
2285 this.state = state;
2286 }
2287
2288 /**
2289 * Send hello message to the switch using the handshake transactions ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002290 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002291 * @throws IOException
2292 */
2293 private void sendHandshakeHelloMessage() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07002294 // The OF protocol requires us to start things off by sending the
2295 // highest
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002296 // version of the protocol supported.
2297
Saurav Dasfcdad072014-08-13 14:15:21 -07002298 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3
2299 // (ofp_version=0x04)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002300 // see Sec. 7.5.1 of the OF1.3.4 spec
Jonathan Hart6eda2302014-08-14 14:57:03 -07002301 OFHello hello;
2302 if (useOnly10) {
2303 hello = factory10.buildHello().setXid(handshakeTransactionIds--).build();
2304 log.info("Sending OF_10 Hello to {}", channel.getRemoteAddress());
2305 } else {
2306 U32 bitmap = U32.ofRaw(0x00000012);
2307 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
2308 .setBitmaps(Collections.singletonList(bitmap))
2309 .build();
2310 OFHello.Builder mb = factory13.buildHello()
2311 .setXid(this.handshakeTransactionIds--)
2312 .setElements(Collections.singletonList(hem));
2313 hello = mb.build();
2314 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
2315 }
2316
2317 channel.write(Collections.singletonList(hello));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002318 }
2319
2320 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002321 * Send featuresRequest msg to the switch using the handshake transactions
2322 * ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002323 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002324 * @throws IOException
2325 */
2326 private void sendHandshakeFeaturesRequestMessage() throws IOException {
2327 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2328 OFMessage m = factory.buildFeaturesRequest()
2329 .setXid(this.handshakeTransactionIds--)
2330 .build();
2331 channel.write(Collections.singletonList(m));
2332 }
2333
2334 private void setSwitchRole(Role role) {
2335 sw.setRole(role);
2336 }
2337
2338 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002339 * Send the configuration requests to tell the switch we want full packets
Jonathan Hart6eda2302014-08-14 14:57:03 -07002340 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002341 * @throws IOException
2342 */
2343 private void sendHandshakeSetConfig() throws IOException {
2344 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Saurav Dasfcdad072014-08-13 14:15:21 -07002345 // log.debug("Sending CONFIG_REQUEST to {}",
2346 // channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002347 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
2348
2349 // Ensure we receive the full packet via PacketIn
2350 // FIXME: We don't set the reassembly flags.
2351 OFSetConfig sc = factory
2352 .buildSetConfig()
2353 .setMissSendLen((short) 0xffff)
2354 .setXid(this.handshakeTransactionIds--)
2355 .build();
2356 msglist.add(sc);
2357
2358 // Barrier
2359 OFBarrierRequest br = factory
2360 .buildBarrierRequest()
2361 .setXid(this.handshakeTransactionIds--)
2362 .build();
2363 msglist.add(br);
2364
2365 // Verify (need barrier?)
2366 OFGetConfigRequest gcr = factory
2367 .buildGetConfigRequest()
2368 .setXid(this.handshakeTransactionIds--)
2369 .build();
2370 msglist.add(gcr);
2371 channel.write(msglist);
2372 }
2373
2374 /**
2375 * send a description state request
Jonathan Hart6eda2302014-08-14 14:57:03 -07002376 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002377 * @throws IOException
2378 */
2379 private void sendHandshakeDescriptionStatsRequest() throws IOException {
2380 // Get Description to set switch-specific flags
2381 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2382 OFDescStatsRequest dreq = factory
2383 .buildDescStatsRequest()
2384 .setXid(handshakeTransactionIds--)
2385 .build();
2386 channel.write(Collections.singletonList(dreq));
2387 }
2388
2389 private void sendHandshakeOFPortDescRequest() throws IOException {
2390 // Get port description for 1.3 switch
2391 OFPortDescStatsRequest preq = factory13
2392 .buildPortDescStatsRequest()
2393 .setXid(handshakeTransactionIds--)
2394 .build();
2395 channel.write(Collections.singletonList(preq));
2396 }
2397
2398 /**
2399 * Read switch properties from storage and set switch attributes accordingly
2400 */
2401 private void readPropertyFromStorage() {
2402 // XXX This is a placeholder for switch configuration
2403 }
2404
2405 ChannelState getStateForTesting() {
2406 return state;
2407 }
2408
Saurav Dascc3e35f2014-10-10 15:33:32 -07002409 public String getChannelSwitchInfo() {
2410 return sw.getStringId();
2411 }
2412
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002413 void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) {
2414 roleChanger = new RoleChanger(roleTimeoutMs);
2415 }
2416
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002417}