blob: 8af45e140e3f61034ba545467e516913cf21962e [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);
alshabibf1216ed2014-09-03 11:53:54 -0700418 h.sw.setOFVersion(h.ofVersion);
419 h.sw.setFeaturesReply(h.featuresReply);
420 h.sw.setPortDescReply(h.portDescReply);
421 h.sw.setConnected(true);
422 h.sw.setChannel(h.channel);
alshabib6171f182014-09-02 19:00:32 -0700423 boolean success = h.sw.connectSwitch();
alshabibd777b202014-08-28 17:52:55 -0700424 if (!success) {
425 disconnectDuplicate(h);
426 return;
427 }
alshabib1f44e8e2014-08-14 15:19:57 -0700428 // set switch information
alshabibf1216ed2014-09-03 11:53:54 -0700429
alshabib1f44e8e2014-08-14 15:19:57 -0700430
alshabib1f44e8e2014-08-14 15:19:57 -0700431
432 log.info("Switch {} bound to class {}, description {}",
433 new Object[] {h.sw, h.sw.getClass(), drep });
434 //Put switch in EQUAL mode until we hear back from the global registry
435 log.debug("Setting new switch {} to EQUAL and sending Role request",
436 h.sw.getStringId());
alshabib6171f182014-09-02 19:00:32 -0700437 h.sw.activateEqualSwitch();
alshabib6f5460b2014-09-03 14:46:17 -0700438 h.setSwitchRole(RoleState.EQUAL);
439
alshabib54ebd9c2014-08-27 18:38:41 -0700440 h.sw.startDriverHandshake();
441 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
442
alshabib1f44e8e2014-08-14 15:19:57 -0700443 }
444
445 @Override
446 void processOFError(OFChannelHandler h, OFErrorMsg m) {
447 logErrorDisconnect(h, m);
448 }
449
450 @Override
451 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
452 throws IOException, SwitchStateException {
453 illegalMessageReceived(h, m);
454 }
455
456 @Override
457 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
458 throws IOException {
459 h.pendingPortStatusMsg.add(m);
460 }
461 },
462
alshabib1f44e8e2014-08-14 15:19:57 -0700463
464 /**
465 * We are waiting for the respective switch driver to complete its
466 * configuration. Notice that we do not consider this to be part of the main
467 * switch-controller handshake. But we do consider it as a step that comes
468 * before we declare the switch as available to the controller.
469 * Next State: depends on the role of this controller for this switch - either
470 * MASTER or EQUAL.
471 */
472 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
473
474 @Override
475 void processOFError(OFChannelHandler h, OFErrorMsg m)
476 throws IOException {
477 // will never be called. We override processOFMessage
478 }
479
480 @Override
481 void processOFMessage(OFChannelHandler h, OFMessage m)
alshabib54ebd9c2014-08-27 18:38:41 -0700482 throws IOException, SwitchStateException {
alshabib1f44e8e2014-08-14 15:19:57 -0700483 if (m.getType() == OFType.ECHO_REQUEST) {
484 processOFEchoRequest(h, (OFEchoRequest) m);
alshabib54ebd9c2014-08-27 18:38:41 -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()) {
491 h.setState(ACTIVE);
492 }
493 }
alshabib1f44e8e2014-08-14 15:19:57 -0700494 } else {
alshabib6171f182014-09-02 19:00:32 -0700495 if (m.getType() == OFType.EXPERIMENTER &&
496 ((OFExperimenter) m).getExperimenter() ==
alshabib54ebd9c2014-08-27 18:38:41 -0700497 RoleManager.NICIRA_EXPERIMENTER) {
498 h.sw.handleNiciraRole(m);
499 } else {
500 h.sw.processDriverHandshakeMessage(m);
501 if (h.sw.isDriverHandshakeComplete()) {
502 h.setState(ACTIVE);
alshabib1f44e8e2014-08-14 15:19:57 -0700503 }
504 }
505 }
506 }
507
508 @Override
509 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
510 throws IOException, SwitchStateException {
511 h.pendingPortStatusMsg.add(m);
512 }
513 },
514
515
516 /**
517 * This controller is in MASTER role for this switch. We enter this state
518 * after requesting and winning control from the global registry.
519 * The main handshake as well as the switch-driver sub-handshake
520 * is complete at this point.
521 * // XXX S reconsider below
522 * In the (near) future we may deterministically assign controllers to
523 * switches at startup.
524 * We only leave this state if the switch disconnects or
525 * if we send a role request for SLAVE /and/ receive the role reply for
526 * SLAVE.
527 */
alshabib54ebd9c2014-08-27 18:38:41 -0700528 ACTIVE(true) {
alshabib1f44e8e2014-08-14 15:19:57 -0700529 @LogMessageDoc(level = "WARN",
530 message = "Received permission error from switch {} while"
531 + "being master. Reasserting master role.",
532 explanation = "The switch has denied an operation likely "
533 + "indicating inconsistent controller roles",
534 recommendation = "This situation can occurs transiently during role"
535 + " changes. If, however, the condition persists or happens"
536 + " frequently this indicates a role inconsistency. "
537 + LogMessageDoc.CHECK_CONTROLLER)
538 @Override
539 void processOFError(OFChannelHandler h, OFErrorMsg m)
540 throws IOException, SwitchStateException {
alshabib1f44e8e2014-08-14 15:19:57 -0700541 // 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
alshabib1f44e8e2014-08-14 15:19:57 -0700553 log.warn("Received permission error from switch {} while" +
554 "being master. Reasserting master role.",
555 h.getSwitchInfoString());
alshabib54ebd9c2014-08-27 18:38:41 -0700556 h.sw.reassertRole();
alshabib1f44e8e2014-08-14 15:19:57 -0700557 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
558 ((OFFlowModFailedErrorMsg) m).getCode() ==
559 OFFlowModFailedCode.ALL_TABLES_FULL) {
560 h.sw.setTableFull(true);
561 } else {
562 logError(h, m);
563 }
564 h.dispatchMessage(m);
565 }
566
567 @Override
568 void processOFStatisticsReply(OFChannelHandler h,
569 OFStatsReply m) {
alshabib54ebd9c2014-08-27 18:38:41 -0700570 h.sw.handleMessage(m);
alshabib1f44e8e2014-08-14 15:19:57 -0700571 }
572
573 @Override
574 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
575 throws IOException, SwitchStateException {
alshabib54ebd9c2014-08-27 18:38:41 -0700576 h.sw.handleNiciraRole(m);
alshabib1f44e8e2014-08-14 15:19:57 -0700577 }
578
579 @Override
580 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
581 throws SwitchStateException, IOException {
alshabib54ebd9c2014-08-27 18:38:41 -0700582 h.sw.handleRole(m);
alshabib1f44e8e2014-08-14 15:19:57 -0700583 }
584
585 @Override
586 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
587 throws IOException, SwitchStateException {
588 handlePortStatusMessage(h, m, true);
589 h.dispatchMessage(m);
590 }
591
592 @Override
593 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
594 throws IOException {
595 h.dispatchMessage(m);
596 }
597
598 @Override
599 void processOFFlowRemoved(OFChannelHandler h,
600 OFFlowRemoved m) throws IOException {
601 h.dispatchMessage(m);
602 }
603
604 @Override
605 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
606 throws IOException {
607 h.dispatchMessage(m);
608 }
609
alshabib1f44e8e2014-08-14 15:19:57 -0700610 };
611
612 private final boolean handshakeComplete;
613 ChannelState(boolean handshakeComplete) {
614 this.handshakeComplete = handshakeComplete;
615 }
616
617 /**
618 * Is this a state in which the handshake has completed?
619 * @return true if the handshake is complete
620 */
621 public boolean isHandshakeComplete() {
622 return handshakeComplete;
623 }
624
625 /**
626 * Get a string specifying the switch connection, state, and
627 * message received. To be used as message for SwitchStateException
628 * or log messages
629 * @param h The channel handler (to get switch information_
630 * @param m The OFMessage that has just been received
631 * @param details A string giving more details about the exact nature
632 * of the problem.
633 * @return
634 */
635 // needs to be protected because enum members are actually subclasses
636 protected String getSwitchStateMessage(OFChannelHandler h,
637 OFMessage m,
638 String details) {
639 return String.format("Switch: [%s], State: [%s], received: [%s]"
640 + ", details: %s",
641 h.getSwitchInfoString(),
642 this.toString(),
643 m.getType().toString(),
644 details);
645 }
646
647 /**
648 * We have an OFMessage we didn't expect given the current state and
649 * we want to treat this as an error.
650 * We currently throw an exception that will terminate the connection
651 * However, we could be more forgiving
652 * @param h the channel handler that received the message
653 * @param m the message
654 * @throws SwitchStateException
655 * @throws SwitchStateExeption we always through the execption
656 */
657 // needs to be protected because enum members are acutally subclasses
658 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
659 throws SwitchStateException {
660 String msg = getSwitchStateMessage(h, m,
661 "Switch should never send this message in the current state");
662 throw new SwitchStateException(msg);
663
664 }
665
666 /**
667 * We have an OFMessage we didn't expect given the current state and
668 * we want to ignore the message.
669 * @param h the channel handler the received the message
670 * @param m the message
671 */
672 protected void unhandledMessageReceived(OFChannelHandler h,
673 OFMessage m) {
alshabib1f44e8e2014-08-14 15:19:57 -0700674 if (log.isDebugEnabled()) {
675 String msg = getSwitchStateMessage(h, m,
676 "Ignoring unexpected message");
677 log.debug(msg);
678 }
679 }
680
681 /**
682 * Log an OpenFlow error message from a switch.
683 * @param sw The switch that sent the error
684 * @param error The error message
685 */
686 @LogMessageDoc(level = "ERROR",
687 message = "Error {error type} {error code} from {switch} "
688 + "in state {state}",
689 explanation = "The switch responded with an unexpected error"
690 + "to an OpenFlow message from the controller",
691 recommendation = "This could indicate improper network operation. "
692 + "If the problem persists restarting the switch and "
693 + "controller may help."
694 )
695 protected void logError(OFChannelHandler h, OFErrorMsg error) {
696 log.error("{} from switch {} in state {}",
697 new Object[] {
698 error,
699 h.getSwitchInfoString(),
700 this.toString()});
701 }
702
703 /**
704 * Log an OpenFlow error message from a switch and disconnect the
705 * channel.
706 *
707 * @param h the IO channel for this switch.
708 * @param error The error message
709 */
710 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
711 logError(h, error);
712 h.channel.disconnect();
713 }
714
715 /**
716 * log an error message for a duplicate dpid and disconnect this channel.
717 * @param h the IO channel for this switch.
718 */
719 protected void disconnectDuplicate(OFChannelHandler h) {
720 log.error("Duplicated dpid or incompleted cleanup - "
721 + "disconnecting channel {}", h.getSwitchInfoString());
722 h.duplicateDpidFound = Boolean.TRUE;
723 h.channel.disconnect();
724 }
725
alshabib1f44e8e2014-08-14 15:19:57 -0700726
alshabib1f44e8e2014-08-14 15:19:57 -0700727
728 /**
729 * Handles all pending port status messages before a switch is declared
730 * activated in MASTER or EQUAL role. Note that since this handling
731 * precedes the activation (and therefore notification to IOFSwitchListerners)
732 * the changes to ports will already be visible once the switch is
733 * activated. As a result, no notifications are sent out for these
734 * pending portStatus messages.
735 * @param h
736 * @throws SwitchStateException
737 */
738 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
739 try {
740 handlePendingPortStatusMessages(h, 0);
741 } catch (SwitchStateException e) {
742 log.error(e.getMessage());
743 }
744 }
745
746 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
747 throws SwitchStateException {
748 if (h.sw == null) {
749 String msg = "State machine error: switch is null. Should never " +
750 "happen";
751 throw new SwitchStateException(msg);
752 }
753 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
754 for (OFPortStatus ps: h.pendingPortStatusMsg) {
755 temp.add(ps);
756 handlePortStatusMessage(h, ps, false);
757 }
758 temp.clear();
759 // expensive but ok - we don't expect too many port-status messages
760 // note that we cannot use clear(), because of the reasons below
761 h.pendingPortStatusMsg.removeAll(temp);
762 // the iterator above takes a snapshot of the list - so while we were
763 // dealing with the pending port-status messages, we could have received
764 // newer ones. Handle them recursively, but break the recursion after
765 // five steps to avoid an attack.
766 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
767 handlePendingPortStatusMessages(h, index);
768 }
769 }
770
771 /**
772 * Handle a port status message.
773 *
774 * Handle a port status message by updating the port maps in the
775 * IOFSwitch instance and notifying Controller about the change so
776 * it can dispatch a switch update.
777 *
778 * @param h The OFChannelHhandler that received the message
779 * @param m The PortStatus message we received
780 * @param doNotify if true switch port changed events will be
781 * dispatched
782 * @throws SwitchStateException
783 *
784 */
785 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
786 boolean doNotify) throws SwitchStateException {
787 if (h.sw == null) {
788 String msg = getSwitchStateMessage(h, m,
789 "State machine error: switch is null. Should never " +
790 "happen");
791 throw new SwitchStateException(msg);
792 }
793
alshabib54ebd9c2014-08-27 18:38:41 -0700794 h.sw.handleMessage(m);
alshabib1f44e8e2014-08-14 15:19:57 -0700795 }
796
alshabib1f44e8e2014-08-14 15:19:57 -0700797
798 /**
799 * Process an OF message received on the channel and
800 * update state accordingly.
801 *
802 * The main "event" of the state machine. Process the received message,
803 * send follow up message if required and update state if required.
804 *
805 * Switches on the message type and calls more specific event handlers
806 * for each individual OF message type. If we receive a message that
807 * is supposed to be sent from a controller to a switch we throw
808 * a SwitchStateExeption.
809 *
810 * The more specific handlers can also throw SwitchStateExceptions
811 *
812 * @param h The OFChannelHandler that received the message
813 * @param m The message we received.
814 * @throws SwitchStateException
815 * @throws IOException
816 */
817 void processOFMessage(OFChannelHandler h, OFMessage m)
818 throws IOException, SwitchStateException {
alshabib1f44e8e2014-08-14 15:19:57 -0700819 switch(m.getType()) {
820 case HELLO:
821 processOFHello(h, (OFHello) m);
822 break;
823 case BARRIER_REPLY:
824 processOFBarrierReply(h, (OFBarrierReply) m);
825 break;
826 case ECHO_REPLY:
827 processOFEchoReply(h, (OFEchoReply) m);
828 break;
829 case ECHO_REQUEST:
830 processOFEchoRequest(h, (OFEchoRequest) m);
831 break;
832 case ERROR:
833 processOFError(h, (OFErrorMsg) m);
834 break;
835 case FEATURES_REPLY:
836 processOFFeaturesReply(h, (OFFeaturesReply) m);
837 break;
838 case FLOW_REMOVED:
839 processOFFlowRemoved(h, (OFFlowRemoved) m);
840 break;
841 case GET_CONFIG_REPLY:
842 processOFGetConfigReply(h, (OFGetConfigReply) m);
843 break;
844 case PACKET_IN:
845 processOFPacketIn(h, (OFPacketIn) m);
846 break;
847 case PORT_STATUS:
848 processOFPortStatus(h, (OFPortStatus) m);
849 break;
850 case QUEUE_GET_CONFIG_REPLY:
851 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
852 break;
853 case STATS_REPLY: // multipart_reply in 1.3
alshabib6171f182014-09-02 19:00:32 -0700854 processOFStatisticsReply(h, (OFStatsReply) m);
855 break;
alshabib1f44e8e2014-08-14 15:19:57 -0700856 case EXPERIMENTER:
857 processOFExperimenter(h, (OFExperimenter) m);
858 break;
859 case ROLE_REPLY:
860 processOFRoleReply(h, (OFRoleReply) m);
861 break;
862 case GET_ASYNC_REPLY:
863 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
864 break;
865
866 // The following messages are sent to switches. The controller
867 // should never receive them
868 case SET_CONFIG:
869 case GET_CONFIG_REQUEST:
870 case PACKET_OUT:
871 case PORT_MOD:
872 case QUEUE_GET_CONFIG_REQUEST:
873 case BARRIER_REQUEST:
874 case STATS_REQUEST: // multipart request in 1.3
875 case FEATURES_REQUEST:
876 case FLOW_MOD:
877 case GROUP_MOD:
878 case TABLE_MOD:
879 case GET_ASYNC_REQUEST:
880 case SET_ASYNC:
881 case METER_MOD:
882 default:
883 illegalMessageReceived(h, m);
884 break;
885 }
886 }
887
888 /*-----------------------------------------------------------------
889 * Default implementation for message handlers in any state.
890 *
891 * Individual states must override these if they want a behavior
892 * that differs from the default.
893 *
894 * In general, these handlers simply ignore the message and do
895 * nothing.
896 *
897 * There are some exceptions though, since some messages really
898 * are handled the same way in every state (e.g., ECHO_REQUST) or
899 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
900 -----------------------------------------------------------------*/
901
902 void processOFHello(OFChannelHandler h, OFHello m)
903 throws IOException, SwitchStateException {
904 // we only expect hello in the WAIT_HELLO state
905 illegalMessageReceived(h, m);
906 }
907
908 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
909 throws IOException {
910 // Silently ignore.
911 }
912
913 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
914 throws IOException {
915 if (h.ofVersion == null) {
916 log.error("No OF version set for {}. Not sending Echo REPLY",
917 h.channel.getRemoteAddress());
918 return;
919 }
920 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
921 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
alshabib6171f182014-09-02 19:00:32 -0700922 OFEchoReply reply = factory
923 .buildEchoReply()
924 .setXid(m.getXid())
925 .setData(m.getData())
926 .build();
927 h.channel.write(Collections.singletonList(reply));
alshabib1f44e8e2014-08-14 15:19:57 -0700928 }
929
930 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
931 throws IOException {
932 // Do nothing with EchoReplies !!
933 }
934
935 // no default implementation for OFError
936 // every state must override it
937 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
938 throws IOException, SwitchStateException;
939
940
941 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
942 throws IOException, SwitchStateException {
943 unhandledMessageReceived(h, m);
944 }
945
946 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
947 throws IOException {
948 unhandledMessageReceived(h, m);
949 }
950
951 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
952 throws IOException, SwitchStateException {
953 // we only expect config replies in the WAIT_CONFIG_REPLY state
954 illegalMessageReceived(h, m);
955 }
956
957 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
958 throws IOException {
959 unhandledMessageReceived(h, m);
960 }
961
962 // no default implementation. Every state needs to handle it.
963 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
964 throws IOException, SwitchStateException;
965
966 void processOFQueueGetConfigReply(OFChannelHandler h,
967 OFQueueGetConfigReply m)
968 throws IOException {
969 unhandledMessageReceived(h, m);
970 }
971
972 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
973 throws IOException, SwitchStateException {
974 unhandledMessageReceived(h, m);
975 }
976
977 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
978 throws IOException, SwitchStateException {
979 // TODO: it might make sense to parse the vendor message here
980 // into the known vendor messages we support and then call more
981 // specific event handlers
982 unhandledMessageReceived(h, m);
983 }
984
985 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
986 throws SwitchStateException, IOException {
987 unhandledMessageReceived(h, m);
988 }
989
990 void processOFGetAsyncReply(OFChannelHandler h,
991 OFAsyncGetReply m) {
992 unhandledMessageReceived(h, m);
993 }
994
alshabib1f44e8e2014-08-14 15:19:57 -0700995 }
996
997
998
999 //*************************
1000 // Channel handler methods
1001 //*************************
1002
1003 @Override
1004 @LogMessageDoc(message = "New switch connection from {ip address}",
1005 explanation = "A new switch has connected from the "
1006 + "specified IP address")
1007 public void channelConnected(ChannelHandlerContext ctx,
1008 ChannelStateEvent e) throws Exception {
alshabib1f44e8e2014-08-14 15:19:57 -07001009 channel = e.getChannel();
1010 log.info("New switch connection from {}",
1011 channel.getRemoteAddress());
1012 sendHandshakeHelloMessage();
1013 setState(ChannelState.WAIT_HELLO);
1014 }
1015
1016 @Override
1017 @LogMessageDoc(message = "Disconnected switch {switch information}",
1018 explanation = "The specified switch has disconnected.")
1019 public void channelDisconnected(ChannelHandlerContext ctx,
1020 ChannelStateEvent e) throws Exception {
1021 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1022 getSwitchInfoString());
1023 if (thisdpid != 0) {
1024 if (!duplicateDpidFound) {
1025 // if the disconnected switch (on this ChannelHandler)
1026 // was not one with a duplicate-dpid, it is safe to remove all
1027 // state for it at the controller. Notice that if the disconnected
1028 // switch was a duplicate-dpid, calling the method below would clear
1029 // all state for the original switch (with the same dpid),
1030 // which we obviously don't want.
alshabib54ebd9c2014-08-27 18:38:41 -07001031 sw.removeConnectedSwitch();
alshabib1f44e8e2014-08-14 15:19:57 -07001032 } else {
1033 // A duplicate was disconnected on this ChannelHandler,
1034 // this is the same switch reconnecting, but the original state was
1035 // not cleaned up - XXX check liveness of original ChannelHandler
1036 duplicateDpidFound = Boolean.FALSE;
1037 }
1038 } else {
1039 log.warn("no dpid in channelHandler registered for "
1040 + "disconnected switch {}", getSwitchInfoString());
1041 }
1042 }
1043
1044 @Override
1045 @LogMessageDocs({
1046 @LogMessageDoc(level = "ERROR",
1047 message = "Disconnecting switch {switch} due to read timeout",
1048 explanation = "The connected switch has failed to send any "
1049 + "messages or respond to echo requests",
alshabib6171f182014-09-02 19:00:32 -07001050 recommendation = LogMessageDoc.CHECK_SWITCH),
alshabib1f44e8e2014-08-14 15:19:57 -07001051 @LogMessageDoc(level = "ERROR",
alshabib6171f182014-09-02 19:00:32 -07001052 message = "Disconnecting switch {switch}: failed to "
1053 + "complete handshake",
1054 explanation = "The switch did not respond correctly "
1055 + "to handshake messages",
1056 recommendation = LogMessageDoc.CHECK_SWITCH),
1057 @LogMessageDoc(level = "ERROR",
1058 message = "Disconnecting switch {switch} due to IO Error: {}",
1059 explanation = "There was an error communicating with the switch",
1060 recommendation = LogMessageDoc.CHECK_SWITCH),
1061 @LogMessageDoc(level = "ERROR",
1062 message = "Disconnecting switch {switch} due to switch "
1063 + "state error: {error}",
1064 explanation = "The switch sent an unexpected message",
1065 recommendation = LogMessageDoc.CHECK_SWITCH),
1066 @LogMessageDoc(level = "ERROR",
1067 message = "Disconnecting switch {switch} due to "
1068 + "message parse failure",
1069 explanation = "Could not parse a message from the switch",
1070 recommendation = LogMessageDoc.CHECK_SWITCH),
1071 @LogMessageDoc(level = "ERROR",
1072 message = "Terminating controller due to storage exception",
1073 explanation = Controller.ERROR_DATABASE,
1074 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1075 @LogMessageDoc(level = "ERROR",
1076 message = "Could not process message: queue full",
1077 explanation = "OpenFlow messages are arriving faster than "
1078 + "the controller can process them.",
1079 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1080 @LogMessageDoc(level = "ERROR",
1081 message = "Error while processing message "
1082 + "from switch {switch} {cause}",
1083 explanation = "An error occurred processing the switch message",
1084 recommendation = LogMessageDoc.GENERIC_ACTION)
alshabib1f44e8e2014-08-14 15:19:57 -07001085 })
1086 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1087 throws Exception {
1088 if (e.getCause() instanceof ReadTimeoutException) {
1089 // switch timeout
1090 log.error("Disconnecting switch {} due to read timeout",
1091 getSwitchInfoString());
alshabib1f44e8e2014-08-14 15:19:57 -07001092 ctx.getChannel().close();
1093 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1094 log.error("Disconnecting switch {}: failed to complete handshake",
1095 getSwitchInfoString());
alshabib1f44e8e2014-08-14 15:19:57 -07001096 ctx.getChannel().close();
1097 } else if (e.getCause() instanceof ClosedChannelException) {
1098 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1099 } else if (e.getCause() instanceof IOException) {
1100 log.error("Disconnecting switch {} due to IO Error: {}",
1101 getSwitchInfoString(), e.getCause().getMessage());
1102 if (log.isDebugEnabled()) {
1103 // still print stack trace if debug is enabled
1104 log.debug("StackTrace for previous Exception: ", e.getCause());
1105 }
alshabib1f44e8e2014-08-14 15:19:57 -07001106 ctx.getChannel().close();
1107 } else if (e.getCause() instanceof SwitchStateException) {
1108 log.error("Disconnecting switch {} due to switch state error: {}",
1109 getSwitchInfoString(), e.getCause().getMessage());
1110 if (log.isDebugEnabled()) {
1111 // still print stack trace if debug is enabled
1112 log.debug("StackTrace for previous Exception: ", e.getCause());
1113 }
alshabib1f44e8e2014-08-14 15:19:57 -07001114 ctx.getChannel().close();
1115 } else if (e.getCause() instanceof OFParseError) {
1116 log.error("Disconnecting switch "
1117 + getSwitchInfoString() +
1118 " due to message parse failure",
1119 e.getCause());
alshabib1f44e8e2014-08-14 15:19:57 -07001120 ctx.getChannel().close();
1121 } else if (e.getCause() instanceof RejectedExecutionException) {
1122 log.warn("Could not process message: queue full");
alshabib1f44e8e2014-08-14 15:19:57 -07001123 } else {
1124 log.error("Error while processing message from switch "
1125 + getSwitchInfoString()
1126 + "state " + this.state, e.getCause());
alshabib1f44e8e2014-08-14 15:19:57 -07001127 ctx.getChannel().close();
1128 }
1129 }
1130
1131 @Override
1132 public String toString() {
1133 return getSwitchInfoString();
1134 }
1135
1136 @Override
1137 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1138 throws Exception {
1139 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1140 OFMessage m = factory.buildEchoRequest().build();
1141 log.info("Sending Echo Request on idle channel: {}",
1142 e.getChannel().getPipeline().getLast().toString());
1143 e.getChannel().write(Collections.singletonList(m));
1144 // XXX S some problems here -- echo request has no transaction id, and
1145 // echo reply is not correlated to the echo request.
1146 }
1147
1148 @Override
1149 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1150 throws Exception {
1151 if (e.getMessage() instanceof List) {
1152 @SuppressWarnings("unchecked")
1153 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1154
1155
1156 for (OFMessage ofm : msglist) {
alshabib1f44e8e2014-08-14 15:19:57 -07001157 // Do the actual packet processing
1158 state.processOFMessage(this, ofm);
1159 }
1160 } else {
alshabib1f44e8e2014-08-14 15:19:57 -07001161 state.processOFMessage(this, (OFMessage) e.getMessage());
1162 }
1163 }
1164
1165
1166
1167 //*************************
1168 // Channel utility methods
1169 //*************************
1170
1171 /**
1172 * Is this a state in which the handshake has completed?
1173 * @return true if the handshake is complete
1174 */
1175 public boolean isHandshakeComplete() {
1176 return this.state.isHandshakeComplete();
1177 }
1178
1179 private void dispatchMessage(OFMessage m) throws IOException {
1180 sw.handleMessage(m);
1181 }
1182
1183 /**
1184 * Return a string describing this switch based on the already available
1185 * information (DPID and/or remote socket).
1186 * @return
1187 */
1188 private String getSwitchInfoString() {
1189 if (sw != null) {
1190 return sw.toString();
1191 }
1192 String channelString;
1193 if (channel == null || channel.getRemoteAddress() == null) {
1194 channelString = "?";
1195 } else {
1196 channelString = channel.getRemoteAddress().toString();
1197 }
1198 String dpidString;
1199 if (featuresReply == null) {
1200 dpidString = "?";
1201 } else {
1202 dpidString = featuresReply.getDatapathId().toString();
1203 }
1204 return String.format("[%s DPID[%s]]", channelString, dpidString);
1205 }
1206
1207 /**
1208 * Update the channels state. Only called from the state machine.
1209 * TODO: enforce restricted state transitions
1210 * @param state
1211 */
1212 private void setState(ChannelState state) {
1213 this.state = state;
1214 }
1215
1216 /**
1217 * Send hello message to the switch using the handshake transactions ids.
1218 * @throws IOException
1219 */
1220 private void sendHandshakeHelloMessage() throws IOException {
1221 // The OF protocol requires us to start things off by sending the highest
1222 // version of the protocol supported.
1223
1224 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1225 // see Sec. 7.5.1 of the OF1.3.4 spec
1226 U32 bitmap = U32.ofRaw(0x00000012);
1227 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1228 .setBitmaps(Collections.singletonList(bitmap))
1229 .build();
1230 OFMessage.Builder mb = factory13.buildHello()
1231 .setXid(this.handshakeTransactionIds--)
1232 .setElements(Collections.singletonList(hem));
1233 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1234 channel.write(Collections.singletonList(mb.build()));
1235 }
1236
1237 /**
1238 * Send featuresRequest msg to the switch using the handshake transactions ids.
1239 * @throws IOException
1240 */
1241 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1242 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1243 OFMessage m = factory.buildFeaturesRequest()
1244 .setXid(this.handshakeTransactionIds--)
1245 .build();
1246 channel.write(Collections.singletonList(m));
1247 }
1248
alshabib54ebd9c2014-08-27 18:38:41 -07001249 private void setSwitchRole(RoleState role) {
alshabib1f44e8e2014-08-14 15:19:57 -07001250 sw.setRole(role);
1251 }
1252
1253 /**
1254 * Send the configuration requests to tell the switch we want full
1255 * packets.
1256 * @throws IOException
1257 */
1258 private void sendHandshakeSetConfig() throws IOException {
1259 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1260 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1261 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1262
1263 // Ensure we receive the full packet via PacketIn
1264 // FIXME: We don't set the reassembly flags.
1265 OFSetConfig sc = factory
1266 .buildSetConfig()
1267 .setMissSendLen((short) 0xffff)
1268 .setXid(this.handshakeTransactionIds--)
1269 .build();
1270 msglist.add(sc);
1271
1272 // Barrier
1273 OFBarrierRequest br = factory
1274 .buildBarrierRequest()
1275 .setXid(this.handshakeTransactionIds--)
1276 .build();
1277 msglist.add(br);
1278
1279 // Verify (need barrier?)
1280 OFGetConfigRequest gcr = factory
1281 .buildGetConfigRequest()
1282 .setXid(this.handshakeTransactionIds--)
1283 .build();
1284 msglist.add(gcr);
1285 channel.write(msglist);
1286 }
1287
1288 /**
1289 * send a description state request.
1290 * @throws IOException
1291 */
1292 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1293 // Get Description to set switch-specific flags
1294 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1295 OFDescStatsRequest dreq = factory
1296 .buildDescStatsRequest()
1297 .setXid(handshakeTransactionIds--)
1298 .build();
1299 channel.write(Collections.singletonList(dreq));
1300 }
1301
1302 private void sendHandshakeOFPortDescRequest() throws IOException {
1303 // Get port description for 1.3 switch
1304 OFPortDescStatsRequest preq = factory13
1305 .buildPortDescStatsRequest()
1306 .setXid(handshakeTransactionIds--)
1307 .build();
1308 channel.write(Collections.singletonList(preq));
1309 }
1310
1311 ChannelState getStateForTesting() {
1312 return state;
1313 }
1314
alshabib1f44e8e2014-08-14 15:19:57 -07001315}