blob: eedfaf22b24a6a28b4243e8668d61dba68d621bd [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
Brian O'Connorabafb502014-12-02 22:26:20 -080017package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070018
19import java.io.IOException;
20import java.nio.channels.ClosedChannelException;
21import java.util.ArrayList;
22import java.util.Collections;
23import java.util.List;
24import java.util.concurrent.CopyOnWriteArrayList;
25import java.util.concurrent.RejectedExecutionException;
26
27import org.jboss.netty.channel.Channel;
28import org.jboss.netty.channel.ChannelHandlerContext;
29import org.jboss.netty.channel.ChannelStateEvent;
30import org.jboss.netty.channel.ExceptionEvent;
31import org.jboss.netty.channel.MessageEvent;
32import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
33import org.jboss.netty.handler.timeout.IdleStateEvent;
34import org.jboss.netty.handler.timeout.ReadTimeoutException;
Charles Chan34155e52016-11-30 18:28:11 -080035import org.onosproject.openflow.controller.Dpid;
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;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800131 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
132 this.portDescReplies = new ArrayList<>();
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());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800309 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700310 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800311 } else {
312 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700313 }
314 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
tom7ef8ff92014-09-17 13:08:06 -0700315 log.info("Received port desc reply for switch at {}",
316 h.getSwitchInfoString());
317 try {
318 h.sendHandshakeSetConfig();
319 } catch (IOException e) {
320 log.error("Unable to send setConfig after PortDescReply. "
321 + "Error: {}", e.getMessage());
322 }
323 h.setState(WAIT_CONFIG_REPLY);
324 }
325
326 @Override
327 void processOFError(OFChannelHandler h, OFErrorMsg m)
328 throws IOException, SwitchStateException {
329 logErrorDisconnect(h, m);
330
331 }
332
333 @Override
334 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
335 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800336 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700337
338 }
339 },
340
341 /**
342 * We are waiting for a config reply message. Once we receive it
343 * we send a DescriptionStatsRequest to the switch.
344 * Next state: WAIT_DESCRIPTION_STAT_REPLY
345 */
346 WAIT_CONFIG_REPLY(false) {
347 @Override
348 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
349 throws IOException {
350 if (m.getMissSendLen() == 0xffff) {
351 log.trace("Config Reply from switch {} confirms "
352 + "miss length set to 0xffff",
353 h.getSwitchInfoString());
354 } else {
355 // FIXME: we can't really deal with switches that don't send
356 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800357 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700358 + "miss length set to {}",
359 h.getSwitchInfoString(),
360 m.getMissSendLen());
361 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100362
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800363 // TODO should this check if 1.3 or later?
Jordi Ortiz91477b82016-11-29 15:22:50 +0100364 if (h.ofVersion == OFVersion.OF_13) {
365 // Meters were introduced in OpenFlow 1.3
366 h.sendMeterFeaturesRequest();
367 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800368 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100369 h.sendHandshakeDescriptionStatsRequest();
370 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
371 }
tom7ef8ff92014-09-17 13:08:06 -0700372 }
373
374 @Override
375 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
376 // do nothing;
377 }
378
379 @Override
380 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
381 throws IOException, SwitchStateException {
382 illegalMessageReceived(h, m);
383 }
384 @Override
385 void processOFStatisticsReply(OFChannelHandler h,
386 OFStatsReply m)
387 throws IOException, SwitchStateException {
388 log.error("Received multipart(stats) message sub-type {}",
389 m.getStatsType());
390 illegalMessageReceived(h, m);
391 }
392
393 @Override
394 void processOFError(OFChannelHandler h, OFErrorMsg m) {
395 logErrorDisconnect(h, m);
396 }
397
398 @Override
399 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
400 throws IOException {
401 h.pendingPortStatusMsg.add(m);
402 }
403 },
404
405
406 /**
407 * We are waiting for a OFDescriptionStat message from the switch.
408 * Once we receive any stat message we try to parse it. If it's not
409 * a description stats message we disconnect. If its the expected
410 * description stats message, we:
411 * - use the switch driver to bind the switch and get an IOFSwitch instance
412 * - setup the IOFSwitch instance
413 * - add switch controller and send the initial role
414 * request to the switch.
415 * Next state: WAIT_INITIAL_ROLE
416 * In the typical case, where switches support role request messages
417 * the next state is where we expect the role reply message.
418 * In the special case that where the switch does not support any kind
419 * of role request messages, we don't send a role message, but we do
420 * request mastership from the registry service. This controller
421 * should become master once we hear back from the registry service.
422 * All following states will have a h.sw instance!
423 */
424 WAIT_DESCRIPTION_STAT_REPLY(false) {
425 @Override
426 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
427 throws SwitchStateException {
428 // Read description, if it has been updated
429 if (m.getStatsType() != OFStatsType.DESC) {
430 log.warn("Expecting Description stats but received stats "
431 + "type {} from {}. Ignoring ...", m.getStatsType(),
432 h.channel.getRemoteAddress());
433 return;
434 }
tom7ef8ff92014-09-17 13:08:06 -0700435 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700436 log.info("Received switch description reply {} from switch at {}",
437 drep, h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700438 // Here is where we differentiate between different kinds of switches
439 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
440
441 h.sw.setOFVersion(h.ofVersion);
442 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700443 //h.sw.setPortDescReply(h.portDescReply);
444 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100445 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700446 h.sw.setConnected(true);
447 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700448// boolean success = h.sw.connectSwitch();
449//
450// if (!success) {
451// disconnectDuplicate(h);
452// return;
453// }
tom7ef8ff92014-09-17 13:08:06 -0700454 // set switch information
455
456
457
alshabib09d48be2014-10-03 15:43:33 -0700458 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800459 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700460 //Put switch in EQUAL mode until we hear back from the global registry
461 //log.debug("Setting new switch {} to EQUAL and sending Role request",
462 // h.sw.getStringId());
463 //h.sw.activateEqualSwitch();
464 //h.setSwitchRole(RoleState.EQUAL);
465
466 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700467 if (h.sw.isDriverHandshakeComplete()) {
468 if (!h.sw.connectSwitch()) {
469 disconnectDuplicate(h);
470 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800471 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700472 h.setState(ACTIVE);
473 } else {
474 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
475 }
tom7ef8ff92014-09-17 13:08:06 -0700476
477 }
478
479 @Override
480 void processOFError(OFChannelHandler h, OFErrorMsg m) {
481 logErrorDisconnect(h, m);
482 }
483
484 @Override
485 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
486 throws IOException, SwitchStateException {
487 illegalMessageReceived(h, m);
488 }
489
490 @Override
491 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
492 throws IOException {
493 h.pendingPortStatusMsg.add(m);
494 }
495 },
496
497
498 /**
499 * We are waiting for the respective switch driver to complete its
500 * configuration. Notice that we do not consider this to be part of the main
501 * switch-controller handshake. But we do consider it as a step that comes
502 * before we declare the switch as available to the controller.
503 * Next State: depends on the role of this controller for this switch - either
504 * MASTER or EQUAL.
505 */
506 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
507
508 @Override
509 void processOFError(OFChannelHandler h, OFErrorMsg m)
510 throws IOException {
511 // will never be called. We override processOFMessage
512 }
513
alshabibd7963912014-10-20 14:52:04 -0700514
515
tom7ef8ff92014-09-17 13:08:06 -0700516 @Override
517 void processOFMessage(OFChannelHandler h, OFMessage m)
518 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700519
520 if (h.sw.isDriverHandshakeComplete()) {
521 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700522 h.state.processOFMessage(h, m);
523 return;
alshabibd7963912014-10-20 14:52:04 -0700524
525 }
526
tom7ef8ff92014-09-17 13:08:06 -0700527 if (m.getType() == OFType.ECHO_REQUEST) {
528 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700529 } else if (m.getType() == OFType.ECHO_REPLY) {
530 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700531 } else if (m.getType() == OFType.ROLE_REPLY) {
532 h.sw.handleRole(m);
533 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800534 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700535 h.sw.processDriverHandshakeMessage(m);
536 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700537 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700538 }
539 }
540 } else {
541 if (m.getType() == OFType.EXPERIMENTER &&
542 ((OFExperimenter) m).getExperimenter() ==
543 RoleManager.NICIRA_EXPERIMENTER) {
544 h.sw.handleNiciraRole(m);
545 } else {
546 h.sw.processDriverHandshakeMessage(m);
547 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700548 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700549 }
550 }
551 }
552 }
553
554 @Override
555 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
556 throws IOException, SwitchStateException {
557 h.pendingPortStatusMsg.add(m);
558 }
alshabibd7963912014-10-20 14:52:04 -0700559
560 private void moveToActive(OFChannelHandler h) {
561 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800562 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700563 h.setState(ACTIVE);
564 if (!success) {
565 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700566 }
567 }
568
tom7ef8ff92014-09-17 13:08:06 -0700569 },
570
Jordi Ortiz91477b82016-11-29 15:22:50 +0100571 /**
572 * We are expecting a OF Multi Part Meter Features Stats Reply.
573 * Notice that this information is only available for switches running
574 * OpenFlow 1.3
575 */
576 WAIT_METER_FEATURES_REPLY(true) {
577 @Override
578 void processOFError(OFChannelHandler h, OFErrorMsg m)
579 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800580 // Hardware switches may reply OFErrorMsg if meter is not supported
581 log.warn("Received OFError {}. It seems {} does not support Meter.",
582 m.getErrType().name(), Dpid.uri(h.thisdpid));
583 log.debug("{}", m);
584 h.sendHandshakeDescriptionStatsRequest();
585 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100586 }
587
588 @Override
589 void processOFStatisticsReply(OFChannelHandler h,
590 OFStatsReply m)
591 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800592 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100593 case METER_FEATURES:
594
595 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800596 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100597 log.info("Received meter features from {} with max meters: {}",
598 h.getSwitchInfoString(),
599 ofmfsr.getFeatures().getMaxMeter());
600 h.meterFeaturesReply = ofmfsr;
601 h.sendHandshakeDescriptionStatsRequest();
602 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
603 break;
604 default:
605 log.error("Unexpected OF Multi Part stats reply");
606 illegalMessageReceived(h, m);
607 break;
608 }
609 }
610
611 @Override
612 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
613 throws IOException, SwitchStateException {
614 illegalMessageReceived(h, m);
615 }
616
617 @Override
618 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
619 throws IOException {
620 h.pendingPortStatusMsg.add(m);
621 }
622 },
623
tom7ef8ff92014-09-17 13:08:06 -0700624
625 /**
626 * This controller is in MASTER role for this switch. We enter this state
627 * after requesting and winning control from the global registry.
628 * The main handshake as well as the switch-driver sub-handshake
629 * is complete at this point.
630 * // XXX S reconsider below
631 * In the (near) future we may deterministically assign controllers to
632 * switches at startup.
633 * We only leave this state if the switch disconnects or
634 * if we send a role request for SLAVE /and/ receive the role reply for
635 * SLAVE.
636 */
637 ACTIVE(true) {
638 @Override
639 void processOFError(OFChannelHandler h, OFErrorMsg m)
640 throws IOException, SwitchStateException {
641 // if we get here, then the error message is for something else
642 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700643 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700644 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700645 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700646 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700647 // We are the master controller and the switch returned
648 // a permission error. This is a likely indicator that
649 // the switch thinks we are slave. Reassert our
650 // role
651 // FIXME: this could be really bad during role transitions
652 // if two controllers are master (even if its only for
653 // a brief period). We might need to see if these errors
654 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700655
tom7ef8ff92014-09-17 13:08:06 -0700656 h.sw.reassertRole();
657 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
658 ((OFFlowModFailedErrorMsg) m).getCode() ==
659 OFFlowModFailedCode.ALL_TABLES_FULL) {
660 h.sw.setTableFull(true);
661 } else {
662 logError(h, m);
663 }
664 h.dispatchMessage(m);
665 }
666
667 @Override
668 void processOFStatisticsReply(OFChannelHandler h,
669 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700670 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
671 h.sw.setPortDescReply((OFPortDescStatsReply) m);
672 }
tom7ef8ff92014-09-17 13:08:06 -0700673 h.dispatchMessage(m);
674 }
675
676 @Override
677 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
678 throws SwitchStateException {
679 h.sw.handleNiciraRole(m);
680 }
681
682 @Override
683 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
684 throws SwitchStateException {
685 h.sw.handleRole(m);
686 }
687
688 @Override
689 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
690 throws SwitchStateException {
691 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800692 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700693 }
694
695 @Override
696 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700697// OFPacketOut out =
698// h.sw.factory().buildPacketOut()
699// .setXid(m.getXid())
700// .setBufferId(m.getBufferId()).build();
701// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700702 h.dispatchMessage(m);
703 }
704
705 @Override
706 void processOFFlowRemoved(OFChannelHandler h,
707 OFFlowRemoved m) {
708 h.dispatchMessage(m);
709 }
710
711 @Override
712 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
713 h.dispatchMessage(m);
714 }
715
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700716 @Override
717 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700718 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700719 h.dispatchMessage(m);
720 }
721
tom7ef8ff92014-09-17 13:08:06 -0700722 };
723
724 private final boolean handshakeComplete;
725 ChannelState(boolean handshakeComplete) {
726 this.handshakeComplete = handshakeComplete;
727 }
728
729 /**
730 * Is this a state in which the handshake has completed?
731 * @return true if the handshake is complete
732 */
733 public boolean isHandshakeComplete() {
734 return handshakeComplete;
735 }
736
737 /**
738 * Get a string specifying the switch connection, state, and
739 * message received. To be used as message for SwitchStateException
740 * or log messages
741 * @param h The channel handler (to get switch information_
742 * @param m The OFMessage that has just been received
743 * @param details A string giving more details about the exact nature
744 * of the problem.
745 * @return display string
746 */
747 // needs to be protected because enum members are actually subclasses
748 protected String getSwitchStateMessage(OFChannelHandler h,
749 OFMessage m,
750 String details) {
751 return String.format("Switch: [%s], State: [%s], received: [%s]"
752 + ", details: %s",
753 h.getSwitchInfoString(),
754 this.toString(),
755 m.getType().toString(),
756 details);
757 }
758
759 /**
760 * We have an OFMessage we didn't expect given the current state and
761 * we want to treat this as an error.
762 * We currently throw an exception that will terminate the connection
763 * However, we could be more forgiving
764 * @param h the channel handler that received the message
765 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700766 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700767 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700768 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700769 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
770 throws SwitchStateException {
771 String msg = getSwitchStateMessage(h, m,
772 "Switch should never send this message in the current state");
773 throw new SwitchStateException(msg);
774
775 }
776
777 /**
778 * We have an OFMessage we didn't expect given the current state and
779 * we want to ignore the message.
780 * @param h the channel handler the received the message
781 * @param m the message
782 */
783 protected void unhandledMessageReceived(OFChannelHandler h,
784 OFMessage m) {
785 if (log.isDebugEnabled()) {
786 String msg = getSwitchStateMessage(h, m,
787 "Ignoring unexpected message");
788 log.debug(msg);
789 }
790 }
791
792 /**
793 * Log an OpenFlow error message from a switch.
794 * @param h The switch that sent the error
795 * @param error The error message
796 */
797 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700798 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700799 error,
800 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800801 this);
tom7ef8ff92014-09-17 13:08:06 -0700802 }
803
804 /**
805 * Log an OpenFlow error message from a switch and disconnect the
806 * channel.
807 *
808 * @param h the IO channel for this switch.
809 * @param error The error message
810 */
811 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
812 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700813 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700814 h.channel.disconnect();
815 }
816
817 /**
818 * log an error message for a duplicate dpid and disconnect this channel.
819 * @param h the IO channel for this switch.
820 */
821 protected void disconnectDuplicate(OFChannelHandler h) {
822 log.error("Duplicated dpid or incompleted cleanup - "
823 + "disconnecting channel {}", h.getSwitchInfoString());
824 h.duplicateDpidFound = Boolean.TRUE;
825 h.channel.disconnect();
826 }
827
828
829
830 /**
831 * Handles all pending port status messages before a switch is declared
832 * activated in MASTER or EQUAL role. Note that since this handling
833 * precedes the activation (and therefore notification to IOFSwitchListerners)
834 * the changes to ports will already be visible once the switch is
835 * activated. As a result, no notifications are sent out for these
836 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700837 *
838 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700839 */
840 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
841 try {
842 handlePendingPortStatusMessages(h, 0);
843 } catch (SwitchStateException e) {
844 log.error(e.getMessage());
845 }
846 }
847
848 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
849 throws SwitchStateException {
850 if (h.sw == null) {
851 String msg = "State machine error: switch is null. Should never " +
852 "happen";
853 throw new SwitchStateException(msg);
854 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800855 log.info("Processing {} pending port status messages for {}",
856 h.pendingPortStatusMsg.size(), h.sw.getStringId());
857
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800858 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700859 for (OFPortStatus ps: h.pendingPortStatusMsg) {
860 temp.add(ps);
861 handlePortStatusMessage(h, ps, false);
862 }
tom7ef8ff92014-09-17 13:08:06 -0700863 // expensive but ok - we don't expect too many port-status messages
864 // note that we cannot use clear(), because of the reasons below
865 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800866 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700867 // the iterator above takes a snapshot of the list - so while we were
868 // dealing with the pending port-status messages, we could have received
869 // newer ones. Handle them recursively, but break the recursion after
870 // five steps to avoid an attack.
871 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
872 handlePendingPortStatusMessages(h, index);
873 }
874 }
875
876 /**
877 * Handle a port status message.
878 *
879 * Handle a port status message by updating the port maps in the
880 * IOFSwitch instance and notifying Controller about the change so
881 * it can dispatch a switch update.
882 *
883 * @param h The OFChannelHhandler that received the message
884 * @param m The PortStatus message we received
885 * @param doNotify if true switch port changed events will be
886 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800887 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700888 *
889 */
890 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
891 boolean doNotify) throws SwitchStateException {
892 if (h.sw == null) {
893 String msg = getSwitchStateMessage(h, m,
894 "State machine error: switch is null. Should never " +
895 "happen");
896 throw new SwitchStateException(msg);
897 }
898
899 h.sw.handleMessage(m);
900 }
901
902
903 /**
904 * Process an OF message received on the channel and
905 * update state accordingly.
906 *
907 * The main "event" of the state machine. Process the received message,
908 * send follow up message if required and update state if required.
909 *
910 * Switches on the message type and calls more specific event handlers
911 * for each individual OF message type. If we receive a message that
912 * is supposed to be sent from a controller to a switch we throw
913 * a SwitchStateExeption.
914 *
915 * The more specific handlers can also throw SwitchStateExceptions
916 *
917 * @param h The OFChannelHandler that received the message
918 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800919 * @throws SwitchStateException if the switch is not bound to the channel
920 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700921 */
922 void processOFMessage(OFChannelHandler h, OFMessage m)
923 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800924 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -0700925 case HELLO:
926 processOFHello(h, (OFHello) m);
927 break;
928 case BARRIER_REPLY:
929 processOFBarrierReply(h, (OFBarrierReply) m);
930 break;
931 case ECHO_REPLY:
932 processOFEchoReply(h, (OFEchoReply) m);
933 break;
934 case ECHO_REQUEST:
935 processOFEchoRequest(h, (OFEchoRequest) m);
936 break;
937 case ERROR:
938 processOFError(h, (OFErrorMsg) m);
939 break;
940 case FEATURES_REPLY:
941 processOFFeaturesReply(h, (OFFeaturesReply) m);
942 break;
943 case FLOW_REMOVED:
944 processOFFlowRemoved(h, (OFFlowRemoved) m);
945 break;
946 case GET_CONFIG_REPLY:
947 processOFGetConfigReply(h, (OFGetConfigReply) m);
948 break;
949 case PACKET_IN:
950 processOFPacketIn(h, (OFPacketIn) m);
951 break;
952 case PORT_STATUS:
953 processOFPortStatus(h, (OFPortStatus) m);
954 break;
955 case QUEUE_GET_CONFIG_REPLY:
956 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
957 break;
958 case STATS_REPLY: // multipart_reply in 1.3
959 processOFStatisticsReply(h, (OFStatsReply) m);
960 break;
961 case EXPERIMENTER:
962 processOFExperimenter(h, (OFExperimenter) m);
963 break;
964 case ROLE_REPLY:
965 processOFRoleReply(h, (OFRoleReply) m);
966 break;
967 case GET_ASYNC_REPLY:
968 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
969 break;
970
971 // The following messages are sent to switches. The controller
972 // should never receive them
973 case SET_CONFIG:
974 case GET_CONFIG_REQUEST:
975 case PACKET_OUT:
976 case PORT_MOD:
977 case QUEUE_GET_CONFIG_REQUEST:
978 case BARRIER_REQUEST:
979 case STATS_REQUEST: // multipart request in 1.3
980 case FEATURES_REQUEST:
981 case FLOW_MOD:
982 case GROUP_MOD:
983 case TABLE_MOD:
984 case GET_ASYNC_REQUEST:
985 case SET_ASYNC:
986 case METER_MOD:
987 default:
988 illegalMessageReceived(h, m);
989 break;
990 }
991 }
992
993 /*-----------------------------------------------------------------
994 * Default implementation for message handlers in any state.
995 *
996 * Individual states must override these if they want a behavior
997 * that differs from the default.
998 *
999 * In general, these handlers simply ignore the message and do
1000 * nothing.
1001 *
1002 * There are some exceptions though, since some messages really
1003 * are handled the same way in every state (e.g., ECHO_REQUST) or
1004 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1005 -----------------------------------------------------------------*/
1006
1007 void processOFHello(OFChannelHandler h, OFHello m)
1008 throws IOException, SwitchStateException {
1009 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001010 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
1011 h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001012 }
1013
1014 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1015 throws IOException {
1016 // Silently ignore.
1017 }
1018
1019 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1020 throws IOException {
1021 if (h.ofVersion == null) {
1022 log.error("No OF version set for {}. Not sending Echo REPLY",
1023 h.channel.getRemoteAddress());
1024 return;
1025 }
1026 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
1027 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
1028 OFEchoReply reply = factory
1029 .buildEchoReply()
1030 .setXid(m.getXid())
1031 .setData(m.getData())
1032 .build();
1033 h.channel.write(Collections.singletonList(reply));
1034 }
1035
1036 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1037 throws IOException {
1038 // Do nothing with EchoReplies !!
1039 }
1040
1041 // no default implementation for OFError
1042 // every state must override it
1043 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1044 throws IOException, SwitchStateException;
1045
1046
1047 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1048 throws IOException, SwitchStateException {
1049 unhandledMessageReceived(h, m);
1050 }
1051
1052 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1053 throws IOException {
1054 unhandledMessageReceived(h, m);
1055 }
1056
1057 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1058 throws IOException, SwitchStateException {
1059 // we only expect config replies in the WAIT_CONFIG_REPLY state
1060 illegalMessageReceived(h, m);
1061 }
1062
1063 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1064 throws IOException {
1065 unhandledMessageReceived(h, m);
1066 }
1067
1068 // no default implementation. Every state needs to handle it.
1069 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1070 throws IOException, SwitchStateException;
1071
1072 void processOFQueueGetConfigReply(OFChannelHandler h,
1073 OFQueueGetConfigReply m)
1074 throws IOException {
1075 unhandledMessageReceived(h, m);
1076 }
1077
1078 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1079 throws IOException, SwitchStateException {
1080 unhandledMessageReceived(h, m);
1081 }
1082
1083 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1084 throws IOException, SwitchStateException {
1085 // TODO: it might make sense to parse the vendor message here
1086 // into the known vendor messages we support and then call more
1087 // specific event handlers
1088 unhandledMessageReceived(h, m);
1089 }
1090
1091 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1092 throws SwitchStateException, IOException {
1093 unhandledMessageReceived(h, m);
1094 }
1095
1096 void processOFGetAsyncReply(OFChannelHandler h,
1097 OFAsyncGetReply m) {
1098 unhandledMessageReceived(h, m);
1099 }
1100
1101 }
1102
1103
1104
1105 //*************************
1106 // Channel handler methods
1107 //*************************
1108
1109 @Override
1110 public void channelConnected(ChannelHandlerContext ctx,
1111 ChannelStateEvent e) throws Exception {
1112 channel = e.getChannel();
1113 log.info("New switch connection from {}",
1114 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001115 /*
1116 hack to wait for the switch to tell us what it's
1117 max version is. This is not spec compliant and should
1118 be removed as soon as switches behave better.
1119 */
1120 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001121 setState(ChannelState.WAIT_HELLO);
1122 }
1123
1124 @Override
1125 public void channelDisconnected(ChannelHandlerContext ctx,
1126 ChannelStateEvent e) throws Exception {
1127 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1128 getSwitchInfoString());
1129 if (thisdpid != 0) {
1130 if (!duplicateDpidFound) {
1131 // if the disconnected switch (on this ChannelHandler)
1132 // was not one with a duplicate-dpid, it is safe to remove all
1133 // state for it at the controller. Notice that if the disconnected
1134 // switch was a duplicate-dpid, calling the method below would clear
1135 // all state for the original switch (with the same dpid),
1136 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001137 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001138 if (sw != null) {
1139 sw.removeConnectedSwitch();
1140 }
tom7ef8ff92014-09-17 13:08:06 -07001141 } else {
1142 // A duplicate was disconnected on this ChannelHandler,
1143 // this is the same switch reconnecting, but the original state was
1144 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001145 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001146 duplicateDpidFound = Boolean.FALSE;
1147 }
1148 } else {
1149 log.warn("no dpid in channelHandler registered for "
1150 + "disconnected switch {}", getSwitchInfoString());
1151 }
1152 }
1153
1154 @Override
1155 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1156 throws Exception {
1157 if (e.getCause() instanceof ReadTimeoutException) {
1158 // switch timeout
1159 log.error("Disconnecting switch {} due to read timeout",
1160 getSwitchInfoString());
1161 ctx.getChannel().close();
1162 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1163 log.error("Disconnecting switch {}: failed to complete handshake",
1164 getSwitchInfoString());
1165 ctx.getChannel().close();
1166 } else if (e.getCause() instanceof ClosedChannelException) {
1167 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1168 } else if (e.getCause() instanceof IOException) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001169 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1170 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1171 log.error("Disconnecting switch {} due to IO Error: {}",
1172 getSwitchInfoString(), e.getCause().getMessage());
1173 if (log.isDebugEnabled()) {
1174 // still print stack trace if debug is enabled
1175 log.debug("StackTrace for previous Exception: ", e.getCause());
1176 }
tom7ef8ff92014-09-17 13:08:06 -07001177 }
1178 ctx.getChannel().close();
1179 } else if (e.getCause() instanceof SwitchStateException) {
1180 log.error("Disconnecting switch {} due to switch state error: {}",
1181 getSwitchInfoString(), e.getCause().getMessage());
1182 if (log.isDebugEnabled()) {
1183 // still print stack trace if debug is enabled
1184 log.debug("StackTrace for previous Exception: ", e.getCause());
1185 }
1186 ctx.getChannel().close();
1187 } else if (e.getCause() instanceof OFParseError) {
1188 log.error("Disconnecting switch "
1189 + getSwitchInfoString() +
1190 " due to message parse failure",
1191 e.getCause());
1192 ctx.getChannel().close();
1193 } else if (e.getCause() instanceof RejectedExecutionException) {
1194 log.warn("Could not process message: queue full");
1195 } else {
1196 log.error("Error while processing message from switch "
1197 + getSwitchInfoString()
1198 + "state " + this.state, e.getCause());
1199 ctx.getChannel().close();
1200 }
1201 }
1202
1203 @Override
1204 public String toString() {
1205 return getSwitchInfoString();
1206 }
1207
1208 @Override
1209 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1210 throws Exception {
1211 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1212 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001213 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001214 e.getChannel().getPipeline().getLast());
tom7ef8ff92014-09-17 13:08:06 -07001215 e.getChannel().write(Collections.singletonList(m));
1216 // XXX S some problems here -- echo request has no transaction id, and
1217 // echo reply is not correlated to the echo request.
1218 }
1219
1220 @Override
1221 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1222 throws Exception {
1223 if (e.getMessage() instanceof List) {
1224 @SuppressWarnings("unchecked")
1225 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1226
1227
1228 for (OFMessage ofm : msglist) {
1229 // Do the actual packet processing
1230 state.processOFMessage(this, ofm);
1231 }
1232 } else {
1233 state.processOFMessage(this, (OFMessage) e.getMessage());
1234 }
1235 }
1236
1237
1238
1239 //*************************
1240 // Channel utility methods
1241 //*************************
1242
1243 /**
1244 * Is this a state in which the handshake has completed?
1245 * @return true if the handshake is complete
1246 */
1247 public boolean isHandshakeComplete() {
1248 return this.state.isHandshakeComplete();
1249 }
1250
1251 private void dispatchMessage(OFMessage m) {
1252 sw.handleMessage(m);
1253 }
1254
1255 /**
1256 * Return a string describing this switch based on the already available
1257 * information (DPID and/or remote socket).
1258 * @return display string
1259 */
1260 private String getSwitchInfoString() {
1261 if (sw != null) {
1262 return sw.toString();
1263 }
1264 String channelString;
1265 if (channel == null || channel.getRemoteAddress() == null) {
1266 channelString = "?";
1267 } else {
1268 channelString = channel.getRemoteAddress().toString();
1269 }
1270 String dpidString;
1271 if (featuresReply == null) {
1272 dpidString = "?";
1273 } else {
1274 dpidString = featuresReply.getDatapathId().toString();
1275 }
1276 return String.format("[%s DPID[%s]]", channelString, dpidString);
1277 }
1278
1279 /**
1280 * Update the channels state. Only called from the state machine.
1281 * TODO: enforce restricted state transitions
1282 * @param state
1283 */
1284 private void setState(ChannelState state) {
1285 this.state = state;
1286 }
1287
1288 /**
1289 * Send hello message to the switch using the handshake transactions ids.
1290 * @throws IOException
1291 */
1292 private void sendHandshakeHelloMessage() throws IOException {
1293 // The OF protocol requires us to start things off by sending the highest
1294 // version of the protocol supported.
1295
1296 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1297 // see Sec. 7.5.1 of the OF1.3.4 spec
1298 U32 bitmap = U32.ofRaw(0x00000012);
1299 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1300 .setBitmaps(Collections.singletonList(bitmap))
1301 .build();
1302 OFMessage.Builder mb = factory13.buildHello()
1303 .setXid(this.handshakeTransactionIds--)
1304 .setElements(Collections.singletonList(hem));
1305 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1306 channel.write(Collections.singletonList(mb.build()));
1307 }
1308
1309 /**
1310 * Send featuresRequest msg to the switch using the handshake transactions ids.
1311 * @throws IOException
1312 */
1313 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1314 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1315 OFMessage m = factory.buildFeaturesRequest()
1316 .setXid(this.handshakeTransactionIds--)
1317 .build();
1318 channel.write(Collections.singletonList(m));
1319 }
1320
1321 /**
1322 * Send the configuration requests to tell the switch we want full
1323 * packets.
1324 * @throws IOException
1325 */
1326 private void sendHandshakeSetConfig() throws IOException {
1327 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1328 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001329 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001330
1331 // Ensure we receive the full packet via PacketIn
1332 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001333 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001334 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001335 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001336 OFSetConfig sc = factory
1337 .buildSetConfig()
1338 .setMissSendLen((short) 0xffff)
1339 .setXid(this.handshakeTransactionIds--)
1340 .build();
1341 msglist.add(sc);
1342 }
tom7ef8ff92014-09-17 13:08:06 -07001343
1344 // Barrier
1345 OFBarrierRequest br = factory
1346 .buildBarrierRequest()
1347 .setXid(this.handshakeTransactionIds--)
1348 .build();
1349 msglist.add(br);
1350
1351 // Verify (need barrier?)
1352 OFGetConfigRequest gcr = factory
1353 .buildGetConfigRequest()
1354 .setXid(this.handshakeTransactionIds--)
1355 .build();
1356 msglist.add(gcr);
1357 channel.write(msglist);
1358 }
1359
1360 /**
1361 * send a description state request.
1362 * @throws IOException
1363 */
1364 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1365 // Get Description to set switch-specific flags
1366 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1367 OFDescStatsRequest dreq = factory
1368 .buildDescStatsRequest()
1369 .setXid(handshakeTransactionIds--)
1370 .build();
1371 channel.write(Collections.singletonList(dreq));
1372 }
1373
Jordi Ortiz91477b82016-11-29 15:22:50 +01001374 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001375 * send a meter features request.
1376 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001377 * @throws IOException
1378 */
1379 private void sendMeterFeaturesRequest() throws IOException {
1380 // Get meter features including the MaxMeters value available for the device
1381 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1382 OFMeterFeaturesStatsRequest mfreq = factory
1383 .buildMeterFeaturesStatsRequest()
1384 .setXid(handshakeTransactionIds--)
1385 .build();
1386 channel.write(Collections.singletonList(mfreq));
1387 }
1388
tom7ef8ff92014-09-17 13:08:06 -07001389 private void sendHandshakeOFPortDescRequest() throws IOException {
1390 // Get port description for 1.3 switch
1391 OFPortDescStatsRequest preq = factory13
1392 .buildPortDescStatsRequest()
1393 .setXid(handshakeTransactionIds--)
1394 .build();
1395 channel.write(Collections.singletonList(preq));
1396 }
1397
1398 ChannelState getStateForTesting() {
1399 return state;
1400 }
1401
1402}