blob: aa238521eda96dbf051ddccf85db4a1e44e7ad19 [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.
80 *
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 Dasfcdad072014-08-13 14:15:21 -070087 private static final long DEFAULT_ROLE_TIMEOUT_MS = 10 * 1000; // 10 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;
116 private OFPortDescStatsReply portDescReply;
117 // 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
Saurav Dasfcdad072014-08-13 14:15:21 -0700126 /**
127 * transaction Ids to use during handshake. Since only one thread calls into
128 * an OFChannelHandler instance, we don't need atomic. We will count down
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700129 */
130 private int handshakeTransactionIds = -1;
131
132 /**
133 * Create a new unconnected OFChannelHandler.
Saurav Dasfcdad072014-08-13 14:15:21 -0700134 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700135 * @param controller
136 */
137 OFChannelHandler(Controller controller) {
138 this.controller = controller;
139 this.counters = controller.getCounters();
140 this.roleChanger = new RoleChanger(DEFAULT_ROLE_TIMEOUT_MS);
141 this.state = ChannelState.INIT;
142 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
143 factory13 = controller.getOFMessageFactory_13();
144 factory10 = controller.getOFMessageFactory_10();
145 controlRequested = Boolean.FALSE;
146 duplicateDpidFound = Boolean.FALSE;
147 }
148
Saurav Dasfcdad072014-08-13 14:15:21 -0700149 // *******************
150 // Role Handling
151 // *******************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700152
153 /**
154 * When we remove a pending role request we use this enum to indicate how we
155 * arrived at the decision. When we send a role request to the switch, we
Saurav Dasfcdad072014-08-13 14:15:21 -0700156 * also use this enum to indicate what we expect back from the switch, so
157 * the role changer can match the reply to our expectation.
158 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700159 * @author gregor, saurav
160 */
161 public enum RoleRecvStatus {
Saurav Dasfcdad072014-08-13 14:15:21 -0700162 /**
163 * The switch returned an error indicating that roles are not supported
164 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700165 UNSUPPORTED,
166 /** The request timed out */
167 NO_REPLY,
168 /** The reply was old, there is a newer request pending */
169 OLD_REPLY,
170 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700171 * The reply's role matched the role that this controller set in the
172 * request message - invoked either initially at startup or to reassert
173 * current role
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700174 */
175 MATCHED_CURRENT_ROLE,
176 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700177 * The reply's role matched the role that this controller set in the
178 * request message - this is the result of a callback from the global
179 * registry, followed by a role request sent to the switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700180 */
181 MATCHED_SET_ROLE,
182 /**
183 * The reply's role was a response to the query made by this controller
184 */
185 REPLY_QUERY,
Saurav Dasfcdad072014-08-13 14:15:21 -0700186 /**
187 * We received a role reply message from the switch but the expectation
188 * was unclear, or there was no expectation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700189 */
190 OTHER_EXPECTATION,
191 }
192
193 /**
194 * Forwards to RoleChanger. See there.
Saurav Dasfcdad072014-08-13 14:15:21 -0700195 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700196 * @param role
197 */
198 public void sendRoleRequest(Role role, RoleRecvStatus expectation) {
199 try {
200 roleChanger.sendRoleRequest(role, expectation);
201 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700202 log.error("Disconnecting switch {} due to IO Error: {}",
203 getSwitchInfoString(), e.getMessage());
204 channel.close();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700205 }
206 }
207
208 // XXX S consider if necessary
209 public void disconnectSwitch() {
210 sw.disconnectSwitch();
211 }
212
213 /**
214 * A utility class to handle role requests and replies for this channel.
215 * After a role request is submitted the role changer keeps track of the
Saurav Dasfcdad072014-08-13 14:15:21 -0700216 * pending request, collects the reply (if any) and times out the request if
217 * necessary.
218 *
219 * To simplify role handling we only keep track of the /last/ pending role
220 * reply send to the switch. If multiple requests are pending and we receive
221 * replies for earlier requests we ignore them. However, this way of
222 * handling pending requests implies that we could wait forever if a new
223 * request is submitted before the timeout triggers. If necessary we could
224 * work around that though.
225 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700226 * @author gregor
227 * @author saurav (added support OF1.3 role messages, and expectations)
228 */
229 private class RoleChanger {
230 // indicates that a request is currently pending
231 // needs to be volatile to allow correct double-check idiom
232 private volatile boolean requestPending;
233 // the transaction Id of the pending request
234 private int pendingXid;
235 // the role that's pending
236 private Role pendingRole;
237 // system time in MS when we send the request
238 private long roleSubmitTime;
239 // the timeout to use
240 private final long roleTimeoutMs;
241 // the expectation set by the caller for the returned role
242 private RoleRecvStatus expectation;
243
244 public RoleChanger(long roleTimeoutMs) {
245 this.requestPending = false;
246 this.roleSubmitTime = 0;
247 this.pendingXid = -1;
248 this.pendingRole = null;
249 this.roleTimeoutMs = roleTimeoutMs;
250 this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
251 }
252
253 /**
254 * Send NX role request message to the switch requesting the specified
255 * role.
Saurav Dasfcdad072014-08-13 14:15:21 -0700256 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700257 * @param sw switch to send the role request message to
258 * @param role role to request
259 */
260 private int sendNxRoleRequest(Role role) throws IOException {
261 // Convert the role enum to the appropriate role to send
262 OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
263 switch (role) {
264 case MASTER:
265 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
266 break;
267 case SLAVE:
268 case EQUAL:
269 default:
270 // ensuring that the only two roles sent to 1.0 switches with
271 // Nicira role support, are MASTER and SLAVE
272 roleToSend = OFNiciraControllerRole.ROLE_SLAVE;
273 log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
274 }
275 int xid = sw.getNextTransactionId();
276 OFExperimenter roleRequest = factory10
277 .buildNiciraControllerRoleRequest()
278 .setXid(xid)
279 .setRole(roleToSend)
280 .build();
281 sw.write(Collections.<OFMessage>singletonList(roleRequest),
Saurav Dasfcdad072014-08-13 14:15:21 -0700282 new FloodlightContext());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700283 return xid;
284 }
285
286 private int sendOF13RoleRequest(Role role) throws IOException {
287 // Convert the role enum to the appropriate role to send
288 OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
289 switch (role) {
290 case EQUAL:
291 roleToSend = OFControllerRole.ROLE_EQUAL;
292 break;
293 case MASTER:
294 roleToSend = OFControllerRole.ROLE_MASTER;
295 break;
296 case SLAVE:
297 roleToSend = OFControllerRole.ROLE_SLAVE;
298 break;
299 default:
300 log.warn("Sending default role.noChange to switch {}."
301 + " Should only be used for queries.", sw);
302 }
303
304 int xid = sw.getNextTransactionId();
305 OFRoleRequest rrm = factory13
306 .buildRoleRequest()
307 .setRole(roleToSend)
308 .setXid(xid)
309 .setGenerationId(sw.getNextGenerationId())
310 .build();
311 sw.write(rrm, null);
312 return xid;
313 }
314
315 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700316 * Send a role request with the given role to the switch and update the
317 * pending request and timestamp. Sends an OFPT_ROLE_REQUEST to an OF1.3
318 * switch, OR Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured
319 * to support it in the IOFSwitch driver. If not supported, this method
320 * sends nothing and returns 'false'. The caller should take appropriate
321 * action.
322 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700323 * One other optimization we do here is that for OF1.0 switches with
324 * Nicira role message support, we force the Role.EQUAL to become
Saurav Dasfcdad072014-08-13 14:15:21 -0700325 * Role.SLAVE, as there is no defined behavior for the Nicira role
326 * OTHER. We cannot expect it to behave like SLAVE. We don't have this
327 * problem with OF1.3 switches, because Role.EQUAL is well defined and
328 * we can simulate SLAVE behavior by using ASYNC messages.
329 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700330 * @param role
331 * @throws IOException
Saurav Dasfcdad072014-08-13 14:15:21 -0700332 * @returns false if and only if the switch does not support
333 * role-request messages, according to the switch driver; true
334 * otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700335 */
336 synchronized boolean sendRoleRequest(Role role, RoleRecvStatus expectation)
337 throws IOException {
338 this.expectation = expectation;
339
340 if (ofVersion == OFVersion.OF_10) {
341 Boolean supportsNxRole = (Boolean)
342 sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE);
343 if (!supportsNxRole) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700344 log.debug("Switch driver indicates no support for Nicira "
345 + "role request messages. Not sending ...");
346 state.handleUnsentRoleMessage(OFChannelHandler.this, role,
347 expectation);
348 return false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700349 }
350 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
351 // make Role.EQUAL become Role.SLAVE
352 role = (role == Role.EQUAL) ? Role.SLAVE : role;
353 pendingXid = sendNxRoleRequest(role);
354 pendingRole = role;
355 roleSubmitTime = System.currentTimeMillis();
356 requestPending = true;
357 } else {
358 // OF1.3 switch, use OFPT_ROLE_REQUEST message
359 pendingXid = sendOF13RoleRequest(role);
360 pendingRole = role;
361 roleSubmitTime = System.currentTimeMillis();
362 requestPending = true;
363 }
364 return true;
365 }
366
367 /**
368 * Deliver a received role reply.
Saurav Dasfcdad072014-08-13 14:15:21 -0700369 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700370 * Check if a request is pending and if the received reply matches the
Saurav Dasfcdad072014-08-13 14:15:21 -0700371 * the expected pending reply (we check both role and xid) we set the
372 * role for the switch/channel.
373 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700374 * If a request is pending but doesn't match the reply we ignore it, and
375 * return
Saurav Dasfcdad072014-08-13 14:15:21 -0700376 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700377 * If no request is pending we disconnect with a SwitchStateException
Saurav Dasfcdad072014-08-13 14:15:21 -0700378 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700379 * @param RoleReplyInfo information about role-reply in format that
Saurav Dasfcdad072014-08-13 14:15:21 -0700380 * controller can understand.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700381 * @throws SwitchStateException if no request is pending
382 */
383 synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
384 throws SwitchStateException {
385 if (!requestPending) {
386 Role currentRole = (sw != null) ? sw.getRole() : null;
387 if (currentRole != null) {
388 if (currentRole == rri.getRole()) {
389 // Don't disconnect if the role reply we received is
390 // for the same role we are already in.
391 log.debug("Received unexpected RoleReply from "
392 + "Switch: {} in State: {}. "
393 + "Role in reply is same as current role of this "
394 + "controller for this sw. Ignoring ...",
395 getSwitchInfoString(), state.toString());
396 return RoleRecvStatus.OTHER_EXPECTATION;
397 } else {
398 String msg = String.format("Switch: [%s], State: [%s], "
399 + "received unexpected RoleReply[%s]. "
400 + "No roles are pending, and this controller's "
401 + "current role:[%s] does not match reply. "
402 + "Disconnecting switch ... ",
403 OFChannelHandler.this.getSwitchInfoString(),
404 OFChannelHandler.this.state.toString(),
405 rri, currentRole);
406 throw new SwitchStateException(msg);
407 }
408 }
409 log.debug("Received unexpected RoleReply {} from "
410 + "Switch: {} in State: {}. "
411 + "This controller has no current role for this sw. "
412 + "Ignoring ...", new Object[] {rri,
Saurav Dasfcdad072014-08-13 14:15:21 -0700413 getSwitchInfoString(), state});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700414 return RoleRecvStatus.OTHER_EXPECTATION;
415 }
416
417 int xid = (int) rri.getXid();
418 Role role = rri.getRole();
Saurav Dasfcdad072014-08-13 14:15:21 -0700419 // XXX S should check generation id meaningfully and other cases of
420 // expectations
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700421 // U64 genId = rri.getGenId();
422
423 if (pendingXid != xid) {
424 log.debug("Received older role reply from " +
425 "switch {} ({}). Ignoring. " +
426 "Waiting for {}, xid={}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700427 new Object[] {getSwitchInfoString(), rri,
428 pendingRole, pendingXid});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700429 return RoleRecvStatus.OLD_REPLY;
430 }
431
432 if (pendingRole == role) {
Saurav Das6de23322014-08-07 18:51:19 -0700433 requestPending = false; // we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700434 log.debug("Received role reply message from {} that matched "
435 + "expected role-reply {} with expectations {}",
436 new Object[] {getSwitchInfoString(), role, expectation});
437 counters.roleReplyReceived.updateCounterWithFlush();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700438 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
439 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
440 return expectation;
441 } else {
442 return RoleRecvStatus.OTHER_EXPECTATION;
443 }
444 }
445
446 // if xids match but role's don't, perhaps its a query (OF1.3)
Saurav Das6de23322014-08-07 18:51:19 -0700447 if (expectation == RoleRecvStatus.REPLY_QUERY) {
448 requestPending = false; // again we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700449 return expectation;
Saurav Das6de23322014-08-07 18:51:19 -0700450 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700451
Saurav Das6de23322014-08-07 18:51:19 -0700452 // It is not clear what this role-reply was about, since it is not
453 // a query and it did not match the pendingRole. But since the xid's
454 // matched, we state that we received what we were waiting for, and
455 // let the caller handle it
456 requestPending = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700457 return RoleRecvStatus.OTHER_EXPECTATION;
458 }
459
460 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700461 * Called if we receive an error message. If the xid matches the pending
462 * request we handle it otherwise we ignore it.
463 *
464 * Note: since we only keep the last pending request we might get error
465 * messages for earlier role requests that we won't be able to handle
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700466 */
467 synchronized RoleRecvStatus deliverError(OFErrorMsg error)
468 throws SwitchStateException {
469 if (!requestPending) {
470 log.debug("Received an error msg from sw {}, but no pending "
471 + "requests in role-changer; not handling ...",
472 getSwitchInfoString());
473 return RoleRecvStatus.OTHER_EXPECTATION;
474 }
475 if (pendingXid != error.getXid()) {
476 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
477 log.debug("Received an error msg from sw {} for a role request,"
478 + " but not for pending request in role-changer; "
479 + " ignoring error {} ...",
480 getSwitchInfoString(), error);
481 }
482 return RoleRecvStatus.OTHER_EXPECTATION;
483 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700484 // it is an error related to a currently pending role request
485 // message
486 requestPending = false; // we got a response, even though it is an
487 // error
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700488 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
489 counters.roleReplyErrorUnsupported.updateCounterWithFlush();
490 log.error("Received a error msg {} from sw {} in state {} for "
491 + "pending role request {}. Switch driver indicates "
492 + "role-messaging is supported. Possible issues in "
493 + "switch driver configuration?", new Object[] {
Saurav Dasfcdad072014-08-13 14:15:21 -0700494 ((OFBadRequestErrorMsg) error).toString(),
495 getSwitchInfoString(), state, pendingRole
496 });
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700497 return RoleRecvStatus.UNSUPPORTED;
498 }
499
500 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
501 OFRoleRequestFailedErrorMsg rrerr =
502 (OFRoleRequestFailedErrorMsg) error;
503 switch (rrerr.getCode()) {
504 case BAD_ROLE:
505 // switch says that current-role-req has bad role?
506 // for now we disconnect
507 // fall-thru
508 case STALE:
509 // switch says that current-role-req has stale gen-id?
510 // for now we disconnect
511 // fall-thru
512 case UNSUP:
513 // switch says that current-role-req has role that
514 // cannot be supported? for now we disconnect
515 String msgx = String.format("Switch: [%s], State: [%s], "
516 + "received Error to for pending role request [%s]. "
517 + "Error:[%s]. Disconnecting switch ... ",
518 OFChannelHandler.this.getSwitchInfoString(),
519 OFChannelHandler.this.state.toString(),
520 pendingRole, rrerr);
521 throw new SwitchStateException(msgx);
522 default:
523 break;
524 }
525 }
526
Saurav Dasfcdad072014-08-13 14:15:21 -0700527 // This error message was for a role request message but we dont
528 // know
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700529 // how to handle errors for nicira role request messages
530 return RoleRecvStatus.OTHER_EXPECTATION;
531 }
532
533 /**
534 * Check if a pending role request has timed out.
Saurav Dasfcdad072014-08-13 14:15:21 -0700535 *
Saurav Das6de23322014-08-07 18:51:19 -0700536 * @throws SwitchStateException
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700537 */
Saurav Das6de23322014-08-07 18:51:19 -0700538 void checkTimeout() throws SwitchStateException {
539 if (!requestPending) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700540 return;
Saurav Das6de23322014-08-07 18:51:19 -0700541 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700542 synchronized (this) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700543 if (!requestPending)
544 return;
545 long now = System.currentTimeMillis();
546 if (now - roleSubmitTime > roleTimeoutMs) {
547 // timeout triggered.
548 counters.roleReplyTimeout.updateCounterWithFlush();
Saurav Das6de23322014-08-07 18:51:19 -0700549 state.handleTimedOutRoleReply(OFChannelHandler.this, pendingRole);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700550 }
551 }
552 }
553
554 }
555
Saurav Dasfcdad072014-08-13 14:15:21 -0700556 // *************************
557 // Channel State Machine
558 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700559
560 /**
561 * The state machine for handling the switch/channel state. All state
Saurav Dasfcdad072014-08-13 14:15:21 -0700562 * transitions should happen from within the state machine (and not from
563 * other parts of the code)
564 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700565 * @author gregor
Saurav Dasfcdad072014-08-13 14:15:21 -0700566 * @author saurav (modified to handle 1.0 & 1.3 switches, EQUAL state,
567 * role-handling )
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700568 */
569 enum ChannelState {
570 /**
571 * Initial state before channel is connected.
572 */
573 INIT(false) {
574 @Override
575 void processOFMessage(OFChannelHandler h, OFMessage m)
576 throws IOException, SwitchStateException {
577 illegalMessageReceived(h, m);
578 }
579
580 @Override
581 void processOFError(OFChannelHandler h, OFErrorMsg m)
582 throws IOException {
583 // need to implement since its abstract but it will never
584 // be called
585 }
586
587 @Override
588 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
589 throws IOException {
590 unhandledMessageReceived(h, m);
591 }
592 },
593
594 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700595 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the
596 * switch. Once we receive the reply, we decide on OF 1.3 or 1.0 switch
597 * - no other protocol version is accepted. We send an OFFeaturesRequest
598 * depending on the protocol version selected Next state is
599 * WAIT_FEATURES_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700600 */
601 WAIT_HELLO(false) {
602 @Override
603 void processOFHello(OFChannelHandler h, OFHello m)
604 throws IOException {
605 // TODO We could check for the optional bitmap, but for now
606 // we are just checking the version number.
607 if (m.getVersion() == OFVersion.OF_13) {
608 log.info("Received {} Hello from {}", m.getVersion(),
609 h.channel.getRemoteAddress());
610 h.ofVersion = OFVersion.OF_13;
611 } else if (m.getVersion() == OFVersion.OF_10) {
612 log.info("Received {} Hello from {} - switching to OF "
613 + "version 1.0", m.getVersion(),
614 h.channel.getRemoteAddress());
615 h.ofVersion = OFVersion.OF_10;
616 } else {
617 log.error("Received Hello of version {} from switch at {}. "
618 + "This controller works with OF1.0 and OF1.3 "
619 + "switches. Disconnecting switch ...",
620 m.getVersion(), h.channel.getRemoteAddress());
621 h.channel.disconnect();
622 return;
623 }
624 h.sendHandshakeFeaturesRequestMessage();
625 h.setState(WAIT_FEATURES_REPLY);
626 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700627
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700628 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700629 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700630 throws IOException, SwitchStateException {
631 illegalMessageReceived(h, m);
632 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700633
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700634 @Override
635 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700636 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700637 throws IOException, SwitchStateException {
638 illegalMessageReceived(h, m);
639 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700640
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700641 @Override
642 void processOFError(OFChannelHandler h, OFErrorMsg m) {
643 logErrorDisconnect(h, m);
644 }
645
646 @Override
647 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
648 throws IOException {
649 unhandledMessageReceived(h, m);
650 }
651 },
652
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700653 /**
654 * We are waiting for a features reply message. Once we receive it, the
Saurav Dasfcdad072014-08-13 14:15:21 -0700655 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0, we
656 * send a SetConfig request, barrier, and GetConfig request and the next
657 * state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700658 * request and the next state is WAIT_PORT_DESC_REPLY.
659 */
660 WAIT_FEATURES_REPLY(false) {
661 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700662 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700663 throws IOException {
664 h.thisdpid = m.getDatapathId().getLong();
665 log.info("Received features reply for switch at {} with dpid {}",
666 h.getSwitchInfoString(), h.thisdpid);
Saurav Dasfcdad072014-08-13 14:15:21 -0700667 // update the controller about this connected switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700668 boolean success = h.controller.addConnectedSwitch(
669 h.thisdpid, h);
670 if (!success) {
671 disconnectDuplicate(h);
672 return;
673 }
674
Saurav Dasfcdad072014-08-13 14:15:21 -0700675 h.featuresReply = m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700676 if (h.ofVersion == OFVersion.OF_10) {
677 h.sendHandshakeSetConfig();
678 h.setState(WAIT_CONFIG_REPLY);
679 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -0700680 // version is 1.3, must get switchport information
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700681 h.sendHandshakeOFPortDescRequest();
682 h.setState(WAIT_PORT_DESC_REPLY);
683 }
684 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700685
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700686 @Override
687 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700688 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700689 throws IOException, SwitchStateException {
690 illegalMessageReceived(h, m);
691 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700692
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700693 @Override
694 void processOFError(OFChannelHandler h, OFErrorMsg m) {
695 logErrorDisconnect(h, m);
696 }
697
698 @Override
699 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
700 throws IOException {
701 unhandledMessageReceived(h, m);
702 }
703 },
704
705 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700706 * We are waiting for a description of the 1.3 switch ports. Once
707 * received, we send a SetConfig request Next State is WAIT_CONFIG_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700708 */
709 WAIT_PORT_DESC_REPLY(false) {
710
711 @Override
712 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
713 throws SwitchStateException {
714 // Read port description
715 if (m.getStatsType() != OFStatsType.PORT_DESC) {
716 log.warn("Expecting port description stats but received stats "
717 + "type {} from {}. Ignoring ...", m.getStatsType(),
718 h.channel.getRemoteAddress());
719 return;
720 }
721 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
722 log.warn("Stats reply indicates more stats from sw {} for "
723 + "port description - not currently handled",
724 h.getSwitchInfoString());
725 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700726 h.portDescReply = (OFPortDescStatsReply) m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700727 log.info("Received port desc reply for switch at {}",
728 h.getSwitchInfoString());
729 try {
730 h.sendHandshakeSetConfig();
731 } catch (IOException e) {
732 log.error("Unable to send setConfig after PortDescReply. "
733 + "Error: {}", e.getMessage());
734 }
735 h.setState(WAIT_CONFIG_REPLY);
736 }
737
738 @Override
739 void processOFError(OFChannelHandler h, OFErrorMsg m)
740 throws IOException, SwitchStateException {
741 logErrorDisconnect(h, m);
742
743 }
744
745 @Override
746 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
747 throws IOException, SwitchStateException {
748 unhandledMessageReceived(h, m);
749
750 }
751 },
752
753 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700754 * We are waiting for a config reply message. Once we receive it we send
755 * a DescriptionStatsRequest to the switch. Next state:
756 * WAIT_DESCRIPTION_STAT_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700757 */
758 WAIT_CONFIG_REPLY(false) {
759 @Override
760 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -0700761 @LogMessageDoc(level = "WARN",
762 message = "Config Reply from {switch} has " +
763 "miss length set to {length}",
764 explanation = "The controller requires that the switch " +
765 "use a miss length of 0xffff for correct " +
766 "function",
767 recommendation = "Use a different switch to ensure " +
768 "correct function")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700769 })
770 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
771 throws IOException {
772 if (m.getMissSendLen() == 0xffff) {
773 log.trace("Config Reply from switch {} confirms "
774 + "miss length set to 0xffff",
775 h.getSwitchInfoString());
776 } else {
777 // FIXME: we can't really deal with switches that don't send
778 // full packets. Shouldn't we drop the connection here?
779 log.warn("Config Reply from switch {} has"
780 + "miss length set to {}",
781 h.getSwitchInfoString(),
782 m.getMissSendLen());
783 }
784 h.sendHandshakeDescriptionStatsRequest();
785 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
786 }
787
788 @Override
789 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
790 // do nothing;
791 }
792
793 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700794 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700795 throws IOException, SwitchStateException {
796 illegalMessageReceived(h, m);
797 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700798
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700799 @Override
800 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700801 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700802 throws IOException, SwitchStateException {
803 log.error("Received multipart(stats) message sub-type {}",
804 m.getStatsType());
805 illegalMessageReceived(h, m);
806 }
807
808 @Override
809 void processOFError(OFChannelHandler h, OFErrorMsg m) {
810 logErrorDisconnect(h, m);
811 }
812
813 @Override
814 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
815 throws IOException {
816 h.pendingPortStatusMsg.add(m);
817 }
818 },
819
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700820 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700821 * We are waiting for a OFDescriptionStat message from the switch. Once
822 * we receive any stat message we try to parse it. If it's not a
823 * description stats message we disconnect. If its the expected
824 * description stats message, we: - use the switch driver to bind the
825 * switch and get an IOFSwitch instance - setup the IOFSwitch instance -
826 * add switch to FloodlightProvider(Controller) and send the initial
827 * role request to the switch. Next state: WAIT_INITIAL_ROLE In the
828 * typical case, where switches support role request messages the next
829 * state is where we expect the role reply message. In the special case
830 * that where the switch does not support any kind of role request
831 * messages, we don't send a role message, but we do request mastership
832 * from the registry service. This controller should become master once
833 * we hear back from the registry service. All following states will
834 * have a h.sw instance!
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700835 */
836 WAIT_DESCRIPTION_STAT_REPLY(false) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700837 @LogMessageDoc(message = "Switch {switch info} bound to class " +
838 "{switch driver}, description {switch description}",
839 explanation = "The specified switch has been bound to " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700840 "a switch driver based on the switch description" +
841 "received from the switch")
842 @Override
843 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
844 throws SwitchStateException {
845 // Read description, if it has been updated
846 if (m.getStatsType() != OFStatsType.DESC) {
847 log.warn("Expecting Description stats but received stats "
848 + "type {} from {}. Ignoring ...", m.getStatsType(),
849 h.channel.getRemoteAddress());
850 return;
851 }
852 log.info("Received switch description reply from switch at {}",
853 h.channel.getRemoteAddress());
854 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasfcdad072014-08-13 14:15:21 -0700855 // Here is where we differentiate between different kinds of
856 // switches
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700857 h.sw = h.controller.getOFSwitchInstance(drep, h.ofVersion);
858 // set switch information
859 h.sw.setOFVersion(h.ofVersion);
860 ((OFSwitchImplBase) h.sw).setFeaturesReply(h.featuresReply);
861 ((OFSwitchImplBase) h.sw).setPortDescReply(h.portDescReply);
862 h.sw.setConnected(true);
863 h.sw.setChannel(h.channel);
864 h.sw.setFloodlightProvider(h.controller);
865 h.sw.setThreadPoolService(h.controller.getThreadPoolService());
866 try {
867 h.sw.setDebugCounterService(h.controller.getDebugCounter());
868 } catch (CounterException e) {
869 h.counters.switchCounterRegistrationFailed
870 .updateCounterNoFlush();
871 log.warn("Could not register counters for switch {} ",
Saurav Dasfcdad072014-08-13 14:15:21 -0700872 h.getSwitchInfoString(), e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700873 }
874
875 log.info("Switch {} bound to class {}, description {}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700876 new Object[] {h.sw, h.sw.getClass(), drep});
877 // Put switch in EQUAL mode until we hear back from the global
878 // registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700879 log.debug("Setting new switch {} to EQUAL and sending Role request",
880 h.sw.getStringId());
881 h.setSwitchRole(Role.EQUAL);
882 try {
883 boolean supportsRRMsg = h.roleChanger.sendRoleRequest(Role.EQUAL,
884 RoleRecvStatus.MATCHED_CURRENT_ROLE);
885 if (!supportsRRMsg) {
886 log.warn("Switch {} does not support role request messages "
887 + "of any kind. No role messages were sent. "
888 + "This controller instance SHOULD become MASTER "
889 + "from the registry process. ",
890 h.getSwitchInfoString());
891 }
892 h.setState(WAIT_INITIAL_ROLE);
893 // request control of switch from global registry -
894 // necessary even if this is the only controller the
895 // switch is connected to.
896 h.controller.submitRegistryRequest(h.sw.getId());
897 } catch (IOException e) {
898 log.error("Exception when sending role request: {} ",
899 e.getMessage());
900 // FIXME shouldn't we disconnect?
901 }
902 }
903
904 @Override
905 void processOFError(OFChannelHandler h, OFErrorMsg m) {
906 logErrorDisconnect(h, m);
907 }
908
909 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700910 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700911 throws IOException, SwitchStateException {
912 illegalMessageReceived(h, m);
913 }
914
915 @Override
916 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
917 throws IOException {
918 h.pendingPortStatusMsg.add(m);
919 }
920 },
921
922 /**
923 * We are waiting for a role reply message in response to a role request
924 * sent after hearing back from the registry service -- OR -- we are
925 * just waiting to hear back from the registry service in the case that
926 * the switch does not support role messages. If completed successfully,
Saurav Dasfcdad072014-08-13 14:15:21 -0700927 * the controller's role for this switch will be set here. Before we
928 * move to the state corresponding to the role, we allow the switch
929 * specific driver to complete its configuration. This configuration
930 * typically depends on the role the controller is playing for this
931 * switch. And so we set the switch role (for 'this' controller) before
932 * we start the driver-sub-handshake. Next State:
933 * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700934 */
935 WAIT_INITIAL_ROLE(false) {
936 @Override
937 void processOFError(OFChannelHandler h, OFErrorMsg m)
938 throws SwitchStateException {
939 // role changer will ignore the error if it isn't for it
940 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
941 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
942 logError(h, m);
943 }
944 }
945
946 @Override
947 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
948 throws IOException, SwitchStateException {
949 Role role = extractNiciraRoleReply(h, m);
950 // If role == null it means the vendor (experimenter) message
951 // wasn't really a Nicira role reply. We ignore this case.
952 if (role != null) {
953 RoleReplyInfo rri = new RoleReplyInfo(role, null, m.getXid());
954 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
955 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
956 setRoleAndStartDriverHandshake(h, rri.getRole());
957 } // else do nothing - wait for the correct expected reply
958 } else {
959 unhandledMessageReceived(h, m);
960 }
961 }
962
963 @Override
964 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
965 throws SwitchStateException, IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -0700966 RoleReplyInfo rri = extractOFRoleReply(h, m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700967 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
968 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
969 setRoleAndStartDriverHandshake(h, rri.getRole());
970 } // else do nothing - wait for the correct expected reply
971 }
972
973 @Override
974 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -0700975 RoleRecvStatus expectation) throws IOException {
976 // typically this is triggered for a switch where role messages
977 // are not supported - we confirm that the role being set is
978 // master and move to the next state
979 if (expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
980 if (role == Role.MASTER) {
981 setRoleAndStartDriverHandshake(h, role);
982 } else {
983 log.error("Expected MASTER role from registry for switch "
984 + "which has no support for role-messages."
985 + "Received {}. It is possible that this switch "
986 + "is connected to other controllers, in which "
987 + "case it should support role messages - not "
988 + "moving forward.", role);
989 }
990 } // else do nothing - wait to hear back from registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700991
992 }
993
994 private void setRoleAndStartDriverHandshake(OFChannelHandler h,
995 Role role) throws IOException {
996 h.setSwitchRole(role);
997 h.sw.startDriverHandshake();
998 if (h.sw.isDriverHandshakeComplete()) {
999 Role mySwitchRole = h.sw.getRole();
1000 if (mySwitchRole == Role.MASTER) {
1001 log.info("Switch-driver sub-handshake complete. "
1002 + "Activating switch {} with Role: MASTER",
1003 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001004 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001005 boolean success = h.controller.addActivatedMasterSwitch(
1006 h.sw.getId(), h.sw);
1007 if (!success) {
1008 disconnectDuplicate(h);
1009 return;
1010 }
1011 h.setState(MASTER);
1012 } else {
1013 log.info("Switch-driver sub-handshake complete. "
1014 + "Activating switch {} with Role: EQUAL",
1015 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001016 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001017 boolean success = h.controller.addActivatedEqualSwitch(
1018 h.sw.getId(), h.sw);
1019 if (!success) {
1020 disconnectDuplicate(h);
1021 return;
1022 }
1023 h.setState(EQUAL);
1024 }
1025 } else {
1026 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
1027 }
1028 }
1029
1030 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001031 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001032 throws IOException, SwitchStateException {
1033 illegalMessageReceived(h, m);
1034 }
1035
1036 @Override
1037 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1038 throws SwitchStateException {
1039 illegalMessageReceived(h, m);
1040 }
1041
1042 @Override
1043 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1044 throws IOException, SwitchStateException {
1045 h.pendingPortStatusMsg.add(m);
1046
1047 }
1048 },
1049
1050 /**
1051 * We are waiting for the respective switch driver to complete its
Saurav Dasfcdad072014-08-13 14:15:21 -07001052 * configuration. Notice that we do not consider this to be part of the
1053 * main switch-controller handshake. But we do consider it as a step
1054 * that comes before we declare the switch as available to the
1055 * controller. Next State: depends on the role of this controller for
1056 * this switch - either MASTER or EQUAL.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001057 */
1058 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
1059
1060 @Override
1061 void processOFError(OFChannelHandler h, OFErrorMsg m)
1062 throws IOException {
1063 // will never be called. We override processOFMessage
1064 }
1065
1066 @Override
1067 void processOFMessage(OFChannelHandler h, OFMessage m)
1068 throws IOException {
1069 if (m.getType() == OFType.ECHO_REQUEST)
Saurav Dasfcdad072014-08-13 14:15:21 -07001070 processOFEchoRequest(h, (OFEchoRequest) m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001071 else {
1072 // FIXME: other message to handle here?
1073 h.sw.processDriverHandshakeMessage(m);
1074 if (h.sw.isDriverHandshakeComplete()) {
1075 // consult the h.sw role and goto that state
1076 Role mySwitchRole = h.sw.getRole();
1077 if (mySwitchRole == Role.MASTER) {
1078 log.info("Switch-driver sub-handshake complete. "
1079 + "Activating switch {} with Role: MASTER",
1080 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001081 handlePendingPortStatusMessages(h); // before
1082 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001083 boolean success = h.controller.addActivatedMasterSwitch(
1084 h.sw.getId(), h.sw);
1085 if (!success) {
1086 disconnectDuplicate(h);
1087 return;
1088 }
1089 h.setState(MASTER);
1090 } else {
1091 log.info("Switch-driver sub-handshake complete. "
1092 + "Activating switch {} with Role: EQUAL",
1093 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001094 handlePendingPortStatusMessages(h); // before
1095 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001096 boolean success = h.controller.addActivatedEqualSwitch(
1097 h.sw.getId(), h.sw);
1098 if (!success) {
1099 disconnectDuplicate(h);
1100 return;
1101 }
1102 h.setState(EQUAL);
1103 }
1104 }
1105 }
1106 }
1107
1108 @Override
1109 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1110 throws IOException, SwitchStateException {
1111 h.pendingPortStatusMsg.add(m);
1112 }
1113 },
1114
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001115 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001116 * This controller is in MASTER role for this switch. We enter this
1117 * state after requesting and winning control from the global registry.
1118 * The main handshake as well as the switch-driver sub-handshake is
1119 * complete at this point. // XXX S reconsider below In the (near)
1120 * future we may deterministically assign controllers to switches at
1121 * startup. We only leave this state if the switch disconnects or if we
1122 * send a role request for SLAVE /and/ receive the role reply for SLAVE.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001123 */
1124 MASTER(true) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001125 @LogMessageDoc(level = "WARN",
1126 message = "Received permission error from switch {} while" +
1127 "being master. Reasserting master role.",
1128 explanation = "The switch has denied an operation likely " +
1129 "indicating inconsistent controller roles",
1130 recommendation = "This situation can occurs transiently during role" +
1131 " changes. If, however, the condition persists or happens" +
1132 " frequently this indicates a role inconsistency. " +
1133 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001134 @Override
1135 void processOFError(OFChannelHandler h, OFErrorMsg m)
1136 throws IOException, SwitchStateException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001137 // first check if the error msg is in response to a role-request
1138 // message
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001139 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1140 if (rrstatus != RoleRecvStatus.OTHER_EXPECTATION) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001141 // rolechanger has handled the error message - we are done
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001142 return;
1143 }
1144
1145 // if we get here, then the error message is for something else
1146 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
1147 ((OFBadRequestErrorMsg) m).getCode() ==
Saurav Dasfcdad072014-08-13 14:15:21 -07001148 OFBadRequestCode.EPERM) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001149 // We are the master controller and the switch returned
1150 // a permission error. This is a likely indicator that
1151 // the switch thinks we are slave. Reassert our
1152 // role
1153 // FIXME: this could be really bad during role transitions
1154 // if two controllers are master (even if its only for
1155 // a brief period). We might need to see if these errors
1156 // persist before we reassert
1157 h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush();
1158 log.warn("Received permission error from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001159 "being master. Reasserting master role.",
1160 h.getSwitchInfoString());
1161 // h.controller.reassertRole(h, Role.MASTER);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001162 // XXX S reassert in role changer or reconsider if all this
1163 // stuff is really needed
1164 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
Saurav Dasfcdad072014-08-13 14:15:21 -07001165 ((OFFlowModFailedErrorMsg) m).getCode() ==
1166 OFFlowModFailedCode.ALL_TABLES_FULL) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001167 h.sw.setTableFull(true);
1168 } else {
1169 logError(h, m);
1170 }
1171 h.dispatchMessage(m);
1172 }
1173
1174 @Override
1175 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001176 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001177 h.sw.deliverStatisticsReply(m);
1178 }
1179
1180 @Override
1181 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1182 throws IOException, SwitchStateException {
1183 Role role = extractNiciraRoleReply(h, m);
1184 if (role == null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001185 // The message wasn't really a Nicira role reply. We just
1186 // dispatch it to the OFMessage listeners in this case.
1187 h.dispatchMessage(m);
1188 return;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001189 }
1190
1191 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Saurav Dasfcdad072014-08-13 14:15:21 -07001192 new RoleReplyInfo(role, null, m.getXid()));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001193 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001194 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001195 }
1196 }
1197
1198 @Override
1199 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1200 throws SwitchStateException, IOException {
1201 RoleReplyInfo rri = extractOFRoleReply(h, m);
1202 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1203 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001204 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001205 }
1206 }
1207
1208 @Override
1209 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1210 throws IOException, SwitchStateException {
1211 handlePortStatusMessage(h, m, true);
1212 h.dispatchMessage(m);
1213 }
1214
1215 @Override
1216 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1217 throws IOException {
1218 h.dispatchMessage(m);
1219 }
1220
1221 @Override
1222 void processOFFlowRemoved(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001223 OFFlowRemoved m) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001224 h.dispatchMessage(m);
1225 }
1226
1227 @Override
1228 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1229 throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001230 h.dispatchMessage(m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001231 }
1232
1233 },
1234
1235 /**
1236 * This controller is in EQUAL role for this switch. We enter this state
1237 * after some /other/ controller instance wins mastership-role over this
1238 * switch. The EQUAL role can be considered the same as the SLAVE role
1239 * if this controller does NOT send commands or packets to the switch.
1240 * This should always be true for OF1.0 switches. XXX S need to enforce.
Saurav Dasfcdad072014-08-13 14:15:21 -07001241 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001242 * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE,
Saurav Dasfcdad072014-08-13 14:15:21 -07001243 * gives us the flexibility that if an app wants to send
1244 * commands/packets to switches, it can, even thought it is running on a
1245 * controller instance that is not in a MASTER role for this switch. Of
1246 * course, it is the job of the app to ensure that commands/packets sent
1247 * by this (EQUAL) controller instance does not clash/conflict with
1248 * commands/packets sent by the MASTER controller for this switch.
1249 * Neither the controller instances, nor the switch provides any kind of
1250 * resolution mechanism should conflicts occur.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001251 */
1252 EQUAL(true) {
1253 @Override
1254 void processOFError(OFChannelHandler h, OFErrorMsg m)
1255 throws IOException, SwitchStateException {
1256 // role changer will ignore the error if it isn't for it
1257 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1258 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
1259 logError(h, m);
1260 h.dispatchMessage(m);
1261 }
1262 }
1263
1264 @Override
1265 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001266 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001267 h.sw.deliverStatisticsReply(m);
1268 }
1269
1270 @Override
1271 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1272 throws IOException, SwitchStateException {
1273 Role role = extractNiciraRoleReply(h, m);
1274 // If role == null it means the message wasn't really a
1275 // Nicira role reply. We ignore it in this state.
1276 if (role != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001277 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001278 new RoleReplyInfo(role, null, m.getXid()));
Saurav Dasfcdad072014-08-13 14:15:21 -07001279 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1280 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001281 }
1282 } else {
1283 unhandledMessageReceived(h, m);
1284 }
1285 }
1286
1287 @Override
1288 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1289 throws SwitchStateException, IOException {
1290 RoleReplyInfo rri = extractOFRoleReply(h, m);
1291 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1292 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001293 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001294 }
1295 }
1296
1297 // XXX S needs more handlers for 1.3 switches in equal role
1298
1299 @Override
1300 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1301 throws IOException, SwitchStateException {
1302 handlePortStatusMessage(h, m, true);
1303 }
1304
1305 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001306 @LogMessageDoc(level = "WARN",
1307 message = "Received PacketIn from switch {} while" +
1308 "being slave. Reasserting slave role.",
1309 explanation = "The switch has receive a PacketIn despite being " +
1310 "in slave role indicating inconsistent controller roles",
1311 recommendation = "This situation can occurs transiently during role" +
1312 " changes. If, however, the condition persists or happens" +
1313 " frequently this indicates a role inconsistency. " +
1314 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001315 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException {
1316 // we don't expect packetIn while slave, reassert we are slave
1317 h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush();
1318 log.warn("Received PacketIn from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001319 "being slave. Reasserting slave role.", h.sw);
1320 // h.controller.reassertRole(h, Role.SLAVE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001321 // XXX reassert in role changer
1322 }
1323 };
1324
1325 private final boolean handshakeComplete;
Saurav Dasfcdad072014-08-13 14:15:21 -07001326
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001327 ChannelState(boolean handshakeComplete) {
1328 this.handshakeComplete = handshakeComplete;
1329 }
1330
1331 /**
1332 * Is this a state in which the handshake has completed?
Saurav Dasfcdad072014-08-13 14:15:21 -07001333 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001334 * @return true if the handshake is complete
1335 */
1336 public boolean isHandshakeComplete() {
1337 return handshakeComplete;
1338 }
1339
1340 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001341 * Get a string specifying the switch connection, state, and message
1342 * received. To be used as message for SwitchStateException or log
1343 * messages
1344 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001345 * @param h The channel handler (to get switch information_
1346 * @param m The OFMessage that has just been received
Saurav Dasfcdad072014-08-13 14:15:21 -07001347 * @param details A string giving more details about the exact nature of
1348 * the problem.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001349 * @return
1350 */
1351 // needs to be protected because enum members are actually subclasses
1352 protected String getSwitchStateMessage(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001353 OFMessage m,
1354 String details) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001355 return String.format("Switch: [%s], State: [%s], received: [%s]"
Saurav Dasfcdad072014-08-13 14:15:21 -07001356 + ", details: %s",
1357 h.getSwitchInfoString(),
1358 this.toString(),
1359 m.getType().toString(),
1360 details);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001361 }
1362
1363 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001364 * We have an OFMessage we didn't expect given the current state and we
1365 * want to treat this as an error. We currently throw an exception that
1366 * will terminate the connection However, we could be more forgiving
1367 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001368 * @param h the channel handler that received the message
1369 * @param m the message
1370 * @throws SwitchStateException
1371 * @throws SwitchStateExeption we always through the execption
1372 */
1373 // needs to be protected because enum members are acutally subclasses
1374 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
1375 throws SwitchStateException {
1376 String msg = getSwitchStateMessage(h, m,
1377 "Switch should never send this message in the current state");
1378 throw new SwitchStateException(msg);
1379
1380 }
1381
1382 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001383 * We have an OFMessage we didn't expect given the current state and we
1384 * want to ignore the message
1385 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001386 * @param h the channel handler the received the message
1387 * @param m the message
1388 */
1389 protected void unhandledMessageReceived(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001390 OFMessage m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001391 h.counters.unhandledMessage.updateCounterNoFlush();
1392 if (log.isDebugEnabled()) {
1393 String msg = getSwitchStateMessage(h, m,
1394 "Ignoring unexpected message");
1395 log.debug(msg);
1396 }
1397 }
1398
1399 /**
1400 * Log an OpenFlow error message from a switch
Saurav Dasfcdad072014-08-13 14:15:21 -07001401 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001402 * @param sw The switch that sent the error
1403 * @param error The error message
1404 */
Saurav Dasfcdad072014-08-13 14:15:21 -07001405 @LogMessageDoc(level = "ERROR",
1406 message = "Error {error type} {error code} from {switch} " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001407 "in state {state}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001408 explanation = "The switch responded with an unexpected error" +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001409 "to an OpenFlow message from the controller",
Saurav Dasfcdad072014-08-13 14:15:21 -07001410 recommendation = "This could indicate improper network operation. " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001411 "If the problem persists restarting the switch and " +
1412 "controller may help."
1413 )
Saurav Dasfcdad072014-08-13 14:15:21 -07001414 protected void logError(OFChannelHandler h, OFErrorMsg error) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001415 log.error("{} from switch {} in state {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001416 new Object[] {
1417 error,
1418 h.getSwitchInfoString(),
1419 this.toString()});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001420 }
1421
1422 /**
1423 * Log an OpenFlow error message from a switch and disconnect the
1424 * channel
Saurav Dasfcdad072014-08-13 14:15:21 -07001425 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001426 * @param sw The switch that sent the error
1427 * @param error The error message
1428 */
1429 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
1430 logError(h, error);
1431 h.channel.disconnect();
1432 }
1433
1434 /**
1435 * log an error message for a duplicate dpid and disconnect this channel
1436 */
1437 protected void disconnectDuplicate(OFChannelHandler h) {
1438 log.error("Duplicated dpid or incompleted cleanup - "
1439 + "disconnecting channel {}", h.getSwitchInfoString());
1440 h.duplicateDpidFound = Boolean.TRUE;
1441 h.channel.disconnect();
1442 }
1443
1444 /**
1445 * Extract the role from an OFVendor message.
Saurav Dasfcdad072014-08-13 14:15:21 -07001446 *
1447 * Extract the role from an OFVendor message if the message is a Nicira
1448 * role reply. Otherwise return null.
1449 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001450 * @param h The channel handler receiving the message
1451 * @param vendorMessage The vendor message to parse.
1452 * @return The role in the message if the message is a Nicira role
Saurav Dasfcdad072014-08-13 14:15:21 -07001453 * reply, null otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001454 * @throws SwitchStateException If the message is a Nicira role reply
Saurav Dasfcdad072014-08-13 14:15:21 -07001455 * but the numeric role value is unknown.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001456 */
1457 protected Role extractNiciraRoleReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001458 OFExperimenter experimenterMsg) throws SwitchStateException {
1459 int vendor = (int) experimenterMsg.getExperimenter();
1460 if (vendor != 0x2320) // magic number representing nicira
1461 return null;
1462 OFNiciraControllerRoleReply nrr =
1463 (OFNiciraControllerRoleReply) experimenterMsg;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001464
Saurav Dasfcdad072014-08-13 14:15:21 -07001465 Role role = null;
1466 OFNiciraControllerRole ncr = nrr.getRole();
1467 switch (ncr) {
1468 case ROLE_MASTER:
1469 role = Role.MASTER;
1470 break;
1471 case ROLE_OTHER:
1472 role = Role.EQUAL;
1473 break;
1474 case ROLE_SLAVE:
1475 role = Role.SLAVE;
1476 break;
1477 default: // handled below
1478 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001479
Saurav Dasfcdad072014-08-13 14:15:21 -07001480 if (role == null) {
1481 String msg = String.format("Switch: [%s], State: [%s], "
1482 + "received NX_ROLE_REPLY with invalid role "
1483 + "value %d",
1484 h.getSwitchInfoString(),
1485 this.toString(),
1486 nrr.getRole());
1487 throw new SwitchStateException(msg);
1488 }
1489 return role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001490 }
1491
1492 /**
1493 * Helper class returns role reply information in the format understood
1494 * by the controller.
1495 */
1496 protected class RoleReplyInfo {
1497 private Role role;
1498 private U64 genId;
1499 private long xid;
1500
Saurav Dasfcdad072014-08-13 14:15:21 -07001501 RoleReplyInfo(Role role, U64 genId, long xid) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001502 this.role = role;
1503 this.genId = genId;
1504 this.xid = xid;
1505 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001506
1507 public Role getRole() {
1508 return role;
1509 }
1510
1511 public U64 getGenId() {
1512 return genId;
1513 }
1514
1515 public long getXid() {
1516 return xid;
1517 }
1518
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001519 @Override
1520 public String toString() {
1521 return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
1522 }
1523 }
1524
1525 /**
1526 * Extract the role information from an OF1.3 Role Reply Message
Saurav Dasfcdad072014-08-13 14:15:21 -07001527 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001528 * @param h
1529 * @param rrmsg
1530 * @return RoleReplyInfo object
1531 * @throws SwitchStateException
1532 */
1533 protected RoleReplyInfo extractOFRoleReply(OFChannelHandler h,
1534 OFRoleReply rrmsg) throws SwitchStateException {
1535 OFControllerRole cr = rrmsg.getRole();
1536 Role role = null;
Saurav Dasfcdad072014-08-13 14:15:21 -07001537 switch (cr) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001538 case ROLE_EQUAL:
1539 role = Role.EQUAL;
1540 break;
1541 case ROLE_MASTER:
1542 role = Role.MASTER;
1543 break;
1544 case ROLE_SLAVE:
1545 role = Role.SLAVE;
1546 break;
1547 case ROLE_NOCHANGE: // switch should send current role
1548 default:
1549 String msg = String.format("Unknown controller role {} "
1550 + "received from switch {}", cr, h.sw);
1551 throw new SwitchStateException(msg);
1552 }
1553
1554 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
1555 }
1556
1557 /**
1558 * Handles all pending port status messages before a switch is declared
1559 * activated in MASTER or EQUAL role. Note that since this handling
Saurav Dasfcdad072014-08-13 14:15:21 -07001560 * precedes the activation (and therefore notification to
1561 * IOFSwitchListerners) the changes to ports will already be visible
1562 * once the switch is activated. As a result, no notifications are sent
1563 * out for these pending portStatus messages.
1564 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001565 * @param h
1566 * @throws SwitchStateException
1567 */
1568 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
1569 try {
1570 handlePendingPortStatusMessages(h, 0);
1571 } catch (SwitchStateException e) {
1572 // do nothing - exception msg printed
1573 }
1574 }
1575
1576 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
1577 throws SwitchStateException {
1578 if (h.sw == null) {
1579 String msg = "State machine error: switch is null. Should never " +
1580 "happen";
1581 throw new SwitchStateException(msg);
1582 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001583 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
1584 for (OFPortStatus ps : h.pendingPortStatusMsg) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001585 temp.add(ps);
1586 handlePortStatusMessage(h, ps, false);
1587 }
1588 temp.clear();
1589 // expensive but ok - we don't expect too many port-status messages
1590 // note that we cannot use clear(), because of the reasons below
1591 h.pendingPortStatusMsg.removeAll(temp);
Saurav Dasfcdad072014-08-13 14:15:21 -07001592 // the iterator above takes a snapshot of the list - so while we
1593 // were
1594 // dealing with the pending port-status messages, we could have
1595 // received
1596 // newer ones. Handle them recursively, but break the recursion
1597 // after
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001598 // five steps to avoid an attack.
1599 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1600 handlePendingPortStatusMessages(h, index);
1601 }
1602 }
1603
1604 /**
1605 * Handle a port status message.
Saurav Dasfcdad072014-08-13 14:15:21 -07001606 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001607 * Handle a port status message by updating the port maps in the
Saurav Dasfcdad072014-08-13 14:15:21 -07001608 * IOFSwitch instance and notifying Controller about the change so it
1609 * can dispatch a switch update.
1610 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001611 * @param h The OFChannelHhandler that received the message
1612 * @param m The PortStatus message we received
Saurav Dasfcdad072014-08-13 14:15:21 -07001613 * @param doNotify if true switch port changed events will be dispatched
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001614 * @throws SwitchStateException
Saurav Dasfcdad072014-08-13 14:15:21 -07001615 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001616 */
1617 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1618 boolean doNotify) throws SwitchStateException {
1619 if (h.sw == null) {
1620 String msg = getSwitchStateMessage(h, m,
1621 "State machine error: switch is null. Should never " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001622 "happen");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001623 throw new SwitchStateException(msg);
1624 }
1625
1626 Collection<PortChangeEvent> changes = h.sw.processOFPortStatus(m);
1627 if (doNotify) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001628 for (PortChangeEvent ev : changes)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001629 h.controller.notifyPortChanged(h.sw.getId(), ev.port, ev.type);
1630 }
1631 }
1632
1633 /**
1634 * Checks if the role received (from the role-reply msg) is different
1635 * from the existing role in the IOFSwitch object for this controller.
Saurav Dasfcdad072014-08-13 14:15:21 -07001636 * If so, it transitions the controller to the new role. Note that the
1637 * caller should have already verified that the role-reply msg received
1638 * was in response to a role-request msg sent out by this controller
1639 * after hearing from the registry service.
1640 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001641 * @param h the ChannelHandler that received the message
1642 * @param role the role in the recieved role reply message
1643 */
1644 protected void checkAndSetRoleTransition(OFChannelHandler h, Role role) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001645 // we received a role-reply in response to a role message
1646 // sent after hearing from the registry service. It is
1647 // possible that the role of this controller instance for
1648 // this switch has changed:
1649 // for 1.0 switch: from MASTER to SLAVE
1650 // for 1.3 switch: from MASTER to EQUAL
1651 if ((h.sw.getRole() == Role.MASTER && role == Role.SLAVE) ||
1652 (h.sw.getRole() == Role.MASTER && role == Role.EQUAL)) {
1653 // the mastership has changed
1654 h.sw.setRole(role);
1655 h.setState(EQUAL);
1656 h.controller.transitionToEqualSwitch(h.sw.getId());
1657 return;
1658 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001659
Saurav Dasfcdad072014-08-13 14:15:21 -07001660 // or for both 1.0 and 1.3 switches from EQUAL to MASTER.
1661 // note that for 1.0, even though we mean SLAVE,
1662 // internally we call the role EQUAL.
1663 if (h.sw.getRole() == Role.EQUAL && role == Role.MASTER) {
1664 // the mastership has changed
1665 h.sw.setRole(role);
1666 h.setState(MASTER);
1667 h.controller.transitionToMasterSwitch(h.sw.getId());
1668 return;
1669 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001670 }
1671
1672 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001673 * Process an OF message received on the channel and update state
1674 * accordingly.
1675 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001676 * The main "event" of the state machine. Process the received message,
1677 * send follow up message if required and update state if required.
Saurav Dasfcdad072014-08-13 14:15:21 -07001678 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001679 * Switches on the message type and calls more specific event handlers
Saurav Dasfcdad072014-08-13 14:15:21 -07001680 * for each individual OF message type. If we receive a message that is
1681 * supposed to be sent from a controller to a switch we throw a
1682 * SwitchStateExeption.
1683 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001684 * The more specific handlers can also throw SwitchStateExceptions
Saurav Dasfcdad072014-08-13 14:15:21 -07001685 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001686 * @param h The OFChannelHandler that received the message
1687 * @param m The message we received.
1688 * @throws SwitchStateException
1689 * @throws IOException
1690 */
1691 void processOFMessage(OFChannelHandler h, OFMessage m)
1692 throws IOException, SwitchStateException {
1693 h.roleChanger.checkTimeout();
Saurav Dasfcdad072014-08-13 14:15:21 -07001694 switch (m.getType()) {
1695 case HELLO:
1696 processOFHello(h, (OFHello) m);
1697 break;
1698 case BARRIER_REPLY:
1699 processOFBarrierReply(h, (OFBarrierReply) m);
1700 break;
1701 case ECHO_REPLY:
1702 processOFEchoReply(h, (OFEchoReply) m);
1703 break;
1704 case ECHO_REQUEST:
1705 processOFEchoRequest(h, (OFEchoRequest) m);
1706 break;
1707 case ERROR:
1708 processOFError(h, (OFErrorMsg) m);
1709 break;
1710 case FEATURES_REPLY:
1711 processOFFeaturesReply(h, (OFFeaturesReply) m);
1712 break;
1713 case FLOW_REMOVED:
1714 processOFFlowRemoved(h, (OFFlowRemoved) m);
1715 break;
1716 case GET_CONFIG_REPLY:
1717 processOFGetConfigReply(h, (OFGetConfigReply) m);
1718 break;
1719 case PACKET_IN:
1720 processOFPacketIn(h, (OFPacketIn) m);
1721 break;
1722 case PORT_STATUS:
1723 processOFPortStatus(h, (OFPortStatus) m);
1724 break;
1725 case QUEUE_GET_CONFIG_REPLY:
1726 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1727 break;
1728 case STATS_REPLY: // multipart_reply in 1.3
1729 processOFStatisticsReply(h, (OFStatsReply) m);
1730 break;
1731 case EXPERIMENTER:
1732 processOFExperimenter(h, (OFExperimenter) m);
1733 break;
1734 case ROLE_REPLY:
1735 processOFRoleReply(h, (OFRoleReply) m);
1736 break;
1737 case GET_ASYNC_REPLY:
1738 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1739 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001740
Saurav Dasfcdad072014-08-13 14:15:21 -07001741 // The following messages are sent to switches. The controller
1742 // should never receive them
1743 case SET_CONFIG:
1744 case GET_CONFIG_REQUEST:
1745 case PACKET_OUT:
1746 case PORT_MOD:
1747 case QUEUE_GET_CONFIG_REQUEST:
1748 case BARRIER_REQUEST:
1749 case STATS_REQUEST: // multipart request in 1.3
1750 case FEATURES_REQUEST:
1751 case FLOW_MOD:
1752 case GROUP_MOD:
1753 case TABLE_MOD:
1754 case GET_ASYNC_REQUEST:
1755 case SET_ASYNC:
1756 case METER_MOD:
1757 default:
1758 illegalMessageReceived(h, m);
1759 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001760 }
1761 }
1762
1763 /*-----------------------------------------------------------------
1764 * Default implementation for message handlers in any state.
1765 *
1766 * Individual states must override these if they want a behavior
1767 * that differs from the default.
1768 *
1769 * In general, these handlers simply ignore the message and do
1770 * nothing.
1771 *
1772 * There are some exceptions though, since some messages really
1773 * are handled the same way in every state (e.g., ECHO_REQUST) or
1774 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1775 -----------------------------------------------------------------*/
1776
1777 void processOFHello(OFChannelHandler h, OFHello m)
1778 throws IOException, SwitchStateException {
1779 // we only expect hello in the WAIT_HELLO state
1780 illegalMessageReceived(h, m);
1781 }
1782
1783 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1784 throws IOException {
1785 // Silently ignore.
1786 }
1787
1788 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001789 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001790 if (h.ofVersion == null) {
1791 log.error("No OF version set for {}. Not sending Echo REPLY",
1792 h.channel.getRemoteAddress());
1793 return;
1794 }
1795 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1796 OFEchoReply reply = factory
1797 .buildEchoReply()
1798 .setXid(m.getXid())
1799 .setData(m.getData())
1800 .build();
1801 h.channel.write(Collections.singletonList(reply));
1802 }
1803
1804 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001805 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001806 // Do nothing with EchoReplies !!
1807 }
1808
1809 // no default implementation for OFError
1810 // every state must override it
1811 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1812 throws IOException, SwitchStateException;
1813
Saurav Dasfcdad072014-08-13 14:15:21 -07001814 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001815 throws IOException, SwitchStateException {
1816 unhandledMessageReceived(h, m);
1817 }
1818
1819 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001820 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001821 unhandledMessageReceived(h, m);
1822 }
1823
1824 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1825 throws IOException, SwitchStateException {
1826 // we only expect config replies in the WAIT_CONFIG_REPLY state
1827 illegalMessageReceived(h, m);
1828 }
1829
1830 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1831 throws IOException {
1832 unhandledMessageReceived(h, m);
1833 }
1834
1835 // no default implementation. Every state needs to handle it.
1836 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1837 throws IOException, SwitchStateException;
1838
1839 void processOFQueueGetConfigReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001840 OFQueueGetConfigReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001841 throws IOException {
1842 unhandledMessageReceived(h, m);
1843 }
1844
1845 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1846 throws IOException, SwitchStateException {
1847 unhandledMessageReceived(h, m);
1848 }
1849
1850 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1851 throws IOException, SwitchStateException {
1852 // TODO: it might make sense to parse the vendor message here
1853 // into the known vendor messages we support and then call more
1854 // specific event handlers
1855 unhandledMessageReceived(h, m);
1856 }
1857
1858 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1859 throws SwitchStateException, IOException {
1860 unhandledMessageReceived(h, m);
1861 }
1862
1863 void processOFGetAsyncReply(OFChannelHandler h,
1864 OFAsyncGetReply m) {
1865 unhandledMessageReceived(h, m);
1866 }
1867
1868 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -07001869 RoleRecvStatus expectation) throws IOException {
1870 // do nothing in most states
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001871 }
Saurav Das6de23322014-08-07 18:51:19 -07001872
1873 /**
1874 * Handles role request messages that have timed out.
1875 * <p>
1876 * Role request messages that don't get role-replies (or errors related
1877 * to the request) time out after DEFAULT_ROLE_TIMEOUT_MS secs, at which
1878 * time the controller state-machine disconnects the switch
Saurav Dasfcdad072014-08-13 14:15:21 -07001879 *
Saurav Das6de23322014-08-07 18:51:19 -07001880 * @param h the channel handler for this switch
1881 * @param pendingRole the role for which no reply was received
1882 * @throws SwitchStateException
1883 */
1884 public void handleTimedOutRoleReply(OFChannelHandler h,
1885 Role pendingRole) throws SwitchStateException {
1886 String msg = String.format("Switch: [%s] State: [%s] did not "
1887 + "reply to role-msg for pending role %s. Disconnecting ...",
1888 h.getSwitchInfoString(), this.toString(), pendingRole);
1889 throw new SwitchStateException(msg);
1890 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001891 }
1892
Saurav Dasfcdad072014-08-13 14:15:21 -07001893 // *************************
1894 // Channel handler methods
1895 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001896
1897 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001898 @LogMessageDoc(message = "New switch connection from {ip address}",
1899 explanation = "A new switch has connected from the " +
1900 "specified IP address")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001901 public void channelConnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001902 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001903 counters.switchConnected.updateCounterWithFlush();
1904 channel = e.getChannel();
1905 log.info("New switch connection from {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001906 channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001907 sendHandshakeHelloMessage();
1908 setState(ChannelState.WAIT_HELLO);
1909 }
1910
1911 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001912 @LogMessageDoc(message = "Disconnected switch {switch information}",
1913 explanation = "The specified switch has disconnected.")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001914 public void channelDisconnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001915 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001916 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Saurav Dasfcdad072014-08-13 14:15:21 -07001917 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001918 if (thisdpid != 0) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001919 if (duplicateDpidFound != Boolean.TRUE) {
1920 // if the disconnected switch (on this ChannelHandler)
1921 // was not one with a duplicate-dpid, it is safe to remove all
1922 // state for it at the controller. Notice that if the
1923 // disconnected
1924 // switch was a duplicate-dpid, calling the method below would
1925 // clear
1926 // all state for the original switch (with the same dpid),
1927 // which we obviously don't want.
1928 controller.removeConnectedSwitch(thisdpid);
1929 } else {
1930 // A duplicate was disconnected on this ChannelHandler,
1931 // this is the same switch reconnecting, but the original state
1932 // was
1933 // not cleaned up - XXX check liveness of original
1934 // ChannelHandler
1935 duplicateDpidFound = Boolean.FALSE;
1936 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001937 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -07001938 log.warn("no dpid in channelHandler registered for "
1939 + "disconnected switch {}", getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001940 }
1941 }
1942
1943 @Override
1944 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -07001945 @LogMessageDoc(level = "ERROR",
1946 message = "Disconnecting switch {switch} due to read timeout",
1947 explanation = "The connected switch has failed to send any " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001948 "messages or respond to echo requests",
Saurav Dasfcdad072014-08-13 14:15:21 -07001949 recommendation = LogMessageDoc.CHECK_SWITCH),
1950 @LogMessageDoc(level = "ERROR",
1951 message = "Disconnecting switch {switch}: failed to " +
1952 "complete handshake",
1953 explanation = "The switch did not respond correctly " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001954 "to handshake messages",
Saurav Dasfcdad072014-08-13 14:15:21 -07001955 recommendation = LogMessageDoc.CHECK_SWITCH),
1956 @LogMessageDoc(level = "ERROR",
1957 message = "Disconnecting switch {switch} due to IO Error: {}",
1958 explanation = "There was an error communicating with the switch",
1959 recommendation = LogMessageDoc.CHECK_SWITCH),
1960 @LogMessageDoc(level = "ERROR",
1961 message = "Disconnecting switch {switch} due to switch " +
1962 "state error: {error}",
1963 explanation = "The switch sent an unexpected message",
1964 recommendation = LogMessageDoc.CHECK_SWITCH),
1965 @LogMessageDoc(level = "ERROR",
1966 message = "Disconnecting switch {switch} due to " +
1967 "message parse failure",
1968 explanation = "Could not parse a message from the switch",
1969 recommendation = LogMessageDoc.CHECK_SWITCH),
1970 @LogMessageDoc(level = "ERROR",
1971 message = "Terminating controller due to storage exception",
1972 explanation = Controller.ERROR_DATABASE,
1973 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1974 @LogMessageDoc(level = "ERROR",
1975 message = "Could not process message: queue full",
1976 explanation = "OpenFlow messages are arriving faster than " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001977 " the controller can process them.",
Saurav Dasfcdad072014-08-13 14:15:21 -07001978 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1979 @LogMessageDoc(level = "ERROR",
1980 message = "Error while processing message " +
1981 "from switch {switch} {cause}",
1982 explanation = "An error occurred processing the switch message",
1983 recommendation = LogMessageDoc.GENERIC_ACTION)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001984 })
1985 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1986 throws Exception {
1987 if (e.getCause() instanceof ReadTimeoutException) {
1988 // switch timeout
1989 log.error("Disconnecting switch {} due to read timeout",
Saurav Dasfcdad072014-08-13 14:15:21 -07001990 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001991 counters.switchDisconnectReadTimeout.updateCounterWithFlush();
1992 ctx.getChannel().close();
1993 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1994 log.error("Disconnecting switch {}: failed to complete handshake",
Saurav Dasfcdad072014-08-13 14:15:21 -07001995 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001996 counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush();
1997 ctx.getChannel().close();
1998 } else if (e.getCause() instanceof ClosedChannelException) {
1999 log.debug("Channel for sw {} already closed", getSwitchInfoString());
2000 } else if (e.getCause() instanceof IOException) {
2001 log.error("Disconnecting switch {} due to IO Error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002002 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002003 if (log.isDebugEnabled()) {
2004 // still print stack trace if debug is enabled
2005 log.debug("StackTrace for previous Exception: ", e.getCause());
2006 }
2007 counters.switchDisconnectIOError.updateCounterWithFlush();
2008 ctx.getChannel().close();
2009 } else if (e.getCause() instanceof SwitchStateException) {
2010 log.error("Disconnecting switch {} due to switch state error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002011 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002012 if (log.isDebugEnabled()) {
2013 // still print stack trace if debug is enabled
2014 log.debug("StackTrace for previous Exception: ", e.getCause());
2015 }
2016 counters.switchDisconnectSwitchStateException.updateCounterWithFlush();
2017 ctx.getChannel().close();
2018 } else if (e.getCause() instanceof OFParseError) {
2019 log.error("Disconnecting switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002020 + getSwitchInfoString() +
2021 " due to message parse failure",
2022 e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002023 counters.switchDisconnectParseError.updateCounterWithFlush();
2024 ctx.getChannel().close();
2025 } else if (e.getCause() instanceof RejectedExecutionException) {
2026 log.warn("Could not process message: queue full");
2027 counters.rejectedExecutionException.updateCounterWithFlush();
2028 } else {
2029 log.error("Error while processing message from switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002030 + getSwitchInfoString()
2031 + "state " + this.state, e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002032 counters.switchDisconnectOtherException.updateCounterWithFlush();
2033 ctx.getChannel().close();
2034 }
2035 }
2036
2037 @Override
2038 public String toString() {
2039 return getSwitchInfoString();
2040 }
2041
2042 @Override
2043 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
2044 throws Exception {
2045 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2046 OFMessage m = factory.buildEchoRequest().build();
2047 log.info("Sending Echo Request on idle channel: {}",
2048 e.getChannel().getPipeline().getLast().toString());
2049 e.getChannel().write(Collections.singletonList(m));
2050 // XXX S some problems here -- echo request has no transaction id, and
2051 // echo reply is not correlated to the echo request.
2052 }
2053
2054 @Override
2055 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
2056 throws Exception {
2057 if (e.getMessage() instanceof List) {
2058 @SuppressWarnings("unchecked")
Saurav Dasfcdad072014-08-13 14:15:21 -07002059 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002060
2061 LoadMonitor.LoadLevel loadlevel;
2062 int packets_dropped = 0;
2063 int packets_allowed = 0;
2064 int lldps_allowed = 0;
2065
2066 if (this.controller.overload_drop) {
2067 loadlevel = this.controller.loadmonitor.getLoadLevel();
2068 }
2069 else {
2070 loadlevel = LoadMonitor.LoadLevel.OK;
2071 }
2072
2073 for (OFMessage ofm : msglist) {
2074 counters.messageReceived.updateCounterNoFlush();
Saurav Dasfcdad072014-08-13 14:15:21 -07002075 // Per-switch input throttling - placeholder for future
2076 // throttling
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002077 /*if (sw != null && sw.inputThrottled(ofm)) {
2078 counters.messageInputThrottled.updateCounterNoFlush();
2079 continue;
2080 }*/
2081 try {
2082 if (this.controller.overload_drop &&
Saurav Dasfcdad072014-08-13 14:15:21 -07002083 !loadlevel.equals(LoadMonitor.LoadLevel.OK)) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002084 switch (ofm.getType()) {
2085 case PACKET_IN:
2086 switch (loadlevel) {
2087 case VERYHIGH:
2088 // Drop all packet-ins, including LLDP/BDDPs
2089 packets_dropped++;
2090 continue;
2091 case HIGH:
2092 // Drop all packet-ins, except LLDP/BDDPs
Saurav Dasfcdad072014-08-13 14:15:21 -07002093 byte[] data = ((OFPacketIn) ofm).getData();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002094 if (data.length > 14) {
Saurav Dasfcdad072014-08-13 14:15:21 -07002095 if (((data[12] == (byte) 0x88) &&
2096 (data[13] == (byte) 0xcc)) ||
2097 ((data[12] == (byte) 0x89) &&
2098 (data[13] == (byte) 0x42))) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002099 lldps_allowed++;
2100 packets_allowed++;
2101 break;
2102 }
2103 }
2104 packets_dropped++;
2105 continue;
2106 default:
2107 // Load not high, go ahead and process msg
2108 packets_allowed++;
2109 break;
2110 }
2111 break;
2112 default:
2113 // Process all non-packet-ins
2114 packets_allowed++;
2115 break;
2116 }
2117 }
2118
2119 // Do the actual packet processing
2120 state.processOFMessage(this, ofm);
2121
Saurav Dasfcdad072014-08-13 14:15:21 -07002122 } catch (Exception ex) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002123 // We are the last handler in the stream, so run the
2124 // exception through the channel again by passing in
2125 // ctx.getChannel().
2126 Channels.fireExceptionCaught(ctx.getChannel(), ex);
2127 }
2128 }
2129
2130 if (loadlevel != LoadMonitor.LoadLevel.OK) {
2131 if (log.isDebugEnabled()) {
2132 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002133 "Overload: Detected {}, packets dropped={}",
2134 loadlevel.toString(), packets_dropped);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002135 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002136 "Overload: Packets allowed={} (LLDP/BDDPs allowed={})",
2137 packets_allowed, lldps_allowed);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002138 }
2139 }
2140 }
2141 else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002142 // Channels.fireExceptionCaught(ctx.getChannel(),
2143 // new
2144 // AssertionError("Message received from Channel is not a list"));
2145 // TODO: Pankaj: move the counters using ONOS metrics implementation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002146
2147 counters.messageReceived.updateCounterNoFlush();
2148 state.processOFMessage(this, (OFMessage) e.getMessage());
2149 }
2150
2151 // Flush all thread local queues etc. generated by this train
2152 // of messages.
2153 this.controller.flushAll();
2154 }
2155
Saurav Dasfcdad072014-08-13 14:15:21 -07002156 // *************************
2157 // Channel utility methods
2158 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002159
2160 /**
2161 * Is this a state in which the handshake has completed?
Saurav Dasfcdad072014-08-13 14:15:21 -07002162 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002163 * @return true if the handshake is complete
2164 */
2165 public boolean isHandshakeComplete() {
2166 return this.state.isHandshakeComplete();
2167 }
2168
2169 private void dispatchMessage(OFMessage m) throws IOException {
2170 // handleMessage will count
2171 this.controller.handleMessage(this.sw, m, null);
2172 }
2173
2174 /**
2175 * Return a string describing this switch based on the already available
2176 * information (DPID and/or remote socket)
Saurav Dasfcdad072014-08-13 14:15:21 -07002177 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002178 * @return
2179 */
2180 private String getSwitchInfoString() {
2181 if (sw != null)
2182 return sw.toString();
2183 String channelString;
2184 if (channel == null || channel.getRemoteAddress() == null) {
2185 channelString = "?";
2186 } else {
2187 channelString = channel.getRemoteAddress().toString();
2188 }
2189 String dpidString;
2190 if (featuresReply == null) {
2191 dpidString = "?";
2192 } else {
2193 dpidString = featuresReply.getDatapathId().toString();
2194 }
2195 return String.format("[%s DPID[%s]]", channelString, dpidString);
2196 }
2197
2198 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002199 * Update the channels state. Only called from the state machine. TODO:
2200 * enforce restricted state transitions
2201 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002202 * @param state
2203 */
2204 private void setState(ChannelState state) {
2205 this.state = state;
2206 }
2207
2208 /**
2209 * Send hello message to the switch using the handshake transactions ids.
Saurav Dasfcdad072014-08-13 14:15:21 -07002210 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002211 * @throws IOException
2212 */
2213 private void sendHandshakeHelloMessage() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07002214 // The OF protocol requires us to start things off by sending the
2215 // highest
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002216 // version of the protocol supported.
2217
Saurav Dasfcdad072014-08-13 14:15:21 -07002218 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3
2219 // (ofp_version=0x04)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002220 // see Sec. 7.5.1 of the OF1.3.4 spec
2221 U32 bitmap = U32.ofRaw(0x00000012);
2222 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
2223 .setBitmaps(Collections.singletonList(bitmap))
2224 .build();
2225 OFMessage.Builder mb = factory13.buildHello()
2226 .setXid(this.handshakeTransactionIds--)
2227 .setElements(Collections.singletonList(hem));
2228 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
2229 channel.write(Collections.singletonList(mb.build()));
2230 }
2231
2232 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002233 * Send featuresRequest msg to the switch using the handshake transactions
2234 * ids.
2235 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002236 * @throws IOException
2237 */
2238 private void sendHandshakeFeaturesRequestMessage() throws IOException {
2239 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2240 OFMessage m = factory.buildFeaturesRequest()
2241 .setXid(this.handshakeTransactionIds--)
2242 .build();
2243 channel.write(Collections.singletonList(m));
2244 }
2245
2246 private void setSwitchRole(Role role) {
2247 sw.setRole(role);
2248 }
2249
2250 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002251 * Send the configuration requests to tell the switch we want full packets
2252 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002253 * @throws IOException
2254 */
2255 private void sendHandshakeSetConfig() throws IOException {
2256 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Saurav Dasfcdad072014-08-13 14:15:21 -07002257 // log.debug("Sending CONFIG_REQUEST to {}",
2258 // channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002259 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
2260
2261 // Ensure we receive the full packet via PacketIn
2262 // FIXME: We don't set the reassembly flags.
2263 OFSetConfig sc = factory
2264 .buildSetConfig()
2265 .setMissSendLen((short) 0xffff)
2266 .setXid(this.handshakeTransactionIds--)
2267 .build();
2268 msglist.add(sc);
2269
2270 // Barrier
2271 OFBarrierRequest br = factory
2272 .buildBarrierRequest()
2273 .setXid(this.handshakeTransactionIds--)
2274 .build();
2275 msglist.add(br);
2276
2277 // Verify (need barrier?)
2278 OFGetConfigRequest gcr = factory
2279 .buildGetConfigRequest()
2280 .setXid(this.handshakeTransactionIds--)
2281 .build();
2282 msglist.add(gcr);
2283 channel.write(msglist);
2284 }
2285
2286 /**
2287 * send a description state request
Saurav Dasfcdad072014-08-13 14:15:21 -07002288 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002289 * @throws IOException
2290 */
2291 private void sendHandshakeDescriptionStatsRequest() throws IOException {
2292 // Get Description to set switch-specific flags
2293 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2294 OFDescStatsRequest dreq = factory
2295 .buildDescStatsRequest()
2296 .setXid(handshakeTransactionIds--)
2297 .build();
2298 channel.write(Collections.singletonList(dreq));
2299 }
2300
2301 private void sendHandshakeOFPortDescRequest() throws IOException {
2302 // Get port description for 1.3 switch
2303 OFPortDescStatsRequest preq = factory13
2304 .buildPortDescStatsRequest()
2305 .setXid(handshakeTransactionIds--)
2306 .build();
2307 channel.write(Collections.singletonList(preq));
2308 }
2309
2310 /**
2311 * Read switch properties from storage and set switch attributes accordingly
2312 */
2313 private void readPropertyFromStorage() {
2314 // XXX This is a placeholder for switch configuration
2315 }
2316
2317 ChannelState getStateForTesting() {
2318 return state;
2319 }
2320
2321 void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) {
2322 roleChanger = new RoleChanger(roleTimeoutMs);
2323 }
2324
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002325}