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