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