blob: 5d9081a28d74ab0c8e97c45630cc116dee4be9cb [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present 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;
Charles Chan34155e52016-11-30 18:28:11 -080036import org.onosproject.openflow.controller.Dpid;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
38import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070039import org.projectfloodlight.openflow.exceptions.OFParseError;
40import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
41import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
42import org.projectfloodlight.openflow.protocol.OFBarrierReply;
43import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
44import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
45import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
46import org.projectfloodlight.openflow.protocol.OFEchoReply;
47import org.projectfloodlight.openflow.protocol.OFEchoRequest;
48import org.projectfloodlight.openflow.protocol.OFErrorMsg;
49import org.projectfloodlight.openflow.protocol.OFErrorType;
50import org.projectfloodlight.openflow.protocol.OFExperimenter;
51import org.projectfloodlight.openflow.protocol.OFFactory;
52import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
53import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
54import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
55import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
56import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
57import org.projectfloodlight.openflow.protocol.OFHello;
58import org.projectfloodlight.openflow.protocol.OFHelloElem;
59import org.projectfloodlight.openflow.protocol.OFMessage;
Jordi Ortiz91477b82016-11-29 15:22:50 +010060import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
61import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsRequest;
tom7ef8ff92014-09-17 13:08:06 -070062import org.projectfloodlight.openflow.protocol.OFPacketIn;
63import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
64import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
65import org.projectfloodlight.openflow.protocol.OFPortStatus;
66import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
67import org.projectfloodlight.openflow.protocol.OFRoleReply;
68import org.projectfloodlight.openflow.protocol.OFSetConfig;
69import org.projectfloodlight.openflow.protocol.OFStatsReply;
70import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
71import org.projectfloodlight.openflow.protocol.OFStatsType;
72import org.projectfloodlight.openflow.protocol.OFType;
73import org.projectfloodlight.openflow.protocol.OFVersion;
74import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
75import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
76import org.projectfloodlight.openflow.types.U32;
77import org.slf4j.Logger;
78import org.slf4j.LoggerFactory;
79
80/**
81 * Channel handler deals with the switch connection and dispatches
82 * switch messages to the appropriate locations.
83 */
84class OFChannelHandler extends IdleStateAwareChannelHandler {
85 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -070086
87 private static final String RESET_BY_PEER = "Connection reset by peer";
88 private static final String BROKEN_PIPE = "Broken pipe";
89
tom7ef8ff92014-09-17 13:08:06 -070090 private final Controller controller;
91 private OpenFlowSwitchDriver sw;
92 private long thisdpid; // channelHandler cached value of connected switch id
93 private Channel channel;
94 // State needs to be volatile because the HandshakeTimeoutHandler
95 // needs to check if the handshake is complete
96 private volatile ChannelState state;
97
98 // When a switch with a duplicate dpid is found (i.e we already have a
99 // connected switch with the same dpid), the new switch is immediately
100 // disconnected. At that point netty callsback channelDisconnected() which
101 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
102 // switch state for the older (still connected) switch
103 private volatile Boolean duplicateDpidFound;
104
105 // Temporary storage for switch-features and port-description
106 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700107 private List<OFPortDescStatsReply> portDescReplies;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100108 private OFMeterFeaturesStatsReply meterFeaturesReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700109 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700110 // a concurrent ArrayList to temporarily store port status messages
111 // before we are ready to deal with them
112 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
113
114 //Indicates the openflow version used by this switch
115 protected OFVersion ofVersion;
116 protected OFFactory factory13;
117 protected OFFactory factory10;
118
119 /** transaction Ids to use during handshake. Since only one thread
120 * calls into an OFChannelHandler instance, we don't need atomic.
121 * We will count down
122 */
123 private int handshakeTransactionIds = -1;
124
125 /**
126 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800127 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700128 */
129 OFChannelHandler(Controller controller) {
130 this.controller = controller;
131 this.state = ChannelState.INIT;
132 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700133 this.portDescReplies = new ArrayList<OFPortDescStatsReply>();
tom7ef8ff92014-09-17 13:08:06 -0700134 factory13 = controller.getOFMessageFactory13();
135 factory10 = controller.getOFMessageFactory10();
136 duplicateDpidFound = Boolean.FALSE;
137 }
138
139
140
141 // XXX S consider if necessary
142 public void disconnectSwitch() {
143 sw.disconnectSwitch();
144 }
145
146
147
148 //*************************
149 // Channel State Machine
150 //*************************
151
152 /**
153 * The state machine for handling the switch/channel state. All state
154 * transitions should happen from within the state machine (and not from other
155 * parts of the code)
156 */
157 enum ChannelState {
158 /**
159 * Initial state before channel is connected.
160 */
161 INIT(false) {
162 @Override
163 void processOFMessage(OFChannelHandler h, OFMessage m)
164 throws IOException, SwitchStateException {
165 illegalMessageReceived(h, m);
166 }
167
168 @Override
169 void processOFError(OFChannelHandler h, OFErrorMsg m)
170 throws IOException {
171 // need to implement since its abstract but it will never
172 // be called
173 }
174
175 @Override
176 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
177 throws IOException {
178 unhandledMessageReceived(h, m);
179 }
180 },
181
182 /**
183 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
184 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
185 * protocol version is accepted.
186 * We send an OFFeaturesRequest depending on the protocol version selected
187 * Next state is WAIT_FEATURES_REPLY
188 */
189 WAIT_HELLO(false) {
190 @Override
191 void processOFHello(OFChannelHandler h, OFHello m)
192 throws IOException {
193 // TODO We could check for the optional bitmap, but for now
194 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500195 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
196 log.debug("Received {} Hello from {} - switching to OF "
197 + "version 1.3", m.getVersion(),
tom7ef8ff92014-09-17 13:08:06 -0700198 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800199 h.sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -0700200 h.ofVersion = OFVersion.OF_13;
Chip Boling68bc6562015-07-06 10:00:01 -0500201 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700202 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700203 + "version 1.0", m.getVersion(),
204 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800205 OFHello hi =
206 h.factory10.buildHello()
207 .setXid(h.handshakeTransactionIds--)
208 .build();
209 h.channel.write(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700210 h.ofVersion = OFVersion.OF_10;
211 } else {
212 log.error("Received Hello of version {} from switch at {}. "
213 + "This controller works with OF1.0 and OF1.3 "
214 + "switches. Disconnecting switch ...",
215 m.getVersion(), h.channel.getRemoteAddress());
216 h.channel.disconnect();
217 return;
218 }
219 h.sendHandshakeFeaturesRequestMessage();
220 h.setState(WAIT_FEATURES_REPLY);
221 }
222 @Override
223 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
224 throws IOException, SwitchStateException {
225 illegalMessageReceived(h, m);
226 }
227 @Override
228 void processOFStatisticsReply(OFChannelHandler h,
229 OFStatsReply m)
230 throws IOException, SwitchStateException {
231 illegalMessageReceived(h, m);
232 }
233 @Override
234 void processOFError(OFChannelHandler h, OFErrorMsg m) {
235 logErrorDisconnect(h, m);
236 }
237
238 @Override
239 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
240 throws IOException {
241 unhandledMessageReceived(h, m);
242 }
243 },
244
245
246 /**
247 * We are waiting for a features reply message. Once we receive it, the
248 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
249 * we send a SetConfig request, barrier, and GetConfig request and the
250 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
251 * request and the next state is WAIT_PORT_DESC_REPLY.
252 */
253 WAIT_FEATURES_REPLY(false) {
254 @Override
255 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
256 throws IOException {
257 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700258 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700259 h.getSwitchInfoString(), h.thisdpid);
260
261 h.featuresReply = m; //temp store
262 if (h.ofVersion == OFVersion.OF_10) {
263 h.sendHandshakeSetConfig();
264 h.setState(WAIT_CONFIG_REPLY);
265 } else {
266 //version is 1.3, must get switchport information
267 h.sendHandshakeOFPortDescRequest();
268 h.setState(WAIT_PORT_DESC_REPLY);
269 }
270 }
271 @Override
272 void processOFStatisticsReply(OFChannelHandler h,
273 OFStatsReply m)
274 throws IOException, SwitchStateException {
275 illegalMessageReceived(h, m);
276 }
277 @Override
278 void processOFError(OFChannelHandler h, OFErrorMsg m) {
279 logErrorDisconnect(h, m);
280 }
281
282 @Override
283 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
284 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800285 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700286 }
287 },
288
289 /**
290 * We are waiting for a description of the 1.3 switch ports.
291 * Once received, we send a SetConfig request
292 * Next State is WAIT_CONFIG_REPLY
293 */
294 WAIT_PORT_DESC_REPLY(false) {
295
296 @Override
297 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
298 throws SwitchStateException {
299 // Read port description
300 if (m.getStatsType() != OFStatsType.PORT_DESC) {
301 log.warn("Expecting port description stats but received stats "
302 + "type {} from {}. Ignoring ...", m.getStatsType(),
303 h.channel.getRemoteAddress());
304 return;
305 }
306 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700307 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700308 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700309 h.getSwitchInfoString());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700310 h.portDescReplies.add((OFPortDescStatsReply)m);
311 return;
tom7ef8ff92014-09-17 13:08:06 -0700312 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700313 else {
314 h.portDescReplies.add((OFPortDescStatsReply)m);
315 }
316 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
tom7ef8ff92014-09-17 13:08:06 -0700317 log.info("Received port desc reply for switch at {}",
318 h.getSwitchInfoString());
319 try {
320 h.sendHandshakeSetConfig();
321 } catch (IOException e) {
322 log.error("Unable to send setConfig after PortDescReply. "
323 + "Error: {}", e.getMessage());
324 }
325 h.setState(WAIT_CONFIG_REPLY);
326 }
327
328 @Override
329 void processOFError(OFChannelHandler h, OFErrorMsg m)
330 throws IOException, SwitchStateException {
331 logErrorDisconnect(h, m);
332
333 }
334
335 @Override
336 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
337 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800338 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700339
340 }
341 },
342
343 /**
344 * We are waiting for a config reply message. Once we receive it
345 * we send a DescriptionStatsRequest to the switch.
346 * Next state: WAIT_DESCRIPTION_STAT_REPLY
347 */
348 WAIT_CONFIG_REPLY(false) {
349 @Override
350 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
351 throws IOException {
352 if (m.getMissSendLen() == 0xffff) {
353 log.trace("Config Reply from switch {} confirms "
354 + "miss length set to 0xffff",
355 h.getSwitchInfoString());
356 } else {
357 // FIXME: we can't really deal with switches that don't send
358 // full packets. Shouldn't we drop the connection here?
359 log.warn("Config Reply from switch {} has"
360 + "miss length set to {}",
361 h.getSwitchInfoString(),
362 m.getMissSendLen());
363 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100364
365 if (h.ofVersion == OFVersion.OF_13) {
366 // Meters were introduced in OpenFlow 1.3
367 h.sendMeterFeaturesRequest();
368 h.setState(WAIT_METER_FEATURES_REPLY);
369 }
370 else {
371 h.sendHandshakeDescriptionStatsRequest();
372 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
373 }
tom7ef8ff92014-09-17 13:08:06 -0700374 }
375
376 @Override
377 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
378 // do nothing;
379 }
380
381 @Override
382 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
383 throws IOException, SwitchStateException {
384 illegalMessageReceived(h, m);
385 }
386 @Override
387 void processOFStatisticsReply(OFChannelHandler h,
388 OFStatsReply m)
389 throws IOException, SwitchStateException {
390 log.error("Received multipart(stats) message sub-type {}",
391 m.getStatsType());
392 illegalMessageReceived(h, m);
393 }
394
395 @Override
396 void processOFError(OFChannelHandler h, OFErrorMsg m) {
397 logErrorDisconnect(h, m);
398 }
399
400 @Override
401 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
402 throws IOException {
403 h.pendingPortStatusMsg.add(m);
404 }
405 },
406
407
408 /**
409 * We are waiting for a OFDescriptionStat message from the switch.
410 * Once we receive any stat message we try to parse it. If it's not
411 * a description stats message we disconnect. If its the expected
412 * description stats message, we:
413 * - use the switch driver to bind the switch and get an IOFSwitch instance
414 * - setup the IOFSwitch instance
415 * - add switch controller and send the initial role
416 * request to the switch.
417 * Next state: WAIT_INITIAL_ROLE
418 * In the typical case, where switches support role request messages
419 * the next state is where we expect the role reply message.
420 * In the special case that where the switch does not support any kind
421 * of role request messages, we don't send a role message, but we do
422 * request mastership from the registry service. This controller
423 * should become master once we hear back from the registry service.
424 * All following states will have a h.sw instance!
425 */
426 WAIT_DESCRIPTION_STAT_REPLY(false) {
427 @Override
428 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
429 throws SwitchStateException {
430 // Read description, if it has been updated
431 if (m.getStatsType() != OFStatsType.DESC) {
432 log.warn("Expecting Description stats but received stats "
433 + "type {} from {}. Ignoring ...", m.getStatsType(),
434 h.channel.getRemoteAddress());
435 return;
436 }
tom7ef8ff92014-09-17 13:08:06 -0700437 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700438 log.info("Received switch description reply {} from switch at {}",
439 drep, h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700440 // Here is where we differentiate between different kinds of switches
441 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
442
443 h.sw.setOFVersion(h.ofVersion);
444 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700445 //h.sw.setPortDescReply(h.portDescReply);
446 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100447 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700448 h.sw.setConnected(true);
449 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700450// boolean success = h.sw.connectSwitch();
451//
452// if (!success) {
453// disconnectDuplicate(h);
454// return;
455// }
tom7ef8ff92014-09-17 13:08:06 -0700456 // set switch information
457
458
459
alshabib09d48be2014-10-03 15:43:33 -0700460 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800461 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700462 //Put switch in EQUAL mode until we hear back from the global registry
463 //log.debug("Setting new switch {} to EQUAL and sending Role request",
464 // h.sw.getStringId());
465 //h.sw.activateEqualSwitch();
466 //h.setSwitchRole(RoleState.EQUAL);
467
468 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700469 if (h.sw.isDriverHandshakeComplete()) {
470 if (!h.sw.connectSwitch()) {
471 disconnectDuplicate(h);
472 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800473 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700474 h.setState(ACTIVE);
475 } else {
476 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
477 }
tom7ef8ff92014-09-17 13:08:06 -0700478
479 }
480
481 @Override
482 void processOFError(OFChannelHandler h, OFErrorMsg m) {
483 logErrorDisconnect(h, m);
484 }
485
486 @Override
487 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
488 throws IOException, SwitchStateException {
489 illegalMessageReceived(h, m);
490 }
491
492 @Override
493 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
494 throws IOException {
495 h.pendingPortStatusMsg.add(m);
496 }
497 },
498
499
500 /**
501 * We are waiting for the respective switch driver to complete its
502 * configuration. Notice that we do not consider this to be part of the main
503 * switch-controller handshake. But we do consider it as a step that comes
504 * before we declare the switch as available to the controller.
505 * Next State: depends on the role of this controller for this switch - either
506 * MASTER or EQUAL.
507 */
508 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
509
510 @Override
511 void processOFError(OFChannelHandler h, OFErrorMsg m)
512 throws IOException {
513 // will never be called. We override processOFMessage
514 }
515
alshabibd7963912014-10-20 14:52:04 -0700516
517
tom7ef8ff92014-09-17 13:08:06 -0700518 @Override
519 void processOFMessage(OFChannelHandler h, OFMessage m)
520 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700521
522 if (h.sw.isDriverHandshakeComplete()) {
523 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700524 h.state.processOFMessage(h, m);
525 return;
alshabibd7963912014-10-20 14:52:04 -0700526
527 }
528
tom7ef8ff92014-09-17 13:08:06 -0700529 if (m.getType() == OFType.ECHO_REQUEST) {
530 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700531 } else if (m.getType() == OFType.ECHO_REPLY) {
532 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700533 } else if (m.getType() == OFType.ROLE_REPLY) {
534 h.sw.handleRole(m);
535 } else if (m.getType() == OFType.ERROR) {
536 if (!h.sw.handleRoleError((OFErrorMsg)m)) {
537 h.sw.processDriverHandshakeMessage(m);
538 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700539 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700540 }
541 }
542 } else {
543 if (m.getType() == OFType.EXPERIMENTER &&
544 ((OFExperimenter) m).getExperimenter() ==
545 RoleManager.NICIRA_EXPERIMENTER) {
546 h.sw.handleNiciraRole(m);
547 } else {
548 h.sw.processDriverHandshakeMessage(m);
549 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700550 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700551 }
552 }
553 }
554 }
555
556 @Override
557 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
558 throws IOException, SwitchStateException {
559 h.pendingPortStatusMsg.add(m);
560 }
alshabibd7963912014-10-20 14:52:04 -0700561
562 private void moveToActive(OFChannelHandler h) {
563 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800564 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700565 h.setState(ACTIVE);
566 if (!success) {
567 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700568 }
569 }
570
tom7ef8ff92014-09-17 13:08:06 -0700571 },
572
Jordi Ortiz91477b82016-11-29 15:22:50 +0100573 /**
574 * We are expecting a OF Multi Part Meter Features Stats Reply.
575 * Notice that this information is only available for switches running
576 * OpenFlow 1.3
577 */
578 WAIT_METER_FEATURES_REPLY(true) {
579 @Override
580 void processOFError(OFChannelHandler h, OFErrorMsg m)
581 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800582 // Hardware switches may reply OFErrorMsg if meter is not supported
583 log.warn("Received OFError {}. It seems {} does not support Meter.",
584 m.getErrType().name(), Dpid.uri(h.thisdpid));
585 log.debug("{}", m);
586 h.sendHandshakeDescriptionStatsRequest();
587 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100588 }
589
590 @Override
591 void processOFStatisticsReply(OFChannelHandler h,
592 OFStatsReply m)
593 throws IOException, SwitchStateException {
594 switch(m.getStatsType()) {
595 case METER_FEATURES:
596
597 log.debug("Received Meter Features");
598 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply)m;
599 log.info("Received meter features from {} with max meters: {}",
600 h.getSwitchInfoString(),
601 ofmfsr.getFeatures().getMaxMeter());
602 h.meterFeaturesReply = ofmfsr;
603 h.sendHandshakeDescriptionStatsRequest();
604 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
605 break;
606 default:
607 log.error("Unexpected OF Multi Part stats reply");
608 illegalMessageReceived(h, m);
609 break;
610 }
611 }
612
613 @Override
614 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
615 throws IOException, SwitchStateException {
616 illegalMessageReceived(h, m);
617 }
618
619 @Override
620 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
621 throws IOException {
622 h.pendingPortStatusMsg.add(m);
623 }
624 },
625
tom7ef8ff92014-09-17 13:08:06 -0700626
627 /**
628 * This controller is in MASTER role for this switch. We enter this state
629 * after requesting and winning control from the global registry.
630 * The main handshake as well as the switch-driver sub-handshake
631 * is complete at this point.
632 * // XXX S reconsider below
633 * In the (near) future we may deterministically assign controllers to
634 * switches at startup.
635 * We only leave this state if the switch disconnects or
636 * if we send a role request for SLAVE /and/ receive the role reply for
637 * SLAVE.
638 */
639 ACTIVE(true) {
640 @Override
641 void processOFError(OFChannelHandler h, OFErrorMsg m)
642 throws IOException, SwitchStateException {
643 // if we get here, then the error message is for something else
644 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700645 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700646 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700647 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700648 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700649 // We are the master controller and the switch returned
650 // a permission error. This is a likely indicator that
651 // the switch thinks we are slave. Reassert our
652 // role
653 // FIXME: this could be really bad during role transitions
654 // if two controllers are master (even if its only for
655 // a brief period). We might need to see if these errors
656 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700657
tom7ef8ff92014-09-17 13:08:06 -0700658 h.sw.reassertRole();
659 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
660 ((OFFlowModFailedErrorMsg) m).getCode() ==
661 OFFlowModFailedCode.ALL_TABLES_FULL) {
662 h.sw.setTableFull(true);
663 } else {
664 logError(h, m);
665 }
666 h.dispatchMessage(m);
667 }
668
669 @Override
670 void processOFStatisticsReply(OFChannelHandler h,
671 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700672 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
673 h.sw.setPortDescReply((OFPortDescStatsReply) m);
674 }
tom7ef8ff92014-09-17 13:08:06 -0700675 h.dispatchMessage(m);
676 }
677
678 @Override
679 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
680 throws SwitchStateException {
681 h.sw.handleNiciraRole(m);
682 }
683
684 @Override
685 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
686 throws SwitchStateException {
687 h.sw.handleRole(m);
688 }
689
690 @Override
691 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
692 throws SwitchStateException {
693 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800694 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700695 }
696
697 @Override
698 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700699// OFPacketOut out =
700// h.sw.factory().buildPacketOut()
701// .setXid(m.getXid())
702// .setBufferId(m.getBufferId()).build();
703// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700704 h.dispatchMessage(m);
705 }
706
707 @Override
708 void processOFFlowRemoved(OFChannelHandler h,
709 OFFlowRemoved m) {
710 h.dispatchMessage(m);
711 }
712
713 @Override
714 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
715 h.dispatchMessage(m);
716 }
717
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700718 @Override
719 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700720 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700721 h.dispatchMessage(m);
722 }
723
tom7ef8ff92014-09-17 13:08:06 -0700724 };
725
726 private final boolean handshakeComplete;
727 ChannelState(boolean handshakeComplete) {
728 this.handshakeComplete = handshakeComplete;
729 }
730
731 /**
732 * Is this a state in which the handshake has completed?
733 * @return true if the handshake is complete
734 */
735 public boolean isHandshakeComplete() {
736 return handshakeComplete;
737 }
738
739 /**
740 * Get a string specifying the switch connection, state, and
741 * message received. To be used as message for SwitchStateException
742 * or log messages
743 * @param h The channel handler (to get switch information_
744 * @param m The OFMessage that has just been received
745 * @param details A string giving more details about the exact nature
746 * of the problem.
747 * @return display string
748 */
749 // needs to be protected because enum members are actually subclasses
750 protected String getSwitchStateMessage(OFChannelHandler h,
751 OFMessage m,
752 String details) {
753 return String.format("Switch: [%s], State: [%s], received: [%s]"
754 + ", details: %s",
755 h.getSwitchInfoString(),
756 this.toString(),
757 m.getType().toString(),
758 details);
759 }
760
761 /**
762 * We have an OFMessage we didn't expect given the current state and
763 * we want to treat this as an error.
764 * We currently throw an exception that will terminate the connection
765 * However, we could be more forgiving
766 * @param h the channel handler that received the message
767 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700768 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700769 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700770 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700771 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
772 throws SwitchStateException {
773 String msg = getSwitchStateMessage(h, m,
774 "Switch should never send this message in the current state");
775 throw new SwitchStateException(msg);
776
777 }
778
779 /**
780 * We have an OFMessage we didn't expect given the current state and
781 * we want to ignore the message.
782 * @param h the channel handler the received the message
783 * @param m the message
784 */
785 protected void unhandledMessageReceived(OFChannelHandler h,
786 OFMessage m) {
787 if (log.isDebugEnabled()) {
788 String msg = getSwitchStateMessage(h, m,
789 "Ignoring unexpected message");
790 log.debug(msg);
791 }
792 }
793
794 /**
795 * Log an OpenFlow error message from a switch.
796 * @param h The switch that sent the error
797 * @param error The error message
798 */
799 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700800 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700801 error,
802 h.getSwitchInfoString(),
Ray Milkey6bc43c22015-11-06 13:22:38 -0800803 this.toString());
tom7ef8ff92014-09-17 13:08:06 -0700804 }
805
806 /**
807 * Log an OpenFlow error message from a switch and disconnect the
808 * channel.
809 *
810 * @param h the IO channel for this switch.
811 * @param error The error message
812 */
813 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
814 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700815 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700816 h.channel.disconnect();
817 }
818
819 /**
820 * log an error message for a duplicate dpid and disconnect this channel.
821 * @param h the IO channel for this switch.
822 */
823 protected void disconnectDuplicate(OFChannelHandler h) {
824 log.error("Duplicated dpid or incompleted cleanup - "
825 + "disconnecting channel {}", h.getSwitchInfoString());
826 h.duplicateDpidFound = Boolean.TRUE;
827 h.channel.disconnect();
828 }
829
830
831
832 /**
833 * Handles all pending port status messages before a switch is declared
834 * activated in MASTER or EQUAL role. Note that since this handling
835 * precedes the activation (and therefore notification to IOFSwitchListerners)
836 * the changes to ports will already be visible once the switch is
837 * activated. As a result, no notifications are sent out for these
838 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700839 *
840 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700841 */
842 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
843 try {
844 handlePendingPortStatusMessages(h, 0);
845 } catch (SwitchStateException e) {
846 log.error(e.getMessage());
847 }
848 }
849
850 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
851 throws SwitchStateException {
852 if (h.sw == null) {
853 String msg = "State machine error: switch is null. Should never " +
854 "happen";
855 throw new SwitchStateException(msg);
856 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800857 log.info("Processing {} pending port status messages for {}",
858 h.pendingPortStatusMsg.size(), h.sw.getStringId());
859
tom7ef8ff92014-09-17 13:08:06 -0700860 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
861 for (OFPortStatus ps: h.pendingPortStatusMsg) {
862 temp.add(ps);
863 handlePortStatusMessage(h, ps, false);
864 }
tom7ef8ff92014-09-17 13:08:06 -0700865 // expensive but ok - we don't expect too many port-status messages
866 // note that we cannot use clear(), because of the reasons below
867 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800868 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700869 // the iterator above takes a snapshot of the list - so while we were
870 // dealing with the pending port-status messages, we could have received
871 // newer ones. Handle them recursively, but break the recursion after
872 // five steps to avoid an attack.
873 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
874 handlePendingPortStatusMessages(h, index);
875 }
876 }
877
878 /**
879 * Handle a port status message.
880 *
881 * Handle a port status message by updating the port maps in the
882 * IOFSwitch instance and notifying Controller about the change so
883 * it can dispatch a switch update.
884 *
885 * @param h The OFChannelHhandler that received the message
886 * @param m The PortStatus message we received
887 * @param doNotify if true switch port changed events will be
888 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800889 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700890 *
891 */
892 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
893 boolean doNotify) throws SwitchStateException {
894 if (h.sw == null) {
895 String msg = getSwitchStateMessage(h, m,
896 "State machine error: switch is null. Should never " +
897 "happen");
898 throw new SwitchStateException(msg);
899 }
900
901 h.sw.handleMessage(m);
902 }
903
904
905 /**
906 * Process an OF message received on the channel and
907 * update state accordingly.
908 *
909 * The main "event" of the state machine. Process the received message,
910 * send follow up message if required and update state if required.
911 *
912 * Switches on the message type and calls more specific event handlers
913 * for each individual OF message type. If we receive a message that
914 * is supposed to be sent from a controller to a switch we throw
915 * a SwitchStateExeption.
916 *
917 * The more specific handlers can also throw SwitchStateExceptions
918 *
919 * @param h The OFChannelHandler that received the message
920 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800921 * @throws SwitchStateException if the switch is not bound to the channel
922 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700923 */
924 void processOFMessage(OFChannelHandler h, OFMessage m)
925 throws IOException, SwitchStateException {
926 switch(m.getType()) {
927 case HELLO:
928 processOFHello(h, (OFHello) m);
929 break;
930 case BARRIER_REPLY:
931 processOFBarrierReply(h, (OFBarrierReply) m);
932 break;
933 case ECHO_REPLY:
934 processOFEchoReply(h, (OFEchoReply) m);
935 break;
936 case ECHO_REQUEST:
937 processOFEchoRequest(h, (OFEchoRequest) m);
938 break;
939 case ERROR:
940 processOFError(h, (OFErrorMsg) m);
941 break;
942 case FEATURES_REPLY:
943 processOFFeaturesReply(h, (OFFeaturesReply) m);
944 break;
945 case FLOW_REMOVED:
946 processOFFlowRemoved(h, (OFFlowRemoved) m);
947 break;
948 case GET_CONFIG_REPLY:
949 processOFGetConfigReply(h, (OFGetConfigReply) m);
950 break;
951 case PACKET_IN:
952 processOFPacketIn(h, (OFPacketIn) m);
953 break;
954 case PORT_STATUS:
955 processOFPortStatus(h, (OFPortStatus) m);
956 break;
957 case QUEUE_GET_CONFIG_REPLY:
958 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
959 break;
960 case STATS_REPLY: // multipart_reply in 1.3
961 processOFStatisticsReply(h, (OFStatsReply) m);
962 break;
963 case EXPERIMENTER:
964 processOFExperimenter(h, (OFExperimenter) m);
965 break;
966 case ROLE_REPLY:
967 processOFRoleReply(h, (OFRoleReply) m);
968 break;
969 case GET_ASYNC_REPLY:
970 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
971 break;
972
973 // The following messages are sent to switches. The controller
974 // should never receive them
975 case SET_CONFIG:
976 case GET_CONFIG_REQUEST:
977 case PACKET_OUT:
978 case PORT_MOD:
979 case QUEUE_GET_CONFIG_REQUEST:
980 case BARRIER_REQUEST:
981 case STATS_REQUEST: // multipart request in 1.3
982 case FEATURES_REQUEST:
983 case FLOW_MOD:
984 case GROUP_MOD:
985 case TABLE_MOD:
986 case GET_ASYNC_REQUEST:
987 case SET_ASYNC:
988 case METER_MOD:
989 default:
990 illegalMessageReceived(h, m);
991 break;
992 }
993 }
994
995 /*-----------------------------------------------------------------
996 * Default implementation for message handlers in any state.
997 *
998 * Individual states must override these if they want a behavior
999 * that differs from the default.
1000 *
1001 * In general, these handlers simply ignore the message and do
1002 * nothing.
1003 *
1004 * There are some exceptions though, since some messages really
1005 * are handled the same way in every state (e.g., ECHO_REQUST) or
1006 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1007 -----------------------------------------------------------------*/
1008
1009 void processOFHello(OFChannelHandler h, OFHello m)
1010 throws IOException, SwitchStateException {
1011 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001012 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
1013 h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001014 }
1015
1016 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1017 throws IOException {
1018 // Silently ignore.
1019 }
1020
1021 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1022 throws IOException {
1023 if (h.ofVersion == null) {
1024 log.error("No OF version set for {}. Not sending Echo REPLY",
1025 h.channel.getRemoteAddress());
1026 return;
1027 }
1028 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
1029 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
1030 OFEchoReply reply = factory
1031 .buildEchoReply()
1032 .setXid(m.getXid())
1033 .setData(m.getData())
1034 .build();
1035 h.channel.write(Collections.singletonList(reply));
1036 }
1037
1038 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1039 throws IOException {
1040 // Do nothing with EchoReplies !!
1041 }
1042
1043 // no default implementation for OFError
1044 // every state must override it
1045 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1046 throws IOException, SwitchStateException;
1047
1048
1049 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1050 throws IOException, SwitchStateException {
1051 unhandledMessageReceived(h, m);
1052 }
1053
1054 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1055 throws IOException {
1056 unhandledMessageReceived(h, m);
1057 }
1058
1059 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1060 throws IOException, SwitchStateException {
1061 // we only expect config replies in the WAIT_CONFIG_REPLY state
1062 illegalMessageReceived(h, m);
1063 }
1064
1065 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1066 throws IOException {
1067 unhandledMessageReceived(h, m);
1068 }
1069
1070 // no default implementation. Every state needs to handle it.
1071 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1072 throws IOException, SwitchStateException;
1073
1074 void processOFQueueGetConfigReply(OFChannelHandler h,
1075 OFQueueGetConfigReply m)
1076 throws IOException {
1077 unhandledMessageReceived(h, m);
1078 }
1079
1080 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1081 throws IOException, SwitchStateException {
1082 unhandledMessageReceived(h, m);
1083 }
1084
1085 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1086 throws IOException, SwitchStateException {
1087 // TODO: it might make sense to parse the vendor message here
1088 // into the known vendor messages we support and then call more
1089 // specific event handlers
1090 unhandledMessageReceived(h, m);
1091 }
1092
1093 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1094 throws SwitchStateException, IOException {
1095 unhandledMessageReceived(h, m);
1096 }
1097
1098 void processOFGetAsyncReply(OFChannelHandler h,
1099 OFAsyncGetReply m) {
1100 unhandledMessageReceived(h, m);
1101 }
1102
1103 }
1104
1105
1106
1107 //*************************
1108 // Channel handler methods
1109 //*************************
1110
1111 @Override
1112 public void channelConnected(ChannelHandlerContext ctx,
1113 ChannelStateEvent e) throws Exception {
1114 channel = e.getChannel();
1115 log.info("New switch connection from {}",
1116 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001117 /*
1118 hack to wait for the switch to tell us what it's
1119 max version is. This is not spec compliant and should
1120 be removed as soon as switches behave better.
1121 */
1122 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001123 setState(ChannelState.WAIT_HELLO);
1124 }
1125
1126 @Override
1127 public void channelDisconnected(ChannelHandlerContext ctx,
1128 ChannelStateEvent e) throws Exception {
1129 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1130 getSwitchInfoString());
1131 if (thisdpid != 0) {
1132 if (!duplicateDpidFound) {
1133 // if the disconnected switch (on this ChannelHandler)
1134 // was not one with a duplicate-dpid, it is safe to remove all
1135 // state for it at the controller. Notice that if the disconnected
1136 // switch was a duplicate-dpid, calling the method below would clear
1137 // all state for the original switch (with the same dpid),
1138 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001139 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001140 if (sw != null) {
1141 sw.removeConnectedSwitch();
1142 }
tom7ef8ff92014-09-17 13:08:06 -07001143 } else {
1144 // A duplicate was disconnected on this ChannelHandler,
1145 // this is the same switch reconnecting, but the original state was
1146 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001147 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001148 duplicateDpidFound = Boolean.FALSE;
1149 }
1150 } else {
1151 log.warn("no dpid in channelHandler registered for "
1152 + "disconnected switch {}", getSwitchInfoString());
1153 }
1154 }
1155
1156 @Override
1157 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1158 throws Exception {
1159 if (e.getCause() instanceof ReadTimeoutException) {
1160 // switch timeout
1161 log.error("Disconnecting switch {} due to read timeout",
1162 getSwitchInfoString());
1163 ctx.getChannel().close();
1164 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1165 log.error("Disconnecting switch {}: failed to complete handshake",
1166 getSwitchInfoString());
1167 ctx.getChannel().close();
1168 } else if (e.getCause() instanceof ClosedChannelException) {
1169 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1170 } else if (e.getCause() instanceof IOException) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001171 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1172 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1173 log.error("Disconnecting switch {} due to IO Error: {}",
1174 getSwitchInfoString(), e.getCause().getMessage());
1175 if (log.isDebugEnabled()) {
1176 // still print stack trace if debug is enabled
1177 log.debug("StackTrace for previous Exception: ", e.getCause());
1178 }
tom7ef8ff92014-09-17 13:08:06 -07001179 }
1180 ctx.getChannel().close();
1181 } else if (e.getCause() instanceof SwitchStateException) {
1182 log.error("Disconnecting switch {} due to switch state error: {}",
1183 getSwitchInfoString(), e.getCause().getMessage());
1184 if (log.isDebugEnabled()) {
1185 // still print stack trace if debug is enabled
1186 log.debug("StackTrace for previous Exception: ", e.getCause());
1187 }
1188 ctx.getChannel().close();
1189 } else if (e.getCause() instanceof OFParseError) {
1190 log.error("Disconnecting switch "
1191 + getSwitchInfoString() +
1192 " due to message parse failure",
1193 e.getCause());
1194 ctx.getChannel().close();
1195 } else if (e.getCause() instanceof RejectedExecutionException) {
1196 log.warn("Could not process message: queue full");
1197 } else {
1198 log.error("Error while processing message from switch "
1199 + getSwitchInfoString()
1200 + "state " + this.state, e.getCause());
1201 ctx.getChannel().close();
1202 }
1203 }
1204
1205 @Override
1206 public String toString() {
1207 return getSwitchInfoString();
1208 }
1209
1210 @Override
1211 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1212 throws Exception {
1213 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1214 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001215 log.debug("Sending Echo Request on idle channel: {}",
tom7ef8ff92014-09-17 13:08:06 -07001216 e.getChannel().getPipeline().getLast().toString());
1217 e.getChannel().write(Collections.singletonList(m));
1218 // XXX S some problems here -- echo request has no transaction id, and
1219 // echo reply is not correlated to the echo request.
1220 }
1221
1222 @Override
1223 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1224 throws Exception {
1225 if (e.getMessage() instanceof List) {
1226 @SuppressWarnings("unchecked")
1227 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1228
1229
1230 for (OFMessage ofm : msglist) {
1231 // Do the actual packet processing
1232 state.processOFMessage(this, ofm);
1233 }
1234 } else {
1235 state.processOFMessage(this, (OFMessage) e.getMessage());
1236 }
1237 }
1238
1239
1240
1241 //*************************
1242 // Channel utility methods
1243 //*************************
1244
1245 /**
1246 * Is this a state in which the handshake has completed?
1247 * @return true if the handshake is complete
1248 */
1249 public boolean isHandshakeComplete() {
1250 return this.state.isHandshakeComplete();
1251 }
1252
1253 private void dispatchMessage(OFMessage m) {
1254 sw.handleMessage(m);
1255 }
1256
1257 /**
1258 * Return a string describing this switch based on the already available
1259 * information (DPID and/or remote socket).
1260 * @return display string
1261 */
1262 private String getSwitchInfoString() {
1263 if (sw != null) {
1264 return sw.toString();
1265 }
1266 String channelString;
1267 if (channel == null || channel.getRemoteAddress() == null) {
1268 channelString = "?";
1269 } else {
1270 channelString = channel.getRemoteAddress().toString();
1271 }
1272 String dpidString;
1273 if (featuresReply == null) {
1274 dpidString = "?";
1275 } else {
1276 dpidString = featuresReply.getDatapathId().toString();
1277 }
1278 return String.format("[%s DPID[%s]]", channelString, dpidString);
1279 }
1280
1281 /**
1282 * Update the channels state. Only called from the state machine.
1283 * TODO: enforce restricted state transitions
1284 * @param state
1285 */
1286 private void setState(ChannelState state) {
1287 this.state = state;
1288 }
1289
1290 /**
1291 * Send hello message to the switch using the handshake transactions ids.
1292 * @throws IOException
1293 */
1294 private void sendHandshakeHelloMessage() throws IOException {
1295 // The OF protocol requires us to start things off by sending the highest
1296 // version of the protocol supported.
1297
1298 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1299 // see Sec. 7.5.1 of the OF1.3.4 spec
1300 U32 bitmap = U32.ofRaw(0x00000012);
1301 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1302 .setBitmaps(Collections.singletonList(bitmap))
1303 .build();
1304 OFMessage.Builder mb = factory13.buildHello()
1305 .setXid(this.handshakeTransactionIds--)
1306 .setElements(Collections.singletonList(hem));
1307 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1308 channel.write(Collections.singletonList(mb.build()));
1309 }
1310
1311 /**
1312 * Send featuresRequest msg to the switch using the handshake transactions ids.
1313 * @throws IOException
1314 */
1315 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1316 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1317 OFMessage m = factory.buildFeaturesRequest()
1318 .setXid(this.handshakeTransactionIds--)
1319 .build();
1320 channel.write(Collections.singletonList(m));
1321 }
1322
1323 /**
1324 * Send the configuration requests to tell the switch we want full
1325 * packets.
1326 * @throws IOException
1327 */
1328 private void sendHandshakeSetConfig() throws IOException {
1329 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1330 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1331 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1332
1333 // Ensure we receive the full packet via PacketIn
1334 // FIXME: We don't set the reassembly flags.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001335 // Only send config to switches to send full packets, if they have a buffer.
1336 // Saves a packet & OFSetConfig can't be handled by certain switches.
1337 if(this.featuresReply.getNBuffers() > 0) {
1338 OFSetConfig sc = factory
1339 .buildSetConfig()
1340 .setMissSendLen((short) 0xffff)
1341 .setXid(this.handshakeTransactionIds--)
1342 .build();
1343 msglist.add(sc);
1344 }
tom7ef8ff92014-09-17 13:08:06 -07001345
1346 // Barrier
1347 OFBarrierRequest br = factory
1348 .buildBarrierRequest()
1349 .setXid(this.handshakeTransactionIds--)
1350 .build();
1351 msglist.add(br);
1352
1353 // Verify (need barrier?)
1354 OFGetConfigRequest gcr = factory
1355 .buildGetConfigRequest()
1356 .setXid(this.handshakeTransactionIds--)
1357 .build();
1358 msglist.add(gcr);
1359 channel.write(msglist);
1360 }
1361
1362 /**
1363 * send a description state request.
1364 * @throws IOException
1365 */
1366 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1367 // Get Description to set switch-specific flags
1368 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1369 OFDescStatsRequest dreq = factory
1370 .buildDescStatsRequest()
1371 .setXid(handshakeTransactionIds--)
1372 .build();
1373 channel.write(Collections.singletonList(dreq));
1374 }
1375
Jordi Ortiz91477b82016-11-29 15:22:50 +01001376 /**
1377 * send a meter features request
1378 * @throws IOException
1379 */
1380 private void sendMeterFeaturesRequest() throws IOException {
1381 // Get meter features including the MaxMeters value available for the device
1382 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1383 OFMeterFeaturesStatsRequest mfreq = factory
1384 .buildMeterFeaturesStatsRequest()
1385 .setXid(handshakeTransactionIds--)
1386 .build();
1387 channel.write(Collections.singletonList(mfreq));
1388 }
1389
tom7ef8ff92014-09-17 13:08:06 -07001390 private void sendHandshakeOFPortDescRequest() throws IOException {
1391 // Get port description for 1.3 switch
1392 OFPortDescStatsRequest preq = factory13
1393 .buildPortDescStatsRequest()
1394 .setXid(handshakeTransactionIds--)
1395 .build();
1396 channel.write(Collections.singletonList(preq));
1397 }
1398
1399 ChannelState getStateForTesting() {
1400 return state;
1401 }
1402
1403}