blob: 2008697b64d9ba85052ab0eb7386e398dda6e387 [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
77
78/**
79 * Channel handler deals with the switch connection and dispatches
80 * switch messages to the appropriate locations.
81 * @author readams, gregor, saurav
82 */
83class OFChannelHandler extends IdleStateAwareChannelHandler {
84
85 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
86
87 private static final long DEFAULT_ROLE_TIMEOUT_MS = 10*1000; // 10 sec
88 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
97
98 // All role messaging is handled by the roleChanger. The channel state machine
99 // coordinates between the roleChanger and the controller-global-registry-service
100 // to determine controller roles per switch.
101 private RoleChanger roleChanger;
102 // Used to coordinate between the controller and the cleanup thread(?)
103 // for access to the global registry on a per switch basis.
104 volatile Boolean controlRequested;
105 // When a switch with a duplicate dpid is found (i.e we already have a
106 // connected switch with the same dpid), the new switch is immediately
107 // disconnected. At that point netty callsback channelDisconnected() which
108 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
109 // switch state for the older (still connected) switch
110 private volatile Boolean duplicateDpidFound;
111
112 // Temporary storage for switch-features and port-description
113 private OFFeaturesReply featuresReply;
114 private OFPortDescStatsReply portDescReply;
115 // a concurrent ArrayList to temporarily store port status messages
116 // before we are ready to deal with them
117 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
118
119 //Indicates the openflow version used by this switch
120 protected OFVersion ofVersion;
121 protected static OFFactory factory13;
122 protected static OFFactory factory10;
123
124 /** transaction Ids to use during handshake. Since only one thread
125 * calls into an OFChannelHandler instance, we don't need atomic.
126 * We will count down
127 */
128 private int handshakeTransactionIds = -1;
129
130 /**
131 * Create a new unconnected OFChannelHandler.
132 * @param controller
133 */
134 OFChannelHandler(Controller controller) {
135 this.controller = controller;
136 this.counters = controller.getCounters();
137 this.roleChanger = new RoleChanger(DEFAULT_ROLE_TIMEOUT_MS);
138 this.state = ChannelState.INIT;
139 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
140 factory13 = controller.getOFMessageFactory_13();
141 factory10 = controller.getOFMessageFactory_10();
142 controlRequested = Boolean.FALSE;
143 duplicateDpidFound = Boolean.FALSE;
144 }
145
146 //*******************
147 // Role Handling
148 //*******************
149
150
151 /**
152 * When we remove a pending role request we use this enum to indicate how we
153 * arrived at the decision. When we send a role request to the switch, we
154 * also use this enum to indicate what we expect back from the switch, so the
155 * role changer can match the reply to our expectation.
156 * @author gregor, saurav
157 */
158 public enum RoleRecvStatus {
159 /** The switch returned an error indicating that roles are not
160 * supported*/
161 UNSUPPORTED,
162 /** The request timed out */
163 NO_REPLY,
164 /** The reply was old, there is a newer request pending */
165 OLD_REPLY,
166 /**
167 * The reply's role matched the role that this controller set in the
168 * request message - invoked either initially at startup or to reassert
169 * current role
170 */
171 MATCHED_CURRENT_ROLE,
172 /**
173 * The reply's role matched the role that this controller set in the
174 * request message - this is the result of a callback from the
175 * global registry, followed by a role request sent to the switch
176 */
177 MATCHED_SET_ROLE,
178 /**
179 * The reply's role was a response to the query made by this controller
180 */
181 REPLY_QUERY,
182 /** We received a role reply message from the switch
183 * but the expectation was unclear, or there was no expectation
184 */
185 OTHER_EXPECTATION,
186 }
187
188 /**
189 * Forwards to RoleChanger. See there.
190 * @param role
191 */
192 public void sendRoleRequest(Role role, RoleRecvStatus expectation) {
193 try {
194 roleChanger.sendRoleRequest(role, expectation);
195 } catch (IOException e) {
196 log.error("Disconnecting switch {} due to IO Error: {}",
197 getSwitchInfoString(), e.getMessage());
198 channel.close();
199 }
200 }
201
202 // XXX S consider if necessary
203 public void disconnectSwitch() {
204 sw.disconnectSwitch();
205 }
206
207 /**
208 * A utility class to handle role requests and replies for this channel.
209 * After a role request is submitted the role changer keeps track of the
210 * pending request, collects the reply (if any) and times out the request
211 * if necessary.
212 *
213 * To simplify role handling we only keep track of the /last/ pending
214 * role reply send to the switch. If multiple requests are pending and
215 * we receive replies for earlier requests we ignore them. However, this
216 * way of handling pending requests implies that we could wait forever if
217 * a new request is submitted before the timeout triggers. If necessary
218 * we could work around that though.
219 * @author gregor
220 * @author saurav (added support OF1.3 role messages, and expectations)
221 */
222 private class RoleChanger {
223 // indicates that a request is currently pending
224 // needs to be volatile to allow correct double-check idiom
225 private volatile boolean requestPending;
226 // the transaction Id of the pending request
227 private int pendingXid;
228 // the role that's pending
229 private Role pendingRole;
230 // system time in MS when we send the request
231 private long roleSubmitTime;
232 // the timeout to use
233 private final long roleTimeoutMs;
234 // the expectation set by the caller for the returned role
235 private RoleRecvStatus expectation;
236
237 public RoleChanger(long roleTimeoutMs) {
238 this.requestPending = false;
239 this.roleSubmitTime = 0;
240 this.pendingXid = -1;
241 this.pendingRole = null;
242 this.roleTimeoutMs = roleTimeoutMs;
243 this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
244 }
245
246 /**
247 * Send NX role request message to the switch requesting the specified
248 * role.
249 *
250 * @param sw switch to send the role request message to
251 * @param role role to request
252 */
253 private int sendNxRoleRequest(Role role) throws IOException {
254 // Convert the role enum to the appropriate role to send
255 OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
256 switch (role) {
257 case MASTER:
258 roleToSend = OFNiciraControllerRole.ROLE_MASTER;
259 break;
260 case SLAVE:
261 case EQUAL:
262 default:
263 // ensuring that the only two roles sent to 1.0 switches with
264 // Nicira role support, are MASTER and SLAVE
265 roleToSend = OFNiciraControllerRole.ROLE_SLAVE;
266 log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
267 }
268 int xid = sw.getNextTransactionId();
269 OFExperimenter roleRequest = factory10
270 .buildNiciraControllerRoleRequest()
271 .setXid(xid)
272 .setRole(roleToSend)
273 .build();
274 sw.write(Collections.<OFMessage>singletonList(roleRequest),
275 new FloodlightContext());
276 return xid;
277 }
278
279 private int sendOF13RoleRequest(Role role) throws IOException {
280 // Convert the role enum to the appropriate role to send
281 OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
282 switch (role) {
283 case EQUAL:
284 roleToSend = OFControllerRole.ROLE_EQUAL;
285 break;
286 case MASTER:
287 roleToSend = OFControllerRole.ROLE_MASTER;
288 break;
289 case SLAVE:
290 roleToSend = OFControllerRole.ROLE_SLAVE;
291 break;
292 default:
293 log.warn("Sending default role.noChange to switch {}."
294 + " Should only be used for queries.", sw);
295 }
296
297 int xid = sw.getNextTransactionId();
298 OFRoleRequest rrm = factory13
299 .buildRoleRequest()
300 .setRole(roleToSend)
301 .setXid(xid)
302 .setGenerationId(sw.getNextGenerationId())
303 .build();
304 sw.write(rrm, null);
305 return xid;
306 }
307
308 /**
309 * Send a role request with the given role to the switch and update
310 * the pending request and timestamp.
311 * Sends an OFPT_ROLE_REQUEST to an OF1.3 switch, OR
312 * Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured to support it
313 * in the IOFSwitch driver. If not supported, this method sends nothing
314 * and returns 'false'. The caller should take appropriate action.
315 *
316 * One other optimization we do here is that for OF1.0 switches with
317 * Nicira role message support, we force the Role.EQUAL to become
318 * Role.SLAVE, as there is no defined behavior for the Nicira role OTHER.
319 * We cannot expect it to behave like SLAVE. We don't have this problem with
320 * OF1.3 switches, because Role.EQUAL is well defined and we can simulate
321 * SLAVE behavior by using ASYNC messages.
322 *
323 * @param role
324 * @throws IOException
325 * @returns false if and only if the switch does not support role-request
326 * messages, according to the switch driver; true otherwise.
327 */
328 synchronized boolean sendRoleRequest(Role role, RoleRecvStatus expectation)
329 throws IOException {
330 this.expectation = expectation;
331
332 if (ofVersion == OFVersion.OF_10) {
333 Boolean supportsNxRole = (Boolean)
334 sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE);
335 if (!supportsNxRole) {
336 log.debug("Switch driver indicates no support for Nicira "
337 + "role request messages. Not sending ...");
338 state.handleUnsentRoleMessage(OFChannelHandler.this, role,
339 expectation);
340 return false;
341 }
342 // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
343 // make Role.EQUAL become Role.SLAVE
344 role = (role == Role.EQUAL) ? Role.SLAVE : role;
345 pendingXid = sendNxRoleRequest(role);
346 pendingRole = role;
347 roleSubmitTime = System.currentTimeMillis();
348 requestPending = true;
349 } else {
350 // OF1.3 switch, use OFPT_ROLE_REQUEST message
351 pendingXid = sendOF13RoleRequest(role);
352 pendingRole = role;
353 roleSubmitTime = System.currentTimeMillis();
354 requestPending = true;
355 }
356 return true;
357 }
358
359 /**
360 * Deliver a received role reply.
361 *
362 * Check if a request is pending and if the received reply matches the
363 * the expected pending reply (we check both role and xid) we set
364 * the role for the switch/channel.
365 *
366 * If a request is pending but doesn't match the reply we ignore it, and
367 * return
368 *
369 * If no request is pending we disconnect with a SwitchStateException
370 *
371 * @param RoleReplyInfo information about role-reply in format that
372 * controller can understand.
373 * @throws SwitchStateException if no request is pending
374 */
375 synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
376 throws SwitchStateException {
377 if (!requestPending) {
378 Role currentRole = (sw != null) ? sw.getRole() : null;
379 if (currentRole != null) {
380 if (currentRole == rri.getRole()) {
381 // Don't disconnect if the role reply we received is
382 // for the same role we are already in.
383 log.debug("Received unexpected RoleReply from "
384 + "Switch: {} in State: {}. "
385 + "Role in reply is same as current role of this "
386 + "controller for this sw. Ignoring ...",
387 getSwitchInfoString(), state.toString());
388 return RoleRecvStatus.OTHER_EXPECTATION;
389 } else {
390 String msg = String.format("Switch: [%s], State: [%s], "
391 + "received unexpected RoleReply[%s]. "
392 + "No roles are pending, and this controller's "
393 + "current role:[%s] does not match reply. "
394 + "Disconnecting switch ... ",
395 OFChannelHandler.this.getSwitchInfoString(),
396 OFChannelHandler.this.state.toString(),
397 rri, currentRole);
398 throw new SwitchStateException(msg);
399 }
400 }
401 log.debug("Received unexpected RoleReply {} from "
402 + "Switch: {} in State: {}. "
403 + "This controller has no current role for this sw. "
404 + "Ignoring ...", new Object[] {rri,
405 getSwitchInfoString(), state});
406 return RoleRecvStatus.OTHER_EXPECTATION;
407 }
408
409 int xid = (int) rri.getXid();
410 Role role = rri.getRole();
411 // XXX S should check generation id meaningfully and other cases of expectations
412 // U64 genId = rri.getGenId();
413
414 if (pendingXid != xid) {
415 log.debug("Received older role reply from " +
416 "switch {} ({}). Ignoring. " +
417 "Waiting for {}, xid={}",
418 new Object[] { getSwitchInfoString(), rri,
419 pendingRole, pendingXid });
420 return RoleRecvStatus.OLD_REPLY;
421 }
422
423 if (pendingRole == role) {
Saurav Das6de23322014-08-07 18:51:19 -0700424 requestPending = false; // we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700425 log.debug("Received role reply message from {} that matched "
426 + "expected role-reply {} with expectations {}",
427 new Object[] {getSwitchInfoString(), role, expectation});
428 counters.roleReplyReceived.updateCounterWithFlush();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700429 if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
430 expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
431 return expectation;
432 } else {
433 return RoleRecvStatus.OTHER_EXPECTATION;
434 }
435 }
436
437 // if xids match but role's don't, perhaps its a query (OF1.3)
Saurav Das6de23322014-08-07 18:51:19 -0700438 if (expectation == RoleRecvStatus.REPLY_QUERY) {
439 requestPending = false; // again we got what we were waiting for
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700440 return expectation;
Saurav Das6de23322014-08-07 18:51:19 -0700441 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700442
Saurav Das6de23322014-08-07 18:51:19 -0700443 // It is not clear what this role-reply was about, since it is not
444 // a query and it did not match the pendingRole. But since the xid's
445 // matched, we state that we received what we were waiting for, and
446 // let the caller handle it
447 requestPending = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700448 return RoleRecvStatus.OTHER_EXPECTATION;
449 }
450
451 /**
452 * Called if we receive an error message. If the xid matches the
453 * pending request we handle it otherwise we ignore it.
454 *
455 * Note: since we only keep the last pending request we might get
456 * error messages for earlier role requests that we won't be able
457 * to handle
458 */
459 synchronized RoleRecvStatus deliverError(OFErrorMsg error)
460 throws SwitchStateException {
461 if (!requestPending) {
462 log.debug("Received an error msg from sw {}, but no pending "
463 + "requests in role-changer; not handling ...",
464 getSwitchInfoString());
465 return RoleRecvStatus.OTHER_EXPECTATION;
466 }
467 if (pendingXid != error.getXid()) {
468 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
469 log.debug("Received an error msg from sw {} for a role request,"
470 + " but not for pending request in role-changer; "
471 + " ignoring error {} ...",
472 getSwitchInfoString(), error);
473 }
474 return RoleRecvStatus.OTHER_EXPECTATION;
475 }
476 // it is an error related to a currently pending role request message
Saurav Das6de23322014-08-07 18:51:19 -0700477 requestPending = false; // we got a response, even though it is an error
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700478 if (error.getErrType() == OFErrorType.BAD_REQUEST) {
479 counters.roleReplyErrorUnsupported.updateCounterWithFlush();
480 log.error("Received a error msg {} from sw {} in state {} for "
481 + "pending role request {}. Switch driver indicates "
482 + "role-messaging is supported. Possible issues in "
483 + "switch driver configuration?", new Object[] {
484 ((OFBadRequestErrorMsg)error).toString(),
485 getSwitchInfoString(), state, pendingRole
486 });
487 return RoleRecvStatus.UNSUPPORTED;
488 }
489
490 if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
491 OFRoleRequestFailedErrorMsg rrerr =
492 (OFRoleRequestFailedErrorMsg) error;
493 switch (rrerr.getCode()) {
494 case BAD_ROLE:
495 // switch says that current-role-req has bad role?
496 // for now we disconnect
497 // fall-thru
498 case STALE:
499 // switch says that current-role-req has stale gen-id?
500 // for now we disconnect
501 // fall-thru
502 case UNSUP:
503 // switch says that current-role-req has role that
504 // cannot be supported? for now we disconnect
505 String msgx = String.format("Switch: [%s], State: [%s], "
506 + "received Error to for pending role request [%s]. "
507 + "Error:[%s]. Disconnecting switch ... ",
508 OFChannelHandler.this.getSwitchInfoString(),
509 OFChannelHandler.this.state.toString(),
510 pendingRole, rrerr);
511 throw new SwitchStateException(msgx);
512 default:
513 break;
514 }
515 }
516
517 // This error message was for a role request message but we dont know
518 // how to handle errors for nicira role request messages
519 return RoleRecvStatus.OTHER_EXPECTATION;
520 }
521
522 /**
523 * Check if a pending role request has timed out.
Saurav Das6de23322014-08-07 18:51:19 -0700524 *
525 * @throws SwitchStateException
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700526 */
Saurav Das6de23322014-08-07 18:51:19 -0700527 void checkTimeout() throws SwitchStateException {
528 if (!requestPending) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700529 return;
Saurav Das6de23322014-08-07 18:51:19 -0700530 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700531 synchronized(this) {
532 if (!requestPending)
533 return;
534 long now = System.currentTimeMillis();
535 if (now - roleSubmitTime > roleTimeoutMs) {
536 // timeout triggered.
537 counters.roleReplyTimeout.updateCounterWithFlush();
Saurav Das6de23322014-08-07 18:51:19 -0700538 state.handleTimedOutRoleReply(OFChannelHandler.this, pendingRole);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700539 }
540 }
541 }
542
543 }
544
545 //*************************
546 // Channel State Machine
547 //*************************
548
549 /**
550 * The state machine for handling the switch/channel state. All state
551 * transitions should happen from within the state machine (and not from other
552 * parts of the code)
553 * @author gregor
554 * @author saurav (modified to handle 1.0 & 1.3 switches, EQUAL state, role-handling )
555 */
556 enum ChannelState {
557 /**
558 * Initial state before channel is connected.
559 */
560 INIT(false) {
561 @Override
562 void processOFMessage(OFChannelHandler h, OFMessage m)
563 throws IOException, SwitchStateException {
564 illegalMessageReceived(h, m);
565 }
566
567 @Override
568 void processOFError(OFChannelHandler h, OFErrorMsg m)
569 throws IOException {
570 // need to implement since its abstract but it will never
571 // be called
572 }
573
574 @Override
575 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
576 throws IOException {
577 unhandledMessageReceived(h, m);
578 }
579 },
580
581 /**
582 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
583 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
584 * protocol version is accepted.
585 * We send an OFFeaturesRequest depending on the protocol version selected
586 * Next state is WAIT_FEATURES_REPLY
587 */
588 WAIT_HELLO(false) {
589 @Override
590 void processOFHello(OFChannelHandler h, OFHello m)
591 throws IOException {
592 // TODO We could check for the optional bitmap, but for now
593 // we are just checking the version number.
594 if (m.getVersion() == OFVersion.OF_13) {
595 log.info("Received {} Hello from {}", m.getVersion(),
596 h.channel.getRemoteAddress());
597 h.ofVersion = OFVersion.OF_13;
598 } else if (m.getVersion() == OFVersion.OF_10) {
599 log.info("Received {} Hello from {} - switching to OF "
600 + "version 1.0", m.getVersion(),
601 h.channel.getRemoteAddress());
602 h.ofVersion = OFVersion.OF_10;
603 } else {
604 log.error("Received Hello of version {} from switch at {}. "
605 + "This controller works with OF1.0 and OF1.3 "
606 + "switches. Disconnecting switch ...",
607 m.getVersion(), h.channel.getRemoteAddress());
608 h.channel.disconnect();
609 return;
610 }
611 h.sendHandshakeFeaturesRequestMessage();
612 h.setState(WAIT_FEATURES_REPLY);
613 }
614 @Override
615 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
616 throws IOException, SwitchStateException {
617 illegalMessageReceived(h, m);
618 }
619 @Override
620 void processOFStatisticsReply(OFChannelHandler h,
621 OFStatsReply m)
622 throws IOException, SwitchStateException {
623 illegalMessageReceived(h, m);
624 }
625 @Override
626 void processOFError(OFChannelHandler h, OFErrorMsg m) {
627 logErrorDisconnect(h, m);
628 }
629
630 @Override
631 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
632 throws IOException {
633 unhandledMessageReceived(h, m);
634 }
635 },
636
637
638 /**
639 * We are waiting for a features reply message. Once we receive it, the
640 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
641 * we send a SetConfig request, barrier, and GetConfig request and the
642 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
643 * request and the next state is WAIT_PORT_DESC_REPLY.
644 */
645 WAIT_FEATURES_REPLY(false) {
646 @Override
647 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
648 throws IOException {
649 h.thisdpid = m.getDatapathId().getLong();
650 log.info("Received features reply for switch at {} with dpid {}",
651 h.getSwitchInfoString(), h.thisdpid);
652 //update the controller about this connected switch
653 boolean success = h.controller.addConnectedSwitch(
654 h.thisdpid, h);
655 if (!success) {
656 disconnectDuplicate(h);
657 return;
658 }
659
660 h.featuresReply = m; //temp store
661 if (h.ofVersion == OFVersion.OF_10) {
662 h.sendHandshakeSetConfig();
663 h.setState(WAIT_CONFIG_REPLY);
664 } else {
665 //version is 1.3, must get switchport information
666 h.sendHandshakeOFPortDescRequest();
667 h.setState(WAIT_PORT_DESC_REPLY);
668 }
669 }
670 @Override
671 void processOFStatisticsReply(OFChannelHandler h,
672 OFStatsReply m)
673 throws IOException, SwitchStateException {
674 illegalMessageReceived(h, m);
675 }
676 @Override
677 void processOFError(OFChannelHandler h, OFErrorMsg m) {
678 logErrorDisconnect(h, m);
679 }
680
681 @Override
682 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
683 throws IOException {
684 unhandledMessageReceived(h, m);
685 }
686 },
687
688 /**
689 * We are waiting for a description of the 1.3 switch ports.
690 * Once received, we send a SetConfig request
691 * Next State is WAIT_CONFIG_REPLY
692 */
693 WAIT_PORT_DESC_REPLY(false) {
694
695 @Override
696 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
697 throws SwitchStateException {
698 // Read port description
699 if (m.getStatsType() != OFStatsType.PORT_DESC) {
700 log.warn("Expecting port description stats but received stats "
701 + "type {} from {}. Ignoring ...", m.getStatsType(),
702 h.channel.getRemoteAddress());
703 return;
704 }
705 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
706 log.warn("Stats reply indicates more stats from sw {} for "
707 + "port description - not currently handled",
708 h.getSwitchInfoString());
709 }
710 h.portDescReply = (OFPortDescStatsReply)m; // temp store
711 log.info("Received port desc reply for switch at {}",
712 h.getSwitchInfoString());
713 try {
714 h.sendHandshakeSetConfig();
715 } catch (IOException e) {
716 log.error("Unable to send setConfig after PortDescReply. "
717 + "Error: {}", e.getMessage());
718 }
719 h.setState(WAIT_CONFIG_REPLY);
720 }
721
722 @Override
723 void processOFError(OFChannelHandler h, OFErrorMsg m)
724 throws IOException, SwitchStateException {
725 logErrorDisconnect(h, m);
726
727 }
728
729 @Override
730 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
731 throws IOException, SwitchStateException {
732 unhandledMessageReceived(h, m);
733
734 }
735 },
736
737 /**
738 * We are waiting for a config reply message. Once we receive it
739 * we send a DescriptionStatsRequest to the switch.
740 * Next state: WAIT_DESCRIPTION_STAT_REPLY
741 */
742 WAIT_CONFIG_REPLY(false) {
743 @Override
744 @LogMessageDocs({
745 @LogMessageDoc(level="WARN",
746 message="Config Reply from {switch} has " +
747 "miss length set to {length}",
748 explanation="The controller requires that the switch " +
749 "use a miss length of 0xffff for correct " +
750 "function",
751 recommendation="Use a different switch to ensure " +
752 "correct function")
753 })
754 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
755 throws IOException {
756 if (m.getMissSendLen() == 0xffff) {
757 log.trace("Config Reply from switch {} confirms "
758 + "miss length set to 0xffff",
759 h.getSwitchInfoString());
760 } else {
761 // FIXME: we can't really deal with switches that don't send
762 // full packets. Shouldn't we drop the connection here?
763 log.warn("Config Reply from switch {} has"
764 + "miss length set to {}",
765 h.getSwitchInfoString(),
766 m.getMissSendLen());
767 }
768 h.sendHandshakeDescriptionStatsRequest();
769 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
770 }
771
772 @Override
773 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
774 // do nothing;
775 }
776
777 @Override
778 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
779 throws IOException, SwitchStateException {
780 illegalMessageReceived(h, m);
781 }
782 @Override
783 void processOFStatisticsReply(OFChannelHandler h,
784 OFStatsReply m)
785 throws IOException, SwitchStateException {
786 log.error("Received multipart(stats) message sub-type {}",
787 m.getStatsType());
788 illegalMessageReceived(h, m);
789 }
790
791 @Override
792 void processOFError(OFChannelHandler h, OFErrorMsg m) {
793 logErrorDisconnect(h, m);
794 }
795
796 @Override
797 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
798 throws IOException {
799 h.pendingPortStatusMsg.add(m);
800 }
801 },
802
803
804 /**
805 * We are waiting for a OFDescriptionStat message from the switch.
806 * Once we receive any stat message we try to parse it. If it's not
807 * a description stats message we disconnect. If its the expected
808 * description stats message, we:
809 * - use the switch driver to bind the switch and get an IOFSwitch instance
810 * - setup the IOFSwitch instance
811 * - add switch to FloodlightProvider(Controller) and send the initial role
812 * request to the switch.
813 * Next state: WAIT_INITIAL_ROLE
814 * In the typical case, where switches support role request messages
815 * the next state is where we expect the role reply message.
816 * In the special case that where the switch does not support any kind
817 * of role request messages, we don't send a role message, but we do
818 * request mastership from the registry service. This controller
819 * should become master once we hear back from the registry service.
820 * All following states will have a h.sw instance!
821 */
822 WAIT_DESCRIPTION_STAT_REPLY(false) {
823 @LogMessageDoc(message="Switch {switch info} bound to class " +
824 "{switch driver}, description {switch description}",
825 explanation="The specified switch has been bound to " +
826 "a switch driver based on the switch description" +
827 "received from the switch")
828 @Override
829 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
830 throws SwitchStateException {
831 // Read description, if it has been updated
832 if (m.getStatsType() != OFStatsType.DESC) {
833 log.warn("Expecting Description stats but received stats "
834 + "type {} from {}. Ignoring ...", m.getStatsType(),
835 h.channel.getRemoteAddress());
836 return;
837 }
838 log.info("Received switch description reply from switch at {}",
839 h.channel.getRemoteAddress());
840 OFDescStatsReply drep = (OFDescStatsReply) m;
841 // Here is where we differentiate between different kinds of switches
842 h.sw = h.controller.getOFSwitchInstance(drep, h.ofVersion);
843 // set switch information
844 h.sw.setOFVersion(h.ofVersion);
845 ((OFSwitchImplBase) h.sw).setFeaturesReply(h.featuresReply);
846 ((OFSwitchImplBase) h.sw).setPortDescReply(h.portDescReply);
847 h.sw.setConnected(true);
848 h.sw.setChannel(h.channel);
849 h.sw.setFloodlightProvider(h.controller);
850 h.sw.setThreadPoolService(h.controller.getThreadPoolService());
851 try {
852 h.sw.setDebugCounterService(h.controller.getDebugCounter());
853 } catch (CounterException e) {
854 h.counters.switchCounterRegistrationFailed
855 .updateCounterNoFlush();
856 log.warn("Could not register counters for switch {} ",
857 h.getSwitchInfoString(), e);
858 }
859
860 log.info("Switch {} bound to class {}, description {}",
861 new Object[] { h.sw, h.sw.getClass(), drep });
862 //Put switch in EQUAL mode until we hear back from the global registry
863 log.debug("Setting new switch {} to EQUAL and sending Role request",
864 h.sw.getStringId());
865 h.setSwitchRole(Role.EQUAL);
866 try {
867 boolean supportsRRMsg = h.roleChanger.sendRoleRequest(Role.EQUAL,
868 RoleRecvStatus.MATCHED_CURRENT_ROLE);
869 if (!supportsRRMsg) {
870 log.warn("Switch {} does not support role request messages "
871 + "of any kind. No role messages were sent. "
872 + "This controller instance SHOULD become MASTER "
873 + "from the registry process. ",
874 h.getSwitchInfoString());
875 }
876 h.setState(WAIT_INITIAL_ROLE);
877 // request control of switch from global registry -
878 // necessary even if this is the only controller the
879 // switch is connected to.
880 h.controller.submitRegistryRequest(h.sw.getId());
881 } catch (IOException e) {
882 log.error("Exception when sending role request: {} ",
883 e.getMessage());
884 // FIXME shouldn't we disconnect?
885 }
886 }
887
888 @Override
889 void processOFError(OFChannelHandler h, OFErrorMsg m) {
890 logErrorDisconnect(h, m);
891 }
892
893 @Override
894 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
895 throws IOException, SwitchStateException {
896 illegalMessageReceived(h, m);
897 }
898
899 @Override
900 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
901 throws IOException {
902 h.pendingPortStatusMsg.add(m);
903 }
904 },
905
906 /**
907 * We are waiting for a role reply message in response to a role request
908 * sent after hearing back from the registry service -- OR -- we are
909 * just waiting to hear back from the registry service in the case that
910 * the switch does not support role messages. If completed successfully,
911 * the controller's role for this switch will be set here.
912 * Before we move to the state corresponding to the role, we allow the
913 * switch specific driver to complete its configuration. This configuration
914 * typically depends on the role the controller is playing for this switch.
915 * And so we set the switch role (for 'this' controller) before we start
916 * the driver-sub-handshake.
917 * Next State: WAIT_SWITCH_DRIVER_SUB_HANDSHAKE
918 */
919 WAIT_INITIAL_ROLE(false) {
920 @Override
921 void processOFError(OFChannelHandler h, OFErrorMsg m)
922 throws SwitchStateException {
923 // role changer will ignore the error if it isn't for it
924 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
925 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
926 logError(h, m);
927 }
928 }
929
930 @Override
931 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
932 throws IOException, SwitchStateException {
933 Role role = extractNiciraRoleReply(h, m);
934 // If role == null it means the vendor (experimenter) message
935 // wasn't really a Nicira role reply. We ignore this case.
936 if (role != null) {
937 RoleReplyInfo rri = new RoleReplyInfo(role, null, m.getXid());
938 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
939 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
940 setRoleAndStartDriverHandshake(h, rri.getRole());
941 } // else do nothing - wait for the correct expected reply
942 } else {
943 unhandledMessageReceived(h, m);
944 }
945 }
946
947 @Override
948 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
949 throws SwitchStateException, IOException {
950 RoleReplyInfo rri = extractOFRoleReply(h,m);
951 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
952 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
953 setRoleAndStartDriverHandshake(h, rri.getRole());
954 } // else do nothing - wait for the correct expected reply
955 }
956
957 @Override
958 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
959 RoleRecvStatus expectation) throws IOException {
960 // typically this is triggered for a switch where role messages
961 // are not supported - we confirm that the role being set is
962 // master and move to the next state
963 if (expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
964 if (role == Role.MASTER) {
965 setRoleAndStartDriverHandshake(h, role);
966 } else {
967 log.error("Expected MASTER role from registry for switch "
968 + "which has no support for role-messages."
969 + "Received {}. It is possible that this switch "
970 + "is connected to other controllers, in which "
971 + "case it should support role messages - not "
972 + "moving forward.", role);
973 }
974 } // else do nothing - wait to hear back from registry
975
976 }
977
978 private void setRoleAndStartDriverHandshake(OFChannelHandler h,
979 Role role) throws IOException {
980 h.setSwitchRole(role);
981 h.sw.startDriverHandshake();
982 if (h.sw.isDriverHandshakeComplete()) {
983 Role mySwitchRole = h.sw.getRole();
984 if (mySwitchRole == Role.MASTER) {
985 log.info("Switch-driver sub-handshake complete. "
986 + "Activating switch {} with Role: MASTER",
987 h.getSwitchInfoString());
988 handlePendingPortStatusMessages(h); //before activation
989 boolean success = h.controller.addActivatedMasterSwitch(
990 h.sw.getId(), h.sw);
991 if (!success) {
992 disconnectDuplicate(h);
993 return;
994 }
995 h.setState(MASTER);
996 } else {
997 log.info("Switch-driver sub-handshake complete. "
998 + "Activating switch {} with Role: EQUAL",
999 h.getSwitchInfoString());
1000 handlePendingPortStatusMessages(h); //before activation
1001 boolean success = h.controller.addActivatedEqualSwitch(
1002 h.sw.getId(), h.sw);
1003 if (!success) {
1004 disconnectDuplicate(h);
1005 return;
1006 }
1007 h.setState(EQUAL);
1008 }
1009 } else {
1010 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
1011 }
1012 }
1013
1014 @Override
1015 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1016 throws IOException, SwitchStateException {
1017 illegalMessageReceived(h, m);
1018 }
1019
1020 @Override
1021 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1022 throws SwitchStateException {
1023 illegalMessageReceived(h, m);
1024 }
1025
1026 @Override
1027 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1028 throws IOException, SwitchStateException {
1029 h.pendingPortStatusMsg.add(m);
1030
1031 }
1032 },
1033
1034 /**
1035 * We are waiting for the respective switch driver to complete its
1036 * configuration. Notice that we do not consider this to be part of the main
1037 * switch-controller handshake. But we do consider it as a step that comes
1038 * before we declare the switch as available to the controller.
1039 * Next State: depends on the role of this controller for this switch - either
1040 * MASTER or EQUAL.
1041 */
1042 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
1043
1044 @Override
1045 void processOFError(OFChannelHandler h, OFErrorMsg m)
1046 throws IOException {
1047 // will never be called. We override processOFMessage
1048 }
1049
1050 @Override
1051 void processOFMessage(OFChannelHandler h, OFMessage m)
1052 throws IOException {
1053 if (m.getType() == OFType.ECHO_REQUEST)
1054 processOFEchoRequest(h, (OFEchoRequest)m);
1055 else {
1056 // FIXME: other message to handle here?
1057 h.sw.processDriverHandshakeMessage(m);
1058 if (h.sw.isDriverHandshakeComplete()) {
1059 // consult the h.sw role and goto that state
1060 Role mySwitchRole = h.sw.getRole();
1061 if (mySwitchRole == Role.MASTER) {
1062 log.info("Switch-driver sub-handshake complete. "
1063 + "Activating switch {} with Role: MASTER",
1064 h.getSwitchInfoString());
1065 handlePendingPortStatusMessages(h); //before activation
1066 boolean success = h.controller.addActivatedMasterSwitch(
1067 h.sw.getId(), h.sw);
1068 if (!success) {
1069 disconnectDuplicate(h);
1070 return;
1071 }
1072 h.setState(MASTER);
1073 } else {
1074 log.info("Switch-driver sub-handshake complete. "
1075 + "Activating switch {} with Role: EQUAL",
1076 h.getSwitchInfoString());
1077 handlePendingPortStatusMessages(h); //before activation
1078 boolean success = h.controller.addActivatedEqualSwitch(
1079 h.sw.getId(), h.sw);
1080 if (!success) {
1081 disconnectDuplicate(h);
1082 return;
1083 }
1084 h.setState(EQUAL);
1085 }
1086 }
1087 }
1088 }
1089
1090 @Override
1091 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1092 throws IOException, SwitchStateException {
1093 h.pendingPortStatusMsg.add(m);
1094 }
1095 },
1096
1097
1098 /**
1099 * This controller is in MASTER role for this switch. We enter this state
1100 * after requesting and winning control from the global registry.
1101 * The main handshake as well as the switch-driver sub-handshake
1102 * is complete at this point.
1103 * // XXX S reconsider below
1104 * In the (near) future we may deterministically assign controllers to
1105 * switches at startup.
1106 * We only leave this state if the switch disconnects or
1107 * if we send a role request for SLAVE /and/ receive the role reply for
1108 * SLAVE.
1109 */
1110 MASTER(true) {
1111 @LogMessageDoc(level="WARN",
1112 message="Received permission error from switch {} while" +
1113 "being master. Reasserting master role.",
1114 explanation="The switch has denied an operation likely " +
1115 "indicating inconsistent controller roles",
1116 recommendation="This situation can occurs transiently during role" +
1117 " changes. If, however, the condition persists or happens" +
1118 " frequently this indicates a role inconsistency. " +
1119 LogMessageDoc.CHECK_CONTROLLER )
1120 @Override
1121 void processOFError(OFChannelHandler h, OFErrorMsg m)
1122 throws IOException, SwitchStateException {
1123 // first check if the error msg is in response to a role-request message
1124 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1125 if (rrstatus != RoleRecvStatus.OTHER_EXPECTATION) {
1126 // rolechanger has handled the error message - we are done
1127 return;
1128 }
1129
1130 // if we get here, then the error message is for something else
1131 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
1132 ((OFBadRequestErrorMsg) m).getCode() ==
1133 OFBadRequestCode.EPERM) {
1134 // We are the master controller and the switch returned
1135 // a permission error. This is a likely indicator that
1136 // the switch thinks we are slave. Reassert our
1137 // role
1138 // FIXME: this could be really bad during role transitions
1139 // if two controllers are master (even if its only for
1140 // a brief period). We might need to see if these errors
1141 // persist before we reassert
1142 h.counters.epermErrorWhileSwitchIsMaster.updateCounterWithFlush();
1143 log.warn("Received permission error from switch {} while" +
1144 "being master. Reasserting master role.",
1145 h.getSwitchInfoString());
1146 //h.controller.reassertRole(h, Role.MASTER);
1147 // XXX S reassert in role changer or reconsider if all this
1148 // stuff is really needed
1149 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
1150 ((OFFlowModFailedErrorMsg) m).getCode() ==
1151 OFFlowModFailedCode.ALL_TABLES_FULL) {
1152 h.sw.setTableFull(true);
1153 } else {
1154 logError(h, m);
1155 }
1156 h.dispatchMessage(m);
1157 }
1158
1159 @Override
1160 void processOFStatisticsReply(OFChannelHandler h,
1161 OFStatsReply m) {
1162 h.sw.deliverStatisticsReply(m);
1163 }
1164
1165 @Override
1166 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1167 throws IOException, SwitchStateException {
1168 Role role = extractNiciraRoleReply(h, m);
1169 if (role == null) {
1170 // The message wasn't really a Nicira role reply. We just
1171 // dispatch it to the OFMessage listeners in this case.
1172 h.dispatchMessage(m);
1173 return;
1174 }
1175
1176 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
1177 new RoleReplyInfo(role, null, m.getXid()));
1178 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1179 checkAndSetRoleTransition(h, role);
1180 }
1181 }
1182
1183 @Override
1184 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1185 throws SwitchStateException, IOException {
1186 RoleReplyInfo rri = extractOFRoleReply(h, m);
1187 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1188 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1189 checkAndSetRoleTransition(h, rri.getRole());
1190 }
1191 }
1192
1193 @Override
1194 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1195 throws IOException, SwitchStateException {
1196 handlePortStatusMessage(h, m, true);
1197 h.dispatchMessage(m);
1198 }
1199
1200 @Override
1201 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1202 throws IOException {
1203 h.dispatchMessage(m);
1204 }
1205
1206 @Override
1207 void processOFFlowRemoved(OFChannelHandler h,
1208 OFFlowRemoved m) throws IOException {
1209 h.dispatchMessage(m);
1210 }
1211
1212 @Override
1213 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1214 throws IOException {
1215 h.dispatchMessage(m);
1216 }
1217
1218 },
1219
1220 /**
1221 * This controller is in EQUAL role for this switch. We enter this state
1222 * after some /other/ controller instance wins mastership-role over this
1223 * switch. The EQUAL role can be considered the same as the SLAVE role
1224 * if this controller does NOT send commands or packets to the switch.
1225 * This should always be true for OF1.0 switches. XXX S need to enforce.
1226 *
1227 * For OF1.3 switches, choosing this state as EQUAL instead of SLAVE,
1228 * gives us the flexibility that if an app wants to send commands/packets
1229 * to switches, it can, even thought it is running on a controller instance
1230 * that is not in a MASTER role for this switch. Of course, it is the job
1231 * of the app to ensure that commands/packets sent by this (EQUAL) controller
1232 * instance does not clash/conflict with commands/packets sent by the MASTER
1233 * controller for this switch. Neither the controller instances, nor the
1234 * switch provides any kind of resolution mechanism should conflicts occur.
1235 */
1236 EQUAL(true) {
1237 @Override
1238 void processOFError(OFChannelHandler h, OFErrorMsg m)
1239 throws IOException, SwitchStateException {
1240 // role changer will ignore the error if it isn't for it
1241 RoleRecvStatus rrstatus = h.roleChanger.deliverError(m);
1242 if (rrstatus == RoleRecvStatus.OTHER_EXPECTATION) {
1243 logError(h, m);
1244 h.dispatchMessage(m);
1245 }
1246 }
1247
1248 @Override
1249 void processOFStatisticsReply(OFChannelHandler h,
1250 OFStatsReply m) {
1251 h.sw.deliverStatisticsReply(m);
1252 }
1253
1254 @Override
1255 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1256 throws IOException, SwitchStateException {
1257 Role role = extractNiciraRoleReply(h, m);
1258 // If role == null it means the message wasn't really a
1259 // Nicira role reply. We ignore it in this state.
1260 if (role != null) {
1261 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(
1262 new RoleReplyInfo(role, null, m.getXid()));
1263 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1264 checkAndSetRoleTransition(h, role);
1265 }
1266 } else {
1267 unhandledMessageReceived(h, m);
1268 }
1269 }
1270
1271 @Override
1272 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1273 throws SwitchStateException, IOException {
1274 RoleReplyInfo rri = extractOFRoleReply(h, m);
1275 RoleRecvStatus rrs = h.roleChanger.deliverRoleReply(rri);
1276 if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
1277 checkAndSetRoleTransition(h, rri.getRole());
1278 }
1279 }
1280
1281 // XXX S needs more handlers for 1.3 switches in equal role
1282
1283 @Override
1284 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1285 throws IOException, SwitchStateException {
1286 handlePortStatusMessage(h, m, true);
1287 }
1288
1289 @Override
1290 @LogMessageDoc(level="WARN",
1291 message="Received PacketIn from switch {} while" +
1292 "being slave. Reasserting slave role.",
1293 explanation="The switch has receive a PacketIn despite being " +
1294 "in slave role indicating inconsistent controller roles",
1295 recommendation="This situation can occurs transiently during role" +
1296 " changes. If, however, the condition persists or happens" +
1297 " frequently this indicates a role inconsistency. " +
1298 LogMessageDoc.CHECK_CONTROLLER )
1299 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) throws IOException {
1300 // we don't expect packetIn while slave, reassert we are slave
1301 h.counters.packetInWhileSwitchIsSlave.updateCounterNoFlush();
1302 log.warn("Received PacketIn from switch {} while" +
1303 "being slave. Reasserting slave role.", h.sw);
1304 //h.controller.reassertRole(h, Role.SLAVE);
1305 // XXX reassert in role changer
1306 }
1307 };
1308
1309 private final boolean handshakeComplete;
1310 ChannelState(boolean handshakeComplete) {
1311 this.handshakeComplete = handshakeComplete;
1312 }
1313
1314 /**
1315 * Is this a state in which the handshake has completed?
1316 * @return true if the handshake is complete
1317 */
1318 public boolean isHandshakeComplete() {
1319 return handshakeComplete;
1320 }
1321
1322 /**
1323 * Get a string specifying the switch connection, state, and
1324 * message received. To be used as message for SwitchStateException
1325 * or log messages
1326 * @param h The channel handler (to get switch information_
1327 * @param m The OFMessage that has just been received
1328 * @param details A string giving more details about the exact nature
1329 * of the problem.
1330 * @return
1331 */
1332 // needs to be protected because enum members are actually subclasses
1333 protected String getSwitchStateMessage(OFChannelHandler h,
1334 OFMessage m,
1335 String details) {
1336 return String.format("Switch: [%s], State: [%s], received: [%s]"
1337 + ", details: %s",
1338 h.getSwitchInfoString(),
1339 this.toString(),
1340 m.getType().toString(),
1341 details);
1342 }
1343
1344 /**
1345 * We have an OFMessage we didn't expect given the current state and
1346 * we want to treat this as an error.
1347 * We currently throw an exception that will terminate the connection
1348 * However, we could be more forgiving
1349 * @param h the channel handler that received the message
1350 * @param m the message
1351 * @throws SwitchStateException
1352 * @throws SwitchStateExeption we always through the execption
1353 */
1354 // needs to be protected because enum members are acutally subclasses
1355 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
1356 throws SwitchStateException {
1357 String msg = getSwitchStateMessage(h, m,
1358 "Switch should never send this message in the current state");
1359 throw new SwitchStateException(msg);
1360
1361 }
1362
1363 /**
1364 * We have an OFMessage we didn't expect given the current state and
1365 * we want to ignore the message
1366 * @param h the channel handler the received the message
1367 * @param m the message
1368 */
1369 protected void unhandledMessageReceived(OFChannelHandler h,
1370 OFMessage m) {
1371 h.counters.unhandledMessage.updateCounterNoFlush();
1372 if (log.isDebugEnabled()) {
1373 String msg = getSwitchStateMessage(h, m,
1374 "Ignoring unexpected message");
1375 log.debug(msg);
1376 }
1377 }
1378
1379 /**
1380 * Log an OpenFlow error message from a switch
1381 * @param sw The switch that sent the error
1382 * @param error The error message
1383 */
1384 @LogMessageDoc(level="ERROR",
1385 message="Error {error type} {error code} from {switch} " +
1386 "in state {state}",
1387 explanation="The switch responded with an unexpected error" +
1388 "to an OpenFlow message from the controller",
1389 recommendation="This could indicate improper network operation. " +
1390 "If the problem persists restarting the switch and " +
1391 "controller may help."
1392 )
1393 protected void logError(OFChannelHandler h, OFErrorMsg error) {
1394 log.error("{} from switch {} in state {}",
1395 new Object[] {
1396 error,
1397 h.getSwitchInfoString(),
1398 this.toString()});
1399 }
1400
1401 /**
1402 * Log an OpenFlow error message from a switch and disconnect the
1403 * channel
1404 * @param sw The switch that sent the error
1405 * @param error The error message
1406 */
1407 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
1408 logError(h, error);
1409 h.channel.disconnect();
1410 }
1411
1412 /**
1413 * log an error message for a duplicate dpid and disconnect this channel
1414 */
1415 protected void disconnectDuplicate(OFChannelHandler h) {
1416 log.error("Duplicated dpid or incompleted cleanup - "
1417 + "disconnecting channel {}", h.getSwitchInfoString());
1418 h.duplicateDpidFound = Boolean.TRUE;
1419 h.channel.disconnect();
1420 }
1421
1422 /**
1423 * Extract the role from an OFVendor message.
1424 *
1425 * Extract the role from an OFVendor message if the message is a
1426 * Nicira role reply. Otherwise return null.
1427 *
1428 * @param h The channel handler receiving the message
1429 * @param vendorMessage The vendor message to parse.
1430 * @return The role in the message if the message is a Nicira role
1431 * reply, null otherwise.
1432 * @throws SwitchStateException If the message is a Nicira role reply
1433 * but the numeric role value is unknown.
1434 */
1435 protected Role extractNiciraRoleReply(OFChannelHandler h,
1436 OFExperimenter experimenterMsg) throws SwitchStateException {
1437 int vendor = (int) experimenterMsg.getExperimenter();
1438 if (vendor != 0x2320) // magic number representing nicira
1439 return null;
1440 OFNiciraControllerRoleReply nrr =
1441 (OFNiciraControllerRoleReply) experimenterMsg;
1442
1443 Role role = null;
1444 OFNiciraControllerRole ncr = nrr.getRole();
1445 switch(ncr) {
1446 case ROLE_MASTER:
1447 role = Role.MASTER;
1448 break;
1449 case ROLE_OTHER:
1450 role = Role.EQUAL;
1451 break;
1452 case ROLE_SLAVE:
1453 role = Role.SLAVE;
1454 break;
1455 default: //handled below
1456 }
1457
1458 if (role == null) {
1459 String msg = String.format("Switch: [%s], State: [%s], "
1460 + "received NX_ROLE_REPLY with invalid role "
1461 + "value %d",
1462 h.getSwitchInfoString(),
1463 this.toString(),
1464 nrr.getRole());
1465 throw new SwitchStateException(msg);
1466 }
1467 return role;
1468 }
1469
1470 /**
1471 * Helper class returns role reply information in the format understood
1472 * by the controller.
1473 */
1474 protected class RoleReplyInfo {
1475 private Role role;
1476 private U64 genId;
1477 private long xid;
1478
1479 RoleReplyInfo (Role role, U64 genId, long xid) {
1480 this.role = role;
1481 this.genId = genId;
1482 this.xid = xid;
1483 }
1484 public Role getRole() { return role; }
1485 public U64 getGenId() { return genId; }
1486 public long getXid() { return xid; }
1487 @Override
1488 public String toString() {
1489 return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
1490 }
1491 }
1492
1493 /**
1494 * Extract the role information from an OF1.3 Role Reply Message
1495 * @param h
1496 * @param rrmsg
1497 * @return RoleReplyInfo object
1498 * @throws SwitchStateException
1499 */
1500 protected RoleReplyInfo extractOFRoleReply(OFChannelHandler h,
1501 OFRoleReply rrmsg) throws SwitchStateException {
1502 OFControllerRole cr = rrmsg.getRole();
1503 Role role = null;
1504 switch(cr) {
1505 case ROLE_EQUAL:
1506 role = Role.EQUAL;
1507 break;
1508 case ROLE_MASTER:
1509 role = Role.MASTER;
1510 break;
1511 case ROLE_SLAVE:
1512 role = Role.SLAVE;
1513 break;
1514 case ROLE_NOCHANGE: // switch should send current role
1515 default:
1516 String msg = String.format("Unknown controller role {} "
1517 + "received from switch {}", cr, h.sw);
1518 throw new SwitchStateException(msg);
1519 }
1520
1521 return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
1522 }
1523
1524 /**
1525 * Handles all pending port status messages before a switch is declared
1526 * activated in MASTER or EQUAL role. Note that since this handling
1527 * precedes the activation (and therefore notification to IOFSwitchListerners)
1528 * the changes to ports will already be visible once the switch is
1529 * activated. As a result, no notifications are sent out for these
1530 * pending portStatus messages.
1531 * @param h
1532 * @throws SwitchStateException
1533 */
1534 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
1535 try {
1536 handlePendingPortStatusMessages(h, 0);
1537 } catch (SwitchStateException e) {
1538 // do nothing - exception msg printed
1539 }
1540 }
1541
1542 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
1543 throws SwitchStateException {
1544 if (h.sw == null) {
1545 String msg = "State machine error: switch is null. Should never " +
1546 "happen";
1547 throw new SwitchStateException(msg);
1548 }
1549 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
1550 for (OFPortStatus ps: h.pendingPortStatusMsg) {
1551 temp.add(ps);
1552 handlePortStatusMessage(h, ps, false);
1553 }
1554 temp.clear();
1555 // expensive but ok - we don't expect too many port-status messages
1556 // note that we cannot use clear(), because of the reasons below
1557 h.pendingPortStatusMsg.removeAll(temp);
1558 // the iterator above takes a snapshot of the list - so while we were
1559 // dealing with the pending port-status messages, we could have received
1560 // newer ones. Handle them recursively, but break the recursion after
1561 // five steps to avoid an attack.
1562 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1563 handlePendingPortStatusMessages(h, index);
1564 }
1565 }
1566
1567 /**
1568 * Handle a port status message.
1569 *
1570 * Handle a port status message by updating the port maps in the
1571 * IOFSwitch instance and notifying Controller about the change so
1572 * it can dispatch a switch update.
1573 *
1574 * @param h The OFChannelHhandler that received the message
1575 * @param m The PortStatus message we received
1576 * @param doNotify if true switch port changed events will be
1577 * dispatched
1578 * @throws SwitchStateException
1579 *
1580 */
1581 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1582 boolean doNotify) throws SwitchStateException {
1583 if (h.sw == null) {
1584 String msg = getSwitchStateMessage(h, m,
1585 "State machine error: switch is null. Should never " +
1586 "happen");
1587 throw new SwitchStateException(msg);
1588 }
1589
1590 Collection<PortChangeEvent> changes = h.sw.processOFPortStatus(m);
1591 if (doNotify) {
1592 for (PortChangeEvent ev: changes)
1593 h.controller.notifyPortChanged(h.sw.getId(), ev.port, ev.type);
1594 }
1595 }
1596
1597 /**
1598 * Checks if the role received (from the role-reply msg) is different
1599 * from the existing role in the IOFSwitch object for this controller.
1600 * If so, it transitions the controller to the new role. Note that
1601 * the caller should have already verified that the role-reply msg
1602 * received was in response to a role-request msg sent out by this
1603 * controller after hearing from the registry service.
1604 *
1605 * @param h the ChannelHandler that received the message
1606 * @param role the role in the recieved role reply message
1607 */
1608 protected void checkAndSetRoleTransition(OFChannelHandler h, Role role) {
1609 // we received a role-reply in response to a role message
1610 // sent after hearing from the registry service. It is
1611 // possible that the role of this controller instance for
1612 // this switch has changed:
1613 // for 1.0 switch: from MASTER to SLAVE
1614 // for 1.3 switch: from MASTER to EQUAL
1615 if ((h.sw.getRole() == Role.MASTER && role == Role.SLAVE) ||
1616 (h.sw.getRole() == Role.MASTER && role == Role.EQUAL)) {
1617 // the mastership has changed
1618 h.sw.setRole(role);
1619 h.setState(EQUAL);
1620 h.controller.transitionToEqualSwitch(h.sw.getId());
1621 return;
1622 }
1623
1624 // or for both 1.0 and 1.3 switches from EQUAL to MASTER.
1625 // note that for 1.0, even though we mean SLAVE,
1626 // internally we call the role EQUAL.
1627 if (h.sw.getRole() == Role.EQUAL && role == Role.MASTER) {
1628 // the mastership has changed
1629 h.sw.setRole(role);
1630 h.setState(MASTER);
1631 h.controller.transitionToMasterSwitch(h.sw.getId());
1632 return;
1633 }
1634 }
1635
1636 /**
1637 * Process an OF message received on the channel and
1638 * update state accordingly.
1639 *
1640 * The main "event" of the state machine. Process the received message,
1641 * send follow up message if required and update state if required.
1642 *
1643 * Switches on the message type and calls more specific event handlers
1644 * for each individual OF message type. If we receive a message that
1645 * is supposed to be sent from a controller to a switch we throw
1646 * a SwitchStateExeption.
1647 *
1648 * The more specific handlers can also throw SwitchStateExceptions
1649 *
1650 * @param h The OFChannelHandler that received the message
1651 * @param m The message we received.
1652 * @throws SwitchStateException
1653 * @throws IOException
1654 */
1655 void processOFMessage(OFChannelHandler h, OFMessage m)
1656 throws IOException, SwitchStateException {
1657 h.roleChanger.checkTimeout();
1658 switch(m.getType()) {
1659 case HELLO:
1660 processOFHello(h, (OFHello)m);
1661 break;
1662 case BARRIER_REPLY:
1663 processOFBarrierReply(h, (OFBarrierReply)m);
1664 break;
1665 case ECHO_REPLY:
1666 processOFEchoReply(h, (OFEchoReply)m);
1667 break;
1668 case ECHO_REQUEST:
1669 processOFEchoRequest(h, (OFEchoRequest)m);
1670 break;
1671 case ERROR:
1672 processOFError(h, (OFErrorMsg)m);
1673 break;
1674 case FEATURES_REPLY:
1675 processOFFeaturesReply(h, (OFFeaturesReply)m);
1676 break;
1677 case FLOW_REMOVED:
1678 processOFFlowRemoved(h, (OFFlowRemoved)m);
1679 break;
1680 case GET_CONFIG_REPLY:
1681 processOFGetConfigReply(h, (OFGetConfigReply)m);
1682 break;
1683 case PACKET_IN:
1684 processOFPacketIn(h, (OFPacketIn)m);
1685 break;
1686 case PORT_STATUS:
1687 processOFPortStatus(h, (OFPortStatus)m);
1688 break;
1689 case QUEUE_GET_CONFIG_REPLY:
1690 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply)m);
1691 break;
1692 case STATS_REPLY: // multipart_reply in 1.3
1693 processOFStatisticsReply(h, (OFStatsReply)m);
1694 break;
1695 case EXPERIMENTER:
1696 processOFExperimenter(h, (OFExperimenter)m);
1697 break;
1698 case ROLE_REPLY:
1699 processOFRoleReply(h, (OFRoleReply)m);
1700 break;
1701 case GET_ASYNC_REPLY:
1702 processOFGetAsyncReply(h, (OFAsyncGetReply)m);
1703 break;
1704
1705 // The following messages are sent to switches. The controller
1706 // should never receive them
1707 case SET_CONFIG:
1708 case GET_CONFIG_REQUEST:
1709 case PACKET_OUT:
1710 case PORT_MOD:
1711 case QUEUE_GET_CONFIG_REQUEST:
1712 case BARRIER_REQUEST:
1713 case STATS_REQUEST: // multipart request in 1.3
1714 case FEATURES_REQUEST:
1715 case FLOW_MOD:
1716 case GROUP_MOD:
1717 case TABLE_MOD:
1718 case GET_ASYNC_REQUEST:
1719 case SET_ASYNC:
1720 case METER_MOD:
1721 default:
1722 illegalMessageReceived(h, m);
1723 break;
1724 }
1725 }
1726
1727 /*-----------------------------------------------------------------
1728 * Default implementation for message handlers in any state.
1729 *
1730 * Individual states must override these if they want a behavior
1731 * that differs from the default.
1732 *
1733 * In general, these handlers simply ignore the message and do
1734 * nothing.
1735 *
1736 * There are some exceptions though, since some messages really
1737 * are handled the same way in every state (e.g., ECHO_REQUST) or
1738 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1739 -----------------------------------------------------------------*/
1740
1741 void processOFHello(OFChannelHandler h, OFHello m)
1742 throws IOException, SwitchStateException {
1743 // we only expect hello in the WAIT_HELLO state
1744 illegalMessageReceived(h, m);
1745 }
1746
1747 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1748 throws IOException {
1749 // Silently ignore.
1750 }
1751
1752 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1753 throws IOException {
1754 if (h.ofVersion == null) {
1755 log.error("No OF version set for {}. Not sending Echo REPLY",
1756 h.channel.getRemoteAddress());
1757 return;
1758 }
1759 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1760 OFEchoReply reply = factory
1761 .buildEchoReply()
1762 .setXid(m.getXid())
1763 .setData(m.getData())
1764 .build();
1765 h.channel.write(Collections.singletonList(reply));
1766 }
1767
1768 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1769 throws IOException {
1770 // Do nothing with EchoReplies !!
1771 }
1772
1773 // no default implementation for OFError
1774 // every state must override it
1775 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1776 throws IOException, SwitchStateException;
1777
1778
1779 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1780 throws IOException, SwitchStateException {
1781 unhandledMessageReceived(h, m);
1782 }
1783
1784 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1785 throws IOException {
1786 unhandledMessageReceived(h, m);
1787 }
1788
1789 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1790 throws IOException, SwitchStateException {
1791 // we only expect config replies in the WAIT_CONFIG_REPLY state
1792 illegalMessageReceived(h, m);
1793 }
1794
1795 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1796 throws IOException {
1797 unhandledMessageReceived(h, m);
1798 }
1799
1800 // no default implementation. Every state needs to handle it.
1801 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1802 throws IOException, SwitchStateException;
1803
1804 void processOFQueueGetConfigReply(OFChannelHandler h,
1805 OFQueueGetConfigReply m)
1806 throws IOException {
1807 unhandledMessageReceived(h, m);
1808 }
1809
1810 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1811 throws IOException, SwitchStateException {
1812 unhandledMessageReceived(h, m);
1813 }
1814
1815 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1816 throws IOException, SwitchStateException {
1817 // TODO: it might make sense to parse the vendor message here
1818 // into the known vendor messages we support and then call more
1819 // specific event handlers
1820 unhandledMessageReceived(h, m);
1821 }
1822
1823 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1824 throws SwitchStateException, IOException {
1825 unhandledMessageReceived(h, m);
1826 }
1827
1828 void processOFGetAsyncReply(OFChannelHandler h,
1829 OFAsyncGetReply m) {
1830 unhandledMessageReceived(h, m);
1831 }
1832
1833 void handleUnsentRoleMessage(OFChannelHandler h, Role role,
1834 RoleRecvStatus expectation) throws IOException {
1835 // do nothing in most states
1836 }
Saurav Das6de23322014-08-07 18:51:19 -07001837
1838 /**
1839 * Handles role request messages that have timed out.
1840 * <p>
1841 * Role request messages that don't get role-replies (or errors related
1842 * to the request) time out after DEFAULT_ROLE_TIMEOUT_MS secs, at which
1843 * time the controller state-machine disconnects the switch
1844 *
1845 * @param h the channel handler for this switch
1846 * @param pendingRole the role for which no reply was received
1847 * @throws SwitchStateException
1848 */
1849 public void handleTimedOutRoleReply(OFChannelHandler h,
1850 Role pendingRole) throws SwitchStateException {
1851 String msg = String.format("Switch: [%s] State: [%s] did not "
1852 + "reply to role-msg for pending role %s. Disconnecting ...",
1853 h.getSwitchInfoString(), this.toString(), pendingRole);
1854 throw new SwitchStateException(msg);
1855 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001856 }
1857
1858
1859
1860 //*************************
1861 // Channel handler methods
1862 //*************************
1863
1864 @Override
1865 @LogMessageDoc(message="New switch connection from {ip address}",
1866 explanation="A new switch has connected from the " +
1867 "specified IP address")
1868 public void channelConnected(ChannelHandlerContext ctx,
1869 ChannelStateEvent e) throws Exception {
1870 counters.switchConnected.updateCounterWithFlush();
1871 channel = e.getChannel();
1872 log.info("New switch connection from {}",
1873 channel.getRemoteAddress());
1874 sendHandshakeHelloMessage();
1875 setState(ChannelState.WAIT_HELLO);
1876 }
1877
1878 @Override
1879 @LogMessageDoc(message="Disconnected switch {switch information}",
1880 explanation="The specified switch has disconnected.")
1881 public void channelDisconnected(ChannelHandlerContext ctx,
1882 ChannelStateEvent e) throws Exception {
1883 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1884 getSwitchInfoString());
1885 if (thisdpid != 0) {
1886 if (duplicateDpidFound != Boolean.TRUE) {
1887 // if the disconnected switch (on this ChannelHandler)
1888 // was not one with a duplicate-dpid, it is safe to remove all
1889 // state for it at the controller. Notice that if the disconnected
1890 // switch was a duplicate-dpid, calling the method below would clear
1891 // all state for the original switch (with the same dpid),
1892 // which we obviously don't want.
1893 controller.removeConnectedSwitch(thisdpid);
1894 } else {
1895 // A duplicate was disconnected on this ChannelHandler,
1896 // this is the same switch reconnecting, but the original state was
1897 // not cleaned up - XXX check liveness of original ChannelHandler
1898 duplicateDpidFound = Boolean.FALSE;
1899 }
1900 } else {
1901 log.warn("no dpid in channelHandler registered for "
1902 + "disconnected switch {}", getSwitchInfoString());
1903 }
1904 }
1905
1906 @Override
1907 @LogMessageDocs({
1908 @LogMessageDoc(level="ERROR",
1909 message="Disconnecting switch {switch} due to read timeout",
1910 explanation="The connected switch has failed to send any " +
1911 "messages or respond to echo requests",
1912 recommendation=LogMessageDoc.CHECK_SWITCH),
1913 @LogMessageDoc(level="ERROR",
1914 message="Disconnecting switch {switch}: failed to " +
1915 "complete handshake",
1916 explanation="The switch did not respond correctly " +
1917 "to handshake messages",
1918 recommendation=LogMessageDoc.CHECK_SWITCH),
1919 @LogMessageDoc(level="ERROR",
1920 message="Disconnecting switch {switch} due to IO Error: {}",
1921 explanation="There was an error communicating with the switch",
1922 recommendation=LogMessageDoc.CHECK_SWITCH),
1923 @LogMessageDoc(level="ERROR",
1924 message="Disconnecting switch {switch} due to switch " +
1925 "state error: {error}",
1926 explanation="The switch sent an unexpected message",
1927 recommendation=LogMessageDoc.CHECK_SWITCH),
1928 @LogMessageDoc(level="ERROR",
1929 message="Disconnecting switch {switch} due to " +
1930 "message parse failure",
1931 explanation="Could not parse a message from the switch",
1932 recommendation=LogMessageDoc.CHECK_SWITCH),
1933 @LogMessageDoc(level="ERROR",
1934 message="Terminating controller due to storage exception",
1935 explanation=Controller.ERROR_DATABASE,
1936 recommendation=LogMessageDoc.CHECK_CONTROLLER),
1937 @LogMessageDoc(level="ERROR",
1938 message="Could not process message: queue full",
1939 explanation="OpenFlow messages are arriving faster than " +
1940 " the controller can process them.",
1941 recommendation=LogMessageDoc.CHECK_CONTROLLER),
1942 @LogMessageDoc(level="ERROR",
1943 message="Error while processing message " +
1944 "from switch {switch} {cause}",
1945 explanation="An error occurred processing the switch message",
1946 recommendation=LogMessageDoc.GENERIC_ACTION)
1947 })
1948 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1949 throws Exception {
1950 if (e.getCause() instanceof ReadTimeoutException) {
1951 // switch timeout
1952 log.error("Disconnecting switch {} due to read timeout",
1953 getSwitchInfoString());
1954 counters.switchDisconnectReadTimeout.updateCounterWithFlush();
1955 ctx.getChannel().close();
1956 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1957 log.error("Disconnecting switch {}: failed to complete handshake",
1958 getSwitchInfoString());
1959 counters.switchDisconnectHandshakeTimeout.updateCounterWithFlush();
1960 ctx.getChannel().close();
1961 } else if (e.getCause() instanceof ClosedChannelException) {
1962 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1963 } else if (e.getCause() instanceof IOException) {
1964 log.error("Disconnecting switch {} due to IO Error: {}",
1965 getSwitchInfoString(), e.getCause().getMessage());
1966 if (log.isDebugEnabled()) {
1967 // still print stack trace if debug is enabled
1968 log.debug("StackTrace for previous Exception: ", e.getCause());
1969 }
1970 counters.switchDisconnectIOError.updateCounterWithFlush();
1971 ctx.getChannel().close();
1972 } else if (e.getCause() instanceof SwitchStateException) {
1973 log.error("Disconnecting switch {} due to switch state error: {}",
1974 getSwitchInfoString(), e.getCause().getMessage());
1975 if (log.isDebugEnabled()) {
1976 // still print stack trace if debug is enabled
1977 log.debug("StackTrace for previous Exception: ", e.getCause());
1978 }
1979 counters.switchDisconnectSwitchStateException.updateCounterWithFlush();
1980 ctx.getChannel().close();
1981 } else if (e.getCause() instanceof OFParseError) {
1982 log.error("Disconnecting switch "
1983 + getSwitchInfoString() +
1984 " due to message parse failure",
1985 e.getCause());
1986 counters.switchDisconnectParseError.updateCounterWithFlush();
1987 ctx.getChannel().close();
1988 } else if (e.getCause() instanceof RejectedExecutionException) {
1989 log.warn("Could not process message: queue full");
1990 counters.rejectedExecutionException.updateCounterWithFlush();
1991 } else {
1992 log.error("Error while processing message from switch "
1993 + getSwitchInfoString()
1994 + "state " + this.state, e.getCause());
1995 counters.switchDisconnectOtherException.updateCounterWithFlush();
1996 ctx.getChannel().close();
1997 }
1998 }
1999
2000 @Override
2001 public String toString() {
2002 return getSwitchInfoString();
2003 }
2004
2005 @Override
2006 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
2007 throws Exception {
2008 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2009 OFMessage m = factory.buildEchoRequest().build();
2010 log.info("Sending Echo Request on idle channel: {}",
2011 e.getChannel().getPipeline().getLast().toString());
2012 e.getChannel().write(Collections.singletonList(m));
2013 // XXX S some problems here -- echo request has no transaction id, and
2014 // echo reply is not correlated to the echo request.
2015 }
2016
2017 @Override
2018 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
2019 throws Exception {
2020 if (e.getMessage() instanceof List) {
2021 @SuppressWarnings("unchecked")
2022 List<OFMessage> msglist = (List<OFMessage>)e.getMessage();
2023
2024 LoadMonitor.LoadLevel loadlevel;
2025 int packets_dropped = 0;
2026 int packets_allowed = 0;
2027 int lldps_allowed = 0;
2028
2029 if (this.controller.overload_drop) {
2030 loadlevel = this.controller.loadmonitor.getLoadLevel();
2031 }
2032 else {
2033 loadlevel = LoadMonitor.LoadLevel.OK;
2034 }
2035
2036 for (OFMessage ofm : msglist) {
2037 counters.messageReceived.updateCounterNoFlush();
2038 // Per-switch input throttling - placeholder for future throttling
2039 /*if (sw != null && sw.inputThrottled(ofm)) {
2040 counters.messageInputThrottled.updateCounterNoFlush();
2041 continue;
2042 }*/
2043 try {
2044 if (this.controller.overload_drop &&
2045 !loadlevel.equals(LoadMonitor.LoadLevel.OK)) {
2046 switch (ofm.getType()) {
2047 case PACKET_IN:
2048 switch (loadlevel) {
2049 case VERYHIGH:
2050 // Drop all packet-ins, including LLDP/BDDPs
2051 packets_dropped++;
2052 continue;
2053 case HIGH:
2054 // Drop all packet-ins, except LLDP/BDDPs
2055 byte[] data = ((OFPacketIn)ofm).getData();
2056 if (data.length > 14) {
2057 if (((data[12] == (byte)0x88) &&
2058 (data[13] == (byte)0xcc)) ||
2059 ((data[12] == (byte)0x89) &&
2060 (data[13] == (byte)0x42))) {
2061 lldps_allowed++;
2062 packets_allowed++;
2063 break;
2064 }
2065 }
2066 packets_dropped++;
2067 continue;
2068 default:
2069 // Load not high, go ahead and process msg
2070 packets_allowed++;
2071 break;
2072 }
2073 break;
2074 default:
2075 // Process all non-packet-ins
2076 packets_allowed++;
2077 break;
2078 }
2079 }
2080
2081 // Do the actual packet processing
2082 state.processOFMessage(this, ofm);
2083
2084 }
2085 catch (Exception ex) {
2086 // We are the last handler in the stream, so run the
2087 // exception through the channel again by passing in
2088 // ctx.getChannel().
2089 Channels.fireExceptionCaught(ctx.getChannel(), ex);
2090 }
2091 }
2092
2093 if (loadlevel != LoadMonitor.LoadLevel.OK) {
2094 if (log.isDebugEnabled()) {
2095 log.debug(
2096 "Overload: Detected {}, packets dropped={}",
2097 loadlevel.toString(), packets_dropped);
2098 log.debug(
2099 "Overload: Packets allowed={} (LLDP/BDDPs allowed={})",
2100 packets_allowed, lldps_allowed);
2101 }
2102 }
2103 }
2104 else {
2105 //Channels.fireExceptionCaught(ctx.getChannel(),
2106 // new AssertionError("Message received from Channel is not a list"));
2107 //TODO: Pankaj: move the counters using ONOS metrics implementation
2108
2109 counters.messageReceived.updateCounterNoFlush();
2110 state.processOFMessage(this, (OFMessage) e.getMessage());
2111 }
2112
2113 // Flush all thread local queues etc. generated by this train
2114 // of messages.
2115 this.controller.flushAll();
2116 }
2117
2118
2119
2120 //*************************
2121 // Channel utility methods
2122 //*************************
2123
2124 /**
2125 * Is this a state in which the handshake has completed?
2126 * @return true if the handshake is complete
2127 */
2128 public boolean isHandshakeComplete() {
2129 return this.state.isHandshakeComplete();
2130 }
2131
2132 private void dispatchMessage(OFMessage m) throws IOException {
2133 // handleMessage will count
2134 this.controller.handleMessage(this.sw, m, null);
2135 }
2136
2137 /**
2138 * Return a string describing this switch based on the already available
2139 * information (DPID and/or remote socket)
2140 * @return
2141 */
2142 private String getSwitchInfoString() {
2143 if (sw != null)
2144 return sw.toString();
2145 String channelString;
2146 if (channel == null || channel.getRemoteAddress() == null) {
2147 channelString = "?";
2148 } else {
2149 channelString = channel.getRemoteAddress().toString();
2150 }
2151 String dpidString;
2152 if (featuresReply == null) {
2153 dpidString = "?";
2154 } else {
2155 dpidString = featuresReply.getDatapathId().toString();
2156 }
2157 return String.format("[%s DPID[%s]]", channelString, dpidString);
2158 }
2159
2160 /**
2161 * Update the channels state. Only called from the state machine.
2162 * TODO: enforce restricted state transitions
2163 * @param state
2164 */
2165 private void setState(ChannelState state) {
2166 this.state = state;
2167 }
2168
2169 /**
2170 * Send hello message to the switch using the handshake transactions ids.
2171 * @throws IOException
2172 */
2173 private void sendHandshakeHelloMessage() throws IOException {
2174 // The OF protocol requires us to start things off by sending the highest
2175 // version of the protocol supported.
2176
2177 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
2178 // see Sec. 7.5.1 of the OF1.3.4 spec
2179 U32 bitmap = U32.ofRaw(0x00000012);
2180 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
2181 .setBitmaps(Collections.singletonList(bitmap))
2182 .build();
2183 OFMessage.Builder mb = factory13.buildHello()
2184 .setXid(this.handshakeTransactionIds--)
2185 .setElements(Collections.singletonList(hem));
2186 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
2187 channel.write(Collections.singletonList(mb.build()));
2188 }
2189
2190 /**
2191 * Send featuresRequest msg to the switch using the handshake transactions ids.
2192 * @throws IOException
2193 */
2194 private void sendHandshakeFeaturesRequestMessage() throws IOException {
2195 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2196 OFMessage m = factory.buildFeaturesRequest()
2197 .setXid(this.handshakeTransactionIds--)
2198 .build();
2199 channel.write(Collections.singletonList(m));
2200 }
2201
2202 private void setSwitchRole(Role role) {
2203 sw.setRole(role);
2204 }
2205
2206 /**
2207 * Send the configuration requests to tell the switch we want full
2208 * packets
2209 * @throws IOException
2210 */
2211 private void sendHandshakeSetConfig() throws IOException {
2212 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2213 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
2214 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
2215
2216 // Ensure we receive the full packet via PacketIn
2217 // FIXME: We don't set the reassembly flags.
2218 OFSetConfig sc = factory
2219 .buildSetConfig()
2220 .setMissSendLen((short) 0xffff)
2221 .setXid(this.handshakeTransactionIds--)
2222 .build();
2223 msglist.add(sc);
2224
2225 // Barrier
2226 OFBarrierRequest br = factory
2227 .buildBarrierRequest()
2228 .setXid(this.handshakeTransactionIds--)
2229 .build();
2230 msglist.add(br);
2231
2232 // Verify (need barrier?)
2233 OFGetConfigRequest gcr = factory
2234 .buildGetConfigRequest()
2235 .setXid(this.handshakeTransactionIds--)
2236 .build();
2237 msglist.add(gcr);
2238 channel.write(msglist);
2239 }
2240
2241 /**
2242 * send a description state request
2243 * @throws IOException
2244 */
2245 private void sendHandshakeDescriptionStatsRequest() throws IOException {
2246 // Get Description to set switch-specific flags
2247 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
2248 OFDescStatsRequest dreq = factory
2249 .buildDescStatsRequest()
2250 .setXid(handshakeTransactionIds--)
2251 .build();
2252 channel.write(Collections.singletonList(dreq));
2253 }
2254
2255 private void sendHandshakeOFPortDescRequest() throws IOException {
2256 // Get port description for 1.3 switch
2257 OFPortDescStatsRequest preq = factory13
2258 .buildPortDescStatsRequest()
2259 .setXid(handshakeTransactionIds--)
2260 .build();
2261 channel.write(Collections.singletonList(preq));
2262 }
2263
2264 /**
2265 * Read switch properties from storage and set switch attributes accordingly
2266 */
2267 private void readPropertyFromStorage() {
2268 // XXX This is a placeholder for switch configuration
2269 }
2270
2271 ChannelState getStateForTesting() {
2272 return state;
2273 }
2274
2275 void useRoleChangerWithOtherTimeoutForTesting(long roleTimeoutMs) {
2276 roleChanger = new RoleChanger(roleTimeoutMs);
2277 }
2278
2279
2280}