blob: 6649fcdc685a9687f27b77719299af568e830547 [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
Yuta HIGUCHI10f45132017-03-01 17:09:32 -080097 /**
98 * Timeout in ms to wait for meter feature reply.
99 */
100 private static final long METER_TIMEOUT = 60_000;
101
102 private volatile long lastStateChange = System.currentTimeMillis();
103
tom7ef8ff92014-09-17 13:08:06 -0700104 // When a switch with a duplicate dpid is found (i.e we already have a
105 // connected switch with the same dpid), the new switch is immediately
106 // disconnected. At that point netty callsback channelDisconnected() which
107 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
108 // switch state for the older (still connected) switch
109 private volatile Boolean duplicateDpidFound;
110
111 // Temporary storage for switch-features and port-description
112 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700113 private List<OFPortDescStatsReply> portDescReplies;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100114 private OFMeterFeaturesStatsReply meterFeaturesReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700115 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700116 // a concurrent ArrayList to temporarily store port status messages
117 // before we are ready to deal with them
118 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
119
120 //Indicates the openflow version used by this switch
121 protected OFVersion ofVersion;
122 protected OFFactory factory13;
123 protected OFFactory factory10;
124
125 /** transaction Ids to use during handshake. Since only one thread
126 * calls into an OFChannelHandler instance, we don't need atomic.
127 * We will count down
128 */
129 private int handshakeTransactionIds = -1;
130
131 /**
132 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800133 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700134 */
135 OFChannelHandler(Controller controller) {
136 this.controller = controller;
137 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800138 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
139 this.portDescReplies = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700140 factory13 = controller.getOFMessageFactory13();
141 factory10 = controller.getOFMessageFactory10();
142 duplicateDpidFound = Boolean.FALSE;
143 }
144
145
146
147 // XXX S consider if necessary
148 public void disconnectSwitch() {
149 sw.disconnectSwitch();
150 }
151
152
153
154 //*************************
155 // Channel State Machine
156 //*************************
157
158 /**
159 * The state machine for handling the switch/channel state. All state
160 * transitions should happen from within the state machine (and not from other
161 * parts of the code)
162 */
163 enum ChannelState {
164 /**
165 * Initial state before channel is connected.
166 */
167 INIT(false) {
168 @Override
169 void processOFMessage(OFChannelHandler h, OFMessage m)
170 throws IOException, SwitchStateException {
171 illegalMessageReceived(h, m);
172 }
173
174 @Override
175 void processOFError(OFChannelHandler h, OFErrorMsg m)
176 throws IOException {
177 // need to implement since its abstract but it will never
178 // be called
179 }
180
181 @Override
182 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
183 throws IOException {
184 unhandledMessageReceived(h, m);
185 }
186 },
187
188 /**
189 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
190 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
191 * protocol version is accepted.
192 * We send an OFFeaturesRequest depending on the protocol version selected
193 * Next state is WAIT_FEATURES_REPLY
194 */
195 WAIT_HELLO(false) {
196 @Override
197 void processOFHello(OFChannelHandler h, OFHello m)
198 throws IOException {
199 // TODO We could check for the optional bitmap, but for now
200 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500201 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
202 log.debug("Received {} Hello from {} - switching to OF "
203 + "version 1.3", m.getVersion(),
tom7ef8ff92014-09-17 13:08:06 -0700204 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800205 h.sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -0700206 h.ofVersion = OFVersion.OF_13;
Chip Boling68bc6562015-07-06 10:00:01 -0500207 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700208 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700209 + "version 1.0", m.getVersion(),
210 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800211 OFHello hi =
212 h.factory10.buildHello()
213 .setXid(h.handshakeTransactionIds--)
214 .build();
215 h.channel.write(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700216 h.ofVersion = OFVersion.OF_10;
217 } else {
218 log.error("Received Hello of version {} from switch at {}. "
219 + "This controller works with OF1.0 and OF1.3 "
220 + "switches. Disconnecting switch ...",
221 m.getVersion(), h.channel.getRemoteAddress());
222 h.channel.disconnect();
223 return;
224 }
225 h.sendHandshakeFeaturesRequestMessage();
226 h.setState(WAIT_FEATURES_REPLY);
227 }
228 @Override
229 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
230 throws IOException, SwitchStateException {
231 illegalMessageReceived(h, m);
232 }
233 @Override
234 void processOFStatisticsReply(OFChannelHandler h,
235 OFStatsReply m)
236 throws IOException, SwitchStateException {
237 illegalMessageReceived(h, m);
238 }
239 @Override
240 void processOFError(OFChannelHandler h, OFErrorMsg m) {
241 logErrorDisconnect(h, m);
242 }
243
244 @Override
245 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
246 throws IOException {
247 unhandledMessageReceived(h, m);
248 }
249 },
250
251
252 /**
253 * We are waiting for a features reply message. Once we receive it, the
254 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
255 * we send a SetConfig request, barrier, and GetConfig request and the
256 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
257 * request and the next state is WAIT_PORT_DESC_REPLY.
258 */
259 WAIT_FEATURES_REPLY(false) {
260 @Override
261 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
262 throws IOException {
263 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700264 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700265 h.getSwitchInfoString(), h.thisdpid);
266
267 h.featuresReply = m; //temp store
268 if (h.ofVersion == OFVersion.OF_10) {
269 h.sendHandshakeSetConfig();
270 h.setState(WAIT_CONFIG_REPLY);
271 } else {
272 //version is 1.3, must get switchport information
273 h.sendHandshakeOFPortDescRequest();
274 h.setState(WAIT_PORT_DESC_REPLY);
275 }
276 }
277 @Override
278 void processOFStatisticsReply(OFChannelHandler h,
279 OFStatsReply m)
280 throws IOException, SwitchStateException {
281 illegalMessageReceived(h, m);
282 }
283 @Override
284 void processOFError(OFChannelHandler h, OFErrorMsg m) {
285 logErrorDisconnect(h, m);
286 }
287
288 @Override
289 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
290 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800291 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700292 }
293 },
294
295 /**
296 * We are waiting for a description of the 1.3 switch ports.
297 * Once received, we send a SetConfig request
298 * Next State is WAIT_CONFIG_REPLY
299 */
300 WAIT_PORT_DESC_REPLY(false) {
301
302 @Override
303 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
304 throws SwitchStateException {
305 // Read port description
306 if (m.getStatsType() != OFStatsType.PORT_DESC) {
307 log.warn("Expecting port description stats but received stats "
308 + "type {} from {}. Ignoring ...", m.getStatsType(),
309 h.channel.getRemoteAddress());
310 return;
311 }
312 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700313 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700314 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700315 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800316 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700317 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800318 } else {
319 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700320 }
321 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
tom7ef8ff92014-09-17 13:08:06 -0700322 log.info("Received port desc reply for switch at {}",
323 h.getSwitchInfoString());
324 try {
325 h.sendHandshakeSetConfig();
326 } catch (IOException e) {
327 log.error("Unable to send setConfig after PortDescReply. "
328 + "Error: {}", e.getMessage());
329 }
330 h.setState(WAIT_CONFIG_REPLY);
331 }
332
333 @Override
334 void processOFError(OFChannelHandler h, OFErrorMsg m)
335 throws IOException, SwitchStateException {
336 logErrorDisconnect(h, m);
337
338 }
339
340 @Override
341 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
342 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800343 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700344
345 }
346 },
347
348 /**
349 * We are waiting for a config reply message. Once we receive it
350 * we send a DescriptionStatsRequest to the switch.
351 * Next state: WAIT_DESCRIPTION_STAT_REPLY
352 */
353 WAIT_CONFIG_REPLY(false) {
354 @Override
355 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
356 throws IOException {
357 if (m.getMissSendLen() == 0xffff) {
358 log.trace("Config Reply from switch {} confirms "
359 + "miss length set to 0xffff",
360 h.getSwitchInfoString());
361 } else {
362 // FIXME: we can't really deal with switches that don't send
363 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800364 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700365 + "miss length set to {}",
366 h.getSwitchInfoString(),
367 m.getMissSendLen());
368 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100369
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800370 // TODO should this check if 1.3 or later?
Jordi Ortiz91477b82016-11-29 15:22:50 +0100371 if (h.ofVersion == OFVersion.OF_13) {
372 // Meters were introduced in OpenFlow 1.3
373 h.sendMeterFeaturesRequest();
374 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800375 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100376 h.sendHandshakeDescriptionStatsRequest();
377 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
378 }
tom7ef8ff92014-09-17 13:08:06 -0700379 }
380
381 @Override
382 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
383 // do nothing;
384 }
385
386 @Override
387 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
388 throws IOException, SwitchStateException {
389 illegalMessageReceived(h, m);
390 }
391 @Override
392 void processOFStatisticsReply(OFChannelHandler h,
393 OFStatsReply m)
394 throws IOException, SwitchStateException {
395 log.error("Received multipart(stats) message sub-type {}",
396 m.getStatsType());
397 illegalMessageReceived(h, m);
398 }
399
400 @Override
401 void processOFError(OFChannelHandler h, OFErrorMsg m) {
402 logErrorDisconnect(h, m);
403 }
404
405 @Override
406 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
407 throws IOException {
408 h.pendingPortStatusMsg.add(m);
409 }
410 },
411
412
413 /**
414 * We are waiting for a OFDescriptionStat message from the switch.
415 * Once we receive any stat message we try to parse it. If it's not
416 * a description stats message we disconnect. If its the expected
417 * description stats message, we:
418 * - use the switch driver to bind the switch and get an IOFSwitch instance
419 * - setup the IOFSwitch instance
420 * - add switch controller and send the initial role
421 * request to the switch.
422 * Next state: WAIT_INITIAL_ROLE
423 * In the typical case, where switches support role request messages
424 * the next state is where we expect the role reply message.
425 * In the special case that where the switch does not support any kind
426 * of role request messages, we don't send a role message, but we do
427 * request mastership from the registry service. This controller
428 * should become master once we hear back from the registry service.
429 * All following states will have a h.sw instance!
430 */
431 WAIT_DESCRIPTION_STAT_REPLY(false) {
432 @Override
433 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
434 throws SwitchStateException {
435 // Read description, if it has been updated
436 if (m.getStatsType() != OFStatsType.DESC) {
437 log.warn("Expecting Description stats but received stats "
438 + "type {} from {}. Ignoring ...", m.getStatsType(),
439 h.channel.getRemoteAddress());
440 return;
441 }
tom7ef8ff92014-09-17 13:08:06 -0700442 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700443 log.info("Received switch description reply {} from switch at {}",
444 drep, h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700445 // Here is where we differentiate between different kinds of switches
446 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
447
448 h.sw.setOFVersion(h.ofVersion);
449 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700450 //h.sw.setPortDescReply(h.portDescReply);
451 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100452 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700453 h.sw.setConnected(true);
454 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700455// boolean success = h.sw.connectSwitch();
456//
457// if (!success) {
458// disconnectDuplicate(h);
459// return;
460// }
tom7ef8ff92014-09-17 13:08:06 -0700461 // set switch information
462
463
464
alshabib09d48be2014-10-03 15:43:33 -0700465 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800466 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700467 //Put switch in EQUAL mode until we hear back from the global registry
468 //log.debug("Setting new switch {} to EQUAL and sending Role request",
469 // h.sw.getStringId());
470 //h.sw.activateEqualSwitch();
471 //h.setSwitchRole(RoleState.EQUAL);
472
473 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700474 if (h.sw.isDriverHandshakeComplete()) {
475 if (!h.sw.connectSwitch()) {
476 disconnectDuplicate(h);
477 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800478 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700479 h.setState(ACTIVE);
480 } else {
481 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
482 }
tom7ef8ff92014-09-17 13:08:06 -0700483
484 }
485
486 @Override
487 void processOFError(OFChannelHandler h, OFErrorMsg m) {
488 logErrorDisconnect(h, m);
489 }
490
491 @Override
492 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
493 throws IOException, SwitchStateException {
494 illegalMessageReceived(h, m);
495 }
496
497 @Override
498 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
499 throws IOException {
500 h.pendingPortStatusMsg.add(m);
501 }
502 },
503
504
505 /**
506 * We are waiting for the respective switch driver to complete its
507 * configuration. Notice that we do not consider this to be part of the main
508 * switch-controller handshake. But we do consider it as a step that comes
509 * before we declare the switch as available to the controller.
510 * Next State: depends on the role of this controller for this switch - either
511 * MASTER or EQUAL.
512 */
513 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
514
515 @Override
516 void processOFError(OFChannelHandler h, OFErrorMsg m)
517 throws IOException {
518 // will never be called. We override processOFMessage
519 }
520
alshabibd7963912014-10-20 14:52:04 -0700521
522
tom7ef8ff92014-09-17 13:08:06 -0700523 @Override
524 void processOFMessage(OFChannelHandler h, OFMessage m)
525 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700526
527 if (h.sw.isDriverHandshakeComplete()) {
528 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700529 h.state.processOFMessage(h, m);
530 return;
alshabibd7963912014-10-20 14:52:04 -0700531
532 }
533
tom7ef8ff92014-09-17 13:08:06 -0700534 if (m.getType() == OFType.ECHO_REQUEST) {
535 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700536 } else if (m.getType() == OFType.ECHO_REPLY) {
537 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700538 } else if (m.getType() == OFType.ROLE_REPLY) {
539 h.sw.handleRole(m);
540 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800541 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700542 h.sw.processDriverHandshakeMessage(m);
543 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700544 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700545 }
546 }
547 } else {
548 if (m.getType() == OFType.EXPERIMENTER &&
549 ((OFExperimenter) m).getExperimenter() ==
550 RoleManager.NICIRA_EXPERIMENTER) {
551 h.sw.handleNiciraRole(m);
552 } else {
553 h.sw.processDriverHandshakeMessage(m);
554 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700555 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700556 }
557 }
558 }
559 }
560
561 @Override
562 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
563 throws IOException, SwitchStateException {
564 h.pendingPortStatusMsg.add(m);
565 }
alshabibd7963912014-10-20 14:52:04 -0700566
567 private void moveToActive(OFChannelHandler h) {
568 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800569 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700570 h.setState(ACTIVE);
571 if (!success) {
572 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700573 }
574 }
575
tom7ef8ff92014-09-17 13:08:06 -0700576 },
577
Jordi Ortiz91477b82016-11-29 15:22:50 +0100578 /**
579 * We are expecting a OF Multi Part Meter Features Stats Reply.
580 * Notice that this information is only available for switches running
581 * OpenFlow 1.3
582 */
583 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800584
585 @Override
586 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
587 throws IOException {
588 super.processOFEchoRequest(h, m);
589 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
590 log.info("{} did not respond to MeterFeaturesRequest on time, " +
591 "moving on without it.",
592 h.getSwitchInfoString());
593 h.sendHandshakeDescriptionStatsRequest();
594 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
595 }
596 }
597
Jordi Ortiz91477b82016-11-29 15:22:50 +0100598 @Override
599 void processOFError(OFChannelHandler h, OFErrorMsg m)
600 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800601 // Hardware switches may reply OFErrorMsg if meter is not supported
602 log.warn("Received OFError {}. It seems {} does not support Meter.",
603 m.getErrType().name(), Dpid.uri(h.thisdpid));
604 log.debug("{}", m);
605 h.sendHandshakeDescriptionStatsRequest();
606 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100607 }
608
609 @Override
610 void processOFStatisticsReply(OFChannelHandler h,
611 OFStatsReply m)
612 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800613 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100614 case METER_FEATURES:
615
616 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800617 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100618 log.info("Received meter features from {} with max meters: {}",
619 h.getSwitchInfoString(),
620 ofmfsr.getFeatures().getMaxMeter());
621 h.meterFeaturesReply = ofmfsr;
622 h.sendHandshakeDescriptionStatsRequest();
623 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
624 break;
625 default:
626 log.error("Unexpected OF Multi Part stats reply");
627 illegalMessageReceived(h, m);
628 break;
629 }
630 }
631
632 @Override
633 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
634 throws IOException, SwitchStateException {
635 illegalMessageReceived(h, m);
636 }
637
638 @Override
639 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
640 throws IOException {
641 h.pendingPortStatusMsg.add(m);
642 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800643
644 @Override
645 void processIdle(OFChannelHandler h) throws IOException {
646 log.info("{} did not respond to MeterFeaturesRequest, " +
647 "moving on without it.",
648 h.getSwitchInfoString());
649 h.sendHandshakeDescriptionStatsRequest();
650 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
651 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100652 },
653
tom7ef8ff92014-09-17 13:08:06 -0700654
655 /**
656 * This controller is in MASTER role for this switch. We enter this state
657 * after requesting and winning control from the global registry.
658 * The main handshake as well as the switch-driver sub-handshake
659 * is complete at this point.
660 * // XXX S reconsider below
661 * In the (near) future we may deterministically assign controllers to
662 * switches at startup.
663 * We only leave this state if the switch disconnects or
664 * if we send a role request for SLAVE /and/ receive the role reply for
665 * SLAVE.
666 */
667 ACTIVE(true) {
668 @Override
669 void processOFError(OFChannelHandler h, OFErrorMsg m)
670 throws IOException, SwitchStateException {
671 // if we get here, then the error message is for something else
672 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700673 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700674 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700675 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700676 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700677 // We are the master controller and the switch returned
678 // a permission error. This is a likely indicator that
679 // the switch thinks we are slave. Reassert our
680 // role
681 // FIXME: this could be really bad during role transitions
682 // if two controllers are master (even if its only for
683 // a brief period). We might need to see if these errors
684 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700685
tom7ef8ff92014-09-17 13:08:06 -0700686 h.sw.reassertRole();
687 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
688 ((OFFlowModFailedErrorMsg) m).getCode() ==
689 OFFlowModFailedCode.ALL_TABLES_FULL) {
690 h.sw.setTableFull(true);
691 } else {
692 logError(h, m);
693 }
694 h.dispatchMessage(m);
695 }
696
697 @Override
698 void processOFStatisticsReply(OFChannelHandler h,
699 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700700 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
701 h.sw.setPortDescReply((OFPortDescStatsReply) m);
702 }
tom7ef8ff92014-09-17 13:08:06 -0700703 h.dispatchMessage(m);
704 }
705
706 @Override
707 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
708 throws SwitchStateException {
709 h.sw.handleNiciraRole(m);
710 }
711
712 @Override
713 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
714 throws SwitchStateException {
715 h.sw.handleRole(m);
716 }
717
718 @Override
719 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
720 throws SwitchStateException {
721 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800722 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700723 }
724
725 @Override
726 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700727// OFPacketOut out =
728// h.sw.factory().buildPacketOut()
729// .setXid(m.getXid())
730// .setBufferId(m.getBufferId()).build();
731// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700732 h.dispatchMessage(m);
733 }
734
735 @Override
736 void processOFFlowRemoved(OFChannelHandler h,
737 OFFlowRemoved m) {
738 h.dispatchMessage(m);
739 }
740
741 @Override
742 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
743 h.dispatchMessage(m);
744 }
745
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700746 @Override
747 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700748 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700749 h.dispatchMessage(m);
750 }
751
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800752 @Override
753 void processIdle(OFChannelHandler h) throws IOException {
754 log.info("{} idle", h.getSwitchInfoString());
755 }
756
tom7ef8ff92014-09-17 13:08:06 -0700757 };
758
759 private final boolean handshakeComplete;
760 ChannelState(boolean handshakeComplete) {
761 this.handshakeComplete = handshakeComplete;
762 }
763
764 /**
765 * Is this a state in which the handshake has completed?
766 * @return true if the handshake is complete
767 */
768 public boolean isHandshakeComplete() {
769 return handshakeComplete;
770 }
771
772 /**
773 * Get a string specifying the switch connection, state, and
774 * message received. To be used as message for SwitchStateException
775 * or log messages
776 * @param h The channel handler (to get switch information_
777 * @param m The OFMessage that has just been received
778 * @param details A string giving more details about the exact nature
779 * of the problem.
780 * @return display string
781 */
782 // needs to be protected because enum members are actually subclasses
783 protected String getSwitchStateMessage(OFChannelHandler h,
784 OFMessage m,
785 String details) {
786 return String.format("Switch: [%s], State: [%s], received: [%s]"
787 + ", details: %s",
788 h.getSwitchInfoString(),
789 this.toString(),
790 m.getType().toString(),
791 details);
792 }
793
794 /**
795 * We have an OFMessage we didn't expect given the current state and
796 * we want to treat this as an error.
797 * We currently throw an exception that will terminate the connection
798 * However, we could be more forgiving
799 * @param h the channel handler that received the message
800 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700801 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700802 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700803 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700804 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
805 throws SwitchStateException {
806 String msg = getSwitchStateMessage(h, m,
807 "Switch should never send this message in the current state");
808 throw new SwitchStateException(msg);
809
810 }
811
812 /**
813 * We have an OFMessage we didn't expect given the current state and
814 * we want to ignore the message.
815 * @param h the channel handler the received the message
816 * @param m the message
817 */
818 protected void unhandledMessageReceived(OFChannelHandler h,
819 OFMessage m) {
820 if (log.isDebugEnabled()) {
821 String msg = getSwitchStateMessage(h, m,
822 "Ignoring unexpected message");
823 log.debug(msg);
824 }
825 }
826
827 /**
828 * Log an OpenFlow error message from a switch.
829 * @param h The switch that sent the error
830 * @param error The error message
831 */
832 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700833 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700834 error,
835 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800836 this);
tom7ef8ff92014-09-17 13:08:06 -0700837 }
838
839 /**
840 * Log an OpenFlow error message from a switch and disconnect the
841 * channel.
842 *
843 * @param h the IO channel for this switch.
844 * @param error The error message
845 */
846 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
847 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700848 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700849 h.channel.disconnect();
850 }
851
852 /**
853 * log an error message for a duplicate dpid and disconnect this channel.
854 * @param h the IO channel for this switch.
855 */
856 protected void disconnectDuplicate(OFChannelHandler h) {
857 log.error("Duplicated dpid or incompleted cleanup - "
858 + "disconnecting channel {}", h.getSwitchInfoString());
859 h.duplicateDpidFound = Boolean.TRUE;
860 h.channel.disconnect();
861 }
862
863
864
865 /**
866 * Handles all pending port status messages before a switch is declared
867 * activated in MASTER or EQUAL role. Note that since this handling
868 * precedes the activation (and therefore notification to IOFSwitchListerners)
869 * the changes to ports will already be visible once the switch is
870 * activated. As a result, no notifications are sent out for these
871 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700872 *
873 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700874 */
875 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
876 try {
877 handlePendingPortStatusMessages(h, 0);
878 } catch (SwitchStateException e) {
879 log.error(e.getMessage());
880 }
881 }
882
883 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
884 throws SwitchStateException {
885 if (h.sw == null) {
886 String msg = "State machine error: switch is null. Should never " +
887 "happen";
888 throw new SwitchStateException(msg);
889 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800890 log.info("Processing {} pending port status messages for {}",
891 h.pendingPortStatusMsg.size(), h.sw.getStringId());
892
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800893 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700894 for (OFPortStatus ps: h.pendingPortStatusMsg) {
895 temp.add(ps);
896 handlePortStatusMessage(h, ps, false);
897 }
tom7ef8ff92014-09-17 13:08:06 -0700898 // expensive but ok - we don't expect too many port-status messages
899 // note that we cannot use clear(), because of the reasons below
900 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800901 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700902 // the iterator above takes a snapshot of the list - so while we were
903 // dealing with the pending port-status messages, we could have received
904 // newer ones. Handle them recursively, but break the recursion after
905 // five steps to avoid an attack.
906 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
907 handlePendingPortStatusMessages(h, index);
908 }
909 }
910
911 /**
912 * Handle a port status message.
913 *
914 * Handle a port status message by updating the port maps in the
915 * IOFSwitch instance and notifying Controller about the change so
916 * it can dispatch a switch update.
917 *
918 * @param h The OFChannelHhandler that received the message
919 * @param m The PortStatus message we received
920 * @param doNotify if true switch port changed events will be
921 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800922 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700923 *
924 */
925 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
926 boolean doNotify) throws SwitchStateException {
927 if (h.sw == null) {
928 String msg = getSwitchStateMessage(h, m,
929 "State machine error: switch is null. Should never " +
930 "happen");
931 throw new SwitchStateException(msg);
932 }
933
934 h.sw.handleMessage(m);
935 }
936
937
938 /**
939 * Process an OF message received on the channel and
940 * update state accordingly.
941 *
942 * The main "event" of the state machine. Process the received message,
943 * send follow up message if required and update state if required.
944 *
945 * Switches on the message type and calls more specific event handlers
946 * for each individual OF message type. If we receive a message that
947 * is supposed to be sent from a controller to a switch we throw
948 * a SwitchStateExeption.
949 *
950 * The more specific handlers can also throw SwitchStateExceptions
951 *
952 * @param h The OFChannelHandler that received the message
953 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800954 * @throws SwitchStateException if the switch is not bound to the channel
955 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700956 */
957 void processOFMessage(OFChannelHandler h, OFMessage m)
958 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800959 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -0700960 case HELLO:
961 processOFHello(h, (OFHello) m);
962 break;
963 case BARRIER_REPLY:
964 processOFBarrierReply(h, (OFBarrierReply) m);
965 break;
966 case ECHO_REPLY:
967 processOFEchoReply(h, (OFEchoReply) m);
968 break;
969 case ECHO_REQUEST:
970 processOFEchoRequest(h, (OFEchoRequest) m);
971 break;
972 case ERROR:
973 processOFError(h, (OFErrorMsg) m);
974 break;
975 case FEATURES_REPLY:
976 processOFFeaturesReply(h, (OFFeaturesReply) m);
977 break;
978 case FLOW_REMOVED:
979 processOFFlowRemoved(h, (OFFlowRemoved) m);
980 break;
981 case GET_CONFIG_REPLY:
982 processOFGetConfigReply(h, (OFGetConfigReply) m);
983 break;
984 case PACKET_IN:
985 processOFPacketIn(h, (OFPacketIn) m);
986 break;
987 case PORT_STATUS:
988 processOFPortStatus(h, (OFPortStatus) m);
989 break;
990 case QUEUE_GET_CONFIG_REPLY:
991 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
992 break;
993 case STATS_REPLY: // multipart_reply in 1.3
994 processOFStatisticsReply(h, (OFStatsReply) m);
995 break;
996 case EXPERIMENTER:
997 processOFExperimenter(h, (OFExperimenter) m);
998 break;
999 case ROLE_REPLY:
1000 processOFRoleReply(h, (OFRoleReply) m);
1001 break;
1002 case GET_ASYNC_REPLY:
1003 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1004 break;
1005
1006 // The following messages are sent to switches. The controller
1007 // should never receive them
1008 case SET_CONFIG:
1009 case GET_CONFIG_REQUEST:
1010 case PACKET_OUT:
1011 case PORT_MOD:
1012 case QUEUE_GET_CONFIG_REQUEST:
1013 case BARRIER_REQUEST:
1014 case STATS_REQUEST: // multipart request in 1.3
1015 case FEATURES_REQUEST:
1016 case FLOW_MOD:
1017 case GROUP_MOD:
1018 case TABLE_MOD:
1019 case GET_ASYNC_REQUEST:
1020 case SET_ASYNC:
1021 case METER_MOD:
1022 default:
1023 illegalMessageReceived(h, m);
1024 break;
1025 }
1026 }
1027
1028 /*-----------------------------------------------------------------
1029 * Default implementation for message handlers in any state.
1030 *
1031 * Individual states must override these if they want a behavior
1032 * that differs from the default.
1033 *
1034 * In general, these handlers simply ignore the message and do
1035 * nothing.
1036 *
1037 * There are some exceptions though, since some messages really
1038 * are handled the same way in every state (e.g., ECHO_REQUST) or
1039 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1040 -----------------------------------------------------------------*/
1041
1042 void processOFHello(OFChannelHandler h, OFHello m)
1043 throws IOException, SwitchStateException {
1044 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001045 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
1046 h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001047 }
1048
1049 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1050 throws IOException {
1051 // Silently ignore.
1052 }
1053
1054 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1055 throws IOException {
1056 if (h.ofVersion == null) {
1057 log.error("No OF version set for {}. Not sending Echo REPLY",
1058 h.channel.getRemoteAddress());
1059 return;
1060 }
1061 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
1062 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
1063 OFEchoReply reply = factory
1064 .buildEchoReply()
1065 .setXid(m.getXid())
1066 .setData(m.getData())
1067 .build();
1068 h.channel.write(Collections.singletonList(reply));
1069 }
1070
1071 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1072 throws IOException {
1073 // Do nothing with EchoReplies !!
1074 }
1075
1076 // no default implementation for OFError
1077 // every state must override it
1078 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1079 throws IOException, SwitchStateException;
1080
1081
1082 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1083 throws IOException, SwitchStateException {
1084 unhandledMessageReceived(h, m);
1085 }
1086
1087 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1088 throws IOException {
1089 unhandledMessageReceived(h, m);
1090 }
1091
1092 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1093 throws IOException, SwitchStateException {
1094 // we only expect config replies in the WAIT_CONFIG_REPLY state
1095 illegalMessageReceived(h, m);
1096 }
1097
1098 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1099 throws IOException {
1100 unhandledMessageReceived(h, m);
1101 }
1102
1103 // no default implementation. Every state needs to handle it.
1104 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1105 throws IOException, SwitchStateException;
1106
1107 void processOFQueueGetConfigReply(OFChannelHandler h,
1108 OFQueueGetConfigReply m)
1109 throws IOException {
1110 unhandledMessageReceived(h, m);
1111 }
1112
1113 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1114 throws IOException, SwitchStateException {
1115 unhandledMessageReceived(h, m);
1116 }
1117
1118 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1119 throws IOException, SwitchStateException {
1120 // TODO: it might make sense to parse the vendor message here
1121 // into the known vendor messages we support and then call more
1122 // specific event handlers
1123 unhandledMessageReceived(h, m);
1124 }
1125
1126 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1127 throws SwitchStateException, IOException {
1128 unhandledMessageReceived(h, m);
1129 }
1130
1131 void processOFGetAsyncReply(OFChannelHandler h,
1132 OFAsyncGetReply m) {
1133 unhandledMessageReceived(h, m);
1134 }
1135
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001136 void processIdle(OFChannelHandler h) throws IOException {
1137 // disconnect channel which did no complete handshake
1138 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1139 h.channel.disconnect();
1140 }
tom7ef8ff92014-09-17 13:08:06 -07001141 }
1142
1143
1144
1145 //*************************
1146 // Channel handler methods
1147 //*************************
1148
1149 @Override
1150 public void channelConnected(ChannelHandlerContext ctx,
1151 ChannelStateEvent e) throws Exception {
1152 channel = e.getChannel();
1153 log.info("New switch connection from {}",
1154 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001155 /*
1156 hack to wait for the switch to tell us what it's
1157 max version is. This is not spec compliant and should
1158 be removed as soon as switches behave better.
1159 */
1160 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001161 setState(ChannelState.WAIT_HELLO);
1162 }
1163
1164 @Override
1165 public void channelDisconnected(ChannelHandlerContext ctx,
1166 ChannelStateEvent e) throws Exception {
1167 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1168 getSwitchInfoString());
1169 if (thisdpid != 0) {
1170 if (!duplicateDpidFound) {
1171 // if the disconnected switch (on this ChannelHandler)
1172 // was not one with a duplicate-dpid, it is safe to remove all
1173 // state for it at the controller. Notice that if the disconnected
1174 // switch was a duplicate-dpid, calling the method below would clear
1175 // all state for the original switch (with the same dpid),
1176 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001177 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001178 if (sw != null) {
1179 sw.removeConnectedSwitch();
1180 }
tom7ef8ff92014-09-17 13:08:06 -07001181 } else {
1182 // A duplicate was disconnected on this ChannelHandler,
1183 // this is the same switch reconnecting, but the original state was
1184 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001185 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001186 duplicateDpidFound = Boolean.FALSE;
1187 }
1188 } else {
1189 log.warn("no dpid in channelHandler registered for "
1190 + "disconnected switch {}", getSwitchInfoString());
1191 }
1192 }
1193
1194 @Override
1195 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1196 throws Exception {
1197 if (e.getCause() instanceof ReadTimeoutException) {
1198 // switch timeout
1199 log.error("Disconnecting switch {} due to read timeout",
1200 getSwitchInfoString());
1201 ctx.getChannel().close();
1202 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1203 log.error("Disconnecting switch {}: failed to complete handshake",
1204 getSwitchInfoString());
1205 ctx.getChannel().close();
1206 } else if (e.getCause() instanceof ClosedChannelException) {
1207 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1208 } else if (e.getCause() instanceof IOException) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001209 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1210 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1211 log.error("Disconnecting switch {} due to IO Error: {}",
1212 getSwitchInfoString(), e.getCause().getMessage());
1213 if (log.isDebugEnabled()) {
1214 // still print stack trace if debug is enabled
1215 log.debug("StackTrace for previous Exception: ", e.getCause());
1216 }
tom7ef8ff92014-09-17 13:08:06 -07001217 }
1218 ctx.getChannel().close();
1219 } else if (e.getCause() instanceof SwitchStateException) {
1220 log.error("Disconnecting switch {} due to switch state error: {}",
1221 getSwitchInfoString(), e.getCause().getMessage());
1222 if (log.isDebugEnabled()) {
1223 // still print stack trace if debug is enabled
1224 log.debug("StackTrace for previous Exception: ", e.getCause());
1225 }
1226 ctx.getChannel().close();
1227 } else if (e.getCause() instanceof OFParseError) {
1228 log.error("Disconnecting switch "
1229 + getSwitchInfoString() +
1230 " due to message parse failure",
1231 e.getCause());
1232 ctx.getChannel().close();
1233 } else if (e.getCause() instanceof RejectedExecutionException) {
1234 log.warn("Could not process message: queue full");
1235 } else {
1236 log.error("Error while processing message from switch "
1237 + getSwitchInfoString()
1238 + "state " + this.state, e.getCause());
1239 ctx.getChannel().close();
1240 }
1241 }
1242
1243 @Override
1244 public String toString() {
1245 return getSwitchInfoString();
1246 }
1247
1248 @Override
1249 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1250 throws Exception {
1251 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1252 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001253 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001254 e.getChannel().getPipeline().getLast());
tom7ef8ff92014-09-17 13:08:06 -07001255 e.getChannel().write(Collections.singletonList(m));
1256 // XXX S some problems here -- echo request has no transaction id, and
1257 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001258 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001259 }
1260
1261 @Override
1262 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1263 throws Exception {
1264 if (e.getMessage() instanceof List) {
1265 @SuppressWarnings("unchecked")
1266 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1267
1268
1269 for (OFMessage ofm : msglist) {
1270 // Do the actual packet processing
1271 state.processOFMessage(this, ofm);
1272 }
1273 } else {
1274 state.processOFMessage(this, (OFMessage) e.getMessage());
1275 }
1276 }
1277
1278
1279
1280 //*************************
1281 // Channel utility methods
1282 //*************************
1283
1284 /**
1285 * Is this a state in which the handshake has completed?
1286 * @return true if the handshake is complete
1287 */
1288 public boolean isHandshakeComplete() {
1289 return this.state.isHandshakeComplete();
1290 }
1291
1292 private void dispatchMessage(OFMessage m) {
1293 sw.handleMessage(m);
1294 }
1295
1296 /**
1297 * Return a string describing this switch based on the already available
1298 * information (DPID and/or remote socket).
1299 * @return display string
1300 */
1301 private String getSwitchInfoString() {
1302 if (sw != null) {
1303 return sw.toString();
1304 }
1305 String channelString;
1306 if (channel == null || channel.getRemoteAddress() == null) {
1307 channelString = "?";
1308 } else {
1309 channelString = channel.getRemoteAddress().toString();
1310 }
1311 String dpidString;
1312 if (featuresReply == null) {
1313 dpidString = "?";
1314 } else {
1315 dpidString = featuresReply.getDatapathId().toString();
1316 }
1317 return String.format("[%s DPID[%s]]", channelString, dpidString);
1318 }
1319
1320 /**
1321 * Update the channels state. Only called from the state machine.
1322 * TODO: enforce restricted state transitions
1323 * @param state
1324 */
1325 private void setState(ChannelState state) {
1326 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001327 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001328 }
1329
1330 /**
1331 * Send hello message to the switch using the handshake transactions ids.
1332 * @throws IOException
1333 */
1334 private void sendHandshakeHelloMessage() throws IOException {
1335 // The OF protocol requires us to start things off by sending the highest
1336 // version of the protocol supported.
1337
1338 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1339 // see Sec. 7.5.1 of the OF1.3.4 spec
1340 U32 bitmap = U32.ofRaw(0x00000012);
1341 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1342 .setBitmaps(Collections.singletonList(bitmap))
1343 .build();
1344 OFMessage.Builder mb = factory13.buildHello()
1345 .setXid(this.handshakeTransactionIds--)
1346 .setElements(Collections.singletonList(hem));
1347 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1348 channel.write(Collections.singletonList(mb.build()));
1349 }
1350
1351 /**
1352 * Send featuresRequest msg to the switch using the handshake transactions ids.
1353 * @throws IOException
1354 */
1355 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1356 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001357 log.debug("Sending FEATURES_REQUEST to {}", channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001358 OFMessage m = factory.buildFeaturesRequest()
1359 .setXid(this.handshakeTransactionIds--)
1360 .build();
1361 channel.write(Collections.singletonList(m));
1362 }
1363
1364 /**
1365 * Send the configuration requests to tell the switch we want full
1366 * packets.
1367 * @throws IOException
1368 */
1369 private void sendHandshakeSetConfig() throws IOException {
1370 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001371 log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001372 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001373
1374 // Ensure we receive the full packet via PacketIn
1375 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001376 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001377 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001378 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001379 OFSetConfig sc = factory
1380 .buildSetConfig()
1381 .setMissSendLen((short) 0xffff)
1382 .setXid(this.handshakeTransactionIds--)
1383 .build();
1384 msglist.add(sc);
1385 }
tom7ef8ff92014-09-17 13:08:06 -07001386
1387 // Barrier
1388 OFBarrierRequest br = factory
1389 .buildBarrierRequest()
1390 .setXid(this.handshakeTransactionIds--)
1391 .build();
1392 msglist.add(br);
1393
1394 // Verify (need barrier?)
1395 OFGetConfigRequest gcr = factory
1396 .buildGetConfigRequest()
1397 .setXid(this.handshakeTransactionIds--)
1398 .build();
1399 msglist.add(gcr);
1400 channel.write(msglist);
1401 }
1402
1403 /**
1404 * send a description state request.
1405 * @throws IOException
1406 */
1407 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1408 // Get Description to set switch-specific flags
1409 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001410 log.debug("Sending DESC_STATS_REQUEST to {}", channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001411 OFDescStatsRequest dreq = factory
1412 .buildDescStatsRequest()
1413 .setXid(handshakeTransactionIds--)
1414 .build();
1415 channel.write(Collections.singletonList(dreq));
1416 }
1417
Jordi Ortiz91477b82016-11-29 15:22:50 +01001418 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001419 * send a meter features request.
1420 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001421 * @throws IOException
1422 */
1423 private void sendMeterFeaturesRequest() throws IOException {
1424 // Get meter features including the MaxMeters value available for the device
1425 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001426 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.getRemoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001427 OFMeterFeaturesStatsRequest mfreq = factory
1428 .buildMeterFeaturesStatsRequest()
1429 .setXid(handshakeTransactionIds--)
1430 .build();
1431 channel.write(Collections.singletonList(mfreq));
1432 }
1433
tom7ef8ff92014-09-17 13:08:06 -07001434 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001435 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001436 // Get port description for 1.3 switch
1437 OFPortDescStatsRequest preq = factory13
1438 .buildPortDescStatsRequest()
1439 .setXid(handshakeTransactionIds--)
1440 .build();
1441 channel.write(Collections.singletonList(preq));
1442 }
1443
1444 ChannelState getStateForTesting() {
1445 return state;
1446 }
1447
1448}