blob: a9c19690cf698356810f519016cf3475dcd33748 [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.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800118 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700119 */
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 {
Thomas Vachuska39274462014-12-02 13:23:50 -0800268 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700269 }
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 {
Thomas Vachuska39274462014-12-02 13:23:50 -0800316 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700317
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 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800441 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700442 h.setState(ACTIVE);
443 } else {
444 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
445 }
tom7ef8ff92014-09-17 13:08:06 -0700446
447 }
448
449 @Override
450 void processOFError(OFChannelHandler h, OFErrorMsg m) {
451 logErrorDisconnect(h, m);
452 }
453
454 @Override
455 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
456 throws IOException, SwitchStateException {
457 illegalMessageReceived(h, m);
458 }
459
460 @Override
461 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
462 throws IOException {
463 h.pendingPortStatusMsg.add(m);
464 }
465 },
466
467
468 /**
469 * We are waiting for the respective switch driver to complete its
470 * configuration. Notice that we do not consider this to be part of the main
471 * switch-controller handshake. But we do consider it as a step that comes
472 * before we declare the switch as available to the controller.
473 * Next State: depends on the role of this controller for this switch - either
474 * MASTER or EQUAL.
475 */
476 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
477
478 @Override
479 void processOFError(OFChannelHandler h, OFErrorMsg m)
480 throws IOException {
481 // will never be called. We override processOFMessage
482 }
483
alshabibd7963912014-10-20 14:52:04 -0700484
485
tom7ef8ff92014-09-17 13:08:06 -0700486 @Override
487 void processOFMessage(OFChannelHandler h, OFMessage m)
488 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700489
490 if (h.sw.isDriverHandshakeComplete()) {
491 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700492 h.state.processOFMessage(h, m);
493 return;
alshabibd7963912014-10-20 14:52:04 -0700494
495 }
496
tom7ef8ff92014-09-17 13:08:06 -0700497 if (m.getType() == OFType.ECHO_REQUEST) {
498 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700499 } else if (m.getType() == OFType.ECHO_REPLY) {
500 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700501 } else if (m.getType() == OFType.ROLE_REPLY) {
502 h.sw.handleRole(m);
503 } else if (m.getType() == OFType.ERROR) {
504 if (!h.sw.handleRoleError((OFErrorMsg)m)) {
505 h.sw.processDriverHandshakeMessage(m);
506 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700507 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700508 }
509 }
510 } else {
511 if (m.getType() == OFType.EXPERIMENTER &&
512 ((OFExperimenter) m).getExperimenter() ==
513 RoleManager.NICIRA_EXPERIMENTER) {
514 h.sw.handleNiciraRole(m);
515 } else {
516 h.sw.processDriverHandshakeMessage(m);
517 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700518 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700519 }
520 }
521 }
522 }
523
524 @Override
525 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
526 throws IOException, SwitchStateException {
527 h.pendingPortStatusMsg.add(m);
528 }
alshabibd7963912014-10-20 14:52:04 -0700529
530 private void moveToActive(OFChannelHandler h) {
531 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800532 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700533 h.setState(ACTIVE);
534 if (!success) {
535 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700536 }
537 }
538
tom7ef8ff92014-09-17 13:08:06 -0700539 },
540
541
542 /**
543 * This controller is in MASTER role for this switch. We enter this state
544 * after requesting and winning control from the global registry.
545 * The main handshake as well as the switch-driver sub-handshake
546 * is complete at this point.
547 * // XXX S reconsider below
548 * In the (near) future we may deterministically assign controllers to
549 * switches at startup.
550 * We only leave this state if the switch disconnects or
551 * if we send a role request for SLAVE /and/ receive the role reply for
552 * SLAVE.
553 */
554 ACTIVE(true) {
555 @Override
556 void processOFError(OFChannelHandler h, OFErrorMsg m)
557 throws IOException, SwitchStateException {
558 // if we get here, then the error message is for something else
559 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
560 ((OFBadRequestErrorMsg) m).getCode() ==
561 OFBadRequestCode.EPERM) {
562 // We are the master controller and the switch returned
563 // a permission error. This is a likely indicator that
564 // the switch thinks we are slave. Reassert our
565 // role
566 // FIXME: this could be really bad during role transitions
567 // if two controllers are master (even if its only for
568 // a brief period). We might need to see if these errors
569 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700570
tom7ef8ff92014-09-17 13:08:06 -0700571 h.sw.reassertRole();
572 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
573 ((OFFlowModFailedErrorMsg) m).getCode() ==
574 OFFlowModFailedCode.ALL_TABLES_FULL) {
575 h.sw.setTableFull(true);
576 } else {
577 logError(h, m);
578 }
579 h.dispatchMessage(m);
580 }
581
582 @Override
583 void processOFStatisticsReply(OFChannelHandler h,
584 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700585 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
586 h.sw.setPortDescReply((OFPortDescStatsReply) m);
587 }
tom7ef8ff92014-09-17 13:08:06 -0700588 h.dispatchMessage(m);
589 }
590
591 @Override
592 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
593 throws SwitchStateException {
594 h.sw.handleNiciraRole(m);
595 }
596
597 @Override
598 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
599 throws SwitchStateException {
600 h.sw.handleRole(m);
601 }
602
603 @Override
604 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
605 throws SwitchStateException {
606 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800607 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700608 }
609
610 @Override
611 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700612// OFPacketOut out =
613// h.sw.factory().buildPacketOut()
614// .setXid(m.getXid())
615// .setBufferId(m.getBufferId()).build();
616// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700617 h.dispatchMessage(m);
618 }
619
620 @Override
621 void processOFFlowRemoved(OFChannelHandler h,
622 OFFlowRemoved m) {
623 h.dispatchMessage(m);
624 }
625
626 @Override
627 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
628 h.dispatchMessage(m);
629 }
630
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700631 @Override
632 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700633 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700634 h.dispatchMessage(m);
635 }
636
tom7ef8ff92014-09-17 13:08:06 -0700637 };
638
639 private final boolean handshakeComplete;
640 ChannelState(boolean handshakeComplete) {
641 this.handshakeComplete = handshakeComplete;
642 }
643
644 /**
645 * Is this a state in which the handshake has completed?
646 * @return true if the handshake is complete
647 */
648 public boolean isHandshakeComplete() {
649 return handshakeComplete;
650 }
651
652 /**
653 * Get a string specifying the switch connection, state, and
654 * message received. To be used as message for SwitchStateException
655 * or log messages
656 * @param h The channel handler (to get switch information_
657 * @param m The OFMessage that has just been received
658 * @param details A string giving more details about the exact nature
659 * of the problem.
660 * @return display string
661 */
662 // needs to be protected because enum members are actually subclasses
663 protected String getSwitchStateMessage(OFChannelHandler h,
664 OFMessage m,
665 String details) {
666 return String.format("Switch: [%s], State: [%s], received: [%s]"
667 + ", details: %s",
668 h.getSwitchInfoString(),
669 this.toString(),
670 m.getType().toString(),
671 details);
672 }
673
674 /**
675 * We have an OFMessage we didn't expect given the current state and
676 * we want to treat this as an error.
677 * We currently throw an exception that will terminate the connection
678 * However, we could be more forgiving
679 * @param h the channel handler that received the message
680 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700681 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700682 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700683 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700684 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
685 throws SwitchStateException {
686 String msg = getSwitchStateMessage(h, m,
687 "Switch should never send this message in the current state");
688 throw new SwitchStateException(msg);
689
690 }
691
692 /**
693 * We have an OFMessage we didn't expect given the current state and
694 * we want to ignore the message.
695 * @param h the channel handler the received the message
696 * @param m the message
697 */
698 protected void unhandledMessageReceived(OFChannelHandler h,
699 OFMessage m) {
700 if (log.isDebugEnabled()) {
701 String msg = getSwitchStateMessage(h, m,
702 "Ignoring unexpected message");
703 log.debug(msg);
704 }
705 }
706
707 /**
708 * Log an OpenFlow error message from a switch.
709 * @param h The switch that sent the error
710 * @param error The error message
711 */
712 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700713 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700714 new Object[] {
715 error,
716 h.getSwitchInfoString(),
717 this.toString()});
718 }
719
720 /**
721 * Log an OpenFlow error message from a switch and disconnect the
722 * channel.
723 *
724 * @param h the IO channel for this switch.
725 * @param error The error message
726 */
727 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
728 logError(h, error);
729 h.channel.disconnect();
730 }
731
732 /**
733 * log an error message for a duplicate dpid and disconnect this channel.
734 * @param h the IO channel for this switch.
735 */
736 protected void disconnectDuplicate(OFChannelHandler h) {
737 log.error("Duplicated dpid or incompleted cleanup - "
738 + "disconnecting channel {}", h.getSwitchInfoString());
739 h.duplicateDpidFound = Boolean.TRUE;
740 h.channel.disconnect();
741 }
742
743
744
745 /**
746 * Handles all pending port status messages before a switch is declared
747 * activated in MASTER or EQUAL role. Note that since this handling
748 * precedes the activation (and therefore notification to IOFSwitchListerners)
749 * the changes to ports will already be visible once the switch is
750 * activated. As a result, no notifications are sent out for these
751 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700752 *
753 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700754 */
755 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
756 try {
757 handlePendingPortStatusMessages(h, 0);
758 } catch (SwitchStateException e) {
759 log.error(e.getMessage());
760 }
761 }
762
763 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
764 throws SwitchStateException {
765 if (h.sw == null) {
766 String msg = "State machine error: switch is null. Should never " +
767 "happen";
768 throw new SwitchStateException(msg);
769 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800770 log.info("Processing {} pending port status messages for {}",
771 h.pendingPortStatusMsg.size(), h.sw.getStringId());
772
tom7ef8ff92014-09-17 13:08:06 -0700773 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
774 for (OFPortStatus ps: h.pendingPortStatusMsg) {
775 temp.add(ps);
776 handlePortStatusMessage(h, ps, false);
777 }
tom7ef8ff92014-09-17 13:08:06 -0700778 // expensive but ok - we don't expect too many port-status messages
779 // note that we cannot use clear(), because of the reasons below
780 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800781 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700782 // the iterator above takes a snapshot of the list - so while we were
783 // dealing with the pending port-status messages, we could have received
784 // newer ones. Handle them recursively, but break the recursion after
785 // five steps to avoid an attack.
786 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
787 handlePendingPortStatusMessages(h, index);
788 }
789 }
790
791 /**
792 * Handle a port status message.
793 *
794 * Handle a port status message by updating the port maps in the
795 * IOFSwitch instance and notifying Controller about the change so
796 * it can dispatch a switch update.
797 *
798 * @param h The OFChannelHhandler that received the message
799 * @param m The PortStatus message we received
800 * @param doNotify if true switch port changed events will be
801 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800802 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700803 *
804 */
805 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
806 boolean doNotify) throws SwitchStateException {
807 if (h.sw == null) {
808 String msg = getSwitchStateMessage(h, m,
809 "State machine error: switch is null. Should never " +
810 "happen");
811 throw new SwitchStateException(msg);
812 }
813
814 h.sw.handleMessage(m);
815 }
816
817
818 /**
819 * Process an OF message received on the channel and
820 * update state accordingly.
821 *
822 * The main "event" of the state machine. Process the received message,
823 * send follow up message if required and update state if required.
824 *
825 * Switches on the message type and calls more specific event handlers
826 * for each individual OF message type. If we receive a message that
827 * is supposed to be sent from a controller to a switch we throw
828 * a SwitchStateExeption.
829 *
830 * The more specific handlers can also throw SwitchStateExceptions
831 *
832 * @param h The OFChannelHandler that received the message
833 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800834 * @throws SwitchStateException if the switch is not bound to the channel
835 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700836 */
837 void processOFMessage(OFChannelHandler h, OFMessage m)
838 throws IOException, SwitchStateException {
839 switch(m.getType()) {
840 case HELLO:
841 processOFHello(h, (OFHello) m);
842 break;
843 case BARRIER_REPLY:
844 processOFBarrierReply(h, (OFBarrierReply) m);
845 break;
846 case ECHO_REPLY:
847 processOFEchoReply(h, (OFEchoReply) m);
848 break;
849 case ECHO_REQUEST:
850 processOFEchoRequest(h, (OFEchoRequest) m);
851 break;
852 case ERROR:
853 processOFError(h, (OFErrorMsg) m);
854 break;
855 case FEATURES_REPLY:
856 processOFFeaturesReply(h, (OFFeaturesReply) m);
857 break;
858 case FLOW_REMOVED:
859 processOFFlowRemoved(h, (OFFlowRemoved) m);
860 break;
861 case GET_CONFIG_REPLY:
862 processOFGetConfigReply(h, (OFGetConfigReply) m);
863 break;
864 case PACKET_IN:
865 processOFPacketIn(h, (OFPacketIn) m);
866 break;
867 case PORT_STATUS:
868 processOFPortStatus(h, (OFPortStatus) m);
869 break;
870 case QUEUE_GET_CONFIG_REPLY:
871 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
872 break;
873 case STATS_REPLY: // multipart_reply in 1.3
874 processOFStatisticsReply(h, (OFStatsReply) m);
875 break;
876 case EXPERIMENTER:
877 processOFExperimenter(h, (OFExperimenter) m);
878 break;
879 case ROLE_REPLY:
880 processOFRoleReply(h, (OFRoleReply) m);
881 break;
882 case GET_ASYNC_REPLY:
883 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
884 break;
885
886 // The following messages are sent to switches. The controller
887 // should never receive them
888 case SET_CONFIG:
889 case GET_CONFIG_REQUEST:
890 case PACKET_OUT:
891 case PORT_MOD:
892 case QUEUE_GET_CONFIG_REQUEST:
893 case BARRIER_REQUEST:
894 case STATS_REQUEST: // multipart request in 1.3
895 case FEATURES_REQUEST:
896 case FLOW_MOD:
897 case GROUP_MOD:
898 case TABLE_MOD:
899 case GET_ASYNC_REQUEST:
900 case SET_ASYNC:
901 case METER_MOD:
902 default:
903 illegalMessageReceived(h, m);
904 break;
905 }
906 }
907
908 /*-----------------------------------------------------------------
909 * Default implementation for message handlers in any state.
910 *
911 * Individual states must override these if they want a behavior
912 * that differs from the default.
913 *
914 * In general, these handlers simply ignore the message and do
915 * nothing.
916 *
917 * There are some exceptions though, since some messages really
918 * are handled the same way in every state (e.g., ECHO_REQUST) or
919 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
920 -----------------------------------------------------------------*/
921
922 void processOFHello(OFChannelHandler h, OFHello m)
923 throws IOException, SwitchStateException {
924 // we only expect hello in the WAIT_HELLO state
925 illegalMessageReceived(h, m);
926 }
927
928 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
929 throws IOException {
930 // Silently ignore.
931 }
932
933 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
934 throws IOException {
935 if (h.ofVersion == null) {
936 log.error("No OF version set for {}. Not sending Echo REPLY",
937 h.channel.getRemoteAddress());
938 return;
939 }
940 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
941 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
942 OFEchoReply reply = factory
943 .buildEchoReply()
944 .setXid(m.getXid())
945 .setData(m.getData())
946 .build();
947 h.channel.write(Collections.singletonList(reply));
948 }
949
950 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
951 throws IOException {
952 // Do nothing with EchoReplies !!
953 }
954
955 // no default implementation for OFError
956 // every state must override it
957 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
958 throws IOException, SwitchStateException;
959
960
961 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
962 throws IOException, SwitchStateException {
963 unhandledMessageReceived(h, m);
964 }
965
966 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
967 throws IOException {
968 unhandledMessageReceived(h, m);
969 }
970
971 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
972 throws IOException, SwitchStateException {
973 // we only expect config replies in the WAIT_CONFIG_REPLY state
974 illegalMessageReceived(h, m);
975 }
976
977 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
978 throws IOException {
979 unhandledMessageReceived(h, m);
980 }
981
982 // no default implementation. Every state needs to handle it.
983 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
984 throws IOException, SwitchStateException;
985
986 void processOFQueueGetConfigReply(OFChannelHandler h,
987 OFQueueGetConfigReply m)
988 throws IOException {
989 unhandledMessageReceived(h, m);
990 }
991
992 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
993 throws IOException, SwitchStateException {
994 unhandledMessageReceived(h, m);
995 }
996
997 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
998 throws IOException, SwitchStateException {
999 // TODO: it might make sense to parse the vendor message here
1000 // into the known vendor messages we support and then call more
1001 // specific event handlers
1002 unhandledMessageReceived(h, m);
1003 }
1004
1005 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1006 throws SwitchStateException, IOException {
1007 unhandledMessageReceived(h, m);
1008 }
1009
1010 void processOFGetAsyncReply(OFChannelHandler h,
1011 OFAsyncGetReply m) {
1012 unhandledMessageReceived(h, m);
1013 }
1014
1015 }
1016
1017
1018
1019 //*************************
1020 // Channel handler methods
1021 //*************************
1022
1023 @Override
1024 public void channelConnected(ChannelHandlerContext ctx,
1025 ChannelStateEvent e) throws Exception {
1026 channel = e.getChannel();
1027 log.info("New switch connection from {}",
1028 channel.getRemoteAddress());
1029 sendHandshakeHelloMessage();
1030 setState(ChannelState.WAIT_HELLO);
1031 }
1032
1033 @Override
1034 public void channelDisconnected(ChannelHandlerContext ctx,
1035 ChannelStateEvent e) throws Exception {
1036 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1037 getSwitchInfoString());
1038 if (thisdpid != 0) {
1039 if (!duplicateDpidFound) {
1040 // if the disconnected switch (on this ChannelHandler)
1041 // was not one with a duplicate-dpid, it is safe to remove all
1042 // state for it at the controller. Notice that if the disconnected
1043 // switch was a duplicate-dpid, calling the method below would clear
1044 // all state for the original switch (with the same dpid),
1045 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001046 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001047 if (sw != null) {
1048 sw.removeConnectedSwitch();
1049 }
tom7ef8ff92014-09-17 13:08:06 -07001050 } else {
1051 // A duplicate was disconnected on this ChannelHandler,
1052 // this is the same switch reconnecting, but the original state was
1053 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001054 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001055 duplicateDpidFound = Boolean.FALSE;
1056 }
1057 } else {
1058 log.warn("no dpid in channelHandler registered for "
1059 + "disconnected switch {}", getSwitchInfoString());
1060 }
1061 }
1062
1063 @Override
1064 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1065 throws Exception {
1066 if (e.getCause() instanceof ReadTimeoutException) {
1067 // switch timeout
1068 log.error("Disconnecting switch {} due to read timeout",
1069 getSwitchInfoString());
1070 ctx.getChannel().close();
1071 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1072 log.error("Disconnecting switch {}: failed to complete handshake",
1073 getSwitchInfoString());
1074 ctx.getChannel().close();
1075 } else if (e.getCause() instanceof ClosedChannelException) {
1076 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1077 } else if (e.getCause() instanceof IOException) {
1078 log.error("Disconnecting switch {} due to IO Error: {}",
1079 getSwitchInfoString(), e.getCause().getMessage());
1080 if (log.isDebugEnabled()) {
1081 // still print stack trace if debug is enabled
1082 log.debug("StackTrace for previous Exception: ", e.getCause());
1083 }
1084 ctx.getChannel().close();
1085 } else if (e.getCause() instanceof SwitchStateException) {
1086 log.error("Disconnecting switch {} due to switch state error: {}",
1087 getSwitchInfoString(), e.getCause().getMessage());
1088 if (log.isDebugEnabled()) {
1089 // still print stack trace if debug is enabled
1090 log.debug("StackTrace for previous Exception: ", e.getCause());
1091 }
1092 ctx.getChannel().close();
1093 } else if (e.getCause() instanceof OFParseError) {
1094 log.error("Disconnecting switch "
1095 + getSwitchInfoString() +
1096 " due to message parse failure",
1097 e.getCause());
1098 ctx.getChannel().close();
1099 } else if (e.getCause() instanceof RejectedExecutionException) {
1100 log.warn("Could not process message: queue full");
1101 } else {
1102 log.error("Error while processing message from switch "
1103 + getSwitchInfoString()
1104 + "state " + this.state, e.getCause());
1105 ctx.getChannel().close();
1106 }
1107 }
1108
1109 @Override
1110 public String toString() {
1111 return getSwitchInfoString();
1112 }
1113
1114 @Override
1115 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1116 throws Exception {
1117 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1118 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001119 log.debug("Sending Echo Request on idle channel: {}",
tom7ef8ff92014-09-17 13:08:06 -07001120 e.getChannel().getPipeline().getLast().toString());
1121 e.getChannel().write(Collections.singletonList(m));
1122 // XXX S some problems here -- echo request has no transaction id, and
1123 // echo reply is not correlated to the echo request.
1124 }
1125
1126 @Override
1127 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1128 throws Exception {
1129 if (e.getMessage() instanceof List) {
1130 @SuppressWarnings("unchecked")
1131 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1132
1133
1134 for (OFMessage ofm : msglist) {
1135 // Do the actual packet processing
1136 state.processOFMessage(this, ofm);
1137 }
1138 } else {
1139 state.processOFMessage(this, (OFMessage) e.getMessage());
1140 }
1141 }
1142
1143
1144
1145 //*************************
1146 // Channel utility methods
1147 //*************************
1148
1149 /**
1150 * Is this a state in which the handshake has completed?
1151 * @return true if the handshake is complete
1152 */
1153 public boolean isHandshakeComplete() {
1154 return this.state.isHandshakeComplete();
1155 }
1156
1157 private void dispatchMessage(OFMessage m) {
1158 sw.handleMessage(m);
1159 }
1160
1161 /**
1162 * Return a string describing this switch based on the already available
1163 * information (DPID and/or remote socket).
1164 * @return display string
1165 */
1166 private String getSwitchInfoString() {
1167 if (sw != null) {
1168 return sw.toString();
1169 }
1170 String channelString;
1171 if (channel == null || channel.getRemoteAddress() == null) {
1172 channelString = "?";
1173 } else {
1174 channelString = channel.getRemoteAddress().toString();
1175 }
1176 String dpidString;
1177 if (featuresReply == null) {
1178 dpidString = "?";
1179 } else {
1180 dpidString = featuresReply.getDatapathId().toString();
1181 }
1182 return String.format("[%s DPID[%s]]", channelString, dpidString);
1183 }
1184
1185 /**
1186 * Update the channels state. Only called from the state machine.
1187 * TODO: enforce restricted state transitions
1188 * @param state
1189 */
1190 private void setState(ChannelState state) {
1191 this.state = state;
1192 }
1193
1194 /**
1195 * Send hello message to the switch using the handshake transactions ids.
1196 * @throws IOException
1197 */
1198 private void sendHandshakeHelloMessage() throws IOException {
1199 // The OF protocol requires us to start things off by sending the highest
1200 // version of the protocol supported.
1201
1202 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1203 // see Sec. 7.5.1 of the OF1.3.4 spec
1204 U32 bitmap = U32.ofRaw(0x00000012);
1205 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1206 .setBitmaps(Collections.singletonList(bitmap))
1207 .build();
1208 OFMessage.Builder mb = factory13.buildHello()
1209 .setXid(this.handshakeTransactionIds--)
1210 .setElements(Collections.singletonList(hem));
1211 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1212 channel.write(Collections.singletonList(mb.build()));
1213 }
1214
1215 /**
1216 * Send featuresRequest msg to the switch using the handshake transactions ids.
1217 * @throws IOException
1218 */
1219 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1220 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1221 OFMessage m = factory.buildFeaturesRequest()
1222 .setXid(this.handshakeTransactionIds--)
1223 .build();
1224 channel.write(Collections.singletonList(m));
1225 }
1226
1227 /**
1228 * Send the configuration requests to tell the switch we want full
1229 * packets.
1230 * @throws IOException
1231 */
1232 private void sendHandshakeSetConfig() throws IOException {
1233 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1234 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1235 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1236
1237 // Ensure we receive the full packet via PacketIn
1238 // FIXME: We don't set the reassembly flags.
1239 OFSetConfig sc = factory
1240 .buildSetConfig()
1241 .setMissSendLen((short) 0xffff)
1242 .setXid(this.handshakeTransactionIds--)
1243 .build();
1244 msglist.add(sc);
1245
1246 // Barrier
1247 OFBarrierRequest br = factory
1248 .buildBarrierRequest()
1249 .setXid(this.handshakeTransactionIds--)
1250 .build();
1251 msglist.add(br);
1252
1253 // Verify (need barrier?)
1254 OFGetConfigRequest gcr = factory
1255 .buildGetConfigRequest()
1256 .setXid(this.handshakeTransactionIds--)
1257 .build();
1258 msglist.add(gcr);
1259 channel.write(msglist);
1260 }
1261
1262 /**
1263 * send a description state request.
1264 * @throws IOException
1265 */
1266 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1267 // Get Description to set switch-specific flags
1268 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1269 OFDescStatsRequest dreq = factory
1270 .buildDescStatsRequest()
1271 .setXid(handshakeTransactionIds--)
1272 .build();
1273 channel.write(Collections.singletonList(dreq));
1274 }
1275
1276 private void sendHandshakeOFPortDescRequest() throws IOException {
1277 // Get port description for 1.3 switch
1278 OFPortDescStatsRequest preq = factory13
1279 .buildPortDescStatsRequest()
1280 .setXid(handshakeTransactionIds--)
1281 .build();
1282 channel.write(Collections.singletonList(preq));
1283 }
1284
1285 ChannelState getStateForTesting() {
1286 return state;
1287 }
1288
1289}