blob: 8b4d444ed7be350732c46a31821de2194e5ffabb [file] [log] [blame]
tom7ef8ff92014-09-17 13:08:06 -07001//CHECKSTYLE:OFF
tom9c94c5b2014-09-17 13:14:42 -07002package org.onlab.onos.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -07003
4import java.io.IOException;
5import java.nio.channels.ClosedChannelException;
6import java.util.ArrayList;
7import java.util.Collections;
8import java.util.List;
9import java.util.concurrent.CopyOnWriteArrayList;
10import java.util.concurrent.RejectedExecutionException;
11
12import org.jboss.netty.channel.Channel;
13import org.jboss.netty.channel.ChannelHandlerContext;
14import org.jboss.netty.channel.ChannelStateEvent;
15import org.jboss.netty.channel.ExceptionEvent;
16import org.jboss.netty.channel.MessageEvent;
17import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
18import org.jboss.netty.handler.timeout.IdleStateEvent;
19import org.jboss.netty.handler.timeout.ReadTimeoutException;
tom9c94c5b2014-09-17 13:14:42 -070020import org.onlab.onos.openflow.controller.driver.OpenFlowSwitchDriver;
21import org.onlab.onos.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070022import org.projectfloodlight.openflow.exceptions.OFParseError;
23import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
24import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
25import org.projectfloodlight.openflow.protocol.OFBarrierReply;
26import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
27import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
28import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
29import org.projectfloodlight.openflow.protocol.OFEchoReply;
30import org.projectfloodlight.openflow.protocol.OFEchoRequest;
31import org.projectfloodlight.openflow.protocol.OFErrorMsg;
32import org.projectfloodlight.openflow.protocol.OFErrorType;
33import org.projectfloodlight.openflow.protocol.OFExperimenter;
34import org.projectfloodlight.openflow.protocol.OFFactory;
35import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
36import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
37import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
38import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
39import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
40import org.projectfloodlight.openflow.protocol.OFHello;
41import org.projectfloodlight.openflow.protocol.OFHelloElem;
42import org.projectfloodlight.openflow.protocol.OFMessage;
43import org.projectfloodlight.openflow.protocol.OFPacketIn;
44import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
45import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
46import org.projectfloodlight.openflow.protocol.OFPortStatus;
47import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
48import org.projectfloodlight.openflow.protocol.OFRoleReply;
49import org.projectfloodlight.openflow.protocol.OFSetConfig;
50import org.projectfloodlight.openflow.protocol.OFStatsReply;
51import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
52import org.projectfloodlight.openflow.protocol.OFStatsType;
53import org.projectfloodlight.openflow.protocol.OFType;
54import org.projectfloodlight.openflow.protocol.OFVersion;
55import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
56import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
57import org.projectfloodlight.openflow.types.U32;
58import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
61/**
62 * Channel handler deals with the switch connection and dispatches
63 * switch messages to the appropriate locations.
64 */
65class OFChannelHandler extends IdleStateAwareChannelHandler {
66 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
67 private final Controller controller;
68 private OpenFlowSwitchDriver sw;
69 private long thisdpid; // channelHandler cached value of connected switch id
70 private Channel channel;
71 // State needs to be volatile because the HandshakeTimeoutHandler
72 // needs to check if the handshake is complete
73 private volatile ChannelState state;
74
75 // When a switch with a duplicate dpid is found (i.e we already have a
76 // connected switch with the same dpid), the new switch is immediately
77 // disconnected. At that point netty callsback channelDisconnected() which
78 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
79 // switch state for the older (still connected) switch
80 private volatile Boolean duplicateDpidFound;
81
82 // Temporary storage for switch-features and port-description
83 private OFFeaturesReply featuresReply;
84 private OFPortDescStatsReply portDescReply;
85 // a concurrent ArrayList to temporarily store port status messages
86 // before we are ready to deal with them
87 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
88
89 //Indicates the openflow version used by this switch
90 protected OFVersion ofVersion;
91 protected OFFactory factory13;
92 protected OFFactory factory10;
93
94 /** transaction Ids to use during handshake. Since only one thread
95 * calls into an OFChannelHandler instance, we don't need atomic.
96 * We will count down
97 */
98 private int handshakeTransactionIds = -1;
99
100 /**
101 * Create a new unconnected OFChannelHandler.
102 * @param controller
103 */
104 OFChannelHandler(Controller controller) {
105 this.controller = controller;
106 this.state = ChannelState.INIT;
107 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
108 factory13 = controller.getOFMessageFactory13();
109 factory10 = controller.getOFMessageFactory10();
110 duplicateDpidFound = Boolean.FALSE;
111 }
112
113
114
115 // XXX S consider if necessary
116 public void disconnectSwitch() {
117 sw.disconnectSwitch();
118 }
119
120
121
122 //*************************
123 // Channel State Machine
124 //*************************
125
126 /**
127 * The state machine for handling the switch/channel state. All state
128 * transitions should happen from within the state machine (and not from other
129 * parts of the code)
130 */
131 enum ChannelState {
132 /**
133 * Initial state before channel is connected.
134 */
135 INIT(false) {
136 @Override
137 void processOFMessage(OFChannelHandler h, OFMessage m)
138 throws IOException, SwitchStateException {
139 illegalMessageReceived(h, m);
140 }
141
142 @Override
143 void processOFError(OFChannelHandler h, OFErrorMsg m)
144 throws IOException {
145 // need to implement since its abstract but it will never
146 // be called
147 }
148
149 @Override
150 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
151 throws IOException {
152 unhandledMessageReceived(h, m);
153 }
154 },
155
156 /**
157 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
158 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
159 * protocol version is accepted.
160 * We send an OFFeaturesRequest depending on the protocol version selected
161 * Next state is WAIT_FEATURES_REPLY
162 */
163 WAIT_HELLO(false) {
164 @Override
165 void processOFHello(OFChannelHandler h, OFHello m)
166 throws IOException {
167 // TODO We could check for the optional bitmap, but for now
168 // we are just checking the version number.
169 if (m.getVersion() == OFVersion.OF_13) {
alshabib09d48be2014-10-03 15:43:33 -0700170 log.debug("Received {} Hello from {}", m.getVersion(),
tom7ef8ff92014-09-17 13:08:06 -0700171 h.channel.getRemoteAddress());
172 h.ofVersion = OFVersion.OF_13;
173 } else if (m.getVersion() == OFVersion.OF_10) {
alshabib09d48be2014-10-03 15:43:33 -0700174 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700175 + "version 1.0", m.getVersion(),
176 h.channel.getRemoteAddress());
177 h.ofVersion = OFVersion.OF_10;
178 } else {
179 log.error("Received Hello of version {} from switch at {}. "
180 + "This controller works with OF1.0 and OF1.3 "
181 + "switches. Disconnecting switch ...",
182 m.getVersion(), h.channel.getRemoteAddress());
183 h.channel.disconnect();
184 return;
185 }
186 h.sendHandshakeFeaturesRequestMessage();
187 h.setState(WAIT_FEATURES_REPLY);
188 }
189 @Override
190 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
191 throws IOException, SwitchStateException {
192 illegalMessageReceived(h, m);
193 }
194 @Override
195 void processOFStatisticsReply(OFChannelHandler h,
196 OFStatsReply m)
197 throws IOException, SwitchStateException {
198 illegalMessageReceived(h, m);
199 }
200 @Override
201 void processOFError(OFChannelHandler h, OFErrorMsg m) {
202 logErrorDisconnect(h, m);
203 }
204
205 @Override
206 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
207 throws IOException {
208 unhandledMessageReceived(h, m);
209 }
210 },
211
212
213 /**
214 * We are waiting for a features reply message. Once we receive it, the
215 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
216 * we send a SetConfig request, barrier, and GetConfig request and the
217 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
218 * request and the next state is WAIT_PORT_DESC_REPLY.
219 */
220 WAIT_FEATURES_REPLY(false) {
221 @Override
222 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
223 throws IOException {
224 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700225 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700226 h.getSwitchInfoString(), h.thisdpid);
227
228 h.featuresReply = m; //temp store
229 if (h.ofVersion == OFVersion.OF_10) {
230 h.sendHandshakeSetConfig();
231 h.setState(WAIT_CONFIG_REPLY);
232 } else {
233 //version is 1.3, must get switchport information
234 h.sendHandshakeOFPortDescRequest();
235 h.setState(WAIT_PORT_DESC_REPLY);
236 }
237 }
238 @Override
239 void processOFStatisticsReply(OFChannelHandler h,
240 OFStatsReply m)
241 throws IOException, SwitchStateException {
242 illegalMessageReceived(h, m);
243 }
244 @Override
245 void processOFError(OFChannelHandler h, OFErrorMsg m) {
246 logErrorDisconnect(h, m);
247 }
248
249 @Override
250 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
251 throws IOException {
252 unhandledMessageReceived(h, m);
253 }
254 },
255
256 /**
257 * We are waiting for a description of the 1.3 switch ports.
258 * Once received, we send a SetConfig request
259 * Next State is WAIT_CONFIG_REPLY
260 */
261 WAIT_PORT_DESC_REPLY(false) {
262
263 @Override
264 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
265 throws SwitchStateException {
266 // Read port description
267 if (m.getStatsType() != OFStatsType.PORT_DESC) {
268 log.warn("Expecting port description stats but received stats "
269 + "type {} from {}. Ignoring ...", m.getStatsType(),
270 h.channel.getRemoteAddress());
271 return;
272 }
273 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
274 log.warn("Stats reply indicates more stats from sw {} for "
275 + "port description - not currently handled",
276 h.getSwitchInfoString());
277 }
278 h.portDescReply = (OFPortDescStatsReply) m; // temp store
279 log.info("Received port desc reply for switch at {}",
280 h.getSwitchInfoString());
281 try {
282 h.sendHandshakeSetConfig();
283 } catch (IOException e) {
284 log.error("Unable to send setConfig after PortDescReply. "
285 + "Error: {}", e.getMessage());
286 }
287 h.setState(WAIT_CONFIG_REPLY);
288 }
289
290 @Override
291 void processOFError(OFChannelHandler h, OFErrorMsg m)
292 throws IOException, SwitchStateException {
293 logErrorDisconnect(h, m);
294
295 }
296
297 @Override
298 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
299 throws IOException, SwitchStateException {
300 unhandledMessageReceived(h, m);
301
302 }
303 },
304
305 /**
306 * We are waiting for a config reply message. Once we receive it
307 * we send a DescriptionStatsRequest to the switch.
308 * Next state: WAIT_DESCRIPTION_STAT_REPLY
309 */
310 WAIT_CONFIG_REPLY(false) {
311 @Override
312 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
313 throws IOException {
314 if (m.getMissSendLen() == 0xffff) {
315 log.trace("Config Reply from switch {} confirms "
316 + "miss length set to 0xffff",
317 h.getSwitchInfoString());
318 } else {
319 // FIXME: we can't really deal with switches that don't send
320 // full packets. Shouldn't we drop the connection here?
321 log.warn("Config Reply from switch {} has"
322 + "miss length set to {}",
323 h.getSwitchInfoString(),
324 m.getMissSendLen());
325 }
326 h.sendHandshakeDescriptionStatsRequest();
327 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
328 }
329
330 @Override
331 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
332 // do nothing;
333 }
334
335 @Override
336 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
337 throws IOException, SwitchStateException {
338 illegalMessageReceived(h, m);
339 }
340 @Override
341 void processOFStatisticsReply(OFChannelHandler h,
342 OFStatsReply m)
343 throws IOException, SwitchStateException {
344 log.error("Received multipart(stats) message sub-type {}",
345 m.getStatsType());
346 illegalMessageReceived(h, m);
347 }
348
349 @Override
350 void processOFError(OFChannelHandler h, OFErrorMsg m) {
351 logErrorDisconnect(h, m);
352 }
353
354 @Override
355 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
356 throws IOException {
357 h.pendingPortStatusMsg.add(m);
358 }
359 },
360
361
362 /**
363 * We are waiting for a OFDescriptionStat message from the switch.
364 * Once we receive any stat message we try to parse it. If it's not
365 * a description stats message we disconnect. If its the expected
366 * description stats message, we:
367 * - use the switch driver to bind the switch and get an IOFSwitch instance
368 * - setup the IOFSwitch instance
369 * - add switch controller and send the initial role
370 * request to the switch.
371 * Next state: WAIT_INITIAL_ROLE
372 * In the typical case, where switches support role request messages
373 * the next state is where we expect the role reply message.
374 * In the special case that where the switch does not support any kind
375 * of role request messages, we don't send a role message, but we do
376 * request mastership from the registry service. This controller
377 * should become master once we hear back from the registry service.
378 * All following states will have a h.sw instance!
379 */
380 WAIT_DESCRIPTION_STAT_REPLY(false) {
381 @Override
382 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
383 throws SwitchStateException {
384 // Read description, if it has been updated
385 if (m.getStatsType() != OFStatsType.DESC) {
386 log.warn("Expecting Description stats but received stats "
387 + "type {} from {}. Ignoring ...", m.getStatsType(),
388 h.channel.getRemoteAddress());
389 return;
390 }
391 log.info("Received switch description reply from switch at {}",
392 h.channel.getRemoteAddress());
393 OFDescStatsReply drep = (OFDescStatsReply) m;
394 // Here is where we differentiate between different kinds of switches
395 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
396
397 h.sw.setOFVersion(h.ofVersion);
398 h.sw.setFeaturesReply(h.featuresReply);
399 h.sw.setPortDescReply(h.portDescReply);
400 h.sw.setConnected(true);
401 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700402// boolean success = h.sw.connectSwitch();
403//
404// if (!success) {
405// disconnectDuplicate(h);
406// return;
407// }
tom7ef8ff92014-09-17 13:08:06 -0700408 // set switch information
409
410
411
alshabib09d48be2014-10-03 15:43:33 -0700412 log.debug("Switch {} bound to class {}, description {}",
tom7ef8ff92014-09-17 13:08:06 -0700413 new Object[] {h.sw, h.sw.getClass(), drep });
414 //Put switch in EQUAL mode until we hear back from the global registry
415 //log.debug("Setting new switch {} to EQUAL and sending Role request",
416 // h.sw.getStringId());
417 //h.sw.activateEqualSwitch();
418 //h.setSwitchRole(RoleState.EQUAL);
419
420 h.sw.startDriverHandshake();
421 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
422
423 }
424
425 @Override
426 void processOFError(OFChannelHandler h, OFErrorMsg m) {
427 logErrorDisconnect(h, m);
428 }
429
430 @Override
431 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
432 throws IOException, SwitchStateException {
433 illegalMessageReceived(h, m);
434 }
435
436 @Override
437 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
438 throws IOException {
439 h.pendingPortStatusMsg.add(m);
440 }
441 },
442
443
444 /**
445 * We are waiting for the respective switch driver to complete its
446 * configuration. Notice that we do not consider this to be part of the main
447 * switch-controller handshake. But we do consider it as a step that comes
448 * before we declare the switch as available to the controller.
449 * Next State: depends on the role of this controller for this switch - either
450 * MASTER or EQUAL.
451 */
452 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
453
454 @Override
455 void processOFError(OFChannelHandler h, OFErrorMsg m)
456 throws IOException {
457 // will never be called. We override processOFMessage
458 }
459
alshabibd7963912014-10-20 14:52:04 -0700460
461
tom7ef8ff92014-09-17 13:08:06 -0700462 @Override
463 void processOFMessage(OFChannelHandler h, OFMessage m)
464 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700465
466 if (h.sw.isDriverHandshakeComplete()) {
467 moveToActive(h);
468
469 }
470
tom7ef8ff92014-09-17 13:08:06 -0700471 if (m.getType() == OFType.ECHO_REQUEST) {
472 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700473 } else if (m.getType() == OFType.ECHO_REPLY) {
474 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700475 } else if (m.getType() == OFType.ROLE_REPLY) {
476 h.sw.handleRole(m);
477 } else if (m.getType() == OFType.ERROR) {
478 if (!h.sw.handleRoleError((OFErrorMsg)m)) {
479 h.sw.processDriverHandshakeMessage(m);
480 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700481 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700482 h.setState(ACTIVE);
483 }
484 }
485 } else {
486 if (m.getType() == OFType.EXPERIMENTER &&
487 ((OFExperimenter) m).getExperimenter() ==
488 RoleManager.NICIRA_EXPERIMENTER) {
489 h.sw.handleNiciraRole(m);
490 } else {
491 h.sw.processDriverHandshakeMessage(m);
492 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700493 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700494 }
495 }
496 }
497 }
498
499 @Override
500 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
501 throws IOException, SwitchStateException {
502 h.pendingPortStatusMsg.add(m);
503 }
alshabibd7963912014-10-20 14:52:04 -0700504
505 private void moveToActive(OFChannelHandler h) {
506 boolean success = h.sw.connectSwitch();
507 h.setState(ACTIVE);
508 if (!success) {
509 disconnectDuplicate(h);
510 return;
511 }
512 }
513
tom7ef8ff92014-09-17 13:08:06 -0700514 },
515
516
517 /**
518 * This controller is in MASTER role for this switch. We enter this state
519 * after requesting and winning control from the global registry.
520 * The main handshake as well as the switch-driver sub-handshake
521 * is complete at this point.
522 * // XXX S reconsider below
523 * In the (near) future we may deterministically assign controllers to
524 * switches at startup.
525 * We only leave this state if the switch disconnects or
526 * if we send a role request for SLAVE /and/ receive the role reply for
527 * SLAVE.
528 */
529 ACTIVE(true) {
530 @Override
531 void processOFError(OFChannelHandler h, OFErrorMsg m)
532 throws IOException, SwitchStateException {
533 // if we get here, then the error message is for something else
534 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
535 ((OFBadRequestErrorMsg) m).getCode() ==
536 OFBadRequestCode.EPERM) {
537 // We are the master controller and the switch returned
538 // a permission error. This is a likely indicator that
539 // the switch thinks we are slave. Reassert our
540 // role
541 // FIXME: this could be really bad during role transitions
542 // if two controllers are master (even if its only for
543 // a brief period). We might need to see if these errors
544 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700545
tom7ef8ff92014-09-17 13:08:06 -0700546 h.sw.reassertRole();
547 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
548 ((OFFlowModFailedErrorMsg) m).getCode() ==
549 OFFlowModFailedCode.ALL_TABLES_FULL) {
550 h.sw.setTableFull(true);
551 } else {
552 logError(h, m);
553 }
554 h.dispatchMessage(m);
555 }
556
557 @Override
558 void processOFStatisticsReply(OFChannelHandler h,
559 OFStatsReply m) {
560 h.dispatchMessage(m);
561 }
562
563 @Override
564 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
565 throws SwitchStateException {
566 h.sw.handleNiciraRole(m);
567 }
568
569 @Override
570 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
571 throws SwitchStateException {
572 h.sw.handleRole(m);
573 }
574
575 @Override
576 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
577 throws SwitchStateException {
578 handlePortStatusMessage(h, m, true);
579 h.dispatchMessage(m);
580 }
581
582 @Override
583 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
584 h.dispatchMessage(m);
585 }
586
587 @Override
588 void processOFFlowRemoved(OFChannelHandler h,
589 OFFlowRemoved m) {
590 h.dispatchMessage(m);
591 }
592
593 @Override
594 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
595 h.dispatchMessage(m);
596 }
597
598 };
599
600 private final boolean handshakeComplete;
601 ChannelState(boolean handshakeComplete) {
602 this.handshakeComplete = handshakeComplete;
603 }
604
605 /**
606 * Is this a state in which the handshake has completed?
607 * @return true if the handshake is complete
608 */
609 public boolean isHandshakeComplete() {
610 return handshakeComplete;
611 }
612
613 /**
614 * Get a string specifying the switch connection, state, and
615 * message received. To be used as message for SwitchStateException
616 * or log messages
617 * @param h The channel handler (to get switch information_
618 * @param m The OFMessage that has just been received
619 * @param details A string giving more details about the exact nature
620 * of the problem.
621 * @return display string
622 */
623 // needs to be protected because enum members are actually subclasses
624 protected String getSwitchStateMessage(OFChannelHandler h,
625 OFMessage m,
626 String details) {
627 return String.format("Switch: [%s], State: [%s], received: [%s]"
628 + ", details: %s",
629 h.getSwitchInfoString(),
630 this.toString(),
631 m.getType().toString(),
632 details);
633 }
634
635 /**
636 * We have an OFMessage we didn't expect given the current state and
637 * we want to treat this as an error.
638 * We currently throw an exception that will terminate the connection
639 * However, we could be more forgiving
640 * @param h the channel handler that received the message
641 * @param m the message
642 * @throws SwitchStateException
643 * @throws SwitchStateExeption we always through the execption
644 */
645 // needs to be protected because enum members are acutally subclasses
646 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
647 throws SwitchStateException {
648 String msg = getSwitchStateMessage(h, m,
649 "Switch should never send this message in the current state");
650 throw new SwitchStateException(msg);
651
652 }
653
654 /**
655 * We have an OFMessage we didn't expect given the current state and
656 * we want to ignore the message.
657 * @param h the channel handler the received the message
658 * @param m the message
659 */
660 protected void unhandledMessageReceived(OFChannelHandler h,
661 OFMessage m) {
662 if (log.isDebugEnabled()) {
663 String msg = getSwitchStateMessage(h, m,
664 "Ignoring unexpected message");
665 log.debug(msg);
666 }
667 }
668
669 /**
670 * Log an OpenFlow error message from a switch.
671 * @param h The switch that sent the error
672 * @param error The error message
673 */
674 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700675 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700676 new Object[] {
677 error,
678 h.getSwitchInfoString(),
679 this.toString()});
680 }
681
682 /**
683 * Log an OpenFlow error message from a switch and disconnect the
684 * channel.
685 *
686 * @param h the IO channel for this switch.
687 * @param error The error message
688 */
689 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
690 logError(h, error);
691 h.channel.disconnect();
692 }
693
694 /**
695 * log an error message for a duplicate dpid and disconnect this channel.
696 * @param h the IO channel for this switch.
697 */
698 protected void disconnectDuplicate(OFChannelHandler h) {
699 log.error("Duplicated dpid or incompleted cleanup - "
700 + "disconnecting channel {}", h.getSwitchInfoString());
701 h.duplicateDpidFound = Boolean.TRUE;
702 h.channel.disconnect();
703 }
704
705
706
707 /**
708 * Handles all pending port status messages before a switch is declared
709 * activated in MASTER or EQUAL role. Note that since this handling
710 * precedes the activation (and therefore notification to IOFSwitchListerners)
711 * the changes to ports will already be visible once the switch is
712 * activated. As a result, no notifications are sent out for these
713 * pending portStatus messages.
714 * @param h
715 * @throws SwitchStateException
716 */
717 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
718 try {
719 handlePendingPortStatusMessages(h, 0);
720 } catch (SwitchStateException e) {
721 log.error(e.getMessage());
722 }
723 }
724
725 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
726 throws SwitchStateException {
727 if (h.sw == null) {
728 String msg = "State machine error: switch is null. Should never " +
729 "happen";
730 throw new SwitchStateException(msg);
731 }
732 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
733 for (OFPortStatus ps: h.pendingPortStatusMsg) {
734 temp.add(ps);
735 handlePortStatusMessage(h, ps, false);
736 }
737 temp.clear();
738 // expensive but ok - we don't expect too many port-status messages
739 // note that we cannot use clear(), because of the reasons below
740 h.pendingPortStatusMsg.removeAll(temp);
741 // the iterator above takes a snapshot of the list - so while we were
742 // dealing with the pending port-status messages, we could have received
743 // newer ones. Handle them recursively, but break the recursion after
744 // five steps to avoid an attack.
745 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
746 handlePendingPortStatusMessages(h, index);
747 }
748 }
749
750 /**
751 * Handle a port status message.
752 *
753 * Handle a port status message by updating the port maps in the
754 * IOFSwitch instance and notifying Controller about the change so
755 * it can dispatch a switch update.
756 *
757 * @param h The OFChannelHhandler that received the message
758 * @param m The PortStatus message we received
759 * @param doNotify if true switch port changed events will be
760 * dispatched
761 * @throws SwitchStateException
762 *
763 */
764 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
765 boolean doNotify) throws SwitchStateException {
766 if (h.sw == null) {
767 String msg = getSwitchStateMessage(h, m,
768 "State machine error: switch is null. Should never " +
769 "happen");
770 throw new SwitchStateException(msg);
771 }
772
773 h.sw.handleMessage(m);
774 }
775
776
777 /**
778 * Process an OF message received on the channel and
779 * update state accordingly.
780 *
781 * The main "event" of the state machine. Process the received message,
782 * send follow up message if required and update state if required.
783 *
784 * Switches on the message type and calls more specific event handlers
785 * for each individual OF message type. If we receive a message that
786 * is supposed to be sent from a controller to a switch we throw
787 * a SwitchStateExeption.
788 *
789 * The more specific handlers can also throw SwitchStateExceptions
790 *
791 * @param h The OFChannelHandler that received the message
792 * @param m The message we received.
793 * @throws SwitchStateException
794 * @throws IOException
795 */
796 void processOFMessage(OFChannelHandler h, OFMessage m)
797 throws IOException, SwitchStateException {
798 switch(m.getType()) {
799 case HELLO:
800 processOFHello(h, (OFHello) m);
801 break;
802 case BARRIER_REPLY:
803 processOFBarrierReply(h, (OFBarrierReply) m);
804 break;
805 case ECHO_REPLY:
806 processOFEchoReply(h, (OFEchoReply) m);
807 break;
808 case ECHO_REQUEST:
809 processOFEchoRequest(h, (OFEchoRequest) m);
810 break;
811 case ERROR:
812 processOFError(h, (OFErrorMsg) m);
813 break;
814 case FEATURES_REPLY:
815 processOFFeaturesReply(h, (OFFeaturesReply) m);
816 break;
817 case FLOW_REMOVED:
818 processOFFlowRemoved(h, (OFFlowRemoved) m);
819 break;
820 case GET_CONFIG_REPLY:
821 processOFGetConfigReply(h, (OFGetConfigReply) m);
822 break;
823 case PACKET_IN:
824 processOFPacketIn(h, (OFPacketIn) m);
825 break;
826 case PORT_STATUS:
827 processOFPortStatus(h, (OFPortStatus) m);
828 break;
829 case QUEUE_GET_CONFIG_REPLY:
830 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
831 break;
832 case STATS_REPLY: // multipart_reply in 1.3
833 processOFStatisticsReply(h, (OFStatsReply) m);
834 break;
835 case EXPERIMENTER:
836 processOFExperimenter(h, (OFExperimenter) m);
837 break;
838 case ROLE_REPLY:
839 processOFRoleReply(h, (OFRoleReply) m);
840 break;
841 case GET_ASYNC_REPLY:
842 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
843 break;
844
845 // The following messages are sent to switches. The controller
846 // should never receive them
847 case SET_CONFIG:
848 case GET_CONFIG_REQUEST:
849 case PACKET_OUT:
850 case PORT_MOD:
851 case QUEUE_GET_CONFIG_REQUEST:
852 case BARRIER_REQUEST:
853 case STATS_REQUEST: // multipart request in 1.3
854 case FEATURES_REQUEST:
855 case FLOW_MOD:
856 case GROUP_MOD:
857 case TABLE_MOD:
858 case GET_ASYNC_REQUEST:
859 case SET_ASYNC:
860 case METER_MOD:
861 default:
862 illegalMessageReceived(h, m);
863 break;
864 }
865 }
866
867 /*-----------------------------------------------------------------
868 * Default implementation for message handlers in any state.
869 *
870 * Individual states must override these if they want a behavior
871 * that differs from the default.
872 *
873 * In general, these handlers simply ignore the message and do
874 * nothing.
875 *
876 * There are some exceptions though, since some messages really
877 * are handled the same way in every state (e.g., ECHO_REQUST) or
878 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
879 -----------------------------------------------------------------*/
880
881 void processOFHello(OFChannelHandler h, OFHello m)
882 throws IOException, SwitchStateException {
883 // we only expect hello in the WAIT_HELLO state
884 illegalMessageReceived(h, m);
885 }
886
887 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
888 throws IOException {
889 // Silently ignore.
890 }
891
892 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
893 throws IOException {
894 if (h.ofVersion == null) {
895 log.error("No OF version set for {}. Not sending Echo REPLY",
896 h.channel.getRemoteAddress());
897 return;
898 }
899 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
900 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
901 OFEchoReply reply = factory
902 .buildEchoReply()
903 .setXid(m.getXid())
904 .setData(m.getData())
905 .build();
906 h.channel.write(Collections.singletonList(reply));
907 }
908
909 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
910 throws IOException {
911 // Do nothing with EchoReplies !!
912 }
913
914 // no default implementation for OFError
915 // every state must override it
916 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
917 throws IOException, SwitchStateException;
918
919
920 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
921 throws IOException, SwitchStateException {
922 unhandledMessageReceived(h, m);
923 }
924
925 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
926 throws IOException {
927 unhandledMessageReceived(h, m);
928 }
929
930 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
931 throws IOException, SwitchStateException {
932 // we only expect config replies in the WAIT_CONFIG_REPLY state
933 illegalMessageReceived(h, m);
934 }
935
936 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
937 throws IOException {
938 unhandledMessageReceived(h, m);
939 }
940
941 // no default implementation. Every state needs to handle it.
942 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
943 throws IOException, SwitchStateException;
944
945 void processOFQueueGetConfigReply(OFChannelHandler h,
946 OFQueueGetConfigReply m)
947 throws IOException {
948 unhandledMessageReceived(h, m);
949 }
950
951 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
952 throws IOException, SwitchStateException {
953 unhandledMessageReceived(h, m);
954 }
955
956 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
957 throws IOException, SwitchStateException {
958 // TODO: it might make sense to parse the vendor message here
959 // into the known vendor messages we support and then call more
960 // specific event handlers
961 unhandledMessageReceived(h, m);
962 }
963
964 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
965 throws SwitchStateException, IOException {
966 unhandledMessageReceived(h, m);
967 }
968
969 void processOFGetAsyncReply(OFChannelHandler h,
970 OFAsyncGetReply m) {
971 unhandledMessageReceived(h, m);
972 }
973
974 }
975
976
977
978 //*************************
979 // Channel handler methods
980 //*************************
981
982 @Override
983 public void channelConnected(ChannelHandlerContext ctx,
984 ChannelStateEvent e) throws Exception {
985 channel = e.getChannel();
986 log.info("New switch connection from {}",
987 channel.getRemoteAddress());
988 sendHandshakeHelloMessage();
989 setState(ChannelState.WAIT_HELLO);
990 }
991
992 @Override
993 public void channelDisconnected(ChannelHandlerContext ctx,
994 ChannelStateEvent e) throws Exception {
995 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
996 getSwitchInfoString());
997 if (thisdpid != 0) {
998 if (!duplicateDpidFound) {
999 // if the disconnected switch (on this ChannelHandler)
1000 // was not one with a duplicate-dpid, it is safe to remove all
1001 // state for it at the controller. Notice that if the disconnected
1002 // switch was a duplicate-dpid, calling the method below would clear
1003 // all state for the original switch (with the same dpid),
1004 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001005 log.info("{}:removal called", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001006 sw.removeConnectedSwitch();
1007 } else {
1008 // A duplicate was disconnected on this ChannelHandler,
1009 // this is the same switch reconnecting, but the original state was
1010 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001011 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001012 duplicateDpidFound = Boolean.FALSE;
1013 }
1014 } else {
1015 log.warn("no dpid in channelHandler registered for "
1016 + "disconnected switch {}", getSwitchInfoString());
1017 }
1018 }
1019
1020 @Override
1021 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1022 throws Exception {
1023 if (e.getCause() instanceof ReadTimeoutException) {
1024 // switch timeout
1025 log.error("Disconnecting switch {} due to read timeout",
1026 getSwitchInfoString());
1027 ctx.getChannel().close();
1028 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1029 log.error("Disconnecting switch {}: failed to complete handshake",
1030 getSwitchInfoString());
1031 ctx.getChannel().close();
1032 } else if (e.getCause() instanceof ClosedChannelException) {
1033 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1034 } else if (e.getCause() instanceof IOException) {
1035 log.error("Disconnecting switch {} due to IO Error: {}",
1036 getSwitchInfoString(), e.getCause().getMessage());
1037 if (log.isDebugEnabled()) {
1038 // still print stack trace if debug is enabled
1039 log.debug("StackTrace for previous Exception: ", e.getCause());
1040 }
1041 ctx.getChannel().close();
1042 } else if (e.getCause() instanceof SwitchStateException) {
1043 log.error("Disconnecting switch {} due to switch state error: {}",
1044 getSwitchInfoString(), e.getCause().getMessage());
1045 if (log.isDebugEnabled()) {
1046 // still print stack trace if debug is enabled
1047 log.debug("StackTrace for previous Exception: ", e.getCause());
1048 }
1049 ctx.getChannel().close();
1050 } else if (e.getCause() instanceof OFParseError) {
1051 log.error("Disconnecting switch "
1052 + getSwitchInfoString() +
1053 " due to message parse failure",
1054 e.getCause());
1055 ctx.getChannel().close();
1056 } else if (e.getCause() instanceof RejectedExecutionException) {
1057 log.warn("Could not process message: queue full");
1058 } else {
1059 log.error("Error while processing message from switch "
1060 + getSwitchInfoString()
1061 + "state " + this.state, e.getCause());
1062 ctx.getChannel().close();
1063 }
1064 }
1065
1066 @Override
1067 public String toString() {
1068 return getSwitchInfoString();
1069 }
1070
1071 @Override
1072 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1073 throws Exception {
1074 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1075 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001076 log.debug("Sending Echo Request on idle channel: {}",
tom7ef8ff92014-09-17 13:08:06 -07001077 e.getChannel().getPipeline().getLast().toString());
1078 e.getChannel().write(Collections.singletonList(m));
1079 // XXX S some problems here -- echo request has no transaction id, and
1080 // echo reply is not correlated to the echo request.
1081 }
1082
1083 @Override
1084 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1085 throws Exception {
1086 if (e.getMessage() instanceof List) {
1087 @SuppressWarnings("unchecked")
1088 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1089
1090
1091 for (OFMessage ofm : msglist) {
1092 // Do the actual packet processing
1093 state.processOFMessage(this, ofm);
1094 }
1095 } else {
1096 state.processOFMessage(this, (OFMessage) e.getMessage());
1097 }
1098 }
1099
1100
1101
1102 //*************************
1103 // Channel utility methods
1104 //*************************
1105
1106 /**
1107 * Is this a state in which the handshake has completed?
1108 * @return true if the handshake is complete
1109 */
1110 public boolean isHandshakeComplete() {
1111 return this.state.isHandshakeComplete();
1112 }
1113
1114 private void dispatchMessage(OFMessage m) {
1115 sw.handleMessage(m);
1116 }
1117
1118 /**
1119 * Return a string describing this switch based on the already available
1120 * information (DPID and/or remote socket).
1121 * @return display string
1122 */
1123 private String getSwitchInfoString() {
1124 if (sw != null) {
1125 return sw.toString();
1126 }
1127 String channelString;
1128 if (channel == null || channel.getRemoteAddress() == null) {
1129 channelString = "?";
1130 } else {
1131 channelString = channel.getRemoteAddress().toString();
1132 }
1133 String dpidString;
1134 if (featuresReply == null) {
1135 dpidString = "?";
1136 } else {
1137 dpidString = featuresReply.getDatapathId().toString();
1138 }
1139 return String.format("[%s DPID[%s]]", channelString, dpidString);
1140 }
1141
1142 /**
1143 * Update the channels state. Only called from the state machine.
1144 * TODO: enforce restricted state transitions
1145 * @param state
1146 */
1147 private void setState(ChannelState state) {
1148 this.state = state;
1149 }
1150
1151 /**
1152 * Send hello message to the switch using the handshake transactions ids.
1153 * @throws IOException
1154 */
1155 private void sendHandshakeHelloMessage() throws IOException {
1156 // The OF protocol requires us to start things off by sending the highest
1157 // version of the protocol supported.
1158
1159 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1160 // see Sec. 7.5.1 of the OF1.3.4 spec
1161 U32 bitmap = U32.ofRaw(0x00000012);
1162 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1163 .setBitmaps(Collections.singletonList(bitmap))
1164 .build();
1165 OFMessage.Builder mb = factory13.buildHello()
1166 .setXid(this.handshakeTransactionIds--)
1167 .setElements(Collections.singletonList(hem));
1168 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1169 channel.write(Collections.singletonList(mb.build()));
1170 }
1171
1172 /**
1173 * Send featuresRequest msg to the switch using the handshake transactions ids.
1174 * @throws IOException
1175 */
1176 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1177 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1178 OFMessage m = factory.buildFeaturesRequest()
1179 .setXid(this.handshakeTransactionIds--)
1180 .build();
1181 channel.write(Collections.singletonList(m));
1182 }
1183
1184 /**
1185 * Send the configuration requests to tell the switch we want full
1186 * packets.
1187 * @throws IOException
1188 */
1189 private void sendHandshakeSetConfig() throws IOException {
1190 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1191 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1192 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1193
1194 // Ensure we receive the full packet via PacketIn
1195 // FIXME: We don't set the reassembly flags.
1196 OFSetConfig sc = factory
1197 .buildSetConfig()
1198 .setMissSendLen((short) 0xffff)
1199 .setXid(this.handshakeTransactionIds--)
1200 .build();
1201 msglist.add(sc);
1202
1203 // Barrier
1204 OFBarrierRequest br = factory
1205 .buildBarrierRequest()
1206 .setXid(this.handshakeTransactionIds--)
1207 .build();
1208 msglist.add(br);
1209
1210 // Verify (need barrier?)
1211 OFGetConfigRequest gcr = factory
1212 .buildGetConfigRequest()
1213 .setXid(this.handshakeTransactionIds--)
1214 .build();
1215 msglist.add(gcr);
1216 channel.write(msglist);
1217 }
1218
1219 /**
1220 * send a description state request.
1221 * @throws IOException
1222 */
1223 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1224 // Get Description to set switch-specific flags
1225 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1226 OFDescStatsRequest dreq = factory
1227 .buildDescStatsRequest()
1228 .setXid(handshakeTransactionIds--)
1229 .build();
1230 channel.write(Collections.singletonList(dreq));
1231 }
1232
1233 private void sendHandshakeOFPortDescRequest() throws IOException {
1234 // Get port description for 1.3 switch
1235 OFPortDescStatsRequest preq = factory13
1236 .buildPortDescStatsRequest()
1237 .setXid(handshakeTransactionIds--)
1238 .build();
1239 channel.write(Collections.singletonList(preq));
1240 }
1241
1242 ChannelState getStateForTesting() {
1243 return state;
1244 }
1245
1246}