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