blob: 11cff69d3123036f28b4a81e5627c4e6146761ac [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 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800622
623 @Override
624 void processIdle(OFChannelHandler h) throws IOException {
625 log.info("{} did not respond to MeterFeaturesRequest, " +
626 "moving on without it.",
627 h.getSwitchInfoString());
628 h.sendHandshakeDescriptionStatsRequest();
629 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
630 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100631 },
632
tom7ef8ff92014-09-17 13:08:06 -0700633
634 /**
635 * This controller is in MASTER role for this switch. We enter this state
636 * after requesting and winning control from the global registry.
637 * The main handshake as well as the switch-driver sub-handshake
638 * is complete at this point.
639 * // XXX S reconsider below
640 * In the (near) future we may deterministically assign controllers to
641 * switches at startup.
642 * We only leave this state if the switch disconnects or
643 * if we send a role request for SLAVE /and/ receive the role reply for
644 * SLAVE.
645 */
646 ACTIVE(true) {
647 @Override
648 void processOFError(OFChannelHandler h, OFErrorMsg m)
649 throws IOException, SwitchStateException {
650 // if we get here, then the error message is for something else
651 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700652 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700653 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700654 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700655 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700656 // We are the master controller and the switch returned
657 // a permission error. This is a likely indicator that
658 // the switch thinks we are slave. Reassert our
659 // role
660 // FIXME: this could be really bad during role transitions
661 // if two controllers are master (even if its only for
662 // a brief period). We might need to see if these errors
663 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700664
tom7ef8ff92014-09-17 13:08:06 -0700665 h.sw.reassertRole();
666 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
667 ((OFFlowModFailedErrorMsg) m).getCode() ==
668 OFFlowModFailedCode.ALL_TABLES_FULL) {
669 h.sw.setTableFull(true);
670 } else {
671 logError(h, m);
672 }
673 h.dispatchMessage(m);
674 }
675
676 @Override
677 void processOFStatisticsReply(OFChannelHandler h,
678 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700679 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
680 h.sw.setPortDescReply((OFPortDescStatsReply) m);
681 }
tom7ef8ff92014-09-17 13:08:06 -0700682 h.dispatchMessage(m);
683 }
684
685 @Override
686 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
687 throws SwitchStateException {
688 h.sw.handleNiciraRole(m);
689 }
690
691 @Override
692 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
693 throws SwitchStateException {
694 h.sw.handleRole(m);
695 }
696
697 @Override
698 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
699 throws SwitchStateException {
700 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800701 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700702 }
703
704 @Override
705 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700706// OFPacketOut out =
707// h.sw.factory().buildPacketOut()
708// .setXid(m.getXid())
709// .setBufferId(m.getBufferId()).build();
710// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700711 h.dispatchMessage(m);
712 }
713
714 @Override
715 void processOFFlowRemoved(OFChannelHandler h,
716 OFFlowRemoved m) {
717 h.dispatchMessage(m);
718 }
719
720 @Override
721 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
722 h.dispatchMessage(m);
723 }
724
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700725 @Override
726 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700727 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700728 h.dispatchMessage(m);
729 }
730
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800731 @Override
732 void processIdle(OFChannelHandler h) throws IOException {
733 log.info("{} idle", h.getSwitchInfoString());
734 }
735
tom7ef8ff92014-09-17 13:08:06 -0700736 };
737
738 private final boolean handshakeComplete;
739 ChannelState(boolean handshakeComplete) {
740 this.handshakeComplete = handshakeComplete;
741 }
742
743 /**
744 * Is this a state in which the handshake has completed?
745 * @return true if the handshake is complete
746 */
747 public boolean isHandshakeComplete() {
748 return handshakeComplete;
749 }
750
751 /**
752 * Get a string specifying the switch connection, state, and
753 * message received. To be used as message for SwitchStateException
754 * or log messages
755 * @param h The channel handler (to get switch information_
756 * @param m The OFMessage that has just been received
757 * @param details A string giving more details about the exact nature
758 * of the problem.
759 * @return display string
760 */
761 // needs to be protected because enum members are actually subclasses
762 protected String getSwitchStateMessage(OFChannelHandler h,
763 OFMessage m,
764 String details) {
765 return String.format("Switch: [%s], State: [%s], received: [%s]"
766 + ", details: %s",
767 h.getSwitchInfoString(),
768 this.toString(),
769 m.getType().toString(),
770 details);
771 }
772
773 /**
774 * We have an OFMessage we didn't expect given the current state and
775 * we want to treat this as an error.
776 * We currently throw an exception that will terminate the connection
777 * However, we could be more forgiving
778 * @param h the channel handler that received the message
779 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700780 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700781 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700782 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700783 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
784 throws SwitchStateException {
785 String msg = getSwitchStateMessage(h, m,
786 "Switch should never send this message in the current state");
787 throw new SwitchStateException(msg);
788
789 }
790
791 /**
792 * We have an OFMessage we didn't expect given the current state and
793 * we want to ignore the message.
794 * @param h the channel handler the received the message
795 * @param m the message
796 */
797 protected void unhandledMessageReceived(OFChannelHandler h,
798 OFMessage m) {
799 if (log.isDebugEnabled()) {
800 String msg = getSwitchStateMessage(h, m,
801 "Ignoring unexpected message");
802 log.debug(msg);
803 }
804 }
805
806 /**
807 * Log an OpenFlow error message from a switch.
808 * @param h The switch that sent the error
809 * @param error The error message
810 */
811 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700812 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700813 error,
814 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800815 this);
tom7ef8ff92014-09-17 13:08:06 -0700816 }
817
818 /**
819 * Log an OpenFlow error message from a switch and disconnect the
820 * channel.
821 *
822 * @param h the IO channel for this switch.
823 * @param error The error message
824 */
825 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
826 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700827 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700828 h.channel.disconnect();
829 }
830
831 /**
832 * log an error message for a duplicate dpid and disconnect this channel.
833 * @param h the IO channel for this switch.
834 */
835 protected void disconnectDuplicate(OFChannelHandler h) {
836 log.error("Duplicated dpid or incompleted cleanup - "
837 + "disconnecting channel {}", h.getSwitchInfoString());
838 h.duplicateDpidFound = Boolean.TRUE;
839 h.channel.disconnect();
840 }
841
842
843
844 /**
845 * Handles all pending port status messages before a switch is declared
846 * activated in MASTER or EQUAL role. Note that since this handling
847 * precedes the activation (and therefore notification to IOFSwitchListerners)
848 * the changes to ports will already be visible once the switch is
849 * activated. As a result, no notifications are sent out for these
850 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700851 *
852 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700853 */
854 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
855 try {
856 handlePendingPortStatusMessages(h, 0);
857 } catch (SwitchStateException e) {
858 log.error(e.getMessage());
859 }
860 }
861
862 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
863 throws SwitchStateException {
864 if (h.sw == null) {
865 String msg = "State machine error: switch is null. Should never " +
866 "happen";
867 throw new SwitchStateException(msg);
868 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800869 log.info("Processing {} pending port status messages for {}",
870 h.pendingPortStatusMsg.size(), h.sw.getStringId());
871
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800872 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700873 for (OFPortStatus ps: h.pendingPortStatusMsg) {
874 temp.add(ps);
875 handlePortStatusMessage(h, ps, false);
876 }
tom7ef8ff92014-09-17 13:08:06 -0700877 // expensive but ok - we don't expect too many port-status messages
878 // note that we cannot use clear(), because of the reasons below
879 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800880 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700881 // the iterator above takes a snapshot of the list - so while we were
882 // dealing with the pending port-status messages, we could have received
883 // newer ones. Handle them recursively, but break the recursion after
884 // five steps to avoid an attack.
885 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
886 handlePendingPortStatusMessages(h, index);
887 }
888 }
889
890 /**
891 * Handle a port status message.
892 *
893 * Handle a port status message by updating the port maps in the
894 * IOFSwitch instance and notifying Controller about the change so
895 * it can dispatch a switch update.
896 *
897 * @param h The OFChannelHhandler that received the message
898 * @param m The PortStatus message we received
899 * @param doNotify if true switch port changed events will be
900 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800901 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700902 *
903 */
904 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
905 boolean doNotify) throws SwitchStateException {
906 if (h.sw == null) {
907 String msg = getSwitchStateMessage(h, m,
908 "State machine error: switch is null. Should never " +
909 "happen");
910 throw new SwitchStateException(msg);
911 }
912
913 h.sw.handleMessage(m);
914 }
915
916
917 /**
918 * Process an OF message received on the channel and
919 * update state accordingly.
920 *
921 * The main "event" of the state machine. Process the received message,
922 * send follow up message if required and update state if required.
923 *
924 * Switches on the message type and calls more specific event handlers
925 * for each individual OF message type. If we receive a message that
926 * is supposed to be sent from a controller to a switch we throw
927 * a SwitchStateExeption.
928 *
929 * The more specific handlers can also throw SwitchStateExceptions
930 *
931 * @param h The OFChannelHandler that received the message
932 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800933 * @throws SwitchStateException if the switch is not bound to the channel
934 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700935 */
936 void processOFMessage(OFChannelHandler h, OFMessage m)
937 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800938 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -0700939 case HELLO:
940 processOFHello(h, (OFHello) m);
941 break;
942 case BARRIER_REPLY:
943 processOFBarrierReply(h, (OFBarrierReply) m);
944 break;
945 case ECHO_REPLY:
946 processOFEchoReply(h, (OFEchoReply) m);
947 break;
948 case ECHO_REQUEST:
949 processOFEchoRequest(h, (OFEchoRequest) m);
950 break;
951 case ERROR:
952 processOFError(h, (OFErrorMsg) m);
953 break;
954 case FEATURES_REPLY:
955 processOFFeaturesReply(h, (OFFeaturesReply) m);
956 break;
957 case FLOW_REMOVED:
958 processOFFlowRemoved(h, (OFFlowRemoved) m);
959 break;
960 case GET_CONFIG_REPLY:
961 processOFGetConfigReply(h, (OFGetConfigReply) m);
962 break;
963 case PACKET_IN:
964 processOFPacketIn(h, (OFPacketIn) m);
965 break;
966 case PORT_STATUS:
967 processOFPortStatus(h, (OFPortStatus) m);
968 break;
969 case QUEUE_GET_CONFIG_REPLY:
970 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
971 break;
972 case STATS_REPLY: // multipart_reply in 1.3
973 processOFStatisticsReply(h, (OFStatsReply) m);
974 break;
975 case EXPERIMENTER:
976 processOFExperimenter(h, (OFExperimenter) m);
977 break;
978 case ROLE_REPLY:
979 processOFRoleReply(h, (OFRoleReply) m);
980 break;
981 case GET_ASYNC_REPLY:
982 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
983 break;
984
985 // The following messages are sent to switches. The controller
986 // should never receive them
987 case SET_CONFIG:
988 case GET_CONFIG_REQUEST:
989 case PACKET_OUT:
990 case PORT_MOD:
991 case QUEUE_GET_CONFIG_REQUEST:
992 case BARRIER_REQUEST:
993 case STATS_REQUEST: // multipart request in 1.3
994 case FEATURES_REQUEST:
995 case FLOW_MOD:
996 case GROUP_MOD:
997 case TABLE_MOD:
998 case GET_ASYNC_REQUEST:
999 case SET_ASYNC:
1000 case METER_MOD:
1001 default:
1002 illegalMessageReceived(h, m);
1003 break;
1004 }
1005 }
1006
1007 /*-----------------------------------------------------------------
1008 * Default implementation for message handlers in any state.
1009 *
1010 * Individual states must override these if they want a behavior
1011 * that differs from the default.
1012 *
1013 * In general, these handlers simply ignore the message and do
1014 * nothing.
1015 *
1016 * There are some exceptions though, since some messages really
1017 * are handled the same way in every state (e.g., ECHO_REQUST) or
1018 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1019 -----------------------------------------------------------------*/
1020
1021 void processOFHello(OFChannelHandler h, OFHello m)
1022 throws IOException, SwitchStateException {
1023 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001024 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
1025 h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001026 }
1027
1028 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1029 throws IOException {
1030 // Silently ignore.
1031 }
1032
1033 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1034 throws IOException {
1035 if (h.ofVersion == null) {
1036 log.error("No OF version set for {}. Not sending Echo REPLY",
1037 h.channel.getRemoteAddress());
1038 return;
1039 }
1040 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
1041 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
1042 OFEchoReply reply = factory
1043 .buildEchoReply()
1044 .setXid(m.getXid())
1045 .setData(m.getData())
1046 .build();
1047 h.channel.write(Collections.singletonList(reply));
1048 }
1049
1050 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1051 throws IOException {
1052 // Do nothing with EchoReplies !!
1053 }
1054
1055 // no default implementation for OFError
1056 // every state must override it
1057 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1058 throws IOException, SwitchStateException;
1059
1060
1061 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1062 throws IOException, SwitchStateException {
1063 unhandledMessageReceived(h, m);
1064 }
1065
1066 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1067 throws IOException {
1068 unhandledMessageReceived(h, m);
1069 }
1070
1071 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1072 throws IOException, SwitchStateException {
1073 // we only expect config replies in the WAIT_CONFIG_REPLY state
1074 illegalMessageReceived(h, m);
1075 }
1076
1077 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1078 throws IOException {
1079 unhandledMessageReceived(h, m);
1080 }
1081
1082 // no default implementation. Every state needs to handle it.
1083 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1084 throws IOException, SwitchStateException;
1085
1086 void processOFQueueGetConfigReply(OFChannelHandler h,
1087 OFQueueGetConfigReply m)
1088 throws IOException {
1089 unhandledMessageReceived(h, m);
1090 }
1091
1092 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1093 throws IOException, SwitchStateException {
1094 unhandledMessageReceived(h, m);
1095 }
1096
1097 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1098 throws IOException, SwitchStateException {
1099 // TODO: it might make sense to parse the vendor message here
1100 // into the known vendor messages we support and then call more
1101 // specific event handlers
1102 unhandledMessageReceived(h, m);
1103 }
1104
1105 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1106 throws SwitchStateException, IOException {
1107 unhandledMessageReceived(h, m);
1108 }
1109
1110 void processOFGetAsyncReply(OFChannelHandler h,
1111 OFAsyncGetReply m) {
1112 unhandledMessageReceived(h, m);
1113 }
1114
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001115 void processIdle(OFChannelHandler h) throws IOException {
1116 // disconnect channel which did no complete handshake
1117 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1118 h.channel.disconnect();
1119 }
tom7ef8ff92014-09-17 13:08:06 -07001120 }
1121
1122
1123
1124 //*************************
1125 // Channel handler methods
1126 //*************************
1127
1128 @Override
1129 public void channelConnected(ChannelHandlerContext ctx,
1130 ChannelStateEvent e) throws Exception {
1131 channel = e.getChannel();
1132 log.info("New switch connection from {}",
1133 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001134 /*
1135 hack to wait for the switch to tell us what it's
1136 max version is. This is not spec compliant and should
1137 be removed as soon as switches behave better.
1138 */
1139 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001140 setState(ChannelState.WAIT_HELLO);
1141 }
1142
1143 @Override
1144 public void channelDisconnected(ChannelHandlerContext ctx,
1145 ChannelStateEvent e) throws Exception {
1146 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1147 getSwitchInfoString());
1148 if (thisdpid != 0) {
1149 if (!duplicateDpidFound) {
1150 // if the disconnected switch (on this ChannelHandler)
1151 // was not one with a duplicate-dpid, it is safe to remove all
1152 // state for it at the controller. Notice that if the disconnected
1153 // switch was a duplicate-dpid, calling the method below would clear
1154 // all state for the original switch (with the same dpid),
1155 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001156 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001157 if (sw != null) {
1158 sw.removeConnectedSwitch();
1159 }
tom7ef8ff92014-09-17 13:08:06 -07001160 } else {
1161 // A duplicate was disconnected on this ChannelHandler,
1162 // this is the same switch reconnecting, but the original state was
1163 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001164 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001165 duplicateDpidFound = Boolean.FALSE;
1166 }
1167 } else {
1168 log.warn("no dpid in channelHandler registered for "
1169 + "disconnected switch {}", getSwitchInfoString());
1170 }
1171 }
1172
1173 @Override
1174 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1175 throws Exception {
1176 if (e.getCause() instanceof ReadTimeoutException) {
1177 // switch timeout
1178 log.error("Disconnecting switch {} due to read timeout",
1179 getSwitchInfoString());
1180 ctx.getChannel().close();
1181 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1182 log.error("Disconnecting switch {}: failed to complete handshake",
1183 getSwitchInfoString());
1184 ctx.getChannel().close();
1185 } else if (e.getCause() instanceof ClosedChannelException) {
1186 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1187 } else if (e.getCause() instanceof IOException) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001188 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1189 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1190 log.error("Disconnecting switch {} due to IO Error: {}",
1191 getSwitchInfoString(), e.getCause().getMessage());
1192 if (log.isDebugEnabled()) {
1193 // still print stack trace if debug is enabled
1194 log.debug("StackTrace for previous Exception: ", e.getCause());
1195 }
tom7ef8ff92014-09-17 13:08:06 -07001196 }
1197 ctx.getChannel().close();
1198 } else if (e.getCause() instanceof SwitchStateException) {
1199 log.error("Disconnecting switch {} due to switch state error: {}",
1200 getSwitchInfoString(), e.getCause().getMessage());
1201 if (log.isDebugEnabled()) {
1202 // still print stack trace if debug is enabled
1203 log.debug("StackTrace for previous Exception: ", e.getCause());
1204 }
1205 ctx.getChannel().close();
1206 } else if (e.getCause() instanceof OFParseError) {
1207 log.error("Disconnecting switch "
1208 + getSwitchInfoString() +
1209 " due to message parse failure",
1210 e.getCause());
1211 ctx.getChannel().close();
1212 } else if (e.getCause() instanceof RejectedExecutionException) {
1213 log.warn("Could not process message: queue full");
1214 } else {
1215 log.error("Error while processing message from switch "
1216 + getSwitchInfoString()
1217 + "state " + this.state, e.getCause());
1218 ctx.getChannel().close();
1219 }
1220 }
1221
1222 @Override
1223 public String toString() {
1224 return getSwitchInfoString();
1225 }
1226
1227 @Override
1228 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1229 throws Exception {
1230 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1231 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001232 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001233 e.getChannel().getPipeline().getLast());
tom7ef8ff92014-09-17 13:08:06 -07001234 e.getChannel().write(Collections.singletonList(m));
1235 // XXX S some problems here -- echo request has no transaction id, and
1236 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001237 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001238 }
1239
1240 @Override
1241 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1242 throws Exception {
1243 if (e.getMessage() instanceof List) {
1244 @SuppressWarnings("unchecked")
1245 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1246
1247
1248 for (OFMessage ofm : msglist) {
1249 // Do the actual packet processing
1250 state.processOFMessage(this, ofm);
1251 }
1252 } else {
1253 state.processOFMessage(this, (OFMessage) e.getMessage());
1254 }
1255 }
1256
1257
1258
1259 //*************************
1260 // Channel utility methods
1261 //*************************
1262
1263 /**
1264 * Is this a state in which the handshake has completed?
1265 * @return true if the handshake is complete
1266 */
1267 public boolean isHandshakeComplete() {
1268 return this.state.isHandshakeComplete();
1269 }
1270
1271 private void dispatchMessage(OFMessage m) {
1272 sw.handleMessage(m);
1273 }
1274
1275 /**
1276 * Return a string describing this switch based on the already available
1277 * information (DPID and/or remote socket).
1278 * @return display string
1279 */
1280 private String getSwitchInfoString() {
1281 if (sw != null) {
1282 return sw.toString();
1283 }
1284 String channelString;
1285 if (channel == null || channel.getRemoteAddress() == null) {
1286 channelString = "?";
1287 } else {
1288 channelString = channel.getRemoteAddress().toString();
1289 }
1290 String dpidString;
1291 if (featuresReply == null) {
1292 dpidString = "?";
1293 } else {
1294 dpidString = featuresReply.getDatapathId().toString();
1295 }
1296 return String.format("[%s DPID[%s]]", channelString, dpidString);
1297 }
1298
1299 /**
1300 * Update the channels state. Only called from the state machine.
1301 * TODO: enforce restricted state transitions
1302 * @param state
1303 */
1304 private void setState(ChannelState state) {
1305 this.state = state;
1306 }
1307
1308 /**
1309 * Send hello message to the switch using the handshake transactions ids.
1310 * @throws IOException
1311 */
1312 private void sendHandshakeHelloMessage() throws IOException {
1313 // The OF protocol requires us to start things off by sending the highest
1314 // version of the protocol supported.
1315
1316 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1317 // see Sec. 7.5.1 of the OF1.3.4 spec
1318 U32 bitmap = U32.ofRaw(0x00000012);
1319 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1320 .setBitmaps(Collections.singletonList(bitmap))
1321 .build();
1322 OFMessage.Builder mb = factory13.buildHello()
1323 .setXid(this.handshakeTransactionIds--)
1324 .setElements(Collections.singletonList(hem));
1325 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1326 channel.write(Collections.singletonList(mb.build()));
1327 }
1328
1329 /**
1330 * Send featuresRequest msg to the switch using the handshake transactions ids.
1331 * @throws IOException
1332 */
1333 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1334 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1335 OFMessage m = factory.buildFeaturesRequest()
1336 .setXid(this.handshakeTransactionIds--)
1337 .build();
1338 channel.write(Collections.singletonList(m));
1339 }
1340
1341 /**
1342 * Send the configuration requests to tell the switch we want full
1343 * packets.
1344 * @throws IOException
1345 */
1346 private void sendHandshakeSetConfig() throws IOException {
1347 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1348 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001349 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001350
1351 // Ensure we receive the full packet via PacketIn
1352 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001353 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001354 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001355 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001356 OFSetConfig sc = factory
1357 .buildSetConfig()
1358 .setMissSendLen((short) 0xffff)
1359 .setXid(this.handshakeTransactionIds--)
1360 .build();
1361 msglist.add(sc);
1362 }
tom7ef8ff92014-09-17 13:08:06 -07001363
1364 // Barrier
1365 OFBarrierRequest br = factory
1366 .buildBarrierRequest()
1367 .setXid(this.handshakeTransactionIds--)
1368 .build();
1369 msglist.add(br);
1370
1371 // Verify (need barrier?)
1372 OFGetConfigRequest gcr = factory
1373 .buildGetConfigRequest()
1374 .setXid(this.handshakeTransactionIds--)
1375 .build();
1376 msglist.add(gcr);
1377 channel.write(msglist);
1378 }
1379
1380 /**
1381 * send a description state request.
1382 * @throws IOException
1383 */
1384 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1385 // Get Description to set switch-specific flags
1386 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1387 OFDescStatsRequest dreq = factory
1388 .buildDescStatsRequest()
1389 .setXid(handshakeTransactionIds--)
1390 .build();
1391 channel.write(Collections.singletonList(dreq));
1392 }
1393
Jordi Ortiz91477b82016-11-29 15:22:50 +01001394 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001395 * send a meter features request.
1396 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001397 * @throws IOException
1398 */
1399 private void sendMeterFeaturesRequest() throws IOException {
1400 // Get meter features including the MaxMeters value available for the device
1401 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1402 OFMeterFeaturesStatsRequest mfreq = factory
1403 .buildMeterFeaturesStatsRequest()
1404 .setXid(handshakeTransactionIds--)
1405 .build();
1406 channel.write(Collections.singletonList(mfreq));
1407 }
1408
tom7ef8ff92014-09-17 13:08:06 -07001409 private void sendHandshakeOFPortDescRequest() throws IOException {
1410 // Get port description for 1.3 switch
1411 OFPortDescStatsRequest preq = factory13
1412 .buildPortDescStatsRequest()
1413 .setXid(handshakeTransactionIds--)
1414 .build();
1415 channel.write(Collections.singletonList(preq));
1416 }
1417
1418 ChannelState getStateForTesting() {
1419 return state;
1420 }
1421
1422}