blob: ccd0f595f2dba1ad90269a4689fbade1eb90878d [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
Brian O'Connorabafb502014-12-02 22:26:20 -080018package org.onosproject.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;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
37import org.onosproject.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());
alshabib70fc7fb2015-01-06 11:04:29 -0800188 h.sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -0700189 h.ofVersion = OFVersion.OF_13;
190 } else if (m.getVersion() == OFVersion.OF_10) {
alshabib09d48be2014-10-03 15:43:33 -0700191 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700192 + "version 1.0", m.getVersion(),
193 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800194 OFHello hi =
195 h.factory10.buildHello()
196 .setXid(h.handshakeTransactionIds--)
197 .build();
198 h.channel.write(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700199 h.ofVersion = OFVersion.OF_10;
200 } else {
201 log.error("Received Hello of version {} from switch at {}. "
202 + "This controller works with OF1.0 and OF1.3 "
203 + "switches. Disconnecting switch ...",
204 m.getVersion(), h.channel.getRemoteAddress());
205 h.channel.disconnect();
206 return;
207 }
208 h.sendHandshakeFeaturesRequestMessage();
209 h.setState(WAIT_FEATURES_REPLY);
210 }
211 @Override
212 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
213 throws IOException, SwitchStateException {
214 illegalMessageReceived(h, m);
215 }
216 @Override
217 void processOFStatisticsReply(OFChannelHandler h,
218 OFStatsReply m)
219 throws IOException, SwitchStateException {
220 illegalMessageReceived(h, m);
221 }
222 @Override
223 void processOFError(OFChannelHandler h, OFErrorMsg m) {
224 logErrorDisconnect(h, m);
225 }
226
227 @Override
228 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
229 throws IOException {
230 unhandledMessageReceived(h, m);
231 }
232 },
233
234
235 /**
236 * We are waiting for a features reply message. Once we receive it, the
237 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
238 * we send a SetConfig request, barrier, and GetConfig request and the
239 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
240 * request and the next state is WAIT_PORT_DESC_REPLY.
241 */
242 WAIT_FEATURES_REPLY(false) {
243 @Override
244 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
245 throws IOException {
246 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700247 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700248 h.getSwitchInfoString(), h.thisdpid);
249
250 h.featuresReply = m; //temp store
251 if (h.ofVersion == OFVersion.OF_10) {
252 h.sendHandshakeSetConfig();
253 h.setState(WAIT_CONFIG_REPLY);
254 } else {
255 //version is 1.3, must get switchport information
256 h.sendHandshakeOFPortDescRequest();
257 h.setState(WAIT_PORT_DESC_REPLY);
258 }
259 }
260 @Override
261 void processOFStatisticsReply(OFChannelHandler h,
262 OFStatsReply m)
263 throws IOException, SwitchStateException {
264 illegalMessageReceived(h, m);
265 }
266 @Override
267 void processOFError(OFChannelHandler h, OFErrorMsg m) {
268 logErrorDisconnect(h, m);
269 }
270
271 @Override
272 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
273 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800274 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700275 }
276 },
277
278 /**
279 * We are waiting for a description of the 1.3 switch ports.
280 * Once received, we send a SetConfig request
281 * Next State is WAIT_CONFIG_REPLY
282 */
283 WAIT_PORT_DESC_REPLY(false) {
284
285 @Override
286 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
287 throws SwitchStateException {
288 // Read port description
289 if (m.getStatsType() != OFStatsType.PORT_DESC) {
290 log.warn("Expecting port description stats but received stats "
291 + "type {} from {}. Ignoring ...", m.getStatsType(),
292 h.channel.getRemoteAddress());
293 return;
294 }
295 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
296 log.warn("Stats reply indicates more stats from sw {} for "
297 + "port description - not currently handled",
298 h.getSwitchInfoString());
299 }
300 h.portDescReply = (OFPortDescStatsReply) m; // temp store
301 log.info("Received port desc reply for switch at {}",
302 h.getSwitchInfoString());
303 try {
304 h.sendHandshakeSetConfig();
305 } catch (IOException e) {
306 log.error("Unable to send setConfig after PortDescReply. "
307 + "Error: {}", e.getMessage());
308 }
309 h.setState(WAIT_CONFIG_REPLY);
310 }
311
312 @Override
313 void processOFError(OFChannelHandler h, OFErrorMsg m)
314 throws IOException, SwitchStateException {
315 logErrorDisconnect(h, m);
316
317 }
318
319 @Override
320 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
321 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800322 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700323
324 }
325 },
326
327 /**
328 * We are waiting for a config reply message. Once we receive it
329 * we send a DescriptionStatsRequest to the switch.
330 * Next state: WAIT_DESCRIPTION_STAT_REPLY
331 */
332 WAIT_CONFIG_REPLY(false) {
333 @Override
334 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
335 throws IOException {
336 if (m.getMissSendLen() == 0xffff) {
337 log.trace("Config Reply from switch {} confirms "
338 + "miss length set to 0xffff",
339 h.getSwitchInfoString());
340 } else {
341 // FIXME: we can't really deal with switches that don't send
342 // full packets. Shouldn't we drop the connection here?
343 log.warn("Config Reply from switch {} has"
344 + "miss length set to {}",
345 h.getSwitchInfoString(),
346 m.getMissSendLen());
347 }
348 h.sendHandshakeDescriptionStatsRequest();
349 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
350 }
351
352 @Override
353 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
354 // do nothing;
355 }
356
357 @Override
358 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
359 throws IOException, SwitchStateException {
360 illegalMessageReceived(h, m);
361 }
362 @Override
363 void processOFStatisticsReply(OFChannelHandler h,
364 OFStatsReply m)
365 throws IOException, SwitchStateException {
366 log.error("Received multipart(stats) message sub-type {}",
367 m.getStatsType());
368 illegalMessageReceived(h, m);
369 }
370
371 @Override
372 void processOFError(OFChannelHandler h, OFErrorMsg m) {
373 logErrorDisconnect(h, m);
374 }
375
376 @Override
377 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
378 throws IOException {
379 h.pendingPortStatusMsg.add(m);
380 }
381 },
382
383
384 /**
385 * We are waiting for a OFDescriptionStat message from the switch.
386 * Once we receive any stat message we try to parse it. If it's not
387 * a description stats message we disconnect. If its the expected
388 * description stats message, we:
389 * - use the switch driver to bind the switch and get an IOFSwitch instance
390 * - setup the IOFSwitch instance
391 * - add switch controller and send the initial role
392 * request to the switch.
393 * Next state: WAIT_INITIAL_ROLE
394 * In the typical case, where switches support role request messages
395 * the next state is where we expect the role reply message.
396 * In the special case that where the switch does not support any kind
397 * of role request messages, we don't send a role message, but we do
398 * request mastership from the registry service. This controller
399 * should become master once we hear back from the registry service.
400 * All following states will have a h.sw instance!
401 */
402 WAIT_DESCRIPTION_STAT_REPLY(false) {
403 @Override
404 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
405 throws SwitchStateException {
406 // Read description, if it has been updated
407 if (m.getStatsType() != OFStatsType.DESC) {
408 log.warn("Expecting Description stats but received stats "
409 + "type {} from {}. Ignoring ...", m.getStatsType(),
410 h.channel.getRemoteAddress());
411 return;
412 }
413 log.info("Received switch description reply from switch at {}",
414 h.channel.getRemoteAddress());
415 OFDescStatsReply drep = (OFDescStatsReply) m;
416 // Here is where we differentiate between different kinds of switches
417 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
418
419 h.sw.setOFVersion(h.ofVersion);
420 h.sw.setFeaturesReply(h.featuresReply);
421 h.sw.setPortDescReply(h.portDescReply);
422 h.sw.setConnected(true);
423 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700424// boolean success = h.sw.connectSwitch();
425//
426// if (!success) {
427// disconnectDuplicate(h);
428// return;
429// }
tom7ef8ff92014-09-17 13:08:06 -0700430 // set switch information
431
432
433
alshabib09d48be2014-10-03 15:43:33 -0700434 log.debug("Switch {} bound to class {}, description {}",
tom7ef8ff92014-09-17 13:08:06 -0700435 new Object[] {h.sw, h.sw.getClass(), drep });
436 //Put switch in EQUAL mode until we hear back from the global registry
437 //log.debug("Setting new switch {} to EQUAL and sending Role request",
438 // h.sw.getStringId());
439 //h.sw.activateEqualSwitch();
440 //h.setSwitchRole(RoleState.EQUAL);
441
442 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700443 if (h.sw.isDriverHandshakeComplete()) {
444 if (!h.sw.connectSwitch()) {
445 disconnectDuplicate(h);
446 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800447 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700448 h.setState(ACTIVE);
449 } else {
450 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
451 }
tom7ef8ff92014-09-17 13:08:06 -0700452
453 }
454
455 @Override
456 void processOFError(OFChannelHandler h, OFErrorMsg m) {
457 logErrorDisconnect(h, m);
458 }
459
460 @Override
461 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
462 throws IOException, SwitchStateException {
463 illegalMessageReceived(h, m);
464 }
465
466 @Override
467 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
468 throws IOException {
469 h.pendingPortStatusMsg.add(m);
470 }
471 },
472
473
474 /**
475 * We are waiting for the respective switch driver to complete its
476 * configuration. Notice that we do not consider this to be part of the main
477 * switch-controller handshake. But we do consider it as a step that comes
478 * before we declare the switch as available to the controller.
479 * Next State: depends on the role of this controller for this switch - either
480 * MASTER or EQUAL.
481 */
482 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
483
484 @Override
485 void processOFError(OFChannelHandler h, OFErrorMsg m)
486 throws IOException {
487 // will never be called. We override processOFMessage
488 }
489
alshabibd7963912014-10-20 14:52:04 -0700490
491
tom7ef8ff92014-09-17 13:08:06 -0700492 @Override
493 void processOFMessage(OFChannelHandler h, OFMessage m)
494 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700495
496 if (h.sw.isDriverHandshakeComplete()) {
497 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700498 h.state.processOFMessage(h, m);
499 return;
alshabibd7963912014-10-20 14:52:04 -0700500
501 }
502
tom7ef8ff92014-09-17 13:08:06 -0700503 if (m.getType() == OFType.ECHO_REQUEST) {
504 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700505 } else if (m.getType() == OFType.ECHO_REPLY) {
506 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700507 } else if (m.getType() == OFType.ROLE_REPLY) {
508 h.sw.handleRole(m);
509 } else if (m.getType() == OFType.ERROR) {
510 if (!h.sw.handleRoleError((OFErrorMsg)m)) {
511 h.sw.processDriverHandshakeMessage(m);
512 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700513 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700514 }
515 }
516 } else {
517 if (m.getType() == OFType.EXPERIMENTER &&
518 ((OFExperimenter) m).getExperimenter() ==
519 RoleManager.NICIRA_EXPERIMENTER) {
520 h.sw.handleNiciraRole(m);
521 } else {
522 h.sw.processDriverHandshakeMessage(m);
523 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700524 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700525 }
526 }
527 }
528 }
529
530 @Override
531 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
532 throws IOException, SwitchStateException {
533 h.pendingPortStatusMsg.add(m);
534 }
alshabibd7963912014-10-20 14:52:04 -0700535
536 private void moveToActive(OFChannelHandler h) {
537 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800538 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700539 h.setState(ACTIVE);
540 if (!success) {
541 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700542 }
543 }
544
tom7ef8ff92014-09-17 13:08:06 -0700545 },
546
547
548 /**
549 * This controller is in MASTER role for this switch. We enter this state
550 * after requesting and winning control from the global registry.
551 * The main handshake as well as the switch-driver sub-handshake
552 * is complete at this point.
553 * // XXX S reconsider below
554 * In the (near) future we may deterministically assign controllers to
555 * switches at startup.
556 * We only leave this state if the switch disconnects or
557 * if we send a role request for SLAVE /and/ receive the role reply for
558 * SLAVE.
559 */
560 ACTIVE(true) {
561 @Override
562 void processOFError(OFChannelHandler h, OFErrorMsg m)
563 throws IOException, SwitchStateException {
564 // if we get here, then the error message is for something else
565 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
566 ((OFBadRequestErrorMsg) m).getCode() ==
567 OFBadRequestCode.EPERM) {
568 // We are the master controller and the switch returned
569 // a permission error. This is a likely indicator that
570 // the switch thinks we are slave. Reassert our
571 // role
572 // FIXME: this could be really bad during role transitions
573 // if two controllers are master (even if its only for
574 // a brief period). We might need to see if these errors
575 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700576
tom7ef8ff92014-09-17 13:08:06 -0700577 h.sw.reassertRole();
578 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
579 ((OFFlowModFailedErrorMsg) m).getCode() ==
580 OFFlowModFailedCode.ALL_TABLES_FULL) {
581 h.sw.setTableFull(true);
582 } else {
583 logError(h, m);
584 }
585 h.dispatchMessage(m);
586 }
587
588 @Override
589 void processOFStatisticsReply(OFChannelHandler h,
590 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700591 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
592 h.sw.setPortDescReply((OFPortDescStatsReply) m);
593 }
tom7ef8ff92014-09-17 13:08:06 -0700594 h.dispatchMessage(m);
595 }
596
597 @Override
598 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
599 throws SwitchStateException {
600 h.sw.handleNiciraRole(m);
601 }
602
603 @Override
604 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
605 throws SwitchStateException {
606 h.sw.handleRole(m);
607 }
608
609 @Override
610 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
611 throws SwitchStateException {
612 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800613 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700614 }
615
616 @Override
617 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700618// OFPacketOut out =
619// h.sw.factory().buildPacketOut()
620// .setXid(m.getXid())
621// .setBufferId(m.getBufferId()).build();
622// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700623 h.dispatchMessage(m);
624 }
625
626 @Override
627 void processOFFlowRemoved(OFChannelHandler h,
628 OFFlowRemoved m) {
629 h.dispatchMessage(m);
630 }
631
632 @Override
633 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
634 h.dispatchMessage(m);
635 }
636
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700637 @Override
638 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700639 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700640 h.dispatchMessage(m);
641 }
642
tom7ef8ff92014-09-17 13:08:06 -0700643 };
644
645 private final boolean handshakeComplete;
646 ChannelState(boolean handshakeComplete) {
647 this.handshakeComplete = handshakeComplete;
648 }
649
650 /**
651 * Is this a state in which the handshake has completed?
652 * @return true if the handshake is complete
653 */
654 public boolean isHandshakeComplete() {
655 return handshakeComplete;
656 }
657
658 /**
659 * Get a string specifying the switch connection, state, and
660 * message received. To be used as message for SwitchStateException
661 * or log messages
662 * @param h The channel handler (to get switch information_
663 * @param m The OFMessage that has just been received
664 * @param details A string giving more details about the exact nature
665 * of the problem.
666 * @return display string
667 */
668 // needs to be protected because enum members are actually subclasses
669 protected String getSwitchStateMessage(OFChannelHandler h,
670 OFMessage m,
671 String details) {
672 return String.format("Switch: [%s], State: [%s], received: [%s]"
673 + ", details: %s",
674 h.getSwitchInfoString(),
675 this.toString(),
676 m.getType().toString(),
677 details);
678 }
679
680 /**
681 * We have an OFMessage we didn't expect given the current state and
682 * we want to treat this as an error.
683 * We currently throw an exception that will terminate the connection
684 * However, we could be more forgiving
685 * @param h the channel handler that received the message
686 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700687 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700688 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700689 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700690 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
691 throws SwitchStateException {
692 String msg = getSwitchStateMessage(h, m,
693 "Switch should never send this message in the current state");
694 throw new SwitchStateException(msg);
695
696 }
697
698 /**
699 * We have an OFMessage we didn't expect given the current state and
700 * we want to ignore the message.
701 * @param h the channel handler the received the message
702 * @param m the message
703 */
704 protected void unhandledMessageReceived(OFChannelHandler h,
705 OFMessage m) {
706 if (log.isDebugEnabled()) {
707 String msg = getSwitchStateMessage(h, m,
708 "Ignoring unexpected message");
709 log.debug(msg);
710 }
711 }
712
713 /**
714 * Log an OpenFlow error message from a switch.
715 * @param h The switch that sent the error
716 * @param error The error message
717 */
718 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700719 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700720 new Object[] {
721 error,
722 h.getSwitchInfoString(),
723 this.toString()});
724 }
725
726 /**
727 * Log an OpenFlow error message from a switch and disconnect the
728 * channel.
729 *
730 * @param h the IO channel for this switch.
731 * @param error The error message
732 */
733 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
734 logError(h, error);
735 h.channel.disconnect();
736 }
737
738 /**
739 * log an error message for a duplicate dpid and disconnect this channel.
740 * @param h the IO channel for this switch.
741 */
742 protected void disconnectDuplicate(OFChannelHandler h) {
743 log.error("Duplicated dpid or incompleted cleanup - "
744 + "disconnecting channel {}", h.getSwitchInfoString());
745 h.duplicateDpidFound = Boolean.TRUE;
746 h.channel.disconnect();
747 }
748
749
750
751 /**
752 * Handles all pending port status messages before a switch is declared
753 * activated in MASTER or EQUAL role. Note that since this handling
754 * precedes the activation (and therefore notification to IOFSwitchListerners)
755 * the changes to ports will already be visible once the switch is
756 * activated. As a result, no notifications are sent out for these
757 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700758 *
759 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700760 */
761 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
762 try {
763 handlePendingPortStatusMessages(h, 0);
764 } catch (SwitchStateException e) {
765 log.error(e.getMessage());
766 }
767 }
768
769 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
770 throws SwitchStateException {
771 if (h.sw == null) {
772 String msg = "State machine error: switch is null. Should never " +
773 "happen";
774 throw new SwitchStateException(msg);
775 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800776 log.info("Processing {} pending port status messages for {}",
777 h.pendingPortStatusMsg.size(), h.sw.getStringId());
778
tom7ef8ff92014-09-17 13:08:06 -0700779 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
780 for (OFPortStatus ps: h.pendingPortStatusMsg) {
781 temp.add(ps);
782 handlePortStatusMessage(h, ps, false);
783 }
tom7ef8ff92014-09-17 13:08:06 -0700784 // expensive but ok - we don't expect too many port-status messages
785 // note that we cannot use clear(), because of the reasons below
786 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800787 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700788 // the iterator above takes a snapshot of the list - so while we were
789 // dealing with the pending port-status messages, we could have received
790 // newer ones. Handle them recursively, but break the recursion after
791 // five steps to avoid an attack.
792 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
793 handlePendingPortStatusMessages(h, index);
794 }
795 }
796
797 /**
798 * Handle a port status message.
799 *
800 * Handle a port status message by updating the port maps in the
801 * IOFSwitch instance and notifying Controller about the change so
802 * it can dispatch a switch update.
803 *
804 * @param h The OFChannelHhandler that received the message
805 * @param m The PortStatus message we received
806 * @param doNotify if true switch port changed events will be
807 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800808 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700809 *
810 */
811 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
812 boolean doNotify) throws SwitchStateException {
813 if (h.sw == null) {
814 String msg = getSwitchStateMessage(h, m,
815 "State machine error: switch is null. Should never " +
816 "happen");
817 throw new SwitchStateException(msg);
818 }
819
820 h.sw.handleMessage(m);
821 }
822
823
824 /**
825 * Process an OF message received on the channel and
826 * update state accordingly.
827 *
828 * The main "event" of the state machine. Process the received message,
829 * send follow up message if required and update state if required.
830 *
831 * Switches on the message type and calls more specific event handlers
832 * for each individual OF message type. If we receive a message that
833 * is supposed to be sent from a controller to a switch we throw
834 * a SwitchStateExeption.
835 *
836 * The more specific handlers can also throw SwitchStateExceptions
837 *
838 * @param h The OFChannelHandler that received the message
839 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800840 * @throws SwitchStateException if the switch is not bound to the channel
841 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700842 */
843 void processOFMessage(OFChannelHandler h, OFMessage m)
844 throws IOException, SwitchStateException {
845 switch(m.getType()) {
846 case HELLO:
847 processOFHello(h, (OFHello) m);
848 break;
849 case BARRIER_REPLY:
850 processOFBarrierReply(h, (OFBarrierReply) m);
851 break;
852 case ECHO_REPLY:
853 processOFEchoReply(h, (OFEchoReply) m);
854 break;
855 case ECHO_REQUEST:
856 processOFEchoRequest(h, (OFEchoRequest) m);
857 break;
858 case ERROR:
859 processOFError(h, (OFErrorMsg) m);
860 break;
861 case FEATURES_REPLY:
862 processOFFeaturesReply(h, (OFFeaturesReply) m);
863 break;
864 case FLOW_REMOVED:
865 processOFFlowRemoved(h, (OFFlowRemoved) m);
866 break;
867 case GET_CONFIG_REPLY:
868 processOFGetConfigReply(h, (OFGetConfigReply) m);
869 break;
870 case PACKET_IN:
871 processOFPacketIn(h, (OFPacketIn) m);
872 break;
873 case PORT_STATUS:
874 processOFPortStatus(h, (OFPortStatus) m);
875 break;
876 case QUEUE_GET_CONFIG_REPLY:
877 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
878 break;
879 case STATS_REPLY: // multipart_reply in 1.3
880 processOFStatisticsReply(h, (OFStatsReply) m);
881 break;
882 case EXPERIMENTER:
883 processOFExperimenter(h, (OFExperimenter) m);
884 break;
885 case ROLE_REPLY:
886 processOFRoleReply(h, (OFRoleReply) m);
887 break;
888 case GET_ASYNC_REPLY:
889 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
890 break;
891
892 // The following messages are sent to switches. The controller
893 // should never receive them
894 case SET_CONFIG:
895 case GET_CONFIG_REQUEST:
896 case PACKET_OUT:
897 case PORT_MOD:
898 case QUEUE_GET_CONFIG_REQUEST:
899 case BARRIER_REQUEST:
900 case STATS_REQUEST: // multipart request in 1.3
901 case FEATURES_REQUEST:
902 case FLOW_MOD:
903 case GROUP_MOD:
904 case TABLE_MOD:
905 case GET_ASYNC_REQUEST:
906 case SET_ASYNC:
907 case METER_MOD:
908 default:
909 illegalMessageReceived(h, m);
910 break;
911 }
912 }
913
914 /*-----------------------------------------------------------------
915 * Default implementation for message handlers in any state.
916 *
917 * Individual states must override these if they want a behavior
918 * that differs from the default.
919 *
920 * In general, these handlers simply ignore the message and do
921 * nothing.
922 *
923 * There are some exceptions though, since some messages really
924 * are handled the same way in every state (e.g., ECHO_REQUST) or
925 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
926 -----------------------------------------------------------------*/
927
928 void processOFHello(OFChannelHandler h, OFHello m)
929 throws IOException, SwitchStateException {
930 // we only expect hello in the WAIT_HELLO state
931 illegalMessageReceived(h, m);
932 }
933
934 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
935 throws IOException {
936 // Silently ignore.
937 }
938
939 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
940 throws IOException {
941 if (h.ofVersion == null) {
942 log.error("No OF version set for {}. Not sending Echo REPLY",
943 h.channel.getRemoteAddress());
944 return;
945 }
946 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
947 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
948 OFEchoReply reply = factory
949 .buildEchoReply()
950 .setXid(m.getXid())
951 .setData(m.getData())
952 .build();
953 h.channel.write(Collections.singletonList(reply));
954 }
955
956 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
957 throws IOException {
958 // Do nothing with EchoReplies !!
959 }
960
961 // no default implementation for OFError
962 // every state must override it
963 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
964 throws IOException, SwitchStateException;
965
966
967 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
968 throws IOException, SwitchStateException {
969 unhandledMessageReceived(h, m);
970 }
971
972 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
973 throws IOException {
974 unhandledMessageReceived(h, m);
975 }
976
977 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
978 throws IOException, SwitchStateException {
979 // we only expect config replies in the WAIT_CONFIG_REPLY state
980 illegalMessageReceived(h, m);
981 }
982
983 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
984 throws IOException {
985 unhandledMessageReceived(h, m);
986 }
987
988 // no default implementation. Every state needs to handle it.
989 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
990 throws IOException, SwitchStateException;
991
992 void processOFQueueGetConfigReply(OFChannelHandler h,
993 OFQueueGetConfigReply m)
994 throws IOException {
995 unhandledMessageReceived(h, m);
996 }
997
998 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
999 throws IOException, SwitchStateException {
1000 unhandledMessageReceived(h, m);
1001 }
1002
1003 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1004 throws IOException, SwitchStateException {
1005 // TODO: it might make sense to parse the vendor message here
1006 // into the known vendor messages we support and then call more
1007 // specific event handlers
1008 unhandledMessageReceived(h, m);
1009 }
1010
1011 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1012 throws SwitchStateException, IOException {
1013 unhandledMessageReceived(h, m);
1014 }
1015
1016 void processOFGetAsyncReply(OFChannelHandler h,
1017 OFAsyncGetReply m) {
1018 unhandledMessageReceived(h, m);
1019 }
1020
1021 }
1022
1023
1024
1025 //*************************
1026 // Channel handler methods
1027 //*************************
1028
1029 @Override
1030 public void channelConnected(ChannelHandlerContext ctx,
1031 ChannelStateEvent e) throws Exception {
1032 channel = e.getChannel();
1033 log.info("New switch connection from {}",
1034 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001035 /*
1036 hack to wait for the switch to tell us what it's
1037 max version is. This is not spec compliant and should
1038 be removed as soon as switches behave better.
1039 */
1040 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001041 setState(ChannelState.WAIT_HELLO);
1042 }
1043
1044 @Override
1045 public void channelDisconnected(ChannelHandlerContext ctx,
1046 ChannelStateEvent e) throws Exception {
1047 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1048 getSwitchInfoString());
1049 if (thisdpid != 0) {
1050 if (!duplicateDpidFound) {
1051 // if the disconnected switch (on this ChannelHandler)
1052 // was not one with a duplicate-dpid, it is safe to remove all
1053 // state for it at the controller. Notice that if the disconnected
1054 // switch was a duplicate-dpid, calling the method below would clear
1055 // all state for the original switch (with the same dpid),
1056 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001057 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001058 if (sw != null) {
1059 sw.removeConnectedSwitch();
1060 }
tom7ef8ff92014-09-17 13:08:06 -07001061 } else {
1062 // A duplicate was disconnected on this ChannelHandler,
1063 // this is the same switch reconnecting, but the original state was
1064 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001065 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001066 duplicateDpidFound = Boolean.FALSE;
1067 }
1068 } else {
1069 log.warn("no dpid in channelHandler registered for "
1070 + "disconnected switch {}", getSwitchInfoString());
1071 }
1072 }
1073
1074 @Override
1075 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1076 throws Exception {
1077 if (e.getCause() instanceof ReadTimeoutException) {
1078 // switch timeout
1079 log.error("Disconnecting switch {} due to read timeout",
1080 getSwitchInfoString());
1081 ctx.getChannel().close();
1082 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1083 log.error("Disconnecting switch {}: failed to complete handshake",
1084 getSwitchInfoString());
1085 ctx.getChannel().close();
1086 } else if (e.getCause() instanceof ClosedChannelException) {
1087 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1088 } else if (e.getCause() instanceof IOException) {
1089 log.error("Disconnecting switch {} due to IO Error: {}",
1090 getSwitchInfoString(), e.getCause().getMessage());
1091 if (log.isDebugEnabled()) {
1092 // still print stack trace if debug is enabled
1093 log.debug("StackTrace for previous Exception: ", e.getCause());
1094 }
1095 ctx.getChannel().close();
1096 } else if (e.getCause() instanceof SwitchStateException) {
1097 log.error("Disconnecting switch {} due to switch state error: {}",
1098 getSwitchInfoString(), e.getCause().getMessage());
1099 if (log.isDebugEnabled()) {
1100 // still print stack trace if debug is enabled
1101 log.debug("StackTrace for previous Exception: ", e.getCause());
1102 }
1103 ctx.getChannel().close();
1104 } else if (e.getCause() instanceof OFParseError) {
1105 log.error("Disconnecting switch "
1106 + getSwitchInfoString() +
1107 " due to message parse failure",
1108 e.getCause());
1109 ctx.getChannel().close();
1110 } else if (e.getCause() instanceof RejectedExecutionException) {
1111 log.warn("Could not process message: queue full");
1112 } else {
1113 log.error("Error while processing message from switch "
1114 + getSwitchInfoString()
1115 + "state " + this.state, e.getCause());
1116 ctx.getChannel().close();
1117 }
1118 }
1119
1120 @Override
1121 public String toString() {
1122 return getSwitchInfoString();
1123 }
1124
1125 @Override
1126 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1127 throws Exception {
1128 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1129 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001130 log.debug("Sending Echo Request on idle channel: {}",
tom7ef8ff92014-09-17 13:08:06 -07001131 e.getChannel().getPipeline().getLast().toString());
1132 e.getChannel().write(Collections.singletonList(m));
1133 // XXX S some problems here -- echo request has no transaction id, and
1134 // echo reply is not correlated to the echo request.
1135 }
1136
1137 @Override
1138 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1139 throws Exception {
1140 if (e.getMessage() instanceof List) {
1141 @SuppressWarnings("unchecked")
1142 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1143
1144
1145 for (OFMessage ofm : msglist) {
1146 // Do the actual packet processing
1147 state.processOFMessage(this, ofm);
1148 }
1149 } else {
1150 state.processOFMessage(this, (OFMessage) e.getMessage());
1151 }
1152 }
1153
1154
1155
1156 //*************************
1157 // Channel utility methods
1158 //*************************
1159
1160 /**
1161 * Is this a state in which the handshake has completed?
1162 * @return true if the handshake is complete
1163 */
1164 public boolean isHandshakeComplete() {
1165 return this.state.isHandshakeComplete();
1166 }
1167
1168 private void dispatchMessage(OFMessage m) {
1169 sw.handleMessage(m);
1170 }
1171
1172 /**
1173 * Return a string describing this switch based on the already available
1174 * information (DPID and/or remote socket).
1175 * @return display string
1176 */
1177 private String getSwitchInfoString() {
1178 if (sw != null) {
1179 return sw.toString();
1180 }
1181 String channelString;
1182 if (channel == null || channel.getRemoteAddress() == null) {
1183 channelString = "?";
1184 } else {
1185 channelString = channel.getRemoteAddress().toString();
1186 }
1187 String dpidString;
1188 if (featuresReply == null) {
1189 dpidString = "?";
1190 } else {
1191 dpidString = featuresReply.getDatapathId().toString();
1192 }
1193 return String.format("[%s DPID[%s]]", channelString, dpidString);
1194 }
1195
1196 /**
1197 * Update the channels state. Only called from the state machine.
1198 * TODO: enforce restricted state transitions
1199 * @param state
1200 */
1201 private void setState(ChannelState state) {
1202 this.state = state;
1203 }
1204
1205 /**
1206 * Send hello message to the switch using the handshake transactions ids.
1207 * @throws IOException
1208 */
1209 private void sendHandshakeHelloMessage() throws IOException {
1210 // The OF protocol requires us to start things off by sending the highest
1211 // version of the protocol supported.
1212
1213 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1214 // see Sec. 7.5.1 of the OF1.3.4 spec
1215 U32 bitmap = U32.ofRaw(0x00000012);
1216 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1217 .setBitmaps(Collections.singletonList(bitmap))
1218 .build();
1219 OFMessage.Builder mb = factory13.buildHello()
1220 .setXid(this.handshakeTransactionIds--)
1221 .setElements(Collections.singletonList(hem));
1222 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1223 channel.write(Collections.singletonList(mb.build()));
1224 }
1225
1226 /**
1227 * Send featuresRequest msg to the switch using the handshake transactions ids.
1228 * @throws IOException
1229 */
1230 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1231 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1232 OFMessage m = factory.buildFeaturesRequest()
1233 .setXid(this.handshakeTransactionIds--)
1234 .build();
1235 channel.write(Collections.singletonList(m));
1236 }
1237
1238 /**
1239 * Send the configuration requests to tell the switch we want full
1240 * packets.
1241 * @throws IOException
1242 */
1243 private void sendHandshakeSetConfig() throws IOException {
1244 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1245 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1246 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1247
1248 // Ensure we receive the full packet via PacketIn
1249 // FIXME: We don't set the reassembly flags.
1250 OFSetConfig sc = factory
1251 .buildSetConfig()
1252 .setMissSendLen((short) 0xffff)
1253 .setXid(this.handshakeTransactionIds--)
1254 .build();
1255 msglist.add(sc);
1256
1257 // Barrier
1258 OFBarrierRequest br = factory
1259 .buildBarrierRequest()
1260 .setXid(this.handshakeTransactionIds--)
1261 .build();
1262 msglist.add(br);
1263
1264 // Verify (need barrier?)
1265 OFGetConfigRequest gcr = factory
1266 .buildGetConfigRequest()
1267 .setXid(this.handshakeTransactionIds--)
1268 .build();
1269 msglist.add(gcr);
1270 channel.write(msglist);
1271 }
1272
1273 /**
1274 * send a description state request.
1275 * @throws IOException
1276 */
1277 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1278 // Get Description to set switch-specific flags
1279 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1280 OFDescStatsRequest dreq = factory
1281 .buildDescStatsRequest()
1282 .setXid(handshakeTransactionIds--)
1283 .build();
1284 channel.write(Collections.singletonList(dreq));
1285 }
1286
1287 private void sendHandshakeOFPortDescRequest() throws IOException {
1288 // Get port description for 1.3 switch
1289 OFPortDescStatsRequest preq = factory13
1290 .buildPortDescStatsRequest()
1291 .setXid(handshakeTransactionIds--)
1292 .build();
1293 channel.write(Collections.singletonList(preq));
1294 }
1295
1296 ChannelState getStateForTesting() {
1297 return state;
1298 }
1299
1300}