blob: 910671047f7fc2b8caa343d658c8adc3c123397f [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
16
tom7ef8ff92014-09-17 13:08:06 -070017//CHECKSTYLE:OFF
Brian O'Connorabafb502014-12-02 22:26:20 -080018package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070019
20import java.io.IOException;
21import java.nio.channels.ClosedChannelException;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.List;
25import java.util.concurrent.CopyOnWriteArrayList;
26import java.util.concurrent.RejectedExecutionException;
27
28import org.jboss.netty.channel.Channel;
29import org.jboss.netty.channel.ChannelHandlerContext;
30import org.jboss.netty.channel.ChannelStateEvent;
31import org.jboss.netty.channel.ExceptionEvent;
32import org.jboss.netty.channel.MessageEvent;
33import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
34import org.jboss.netty.handler.timeout.IdleStateEvent;
35import org.jboss.netty.handler.timeout.ReadTimeoutException;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
37import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070038import org.projectfloodlight.openflow.exceptions.OFParseError;
39import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
40import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
41import org.projectfloodlight.openflow.protocol.OFBarrierReply;
42import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
43import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
44import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
45import org.projectfloodlight.openflow.protocol.OFEchoReply;
46import org.projectfloodlight.openflow.protocol.OFEchoRequest;
47import org.projectfloodlight.openflow.protocol.OFErrorMsg;
48import org.projectfloodlight.openflow.protocol.OFErrorType;
49import org.projectfloodlight.openflow.protocol.OFExperimenter;
50import org.projectfloodlight.openflow.protocol.OFFactory;
51import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
52import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
53import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
54import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
55import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
56import org.projectfloodlight.openflow.protocol.OFHello;
57import org.projectfloodlight.openflow.protocol.OFHelloElem;
58import org.projectfloodlight.openflow.protocol.OFMessage;
59import org.projectfloodlight.openflow.protocol.OFPacketIn;
60import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
61import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
62import org.projectfloodlight.openflow.protocol.OFPortStatus;
63import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
64import org.projectfloodlight.openflow.protocol.OFRoleReply;
65import org.projectfloodlight.openflow.protocol.OFSetConfig;
66import org.projectfloodlight.openflow.protocol.OFStatsReply;
67import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
68import org.projectfloodlight.openflow.protocol.OFStatsType;
69import org.projectfloodlight.openflow.protocol.OFType;
70import org.projectfloodlight.openflow.protocol.OFVersion;
71import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
72import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
73import org.projectfloodlight.openflow.types.U32;
74import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
77/**
78 * Channel handler deals with the switch connection and dispatches
79 * switch messages to the appropriate locations.
80 */
81class OFChannelHandler extends IdleStateAwareChannelHandler {
82 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -070083
84 private static final String RESET_BY_PEER = "Connection reset by peer";
85 private static final String BROKEN_PIPE = "Broken pipe";
86
tom7ef8ff92014-09-17 13:08:06 -070087 private final Controller controller;
88 private OpenFlowSwitchDriver sw;
89 private long thisdpid; // channelHandler cached value of connected switch id
90 private Channel channel;
91 // State needs to be volatile because the HandshakeTimeoutHandler
92 // needs to check if the handshake is complete
93 private volatile ChannelState state;
94
95 // When a switch with a duplicate dpid is found (i.e we already have a
96 // connected switch with the same dpid), the new switch is immediately
97 // disconnected. At that point netty callsback channelDisconnected() which
98 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
99 // switch state for the older (still connected) switch
100 private volatile Boolean duplicateDpidFound;
101
102 // Temporary storage for switch-features and port-description
103 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700104 private List<OFPortDescStatsReply> portDescReplies;
105 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700106 // a concurrent ArrayList to temporarily store port status messages
107 // before we are ready to deal with them
108 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
109
110 //Indicates the openflow version used by this switch
111 protected OFVersion ofVersion;
112 protected OFFactory factory13;
113 protected OFFactory factory10;
114
115 /** transaction Ids to use during handshake. Since only one thread
116 * calls into an OFChannelHandler instance, we don't need atomic.
117 * We will count down
118 */
119 private int handshakeTransactionIds = -1;
120
121 /**
122 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800123 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700124 */
125 OFChannelHandler(Controller controller) {
126 this.controller = controller;
127 this.state = ChannelState.INIT;
128 this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700129 this.portDescReplies = new ArrayList<OFPortDescStatsReply>();
tom7ef8ff92014-09-17 13:08:06 -0700130 factory13 = controller.getOFMessageFactory13();
131 factory10 = controller.getOFMessageFactory10();
132 duplicateDpidFound = Boolean.FALSE;
133 }
134
135
136
137 // XXX S consider if necessary
138 public void disconnectSwitch() {
139 sw.disconnectSwitch();
140 }
141
142
143
144 //*************************
145 // Channel State Machine
146 //*************************
147
148 /**
149 * The state machine for handling the switch/channel state. All state
150 * transitions should happen from within the state machine (and not from other
151 * parts of the code)
152 */
153 enum ChannelState {
154 /**
155 * Initial state before channel is connected.
156 */
157 INIT(false) {
158 @Override
159 void processOFMessage(OFChannelHandler h, OFMessage m)
160 throws IOException, SwitchStateException {
161 illegalMessageReceived(h, m);
162 }
163
164 @Override
165 void processOFError(OFChannelHandler h, OFErrorMsg m)
166 throws IOException {
167 // need to implement since its abstract but it will never
168 // be called
169 }
170
171 @Override
172 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
173 throws IOException {
174 unhandledMessageReceived(h, m);
175 }
176 },
177
178 /**
179 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
180 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
181 * protocol version is accepted.
182 * We send an OFFeaturesRequest depending on the protocol version selected
183 * Next state is WAIT_FEATURES_REPLY
184 */
185 WAIT_HELLO(false) {
186 @Override
187 void processOFHello(OFChannelHandler h, OFHello m)
188 throws IOException {
189 // TODO We could check for the optional bitmap, but for now
190 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500191 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
192 log.debug("Received {} Hello from {} - switching to OF "
193 + "version 1.3", m.getVersion(),
tom7ef8ff92014-09-17 13:08:06 -0700194 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800195 h.sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -0700196 h.ofVersion = OFVersion.OF_13;
Chip Boling68bc6562015-07-06 10:00:01 -0500197 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700198 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700199 + "version 1.0", m.getVersion(),
200 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800201 OFHello hi =
202 h.factory10.buildHello()
203 .setXid(h.handshakeTransactionIds--)
204 .build();
205 h.channel.write(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700206 h.ofVersion = OFVersion.OF_10;
207 } else {
208 log.error("Received Hello of version {} from switch at {}. "
209 + "This controller works with OF1.0 and OF1.3 "
210 + "switches. Disconnecting switch ...",
211 m.getVersion(), h.channel.getRemoteAddress());
212 h.channel.disconnect();
213 return;
214 }
215 h.sendHandshakeFeaturesRequestMessage();
216 h.setState(WAIT_FEATURES_REPLY);
217 }
218 @Override
219 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
220 throws IOException, SwitchStateException {
221 illegalMessageReceived(h, m);
222 }
223 @Override
224 void processOFStatisticsReply(OFChannelHandler h,
225 OFStatsReply m)
226 throws IOException, SwitchStateException {
227 illegalMessageReceived(h, m);
228 }
229 @Override
230 void processOFError(OFChannelHandler h, OFErrorMsg m) {
231 logErrorDisconnect(h, m);
232 }
233
234 @Override
235 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
236 throws IOException {
237 unhandledMessageReceived(h, m);
238 }
239 },
240
241
242 /**
243 * We are waiting for a features reply message. Once we receive it, the
244 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
245 * we send a SetConfig request, barrier, and GetConfig request and the
246 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
247 * request and the next state is WAIT_PORT_DESC_REPLY.
248 */
249 WAIT_FEATURES_REPLY(false) {
250 @Override
251 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
252 throws IOException {
253 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700254 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700255 h.getSwitchInfoString(), h.thisdpid);
256
257 h.featuresReply = m; //temp store
258 if (h.ofVersion == OFVersion.OF_10) {
259 h.sendHandshakeSetConfig();
260 h.setState(WAIT_CONFIG_REPLY);
261 } else {
262 //version is 1.3, must get switchport information
263 h.sendHandshakeOFPortDescRequest();
264 h.setState(WAIT_PORT_DESC_REPLY);
265 }
266 }
267 @Override
268 void processOFStatisticsReply(OFChannelHandler h,
269 OFStatsReply m)
270 throws IOException, SwitchStateException {
271 illegalMessageReceived(h, m);
272 }
273 @Override
274 void processOFError(OFChannelHandler h, OFErrorMsg m) {
275 logErrorDisconnect(h, m);
276 }
277
278 @Override
279 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
280 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800281 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700282 }
283 },
284
285 /**
286 * We are waiting for a description of the 1.3 switch ports.
287 * Once received, we send a SetConfig request
288 * Next State is WAIT_CONFIG_REPLY
289 */
290 WAIT_PORT_DESC_REPLY(false) {
291
292 @Override
293 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
294 throws SwitchStateException {
295 // Read port description
296 if (m.getStatsType() != OFStatsType.PORT_DESC) {
297 log.warn("Expecting port description stats but received stats "
298 + "type {} from {}. Ignoring ...", m.getStatsType(),
299 h.channel.getRemoteAddress());
300 return;
301 }
302 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700303 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700304 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700305 h.getSwitchInfoString());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700306 h.portDescReplies.add((OFPortDescStatsReply)m);
307 return;
tom7ef8ff92014-09-17 13:08:06 -0700308 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700309 else {
310 h.portDescReplies.add((OFPortDescStatsReply)m);
311 }
312 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
tom7ef8ff92014-09-17 13:08:06 -0700313 log.info("Received port desc reply for switch at {}",
314 h.getSwitchInfoString());
315 try {
316 h.sendHandshakeSetConfig();
317 } catch (IOException e) {
318 log.error("Unable to send setConfig after PortDescReply. "
319 + "Error: {}", e.getMessage());
320 }
321 h.setState(WAIT_CONFIG_REPLY);
322 }
323
324 @Override
325 void processOFError(OFChannelHandler h, OFErrorMsg m)
326 throws IOException, SwitchStateException {
327 logErrorDisconnect(h, m);
328
329 }
330
331 @Override
332 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
333 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800334 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700335
336 }
337 },
338
339 /**
340 * We are waiting for a config reply message. Once we receive it
341 * we send a DescriptionStatsRequest to the switch.
342 * Next state: WAIT_DESCRIPTION_STAT_REPLY
343 */
344 WAIT_CONFIG_REPLY(false) {
345 @Override
346 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
347 throws IOException {
348 if (m.getMissSendLen() == 0xffff) {
349 log.trace("Config Reply from switch {} confirms "
350 + "miss length set to 0xffff",
351 h.getSwitchInfoString());
352 } else {
353 // FIXME: we can't really deal with switches that don't send
354 // full packets. Shouldn't we drop the connection here?
355 log.warn("Config Reply from switch {} has"
356 + "miss length set to {}",
357 h.getSwitchInfoString(),
358 m.getMissSendLen());
359 }
360 h.sendHandshakeDescriptionStatsRequest();
361 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
362 }
363
364 @Override
365 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
366 // do nothing;
367 }
368
369 @Override
370 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
371 throws IOException, SwitchStateException {
372 illegalMessageReceived(h, m);
373 }
374 @Override
375 void processOFStatisticsReply(OFChannelHandler h,
376 OFStatsReply m)
377 throws IOException, SwitchStateException {
378 log.error("Received multipart(stats) message sub-type {}",
379 m.getStatsType());
380 illegalMessageReceived(h, m);
381 }
382
383 @Override
384 void processOFError(OFChannelHandler h, OFErrorMsg m) {
385 logErrorDisconnect(h, m);
386 }
387
388 @Override
389 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
390 throws IOException {
391 h.pendingPortStatusMsg.add(m);
392 }
393 },
394
395
396 /**
397 * We are waiting for a OFDescriptionStat message from the switch.
398 * Once we receive any stat message we try to parse it. If it's not
399 * a description stats message we disconnect. If its the expected
400 * description stats message, we:
401 * - use the switch driver to bind the switch and get an IOFSwitch instance
402 * - setup the IOFSwitch instance
403 * - add switch controller and send the initial role
404 * request to the switch.
405 * Next state: WAIT_INITIAL_ROLE
406 * In the typical case, where switches support role request messages
407 * the next state is where we expect the role reply message.
408 * In the special case that where the switch does not support any kind
409 * of role request messages, we don't send a role message, but we do
410 * request mastership from the registry service. This controller
411 * should become master once we hear back from the registry service.
412 * All following states will have a h.sw instance!
413 */
414 WAIT_DESCRIPTION_STAT_REPLY(false) {
415 @Override
416 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
417 throws SwitchStateException {
418 // Read description, if it has been updated
419 if (m.getStatsType() != OFStatsType.DESC) {
420 log.warn("Expecting Description stats but received stats "
421 + "type {} from {}. Ignoring ...", m.getStatsType(),
422 h.channel.getRemoteAddress());
423 return;
424 }
tom7ef8ff92014-09-17 13:08:06 -0700425 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700426 log.info("Received switch description reply {} from switch at {}",
427 drep, h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700428 // Here is where we differentiate between different kinds of switches
429 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
430
431 h.sw.setOFVersion(h.ofVersion);
432 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700433 //h.sw.setPortDescReply(h.portDescReply);
434 h.sw.setPortDescReplies(h.portDescReplies);
tom7ef8ff92014-09-17 13:08:06 -0700435 h.sw.setConnected(true);
436 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700437// boolean success = h.sw.connectSwitch();
438//
439// if (!success) {
440// disconnectDuplicate(h);
441// return;
442// }
tom7ef8ff92014-09-17 13:08:06 -0700443 // set switch information
444
445
446
alshabib09d48be2014-10-03 15:43:33 -0700447 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800448 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700449 //Put switch in EQUAL mode until we hear back from the global registry
450 //log.debug("Setting new switch {} to EQUAL and sending Role request",
451 // h.sw.getStringId());
452 //h.sw.activateEqualSwitch();
453 //h.setSwitchRole(RoleState.EQUAL);
454
455 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700456 if (h.sw.isDriverHandshakeComplete()) {
457 if (!h.sw.connectSwitch()) {
458 disconnectDuplicate(h);
459 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800460 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700461 h.setState(ACTIVE);
462 } else {
463 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
464 }
tom7ef8ff92014-09-17 13:08:06 -0700465
466 }
467
468 @Override
469 void processOFError(OFChannelHandler h, OFErrorMsg m) {
470 logErrorDisconnect(h, m);
471 }
472
473 @Override
474 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
475 throws IOException, SwitchStateException {
476 illegalMessageReceived(h, m);
477 }
478
479 @Override
480 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
481 throws IOException {
482 h.pendingPortStatusMsg.add(m);
483 }
484 },
485
486
487 /**
488 * We are waiting for the respective switch driver to complete its
489 * configuration. Notice that we do not consider this to be part of the main
490 * switch-controller handshake. But we do consider it as a step that comes
491 * before we declare the switch as available to the controller.
492 * Next State: depends on the role of this controller for this switch - either
493 * MASTER or EQUAL.
494 */
495 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
496
497 @Override
498 void processOFError(OFChannelHandler h, OFErrorMsg m)
499 throws IOException {
500 // will never be called. We override processOFMessage
501 }
502
alshabibd7963912014-10-20 14:52:04 -0700503
504
tom7ef8ff92014-09-17 13:08:06 -0700505 @Override
506 void processOFMessage(OFChannelHandler h, OFMessage m)
507 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700508
509 if (h.sw.isDriverHandshakeComplete()) {
510 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700511 h.state.processOFMessage(h, m);
512 return;
alshabibd7963912014-10-20 14:52:04 -0700513
514 }
515
tom7ef8ff92014-09-17 13:08:06 -0700516 if (m.getType() == OFType.ECHO_REQUEST) {
517 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700518 } else if (m.getType() == OFType.ECHO_REPLY) {
519 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700520 } else if (m.getType() == OFType.ROLE_REPLY) {
521 h.sw.handleRole(m);
522 } else if (m.getType() == OFType.ERROR) {
523 if (!h.sw.handleRoleError((OFErrorMsg)m)) {
524 h.sw.processDriverHandshakeMessage(m);
525 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700526 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700527 }
528 }
529 } else {
530 if (m.getType() == OFType.EXPERIMENTER &&
531 ((OFExperimenter) m).getExperimenter() ==
532 RoleManager.NICIRA_EXPERIMENTER) {
533 h.sw.handleNiciraRole(m);
534 } else {
535 h.sw.processDriverHandshakeMessage(m);
536 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700537 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700538 }
539 }
540 }
541 }
542
543 @Override
544 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
545 throws IOException, SwitchStateException {
546 h.pendingPortStatusMsg.add(m);
547 }
alshabibd7963912014-10-20 14:52:04 -0700548
549 private void moveToActive(OFChannelHandler h) {
550 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800551 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700552 h.setState(ACTIVE);
553 if (!success) {
554 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700555 }
556 }
557
tom7ef8ff92014-09-17 13:08:06 -0700558 },
559
560
561 /**
562 * This controller is in MASTER role for this switch. We enter this state
563 * after requesting and winning control from the global registry.
564 * The main handshake as well as the switch-driver sub-handshake
565 * is complete at this point.
566 * // XXX S reconsider below
567 * In the (near) future we may deterministically assign controllers to
568 * switches at startup.
569 * We only leave this state if the switch disconnects or
570 * if we send a role request for SLAVE /and/ receive the role reply for
571 * SLAVE.
572 */
573 ACTIVE(true) {
574 @Override
575 void processOFError(OFChannelHandler h, OFErrorMsg m)
576 throws IOException, SwitchStateException {
577 // if we get here, then the error message is for something else
578 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700579 (((OFBadRequestErrorMsg) m).getCode() ==
580 OFBadRequestCode.EPERM) ||
tom7ef8ff92014-09-17 13:08:06 -0700581 ((OFBadRequestErrorMsg) m).getCode() ==
Ray Milkey30d19652016-09-06 12:09:46 -0700582 OFBadRequestCode.IS_SLAVE) {
tom7ef8ff92014-09-17 13:08:06 -0700583 // We are the master controller and the switch returned
584 // a permission error. This is a likely indicator that
585 // the switch thinks we are slave. Reassert our
586 // role
587 // FIXME: this could be really bad during role transitions
588 // if two controllers are master (even if its only for
589 // a brief period). We might need to see if these errors
590 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700591
tom7ef8ff92014-09-17 13:08:06 -0700592 h.sw.reassertRole();
593 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
594 ((OFFlowModFailedErrorMsg) m).getCode() ==
595 OFFlowModFailedCode.ALL_TABLES_FULL) {
596 h.sw.setTableFull(true);
597 } else {
598 logError(h, m);
599 }
600 h.dispatchMessage(m);
601 }
602
603 @Override
604 void processOFStatisticsReply(OFChannelHandler h,
605 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700606 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
607 h.sw.setPortDescReply((OFPortDescStatsReply) m);
608 }
tom7ef8ff92014-09-17 13:08:06 -0700609 h.dispatchMessage(m);
610 }
611
612 @Override
613 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
614 throws SwitchStateException {
615 h.sw.handleNiciraRole(m);
616 }
617
618 @Override
619 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
620 throws SwitchStateException {
621 h.sw.handleRole(m);
622 }
623
624 @Override
625 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
626 throws SwitchStateException {
627 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800628 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700629 }
630
631 @Override
632 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700633// OFPacketOut out =
634// h.sw.factory().buildPacketOut()
635// .setXid(m.getXid())
636// .setBufferId(m.getBufferId()).build();
637// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700638 h.dispatchMessage(m);
639 }
640
641 @Override
642 void processOFFlowRemoved(OFChannelHandler h,
643 OFFlowRemoved m) {
644 h.dispatchMessage(m);
645 }
646
647 @Override
648 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
649 h.dispatchMessage(m);
650 }
651
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700652 @Override
653 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700654 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700655 h.dispatchMessage(m);
656 }
657
tom7ef8ff92014-09-17 13:08:06 -0700658 };
659
660 private final boolean handshakeComplete;
661 ChannelState(boolean handshakeComplete) {
662 this.handshakeComplete = handshakeComplete;
663 }
664
665 /**
666 * Is this a state in which the handshake has completed?
667 * @return true if the handshake is complete
668 */
669 public boolean isHandshakeComplete() {
670 return handshakeComplete;
671 }
672
673 /**
674 * Get a string specifying the switch connection, state, and
675 * message received. To be used as message for SwitchStateException
676 * or log messages
677 * @param h The channel handler (to get switch information_
678 * @param m The OFMessage that has just been received
679 * @param details A string giving more details about the exact nature
680 * of the problem.
681 * @return display string
682 */
683 // needs to be protected because enum members are actually subclasses
684 protected String getSwitchStateMessage(OFChannelHandler h,
685 OFMessage m,
686 String details) {
687 return String.format("Switch: [%s], State: [%s], received: [%s]"
688 + ", details: %s",
689 h.getSwitchInfoString(),
690 this.toString(),
691 m.getType().toString(),
692 details);
693 }
694
695 /**
696 * We have an OFMessage we didn't expect given the current state and
697 * we want to treat this as an error.
698 * We currently throw an exception that will terminate the connection
699 * However, we could be more forgiving
700 * @param h the channel handler that received the message
701 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700702 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700703 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700704 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700705 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
706 throws SwitchStateException {
707 String msg = getSwitchStateMessage(h, m,
708 "Switch should never send this message in the current state");
709 throw new SwitchStateException(msg);
710
711 }
712
713 /**
714 * We have an OFMessage we didn't expect given the current state and
715 * we want to ignore the message.
716 * @param h the channel handler the received the message
717 * @param m the message
718 */
719 protected void unhandledMessageReceived(OFChannelHandler h,
720 OFMessage m) {
721 if (log.isDebugEnabled()) {
722 String msg = getSwitchStateMessage(h, m,
723 "Ignoring unexpected message");
724 log.debug(msg);
725 }
726 }
727
728 /**
729 * Log an OpenFlow error message from a switch.
730 * @param h The switch that sent the error
731 * @param error The error message
732 */
733 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700734 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700735 error,
736 h.getSwitchInfoString(),
Ray Milkey6bc43c22015-11-06 13:22:38 -0800737 this.toString());
tom7ef8ff92014-09-17 13:08:06 -0700738 }
739
740 /**
741 * Log an OpenFlow error message from a switch and disconnect the
742 * channel.
743 *
744 * @param h the IO channel for this switch.
745 * @param error The error message
746 */
747 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
748 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700749 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700750 h.channel.disconnect();
751 }
752
753 /**
754 * log an error message for a duplicate dpid and disconnect this channel.
755 * @param h the IO channel for this switch.
756 */
757 protected void disconnectDuplicate(OFChannelHandler h) {
758 log.error("Duplicated dpid or incompleted cleanup - "
759 + "disconnecting channel {}", h.getSwitchInfoString());
760 h.duplicateDpidFound = Boolean.TRUE;
761 h.channel.disconnect();
762 }
763
764
765
766 /**
767 * Handles all pending port status messages before a switch is declared
768 * activated in MASTER or EQUAL role. Note that since this handling
769 * precedes the activation (and therefore notification to IOFSwitchListerners)
770 * the changes to ports will already be visible once the switch is
771 * activated. As a result, no notifications are sent out for these
772 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700773 *
774 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700775 */
776 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
777 try {
778 handlePendingPortStatusMessages(h, 0);
779 } catch (SwitchStateException e) {
780 log.error(e.getMessage());
781 }
782 }
783
784 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
785 throws SwitchStateException {
786 if (h.sw == null) {
787 String msg = "State machine error: switch is null. Should never " +
788 "happen";
789 throw new SwitchStateException(msg);
790 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800791 log.info("Processing {} pending port status messages for {}",
792 h.pendingPortStatusMsg.size(), h.sw.getStringId());
793
tom7ef8ff92014-09-17 13:08:06 -0700794 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
795 for (OFPortStatus ps: h.pendingPortStatusMsg) {
796 temp.add(ps);
797 handlePortStatusMessage(h, ps, false);
798 }
tom7ef8ff92014-09-17 13:08:06 -0700799 // expensive but ok - we don't expect too many port-status messages
800 // note that we cannot use clear(), because of the reasons below
801 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800802 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700803 // the iterator above takes a snapshot of the list - so while we were
804 // dealing with the pending port-status messages, we could have received
805 // newer ones. Handle them recursively, but break the recursion after
806 // five steps to avoid an attack.
807 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
808 handlePendingPortStatusMessages(h, index);
809 }
810 }
811
812 /**
813 * Handle a port status message.
814 *
815 * Handle a port status message by updating the port maps in the
816 * IOFSwitch instance and notifying Controller about the change so
817 * it can dispatch a switch update.
818 *
819 * @param h The OFChannelHhandler that received the message
820 * @param m The PortStatus message we received
821 * @param doNotify if true switch port changed events will be
822 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800823 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700824 *
825 */
826 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
827 boolean doNotify) throws SwitchStateException {
828 if (h.sw == null) {
829 String msg = getSwitchStateMessage(h, m,
830 "State machine error: switch is null. Should never " +
831 "happen");
832 throw new SwitchStateException(msg);
833 }
834
835 h.sw.handleMessage(m);
836 }
837
838
839 /**
840 * Process an OF message received on the channel and
841 * update state accordingly.
842 *
843 * The main "event" of the state machine. Process the received message,
844 * send follow up message if required and update state if required.
845 *
846 * Switches on the message type and calls more specific event handlers
847 * for each individual OF message type. If we receive a message that
848 * is supposed to be sent from a controller to a switch we throw
849 * a SwitchStateExeption.
850 *
851 * The more specific handlers can also throw SwitchStateExceptions
852 *
853 * @param h The OFChannelHandler that received the message
854 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800855 * @throws SwitchStateException if the switch is not bound to the channel
856 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700857 */
858 void processOFMessage(OFChannelHandler h, OFMessage m)
859 throws IOException, SwitchStateException {
860 switch(m.getType()) {
861 case HELLO:
862 processOFHello(h, (OFHello) m);
863 break;
864 case BARRIER_REPLY:
865 processOFBarrierReply(h, (OFBarrierReply) m);
866 break;
867 case ECHO_REPLY:
868 processOFEchoReply(h, (OFEchoReply) m);
869 break;
870 case ECHO_REQUEST:
871 processOFEchoRequest(h, (OFEchoRequest) m);
872 break;
873 case ERROR:
874 processOFError(h, (OFErrorMsg) m);
875 break;
876 case FEATURES_REPLY:
877 processOFFeaturesReply(h, (OFFeaturesReply) m);
878 break;
879 case FLOW_REMOVED:
880 processOFFlowRemoved(h, (OFFlowRemoved) m);
881 break;
882 case GET_CONFIG_REPLY:
883 processOFGetConfigReply(h, (OFGetConfigReply) m);
884 break;
885 case PACKET_IN:
886 processOFPacketIn(h, (OFPacketIn) m);
887 break;
888 case PORT_STATUS:
889 processOFPortStatus(h, (OFPortStatus) m);
890 break;
891 case QUEUE_GET_CONFIG_REPLY:
892 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
893 break;
894 case STATS_REPLY: // multipart_reply in 1.3
895 processOFStatisticsReply(h, (OFStatsReply) m);
896 break;
897 case EXPERIMENTER:
898 processOFExperimenter(h, (OFExperimenter) m);
899 break;
900 case ROLE_REPLY:
901 processOFRoleReply(h, (OFRoleReply) m);
902 break;
903 case GET_ASYNC_REPLY:
904 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
905 break;
906
907 // The following messages are sent to switches. The controller
908 // should never receive them
909 case SET_CONFIG:
910 case GET_CONFIG_REQUEST:
911 case PACKET_OUT:
912 case PORT_MOD:
913 case QUEUE_GET_CONFIG_REQUEST:
914 case BARRIER_REQUEST:
915 case STATS_REQUEST: // multipart request in 1.3
916 case FEATURES_REQUEST:
917 case FLOW_MOD:
918 case GROUP_MOD:
919 case TABLE_MOD:
920 case GET_ASYNC_REQUEST:
921 case SET_ASYNC:
922 case METER_MOD:
923 default:
924 illegalMessageReceived(h, m);
925 break;
926 }
927 }
928
929 /*-----------------------------------------------------------------
930 * Default implementation for message handlers in any state.
931 *
932 * Individual states must override these if they want a behavior
933 * that differs from the default.
934 *
935 * In general, these handlers simply ignore the message and do
936 * nothing.
937 *
938 * There are some exceptions though, since some messages really
939 * are handled the same way in every state (e.g., ECHO_REQUST) or
940 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
941 -----------------------------------------------------------------*/
942
943 void processOFHello(OFChannelHandler h, OFHello m)
944 throws IOException, SwitchStateException {
945 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -0700946 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
947 h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700948 }
949
950 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
951 throws IOException {
952 // Silently ignore.
953 }
954
955 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
956 throws IOException {
957 if (h.ofVersion == null) {
958 log.error("No OF version set for {}. Not sending Echo REPLY",
959 h.channel.getRemoteAddress());
960 return;
961 }
962 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
963 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
964 OFEchoReply reply = factory
965 .buildEchoReply()
966 .setXid(m.getXid())
967 .setData(m.getData())
968 .build();
969 h.channel.write(Collections.singletonList(reply));
970 }
971
972 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
973 throws IOException {
974 // Do nothing with EchoReplies !!
975 }
976
977 // no default implementation for OFError
978 // every state must override it
979 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
980 throws IOException, SwitchStateException;
981
982
983 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
984 throws IOException, SwitchStateException {
985 unhandledMessageReceived(h, m);
986 }
987
988 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
989 throws IOException {
990 unhandledMessageReceived(h, m);
991 }
992
993 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
994 throws IOException, SwitchStateException {
995 // we only expect config replies in the WAIT_CONFIG_REPLY state
996 illegalMessageReceived(h, m);
997 }
998
999 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1000 throws IOException {
1001 unhandledMessageReceived(h, m);
1002 }
1003
1004 // no default implementation. Every state needs to handle it.
1005 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1006 throws IOException, SwitchStateException;
1007
1008 void processOFQueueGetConfigReply(OFChannelHandler h,
1009 OFQueueGetConfigReply m)
1010 throws IOException {
1011 unhandledMessageReceived(h, m);
1012 }
1013
1014 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1015 throws IOException, SwitchStateException {
1016 unhandledMessageReceived(h, m);
1017 }
1018
1019 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1020 throws IOException, SwitchStateException {
1021 // TODO: it might make sense to parse the vendor message here
1022 // into the known vendor messages we support and then call more
1023 // specific event handlers
1024 unhandledMessageReceived(h, m);
1025 }
1026
1027 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1028 throws SwitchStateException, IOException {
1029 unhandledMessageReceived(h, m);
1030 }
1031
1032 void processOFGetAsyncReply(OFChannelHandler h,
1033 OFAsyncGetReply m) {
1034 unhandledMessageReceived(h, m);
1035 }
1036
1037 }
1038
1039
1040
1041 //*************************
1042 // Channel handler methods
1043 //*************************
1044
1045 @Override
1046 public void channelConnected(ChannelHandlerContext ctx,
1047 ChannelStateEvent e) throws Exception {
1048 channel = e.getChannel();
1049 log.info("New switch connection from {}",
1050 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001051 /*
1052 hack to wait for the switch to tell us what it's
1053 max version is. This is not spec compliant and should
1054 be removed as soon as switches behave better.
1055 */
1056 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001057 setState(ChannelState.WAIT_HELLO);
1058 }
1059
1060 @Override
1061 public void channelDisconnected(ChannelHandlerContext ctx,
1062 ChannelStateEvent e) throws Exception {
1063 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1064 getSwitchInfoString());
1065 if (thisdpid != 0) {
1066 if (!duplicateDpidFound) {
1067 // if the disconnected switch (on this ChannelHandler)
1068 // was not one with a duplicate-dpid, it is safe to remove all
1069 // state for it at the controller. Notice that if the disconnected
1070 // switch was a duplicate-dpid, calling the method below would clear
1071 // all state for the original switch (with the same dpid),
1072 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001073 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001074 if (sw != null) {
1075 sw.removeConnectedSwitch();
1076 }
tom7ef8ff92014-09-17 13:08:06 -07001077 } else {
1078 // A duplicate was disconnected on this ChannelHandler,
1079 // this is the same switch reconnecting, but the original state was
1080 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001081 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001082 duplicateDpidFound = Boolean.FALSE;
1083 }
1084 } else {
1085 log.warn("no dpid in channelHandler registered for "
1086 + "disconnected switch {}", getSwitchInfoString());
1087 }
1088 }
1089
1090 @Override
1091 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1092 throws Exception {
1093 if (e.getCause() instanceof ReadTimeoutException) {
1094 // switch timeout
1095 log.error("Disconnecting switch {} due to read timeout",
1096 getSwitchInfoString());
1097 ctx.getChannel().close();
1098 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1099 log.error("Disconnecting switch {}: failed to complete handshake",
1100 getSwitchInfoString());
1101 ctx.getChannel().close();
1102 } else if (e.getCause() instanceof ClosedChannelException) {
1103 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1104 } else if (e.getCause() instanceof IOException) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001105 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1106 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1107 log.error("Disconnecting switch {} due to IO Error: {}",
1108 getSwitchInfoString(), e.getCause().getMessage());
1109 if (log.isDebugEnabled()) {
1110 // still print stack trace if debug is enabled
1111 log.debug("StackTrace for previous Exception: ", e.getCause());
1112 }
tom7ef8ff92014-09-17 13:08:06 -07001113 }
1114 ctx.getChannel().close();
1115 } else if (e.getCause() instanceof SwitchStateException) {
1116 log.error("Disconnecting switch {} due to switch state error: {}",
1117 getSwitchInfoString(), e.getCause().getMessage());
1118 if (log.isDebugEnabled()) {
1119 // still print stack trace if debug is enabled
1120 log.debug("StackTrace for previous Exception: ", e.getCause());
1121 }
1122 ctx.getChannel().close();
1123 } else if (e.getCause() instanceof OFParseError) {
1124 log.error("Disconnecting switch "
1125 + getSwitchInfoString() +
1126 " due to message parse failure",
1127 e.getCause());
1128 ctx.getChannel().close();
1129 } else if (e.getCause() instanceof RejectedExecutionException) {
1130 log.warn("Could not process message: queue full");
1131 } else {
1132 log.error("Error while processing message from switch "
1133 + getSwitchInfoString()
1134 + "state " + this.state, e.getCause());
1135 ctx.getChannel().close();
1136 }
1137 }
1138
1139 @Override
1140 public String toString() {
1141 return getSwitchInfoString();
1142 }
1143
1144 @Override
1145 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1146 throws Exception {
1147 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1148 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001149 log.debug("Sending Echo Request on idle channel: {}",
tom7ef8ff92014-09-17 13:08:06 -07001150 e.getChannel().getPipeline().getLast().toString());
1151 e.getChannel().write(Collections.singletonList(m));
1152 // XXX S some problems here -- echo request has no transaction id, and
1153 // echo reply is not correlated to the echo request.
1154 }
1155
1156 @Override
1157 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1158 throws Exception {
1159 if (e.getMessage() instanceof List) {
1160 @SuppressWarnings("unchecked")
1161 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1162
1163
1164 for (OFMessage ofm : msglist) {
1165 // Do the actual packet processing
1166 state.processOFMessage(this, ofm);
1167 }
1168 } else {
1169 state.processOFMessage(this, (OFMessage) e.getMessage());
1170 }
1171 }
1172
1173
1174
1175 //*************************
1176 // Channel utility methods
1177 //*************************
1178
1179 /**
1180 * Is this a state in which the handshake has completed?
1181 * @return true if the handshake is complete
1182 */
1183 public boolean isHandshakeComplete() {
1184 return this.state.isHandshakeComplete();
1185 }
1186
1187 private void dispatchMessage(OFMessage m) {
1188 sw.handleMessage(m);
1189 }
1190
1191 /**
1192 * Return a string describing this switch based on the already available
1193 * information (DPID and/or remote socket).
1194 * @return display string
1195 */
1196 private String getSwitchInfoString() {
1197 if (sw != null) {
1198 return sw.toString();
1199 }
1200 String channelString;
1201 if (channel == null || channel.getRemoteAddress() == null) {
1202 channelString = "?";
1203 } else {
1204 channelString = channel.getRemoteAddress().toString();
1205 }
1206 String dpidString;
1207 if (featuresReply == null) {
1208 dpidString = "?";
1209 } else {
1210 dpidString = featuresReply.getDatapathId().toString();
1211 }
1212 return String.format("[%s DPID[%s]]", channelString, dpidString);
1213 }
1214
1215 /**
1216 * Update the channels state. Only called from the state machine.
1217 * TODO: enforce restricted state transitions
1218 * @param state
1219 */
1220 private void setState(ChannelState state) {
1221 this.state = state;
1222 }
1223
1224 /**
1225 * Send hello message to the switch using the handshake transactions ids.
1226 * @throws IOException
1227 */
1228 private void sendHandshakeHelloMessage() throws IOException {
1229 // The OF protocol requires us to start things off by sending the highest
1230 // version of the protocol supported.
1231
1232 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1233 // see Sec. 7.5.1 of the OF1.3.4 spec
1234 U32 bitmap = U32.ofRaw(0x00000012);
1235 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1236 .setBitmaps(Collections.singletonList(bitmap))
1237 .build();
1238 OFMessage.Builder mb = factory13.buildHello()
1239 .setXid(this.handshakeTransactionIds--)
1240 .setElements(Collections.singletonList(hem));
1241 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1242 channel.write(Collections.singletonList(mb.build()));
1243 }
1244
1245 /**
1246 * Send featuresRequest msg to the switch using the handshake transactions ids.
1247 * @throws IOException
1248 */
1249 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1250 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1251 OFMessage m = factory.buildFeaturesRequest()
1252 .setXid(this.handshakeTransactionIds--)
1253 .build();
1254 channel.write(Collections.singletonList(m));
1255 }
1256
1257 /**
1258 * Send the configuration requests to tell the switch we want full
1259 * packets.
1260 * @throws IOException
1261 */
1262 private void sendHandshakeSetConfig() throws IOException {
1263 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1264 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1265 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1266
1267 // Ensure we receive the full packet via PacketIn
1268 // FIXME: We don't set the reassembly flags.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001269 // Only send config to switches to send full packets, if they have a buffer.
1270 // Saves a packet & OFSetConfig can't be handled by certain switches.
1271 if(this.featuresReply.getNBuffers() > 0) {
1272 OFSetConfig sc = factory
1273 .buildSetConfig()
1274 .setMissSendLen((short) 0xffff)
1275 .setXid(this.handshakeTransactionIds--)
1276 .build();
1277 msglist.add(sc);
1278 }
tom7ef8ff92014-09-17 13:08:06 -07001279
1280 // Barrier
1281 OFBarrierRequest br = factory
1282 .buildBarrierRequest()
1283 .setXid(this.handshakeTransactionIds--)
1284 .build();
1285 msglist.add(br);
1286
1287 // Verify (need barrier?)
1288 OFGetConfigRequest gcr = factory
1289 .buildGetConfigRequest()
1290 .setXid(this.handshakeTransactionIds--)
1291 .build();
1292 msglist.add(gcr);
1293 channel.write(msglist);
1294 }
1295
1296 /**
1297 * send a description state request.
1298 * @throws IOException
1299 */
1300 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1301 // Get Description to set switch-specific flags
1302 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1303 OFDescStatsRequest dreq = factory
1304 .buildDescStatsRequest()
1305 .setXid(handshakeTransactionIds--)
1306 .build();
1307 channel.write(Collections.singletonList(dreq));
1308 }
1309
1310 private void sendHandshakeOFPortDescRequest() throws IOException {
1311 // Get port description for 1.3 switch
1312 OFPortDescStatsRequest preq = factory13
1313 .buildPortDescStatsRequest()
1314 .setXid(handshakeTransactionIds--)
1315 .build();
1316 channel.write(Collections.singletonList(preq));
1317 }
1318
1319 ChannelState getStateForTesting() {
1320 return state;
1321 }
1322
1323}