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