blob: 100203fbc50119b1c11e2a835aa13bcff4b4ea0b [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;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080024import java.util.Optional;
tom7ef8ff92014-09-17 13:08:06 -070025import java.util.concurrent.CopyOnWriteArrayList;
26import java.util.concurrent.RejectedExecutionException;
27
28import org.jboss.netty.channel.Channel;
29import org.jboss.netty.channel.ChannelHandlerContext;
30import org.jboss.netty.channel.ChannelStateEvent;
31import org.jboss.netty.channel.ExceptionEvent;
32import org.jboss.netty.channel.MessageEvent;
33import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
34import org.jboss.netty.handler.timeout.IdleStateEvent;
35import org.jboss.netty.handler.timeout.ReadTimeoutException;
Charles Chan34155e52016-11-30 18:28:11 -080036import org.onosproject.openflow.controller.Dpid;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
38import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070039import org.projectfloodlight.openflow.exceptions.OFParseError;
40import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
41import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
42import org.projectfloodlight.openflow.protocol.OFBarrierReply;
43import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
44import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
45import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
46import org.projectfloodlight.openflow.protocol.OFEchoReply;
47import org.projectfloodlight.openflow.protocol.OFEchoRequest;
48import org.projectfloodlight.openflow.protocol.OFErrorMsg;
49import org.projectfloodlight.openflow.protocol.OFErrorType;
50import org.projectfloodlight.openflow.protocol.OFExperimenter;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080051import org.projectfloodlight.openflow.protocol.OFFactories;
tom7ef8ff92014-09-17 13:08:06 -070052import org.projectfloodlight.openflow.protocol.OFFactory;
53import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
54import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
55import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
56import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
57import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
58import org.projectfloodlight.openflow.protocol.OFHello;
59import org.projectfloodlight.openflow.protocol.OFHelloElem;
60import org.projectfloodlight.openflow.protocol.OFMessage;
Jordi Ortiz91477b82016-11-29 15:22:50 +010061import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
62import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsRequest;
tom7ef8ff92014-09-17 13:08:06 -070063import org.projectfloodlight.openflow.protocol.OFPacketIn;
64import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
65import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
66import org.projectfloodlight.openflow.protocol.OFPortStatus;
67import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
68import org.projectfloodlight.openflow.protocol.OFRoleReply;
69import org.projectfloodlight.openflow.protocol.OFSetConfig;
70import org.projectfloodlight.openflow.protocol.OFStatsReply;
71import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
72import org.projectfloodlight.openflow.protocol.OFStatsType;
73import org.projectfloodlight.openflow.protocol.OFType;
74import org.projectfloodlight.openflow.protocol.OFVersion;
75import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
76import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
77import org.projectfloodlight.openflow.types.U32;
78import org.slf4j.Logger;
79import org.slf4j.LoggerFactory;
80
81/**
82 * Channel handler deals with the switch connection and dispatches
83 * switch messages to the appropriate locations.
84 */
85class OFChannelHandler extends IdleStateAwareChannelHandler {
86 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -070087
88 private static final String RESET_BY_PEER = "Connection reset by peer";
89 private static final String BROKEN_PIPE = "Broken pipe";
90
tom7ef8ff92014-09-17 13:08:06 -070091 private final Controller controller;
92 private OpenFlowSwitchDriver sw;
93 private long thisdpid; // channelHandler cached value of connected switch id
94 private Channel channel;
95 // State needs to be volatile because the HandshakeTimeoutHandler
96 // needs to check if the handshake is complete
97 private volatile ChannelState state;
98
Yuta HIGUCHI10f45132017-03-01 17:09:32 -080099 /**
100 * Timeout in ms to wait for meter feature reply.
101 */
102 private static final long METER_TIMEOUT = 60_000;
103
104 private volatile long lastStateChange = System.currentTimeMillis();
105
tom7ef8ff92014-09-17 13:08:06 -0700106 // When a switch with a duplicate dpid is found (i.e we already have a
107 // connected switch with the same dpid), the new switch is immediately
108 // disconnected. At that point netty callsback channelDisconnected() which
109 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
110 // switch state for the older (still connected) switch
111 private volatile Boolean duplicateDpidFound;
112
113 // Temporary storage for switch-features and port-description
114 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700115 private List<OFPortDescStatsReply> portDescReplies;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100116 private OFMeterFeaturesStatsReply meterFeaturesReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700117 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700118 // a concurrent ArrayList to temporarily store port status messages
119 // before we are ready to deal with them
120 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
121
122 //Indicates the openflow version used by this switch
123 protected OFVersion ofVersion;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800124
125 // deprecated in 1.10.0
126 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700127 protected OFFactory factory13;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800128 // deprecated in 1.10.0
129 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700130 protected OFFactory factory10;
131
132 /** transaction Ids to use during handshake. Since only one thread
133 * calls into an OFChannelHandler instance, we don't need atomic.
134 * We will count down
135 */
136 private int handshakeTransactionIds = -1;
137
138 /**
139 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800140 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700141 */
142 OFChannelHandler(Controller controller) {
143 this.controller = controller;
144 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800145 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
146 this.portDescReplies = new ArrayList<>();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800147 factory13 = OFFactories.getFactory(OFVersion.OF_13);
148 factory10 = OFFactories.getFactory(OFVersion.OF_10);
tom7ef8ff92014-09-17 13:08:06 -0700149 duplicateDpidFound = Boolean.FALSE;
150 }
151
152
153
154 // XXX S consider if necessary
155 public void disconnectSwitch() {
156 sw.disconnectSwitch();
157 }
158
159
160
161 //*************************
162 // Channel State Machine
163 //*************************
164
165 /**
166 * The state machine for handling the switch/channel state. All state
167 * transitions should happen from within the state machine (and not from other
168 * parts of the code)
169 */
170 enum ChannelState {
171 /**
172 * Initial state before channel is connected.
173 */
174 INIT(false) {
175 @Override
176 void processOFMessage(OFChannelHandler h, OFMessage m)
177 throws IOException, SwitchStateException {
178 illegalMessageReceived(h, m);
179 }
180
181 @Override
182 void processOFError(OFChannelHandler h, OFErrorMsg m)
183 throws IOException {
184 // need to implement since its abstract but it will never
185 // be called
186 }
187
188 @Override
189 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
190 throws IOException {
191 unhandledMessageReceived(h, m);
192 }
193 },
194
195 /**
196 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
197 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
198 * protocol version is accepted.
199 * We send an OFFeaturesRequest depending on the protocol version selected
200 * Next state is WAIT_FEATURES_REPLY
201 */
202 WAIT_HELLO(false) {
203 @Override
204 void processOFHello(OFChannelHandler h, OFHello m)
205 throws IOException {
206 // TODO We could check for the optional bitmap, but for now
207 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500208 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
209 log.debug("Received {} Hello from {} - switching to OF "
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800210 + "version 1.3+", m.getVersion(),
tom7ef8ff92014-09-17 13:08:06 -0700211 h.channel.getRemoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800212 h.ofVersion = m.getVersion();
alshabib70fc7fb2015-01-06 11:04:29 -0800213 h.sendHandshakeHelloMessage();
Chip Boling68bc6562015-07-06 10:00:01 -0500214 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700215 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700216 + "version 1.0", m.getVersion(),
217 h.channel.getRemoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800218 h.ofVersion = m.getVersion();
alshabib70fc7fb2015-01-06 11:04:29 -0800219 OFHello hi =
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800220 OFFactories.getFactory(h.ofVersion).buildHello()
alshabib70fc7fb2015-01-06 11:04:29 -0800221 .setXid(h.handshakeTransactionIds--)
222 .build();
223 h.channel.write(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700224 } else {
225 log.error("Received Hello of version {} from switch at {}. "
226 + "This controller works with OF1.0 and OF1.3 "
227 + "switches. Disconnecting switch ...",
228 m.getVersion(), h.channel.getRemoteAddress());
229 h.channel.disconnect();
230 return;
231 }
232 h.sendHandshakeFeaturesRequestMessage();
233 h.setState(WAIT_FEATURES_REPLY);
234 }
235 @Override
236 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
237 throws IOException, SwitchStateException {
238 illegalMessageReceived(h, m);
239 }
240 @Override
241 void processOFStatisticsReply(OFChannelHandler h,
242 OFStatsReply m)
243 throws IOException, SwitchStateException {
244 illegalMessageReceived(h, m);
245 }
246 @Override
247 void processOFError(OFChannelHandler h, OFErrorMsg m) {
248 logErrorDisconnect(h, m);
249 }
250
251 @Override
252 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
253 throws IOException {
254 unhandledMessageReceived(h, m);
255 }
256 },
257
258
259 /**
260 * We are waiting for a features reply message. Once we receive it, the
261 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
262 * we send a SetConfig request, barrier, and GetConfig request and the
263 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
264 * request and the next state is WAIT_PORT_DESC_REPLY.
265 */
266 WAIT_FEATURES_REPLY(false) {
267 @Override
268 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
269 throws IOException {
270 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700271 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700272 h.getSwitchInfoString(), h.thisdpid);
273
274 h.featuresReply = m; //temp store
275 if (h.ofVersion == OFVersion.OF_10) {
276 h.sendHandshakeSetConfig();
277 h.setState(WAIT_CONFIG_REPLY);
278 } else {
279 //version is 1.3, must get switchport information
280 h.sendHandshakeOFPortDescRequest();
281 h.setState(WAIT_PORT_DESC_REPLY);
282 }
283 }
284 @Override
285 void processOFStatisticsReply(OFChannelHandler h,
286 OFStatsReply m)
287 throws IOException, SwitchStateException {
288 illegalMessageReceived(h, m);
289 }
290 @Override
291 void processOFError(OFChannelHandler h, OFErrorMsg m) {
292 logErrorDisconnect(h, m);
293 }
294
295 @Override
296 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
297 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800298 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700299 }
300 },
301
302 /**
303 * We are waiting for a description of the 1.3 switch ports.
304 * Once received, we send a SetConfig request
305 * Next State is WAIT_CONFIG_REPLY
306 */
307 WAIT_PORT_DESC_REPLY(false) {
308
309 @Override
310 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
311 throws SwitchStateException {
312 // Read port description
313 if (m.getStatsType() != OFStatsType.PORT_DESC) {
314 log.warn("Expecting port description stats but received stats "
315 + "type {} from {}. Ignoring ...", m.getStatsType(),
316 h.channel.getRemoteAddress());
317 return;
318 }
319 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700320 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700321 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700322 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800323 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700324 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800325 } else {
326 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700327 }
328 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
tom7ef8ff92014-09-17 13:08:06 -0700329 log.info("Received port desc reply for switch at {}",
330 h.getSwitchInfoString());
331 try {
332 h.sendHandshakeSetConfig();
333 } catch (IOException e) {
334 log.error("Unable to send setConfig after PortDescReply. "
335 + "Error: {}", e.getMessage());
336 }
337 h.setState(WAIT_CONFIG_REPLY);
338 }
339
340 @Override
341 void processOFError(OFChannelHandler h, OFErrorMsg m)
342 throws IOException, SwitchStateException {
343 logErrorDisconnect(h, m);
344
345 }
346
347 @Override
348 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
349 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800350 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700351
352 }
353 },
354
355 /**
356 * We are waiting for a config reply message. Once we receive it
357 * we send a DescriptionStatsRequest to the switch.
358 * Next state: WAIT_DESCRIPTION_STAT_REPLY
359 */
360 WAIT_CONFIG_REPLY(false) {
361 @Override
362 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
363 throws IOException {
364 if (m.getMissSendLen() == 0xffff) {
365 log.trace("Config Reply from switch {} confirms "
366 + "miss length set to 0xffff",
367 h.getSwitchInfoString());
368 } else {
369 // FIXME: we can't really deal with switches that don't send
370 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800371 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700372 + "miss length set to {}",
373 h.getSwitchInfoString(),
374 m.getMissSendLen());
375 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100376
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800377 nextState(h);
378 }
379
380 /**
381 * Transition to next state based on OF version.
382 *
383 * @param h current channel handler
384 * @throws IOException
385 */
386 private void nextState(OFChannelHandler h) throws IOException {
387 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100388 // Meters were introduced in OpenFlow 1.3
389 h.sendMeterFeaturesRequest();
390 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800391 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100392 h.sendHandshakeDescriptionStatsRequest();
393 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
394 }
tom7ef8ff92014-09-17 13:08:06 -0700395 }
396
397 @Override
398 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
399 // do nothing;
400 }
401
402 @Override
403 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
404 throws IOException, SwitchStateException {
405 illegalMessageReceived(h, m);
406 }
407 @Override
408 void processOFStatisticsReply(OFChannelHandler h,
409 OFStatsReply m)
410 throws IOException, SwitchStateException {
411 log.error("Received multipart(stats) message sub-type {}",
412 m.getStatsType());
413 illegalMessageReceived(h, m);
414 }
415
416 @Override
417 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800418 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
419 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
420 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
421 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
422 try {
423 nextState(h);
424 return;
425 } catch (IOException e) {
426 log.error("Exception thrown transitioning to next", e);
427 logErrorDisconnect(h, m);
428 }
429 }
430 }
tom7ef8ff92014-09-17 13:08:06 -0700431 logErrorDisconnect(h, m);
432 }
433
434 @Override
435 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
436 throws IOException {
437 h.pendingPortStatusMsg.add(m);
438 }
439 },
440
441
442 /**
443 * We are waiting for a OFDescriptionStat message from the switch.
444 * Once we receive any stat message we try to parse it. If it's not
445 * a description stats message we disconnect. If its the expected
446 * description stats message, we:
447 * - use the switch driver to bind the switch and get an IOFSwitch instance
448 * - setup the IOFSwitch instance
449 * - add switch controller and send the initial role
450 * request to the switch.
451 * Next state: WAIT_INITIAL_ROLE
452 * In the typical case, where switches support role request messages
453 * the next state is where we expect the role reply message.
454 * In the special case that where the switch does not support any kind
455 * of role request messages, we don't send a role message, but we do
456 * request mastership from the registry service. This controller
457 * should become master once we hear back from the registry service.
458 * All following states will have a h.sw instance!
459 */
460 WAIT_DESCRIPTION_STAT_REPLY(false) {
461 @Override
462 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
463 throws SwitchStateException {
464 // Read description, if it has been updated
465 if (m.getStatsType() != OFStatsType.DESC) {
466 log.warn("Expecting Description stats but received stats "
467 + "type {} from {}. Ignoring ...", m.getStatsType(),
468 h.channel.getRemoteAddress());
469 return;
470 }
tom7ef8ff92014-09-17 13:08:06 -0700471 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700472 log.info("Received switch description reply {} from switch at {}",
473 drep, h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700474 // Here is where we differentiate between different kinds of switches
475 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
476
477 h.sw.setOFVersion(h.ofVersion);
478 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700479 //h.sw.setPortDescReply(h.portDescReply);
480 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100481 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700482 h.sw.setConnected(true);
483 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700484// boolean success = h.sw.connectSwitch();
485//
486// if (!success) {
487// disconnectDuplicate(h);
488// return;
489// }
tom7ef8ff92014-09-17 13:08:06 -0700490 // set switch information
491
492
493
alshabib09d48be2014-10-03 15:43:33 -0700494 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800495 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700496 //Put switch in EQUAL mode until we hear back from the global registry
497 //log.debug("Setting new switch {} to EQUAL and sending Role request",
498 // h.sw.getStringId());
499 //h.sw.activateEqualSwitch();
500 //h.setSwitchRole(RoleState.EQUAL);
501
502 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700503 if (h.sw.isDriverHandshakeComplete()) {
504 if (!h.sw.connectSwitch()) {
505 disconnectDuplicate(h);
506 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800507 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700508 h.setState(ACTIVE);
509 } else {
510 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
511 }
tom7ef8ff92014-09-17 13:08:06 -0700512
513 }
514
515 @Override
516 void processOFError(OFChannelHandler h, OFErrorMsg m) {
517 logErrorDisconnect(h, m);
518 }
519
520 @Override
521 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
522 throws IOException, SwitchStateException {
523 illegalMessageReceived(h, m);
524 }
525
526 @Override
527 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
528 throws IOException {
529 h.pendingPortStatusMsg.add(m);
530 }
531 },
532
533
534 /**
535 * We are waiting for the respective switch driver to complete its
536 * configuration. Notice that we do not consider this to be part of the main
537 * switch-controller handshake. But we do consider it as a step that comes
538 * before we declare the switch as available to the controller.
539 * Next State: depends on the role of this controller for this switch - either
540 * MASTER or EQUAL.
541 */
542 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
543
544 @Override
545 void processOFError(OFChannelHandler h, OFErrorMsg m)
546 throws IOException {
547 // will never be called. We override processOFMessage
548 }
549
alshabibd7963912014-10-20 14:52:04 -0700550
551
tom7ef8ff92014-09-17 13:08:06 -0700552 @Override
553 void processOFMessage(OFChannelHandler h, OFMessage m)
554 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700555
556 if (h.sw.isDriverHandshakeComplete()) {
557 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700558 h.state.processOFMessage(h, m);
559 return;
alshabibd7963912014-10-20 14:52:04 -0700560
561 }
562
tom7ef8ff92014-09-17 13:08:06 -0700563 if (m.getType() == OFType.ECHO_REQUEST) {
564 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700565 } else if (m.getType() == OFType.ECHO_REPLY) {
566 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700567 } else if (m.getType() == OFType.ROLE_REPLY) {
568 h.sw.handleRole(m);
569 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800570 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700571 h.sw.processDriverHandshakeMessage(m);
572 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700573 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700574 }
575 }
576 } else {
577 if (m.getType() == OFType.EXPERIMENTER &&
578 ((OFExperimenter) m).getExperimenter() ==
579 RoleManager.NICIRA_EXPERIMENTER) {
580 h.sw.handleNiciraRole(m);
581 } else {
582 h.sw.processDriverHandshakeMessage(m);
583 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700584 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700585 }
586 }
587 }
588 }
589
590 @Override
591 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
592 throws IOException, SwitchStateException {
593 h.pendingPortStatusMsg.add(m);
594 }
alshabibd7963912014-10-20 14:52:04 -0700595
596 private void moveToActive(OFChannelHandler h) {
597 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800598 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700599 h.setState(ACTIVE);
600 if (!success) {
601 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700602 }
603 }
604
tom7ef8ff92014-09-17 13:08:06 -0700605 },
606
Jordi Ortiz91477b82016-11-29 15:22:50 +0100607 /**
608 * We are expecting a OF Multi Part Meter Features Stats Reply.
609 * Notice that this information is only available for switches running
610 * OpenFlow 1.3
611 */
612 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800613
614 @Override
615 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
616 throws IOException {
617 super.processOFEchoRequest(h, m);
618 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
619 log.info("{} did not respond to MeterFeaturesRequest on time, " +
620 "moving on without it.",
621 h.getSwitchInfoString());
622 h.sendHandshakeDescriptionStatsRequest();
623 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
624 }
625 }
626
Jordi Ortiz91477b82016-11-29 15:22:50 +0100627 @Override
628 void processOFError(OFChannelHandler h, OFErrorMsg m)
629 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800630 // Hardware switches may reply OFErrorMsg if meter is not supported
631 log.warn("Received OFError {}. It seems {} does not support Meter.",
632 m.getErrType().name(), Dpid.uri(h.thisdpid));
633 log.debug("{}", m);
634 h.sendHandshakeDescriptionStatsRequest();
635 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100636 }
637
638 @Override
639 void processOFStatisticsReply(OFChannelHandler h,
640 OFStatsReply m)
641 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800642 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100643 case METER_FEATURES:
644
645 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800646 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100647 log.info("Received meter features from {} with max meters: {}",
648 h.getSwitchInfoString(),
649 ofmfsr.getFeatures().getMaxMeter());
650 h.meterFeaturesReply = ofmfsr;
651 h.sendHandshakeDescriptionStatsRequest();
652 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
653 break;
654 default:
655 log.error("Unexpected OF Multi Part stats reply");
656 illegalMessageReceived(h, m);
657 break;
658 }
659 }
660
661 @Override
662 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
663 throws IOException, SwitchStateException {
664 illegalMessageReceived(h, m);
665 }
666
667 @Override
668 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
669 throws IOException {
670 h.pendingPortStatusMsg.add(m);
671 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800672
673 @Override
674 void processIdle(OFChannelHandler h) throws IOException {
675 log.info("{} did not respond to MeterFeaturesRequest, " +
676 "moving on without it.",
677 h.getSwitchInfoString());
678 h.sendHandshakeDescriptionStatsRequest();
679 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
680 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100681 },
682
tom7ef8ff92014-09-17 13:08:06 -0700683
684 /**
685 * This controller is in MASTER role for this switch. We enter this state
686 * after requesting and winning control from the global registry.
687 * The main handshake as well as the switch-driver sub-handshake
688 * is complete at this point.
689 * // XXX S reconsider below
690 * In the (near) future we may deterministically assign controllers to
691 * switches at startup.
692 * We only leave this state if the switch disconnects or
693 * if we send a role request for SLAVE /and/ receive the role reply for
694 * SLAVE.
695 */
696 ACTIVE(true) {
697 @Override
698 void processOFError(OFChannelHandler h, OFErrorMsg m)
699 throws IOException, SwitchStateException {
700 // if we get here, then the error message is for something else
701 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700702 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700703 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700704 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700705 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700706 // We are the master controller and the switch returned
707 // a permission error. This is a likely indicator that
708 // the switch thinks we are slave. Reassert our
709 // role
710 // FIXME: this could be really bad during role transitions
711 // if two controllers are master (even if its only for
712 // a brief period). We might need to see if these errors
713 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700714
tom7ef8ff92014-09-17 13:08:06 -0700715 h.sw.reassertRole();
716 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
717 ((OFFlowModFailedErrorMsg) m).getCode() ==
718 OFFlowModFailedCode.ALL_TABLES_FULL) {
719 h.sw.setTableFull(true);
720 } else {
721 logError(h, m);
722 }
723 h.dispatchMessage(m);
724 }
725
726 @Override
727 void processOFStatisticsReply(OFChannelHandler h,
728 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700729 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
730 h.sw.setPortDescReply((OFPortDescStatsReply) m);
731 }
tom7ef8ff92014-09-17 13:08:06 -0700732 h.dispatchMessage(m);
733 }
734
735 @Override
736 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
737 throws SwitchStateException {
738 h.sw.handleNiciraRole(m);
739 }
740
741 @Override
742 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
743 throws SwitchStateException {
744 h.sw.handleRole(m);
745 }
746
747 @Override
748 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
749 throws SwitchStateException {
750 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800751 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700752 }
753
754 @Override
755 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700756// OFPacketOut out =
757// h.sw.factory().buildPacketOut()
758// .setXid(m.getXid())
759// .setBufferId(m.getBufferId()).build();
760// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700761 h.dispatchMessage(m);
762 }
763
764 @Override
765 void processOFFlowRemoved(OFChannelHandler h,
766 OFFlowRemoved m) {
767 h.dispatchMessage(m);
768 }
769
770 @Override
771 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
772 h.dispatchMessage(m);
773 }
774
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700775 @Override
776 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700777 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700778 h.dispatchMessage(m);
779 }
780
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800781 @Override
782 void processIdle(OFChannelHandler h) throws IOException {
783 log.info("{} idle", h.getSwitchInfoString());
784 }
785
tom7ef8ff92014-09-17 13:08:06 -0700786 };
787
788 private final boolean handshakeComplete;
789 ChannelState(boolean handshakeComplete) {
790 this.handshakeComplete = handshakeComplete;
791 }
792
793 /**
794 * Is this a state in which the handshake has completed?
795 * @return true if the handshake is complete
796 */
797 public boolean isHandshakeComplete() {
798 return handshakeComplete;
799 }
800
801 /**
802 * Get a string specifying the switch connection, state, and
803 * message received. To be used as message for SwitchStateException
804 * or log messages
805 * @param h The channel handler (to get switch information_
806 * @param m The OFMessage that has just been received
807 * @param details A string giving more details about the exact nature
808 * of the problem.
809 * @return display string
810 */
811 // needs to be protected because enum members are actually subclasses
812 protected String getSwitchStateMessage(OFChannelHandler h,
813 OFMessage m,
814 String details) {
815 return String.format("Switch: [%s], State: [%s], received: [%s]"
816 + ", details: %s",
817 h.getSwitchInfoString(),
818 this.toString(),
819 m.getType().toString(),
820 details);
821 }
822
823 /**
824 * We have an OFMessage we didn't expect given the current state and
825 * we want to treat this as an error.
826 * We currently throw an exception that will terminate the connection
827 * However, we could be more forgiving
828 * @param h the channel handler that received the message
829 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700830 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700831 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700832 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700833 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
834 throws SwitchStateException {
835 String msg = getSwitchStateMessage(h, m,
836 "Switch should never send this message in the current state");
837 throw new SwitchStateException(msg);
838
839 }
840
841 /**
842 * We have an OFMessage we didn't expect given the current state and
843 * we want to ignore the message.
844 * @param h the channel handler the received the message
845 * @param m the message
846 */
847 protected void unhandledMessageReceived(OFChannelHandler h,
848 OFMessage m) {
849 if (log.isDebugEnabled()) {
850 String msg = getSwitchStateMessage(h, m,
851 "Ignoring unexpected message");
852 log.debug(msg);
853 }
854 }
855
856 /**
857 * Log an OpenFlow error message from a switch.
858 * @param h The switch that sent the error
859 * @param error The error message
860 */
861 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700862 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700863 error,
864 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800865 this);
tom7ef8ff92014-09-17 13:08:06 -0700866 }
867
868 /**
869 * Log an OpenFlow error message from a switch and disconnect the
870 * channel.
871 *
872 * @param h the IO channel for this switch.
873 * @param error The error message
874 */
875 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
876 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700877 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700878 h.channel.disconnect();
879 }
880
881 /**
882 * log an error message for a duplicate dpid and disconnect this channel.
883 * @param h the IO channel for this switch.
884 */
885 protected void disconnectDuplicate(OFChannelHandler h) {
886 log.error("Duplicated dpid or incompleted cleanup - "
887 + "disconnecting channel {}", h.getSwitchInfoString());
888 h.duplicateDpidFound = Boolean.TRUE;
889 h.channel.disconnect();
890 }
891
892
893
894 /**
895 * Handles all pending port status messages before a switch is declared
896 * activated in MASTER or EQUAL role. Note that since this handling
897 * precedes the activation (and therefore notification to IOFSwitchListerners)
898 * the changes to ports will already be visible once the switch is
899 * activated. As a result, no notifications are sent out for these
900 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700901 *
902 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700903 */
904 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
905 try {
906 handlePendingPortStatusMessages(h, 0);
907 } catch (SwitchStateException e) {
908 log.error(e.getMessage());
909 }
910 }
911
912 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
913 throws SwitchStateException {
914 if (h.sw == null) {
915 String msg = "State machine error: switch is null. Should never " +
916 "happen";
917 throw new SwitchStateException(msg);
918 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800919 log.info("Processing {} pending port status messages for {}",
920 h.pendingPortStatusMsg.size(), h.sw.getStringId());
921
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800922 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700923 for (OFPortStatus ps: h.pendingPortStatusMsg) {
924 temp.add(ps);
925 handlePortStatusMessage(h, ps, false);
926 }
tom7ef8ff92014-09-17 13:08:06 -0700927 // expensive but ok - we don't expect too many port-status messages
928 // note that we cannot use clear(), because of the reasons below
929 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800930 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700931 // the iterator above takes a snapshot of the list - so while we were
932 // dealing with the pending port-status messages, we could have received
933 // newer ones. Handle them recursively, but break the recursion after
934 // five steps to avoid an attack.
935 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
936 handlePendingPortStatusMessages(h, index);
937 }
938 }
939
940 /**
941 * Handle a port status message.
942 *
943 * Handle a port status message by updating the port maps in the
944 * IOFSwitch instance and notifying Controller about the change so
945 * it can dispatch a switch update.
946 *
947 * @param h The OFChannelHhandler that received the message
948 * @param m The PortStatus message we received
949 * @param doNotify if true switch port changed events will be
950 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800951 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700952 *
953 */
954 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
955 boolean doNotify) throws SwitchStateException {
956 if (h.sw == null) {
957 String msg = getSwitchStateMessage(h, m,
958 "State machine error: switch is null. Should never " +
959 "happen");
960 throw new SwitchStateException(msg);
961 }
962
963 h.sw.handleMessage(m);
964 }
965
966
967 /**
968 * Process an OF message received on the channel and
969 * update state accordingly.
970 *
971 * The main "event" of the state machine. Process the received message,
972 * send follow up message if required and update state if required.
973 *
974 * Switches on the message type and calls more specific event handlers
975 * for each individual OF message type. If we receive a message that
976 * is supposed to be sent from a controller to a switch we throw
977 * a SwitchStateExeption.
978 *
979 * The more specific handlers can also throw SwitchStateExceptions
980 *
981 * @param h The OFChannelHandler that received the message
982 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800983 * @throws SwitchStateException if the switch is not bound to the channel
984 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700985 */
986 void processOFMessage(OFChannelHandler h, OFMessage m)
987 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800988 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -0700989 case HELLO:
990 processOFHello(h, (OFHello) m);
991 break;
992 case BARRIER_REPLY:
993 processOFBarrierReply(h, (OFBarrierReply) m);
994 break;
995 case ECHO_REPLY:
996 processOFEchoReply(h, (OFEchoReply) m);
997 break;
998 case ECHO_REQUEST:
999 processOFEchoRequest(h, (OFEchoRequest) m);
1000 break;
1001 case ERROR:
1002 processOFError(h, (OFErrorMsg) m);
1003 break;
1004 case FEATURES_REPLY:
1005 processOFFeaturesReply(h, (OFFeaturesReply) m);
1006 break;
1007 case FLOW_REMOVED:
1008 processOFFlowRemoved(h, (OFFlowRemoved) m);
1009 break;
1010 case GET_CONFIG_REPLY:
1011 processOFGetConfigReply(h, (OFGetConfigReply) m);
1012 break;
1013 case PACKET_IN:
1014 processOFPacketIn(h, (OFPacketIn) m);
1015 break;
1016 case PORT_STATUS:
1017 processOFPortStatus(h, (OFPortStatus) m);
1018 break;
1019 case QUEUE_GET_CONFIG_REPLY:
1020 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1021 break;
1022 case STATS_REPLY: // multipart_reply in 1.3
1023 processOFStatisticsReply(h, (OFStatsReply) m);
1024 break;
1025 case EXPERIMENTER:
1026 processOFExperimenter(h, (OFExperimenter) m);
1027 break;
1028 case ROLE_REPLY:
1029 processOFRoleReply(h, (OFRoleReply) m);
1030 break;
1031 case GET_ASYNC_REPLY:
1032 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1033 break;
1034
1035 // The following messages are sent to switches. The controller
1036 // should never receive them
1037 case SET_CONFIG:
1038 case GET_CONFIG_REQUEST:
1039 case PACKET_OUT:
1040 case PORT_MOD:
1041 case QUEUE_GET_CONFIG_REQUEST:
1042 case BARRIER_REQUEST:
1043 case STATS_REQUEST: // multipart request in 1.3
1044 case FEATURES_REQUEST:
1045 case FLOW_MOD:
1046 case GROUP_MOD:
1047 case TABLE_MOD:
1048 case GET_ASYNC_REQUEST:
1049 case SET_ASYNC:
1050 case METER_MOD:
1051 default:
1052 illegalMessageReceived(h, m);
1053 break;
1054 }
1055 }
1056
1057 /*-----------------------------------------------------------------
1058 * Default implementation for message handlers in any state.
1059 *
1060 * Individual states must override these if they want a behavior
1061 * that differs from the default.
1062 *
1063 * In general, these handlers simply ignore the message and do
1064 * nothing.
1065 *
1066 * There are some exceptions though, since some messages really
1067 * are handled the same way in every state (e.g., ECHO_REQUST) or
1068 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1069 -----------------------------------------------------------------*/
1070
1071 void processOFHello(OFChannelHandler h, OFHello m)
1072 throws IOException, SwitchStateException {
1073 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001074 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
1075 h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001076 }
1077
1078 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1079 throws IOException {
1080 // Silently ignore.
1081 }
1082
1083 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1084 throws IOException {
1085 if (h.ofVersion == null) {
1086 log.error("No OF version set for {}. Not sending Echo REPLY",
1087 h.channel.getRemoteAddress());
1088 return;
1089 }
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001090 OFFactory factory = OFFactories.getFactory(h.ofVersion);
tom7ef8ff92014-09-17 13:08:06 -07001091 OFEchoReply reply = factory
1092 .buildEchoReply()
1093 .setXid(m.getXid())
1094 .setData(m.getData())
1095 .build();
1096 h.channel.write(Collections.singletonList(reply));
1097 }
1098
1099 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1100 throws IOException {
1101 // Do nothing with EchoReplies !!
1102 }
1103
1104 // no default implementation for OFError
1105 // every state must override it
1106 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1107 throws IOException, SwitchStateException;
1108
1109
1110 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1111 throws IOException, SwitchStateException {
1112 unhandledMessageReceived(h, m);
1113 }
1114
1115 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1116 throws IOException {
1117 unhandledMessageReceived(h, m);
1118 }
1119
1120 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1121 throws IOException, SwitchStateException {
1122 // we only expect config replies in the WAIT_CONFIG_REPLY state
1123 illegalMessageReceived(h, m);
1124 }
1125
1126 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1127 throws IOException {
1128 unhandledMessageReceived(h, m);
1129 }
1130
1131 // no default implementation. Every state needs to handle it.
1132 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1133 throws IOException, SwitchStateException;
1134
1135 void processOFQueueGetConfigReply(OFChannelHandler h,
1136 OFQueueGetConfigReply m)
1137 throws IOException {
1138 unhandledMessageReceived(h, m);
1139 }
1140
1141 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1142 throws IOException, SwitchStateException {
1143 unhandledMessageReceived(h, m);
1144 }
1145
1146 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1147 throws IOException, SwitchStateException {
1148 // TODO: it might make sense to parse the vendor message here
1149 // into the known vendor messages we support and then call more
1150 // specific event handlers
1151 unhandledMessageReceived(h, m);
1152 }
1153
1154 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1155 throws SwitchStateException, IOException {
1156 unhandledMessageReceived(h, m);
1157 }
1158
1159 void processOFGetAsyncReply(OFChannelHandler h,
1160 OFAsyncGetReply m) {
1161 unhandledMessageReceived(h, m);
1162 }
1163
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001164 void processIdle(OFChannelHandler h) throws IOException {
1165 // disconnect channel which did no complete handshake
1166 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1167 h.channel.disconnect();
1168 }
tom7ef8ff92014-09-17 13:08:06 -07001169 }
1170
1171
1172
1173 //*************************
1174 // Channel handler methods
1175 //*************************
1176
1177 @Override
1178 public void channelConnected(ChannelHandlerContext ctx,
1179 ChannelStateEvent e) throws Exception {
1180 channel = e.getChannel();
1181 log.info("New switch connection from {}",
1182 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001183 /*
1184 hack to wait for the switch to tell us what it's
1185 max version is. This is not spec compliant and should
1186 be removed as soon as switches behave better.
1187 */
1188 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001189 setState(ChannelState.WAIT_HELLO);
1190 }
1191
1192 @Override
1193 public void channelDisconnected(ChannelHandlerContext ctx,
1194 ChannelStateEvent e) throws Exception {
1195 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1196 getSwitchInfoString());
1197 if (thisdpid != 0) {
1198 if (!duplicateDpidFound) {
1199 // if the disconnected switch (on this ChannelHandler)
1200 // was not one with a duplicate-dpid, it is safe to remove all
1201 // state for it at the controller. Notice that if the disconnected
1202 // switch was a duplicate-dpid, calling the method below would clear
1203 // all state for the original switch (with the same dpid),
1204 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001205 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001206 if (sw != null) {
1207 sw.removeConnectedSwitch();
1208 }
tom7ef8ff92014-09-17 13:08:06 -07001209 } else {
1210 // A duplicate was disconnected on this ChannelHandler,
1211 // this is the same switch reconnecting, but the original state was
1212 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001213 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001214 duplicateDpidFound = Boolean.FALSE;
1215 }
1216 } else {
1217 log.warn("no dpid in channelHandler registered for "
1218 + "disconnected switch {}", getSwitchInfoString());
1219 }
1220 }
1221
1222 @Override
1223 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1224 throws Exception {
1225 if (e.getCause() instanceof ReadTimeoutException) {
1226 // switch timeout
1227 log.error("Disconnecting switch {} due to read timeout",
1228 getSwitchInfoString());
1229 ctx.getChannel().close();
1230 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1231 log.error("Disconnecting switch {}: failed to complete handshake",
1232 getSwitchInfoString());
1233 ctx.getChannel().close();
1234 } else if (e.getCause() instanceof ClosedChannelException) {
1235 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1236 } else if (e.getCause() instanceof IOException) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001237 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1238 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1239 log.error("Disconnecting switch {} due to IO Error: {}",
1240 getSwitchInfoString(), e.getCause().getMessage());
1241 if (log.isDebugEnabled()) {
1242 // still print stack trace if debug is enabled
1243 log.debug("StackTrace for previous Exception: ", e.getCause());
1244 }
tom7ef8ff92014-09-17 13:08:06 -07001245 }
1246 ctx.getChannel().close();
1247 } else if (e.getCause() instanceof SwitchStateException) {
1248 log.error("Disconnecting switch {} due to switch state error: {}",
1249 getSwitchInfoString(), e.getCause().getMessage());
1250 if (log.isDebugEnabled()) {
1251 // still print stack trace if debug is enabled
1252 log.debug("StackTrace for previous Exception: ", e.getCause());
1253 }
1254 ctx.getChannel().close();
1255 } else if (e.getCause() instanceof OFParseError) {
1256 log.error("Disconnecting switch "
1257 + getSwitchInfoString() +
1258 " due to message parse failure",
1259 e.getCause());
1260 ctx.getChannel().close();
1261 } else if (e.getCause() instanceof RejectedExecutionException) {
1262 log.warn("Could not process message: queue full");
1263 } else {
1264 log.error("Error while processing message from switch "
1265 + getSwitchInfoString()
1266 + "state " + this.state, e.getCause());
1267 ctx.getChannel().close();
1268 }
1269 }
1270
1271 @Override
1272 public String toString() {
1273 return getSwitchInfoString();
1274 }
1275
1276 @Override
1277 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1278 throws Exception {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001279 OFFactory factory = OFFactories.getFactory(ofVersion);
tom7ef8ff92014-09-17 13:08:06 -07001280 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001281 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001282 e.getChannel().getPipeline().getLast());
tom7ef8ff92014-09-17 13:08:06 -07001283 e.getChannel().write(Collections.singletonList(m));
1284 // XXX S some problems here -- echo request has no transaction id, and
1285 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001286 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001287 }
1288
1289 @Override
1290 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1291 throws Exception {
1292 if (e.getMessage() instanceof List) {
1293 @SuppressWarnings("unchecked")
1294 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1295
1296
1297 for (OFMessage ofm : msglist) {
1298 // Do the actual packet processing
1299 state.processOFMessage(this, ofm);
1300 }
1301 } else {
1302 state.processOFMessage(this, (OFMessage) e.getMessage());
1303 }
1304 }
1305
1306
1307
1308 //*************************
1309 // Channel utility methods
1310 //*************************
1311
1312 /**
1313 * Is this a state in which the handshake has completed?
1314 * @return true if the handshake is complete
1315 */
1316 public boolean isHandshakeComplete() {
1317 return this.state.isHandshakeComplete();
1318 }
1319
1320 private void dispatchMessage(OFMessage m) {
1321 sw.handleMessage(m);
1322 }
1323
1324 /**
1325 * Return a string describing this switch based on the already available
1326 * information (DPID and/or remote socket).
1327 * @return display string
1328 */
1329 private String getSwitchInfoString() {
1330 if (sw != null) {
1331 return sw.toString();
1332 }
1333 String channelString;
1334 if (channel == null || channel.getRemoteAddress() == null) {
1335 channelString = "?";
1336 } else {
1337 channelString = channel.getRemoteAddress().toString();
1338 }
1339 String dpidString;
1340 if (featuresReply == null) {
1341 dpidString = "?";
1342 } else {
1343 dpidString = featuresReply.getDatapathId().toString();
1344 }
1345 return String.format("[%s DPID[%s]]", channelString, dpidString);
1346 }
1347
1348 /**
1349 * Update the channels state. Only called from the state machine.
1350 * TODO: enforce restricted state transitions
1351 * @param state
1352 */
1353 private void setState(ChannelState state) {
1354 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001355 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001356 }
1357
1358 /**
1359 * Send hello message to the switch using the handshake transactions ids.
1360 * @throws IOException
1361 */
1362 private void sendHandshakeHelloMessage() throws IOException {
1363 // The OF protocol requires us to start things off by sending the highest
1364 // version of the protocol supported.
1365
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001366 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001367 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001368 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1369 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001370 (0b1 << OFVersion.OF_14.getWireVersion()) |
1371 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001372 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1373 OFHelloElem hem = OFFactories.getFactory(version)
1374 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001375 .setBitmaps(Collections.singletonList(bitmap))
1376 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001377 OFMessage.Builder mb = OFFactories.getFactory(version)
1378 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001379 .setXid(this.handshakeTransactionIds--)
1380 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001381 log.info("Sending {} Hello to {}", version, channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001382 channel.write(Collections.singletonList(mb.build()));
1383 }
1384
1385 /**
1386 * Send featuresRequest msg to the switch using the handshake transactions ids.
1387 * @throws IOException
1388 */
1389 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001390 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001391 log.debug("Sending FEATURES_REQUEST to {}", channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001392 OFMessage m = factory.buildFeaturesRequest()
1393 .setXid(this.handshakeTransactionIds--)
1394 .build();
1395 channel.write(Collections.singletonList(m));
1396 }
1397
1398 /**
1399 * Send the configuration requests to tell the switch we want full
1400 * packets.
1401 * @throws IOException
1402 */
1403 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001404 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001405 log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001406 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001407
1408 // Ensure we receive the full packet via PacketIn
1409 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001410 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001411 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001412 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001413 OFSetConfig sc = factory
1414 .buildSetConfig()
1415 .setMissSendLen((short) 0xffff)
1416 .setXid(this.handshakeTransactionIds--)
1417 .build();
1418 msglist.add(sc);
1419 }
tom7ef8ff92014-09-17 13:08:06 -07001420
1421 // Barrier
1422 OFBarrierRequest br = factory
1423 .buildBarrierRequest()
1424 .setXid(this.handshakeTransactionIds--)
1425 .build();
1426 msglist.add(br);
1427
1428 // Verify (need barrier?)
1429 OFGetConfigRequest gcr = factory
1430 .buildGetConfigRequest()
1431 .setXid(this.handshakeTransactionIds--)
1432 .build();
1433 msglist.add(gcr);
1434 channel.write(msglist);
1435 }
1436
1437 /**
1438 * send a description state request.
1439 * @throws IOException
1440 */
1441 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1442 // Get Description to set switch-specific flags
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001443 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001444 log.debug("Sending DESC_STATS_REQUEST to {}", channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001445 OFDescStatsRequest dreq = factory
1446 .buildDescStatsRequest()
1447 .setXid(handshakeTransactionIds--)
1448 .build();
1449 channel.write(Collections.singletonList(dreq));
1450 }
1451
Jordi Ortiz91477b82016-11-29 15:22:50 +01001452 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001453 * send a meter features request.
1454 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001455 * @throws IOException
1456 */
1457 private void sendMeterFeaturesRequest() throws IOException {
1458 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001459 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001460 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.getRemoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001461 OFMeterFeaturesStatsRequest mfreq = factory
1462 .buildMeterFeaturesStatsRequest()
1463 .setXid(handshakeTransactionIds--)
1464 .build();
1465 channel.write(Collections.singletonList(mfreq));
1466 }
1467
tom7ef8ff92014-09-17 13:08:06 -07001468 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001469 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.getRemoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001470 // Get port description for 1.3+ switch
1471 OFPortDescStatsRequest preq = OFFactories.getFactory(ofVersion)
tom7ef8ff92014-09-17 13:08:06 -07001472 .buildPortDescStatsRequest()
1473 .setXid(handshakeTransactionIds--)
1474 .build();
1475 channel.write(Collections.singletonList(preq));
1476 }
1477
1478 ChannelState getStateForTesting() {
1479 return state;
1480 }
1481
1482}