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