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