blob: 5f844f59861b877ed4adbc7767c2fa3f1807a8ee [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.core.internal;
2
3import java.io.IOException;
4import java.nio.channels.ClosedChannelException;
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.List;
9import java.util.concurrent.CopyOnWriteArrayList;
10import java.util.concurrent.RejectedExecutionException;
11
12import net.floodlightcontroller.core.FloodlightContext;
13import net.floodlightcontroller.core.IFloodlightProviderService.Role;
14import net.floodlightcontroller.core.IOFSwitch;
15import net.floodlightcontroller.core.IOFSwitch.PortChangeEvent;
16import net.floodlightcontroller.core.annotations.LogMessageDoc;
17import net.floodlightcontroller.core.annotations.LogMessageDocs;
18import net.floodlightcontroller.core.internal.Controller.Counters;
19import net.floodlightcontroller.core.internal.OFChannelHandler.ChannelState.RoleReplyInfo;
20import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
21import net.floodlightcontroller.util.LoadMonitor;
22
23import org.jboss.netty.channel.Channel;
24import org.jboss.netty.channel.ChannelHandlerContext;
25import org.jboss.netty.channel.ChannelStateEvent;
26import org.jboss.netty.channel.Channels;
27import org.jboss.netty.channel.ExceptionEvent;
28import org.jboss.netty.channel.MessageEvent;
29import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
30import org.jboss.netty.handler.timeout.IdleStateEvent;
31import org.jboss.netty.handler.timeout.ReadTimeoutException;
32import org.projectfloodlight.openflow.exceptions.OFParseError;
33import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
34import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
35import org.projectfloodlight.openflow.protocol.OFBarrierReply;
36import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
37import org.projectfloodlight.openflow.protocol.OFControllerRole;
38import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
39import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
40import org.projectfloodlight.openflow.protocol.OFEchoReply;
41import org.projectfloodlight.openflow.protocol.OFEchoRequest;
42import org.projectfloodlight.openflow.protocol.OFErrorMsg;
43import org.projectfloodlight.openflow.protocol.OFErrorType;
44import org.projectfloodlight.openflow.protocol.OFExperimenter;
45import org.projectfloodlight.openflow.protocol.OFFactory;
46import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
47import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
48import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
49import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
50import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
51import org.projectfloodlight.openflow.protocol.OFHello;
52import org.projectfloodlight.openflow.protocol.OFHelloElem;
53import org.projectfloodlight.openflow.protocol.OFMessage;
54import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
55import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
56import org.projectfloodlight.openflow.protocol.OFPacketIn;
57import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
58import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
59import org.projectfloodlight.openflow.protocol.OFPortStatus;
60import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
61import org.projectfloodlight.openflow.protocol.OFRoleReply;
62import org.projectfloodlight.openflow.protocol.OFRoleRequest;
63import org.projectfloodlight.openflow.protocol.OFSetConfig;
64import org.projectfloodlight.openflow.protocol.OFStatsReply;
65import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
66import org.projectfloodlight.openflow.protocol.OFStatsType;
67import org.projectfloodlight.openflow.protocol.OFType;
68import org.projectfloodlight.openflow.protocol.OFVersion;
69import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
70import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
71import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg;
72import org.projectfloodlight.openflow.types.U32;
73import org.projectfloodlight.openflow.types.U64;
74import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070077/**
Saurav Dasfcdad072014-08-13 14:15:21 -070078 * Channel handler deals with the switch connection and dispatches switch
79 * messages to the appropriate locations.
Jonathan Hart6eda2302014-08-14 14:57:03 -070080 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070081 * @author readams, gregor, saurav
82 */
83class OFChannelHandler extends IdleStateAwareChannelHandler {
84
85 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
86
Saurav Das85399942014-09-02 16:11:48 -070087 private static final long DEFAULT_ROLE_TIMEOUT_MS = 3 * 1000; // 3 sec
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070088 private final Controller controller;
89 private final Counters counters;
90 private IOFSwitch sw;
91 private long thisdpid; // channelHandler cached value of connected switch id
92 private Channel channel;
93 // State needs to be volatile because the HandshakeTimeoutHandler
94 // needs to check if the handshake is complete
95 private volatile ChannelState state;
96
Saurav Dasfcdad072014-08-13 14:15:21 -070097 // All role messaging is handled by the roleChanger. The channel state
98 // machine
99 // coordinates between the roleChanger and the
100 // controller-global-registry-service
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700101 // to determine controller roles per switch.
102 private RoleChanger roleChanger;
103 // Used to coordinate between the controller and the cleanup thread(?)
104 // for access to the global registry on a per switch basis.
105 volatile Boolean controlRequested;
106 // When a switch with a duplicate dpid is found (i.e we already have a
107 // connected switch with the same dpid), the new switch is immediately
108 // disconnected. At that point netty callsback channelDisconnected() which
Saurav Dasfcdad072014-08-13 14:15:21 -0700109 // proceeds to cleaup switch state - we need to ensure that it does not
110 // cleanup
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700111 // switch state for the older (still connected) switch
112 private volatile Boolean duplicateDpidFound;
113
114 // Temporary storage for switch-features and port-description
115 private OFFeaturesReply featuresReply;
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
Jonathan Hart6eda2302014-08-14 14:57:03 -0700126 // Set to true if the controller should send a 1.0 HELLO instead of a 1.3
127 // HELLO. This is to support older switches that can't support 1.3 HELLO
128 // messages correctly.
129 static boolean useOnly10 = false;
130
Saurav Dasfcdad072014-08-13 14:15:21 -0700131 /**
132 * transaction Ids to use during handshake. Since only one thread calls into
133 * an OFChannelHandler instance, we don't need atomic. We will count down
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700134 */
135 private int handshakeTransactionIds = -1;
136
137 /**
138 * Create a new unconnected OFChannelHandler.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700139 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700140 * @param controller
141 */
142 OFChannelHandler(Controller controller) {
143 this.controller = controller;
144 this.counters = controller.getCounters();
145 this.roleChanger = new RoleChanger(DEFAULT_ROLE_TIMEOUT_MS);
146 this.state = ChannelState.INIT;
147 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
148 factory13 = controller.getOFMessageFactory_13();
149 factory10 = controller.getOFMessageFactory_10();
150 controlRequested = Boolean.FALSE;
151 duplicateDpidFound = Boolean.FALSE;
152 }
153
Saurav Dasfcdad072014-08-13 14:15:21 -0700154 // *******************
155 // Role Handling
156 // *******************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700157
158 /**
159 * When we remove a pending role request we use this enum to indicate how we
160 * arrived at the decision. When we send a role request to the switch, we
Saurav Dasfcdad072014-08-13 14:15:21 -0700161 * also use this enum to indicate what we expect back from the switch, so
162 * the role changer can match the reply to our expectation.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700163 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700164 * @author gregor, saurav
165 */
166 public enum RoleRecvStatus {
Saurav Dasfcdad072014-08-13 14:15:21 -0700167 /**
168 * The switch returned an error indicating that roles are not supported
169 */
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700170 UNSUPPORTED,
171 /** The request timed out */
172 NO_REPLY,
173 /** The reply was old, there is a newer request pending */
174 OLD_REPLY,
175 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700176 * The reply's role matched the role that this controller set in the
177 * request message - invoked either initially at startup or to reassert
178 * current role
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700179 */
180 MATCHED_CURRENT_ROLE,
181 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700182 * The reply's role matched the role that this controller set in the
183 * request message - this is the result of a callback from the global
184 * registry, followed by a role request sent to the switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700185 */
186 MATCHED_SET_ROLE,
187 /**
188 * The reply's role was a response to the query made by this controller
189 */
190 REPLY_QUERY,
Saurav Dasfcdad072014-08-13 14:15:21 -0700191 /**
192 * We received a role reply message from the switch but the expectation
193 * was unclear, or there was no expectation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700194 */
195 OTHER_EXPECTATION,
196 }
197
198 /**
199 * Forwards to RoleChanger. See there.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700200 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700201 * @param role
202 */
203 public void sendRoleRequest(Role role, RoleRecvStatus expectation) {
204 try {
205 roleChanger.sendRoleRequest(role, expectation);
206 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700207 log.error("Disconnecting switch {} due to IO Error: {}",
208 getSwitchInfoString(), e.getMessage());
209 channel.close();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700210 }
211 }
212
213 // XXX S consider if necessary
214 public void disconnectSwitch() {
215 sw.disconnectSwitch();
216 }
217
218 /**
219 * A utility class to handle role requests and replies for this channel.
220 * After a role request is submitted the role changer keeps track of the
Saurav Dasfcdad072014-08-13 14:15:21 -0700221 * pending request, collects the reply (if any) and times out the request if
222 * necessary.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700223 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700224 * To simplify role handling we only keep track of the /last/ pending role
225 * reply send to the switch. If multiple requests are pending and we receive
226 * replies for earlier requests we ignore them. However, this way of
227 * handling pending requests implies that we could wait forever if a new
228 * request is submitted before the timeout triggers. If necessary we could
229 * work around that though.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700230 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700231 * @author gregor
232 * @author saurav (added support OF1.3 role messages, and expectations)
233 */
234 private class RoleChanger {
235 // indicates that a request is currently pending
236 // needs to be volatile to allow correct double-check idiom
237 private volatile boolean requestPending;
238 // the transaction Id of the pending request
239 private int pendingXid;
240 // the role that's pending
241 private Role pendingRole;
242 // system time in MS when we send the request
243 private long roleSubmitTime;
244 // the timeout to use
245 private final long roleTimeoutMs;
246 // the expectation set by the caller for the returned role
247 private RoleRecvStatus expectation;
248
249 public RoleChanger(long roleTimeoutMs) {
250 this.requestPending = false;
251 this.roleSubmitTime = 0;
252 this.pendingXid = -1;
253 this.pendingRole = null;
254 this.roleTimeoutMs = roleTimeoutMs;
255 this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
256 }
257
258 /**
259 * Send NX role request message to the switch requesting the specified
260 * role.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700261 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700262 * @param sw switch to send the role request message to
263 * @param role role to request
264 */
265 private int sendNxRoleRequest(Role role) throws IOException {
266 // Convert the role enum to the appropriate role to send
267 OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
268 switch (role) {
269 case MASTER:
270 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
271 break;
272 case SLAVE:
273 case EQUAL:
274 default:
275 // ensuring that the only two roles sent to 1.0 switches with
276 // Nicira role support, are MASTER and SLAVE
277 roleToSend = OFNiciraControllerRole.ROLE_SLAVE;
278 log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
279 }
280 int xid = sw.getNextTransactionId();
281 OFExperimenter roleRequest = factory10
282 .buildNiciraControllerRoleRequest()
283 .setXid(xid)
284 .setRole(roleToSend)
285 .build();
286 sw.write(Collections.<OFMessage>singletonList(roleRequest),
Saurav Dasfcdad072014-08-13 14:15:21 -0700287 new FloodlightContext());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700288 return xid;
289 }
290
291 private int sendOF13RoleRequest(Role role) throws IOException {
292 // Convert the role enum to the appropriate role to send
293 OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
294 switch (role) {
295 case EQUAL:
296 roleToSend = OFControllerRole.ROLE_EQUAL;
297 break;
298 case MASTER:
299 roleToSend = OFControllerRole.ROLE_MASTER;
300 break;
301 case SLAVE:
302 roleToSend = OFControllerRole.ROLE_SLAVE;
303 break;
304 default:
305 log.warn("Sending default role.noChange to switch {}."
306 + " Should only be used for queries.", sw);
307 }
308
309 int xid = sw.getNextTransactionId();
310 OFRoleRequest rrm = factory13
311 .buildRoleRequest()
312 .setRole(roleToSend)
313 .setXid(xid)
314 .setGenerationId(sw.getNextGenerationId())
315 .build();
316 sw.write(rrm, null);
317 return xid;
318 }
319
320 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700321 * Send a role request with the given role to the switch and update the
322 * pending request and timestamp. Sends an OFPT_ROLE_REQUEST to an OF1.3
323 * switch, OR Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured
324 * to support it in the IOFSwitch driver. If not supported, this method
325 * sends nothing and returns 'false'. The caller should take appropriate
326 * action.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700327 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700328 * One other optimization we do here is that for OF1.0 switches with
329 * Nicira role message support, we force the Role.EQUAL to become
Saurav Dasfcdad072014-08-13 14:15:21 -0700330 * Role.SLAVE, as there is no defined behavior for the Nicira role
331 * OTHER. We cannot expect it to behave like SLAVE. We don't have this
332 * problem with OF1.3 switches, because Role.EQUAL is well defined and
333 * we can simulate SLAVE behavior by using ASYNC messages.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700334 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700335 * @param role
336 * @throws IOException
Saurav Dasfcdad072014-08-13 14:15:21 -0700337 * @returns false if and only if the switch does not support
338 * role-request messages, according to the switch driver; true
339 * otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700340 */
341 synchronized boolean sendRoleRequest(Role role, RoleRecvStatus expectation)
342 throws IOException {
343 this.expectation = expectation;
344
345 if (ofVersion == OFVersion.OF_10) {
346 Boolean supportsNxRole = (Boolean)
347 sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE);
348 if (!supportsNxRole) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700349 log.debug("Switch driver indicates no support for Nicira "
350 + "role request messages. Not sending ...");
351 state.handleUnsentRoleMessage(OFChannelHandler.this, role,
352 expectation);
353 return false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700354 }
355 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
356 // make Role.EQUAL become Role.SLAVE
357 role = (role == Role.EQUAL) ? Role.SLAVE : role;
358 pendingXid = sendNxRoleRequest(role);
359 pendingRole = role;
360 roleSubmitTime = System.currentTimeMillis();
361 requestPending = true;
362 } else {
363 // OF1.3 switch, use OFPT_ROLE_REQUEST message
364 pendingXid = sendOF13RoleRequest(role);
365 pendingRole = role;
366 roleSubmitTime = System.currentTimeMillis();
367 requestPending = true;
368 }
369 return true;
370 }
371
372 /**
373 * Deliver a received role reply.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700374 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700375 * Check if a request is pending and if the received reply matches the
Saurav Dasfcdad072014-08-13 14:15:21 -0700376 * the expected pending reply (we check both role and xid) we set the
377 * role for the switch/channel.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700378 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700379 * If a request is pending but doesn't match the reply we ignore it, and
380 * return
Jonathan Hart6eda2302014-08-14 14:57:03 -0700381 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700382 * If no request is pending we disconnect with a SwitchStateException
Jonathan Hart6eda2302014-08-14 14:57:03 -0700383 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700384 * @param RoleReplyInfo information about role-reply in format that
Saurav Dasfcdad072014-08-13 14:15:21 -0700385 * controller can understand.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700386 * @throws SwitchStateException if no request is pending
387 */
388 synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
389 throws SwitchStateException {
390 if (!requestPending) {
391 Role currentRole = (sw != null) ? sw.getRole() : null;
392 if (currentRole != null) {
393 if (currentRole == rri.getRole()) {
394 // Don't disconnect if the role reply we received is
395 // for the same role we are already in.
396 log.debug("Received unexpected RoleReply from "
397 + "Switch: {} in State: {}. "
398 + "Role in reply is same as current role of this "
399 + "controller for this sw. Ignoring ...",
400 getSwitchInfoString(), state.toString());
401 return RoleRecvStatus.OTHER_EXPECTATION;
402 } else {
403 String msg = String.format("Switch: [%s], State: [%s], "
404 + "received unexpected RoleReply[%s]. "
405 + "No roles are pending, and this controller's "
406 + "current role:[%s] does not match reply. "
407 + "Disconnecting switch ... ",
408 OFChannelHandler.this.getSwitchInfoString(),
409 OFChannelHandler.this.state.toString(),
410 rri, currentRole);
411 throw new SwitchStateException(msg);
412 }
413 }
414 log.debug("Received unexpected RoleReply {} from "
415 + "Switch: {} in State: {}. "
416 + "This controller has no current role for this sw. "
417 + "Ignoring ...", new Object[] {rri,
Saurav Dasfcdad072014-08-13 14:15:21 -0700418 getSwitchInfoString(), state});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700419 return RoleRecvStatus.OTHER_EXPECTATION;
420 }
421
422 int xid = (int) rri.getXid();
423 Role role = rri.getRole();
Saurav Dasfcdad072014-08-13 14:15:21 -0700424 // XXX S should check generation id meaningfully and other cases of
425 // expectations
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700426 // U64 genId = rri.getGenId();
427
428 if (pendingXid != xid) {
429 log.debug("Received older role reply from " +
430 "switch {} ({}). Ignoring. " +
431 "Waiting for {}, xid={}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700432 new Object[] {getSwitchInfoString(), rri,
433 pendingRole, pendingXid});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700434 return RoleRecvStatus.OLD_REPLY;
435 }
436
437 if (pendingRole == role) {
Saurav Das6de23322014-08-07 18:51:19 -0700438 requestPending = false; // we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700439 log.debug("Received role reply message from {} that matched "
440 + "expected role-reply {} with expectations {}",
441 new Object[] {getSwitchInfoString(), role, expectation});
442 counters.roleReplyReceived.updateCounterWithFlush();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700443 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
444 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
445 return expectation;
446 } else {
447 return RoleRecvStatus.OTHER_EXPECTATION;
448 }
449 }
450
451 // if xids match but role's don't, perhaps its a query (OF1.3)
Saurav Das6de23322014-08-07 18:51:19 -0700452 if (expectation == RoleRecvStatus.REPLY_QUERY) {
453 requestPending = false; // again we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700454 return expectation;
Saurav Das6de23322014-08-07 18:51:19 -0700455 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700456
Saurav Das6de23322014-08-07 18:51:19 -0700457 // It is not clear what this role-reply was about, since it is not
458 // a query and it did not match the pendingRole. But since the xid's
459 // matched, we state that we received what we were waiting for, and
460 // let the caller handle it
461 requestPending = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700462 return RoleRecvStatus.OTHER_EXPECTATION;
463 }
464
465 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700466 * Called if we receive an error message. If the xid matches the pending
467 * request we handle it otherwise we ignore it.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700468 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700469 * Note: since we only keep the last pending request we might get error
470 * messages for earlier role requests that we won't be able to handle
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700471 */
472 synchronized RoleRecvStatus deliverError(OFErrorMsg error)
473 throws SwitchStateException {
474 if (!requestPending) {
475 log.debug("Received an error msg from sw {}, but no pending "
476 + "requests in role-changer; not handling ...",
477 getSwitchInfoString());
478 return RoleRecvStatus.OTHER_EXPECTATION;
479 }
480 if (pendingXid != error.getXid()) {
481 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
482 log.debug("Received an error msg from sw {} for a role request,"
483 + " but not for pending request in role-changer; "
484 + " ignoring error {} ...",
485 getSwitchInfoString(), error);
486 }
487 return RoleRecvStatus.OTHER_EXPECTATION;
488 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700489 // it is an error related to a currently pending role request
490 // message
491 requestPending = false; // we got a response, even though it is an
492 // error
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700493 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
494 counters.roleReplyErrorUnsupported.updateCounterWithFlush();
495 log.error("Received a error msg {} from sw {} in state {} for "
496 + "pending role request {}. Switch driver indicates "
497 + "role-messaging is supported. Possible issues in "
498 + "switch driver configuration?", new Object[] {
Saurav Dasfcdad072014-08-13 14:15:21 -0700499 ((OFBadRequestErrorMsg) error).toString(),
500 getSwitchInfoString(), state, pendingRole
501 });
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700502 return RoleRecvStatus.UNSUPPORTED;
503 }
504
505 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
506 OFRoleRequestFailedErrorMsg rrerr =
507 (OFRoleRequestFailedErrorMsg) error;
508 switch (rrerr.getCode()) {
509 case BAD_ROLE:
510 // switch says that current-role-req has bad role?
511 // for now we disconnect
512 // fall-thru
513 case STALE:
514 // switch says that current-role-req has stale gen-id?
515 // for now we disconnect
516 // fall-thru
517 case UNSUP:
518 // switch says that current-role-req has role that
519 // cannot be supported? for now we disconnect
520 String msgx = String.format("Switch: [%s], State: [%s], "
521 + "received Error to for pending role request [%s]. "
522 + "Error:[%s]. Disconnecting switch ... ",
523 OFChannelHandler.this.getSwitchInfoString(),
524 OFChannelHandler.this.state.toString(),
525 pendingRole, rrerr);
526 throw new SwitchStateException(msgx);
527 default:
528 break;
529 }
530 }
531
Saurav Dasfcdad072014-08-13 14:15:21 -0700532 // This error message was for a role request message but we dont
533 // know
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700534 // how to handle errors for nicira role request messages
535 return RoleRecvStatus.OTHER_EXPECTATION;
536 }
537
538 /**
539 * Check if a pending role request has timed out.
Jonathan Hart6eda2302014-08-14 14:57:03 -0700540 *
Saurav Das6de23322014-08-07 18:51:19 -0700541 * @throws SwitchStateException
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700542 */
Saurav Das6de23322014-08-07 18:51:19 -0700543 void checkTimeout() throws SwitchStateException {
544 if (!requestPending) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700545 return;
Saurav Das6de23322014-08-07 18:51:19 -0700546 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700547 synchronized (this) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700548 if (!requestPending)
549 return;
550 long now = System.currentTimeMillis();
551 if (now - roleSubmitTime > roleTimeoutMs) {
552 // timeout triggered.
553 counters.roleReplyTimeout.updateCounterWithFlush();
Saurav Das6de23322014-08-07 18:51:19 -0700554 state.handleTimedOutRoleReply(OFChannelHandler.this, pendingRole);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700555 }
556 }
557 }
558
559 }
560
Saurav Dasfcdad072014-08-13 14:15:21 -0700561 // *************************
562 // Channel State Machine
563 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700564
565 /**
566 * The state machine for handling the switch/channel state. All state
Saurav Dasfcdad072014-08-13 14:15:21 -0700567 * transitions should happen from within the state machine (and not from
568 * other parts of the code)
Jonathan Hart6eda2302014-08-14 14:57:03 -0700569 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700570 * @author gregor
Saurav Dasfcdad072014-08-13 14:15:21 -0700571 * @author saurav (modified to handle 1.0 & 1.3 switches, EQUAL state,
572 * role-handling )
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700573 */
574 enum ChannelState {
575 /**
576 * Initial state before channel is connected.
577 */
578 INIT(false) {
579 @Override
580 void processOFMessage(OFChannelHandler h, OFMessage m)
581 throws IOException, SwitchStateException {
582 illegalMessageReceived(h, m);
583 }
584
585 @Override
586 void processOFError(OFChannelHandler h, OFErrorMsg m)
587 throws IOException {
588 // need to implement since its abstract but it will never
589 // be called
590 }
591
592 @Override
593 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
594 throws IOException {
595 unhandledMessageReceived(h, m);
596 }
597 },
598
599 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700600 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the
601 * switch. Once we receive the reply, we decide on OF 1.3 or 1.0 switch
602 * - no other protocol version is accepted. We send an OFFeaturesRequest
603 * depending on the protocol version selected Next state is
604 * WAIT_FEATURES_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700605 */
606 WAIT_HELLO(false) {
607 @Override
608 void processOFHello(OFChannelHandler h, OFHello m)
609 throws IOException {
610 // TODO We could check for the optional bitmap, but for now
611 // we are just checking the version number.
612 if (m.getVersion() == OFVersion.OF_13) {
613 log.info("Received {} Hello from {}", m.getVersion(),
614 h.channel.getRemoteAddress());
615 h.ofVersion = OFVersion.OF_13;
616 } else if (m.getVersion() == OFVersion.OF_10) {
617 log.info("Received {} Hello from {} - switching to OF "
618 + "version 1.0", m.getVersion(),
619 h.channel.getRemoteAddress());
620 h.ofVersion = OFVersion.OF_10;
621 } else {
622 log.error("Received Hello of version {} from switch at {}. "
623 + "This controller works with OF1.0 and OF1.3 "
624 + "switches. Disconnecting switch ...",
625 m.getVersion(), h.channel.getRemoteAddress());
626 h.channel.disconnect();
627 return;
628 }
629 h.sendHandshakeFeaturesRequestMessage();
630 h.setState(WAIT_FEATURES_REPLY);
631 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700632
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700633 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700634 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700635 throws IOException, SwitchStateException {
636 illegalMessageReceived(h, m);
637 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700638
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700639 @Override
640 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700641 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700642 throws IOException, SwitchStateException {
643 illegalMessageReceived(h, m);
644 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700645
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700646 @Override
647 void processOFError(OFChannelHandler h, OFErrorMsg m) {
648 logErrorDisconnect(h, m);
649 }
650
651 @Override
652 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
653 throws IOException {
654 unhandledMessageReceived(h, m);
655 }
656 },
657
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700658 /**
659 * We are waiting for a features reply message. Once we receive it, the
Saurav Dasfcdad072014-08-13 14:15:21 -0700660 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0, we
661 * send a SetConfig request, barrier, and GetConfig request and the next
662 * state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700663 * request and the next state is WAIT_PORT_DESC_REPLY.
664 */
665 WAIT_FEATURES_REPLY(false) {
666 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700667 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700668 throws IOException {
669 h.thisdpid = m.getDatapathId().getLong();
670 log.info("Received features reply for switch at {} with dpid {}",
671 h.getSwitchInfoString(), h.thisdpid);
Saurav Dasfcdad072014-08-13 14:15:21 -0700672 // update the controller about this connected switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700673 boolean success = h.controller.addConnectedSwitch(
674 h.thisdpid, h);
675 if (!success) {
676 disconnectDuplicate(h);
677 return;
678 }
679
Saurav Dasfcdad072014-08-13 14:15:21 -0700680 h.featuresReply = m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700681 if (h.ofVersion == OFVersion.OF_10) {
682 h.sendHandshakeSetConfig();
683 h.setState(WAIT_CONFIG_REPLY);
684 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -0700685 // version is 1.3, must get switchport information
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700686 h.sendHandshakeOFPortDescRequest();
687 h.setState(WAIT_PORT_DESC_REPLY);
688 }
689 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700690
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700691 @Override
692 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700693 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700694 throws IOException, SwitchStateException {
695 illegalMessageReceived(h, m);
696 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700697
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700698 @Override
699 void processOFError(OFChannelHandler h, OFErrorMsg m) {
700 logErrorDisconnect(h, m);
701 }
702
703 @Override
704 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
705 throws IOException {
706 unhandledMessageReceived(h, m);
707 }
708 },
709
710 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700711 * We are waiting for a description of the 1.3 switch ports. Once
712 * received, we send a SetConfig request Next State is WAIT_CONFIG_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700713 */
714 WAIT_PORT_DESC_REPLY(false) {
715
716 @Override
717 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
718 throws SwitchStateException {
719 // Read port description
720 if (m.getStatsType() != OFStatsType.PORT_DESC) {
721 log.warn("Expecting port description stats but received stats "
722 + "type {} from {}. Ignoring ...", m.getStatsType(),
723 h.channel.getRemoteAddress());
724 return;
725 }
726 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
727 log.warn("Stats reply indicates more stats from sw {} for "
728 + "port description - not currently handled",
729 h.getSwitchInfoString());
730 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700731 h.portDescReply = (OFPortDescStatsReply) m; // temp store
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700732 log.info("Received port desc reply for switch at {}",
733 h.getSwitchInfoString());
734 try {
735 h.sendHandshakeSetConfig();
736 } catch (IOException e) {
737 log.error("Unable to send setConfig after PortDescReply. "
738 + "Error: {}", e.getMessage());
739 }
740 h.setState(WAIT_CONFIG_REPLY);
741 }
742
743 @Override
744 void processOFError(OFChannelHandler h, OFErrorMsg m)
745 throws IOException, SwitchStateException {
746 logErrorDisconnect(h, m);
747
748 }
749
750 @Override
751 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
752 throws IOException, SwitchStateException {
753 unhandledMessageReceived(h, m);
754
755 }
756 },
757
758 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700759 * We are waiting for a config reply message. Once we receive it we send
760 * a DescriptionStatsRequest to the switch. Next state:
761 * WAIT_DESCRIPTION_STAT_REPLY
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700762 */
763 WAIT_CONFIG_REPLY(false) {
764 @Override
765 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -0700766 @LogMessageDoc(level = "WARN",
767 message = "Config Reply from {switch} has " +
768 "miss length set to {length}",
769 explanation = "The controller requires that the switch " +
770 "use a miss length of 0xffff for correct " +
771 "function",
772 recommendation = "Use a different switch to ensure " +
773 "correct function")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700774 })
775 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
776 throws IOException {
777 if (m.getMissSendLen() == 0xffff) {
778 log.trace("Config Reply from switch {} confirms "
779 + "miss length set to 0xffff",
780 h.getSwitchInfoString());
781 } else {
782 // FIXME: we can't really deal with switches that don't send
783 // full packets. Shouldn't we drop the connection here?
784 log.warn("Config Reply from switch {} has"
785 + "miss length set to {}",
786 h.getSwitchInfoString(),
787 m.getMissSendLen());
788 }
789 h.sendHandshakeDescriptionStatsRequest();
790 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
791 }
792
793 @Override
794 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
795 // do nothing;
796 }
797
798 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700799 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700800 throws IOException, SwitchStateException {
801 illegalMessageReceived(h, m);
802 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700803
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700804 @Override
805 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -0700806 OFStatsReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700807 throws IOException, SwitchStateException {
808 log.error("Received multipart(stats) message sub-type {}",
809 m.getStatsType());
810 illegalMessageReceived(h, m);
811 }
812
813 @Override
814 void processOFError(OFChannelHandler h, OFErrorMsg m) {
815 logErrorDisconnect(h, m);
816 }
817
818 @Override
819 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
820 throws IOException {
821 h.pendingPortStatusMsg.add(m);
822 }
823 },
824
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700825 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700826 * We are waiting for a OFDescriptionStat message from the switch. Once
827 * we receive any stat message we try to parse it. If it's not a
828 * description stats message we disconnect. If its the expected
829 * description stats message, we: - use the switch driver to bind the
830 * switch and get an IOFSwitch instance - setup the IOFSwitch instance -
831 * add switch to FloodlightProvider(Controller) and send the initial
832 * role request to the switch. Next state: WAIT_INITIAL_ROLE In the
833 * typical case, where switches support role request messages the next
834 * state is where we expect the role reply message. In the special case
835 * that where the switch does not support any kind of role request
836 * messages, we don't send a role message, but we do request mastership
837 * from the registry service. This controller should become master once
838 * we hear back from the registry service. All following states will
839 * have a h.sw instance!
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700840 */
841 WAIT_DESCRIPTION_STAT_REPLY(false) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700842 @LogMessageDoc(message = "Switch {switch info} bound to class " +
843 "{switch driver}, description {switch description}",
844 explanation = "The specified switch has been bound to " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700845 "a switch driver based on the switch description" +
846 "received from the switch")
847 @Override
848 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
849 throws SwitchStateException {
850 // Read description, if it has been updated
851 if (m.getStatsType() != OFStatsType.DESC) {
852 log.warn("Expecting Description stats but received stats "
853 + "type {} from {}. Ignoring ...", m.getStatsType(),
854 h.channel.getRemoteAddress());
855 return;
856 }
857 log.info("Received switch description reply from switch at {}",
858 h.channel.getRemoteAddress());
859 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasfcdad072014-08-13 14:15:21 -0700860 // Here is where we differentiate between different kinds of
861 // switches
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700862 h.sw = h.controller.getOFSwitchInstance(drep, h.ofVersion);
863 // set switch information
864 h.sw.setOFVersion(h.ofVersion);
865 ((OFSwitchImplBase) h.sw).setFeaturesReply(h.featuresReply);
866 ((OFSwitchImplBase) h.sw).setPortDescReply(h.portDescReply);
867 h.sw.setConnected(true);
868 h.sw.setChannel(h.channel);
869 h.sw.setFloodlightProvider(h.controller);
870 h.sw.setThreadPoolService(h.controller.getThreadPoolService());
871 try {
872 h.sw.setDebugCounterService(h.controller.getDebugCounter());
873 } catch (CounterException e) {
874 h.counters.switchCounterRegistrationFailed
875 .updateCounterNoFlush();
876 log.warn("Could not register counters for switch {} ",
Saurav Dasfcdad072014-08-13 14:15:21 -0700877 h.getSwitchInfoString(), e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700878 }
879
880 log.info("Switch {} bound to class {}, description {}",
Saurav Dasfcdad072014-08-13 14:15:21 -0700881 new Object[] {h.sw, h.sw.getClass(), drep});
882 // Put switch in EQUAL mode until we hear back from the global
883 // registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700884 log.debug("Setting new switch {} to EQUAL and sending Role request",
885 h.sw.getStringId());
886 h.setSwitchRole(Role.EQUAL);
887 try {
888 boolean supportsRRMsg = h.roleChanger.sendRoleRequest(Role.EQUAL,
889 RoleRecvStatus.MATCHED_CURRENT_ROLE);
890 if (!supportsRRMsg) {
891 log.warn("Switch {} does not support role request messages "
892 + "of any kind. No role messages were sent. "
893 + "This controller instance SHOULD become MASTER "
894 + "from the registry process. ",
895 h.getSwitchInfoString());
896 }
897 h.setState(WAIT_INITIAL_ROLE);
898 // request control of switch from global registry -
899 // necessary even if this is the only controller the
900 // switch is connected to.
901 h.controller.submitRegistryRequest(h.sw.getId());
902 } catch (IOException e) {
903 log.error("Exception when sending role request: {} ",
904 e.getMessage());
905 // FIXME shouldn't we disconnect?
906 }
907 }
908
909 @Override
910 void processOFError(OFChannelHandler h, OFErrorMsg m) {
911 logErrorDisconnect(h, m);
912 }
913
914 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700915 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700916 throws IOException, SwitchStateException {
917 illegalMessageReceived(h, m);
918 }
919
920 @Override
921 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
922 throws IOException {
923 h.pendingPortStatusMsg.add(m);
924 }
925 },
926
927 /**
928 * We are waiting for a role reply message in response to a role request
929 * sent after hearing back from the registry service -- OR -- we are
930 * just waiting to hear back from the registry service in the case that
931 * the switch does not support role messages. If completed successfully,
Saurav Dasfcdad072014-08-13 14:15:21 -0700932 * the controller's role for this switch will be set here. Before we
933 * move to the state corresponding to the role, we allow the switch
934 * specific driver to complete its configuration. This configuration
935 * typically depends on the role the controller is playing for this
936 * switch. And so we set the switch role (for 'this' controller) before
937 * we start the driver-sub-handshake. Next State:
938 * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700939 */
940 WAIT_INITIAL_ROLE(false) {
941 @Override
942 void processOFError(OFChannelHandler h, OFErrorMsg m)
943 throws SwitchStateException {
944 // role changer will ignore the error if it isn't for it
945 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
946 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
947 logError(h, m);
948 }
949 }
950
951 @Override
952 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
953 throws IOException, SwitchStateException {
954 Role role = extractNiciraRoleReply(h, m);
955 // If role == null it means the vendor (experimenter) message
956 // wasn't really a Nicira role reply. We ignore this case.
957 if (role != null) {
958 RoleReplyInfo rri = new RoleReplyInfo(role, null, m.getXid());
959 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
960 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
961 setRoleAndStartDriverHandshake(h, rri.getRole());
962 } // else do nothing - wait for the correct expected reply
963 } else {
964 unhandledMessageReceived(h, m);
965 }
966 }
967
968 @Override
969 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
970 throws SwitchStateException, IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -0700971 RoleReplyInfo rri = extractOFRoleReply(h, m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700972 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
973 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
974 setRoleAndStartDriverHandshake(h, rri.getRole());
975 } // else do nothing - wait for the correct expected reply
976 }
977
978 @Override
979 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -0700980 RoleRecvStatus expectation) throws IOException {
981 // typically this is triggered for a switch where role messages
982 // are not supported - we confirm that the role being set is
983 // master and move to the next state
984 if (expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
985 if (role == Role.MASTER) {
986 setRoleAndStartDriverHandshake(h, role);
987 } else {
988 log.error("Expected MASTER role from registry for switch "
989 + "which has no support for role-messages."
990 + "Received {}. It is possible that this switch "
991 + "is connected to other controllers, in which "
992 + "case it should support role messages - not "
993 + "moving forward.", role);
994 }
995 } // else do nothing - wait to hear back from registry
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700996
997 }
998
999 private void setRoleAndStartDriverHandshake(OFChannelHandler h,
1000 Role role) throws IOException {
1001 h.setSwitchRole(role);
1002 h.sw.startDriverHandshake();
1003 if (h.sw.isDriverHandshakeComplete()) {
1004 Role mySwitchRole = h.sw.getRole();
1005 if (mySwitchRole == Role.MASTER) {
1006 log.info("Switch-driver sub-handshake complete. "
1007 + "Activating switch {} with Role: MASTER",
1008 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001009 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001010 boolean success = h.controller.addActivatedMasterSwitch(
1011 h.sw.getId(), h.sw);
1012 if (!success) {
1013 disconnectDuplicate(h);
1014 return;
1015 }
1016 h.setState(MASTER);
1017 } else {
1018 log.info("Switch-driver sub-handshake complete. "
1019 + "Activating switch {} with Role: EQUAL",
1020 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001021 handlePendingPortStatusMessages(h); // before activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001022 boolean success = h.controller.addActivatedEqualSwitch(
1023 h.sw.getId(), h.sw);
1024 if (!success) {
1025 disconnectDuplicate(h);
1026 return;
1027 }
1028 h.setState(EQUAL);
1029 }
1030 } else {
1031 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
1032 }
1033 }
1034
1035 @Override
Saurav Das85399942014-09-02 16:11:48 -07001036 public void handleTimedOutHandshake(OFChannelHandler h,
1037 ChannelHandlerContext ctx) throws IOException {
1038 log.info("Handshake timed out waiting to hear back from registry "
1039 + "service. Moving to Role EQUAL for switch {}",
1040 h.getSwitchInfoString());
1041 setRoleAndStartDriverHandshake(h, Role.EQUAL);
1042 }
1043
1044 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001045 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001046 throws IOException, SwitchStateException {
1047 illegalMessageReceived(h, m);
1048 }
1049
1050 @Override
1051 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1052 throws SwitchStateException {
1053 illegalMessageReceived(h, m);
1054 }
1055
1056 @Override
1057 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1058 throws IOException, SwitchStateException {
1059 h.pendingPortStatusMsg.add(m);
1060
1061 }
1062 },
1063
1064 /**
1065 * We are waiting for the respective switch driver to complete its
Saurav Dasfcdad072014-08-13 14:15:21 -07001066 * configuration. Notice that we do not consider this to be part of the
1067 * main switch-controller handshake. But we do consider it as a step
1068 * that comes before we declare the switch as available to the
1069 * controller. Next State: depends on the role of this controller for
1070 * this switch - either MASTER or EQUAL.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001071 */
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001072 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(false) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001073
1074 @Override
1075 void processOFError(OFChannelHandler h, OFErrorMsg m)
1076 throws IOException {
1077 // will never be called. We override processOFMessage
1078 }
1079
1080 @Override
1081 void processOFMessage(OFChannelHandler h, OFMessage m)
1082 throws IOException {
1083 if (m.getType() == OFType.ECHO_REQUEST)
Saurav Dasfcdad072014-08-13 14:15:21 -07001084 processOFEchoRequest(h, (OFEchoRequest) m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001085 else {
1086 // FIXME: other message to handle here?
1087 h.sw.processDriverHandshakeMessage(m);
1088 if (h.sw.isDriverHandshakeComplete()) {
1089 // consult the h.sw role and goto that state
1090 Role mySwitchRole = h.sw.getRole();
1091 if (mySwitchRole == Role.MASTER) {
1092 log.info("Switch-driver sub-handshake complete. "
1093 + "Activating switch {} with Role: MASTER",
1094 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001095 handlePendingPortStatusMessages(h); // before
1096 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001097 boolean success = h.controller.addActivatedMasterSwitch(
1098 h.sw.getId(), h.sw);
1099 if (!success) {
1100 disconnectDuplicate(h);
1101 return;
1102 }
1103 h.setState(MASTER);
1104 } else {
1105 log.info("Switch-driver sub-handshake complete. "
1106 + "Activating switch {} with Role: EQUAL",
1107 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001108 handlePendingPortStatusMessages(h); // before
1109 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001110 boolean success = h.controller.addActivatedEqualSwitch(
1111 h.sw.getId(), h.sw);
1112 if (!success) {
1113 disconnectDuplicate(h);
1114 return;
1115 }
1116 h.setState(EQUAL);
1117 }
1118 }
1119 }
1120 }
1121
1122 @Override
1123 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1124 throws IOException, SwitchStateException {
1125 h.pendingPortStatusMsg.add(m);
1126 }
1127 },
1128
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001129 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001130 * This controller is in MASTER role for this switch. We enter this
1131 * state after requesting and winning control from the global registry.
1132 * The main handshake as well as the switch-driver sub-handshake is
1133 * complete at this point. // XXX S reconsider below In the (near)
1134 * future we may deterministically assign controllers to switches at
1135 * startup. We only leave this state if the switch disconnects or if we
1136 * send a role request for SLAVE /and/ receive the role reply for SLAVE.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001137 */
1138 MASTER(true) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001139 @LogMessageDoc(level = "WARN",
1140 message = "Received permission error from switch {} while" +
1141 "being master. Reasserting master role.",
1142 explanation = "The switch has denied an operation likely " +
1143 "indicating inconsistent controller roles",
1144 recommendation = "This situation can occurs transiently during role" +
1145 " changes. If, however, the condition persists or happens" +
1146 " frequently this indicates a role inconsistency. " +
1147 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001148 @Override
1149 void processOFError(OFChannelHandler h, OFErrorMsg m)
1150 throws IOException, SwitchStateException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001151 // first check if the error msg is in response to a role-request
1152 // message
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001153 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1154 if (rrstatus != RoleRecvStatus.OTHER_EXPECTATION) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001155 // rolechanger has handled the error message - we are done
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001156 return;
1157 }
1158
1159 // if we get here, then the error message is for something else
1160 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
1161 ((OFBadRequestErrorMsg) m).getCode() ==
Saurav Dasfcdad072014-08-13 14:15:21 -07001162 OFBadRequestCode.EPERM) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001163 // We are the master controller and the switch returned
1164 // a permission error. This is a likely indicator that
1165 // the switch thinks we are slave. Reassert our
1166 // role
1167 // FIXME: this could be really bad during role transitions
1168 // if two controllers are master (even if its only for
1169 // a brief period). We might need to see if these errors
1170 // persist before we reassert
1171 h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush();
1172 log.warn("Received permission error from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001173 "being master. Reasserting master role.",
1174 h.getSwitchInfoString());
1175 // h.controller.reassertRole(h, Role.MASTER);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001176 // XXX S reassert in role changer or reconsider if all this
1177 // stuff is really needed
1178 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
Saurav Dasfcdad072014-08-13 14:15:21 -07001179 ((OFFlowModFailedErrorMsg) m).getCode() ==
1180 OFFlowModFailedCode.ALL_TABLES_FULL) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001181 h.sw.setTableFull(true);
1182 } else {
1183 logError(h, m);
1184 }
1185 h.dispatchMessage(m);
1186 }
1187
1188 @Override
1189 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001190 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001191 h.sw.deliverStatisticsReply(m);
1192 }
1193
1194 @Override
1195 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1196 throws IOException, SwitchStateException {
1197 Role role = extractNiciraRoleReply(h, m);
1198 if (role == null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001199 // The message wasn't really a Nicira role reply. We just
1200 // dispatch it to the OFMessage listeners in this case.
1201 h.dispatchMessage(m);
1202 return;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001203 }
1204
1205 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Saurav Dasfcdad072014-08-13 14:15:21 -07001206 new RoleReplyInfo(role, null, m.getXid()));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001207 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001208 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001209 }
1210 }
1211
1212 @Override
1213 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1214 throws SwitchStateException, IOException {
1215 RoleReplyInfo rri = extractOFRoleReply(h, m);
1216 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1217 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001218 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001219 }
1220 }
1221
1222 @Override
1223 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1224 throws IOException, SwitchStateException {
1225 handlePortStatusMessage(h, m, true);
1226 h.dispatchMessage(m);
1227 }
1228
1229 @Override
1230 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1231 throws IOException {
1232 h.dispatchMessage(m);
1233 }
1234
1235 @Override
1236 void processOFFlowRemoved(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001237 OFFlowRemoved m) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001238 h.dispatchMessage(m);
1239 }
1240
1241 @Override
1242 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1243 throws IOException {
Saurav Dasd84178f2014-09-29 17:48:54 -07001244 h.sw.deliverBarrierReply(m);
Saurav Dasfcdad072014-08-13 14:15:21 -07001245 h.dispatchMessage(m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001246 }
1247
1248 },
1249
1250 /**
1251 * This controller is in EQUAL role for this switch. We enter this state
1252 * after some /other/ controller instance wins mastership-role over this
1253 * switch. The EQUAL role can be considered the same as the SLAVE role
1254 * if this controller does NOT send commands or packets to the switch.
1255 * This should always be true for OF1.0 switches. XXX S need to enforce.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001256 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001257 * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE,
Saurav Dasfcdad072014-08-13 14:15:21 -07001258 * gives us the flexibility that if an app wants to send
1259 * commands/packets to switches, it can, even thought it is running on a
1260 * controller instance that is not in a MASTER role for this switch. Of
1261 * course, it is the job of the app to ensure that commands/packets sent
1262 * by this (EQUAL) controller instance does not clash/conflict with
1263 * commands/packets sent by the MASTER controller for this switch.
1264 * Neither the controller instances, nor the switch provides any kind of
1265 * resolution mechanism should conflicts occur.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001266 */
1267 EQUAL(true) {
1268 @Override
1269 void processOFError(OFChannelHandler h, OFErrorMsg m)
1270 throws IOException, SwitchStateException {
1271 // role changer will ignore the error if it isn't for it
1272 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1273 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
1274 logError(h, m);
1275 h.dispatchMessage(m);
1276 }
1277 }
1278
1279 @Override
1280 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001281 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001282 h.sw.deliverStatisticsReply(m);
1283 }
1284
1285 @Override
1286 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1287 throws IOException, SwitchStateException {
1288 Role role = extractNiciraRoleReply(h, m);
1289 // If role == null it means the message wasn't really a
1290 // Nicira role reply. We ignore it in this state.
1291 if (role != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001292 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001293 new RoleReplyInfo(role, null, m.getXid()));
Saurav Dasfcdad072014-08-13 14:15:21 -07001294 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1295 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001296 }
1297 } else {
1298 unhandledMessageReceived(h, m);
1299 }
1300 }
1301
1302 @Override
1303 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1304 throws SwitchStateException, IOException {
1305 RoleReplyInfo rri = extractOFRoleReply(h, m);
1306 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1307 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001308 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001309 }
1310 }
1311
1312 // XXX S needs more handlers for 1.3 switches in equal role
1313
1314 @Override
1315 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1316 throws IOException, SwitchStateException {
1317 handlePortStatusMessage(h, m, true);
1318 }
1319
1320 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001321 @LogMessageDoc(level = "WARN",
1322 message = "Received PacketIn from switch {} while" +
1323 "being slave. Reasserting slave role.",
1324 explanation = "The switch has receive a PacketIn despite being " +
1325 "in slave role indicating inconsistent controller roles",
1326 recommendation = "This situation can occurs transiently during role" +
1327 " changes. If, however, the condition persists or happens" +
1328 " frequently this indicates a role inconsistency. " +
1329 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001330 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException {
1331 // we don't expect packetIn while slave, reassert we are slave
1332 h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush();
1333 log.warn("Received PacketIn from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001334 "being slave. Reasserting slave role.", h.sw);
1335 // h.controller.reassertRole(h, Role.SLAVE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001336 // XXX reassert in role changer
1337 }
1338 };
1339
1340 private final boolean handshakeComplete;
Saurav Dasfcdad072014-08-13 14:15:21 -07001341
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001342 ChannelState(boolean handshakeComplete) {
1343 this.handshakeComplete = handshakeComplete;
1344 }
1345
1346 /**
1347 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07001348 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001349 * @return true if the handshake is complete
1350 */
1351 public boolean isHandshakeComplete() {
1352 return handshakeComplete;
1353 }
1354
1355 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001356 * Get a string specifying the switch connection, state, and message
1357 * received. To be used as message for SwitchStateException or log
1358 * messages
Jonathan Hart6eda2302014-08-14 14:57:03 -07001359 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001360 * @param h The channel handler (to get switch information_
1361 * @param m The OFMessage that has just been received
Saurav Dasfcdad072014-08-13 14:15:21 -07001362 * @param details A string giving more details about the exact nature of
1363 * the problem.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001364 * @return
1365 */
1366 // needs to be protected because enum members are actually subclasses
1367 protected String getSwitchStateMessage(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001368 OFMessage m,
1369 String details) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001370 return String.format("Switch: [%s], State: [%s], received: [%s]"
Saurav Dasfcdad072014-08-13 14:15:21 -07001371 + ", details: %s",
1372 h.getSwitchInfoString(),
1373 this.toString(),
1374 m.getType().toString(),
1375 details);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001376 }
1377
1378 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001379 * We have an OFMessage we didn't expect given the current state and we
1380 * want to treat this as an error. We currently throw an exception that
1381 * will terminate the connection However, we could be more forgiving
Jonathan Hart6eda2302014-08-14 14:57:03 -07001382 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001383 * @param h the channel handler that received the message
1384 * @param m the message
1385 * @throws SwitchStateException
1386 * @throws SwitchStateExeption we always through the execption
1387 */
1388 // needs to be protected because enum members are acutally subclasses
1389 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
1390 throws SwitchStateException {
1391 String msg = getSwitchStateMessage(h, m,
1392 "Switch should never send this message in the current state");
1393 throw new SwitchStateException(msg);
1394
1395 }
1396
1397 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001398 * We have an OFMessage we didn't expect given the current state and we
1399 * want to ignore the message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001400 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001401 * @param h the channel handler the received the message
1402 * @param m the message
1403 */
1404 protected void unhandledMessageReceived(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001405 OFMessage m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001406 h.counters.unhandledMessage.updateCounterNoFlush();
1407 if (log.isDebugEnabled()) {
1408 String msg = getSwitchStateMessage(h, m,
1409 "Ignoring unexpected message");
1410 log.debug(msg);
1411 }
1412 }
1413
1414 /**
1415 * Log an OpenFlow error message from a switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001416 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001417 * @param sw The switch that sent the error
1418 * @param error The error message
1419 */
Saurav Dasfcdad072014-08-13 14:15:21 -07001420 @LogMessageDoc(level = "ERROR",
1421 message = "Error {error type} {error code} from {switch} " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001422 "in state {state}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001423 explanation = "The switch responded with an unexpected error" +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001424 "to an OpenFlow message from the controller",
Saurav Dasfcdad072014-08-13 14:15:21 -07001425 recommendation = "This could indicate improper network operation. " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001426 "If the problem persists restarting the switch and " +
1427 "controller may help."
1428 )
Saurav Dasfcdad072014-08-13 14:15:21 -07001429 protected void logError(OFChannelHandler h, OFErrorMsg error) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001430 log.error("{} from switch {} in state {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001431 new Object[] {
1432 error,
1433 h.getSwitchInfoString(),
1434 this.toString()});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001435 }
1436
1437 /**
1438 * Log an OpenFlow error message from a switch and disconnect the
1439 * channel
Jonathan Hart6eda2302014-08-14 14:57:03 -07001440 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001441 * @param sw The switch that sent the error
1442 * @param error The error message
1443 */
1444 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
1445 logError(h, error);
1446 h.channel.disconnect();
1447 }
1448
1449 /**
1450 * log an error message for a duplicate dpid and disconnect this channel
1451 */
1452 protected void disconnectDuplicate(OFChannelHandler h) {
1453 log.error("Duplicated dpid or incompleted cleanup - "
1454 + "disconnecting channel {}", h.getSwitchInfoString());
1455 h.duplicateDpidFound = Boolean.TRUE;
1456 h.channel.disconnect();
1457 }
1458
1459 /**
1460 * Extract the role from an OFVendor message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001461 *
Saurav Dasfcdad072014-08-13 14:15:21 -07001462 * Extract the role from an OFVendor message if the message is a Nicira
1463 * role reply. Otherwise return null.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001464 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001465 * @param h The channel handler receiving the message
1466 * @param vendorMessage The vendor message to parse.
1467 * @return The role in the message if the message is a Nicira role
Saurav Dasfcdad072014-08-13 14:15:21 -07001468 * reply, null otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001469 * @throws SwitchStateException If the message is a Nicira role reply
Saurav Dasfcdad072014-08-13 14:15:21 -07001470 * but the numeric role value is unknown.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001471 */
1472 protected Role extractNiciraRoleReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001473 OFExperimenter experimenterMsg) throws SwitchStateException {
1474 int vendor = (int) experimenterMsg.getExperimenter();
1475 if (vendor != 0x2320) // magic number representing nicira
1476 return null;
1477 OFNiciraControllerRoleReply nrr =
1478 (OFNiciraControllerRoleReply) experimenterMsg;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001479
Saurav Dasfcdad072014-08-13 14:15:21 -07001480 Role role = null;
1481 OFNiciraControllerRole ncr = nrr.getRole();
1482 switch (ncr) {
1483 case ROLE_MASTER:
1484 role = Role.MASTER;
1485 break;
1486 case ROLE_OTHER:
1487 role = Role.EQUAL;
1488 break;
1489 case ROLE_SLAVE:
1490 role = Role.SLAVE;
1491 break;
1492 default: // handled below
1493 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001494
Saurav Dasfcdad072014-08-13 14:15:21 -07001495 if (role == null) {
1496 String msg = String.format("Switch: [%s], State: [%s], "
1497 + "received NX_ROLE_REPLY with invalid role "
1498 + "value %d",
1499 h.getSwitchInfoString(),
1500 this.toString(),
1501 nrr.getRole());
1502 throw new SwitchStateException(msg);
1503 }
1504 return role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001505 }
1506
1507 /**
1508 * Helper class returns role reply information in the format understood
1509 * by the controller.
1510 */
1511 protected class RoleReplyInfo {
1512 private Role role;
1513 private U64 genId;
1514 private long xid;
1515
Saurav Dasfcdad072014-08-13 14:15:21 -07001516 RoleReplyInfo(Role role, U64 genId, long xid) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001517 this.role = role;
1518 this.genId = genId;
1519 this.xid = xid;
1520 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001521
1522 public Role getRole() {
1523 return role;
1524 }
1525
1526 public U64 getGenId() {
1527 return genId;
1528 }
1529
1530 public long getXid() {
1531 return xid;
1532 }
1533
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001534 @Override
1535 public String toString() {
1536 return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
1537 }
1538 }
1539
1540 /**
1541 * Extract the role information from an OF1.3 Role Reply Message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001542 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001543 * @param h
1544 * @param rrmsg
1545 * @return RoleReplyInfo object
1546 * @throws SwitchStateException
1547 */
1548 protected RoleReplyInfo extractOFRoleReply(OFChannelHandler h,
1549 OFRoleReply rrmsg) throws SwitchStateException {
1550 OFControllerRole cr = rrmsg.getRole();
1551 Role role = null;
Saurav Dasfcdad072014-08-13 14:15:21 -07001552 switch (cr) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001553 case ROLE_EQUAL:
1554 role = Role.EQUAL;
1555 break;
1556 case ROLE_MASTER:
1557 role = Role.MASTER;
1558 break;
1559 case ROLE_SLAVE:
1560 role = Role.SLAVE;
1561 break;
1562 case ROLE_NOCHANGE: // switch should send current role
1563 default:
1564 String msg = String.format("Unknown controller role {} "
1565 + "received from switch {}", cr, h.sw);
1566 throw new SwitchStateException(msg);
1567 }
1568
1569 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
1570 }
1571
1572 /**
1573 * Handles all pending port status messages before a switch is declared
1574 * activated in MASTER or EQUAL role. Note that since this handling
Saurav Dasfcdad072014-08-13 14:15:21 -07001575 * precedes the activation (and therefore notification to
1576 * IOFSwitchListerners) the changes to ports will already be visible
1577 * once the switch is activated. As a result, no notifications are sent
1578 * out for these pending portStatus messages.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001579 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001580 * @param h
1581 * @throws SwitchStateException
1582 */
1583 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
1584 try {
1585 handlePendingPortStatusMessages(h, 0);
1586 } catch (SwitchStateException e) {
1587 // do nothing - exception msg printed
1588 }
1589 }
1590
1591 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
1592 throws SwitchStateException {
1593 if (h.sw == null) {
1594 String msg = "State machine error: switch is null. Should never " +
1595 "happen";
1596 throw new SwitchStateException(msg);
1597 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001598 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
1599 for (OFPortStatus ps : h.pendingPortStatusMsg) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001600 temp.add(ps);
1601 handlePortStatusMessage(h, ps, false);
1602 }
1603 temp.clear();
1604 // expensive but ok - we don't expect too many port-status messages
1605 // note that we cannot use clear(), because of the reasons below
1606 h.pendingPortStatusMsg.removeAll(temp);
Saurav Dasfcdad072014-08-13 14:15:21 -07001607 // the iterator above takes a snapshot of the list - so while we
1608 // were
1609 // dealing with the pending port-status messages, we could have
1610 // received
1611 // newer ones. Handle them recursively, but break the recursion
1612 // after
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001613 // five steps to avoid an attack.
1614 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1615 handlePendingPortStatusMessages(h, index);
1616 }
1617 }
1618
1619 /**
1620 * Handle a port status message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001621 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001622 * Handle a port status message by updating the port maps in the
Saurav Dasfcdad072014-08-13 14:15:21 -07001623 * IOFSwitch instance and notifying Controller about the change so it
1624 * can dispatch a switch update.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001625 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001626 * @param h The OFChannelHhandler that received the message
1627 * @param m The PortStatus message we received
Saurav Dasfcdad072014-08-13 14:15:21 -07001628 * @param doNotify if true switch port changed events will be dispatched
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001629 * @throws SwitchStateException
Jonathan Hart6eda2302014-08-14 14:57:03 -07001630 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001631 */
1632 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1633 boolean doNotify) throws SwitchStateException {
1634 if (h.sw == null) {
1635 String msg = getSwitchStateMessage(h, m,
1636 "State machine error: switch is null. Should never " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001637 "happen");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001638 throw new SwitchStateException(msg);
1639 }
1640
1641 Collection<PortChangeEvent> changes = h.sw.processOFPortStatus(m);
1642 if (doNotify) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001643 for (PortChangeEvent ev : changes)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001644 h.controller.notifyPortChanged(h.sw.getId(), ev.port, ev.type);
1645 }
1646 }
1647
1648 /**
1649 * Checks if the role received (from the role-reply msg) is different
1650 * from the existing role in the IOFSwitch object for this controller.
Saurav Dasfcdad072014-08-13 14:15:21 -07001651 * If so, it transitions the controller to the new role. Note that the
1652 * caller should have already verified that the role-reply msg received
1653 * was in response to a role-request msg sent out by this controller
1654 * after hearing from the registry service.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001655 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001656 * @param h the ChannelHandler that received the message
1657 * @param role the role in the recieved role reply message
1658 */
1659 protected void checkAndSetRoleTransition(OFChannelHandler h, Role role) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001660 // we received a role-reply in response to a role message
1661 // sent after hearing from the registry service. It is
1662 // possible that the role of this controller instance for
1663 // this switch has changed:
1664 // for 1.0 switch: from MASTER to SLAVE
1665 // for 1.3 switch: from MASTER to EQUAL
1666 if ((h.sw.getRole() == Role.MASTER && role == Role.SLAVE) ||
1667 (h.sw.getRole() == Role.MASTER && role == Role.EQUAL)) {
1668 // the mastership has changed
Saurav Dasd84178f2014-09-29 17:48:54 -07001669 if (role == Role.SLAVE) {
1670 role = Role.EQUAL;
1671 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001672 h.sw.setRole(role);
1673 h.setState(EQUAL);
1674 h.controller.transitionToEqualSwitch(h.sw.getId());
1675 return;
1676 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001677
Saurav Dasfcdad072014-08-13 14:15:21 -07001678 // or for both 1.0 and 1.3 switches from EQUAL to MASTER.
1679 // note that for 1.0, even though we mean SLAVE,
1680 // internally we call the role EQUAL.
1681 if (h.sw.getRole() == Role.EQUAL && role == Role.MASTER) {
1682 // the mastership has changed
1683 h.sw.setRole(role);
1684 h.setState(MASTER);
1685 h.controller.transitionToMasterSwitch(h.sw.getId());
1686 return;
1687 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001688 }
1689
1690 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001691 * Process an OF message received on the channel and update state
1692 * accordingly.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001693 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001694 * The main "event" of the state machine. Process the received message,
1695 * send follow up message if required and update state if required.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001696 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001697 * Switches on the message type and calls more specific event handlers
Saurav Dasfcdad072014-08-13 14:15:21 -07001698 * for each individual OF message type. If we receive a message that is
1699 * supposed to be sent from a controller to a switch we throw a
1700 * SwitchStateExeption.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001701 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001702 * The more specific handlers can also throw SwitchStateExceptions
Jonathan Hart6eda2302014-08-14 14:57:03 -07001703 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001704 * @param h The OFChannelHandler that received the message
1705 * @param m The message we received.
1706 * @throws SwitchStateException
1707 * @throws IOException
1708 */
1709 void processOFMessage(OFChannelHandler h, OFMessage m)
1710 throws IOException, SwitchStateException {
1711 h.roleChanger.checkTimeout();
Saurav Dasfcdad072014-08-13 14:15:21 -07001712 switch (m.getType()) {
1713 case HELLO:
1714 processOFHello(h, (OFHello) m);
1715 break;
1716 case BARRIER_REPLY:
1717 processOFBarrierReply(h, (OFBarrierReply) m);
1718 break;
1719 case ECHO_REPLY:
1720 processOFEchoReply(h, (OFEchoReply) m);
1721 break;
1722 case ECHO_REQUEST:
1723 processOFEchoRequest(h, (OFEchoRequest) m);
1724 break;
1725 case ERROR:
1726 processOFError(h, (OFErrorMsg) m);
1727 break;
1728 case FEATURES_REPLY:
1729 processOFFeaturesReply(h, (OFFeaturesReply) m);
1730 break;
1731 case FLOW_REMOVED:
1732 processOFFlowRemoved(h, (OFFlowRemoved) m);
1733 break;
1734 case GET_CONFIG_REPLY:
1735 processOFGetConfigReply(h, (OFGetConfigReply) m);
1736 break;
1737 case PACKET_IN:
1738 processOFPacketIn(h, (OFPacketIn) m);
1739 break;
1740 case PORT_STATUS:
1741 processOFPortStatus(h, (OFPortStatus) m);
1742 break;
1743 case QUEUE_GET_CONFIG_REPLY:
1744 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1745 break;
1746 case STATS_REPLY: // multipart_reply in 1.3
1747 processOFStatisticsReply(h, (OFStatsReply) m);
1748 break;
1749 case EXPERIMENTER:
1750 processOFExperimenter(h, (OFExperimenter) m);
1751 break;
1752 case ROLE_REPLY:
1753 processOFRoleReply(h, (OFRoleReply) m);
1754 break;
1755 case GET_ASYNC_REPLY:
1756 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1757 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001758
Saurav Dasfcdad072014-08-13 14:15:21 -07001759 // The following messages are sent to switches. The controller
1760 // should never receive them
1761 case SET_CONFIG:
1762 case GET_CONFIG_REQUEST:
1763 case PACKET_OUT:
1764 case PORT_MOD:
1765 case QUEUE_GET_CONFIG_REQUEST:
1766 case BARRIER_REQUEST:
1767 case STATS_REQUEST: // multipart request in 1.3
1768 case FEATURES_REQUEST:
1769 case FLOW_MOD:
1770 case GROUP_MOD:
1771 case TABLE_MOD:
1772 case GET_ASYNC_REQUEST:
1773 case SET_ASYNC:
1774 case METER_MOD:
1775 default:
1776 illegalMessageReceived(h, m);
1777 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001778 }
1779 }
1780
1781 /*-----------------------------------------------------------------
1782 * Default implementation for message handlers in any state.
1783 *
1784 * Individual states must override these if they want a behavior
1785 * that differs from the default.
1786 *
1787 * In general, these handlers simply ignore the message and do
1788 * nothing.
1789 *
1790 * There are some exceptions though, since some messages really
1791 * are handled the same way in every state (e.g., ECHO_REQUST) or
1792 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1793 -----------------------------------------------------------------*/
1794
1795 void processOFHello(OFChannelHandler h, OFHello m)
1796 throws IOException, SwitchStateException {
1797 // we only expect hello in the WAIT_HELLO state
1798 illegalMessageReceived(h, m);
1799 }
1800
1801 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1802 throws IOException {
1803 // Silently ignore.
1804 }
1805
1806 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001807 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001808 if (h.ofVersion == null) {
1809 log.error("No OF version set for {}. Not sending Echo REPLY",
1810 h.channel.getRemoteAddress());
1811 return;
1812 }
1813 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1814 OFEchoReply reply = factory
1815 .buildEchoReply()
1816 .setXid(m.getXid())
1817 .setData(m.getData())
1818 .build();
1819 h.channel.write(Collections.singletonList(reply));
1820 }
1821
1822 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001823 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001824 // Do nothing with EchoReplies !!
1825 }
1826
1827 // no default implementation for OFError
1828 // every state must override it
1829 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1830 throws IOException, SwitchStateException;
1831
Saurav Dasfcdad072014-08-13 14:15:21 -07001832 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001833 throws IOException, SwitchStateException {
1834 unhandledMessageReceived(h, m);
1835 }
1836
1837 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001838 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001839 unhandledMessageReceived(h, m);
1840 }
1841
1842 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1843 throws IOException, SwitchStateException {
1844 // we only expect config replies in the WAIT_CONFIG_REPLY state
1845 illegalMessageReceived(h, m);
1846 }
1847
1848 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1849 throws IOException {
1850 unhandledMessageReceived(h, m);
1851 }
1852
1853 // no default implementation. Every state needs to handle it.
1854 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1855 throws IOException, SwitchStateException;
1856
1857 void processOFQueueGetConfigReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001858 OFQueueGetConfigReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001859 throws IOException {
1860 unhandledMessageReceived(h, m);
1861 }
1862
1863 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1864 throws IOException, SwitchStateException {
1865 unhandledMessageReceived(h, m);
1866 }
1867
1868 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1869 throws IOException, SwitchStateException {
1870 // TODO: it might make sense to parse the vendor message here
1871 // into the known vendor messages we support and then call more
1872 // specific event handlers
1873 unhandledMessageReceived(h, m);
1874 }
1875
1876 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1877 throws SwitchStateException, IOException {
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001878 // A role reply message received by the default handler implies
1879 // that the channel state-machine is in a state where it does not
1880 // expect a role message. That in turn implies that role-request was
1881 // sent out by this controller, as a result of a callback
1882 // from the registry service, because the chosen master (some other
1883 // instance) died, while this controller instance has not completed
1884 // handshake yet. Best to disconnect switch and start over.
1885 illegalMessageReceived(h, m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001886 }
1887
1888 void processOFGetAsyncReply(OFChannelHandler h,
1889 OFAsyncGetReply m) {
1890 unhandledMessageReceived(h, m);
1891 }
1892
1893 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -07001894 RoleRecvStatus expectation) throws IOException {
1895 // do nothing in most states
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001896 }
Saurav Das6de23322014-08-07 18:51:19 -07001897
1898 /**
1899 * Handles role request messages that have timed out.
1900 * <p>
1901 * Role request messages that don't get role-replies (or errors related
1902 * to the request) time out after DEFAULT_ROLE_TIMEOUT_MS secs, at which
1903 * time the controller state-machine disconnects the switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001904 *
Saurav Das6de23322014-08-07 18:51:19 -07001905 * @param h the channel handler for this switch
1906 * @param pendingRole the role for which no reply was received
1907 * @throws SwitchStateException
1908 */
1909 public void handleTimedOutRoleReply(OFChannelHandler h,
1910 Role pendingRole) throws SwitchStateException {
1911 String msg = String.format("Switch: [%s] State: [%s] did not "
1912 + "reply to role-msg for pending role %s. Disconnecting ...",
1913 h.getSwitchInfoString(), this.toString(), pendingRole);
1914 throw new SwitchStateException(msg);
1915 }
Saurav Das85399942014-09-02 16:11:48 -07001916
1917 /**
1918 * Handles switch handshake timeout.
1919 * <p>
1920 * If the handshake times-out while the switch is in any state other
1921 * than WAIT_INITIAL_ROLE, then the switch is disconnected.
1922 * <p>
1923 * If the switch is in WAIT_INITIAL_ROLE state, and a pending role reply
1924 * is not received, it would trigger the role reply timeout, which would
1925 * be handled by handleTimedOutRoleReply (which would disconnect the
1926 * switch).
1927 * <p>
1928 * If the switch is in WAIT_INITIAL_ROLE state, when the handshake
1929 * timeout is triggered, then it's because we have not heard back from
1930 * the registry service regarding switch mastership. In this case, we
1931 * move to EQUAL (or SLAVE) state. See override for this method in
1932 * WAIT_INITIAL_ROLE state.
1933 * <p>
1934 * XXX: This is required today as the registry service does not reply
1935 * with role.slave to a mastership request, i.e it only replies to the
1936 * controller that wins mastership. Once the registry API changes to
1937 * reply to every request, we would not need to wait for a timeout to
1938 * move to Role.EQUAL (or SLAVE).
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001939 *
Saurav Das85399942014-09-02 16:11:48 -07001940 * @param h the channel handler for this switch
1941 * @param ctx the netty channel handler context for the channel 'h'
1942 * @throws IOException
1943 */
1944 public void handleTimedOutHandshake(OFChannelHandler h,
1945 ChannelHandlerContext ctx) throws IOException {
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001946 log.error("Disconnecting switch {}: failed to complete handshake " +
1947 "in state {} (with driverState: {})",
1948 h.getSwitchInfoString(), h.getStateForTesting(),
1949 h.sw.getSwitchDriverState());
Saurav Das85399942014-09-02 16:11:48 -07001950 h.counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush();
1951 ctx.getChannel().close();
1952 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001953 }
1954
Saurav Dasfcdad072014-08-13 14:15:21 -07001955 // *************************
1956 // Channel handler methods
1957 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001958
1959 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001960 @LogMessageDoc(message = "New switch connection from {ip address}",
1961 explanation = "A new switch has connected from the " +
1962 "specified IP address")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001963 public void channelConnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001964 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001965 counters.switchConnected.updateCounterWithFlush();
1966 channel = e.getChannel();
1967 log.info("New switch connection from {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001968 channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001969 sendHandshakeHelloMessage();
1970 setState(ChannelState.WAIT_HELLO);
1971 }
1972
1973 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001974 @LogMessageDoc(message = "Disconnected switch {switch information}",
1975 explanation = "The specified switch has disconnected.")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001976 public void channelDisconnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001977 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001978 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Saurav Dasfcdad072014-08-13 14:15:21 -07001979 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001980 if (thisdpid != 0) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001981 if (duplicateDpidFound != Boolean.TRUE) {
1982 // if the disconnected switch (on this ChannelHandler)
1983 // was not one with a duplicate-dpid, it is safe to remove all
1984 // state for it at the controller. Notice that if the
1985 // disconnected
1986 // switch was a duplicate-dpid, calling the method below would
1987 // clear
1988 // all state for the original switch (with the same dpid),
1989 // which we obviously don't want.
1990 controller.removeConnectedSwitch(thisdpid);
1991 } else {
1992 // A duplicate was disconnected on this ChannelHandler,
1993 // this is the same switch reconnecting, but the original state
1994 // was
1995 // not cleaned up - XXX check liveness of original
1996 // ChannelHandler
1997 duplicateDpidFound = Boolean.FALSE;
1998 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001999 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002000 log.warn("no dpid in channelHandler registered for "
2001 + "disconnected switch {}", getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002002 }
2003 }
2004
2005 @Override
2006 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -07002007 @LogMessageDoc(level = "ERROR",
2008 message = "Disconnecting switch {switch} due to read timeout",
2009 explanation = "The connected switch has failed to send any " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002010 "messages or respond to echo requests",
Saurav Dasfcdad072014-08-13 14:15:21 -07002011 recommendation = LogMessageDoc.CHECK_SWITCH),
2012 @LogMessageDoc(level = "ERROR",
2013 message = "Disconnecting switch {switch}: failed to " +
2014 "complete handshake",
2015 explanation = "The switch did not respond correctly " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002016 "to handshake messages",
Saurav Dasfcdad072014-08-13 14:15:21 -07002017 recommendation = LogMessageDoc.CHECK_SWITCH),
2018 @LogMessageDoc(level = "ERROR",
2019 message = "Disconnecting switch {switch} due to IO Error: {}",
2020 explanation = "There was an error communicating with the switch",
2021 recommendation = LogMessageDoc.CHECK_SWITCH),
2022 @LogMessageDoc(level = "ERROR",
2023 message = "Disconnecting switch {switch} due to switch " +
2024 "state error: {error}",
2025 explanation = "The switch sent an unexpected message",
2026 recommendation = LogMessageDoc.CHECK_SWITCH),
2027 @LogMessageDoc(level = "ERROR",
2028 message = "Disconnecting switch {switch} due to " +
2029 "message parse failure",
2030 explanation = "Could not parse a message from the switch",
2031 recommendation = LogMessageDoc.CHECK_SWITCH),
2032 @LogMessageDoc(level = "ERROR",
2033 message = "Terminating controller due to storage exception",
2034 explanation = Controller.ERROR_DATABASE,
2035 recommendation = LogMessageDoc.CHECK_CONTROLLER),
2036 @LogMessageDoc(level = "ERROR",
2037 message = "Could not process message: queue full",
2038 explanation = "OpenFlow messages are arriving faster than " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002039 " the controller can process them.",
Saurav Dasfcdad072014-08-13 14:15:21 -07002040 recommendation = LogMessageDoc.CHECK_CONTROLLER),
2041 @LogMessageDoc(level = "ERROR",
2042 message = "Error while processing message " +
2043 "from switch {switch} {cause}",
2044 explanation = "An error occurred processing the switch message",
2045 recommendation = LogMessageDoc.GENERIC_ACTION)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002046 })
2047 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
2048 throws Exception {
2049 if (e.getCause() instanceof ReadTimeoutException) {
2050 // switch timeout
2051 log.error("Disconnecting switch {} due to read timeout",
Saurav Dasfcdad072014-08-13 14:15:21 -07002052 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002053 counters.switchDisconnectReadTimeout.updateCounterWithFlush();
2054 ctx.getChannel().close();
2055 } else if (e.getCause() instanceof HandshakeTimeoutException) {
Saurav Das85399942014-09-02 16:11:48 -07002056 // handle timeout within state-machine - different actions taken
2057 // depending on current state
2058 state.handleTimedOutHandshake(this, ctx);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002059 } else if (e.getCause() instanceof ClosedChannelException) {
2060 log.debug("Channel for sw {} already closed", getSwitchInfoString());
2061 } else if (e.getCause() instanceof IOException) {
2062 log.error("Disconnecting switch {} due to IO Error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002063 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002064 if (log.isDebugEnabled()) {
2065 // still print stack trace if debug is enabled
2066 log.debug("StackTrace for previous Exception: ", e.getCause());
2067 }
2068 counters.switchDisconnectIOError.updateCounterWithFlush();
2069 ctx.getChannel().close();
2070 } else if (e.getCause() instanceof SwitchStateException) {
2071 log.error("Disconnecting switch {} due to switch state error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002072 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002073 if (log.isDebugEnabled()) {
2074 // still print stack trace if debug is enabled
2075 log.debug("StackTrace for previous Exception: ", e.getCause());
2076 }
2077 counters.switchDisconnectSwitchStateException.updateCounterWithFlush();
2078 ctx.getChannel().close();
2079 } else if (e.getCause() instanceof OFParseError) {
Saurav Dascc3e35f2014-10-10 15:33:32 -07002080 log.error("Parse failure in switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002081 + getSwitchInfoString() +
2082 " due to message parse failure",
2083 e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002084 counters.switchDisconnectParseError.updateCounterWithFlush();
Saurav Dascc3e35f2014-10-10 15:33:32 -07002085 // OFParse errors should now be handled in the OFMessageDecoder
2086 // So it should never get here. Nevertheless we should not close
2087 // channels for parse errors.
2088 // ctx.getChannel().close();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002089 } else if (e.getCause() instanceof RejectedExecutionException) {
2090 log.warn("Could not process message: queue full");
2091 counters.rejectedExecutionException.updateCounterWithFlush();
2092 } else {
2093 log.error("Error while processing message from switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002094 + getSwitchInfoString()
2095 + "state " + this.state, e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002096 counters.switchDisconnectOtherException.updateCounterWithFlush();
Saurav Das91890e42014-10-03 15:54:21 -07002097 ctx.getChannel().close(); // NPE's land here.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002098 }
2099 }
2100
2101 @Override
2102 public String toString() {
2103 return getSwitchInfoString();
2104 }
2105
2106 @Override
2107 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
2108 throws Exception {
2109 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2110 OFMessage m = factory.buildEchoRequest().build();
2111 log.info("Sending Echo Request on idle channel: {}",
2112 e.getChannel().getPipeline().getLast().toString());
2113 e.getChannel().write(Collections.singletonList(m));
2114 // XXX S some problems here -- echo request has no transaction id, and
2115 // echo reply is not correlated to the echo request.
2116 }
2117
2118 @Override
2119 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
2120 throws Exception {
2121 if (e.getMessage() instanceof List) {
2122 @SuppressWarnings("unchecked")
Saurav Dasfcdad072014-08-13 14:15:21 -07002123 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002124
2125 LoadMonitor.LoadLevel loadlevel;
2126 int packets_dropped = 0;
2127 int packets_allowed = 0;
2128 int lldps_allowed = 0;
2129
2130 if (this.controller.overload_drop) {
2131 loadlevel = this.controller.loadmonitor.getLoadLevel();
2132 }
2133 else {
2134 loadlevel = LoadMonitor.LoadLevel.OK;
2135 }
2136
2137 for (OFMessage ofm : msglist) {
2138 counters.messageReceived.updateCounterNoFlush();
Saurav Dasfcdad072014-08-13 14:15:21 -07002139 // Per-switch input throttling - placeholder for future
2140 // throttling
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002141 /*if (sw != null && sw.inputThrottled(ofm)) {
2142 counters.messageInputThrottled.updateCounterNoFlush();
2143 continue;
2144 }*/
2145 try {
2146 if (this.controller.overload_drop &&
Saurav Dasfcdad072014-08-13 14:15:21 -07002147 !loadlevel.equals(LoadMonitor.LoadLevel.OK)) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002148 switch (ofm.getType()) {
2149 case PACKET_IN:
2150 switch (loadlevel) {
2151 case VERYHIGH:
2152 // Drop all packet-ins, including LLDP/BDDPs
2153 packets_dropped++;
2154 continue;
2155 case HIGH:
2156 // Drop all packet-ins, except LLDP/BDDPs
Saurav Dasfcdad072014-08-13 14:15:21 -07002157 byte[] data = ((OFPacketIn) ofm).getData();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002158 if (data.length > 14) {
Saurav Dasfcdad072014-08-13 14:15:21 -07002159 if (((data[12] == (byte) 0x88) &&
2160 (data[13] == (byte) 0xcc)) ||
2161 ((data[12] == (byte) 0x89) &&
2162 (data[13] == (byte) 0x42))) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002163 lldps_allowed++;
2164 packets_allowed++;
2165 break;
2166 }
2167 }
2168 packets_dropped++;
2169 continue;
2170 default:
2171 // Load not high, go ahead and process msg
2172 packets_allowed++;
2173 break;
2174 }
2175 break;
2176 default:
2177 // Process all non-packet-ins
2178 packets_allowed++;
2179 break;
2180 }
2181 }
2182
2183 // Do the actual packet processing
2184 state.processOFMessage(this, ofm);
2185
Saurav Dasfcdad072014-08-13 14:15:21 -07002186 } catch (Exception ex) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002187 // We are the last handler in the stream, so run the
2188 // exception through the channel again by passing in
2189 // ctx.getChannel().
2190 Channels.fireExceptionCaught(ctx.getChannel(), ex);
2191 }
2192 }
2193
2194 if (loadlevel != LoadMonitor.LoadLevel.OK) {
2195 if (log.isDebugEnabled()) {
2196 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002197 "Overload: Detected {}, packets dropped={}",
2198 loadlevel.toString(), packets_dropped);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002199 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002200 "Overload: Packets allowed={} (LLDP/BDDPs allowed={})",
2201 packets_allowed, lldps_allowed);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002202 }
2203 }
2204 }
2205 else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002206 // Channels.fireExceptionCaught(ctx.getChannel(),
2207 // new
2208 // AssertionError("Message received from Channel is not a list"));
2209 // TODO: Pankaj: move the counters using ONOS metrics implementation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002210
2211 counters.messageReceived.updateCounterNoFlush();
2212 state.processOFMessage(this, (OFMessage) e.getMessage());
2213 }
2214
2215 // Flush all thread local queues etc. generated by this train
2216 // of messages.
2217 this.controller.flushAll();
2218 }
2219
Saurav Dasfcdad072014-08-13 14:15:21 -07002220 // *************************
2221 // Channel utility methods
2222 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002223
2224 /**
2225 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07002226 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002227 * @return true if the handshake is complete
2228 */
2229 public boolean isHandshakeComplete() {
2230 return this.state.isHandshakeComplete();
2231 }
2232
2233 private void dispatchMessage(OFMessage m) throws IOException {
2234 // handleMessage will count
2235 this.controller.handleMessage(this.sw, m, null);
2236 }
2237
2238 /**
2239 * Return a string describing this switch based on the already available
2240 * information (DPID and/or remote socket)
Jonathan Hart6eda2302014-08-14 14:57:03 -07002241 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002242 * @return
2243 */
2244 private String getSwitchInfoString() {
2245 if (sw != null)
2246 return sw.toString();
2247 String channelString;
2248 if (channel == null || channel.getRemoteAddress() == null) {
2249 channelString = "?";
2250 } else {
2251 channelString = channel.getRemoteAddress().toString();
2252 }
2253 String dpidString;
2254 if (featuresReply == null) {
2255 dpidString = "?";
2256 } else {
2257 dpidString = featuresReply.getDatapathId().toString();
2258 }
2259 return String.format("[%s DPID[%s]]", channelString, dpidString);
2260 }
2261
2262 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002263 * Update the channels state. Only called from the state machine. TODO:
2264 * enforce restricted state transitions
Jonathan Hart6eda2302014-08-14 14:57:03 -07002265 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002266 * @param state
2267 */
2268 private void setState(ChannelState state) {
2269 this.state = state;
2270 }
2271
2272 /**
2273 * Send hello message to the switch using the handshake transactions ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002274 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002275 * @throws IOException
2276 */
2277 private void sendHandshakeHelloMessage() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07002278 // The OF protocol requires us to start things off by sending the
2279 // highest
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002280 // version of the protocol supported.
2281
Saurav Dasfcdad072014-08-13 14:15:21 -07002282 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3
2283 // (ofp_version=0x04)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002284 // see Sec. 7.5.1 of the OF1.3.4 spec
Jonathan Hart6eda2302014-08-14 14:57:03 -07002285 OFHello hello;
2286 if (useOnly10) {
2287 hello = factory10.buildHello().setXid(handshakeTransactionIds--).build();
2288 log.info("Sending OF_10 Hello to {}", channel.getRemoteAddress());
2289 } else {
2290 U32 bitmap = U32.ofRaw(0x00000012);
2291 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
2292 .setBitmaps(Collections.singletonList(bitmap))
2293 .build();
2294 OFHello.Builder mb = factory13.buildHello()
2295 .setXid(this.handshakeTransactionIds--)
2296 .setElements(Collections.singletonList(hem));
2297 hello = mb.build();
2298 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
2299 }
2300
2301 channel.write(Collections.singletonList(hello));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002302 }
2303
2304 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002305 * Send featuresRequest msg to the switch using the handshake transactions
2306 * ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002307 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002308 * @throws IOException
2309 */
2310 private void sendHandshakeFeaturesRequestMessage() throws IOException {
2311 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2312 OFMessage m = factory.buildFeaturesRequest()
2313 .setXid(this.handshakeTransactionIds--)
2314 .build();
2315 channel.write(Collections.singletonList(m));
2316 }
2317
2318 private void setSwitchRole(Role role) {
2319 sw.setRole(role);
2320 }
2321
2322 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002323 * Send the configuration requests to tell the switch we want full packets
Jonathan Hart6eda2302014-08-14 14:57:03 -07002324 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002325 * @throws IOException
2326 */
2327 private void sendHandshakeSetConfig() throws IOException {
2328 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Saurav Dasfcdad072014-08-13 14:15:21 -07002329 // log.debug("Sending CONFIG_REQUEST to {}",
2330 // channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002331 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
2332
2333 // Ensure we receive the full packet via PacketIn
2334 // FIXME: We don't set the reassembly flags.
2335 OFSetConfig sc = factory
2336 .buildSetConfig()
2337 .setMissSendLen((short) 0xffff)
2338 .setXid(this.handshakeTransactionIds--)
2339 .build();
2340 msglist.add(sc);
2341
2342 // Barrier
2343 OFBarrierRequest br = factory
2344 .buildBarrierRequest()
2345 .setXid(this.handshakeTransactionIds--)
2346 .build();
2347 msglist.add(br);
2348
2349 // Verify (need barrier?)
2350 OFGetConfigRequest gcr = factory
2351 .buildGetConfigRequest()
2352 .setXid(this.handshakeTransactionIds--)
2353 .build();
2354 msglist.add(gcr);
2355 channel.write(msglist);
2356 }
2357
2358 /**
2359 * send a description state request
Jonathan Hart6eda2302014-08-14 14:57:03 -07002360 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002361 * @throws IOException
2362 */
2363 private void sendHandshakeDescriptionStatsRequest() throws IOException {
2364 // Get Description to set switch-specific flags
2365 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2366 OFDescStatsRequest dreq = factory
2367 .buildDescStatsRequest()
2368 .setXid(handshakeTransactionIds--)
2369 .build();
2370 channel.write(Collections.singletonList(dreq));
2371 }
2372
2373 private void sendHandshakeOFPortDescRequest() throws IOException {
2374 // Get port description for 1.3 switch
2375 OFPortDescStatsRequest preq = factory13
2376 .buildPortDescStatsRequest()
2377 .setXid(handshakeTransactionIds--)
2378 .build();
2379 channel.write(Collections.singletonList(preq));
2380 }
2381
2382 /**
2383 * Read switch properties from storage and set switch attributes accordingly
2384 */
2385 private void readPropertyFromStorage() {
2386 // XXX This is a placeholder for switch configuration
2387 }
2388
2389 ChannelState getStateForTesting() {
2390 return state;
2391 }
2392
Saurav Dascc3e35f2014-10-10 15:33:32 -07002393 public String getChannelSwitchInfo() {
2394 return sw.getStringId();
2395 }
2396
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002397 void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) {
2398 roleChanger = new RoleChanger(roleTimeoutMs);
2399 }
2400
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002401}