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