blob: a69e933d7f27125ac0aead842f6dc7311cfd6cc7 [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 Dasfcdad072014-08-13 14:15:21 -070087 private static final long DEFAULT_ROLE_TIMEOUT_MS = 10 * 1000; // 10 sec
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070088 private final Controller controller;
89 private final Counters counters;
90 private IOFSwitch sw;
91 private long thisdpid; // channelHandler cached value of connected switch id
92 private Channel channel;
93 // State needs to be volatile because the HandshakeTimeoutHandler
94 // needs to check if the handshake is complete
95 private volatile ChannelState state;
96
Saurav Dasfcdad072014-08-13 14:15:21 -070097 // All role messaging is handled by the roleChanger. The channel state
98 // machine
99 // coordinates between the roleChanger and the
100 // controller-global-registry-service
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700101 // to determine controller roles per switch.
102 private RoleChanger roleChanger;
103 // Used to coordinate between the controller and the cleanup thread(?)
104 // for access to the global registry on a per switch basis.
105 volatile Boolean controlRequested;
106 // When a switch with a duplicate dpid is found (i.e we already have a
107 // connected switch with the same dpid), the new switch is immediately
108 // disconnected. At that point netty callsback channelDisconnected() which
Saurav Dasfcdad072014-08-13 14:15:21 -0700109 // proceeds to cleaup switch state - we need to ensure that it does not
110 // cleanup
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700111 // switch state for the older (still connected) switch
112 private volatile Boolean duplicateDpidFound;
113
114 // Temporary storage for switch-features and port-description
115 private OFFeaturesReply featuresReply;
116 private OFPortDescStatsReply portDescReply;
117 // a concurrent ArrayList to temporarily store port status messages
118 // before we are ready to deal with them
119 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
120
Saurav Dasfcdad072014-08-13 14:15:21 -0700121 // Indicates the openflow version used by this switch
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700122 protected OFVersion ofVersion;
123 protected static OFFactory factory13;
124 protected static OFFactory factory10;
125
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 Dasfcdad072014-08-13 14:15:21 -07001036 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001037 throws IOException, SwitchStateException {
1038 illegalMessageReceived(h, m);
1039 }
1040
1041 @Override
1042 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1043 throws SwitchStateException {
1044 illegalMessageReceived(h, m);
1045 }
1046
1047 @Override
1048 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1049 throws IOException, SwitchStateException {
1050 h.pendingPortStatusMsg.add(m);
1051
1052 }
1053 },
1054
1055 /**
1056 * We are waiting for the respective switch driver to complete its
Saurav Dasfcdad072014-08-13 14:15:21 -07001057 * configuration. Notice that we do not consider this to be part of the
1058 * main switch-controller handshake. But we do consider it as a step
1059 * that comes before we declare the switch as available to the
1060 * controller. Next State: depends on the role of this controller for
1061 * this switch - either MASTER or EQUAL.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001062 */
1063 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
1064
1065 @Override
1066 void processOFError(OFChannelHandler h, OFErrorMsg m)
1067 throws IOException {
1068 // will never be called. We override processOFMessage
1069 }
1070
1071 @Override
1072 void processOFMessage(OFChannelHandler h, OFMessage m)
1073 throws IOException {
1074 if (m.getType() == OFType.ECHO_REQUEST)
Saurav Dasfcdad072014-08-13 14:15:21 -07001075 processOFEchoRequest(h, (OFEchoRequest) m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001076 else {
1077 // FIXME: other message to handle here?
1078 h.sw.processDriverHandshakeMessage(m);
1079 if (h.sw.isDriverHandshakeComplete()) {
1080 // consult the h.sw role and goto that state
1081 Role mySwitchRole = h.sw.getRole();
1082 if (mySwitchRole == Role.MASTER) {
1083 log.info("Switch-driver sub-handshake complete. "
1084 + "Activating switch {} with Role: MASTER",
1085 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001086 handlePendingPortStatusMessages(h); // before
1087 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001088 boolean success = h.controller.addActivatedMasterSwitch(
1089 h.sw.getId(), h.sw);
1090 if (!success) {
1091 disconnectDuplicate(h);
1092 return;
1093 }
1094 h.setState(MASTER);
1095 } else {
1096 log.info("Switch-driver sub-handshake complete. "
1097 + "Activating switch {} with Role: EQUAL",
1098 h.getSwitchInfoString());
Saurav Dasfcdad072014-08-13 14:15:21 -07001099 handlePendingPortStatusMessages(h); // before
1100 // activation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001101 boolean success = h.controller.addActivatedEqualSwitch(
1102 h.sw.getId(), h.sw);
1103 if (!success) {
1104 disconnectDuplicate(h);
1105 return;
1106 }
1107 h.setState(EQUAL);
1108 }
1109 }
1110 }
1111 }
1112
1113 @Override
1114 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1115 throws IOException, SwitchStateException {
1116 h.pendingPortStatusMsg.add(m);
1117 }
1118 },
1119
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001120 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001121 * This controller is in MASTER role for this switch. We enter this
1122 * state after requesting and winning control from the global registry.
1123 * The main handshake as well as the switch-driver sub-handshake is
1124 * complete at this point. // XXX S reconsider below In the (near)
1125 * future we may deterministically assign controllers to switches at
1126 * startup. We only leave this state if the switch disconnects or if we
1127 * send a role request for SLAVE /and/ receive the role reply for SLAVE.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001128 */
1129 MASTER(true) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001130 @LogMessageDoc(level = "WARN",
1131 message = "Received permission error from switch {} while" +
1132 "being master. Reasserting master role.",
1133 explanation = "The switch has denied an operation likely " +
1134 "indicating inconsistent controller roles",
1135 recommendation = "This situation can occurs transiently during role" +
1136 " changes. If, however, the condition persists or happens" +
1137 " frequently this indicates a role inconsistency. " +
1138 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001139 @Override
1140 void processOFError(OFChannelHandler h, OFErrorMsg m)
1141 throws IOException, SwitchStateException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001142 // first check if the error msg is in response to a role-request
1143 // message
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001144 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1145 if (rrstatus != RoleRecvStatus.OTHER_EXPECTATION) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001146 // rolechanger has handled the error message - we are done
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001147 return;
1148 }
1149
1150 // if we get here, then the error message is for something else
1151 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
1152 ((OFBadRequestErrorMsg) m).getCode() ==
Saurav Dasfcdad072014-08-13 14:15:21 -07001153 OFBadRequestCode.EPERM) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001154 // We are the master controller and the switch returned
1155 // a permission error. This is a likely indicator that
1156 // the switch thinks we are slave. Reassert our
1157 // role
1158 // FIXME: this could be really bad during role transitions
1159 // if two controllers are master (even if its only for
1160 // a brief period). We might need to see if these errors
1161 // persist before we reassert
1162 h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush();
1163 log.warn("Received permission error from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001164 "being master. Reasserting master role.",
1165 h.getSwitchInfoString());
1166 // h.controller.reassertRole(h, Role.MASTER);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001167 // XXX S reassert in role changer or reconsider if all this
1168 // stuff is really needed
1169 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
Saurav Dasfcdad072014-08-13 14:15:21 -07001170 ((OFFlowModFailedErrorMsg) m).getCode() ==
1171 OFFlowModFailedCode.ALL_TABLES_FULL) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001172 h.sw.setTableFull(true);
1173 } else {
1174 logError(h, m);
1175 }
1176 h.dispatchMessage(m);
1177 }
1178
1179 @Override
1180 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001181 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001182 h.sw.deliverStatisticsReply(m);
1183 }
1184
1185 @Override
1186 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1187 throws IOException, SwitchStateException {
1188 Role role = extractNiciraRoleReply(h, m);
1189 if (role == null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001190 // The message wasn't really a Nicira role reply. We just
1191 // dispatch it to the OFMessage listeners in this case.
1192 h.dispatchMessage(m);
1193 return;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001194 }
1195
1196 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Saurav Dasfcdad072014-08-13 14:15:21 -07001197 new RoleReplyInfo(role, null, m.getXid()));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001198 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001199 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001200 }
1201 }
1202
1203 @Override
1204 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1205 throws SwitchStateException, IOException {
1206 RoleReplyInfo rri = extractOFRoleReply(h, m);
1207 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1208 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001209 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001210 }
1211 }
1212
1213 @Override
1214 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1215 throws IOException, SwitchStateException {
1216 handlePortStatusMessage(h, m, true);
1217 h.dispatchMessage(m);
1218 }
1219
1220 @Override
1221 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1222 throws IOException {
1223 h.dispatchMessage(m);
1224 }
1225
1226 @Override
1227 void processOFFlowRemoved(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001228 OFFlowRemoved m) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001229 h.dispatchMessage(m);
1230 }
1231
1232 @Override
1233 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1234 throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001235 h.dispatchMessage(m);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001236 }
1237
1238 },
1239
1240 /**
1241 * This controller is in EQUAL role for this switch. We enter this state
1242 * after some /other/ controller instance wins mastership-role over this
1243 * switch. The EQUAL role can be considered the same as the SLAVE role
1244 * if this controller does NOT send commands or packets to the switch.
1245 * This should always be true for OF1.0 switches. XXX S need to enforce.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001246 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001247 * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE,
Saurav Dasfcdad072014-08-13 14:15:21 -07001248 * gives us the flexibility that if an app wants to send
1249 * commands/packets to switches, it can, even thought it is running on a
1250 * controller instance that is not in a MASTER role for this switch. Of
1251 * course, it is the job of the app to ensure that commands/packets sent
1252 * by this (EQUAL) controller instance does not clash/conflict with
1253 * commands/packets sent by the MASTER controller for this switch.
1254 * Neither the controller instances, nor the switch provides any kind of
1255 * resolution mechanism should conflicts occur.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001256 */
1257 EQUAL(true) {
1258 @Override
1259 void processOFError(OFChannelHandler h, OFErrorMsg m)
1260 throws IOException, SwitchStateException {
1261 // role changer will ignore the error if it isn't for it
1262 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1263 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
1264 logError(h, m);
1265 h.dispatchMessage(m);
1266 }
1267 }
1268
1269 @Override
1270 void processOFStatisticsReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001271 OFStatsReply m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001272 h.sw.deliverStatisticsReply(m);
1273 }
1274
1275 @Override
1276 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1277 throws IOException, SwitchStateException {
1278 Role role = extractNiciraRoleReply(h, m);
1279 // If role == null it means the message wasn't really a
1280 // Nicira role reply. We ignore it in this state.
1281 if (role != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001282 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001283 new RoleReplyInfo(role, null, m.getXid()));
Saurav Dasfcdad072014-08-13 14:15:21 -07001284 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1285 checkAndSetRoleTransition(h, role);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001286 }
1287 } else {
1288 unhandledMessageReceived(h, m);
1289 }
1290 }
1291
1292 @Override
1293 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1294 throws SwitchStateException, IOException {
1295 RoleReplyInfo rri = extractOFRoleReply(h, m);
1296 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1297 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001298 checkAndSetRoleTransition(h, rri.getRole());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001299 }
1300 }
1301
1302 // XXX S needs more handlers for 1.3 switches in equal role
1303
1304 @Override
1305 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1306 throws IOException, SwitchStateException {
1307 handlePortStatusMessage(h, m, true);
1308 }
1309
1310 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001311 @LogMessageDoc(level = "WARN",
1312 message = "Received PacketIn from switch {} while" +
1313 "being slave. Reasserting slave role.",
1314 explanation = "The switch has receive a PacketIn despite being " +
1315 "in slave role indicating inconsistent controller roles",
1316 recommendation = "This situation can occurs transiently during role" +
1317 " changes. If, however, the condition persists or happens" +
1318 " frequently this indicates a role inconsistency. " +
1319 LogMessageDoc.CHECK_CONTROLLER)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001320 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException {
1321 // we don't expect packetIn while slave, reassert we are slave
1322 h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush();
1323 log.warn("Received PacketIn from switch {} while" +
Saurav Dasfcdad072014-08-13 14:15:21 -07001324 "being slave. Reasserting slave role.", h.sw);
1325 // h.controller.reassertRole(h, Role.SLAVE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001326 // XXX reassert in role changer
1327 }
1328 };
1329
1330 private final boolean handshakeComplete;
Saurav Dasfcdad072014-08-13 14:15:21 -07001331
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001332 ChannelState(boolean handshakeComplete) {
1333 this.handshakeComplete = handshakeComplete;
1334 }
1335
1336 /**
1337 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07001338 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001339 * @return true if the handshake is complete
1340 */
1341 public boolean isHandshakeComplete() {
1342 return handshakeComplete;
1343 }
1344
1345 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001346 * Get a string specifying the switch connection, state, and message
1347 * received. To be used as message for SwitchStateException or log
1348 * messages
Jonathan Hart6eda2302014-08-14 14:57:03 -07001349 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001350 * @param h The channel handler (to get switch information_
1351 * @param m The OFMessage that has just been received
Saurav Dasfcdad072014-08-13 14:15:21 -07001352 * @param details A string giving more details about the exact nature of
1353 * the problem.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001354 * @return
1355 */
1356 // needs to be protected because enum members are actually subclasses
1357 protected String getSwitchStateMessage(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001358 OFMessage m,
1359 String details) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001360 return String.format("Switch: [%s], State: [%s], received: [%s]"
Saurav Dasfcdad072014-08-13 14:15:21 -07001361 + ", details: %s",
1362 h.getSwitchInfoString(),
1363 this.toString(),
1364 m.getType().toString(),
1365 details);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001366 }
1367
1368 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001369 * We have an OFMessage we didn't expect given the current state and we
1370 * want to treat this as an error. We currently throw an exception that
1371 * will terminate the connection However, we could be more forgiving
Jonathan Hart6eda2302014-08-14 14:57:03 -07001372 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001373 * @param h the channel handler that received the message
1374 * @param m the message
1375 * @throws SwitchStateException
1376 * @throws SwitchStateExeption we always through the execption
1377 */
1378 // needs to be protected because enum members are acutally subclasses
1379 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
1380 throws SwitchStateException {
1381 String msg = getSwitchStateMessage(h, m,
1382 "Switch should never send this message in the current state");
1383 throw new SwitchStateException(msg);
1384
1385 }
1386
1387 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001388 * We have an OFMessage we didn't expect given the current state and we
1389 * want to ignore the message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001390 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001391 * @param h the channel handler the received the message
1392 * @param m the message
1393 */
1394 protected void unhandledMessageReceived(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001395 OFMessage m) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001396 h.counters.unhandledMessage.updateCounterNoFlush();
1397 if (log.isDebugEnabled()) {
1398 String msg = getSwitchStateMessage(h, m,
1399 "Ignoring unexpected message");
1400 log.debug(msg);
1401 }
1402 }
1403
1404 /**
1405 * Log an OpenFlow error message from a switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001406 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001407 * @param sw The switch that sent the error
1408 * @param error The error message
1409 */
Saurav Dasfcdad072014-08-13 14:15:21 -07001410 @LogMessageDoc(level = "ERROR",
1411 message = "Error {error type} {error code} from {switch} " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001412 "in state {state}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001413 explanation = "The switch responded with an unexpected error" +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001414 "to an OpenFlow message from the controller",
Saurav Dasfcdad072014-08-13 14:15:21 -07001415 recommendation = "This could indicate improper network operation. " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001416 "If the problem persists restarting the switch and " +
1417 "controller may help."
1418 )
Saurav Dasfcdad072014-08-13 14:15:21 -07001419 protected void logError(OFChannelHandler h, OFErrorMsg error) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001420 log.error("{} from switch {} in state {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001421 new Object[] {
1422 error,
1423 h.getSwitchInfoString(),
1424 this.toString()});
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001425 }
1426
1427 /**
1428 * Log an OpenFlow error message from a switch and disconnect the
1429 * channel
Jonathan Hart6eda2302014-08-14 14:57:03 -07001430 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001431 * @param sw The switch that sent the error
1432 * @param error The error message
1433 */
1434 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
1435 logError(h, error);
1436 h.channel.disconnect();
1437 }
1438
1439 /**
1440 * log an error message for a duplicate dpid and disconnect this channel
1441 */
1442 protected void disconnectDuplicate(OFChannelHandler h) {
1443 log.error("Duplicated dpid or incompleted cleanup - "
1444 + "disconnecting channel {}", h.getSwitchInfoString());
1445 h.duplicateDpidFound = Boolean.TRUE;
1446 h.channel.disconnect();
1447 }
1448
1449 /**
1450 * Extract the role from an OFVendor message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001451 *
Saurav Dasfcdad072014-08-13 14:15:21 -07001452 * Extract the role from an OFVendor message if the message is a Nicira
1453 * role reply. Otherwise return null.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001454 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001455 * @param h The channel handler receiving the message
1456 * @param vendorMessage The vendor message to parse.
1457 * @return The role in the message if the message is a Nicira role
Saurav Dasfcdad072014-08-13 14:15:21 -07001458 * reply, null otherwise.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001459 * @throws SwitchStateException If the message is a Nicira role reply
Saurav Dasfcdad072014-08-13 14:15:21 -07001460 * but the numeric role value is unknown.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001461 */
1462 protected Role extractNiciraRoleReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001463 OFExperimenter experimenterMsg) throws SwitchStateException {
1464 int vendor = (int) experimenterMsg.getExperimenter();
1465 if (vendor != 0x2320) // magic number representing nicira
1466 return null;
1467 OFNiciraControllerRoleReply nrr =
1468 (OFNiciraControllerRoleReply) experimenterMsg;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001469
Saurav Dasfcdad072014-08-13 14:15:21 -07001470 Role role = null;
1471 OFNiciraControllerRole ncr = nrr.getRole();
1472 switch (ncr) {
1473 case ROLE_MASTER:
1474 role = Role.MASTER;
1475 break;
1476 case ROLE_OTHER:
1477 role = Role.EQUAL;
1478 break;
1479 case ROLE_SLAVE:
1480 role = Role.SLAVE;
1481 break;
1482 default: // handled below
1483 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001484
Saurav Dasfcdad072014-08-13 14:15:21 -07001485 if (role == null) {
1486 String msg = String.format("Switch: [%s], State: [%s], "
1487 + "received NX_ROLE_REPLY with invalid role "
1488 + "value %d",
1489 h.getSwitchInfoString(),
1490 this.toString(),
1491 nrr.getRole());
1492 throw new SwitchStateException(msg);
1493 }
1494 return role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001495 }
1496
1497 /**
1498 * Helper class returns role reply information in the format understood
1499 * by the controller.
1500 */
1501 protected class RoleReplyInfo {
1502 private Role role;
1503 private U64 genId;
1504 private long xid;
1505
Saurav Dasfcdad072014-08-13 14:15:21 -07001506 RoleReplyInfo(Role role, U64 genId, long xid) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001507 this.role = role;
1508 this.genId = genId;
1509 this.xid = xid;
1510 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001511
1512 public Role getRole() {
1513 return role;
1514 }
1515
1516 public U64 getGenId() {
1517 return genId;
1518 }
1519
1520 public long getXid() {
1521 return xid;
1522 }
1523
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001524 @Override
1525 public String toString() {
1526 return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
1527 }
1528 }
1529
1530 /**
1531 * Extract the role information from an OF1.3 Role Reply Message
Jonathan Hart6eda2302014-08-14 14:57:03 -07001532 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001533 * @param h
1534 * @param rrmsg
1535 * @return RoleReplyInfo object
1536 * @throws SwitchStateException
1537 */
1538 protected RoleReplyInfo extractOFRoleReply(OFChannelHandler h,
1539 OFRoleReply rrmsg) throws SwitchStateException {
1540 OFControllerRole cr = rrmsg.getRole();
1541 Role role = null;
Saurav Dasfcdad072014-08-13 14:15:21 -07001542 switch (cr) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001543 case ROLE_EQUAL:
1544 role = Role.EQUAL;
1545 break;
1546 case ROLE_MASTER:
1547 role = Role.MASTER;
1548 break;
1549 case ROLE_SLAVE:
1550 role = Role.SLAVE;
1551 break;
1552 case ROLE_NOCHANGE: // switch should send current role
1553 default:
1554 String msg = String.format("Unknown controller role {} "
1555 + "received from switch {}", cr, h.sw);
1556 throw new SwitchStateException(msg);
1557 }
1558
1559 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
1560 }
1561
1562 /**
1563 * Handles all pending port status messages before a switch is declared
1564 * activated in MASTER or EQUAL role. Note that since this handling
Saurav Dasfcdad072014-08-13 14:15:21 -07001565 * precedes the activation (and therefore notification to
1566 * IOFSwitchListerners) the changes to ports will already be visible
1567 * once the switch is activated. As a result, no notifications are sent
1568 * out for these pending portStatus messages.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001569 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001570 * @param h
1571 * @throws SwitchStateException
1572 */
1573 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
1574 try {
1575 handlePendingPortStatusMessages(h, 0);
1576 } catch (SwitchStateException e) {
1577 // do nothing - exception msg printed
1578 }
1579 }
1580
1581 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
1582 throws SwitchStateException {
1583 if (h.sw == null) {
1584 String msg = "State machine error: switch is null. Should never " +
1585 "happen";
1586 throw new SwitchStateException(msg);
1587 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001588 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
1589 for (OFPortStatus ps : h.pendingPortStatusMsg) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001590 temp.add(ps);
1591 handlePortStatusMessage(h, ps, false);
1592 }
1593 temp.clear();
1594 // expensive but ok - we don't expect too many port-status messages
1595 // note that we cannot use clear(), because of the reasons below
1596 h.pendingPortStatusMsg.removeAll(temp);
Saurav Dasfcdad072014-08-13 14:15:21 -07001597 // the iterator above takes a snapshot of the list - so while we
1598 // were
1599 // dealing with the pending port-status messages, we could have
1600 // received
1601 // newer ones. Handle them recursively, but break the recursion
1602 // after
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001603 // five steps to avoid an attack.
1604 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1605 handlePendingPortStatusMessages(h, index);
1606 }
1607 }
1608
1609 /**
1610 * Handle a port status message.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001611 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001612 * Handle a port status message by updating the port maps in the
Saurav Dasfcdad072014-08-13 14:15:21 -07001613 * IOFSwitch instance and notifying Controller about the change so it
1614 * can dispatch a switch update.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001615 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001616 * @param h The OFChannelHhandler that received the message
1617 * @param m The PortStatus message we received
Saurav Dasfcdad072014-08-13 14:15:21 -07001618 * @param doNotify if true switch port changed events will be dispatched
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001619 * @throws SwitchStateException
Jonathan Hart6eda2302014-08-14 14:57:03 -07001620 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001621 */
1622 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1623 boolean doNotify) throws SwitchStateException {
1624 if (h.sw == null) {
1625 String msg = getSwitchStateMessage(h, m,
1626 "State machine error: switch is null. Should never " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001627 "happen");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001628 throw new SwitchStateException(msg);
1629 }
1630
1631 Collection<PortChangeEvent> changes = h.sw.processOFPortStatus(m);
1632 if (doNotify) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001633 for (PortChangeEvent ev : changes)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001634 h.controller.notifyPortChanged(h.sw.getId(), ev.port, ev.type);
1635 }
1636 }
1637
1638 /**
1639 * Checks if the role received (from the role-reply msg) is different
1640 * from the existing role in the IOFSwitch object for this controller.
Saurav Dasfcdad072014-08-13 14:15:21 -07001641 * If so, it transitions the controller to the new role. Note that the
1642 * caller should have already verified that the role-reply msg received
1643 * was in response to a role-request msg sent out by this controller
1644 * after hearing from the registry service.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001645 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001646 * @param h the ChannelHandler that received the message
1647 * @param role the role in the recieved role reply message
1648 */
1649 protected void checkAndSetRoleTransition(OFChannelHandler h, Role role) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001650 // we received a role-reply in response to a role message
1651 // sent after hearing from the registry service. It is
1652 // possible that the role of this controller instance for
1653 // this switch has changed:
1654 // for 1.0 switch: from MASTER to SLAVE
1655 // for 1.3 switch: from MASTER to EQUAL
1656 if ((h.sw.getRole() == Role.MASTER && role == Role.SLAVE) ||
1657 (h.sw.getRole() == Role.MASTER && role == Role.EQUAL)) {
1658 // the mastership has changed
1659 h.sw.setRole(role);
1660 h.setState(EQUAL);
1661 h.controller.transitionToEqualSwitch(h.sw.getId());
1662 return;
1663 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001664
Saurav Dasfcdad072014-08-13 14:15:21 -07001665 // or for both 1.0 and 1.3 switches from EQUAL to MASTER.
1666 // note that for 1.0, even though we mean SLAVE,
1667 // internally we call the role EQUAL.
1668 if (h.sw.getRole() == Role.EQUAL && role == Role.MASTER) {
1669 // the mastership has changed
1670 h.sw.setRole(role);
1671 h.setState(MASTER);
1672 h.controller.transitionToMasterSwitch(h.sw.getId());
1673 return;
1674 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001675 }
1676
1677 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07001678 * Process an OF message received on the channel and update state
1679 * accordingly.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001680 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001681 * The main "event" of the state machine. Process the received message,
1682 * send follow up message if required and update state if required.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001683 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001684 * Switches on the message type and calls more specific event handlers
Saurav Dasfcdad072014-08-13 14:15:21 -07001685 * for each individual OF message type. If we receive a message that is
1686 * supposed to be sent from a controller to a switch we throw a
1687 * SwitchStateExeption.
Jonathan Hart6eda2302014-08-14 14:57:03 -07001688 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001689 * The more specific handlers can also throw SwitchStateExceptions
Jonathan Hart6eda2302014-08-14 14:57:03 -07001690 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001691 * @param h The OFChannelHandler that received the message
1692 * @param m The message we received.
1693 * @throws SwitchStateException
1694 * @throws IOException
1695 */
1696 void processOFMessage(OFChannelHandler h, OFMessage m)
1697 throws IOException, SwitchStateException {
1698 h.roleChanger.checkTimeout();
Saurav Dasfcdad072014-08-13 14:15:21 -07001699 switch (m.getType()) {
1700 case HELLO:
1701 processOFHello(h, (OFHello) m);
1702 break;
1703 case BARRIER_REPLY:
1704 processOFBarrierReply(h, (OFBarrierReply) m);
1705 break;
1706 case ECHO_REPLY:
1707 processOFEchoReply(h, (OFEchoReply) m);
1708 break;
1709 case ECHO_REQUEST:
1710 processOFEchoRequest(h, (OFEchoRequest) m);
1711 break;
1712 case ERROR:
1713 processOFError(h, (OFErrorMsg) m);
1714 break;
1715 case FEATURES_REPLY:
1716 processOFFeaturesReply(h, (OFFeaturesReply) m);
1717 break;
1718 case FLOW_REMOVED:
1719 processOFFlowRemoved(h, (OFFlowRemoved) m);
1720 break;
1721 case GET_CONFIG_REPLY:
1722 processOFGetConfigReply(h, (OFGetConfigReply) m);
1723 break;
1724 case PACKET_IN:
1725 processOFPacketIn(h, (OFPacketIn) m);
1726 break;
1727 case PORT_STATUS:
1728 processOFPortStatus(h, (OFPortStatus) m);
1729 break;
1730 case QUEUE_GET_CONFIG_REPLY:
1731 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1732 break;
1733 case STATS_REPLY: // multipart_reply in 1.3
1734 processOFStatisticsReply(h, (OFStatsReply) m);
1735 break;
1736 case EXPERIMENTER:
1737 processOFExperimenter(h, (OFExperimenter) m);
1738 break;
1739 case ROLE_REPLY:
1740 processOFRoleReply(h, (OFRoleReply) m);
1741 break;
1742 case GET_ASYNC_REPLY:
1743 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1744 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001745
Saurav Dasfcdad072014-08-13 14:15:21 -07001746 // The following messages are sent to switches. The controller
1747 // should never receive them
1748 case SET_CONFIG:
1749 case GET_CONFIG_REQUEST:
1750 case PACKET_OUT:
1751 case PORT_MOD:
1752 case QUEUE_GET_CONFIG_REQUEST:
1753 case BARRIER_REQUEST:
1754 case STATS_REQUEST: // multipart request in 1.3
1755 case FEATURES_REQUEST:
1756 case FLOW_MOD:
1757 case GROUP_MOD:
1758 case TABLE_MOD:
1759 case GET_ASYNC_REQUEST:
1760 case SET_ASYNC:
1761 case METER_MOD:
1762 default:
1763 illegalMessageReceived(h, m);
1764 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001765 }
1766 }
1767
1768 /*-----------------------------------------------------------------
1769 * Default implementation for message handlers in any state.
1770 *
1771 * Individual states must override these if they want a behavior
1772 * that differs from the default.
1773 *
1774 * In general, these handlers simply ignore the message and do
1775 * nothing.
1776 *
1777 * There are some exceptions though, since some messages really
1778 * are handled the same way in every state (e.g., ECHO_REQUST) or
1779 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1780 -----------------------------------------------------------------*/
1781
1782 void processOFHello(OFChannelHandler h, OFHello m)
1783 throws IOException, SwitchStateException {
1784 // we only expect hello in the WAIT_HELLO state
1785 illegalMessageReceived(h, m);
1786 }
1787
1788 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1789 throws IOException {
1790 // Silently ignore.
1791 }
1792
1793 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001794 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001795 if (h.ofVersion == null) {
1796 log.error("No OF version set for {}. Not sending Echo REPLY",
1797 h.channel.getRemoteAddress());
1798 return;
1799 }
1800 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1801 OFEchoReply reply = factory
1802 .buildEchoReply()
1803 .setXid(m.getXid())
1804 .setData(m.getData())
1805 .build();
1806 h.channel.write(Collections.singletonList(reply));
1807 }
1808
1809 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001810 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001811 // Do nothing with EchoReplies !!
1812 }
1813
1814 // no default implementation for OFError
1815 // every state must override it
1816 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1817 throws IOException, SwitchStateException;
1818
Saurav Dasfcdad072014-08-13 14:15:21 -07001819 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001820 throws IOException, SwitchStateException {
1821 unhandledMessageReceived(h, m);
1822 }
1823
1824 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
Saurav Dasfcdad072014-08-13 14:15:21 -07001825 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001826 unhandledMessageReceived(h, m);
1827 }
1828
1829 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1830 throws IOException, SwitchStateException {
1831 // we only expect config replies in the WAIT_CONFIG_REPLY state
1832 illegalMessageReceived(h, m);
1833 }
1834
1835 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1836 throws IOException {
1837 unhandledMessageReceived(h, m);
1838 }
1839
1840 // no default implementation. Every state needs to handle it.
1841 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1842 throws IOException, SwitchStateException;
1843
1844 void processOFQueueGetConfigReply(OFChannelHandler h,
Saurav Dasfcdad072014-08-13 14:15:21 -07001845 OFQueueGetConfigReply m)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001846 throws IOException {
1847 unhandledMessageReceived(h, m);
1848 }
1849
1850 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1851 throws IOException, SwitchStateException {
1852 unhandledMessageReceived(h, m);
1853 }
1854
1855 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1856 throws IOException, SwitchStateException {
1857 // TODO: it might make sense to parse the vendor message here
1858 // into the known vendor messages we support and then call more
1859 // specific event handlers
1860 unhandledMessageReceived(h, m);
1861 }
1862
1863 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1864 throws SwitchStateException, IOException {
1865 unhandledMessageReceived(h, m);
1866 }
1867
1868 void processOFGetAsyncReply(OFChannelHandler h,
1869 OFAsyncGetReply m) {
1870 unhandledMessageReceived(h, m);
1871 }
1872
1873 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
Saurav Dasfcdad072014-08-13 14:15:21 -07001874 RoleRecvStatus expectation) throws IOException {
1875 // do nothing in most states
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001876 }
Saurav Das6de23322014-08-07 18:51:19 -07001877
1878 /**
1879 * Handles role request messages that have timed out.
1880 * <p>
1881 * Role request messages that don't get role-replies (or errors related
1882 * to the request) time out after DEFAULT_ROLE_TIMEOUT_MS secs, at which
1883 * time the controller state-machine disconnects the switch
Jonathan Hart6eda2302014-08-14 14:57:03 -07001884 *
Saurav Das6de23322014-08-07 18:51:19 -07001885 * @param h the channel handler for this switch
1886 * @param pendingRole the role for which no reply was received
1887 * @throws SwitchStateException
1888 */
1889 public void handleTimedOutRoleReply(OFChannelHandler h,
1890 Role pendingRole) throws SwitchStateException {
1891 String msg = String.format("Switch: [%s] State: [%s] did not "
1892 + "reply to role-msg for pending role %s. Disconnecting ...",
1893 h.getSwitchInfoString(), this.toString(), pendingRole);
1894 throw new SwitchStateException(msg);
1895 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001896 }
1897
Saurav Dasfcdad072014-08-13 14:15:21 -07001898 // *************************
1899 // Channel handler methods
1900 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001901
1902 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001903 @LogMessageDoc(message = "New switch connection from {ip address}",
1904 explanation = "A new switch has connected from the " +
1905 "specified IP address")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001906 public void channelConnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001907 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001908 counters.switchConnected.updateCounterWithFlush();
1909 channel = e.getChannel();
1910 log.info("New switch connection from {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07001911 channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001912 sendHandshakeHelloMessage();
1913 setState(ChannelState.WAIT_HELLO);
1914 }
1915
1916 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -07001917 @LogMessageDoc(message = "Disconnected switch {switch information}",
1918 explanation = "The specified switch has disconnected.")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001919 public void channelDisconnected(ChannelHandlerContext ctx,
Saurav Dasfcdad072014-08-13 14:15:21 -07001920 ChannelStateEvent e) throws Exception {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001921 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Saurav Dasfcdad072014-08-13 14:15:21 -07001922 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001923 if (thisdpid != 0) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001924 if (duplicateDpidFound != Boolean.TRUE) {
1925 // if the disconnected switch (on this ChannelHandler)
1926 // was not one with a duplicate-dpid, it is safe to remove all
1927 // state for it at the controller. Notice that if the
1928 // disconnected
1929 // switch was a duplicate-dpid, calling the method below would
1930 // clear
1931 // all state for the original switch (with the same dpid),
1932 // which we obviously don't want.
1933 controller.removeConnectedSwitch(thisdpid);
1934 } else {
1935 // A duplicate was disconnected on this ChannelHandler,
1936 // this is the same switch reconnecting, but the original state
1937 // was
1938 // not cleaned up - XXX check liveness of original
1939 // ChannelHandler
1940 duplicateDpidFound = Boolean.FALSE;
1941 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001942 } else {
Saurav Dasfcdad072014-08-13 14:15:21 -07001943 log.warn("no dpid in channelHandler registered for "
1944 + "disconnected switch {}", getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001945 }
1946 }
1947
1948 @Override
1949 @LogMessageDocs({
Saurav Dasfcdad072014-08-13 14:15:21 -07001950 @LogMessageDoc(level = "ERROR",
1951 message = "Disconnecting switch {switch} due to read timeout",
1952 explanation = "The connected switch has failed to send any " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001953 "messages or respond to echo requests",
Saurav Dasfcdad072014-08-13 14:15:21 -07001954 recommendation = LogMessageDoc.CHECK_SWITCH),
1955 @LogMessageDoc(level = "ERROR",
1956 message = "Disconnecting switch {switch}: failed to " +
1957 "complete handshake",
1958 explanation = "The switch did not respond correctly " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001959 "to handshake messages",
Saurav Dasfcdad072014-08-13 14:15:21 -07001960 recommendation = LogMessageDoc.CHECK_SWITCH),
1961 @LogMessageDoc(level = "ERROR",
1962 message = "Disconnecting switch {switch} due to IO Error: {}",
1963 explanation = "There was an error communicating with the switch",
1964 recommendation = LogMessageDoc.CHECK_SWITCH),
1965 @LogMessageDoc(level = "ERROR",
1966 message = "Disconnecting switch {switch} due to switch " +
1967 "state error: {error}",
1968 explanation = "The switch sent an unexpected message",
1969 recommendation = LogMessageDoc.CHECK_SWITCH),
1970 @LogMessageDoc(level = "ERROR",
1971 message = "Disconnecting switch {switch} due to " +
1972 "message parse failure",
1973 explanation = "Could not parse a message from the switch",
1974 recommendation = LogMessageDoc.CHECK_SWITCH),
1975 @LogMessageDoc(level = "ERROR",
1976 message = "Terminating controller due to storage exception",
1977 explanation = Controller.ERROR_DATABASE,
1978 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1979 @LogMessageDoc(level = "ERROR",
1980 message = "Could not process message: queue full",
1981 explanation = "OpenFlow messages are arriving faster than " +
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001982 " the controller can process them.",
Saurav Dasfcdad072014-08-13 14:15:21 -07001983 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1984 @LogMessageDoc(level = "ERROR",
1985 message = "Error while processing message " +
1986 "from switch {switch} {cause}",
1987 explanation = "An error occurred processing the switch message",
1988 recommendation = LogMessageDoc.GENERIC_ACTION)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001989 })
1990 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1991 throws Exception {
1992 if (e.getCause() instanceof ReadTimeoutException) {
1993 // switch timeout
1994 log.error("Disconnecting switch {} due to read timeout",
Saurav Dasfcdad072014-08-13 14:15:21 -07001995 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001996 counters.switchDisconnectReadTimeout.updateCounterWithFlush();
1997 ctx.getChannel().close();
1998 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1999 log.error("Disconnecting switch {}: failed to complete handshake",
Saurav Dasfcdad072014-08-13 14:15:21 -07002000 getSwitchInfoString());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002001 counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush();
2002 ctx.getChannel().close();
2003 } else if (e.getCause() instanceof ClosedChannelException) {
2004 log.debug("Channel for sw {} already closed", getSwitchInfoString());
2005 } else if (e.getCause() instanceof IOException) {
2006 log.error("Disconnecting switch {} due to IO Error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002007 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002008 if (log.isDebugEnabled()) {
2009 // still print stack trace if debug is enabled
2010 log.debug("StackTrace for previous Exception: ", e.getCause());
2011 }
2012 counters.switchDisconnectIOError.updateCounterWithFlush();
2013 ctx.getChannel().close();
2014 } else if (e.getCause() instanceof SwitchStateException) {
2015 log.error("Disconnecting switch {} due to switch state error: {}",
Saurav Dasfcdad072014-08-13 14:15:21 -07002016 getSwitchInfoString(), e.getCause().getMessage());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002017 if (log.isDebugEnabled()) {
2018 // still print stack trace if debug is enabled
2019 log.debug("StackTrace for previous Exception: ", e.getCause());
2020 }
2021 counters.switchDisconnectSwitchStateException.updateCounterWithFlush();
2022 ctx.getChannel().close();
2023 } else if (e.getCause() instanceof OFParseError) {
2024 log.error("Disconnecting switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002025 + getSwitchInfoString() +
2026 " due to message parse failure",
2027 e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002028 counters.switchDisconnectParseError.updateCounterWithFlush();
2029 ctx.getChannel().close();
2030 } else if (e.getCause() instanceof RejectedExecutionException) {
2031 log.warn("Could not process message: queue full");
2032 counters.rejectedExecutionException.updateCounterWithFlush();
2033 } else {
2034 log.error("Error while processing message from switch "
Saurav Dasfcdad072014-08-13 14:15:21 -07002035 + getSwitchInfoString()
2036 + "state " + this.state, e.getCause());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002037 counters.switchDisconnectOtherException.updateCounterWithFlush();
2038 ctx.getChannel().close();
2039 }
2040 }
2041
2042 @Override
2043 public String toString() {
2044 return getSwitchInfoString();
2045 }
2046
2047 @Override
2048 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
2049 throws Exception {
2050 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2051 OFMessage m = factory.buildEchoRequest().build();
2052 log.info("Sending Echo Request on idle channel: {}",
2053 e.getChannel().getPipeline().getLast().toString());
2054 e.getChannel().write(Collections.singletonList(m));
2055 // XXX S some problems here -- echo request has no transaction id, and
2056 // echo reply is not correlated to the echo request.
2057 }
2058
2059 @Override
2060 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
2061 throws Exception {
2062 if (e.getMessage() instanceof List) {
2063 @SuppressWarnings("unchecked")
Saurav Dasfcdad072014-08-13 14:15:21 -07002064 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002065
2066 LoadMonitor.LoadLevel loadlevel;
2067 int packets_dropped = 0;
2068 int packets_allowed = 0;
2069 int lldps_allowed = 0;
2070
2071 if (this.controller.overload_drop) {
2072 loadlevel = this.controller.loadmonitor.getLoadLevel();
2073 }
2074 else {
2075 loadlevel = LoadMonitor.LoadLevel.OK;
2076 }
2077
2078 for (OFMessage ofm : msglist) {
2079 counters.messageReceived.updateCounterNoFlush();
Saurav Dasfcdad072014-08-13 14:15:21 -07002080 // Per-switch input throttling - placeholder for future
2081 // throttling
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002082 /*if (sw != null && sw.inputThrottled(ofm)) {
2083 counters.messageInputThrottled.updateCounterNoFlush();
2084 continue;
2085 }*/
2086 try {
2087 if (this.controller.overload_drop &&
Saurav Dasfcdad072014-08-13 14:15:21 -07002088 !loadlevel.equals(LoadMonitor.LoadLevel.OK)) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002089 switch (ofm.getType()) {
2090 case PACKET_IN:
2091 switch (loadlevel) {
2092 case VERYHIGH:
2093 // Drop all packet-ins, including LLDP/BDDPs
2094 packets_dropped++;
2095 continue;
2096 case HIGH:
2097 // Drop all packet-ins, except LLDP/BDDPs
Saurav Dasfcdad072014-08-13 14:15:21 -07002098 byte[] data = ((OFPacketIn) ofm).getData();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002099 if (data.length > 14) {
Saurav Dasfcdad072014-08-13 14:15:21 -07002100 if (((data[12] == (byte) 0x88) &&
2101 (data[13] == (byte) 0xcc)) ||
2102 ((data[12] == (byte) 0x89) &&
2103 (data[13] == (byte) 0x42))) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002104 lldps_allowed++;
2105 packets_allowed++;
2106 break;
2107 }
2108 }
2109 packets_dropped++;
2110 continue;
2111 default:
2112 // Load not high, go ahead and process msg
2113 packets_allowed++;
2114 break;
2115 }
2116 break;
2117 default:
2118 // Process all non-packet-ins
2119 packets_allowed++;
2120 break;
2121 }
2122 }
2123
2124 // Do the actual packet processing
2125 state.processOFMessage(this, ofm);
2126
Saurav Dasfcdad072014-08-13 14:15:21 -07002127 } catch (Exception ex) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002128 // We are the last handler in the stream, so run the
2129 // exception through the channel again by passing in
2130 // ctx.getChannel().
2131 Channels.fireExceptionCaught(ctx.getChannel(), ex);
2132 }
2133 }
2134
2135 if (loadlevel != LoadMonitor.LoadLevel.OK) {
2136 if (log.isDebugEnabled()) {
2137 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002138 "Overload: Detected {}, packets dropped={}",
2139 loadlevel.toString(), packets_dropped);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002140 log.debug(
Saurav Dasfcdad072014-08-13 14:15:21 -07002141 "Overload: Packets allowed={} (LLDP/BDDPs allowed={})",
2142 packets_allowed, lldps_allowed);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002143 }
2144 }
2145 }
2146 else {
Saurav Dasfcdad072014-08-13 14:15:21 -07002147 // Channels.fireExceptionCaught(ctx.getChannel(),
2148 // new
2149 // AssertionError("Message received from Channel is not a list"));
2150 // TODO: Pankaj: move the counters using ONOS metrics implementation
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002151
2152 counters.messageReceived.updateCounterNoFlush();
2153 state.processOFMessage(this, (OFMessage) e.getMessage());
2154 }
2155
2156 // Flush all thread local queues etc. generated by this train
2157 // of messages.
2158 this.controller.flushAll();
2159 }
2160
Saurav Dasfcdad072014-08-13 14:15:21 -07002161 // *************************
2162 // Channel utility methods
2163 // *************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002164
2165 /**
2166 * Is this a state in which the handshake has completed?
Jonathan Hart6eda2302014-08-14 14:57:03 -07002167 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002168 * @return true if the handshake is complete
2169 */
2170 public boolean isHandshakeComplete() {
2171 return this.state.isHandshakeComplete();
2172 }
2173
2174 private void dispatchMessage(OFMessage m) throws IOException {
2175 // handleMessage will count
2176 this.controller.handleMessage(this.sw, m, null);
2177 }
2178
2179 /**
2180 * Return a string describing this switch based on the already available
2181 * information (DPID and/or remote socket)
Jonathan Hart6eda2302014-08-14 14:57:03 -07002182 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002183 * @return
2184 */
2185 private String getSwitchInfoString() {
2186 if (sw != null)
2187 return sw.toString();
2188 String channelString;
2189 if (channel == null || channel.getRemoteAddress() == null) {
2190 channelString = "?";
2191 } else {
2192 channelString = channel.getRemoteAddress().toString();
2193 }
2194 String dpidString;
2195 if (featuresReply == null) {
2196 dpidString = "?";
2197 } else {
2198 dpidString = featuresReply.getDatapathId().toString();
2199 }
2200 return String.format("[%s DPID[%s]]", channelString, dpidString);
2201 }
2202
2203 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002204 * Update the channels state. Only called from the state machine. TODO:
2205 * enforce restricted state transitions
Jonathan Hart6eda2302014-08-14 14:57:03 -07002206 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002207 * @param state
2208 */
2209 private void setState(ChannelState state) {
2210 this.state = state;
2211 }
2212
2213 /**
2214 * Send hello message to the switch using the handshake transactions ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002215 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002216 * @throws IOException
2217 */
2218 private void sendHandshakeHelloMessage() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07002219 // The OF protocol requires us to start things off by sending the
2220 // highest
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002221 // version of the protocol supported.
2222
Saurav Dasfcdad072014-08-13 14:15:21 -07002223 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3
2224 // (ofp_version=0x04)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002225 // see Sec. 7.5.1 of the OF1.3.4 spec
Jonathan Hart6eda2302014-08-14 14:57:03 -07002226 OFHello hello;
2227 if (useOnly10) {
2228 hello = factory10.buildHello().setXid(handshakeTransactionIds--).build();
2229 log.info("Sending OF_10 Hello to {}", channel.getRemoteAddress());
2230 } else {
2231 U32 bitmap = U32.ofRaw(0x00000012);
2232 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
2233 .setBitmaps(Collections.singletonList(bitmap))
2234 .build();
2235 OFHello.Builder mb = factory13.buildHello()
2236 .setXid(this.handshakeTransactionIds--)
2237 .setElements(Collections.singletonList(hem));
2238 hello = mb.build();
2239 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
2240 }
2241
2242 channel.write(Collections.singletonList(hello));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002243 }
2244
2245 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002246 * Send featuresRequest msg to the switch using the handshake transactions
2247 * ids.
Jonathan Hart6eda2302014-08-14 14:57:03 -07002248 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002249 * @throws IOException
2250 */
2251 private void sendHandshakeFeaturesRequestMessage() throws IOException {
2252 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2253 OFMessage m = factory.buildFeaturesRequest()
2254 .setXid(this.handshakeTransactionIds--)
2255 .build();
2256 channel.write(Collections.singletonList(m));
2257 }
2258
2259 private void setSwitchRole(Role role) {
2260 sw.setRole(role);
2261 }
2262
2263 /**
Saurav Dasfcdad072014-08-13 14:15:21 -07002264 * Send the configuration requests to tell the switch we want full packets
Jonathan Hart6eda2302014-08-14 14:57:03 -07002265 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002266 * @throws IOException
2267 */
2268 private void sendHandshakeSetConfig() throws IOException {
2269 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Saurav Dasfcdad072014-08-13 14:15:21 -07002270 // log.debug("Sending CONFIG_REQUEST to {}",
2271 // channel.getRemoteAddress());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002272 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
2273
2274 // Ensure we receive the full packet via PacketIn
2275 // FIXME: We don't set the reassembly flags.
2276 OFSetConfig sc = factory
2277 .buildSetConfig()
2278 .setMissSendLen((short) 0xffff)
2279 .setXid(this.handshakeTransactionIds--)
2280 .build();
2281 msglist.add(sc);
2282
2283 // Barrier
2284 OFBarrierRequest br = factory
2285 .buildBarrierRequest()
2286 .setXid(this.handshakeTransactionIds--)
2287 .build();
2288 msglist.add(br);
2289
2290 // Verify (need barrier?)
2291 OFGetConfigRequest gcr = factory
2292 .buildGetConfigRequest()
2293 .setXid(this.handshakeTransactionIds--)
2294 .build();
2295 msglist.add(gcr);
2296 channel.write(msglist);
2297 }
2298
2299 /**
2300 * send a description state request
Jonathan Hart6eda2302014-08-14 14:57:03 -07002301 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002302 * @throws IOException
2303 */
2304 private void sendHandshakeDescriptionStatsRequest() throws IOException {
2305 // Get Description to set switch-specific flags
2306 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2307 OFDescStatsRequest dreq = factory
2308 .buildDescStatsRequest()
2309 .setXid(handshakeTransactionIds--)
2310 .build();
2311 channel.write(Collections.singletonList(dreq));
2312 }
2313
2314 private void sendHandshakeOFPortDescRequest() throws IOException {
2315 // Get port description for 1.3 switch
2316 OFPortDescStatsRequest preq = factory13
2317 .buildPortDescStatsRequest()
2318 .setXid(handshakeTransactionIds--)
2319 .build();
2320 channel.write(Collections.singletonList(preq));
2321 }
2322
2323 /**
2324 * Read switch properties from storage and set switch attributes accordingly
2325 */
2326 private void readPropertyFromStorage() {
2327 // XXX This is a placeholder for switch configuration
2328 }
2329
2330 ChannelState getStateForTesting() {
2331 return state;
2332 }
2333
2334 void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) {
2335 roleChanger = new RoleChanger(roleTimeoutMs);
2336 }
2337
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07002338}