blob: 56dd085b55644cbb1a84f69fb6ed3a98473a1091 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 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.
191 if (m.getVersion() == OFVersion.OF_13) {
alshabib09d48be2014-10-03 15:43:33 -0700192 log.debug("Received {} Hello from {}", m.getVersion(),
tom7ef8ff92014-09-17 13:08:06 -0700193 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800194 h.sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -0700195 h.ofVersion = OFVersion.OF_13;
196 } else if (m.getVersion() == OFVersion.OF_10) {
alshabib09d48be2014-10-03 15:43:33 -0700197 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700198 + "version 1.0", m.getVersion(),
199 h.channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -0800200 OFHello hi =
201 h.factory10.buildHello()
202 .setXid(h.handshakeTransactionIds--)
203 .build();
204 h.channel.write(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700205 h.ofVersion = OFVersion.OF_10;
206 } else {
207 log.error("Received Hello of version {} from switch at {}. "
208 + "This controller works with OF1.0 and OF1.3 "
209 + "switches. Disconnecting switch ...",
210 m.getVersion(), h.channel.getRemoteAddress());
211 h.channel.disconnect();
212 return;
213 }
214 h.sendHandshakeFeaturesRequestMessage();
215 h.setState(WAIT_FEATURES_REPLY);
216 }
217 @Override
218 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
219 throws IOException, SwitchStateException {
220 illegalMessageReceived(h, m);
221 }
222 @Override
223 void processOFStatisticsReply(OFChannelHandler h,
224 OFStatsReply m)
225 throws IOException, SwitchStateException {
226 illegalMessageReceived(h, m);
227 }
228 @Override
229 void processOFError(OFChannelHandler h, OFErrorMsg m) {
230 logErrorDisconnect(h, m);
231 }
232
233 @Override
234 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
235 throws IOException {
236 unhandledMessageReceived(h, m);
237 }
238 },
239
240
241 /**
242 * We are waiting for a features reply message. Once we receive it, the
243 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
244 * we send a SetConfig request, barrier, and GetConfig request and the
245 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
246 * request and the next state is WAIT_PORT_DESC_REPLY.
247 */
248 WAIT_FEATURES_REPLY(false) {
249 @Override
250 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
251 throws IOException {
252 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700253 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700254 h.getSwitchInfoString(), h.thisdpid);
255
256 h.featuresReply = m; //temp store
257 if (h.ofVersion == OFVersion.OF_10) {
258 h.sendHandshakeSetConfig();
259 h.setState(WAIT_CONFIG_REPLY);
260 } else {
261 //version is 1.3, must get switchport information
262 h.sendHandshakeOFPortDescRequest();
263 h.setState(WAIT_PORT_DESC_REPLY);
264 }
265 }
266 @Override
267 void processOFStatisticsReply(OFChannelHandler h,
268 OFStatsReply m)
269 throws IOException, SwitchStateException {
270 illegalMessageReceived(h, m);
271 }
272 @Override
273 void processOFError(OFChannelHandler h, OFErrorMsg m) {
274 logErrorDisconnect(h, m);
275 }
276
277 @Override
278 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
279 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800280 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700281 }
282 },
283
284 /**
285 * We are waiting for a description of the 1.3 switch ports.
286 * Once received, we send a SetConfig request
287 * Next State is WAIT_CONFIG_REPLY
288 */
289 WAIT_PORT_DESC_REPLY(false) {
290
291 @Override
292 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
293 throws SwitchStateException {
294 // Read port description
295 if (m.getStatsType() != OFStatsType.PORT_DESC) {
296 log.warn("Expecting port description stats but received stats "
297 + "type {} from {}. Ignoring ...", m.getStatsType(),
298 h.channel.getRemoteAddress());
299 return;
300 }
301 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700302 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700303 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700304 h.getSwitchInfoString());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700305 h.portDescReplies.add((OFPortDescStatsReply)m);
306 return;
tom7ef8ff92014-09-17 13:08:06 -0700307 }
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700308 else {
309 h.portDescReplies.add((OFPortDescStatsReply)m);
310 }
311 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
tom7ef8ff92014-09-17 13:08:06 -0700312 log.info("Received port desc reply for switch at {}",
313 h.getSwitchInfoString());
314 try {
315 h.sendHandshakeSetConfig();
316 } catch (IOException e) {
317 log.error("Unable to send setConfig after PortDescReply. "
318 + "Error: {}", e.getMessage());
319 }
320 h.setState(WAIT_CONFIG_REPLY);
321 }
322
323 @Override
324 void processOFError(OFChannelHandler h, OFErrorMsg m)
325 throws IOException, SwitchStateException {
326 logErrorDisconnect(h, m);
327
328 }
329
330 @Override
331 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
332 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800333 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700334
335 }
336 },
337
338 /**
339 * We are waiting for a config reply message. Once we receive it
340 * we send a DescriptionStatsRequest to the switch.
341 * Next state: WAIT_DESCRIPTION_STAT_REPLY
342 */
343 WAIT_CONFIG_REPLY(false) {
344 @Override
345 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
346 throws IOException {
347 if (m.getMissSendLen() == 0xffff) {
348 log.trace("Config Reply from switch {} confirms "
349 + "miss length set to 0xffff",
350 h.getSwitchInfoString());
351 } else {
352 // FIXME: we can't really deal with switches that don't send
353 // full packets. Shouldn't we drop the connection here?
354 log.warn("Config Reply from switch {} has"
355 + "miss length set to {}",
356 h.getSwitchInfoString(),
357 m.getMissSendLen());
358 }
359 h.sendHandshakeDescriptionStatsRequest();
360 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
361 }
362
363 @Override
364 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
365 // do nothing;
366 }
367
368 @Override
369 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
370 throws IOException, SwitchStateException {
371 illegalMessageReceived(h, m);
372 }
373 @Override
374 void processOFStatisticsReply(OFChannelHandler h,
375 OFStatsReply m)
376 throws IOException, SwitchStateException {
377 log.error("Received multipart(stats) message sub-type {}",
378 m.getStatsType());
379 illegalMessageReceived(h, m);
380 }
381
382 @Override
383 void processOFError(OFChannelHandler h, OFErrorMsg m) {
384 logErrorDisconnect(h, m);
385 }
386
387 @Override
388 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
389 throws IOException {
390 h.pendingPortStatusMsg.add(m);
391 }
392 },
393
394
395 /**
396 * We are waiting for a OFDescriptionStat message from the switch.
397 * Once we receive any stat message we try to parse it. If it's not
398 * a description stats message we disconnect. If its the expected
399 * description stats message, we:
400 * - use the switch driver to bind the switch and get an IOFSwitch instance
401 * - setup the IOFSwitch instance
402 * - add switch controller and send the initial role
403 * request to the switch.
404 * Next state: WAIT_INITIAL_ROLE
405 * In the typical case, where switches support role request messages
406 * the next state is where we expect the role reply message.
407 * In the special case that where the switch does not support any kind
408 * of role request messages, we don't send a role message, but we do
409 * request mastership from the registry service. This controller
410 * should become master once we hear back from the registry service.
411 * All following states will have a h.sw instance!
412 */
413 WAIT_DESCRIPTION_STAT_REPLY(false) {
414 @Override
415 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
416 throws SwitchStateException {
417 // Read description, if it has been updated
418 if (m.getStatsType() != OFStatsType.DESC) {
419 log.warn("Expecting Description stats but received stats "
420 + "type {} from {}. Ignoring ...", m.getStatsType(),
421 h.channel.getRemoteAddress());
422 return;
423 }
tom7ef8ff92014-09-17 13:08:06 -0700424 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700425 log.info("Received switch description reply {} from switch at {}",
426 drep, h.channel.getRemoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700427 // Here is where we differentiate between different kinds of switches
428 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
429
430 h.sw.setOFVersion(h.ofVersion);
431 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700432 //h.sw.setPortDescReply(h.portDescReply);
433 h.sw.setPortDescReplies(h.portDescReplies);
tom7ef8ff92014-09-17 13:08:06 -0700434 h.sw.setConnected(true);
435 h.sw.setChannel(h.channel);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700436// boolean success = h.sw.connectSwitch();
437//
438// if (!success) {
439// disconnectDuplicate(h);
440// return;
441// }
tom7ef8ff92014-09-17 13:08:06 -0700442 // set switch information
443
444
445
alshabib09d48be2014-10-03 15:43:33 -0700446 log.debug("Switch {} bound to class {}, description {}",
tom7ef8ff92014-09-17 13:08:06 -0700447 new Object[] {h.sw, h.sw.getClass(), drep });
448 //Put switch in EQUAL mode until we hear back from the global registry
449 //log.debug("Setting new switch {} to EQUAL and sending Role request",
450 // h.sw.getStringId());
451 //h.sw.activateEqualSwitch();
452 //h.setSwitchRole(RoleState.EQUAL);
453
454 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700455 if (h.sw.isDriverHandshakeComplete()) {
456 if (!h.sw.connectSwitch()) {
457 disconnectDuplicate(h);
458 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800459 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700460 h.setState(ACTIVE);
461 } else {
462 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
463 }
tom7ef8ff92014-09-17 13:08:06 -0700464
465 }
466
467 @Override
468 void processOFError(OFChannelHandler h, OFErrorMsg m) {
469 logErrorDisconnect(h, m);
470 }
471
472 @Override
473 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
474 throws IOException, SwitchStateException {
475 illegalMessageReceived(h, m);
476 }
477
478 @Override
479 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
480 throws IOException {
481 h.pendingPortStatusMsg.add(m);
482 }
483 },
484
485
486 /**
487 * We are waiting for the respective switch driver to complete its
488 * configuration. Notice that we do not consider this to be part of the main
489 * switch-controller handshake. But we do consider it as a step that comes
490 * before we declare the switch as available to the controller.
491 * Next State: depends on the role of this controller for this switch - either
492 * MASTER or EQUAL.
493 */
494 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
495
496 @Override
497 void processOFError(OFChannelHandler h, OFErrorMsg m)
498 throws IOException {
499 // will never be called. We override processOFMessage
500 }
501
alshabibd7963912014-10-20 14:52:04 -0700502
503
tom7ef8ff92014-09-17 13:08:06 -0700504 @Override
505 void processOFMessage(OFChannelHandler h, OFMessage m)
506 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700507
508 if (h.sw.isDriverHandshakeComplete()) {
509 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700510 h.state.processOFMessage(h, m);
511 return;
alshabibd7963912014-10-20 14:52:04 -0700512
513 }
514
tom7ef8ff92014-09-17 13:08:06 -0700515 if (m.getType() == OFType.ECHO_REQUEST) {
516 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700517 } else if (m.getType() == OFType.ECHO_REPLY) {
518 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700519 } else if (m.getType() == OFType.ROLE_REPLY) {
520 h.sw.handleRole(m);
521 } else if (m.getType() == OFType.ERROR) {
522 if (!h.sw.handleRoleError((OFErrorMsg)m)) {
523 h.sw.processDriverHandshakeMessage(m);
524 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700525 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700526 }
527 }
528 } else {
529 if (m.getType() == OFType.EXPERIMENTER &&
530 ((OFExperimenter) m).getExperimenter() ==
531 RoleManager.NICIRA_EXPERIMENTER) {
532 h.sw.handleNiciraRole(m);
533 } else {
534 h.sw.processDriverHandshakeMessage(m);
535 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700536 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700537 }
538 }
539 }
540 }
541
542 @Override
543 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
544 throws IOException, SwitchStateException {
545 h.pendingPortStatusMsg.add(m);
546 }
alshabibd7963912014-10-20 14:52:04 -0700547
548 private void moveToActive(OFChannelHandler h) {
549 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800550 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700551 h.setState(ACTIVE);
552 if (!success) {
553 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700554 }
555 }
556
tom7ef8ff92014-09-17 13:08:06 -0700557 },
558
559
560 /**
561 * This controller is in MASTER role for this switch. We enter this state
562 * after requesting and winning control from the global registry.
563 * The main handshake as well as the switch-driver sub-handshake
564 * is complete at this point.
565 * // XXX S reconsider below
566 * In the (near) future we may deterministically assign controllers to
567 * switches at startup.
568 * We only leave this state if the switch disconnects or
569 * if we send a role request for SLAVE /and/ receive the role reply for
570 * SLAVE.
571 */
572 ACTIVE(true) {
573 @Override
574 void processOFError(OFChannelHandler h, OFErrorMsg m)
575 throws IOException, SwitchStateException {
576 // if we get here, then the error message is for something else
577 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
578 ((OFBadRequestErrorMsg) m).getCode() ==
579 OFBadRequestCode.EPERM) {
580 // We are the master controller and the switch returned
581 // a permission error. This is a likely indicator that
582 // the switch thinks we are slave. Reassert our
583 // role
584 // FIXME: this could be really bad during role transitions
585 // if two controllers are master (even if its only for
586 // a brief period). We might need to see if these errors
587 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700588
tom7ef8ff92014-09-17 13:08:06 -0700589 h.sw.reassertRole();
590 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
591 ((OFFlowModFailedErrorMsg) m).getCode() ==
592 OFFlowModFailedCode.ALL_TABLES_FULL) {
593 h.sw.setTableFull(true);
594 } else {
595 logError(h, m);
596 }
597 h.dispatchMessage(m);
598 }
599
600 @Override
601 void processOFStatisticsReply(OFChannelHandler h,
602 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700603 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
604 h.sw.setPortDescReply((OFPortDescStatsReply) m);
605 }
tom7ef8ff92014-09-17 13:08:06 -0700606 h.dispatchMessage(m);
607 }
608
609 @Override
610 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
611 throws SwitchStateException {
612 h.sw.handleNiciraRole(m);
613 }
614
615 @Override
616 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
617 throws SwitchStateException {
618 h.sw.handleRole(m);
619 }
620
621 @Override
622 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
623 throws SwitchStateException {
624 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800625 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700626 }
627
628 @Override
629 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700630// OFPacketOut out =
631// h.sw.factory().buildPacketOut()
632// .setXid(m.getXid())
633// .setBufferId(m.getBufferId()).build();
634// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700635 h.dispatchMessage(m);
636 }
637
638 @Override
639 void processOFFlowRemoved(OFChannelHandler h,
640 OFFlowRemoved m) {
641 h.dispatchMessage(m);
642 }
643
644 @Override
645 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
646 h.dispatchMessage(m);
647 }
648
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700649 @Override
650 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700651 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700652 h.dispatchMessage(m);
653 }
654
tom7ef8ff92014-09-17 13:08:06 -0700655 };
656
657 private final boolean handshakeComplete;
658 ChannelState(boolean handshakeComplete) {
659 this.handshakeComplete = handshakeComplete;
660 }
661
662 /**
663 * Is this a state in which the handshake has completed?
664 * @return true if the handshake is complete
665 */
666 public boolean isHandshakeComplete() {
667 return handshakeComplete;
668 }
669
670 /**
671 * Get a string specifying the switch connection, state, and
672 * message received. To be used as message for SwitchStateException
673 * or log messages
674 * @param h The channel handler (to get switch information_
675 * @param m The OFMessage that has just been received
676 * @param details A string giving more details about the exact nature
677 * of the problem.
678 * @return display string
679 */
680 // needs to be protected because enum members are actually subclasses
681 protected String getSwitchStateMessage(OFChannelHandler h,
682 OFMessage m,
683 String details) {
684 return String.format("Switch: [%s], State: [%s], received: [%s]"
685 + ", details: %s",
686 h.getSwitchInfoString(),
687 this.toString(),
688 m.getType().toString(),
689 details);
690 }
691
692 /**
693 * We have an OFMessage we didn't expect given the current state and
694 * we want to treat this as an error.
695 * We currently throw an exception that will terminate the connection
696 * However, we could be more forgiving
697 * @param h the channel handler that received the message
698 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700699 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700700 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700701 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700702 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
703 throws SwitchStateException {
704 String msg = getSwitchStateMessage(h, m,
705 "Switch should never send this message in the current state");
706 throw new SwitchStateException(msg);
707
708 }
709
710 /**
711 * We have an OFMessage we didn't expect given the current state and
712 * we want to ignore the message.
713 * @param h the channel handler the received the message
714 * @param m the message
715 */
716 protected void unhandledMessageReceived(OFChannelHandler h,
717 OFMessage m) {
718 if (log.isDebugEnabled()) {
719 String msg = getSwitchStateMessage(h, m,
720 "Ignoring unexpected message");
721 log.debug(msg);
722 }
723 }
724
725 /**
726 * Log an OpenFlow error message from a switch.
727 * @param h The switch that sent the error
728 * @param error The error message
729 */
730 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700731 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700732 new Object[] {
733 error,
734 h.getSwitchInfoString(),
735 this.toString()});
736 }
737
738 /**
739 * Log an OpenFlow error message from a switch and disconnect the
740 * channel.
741 *
742 * @param h the IO channel for this switch.
743 * @param error The error message
744 */
745 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
746 logError(h, error);
747 h.channel.disconnect();
748 }
749
750 /**
751 * log an error message for a duplicate dpid and disconnect this channel.
752 * @param h the IO channel for this switch.
753 */
754 protected void disconnectDuplicate(OFChannelHandler h) {
755 log.error("Duplicated dpid or incompleted cleanup - "
756 + "disconnecting channel {}", h.getSwitchInfoString());
757 h.duplicateDpidFound = Boolean.TRUE;
758 h.channel.disconnect();
759 }
760
761
762
763 /**
764 * Handles all pending port status messages before a switch is declared
765 * activated in MASTER or EQUAL role. Note that since this handling
766 * precedes the activation (and therefore notification to IOFSwitchListerners)
767 * the changes to ports will already be visible once the switch is
768 * activated. As a result, no notifications are sent out for these
769 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700770 *
771 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700772 */
773 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
774 try {
775 handlePendingPortStatusMessages(h, 0);
776 } catch (SwitchStateException e) {
777 log.error(e.getMessage());
778 }
779 }
780
781 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
782 throws SwitchStateException {
783 if (h.sw == null) {
784 String msg = "State machine error: switch is null. Should never " +
785 "happen";
786 throw new SwitchStateException(msg);
787 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800788 log.info("Processing {} pending port status messages for {}",
789 h.pendingPortStatusMsg.size(), h.sw.getStringId());
790
tom7ef8ff92014-09-17 13:08:06 -0700791 ArrayList<OFPortStatus> temp = new ArrayList<OFPortStatus>();
792 for (OFPortStatus ps: h.pendingPortStatusMsg) {
793 temp.add(ps);
794 handlePortStatusMessage(h, ps, false);
795 }
tom7ef8ff92014-09-17 13:08:06 -0700796 // expensive but ok - we don't expect too many port-status messages
797 // note that we cannot use clear(), because of the reasons below
798 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800799 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700800 // the iterator above takes a snapshot of the list - so while we were
801 // dealing with the pending port-status messages, we could have received
802 // newer ones. Handle them recursively, but break the recursion after
803 // five steps to avoid an attack.
804 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
805 handlePendingPortStatusMessages(h, index);
806 }
807 }
808
809 /**
810 * Handle a port status message.
811 *
812 * Handle a port status message by updating the port maps in the
813 * IOFSwitch instance and notifying Controller about the change so
814 * it can dispatch a switch update.
815 *
816 * @param h The OFChannelHhandler that received the message
817 * @param m The PortStatus message we received
818 * @param doNotify if true switch port changed events will be
819 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800820 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -0700821 *
822 */
823 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
824 boolean doNotify) throws SwitchStateException {
825 if (h.sw == null) {
826 String msg = getSwitchStateMessage(h, m,
827 "State machine error: switch is null. Should never " +
828 "happen");
829 throw new SwitchStateException(msg);
830 }
831
832 h.sw.handleMessage(m);
833 }
834
835
836 /**
837 * Process an OF message received on the channel and
838 * update state accordingly.
839 *
840 * The main "event" of the state machine. Process the received message,
841 * send follow up message if required and update state if required.
842 *
843 * Switches on the message type and calls more specific event handlers
844 * for each individual OF message type. If we receive a message that
845 * is supposed to be sent from a controller to a switch we throw
846 * a SwitchStateExeption.
847 *
848 * The more specific handlers can also throw SwitchStateExceptions
849 *
850 * @param h The OFChannelHandler that received the message
851 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800852 * @throws SwitchStateException if the switch is not bound to the channel
853 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -0700854 */
855 void processOFMessage(OFChannelHandler h, OFMessage m)
856 throws IOException, SwitchStateException {
857 switch(m.getType()) {
858 case HELLO:
859 processOFHello(h, (OFHello) m);
860 break;
861 case BARRIER_REPLY:
862 processOFBarrierReply(h, (OFBarrierReply) m);
863 break;
864 case ECHO_REPLY:
865 processOFEchoReply(h, (OFEchoReply) m);
866 break;
867 case ECHO_REQUEST:
868 processOFEchoRequest(h, (OFEchoRequest) m);
869 break;
870 case ERROR:
871 processOFError(h, (OFErrorMsg) m);
872 break;
873 case FEATURES_REPLY:
874 processOFFeaturesReply(h, (OFFeaturesReply) m);
875 break;
876 case FLOW_REMOVED:
877 processOFFlowRemoved(h, (OFFlowRemoved) m);
878 break;
879 case GET_CONFIG_REPLY:
880 processOFGetConfigReply(h, (OFGetConfigReply) m);
881 break;
882 case PACKET_IN:
883 processOFPacketIn(h, (OFPacketIn) m);
884 break;
885 case PORT_STATUS:
886 processOFPortStatus(h, (OFPortStatus) m);
887 break;
888 case QUEUE_GET_CONFIG_REPLY:
889 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
890 break;
891 case STATS_REPLY: // multipart_reply in 1.3
892 processOFStatisticsReply(h, (OFStatsReply) m);
893 break;
894 case EXPERIMENTER:
895 processOFExperimenter(h, (OFExperimenter) m);
896 break;
897 case ROLE_REPLY:
898 processOFRoleReply(h, (OFRoleReply) m);
899 break;
900 case GET_ASYNC_REPLY:
901 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
902 break;
903
904 // The following messages are sent to switches. The controller
905 // should never receive them
906 case SET_CONFIG:
907 case GET_CONFIG_REQUEST:
908 case PACKET_OUT:
909 case PORT_MOD:
910 case QUEUE_GET_CONFIG_REQUEST:
911 case BARRIER_REQUEST:
912 case STATS_REQUEST: // multipart request in 1.3
913 case FEATURES_REQUEST:
914 case FLOW_MOD:
915 case GROUP_MOD:
916 case TABLE_MOD:
917 case GET_ASYNC_REQUEST:
918 case SET_ASYNC:
919 case METER_MOD:
920 default:
921 illegalMessageReceived(h, m);
922 break;
923 }
924 }
925
926 /*-----------------------------------------------------------------
927 * Default implementation for message handlers in any state.
928 *
929 * Individual states must override these if they want a behavior
930 * that differs from the default.
931 *
932 * In general, these handlers simply ignore the message and do
933 * nothing.
934 *
935 * There are some exceptions though, since some messages really
936 * are handled the same way in every state (e.g., ECHO_REQUST) or
937 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
938 -----------------------------------------------------------------*/
939
940 void processOFHello(OFChannelHandler h, OFHello m)
941 throws IOException, SwitchStateException {
942 // we only expect hello in the WAIT_HELLO state
943 illegalMessageReceived(h, m);
944 }
945
946 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
947 throws IOException {
948 // Silently ignore.
949 }
950
951 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
952 throws IOException {
953 if (h.ofVersion == null) {
954 log.error("No OF version set for {}. Not sending Echo REPLY",
955 h.channel.getRemoteAddress());
956 return;
957 }
958 OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
959 h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
960 OFEchoReply reply = factory
961 .buildEchoReply()
962 .setXid(m.getXid())
963 .setData(m.getData())
964 .build();
965 h.channel.write(Collections.singletonList(reply));
966 }
967
968 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
969 throws IOException {
970 // Do nothing with EchoReplies !!
971 }
972
973 // no default implementation for OFError
974 // every state must override it
975 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
976 throws IOException, SwitchStateException;
977
978
979 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
980 throws IOException, SwitchStateException {
981 unhandledMessageReceived(h, m);
982 }
983
984 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
985 throws IOException {
986 unhandledMessageReceived(h, m);
987 }
988
989 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
990 throws IOException, SwitchStateException {
991 // we only expect config replies in the WAIT_CONFIG_REPLY state
992 illegalMessageReceived(h, m);
993 }
994
995 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
996 throws IOException {
997 unhandledMessageReceived(h, m);
998 }
999
1000 // no default implementation. Every state needs to handle it.
1001 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1002 throws IOException, SwitchStateException;
1003
1004 void processOFQueueGetConfigReply(OFChannelHandler h,
1005 OFQueueGetConfigReply m)
1006 throws IOException {
1007 unhandledMessageReceived(h, m);
1008 }
1009
1010 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1011 throws IOException, SwitchStateException {
1012 unhandledMessageReceived(h, m);
1013 }
1014
1015 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1016 throws IOException, SwitchStateException {
1017 // TODO: it might make sense to parse the vendor message here
1018 // into the known vendor messages we support and then call more
1019 // specific event handlers
1020 unhandledMessageReceived(h, m);
1021 }
1022
1023 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1024 throws SwitchStateException, IOException {
1025 unhandledMessageReceived(h, m);
1026 }
1027
1028 void processOFGetAsyncReply(OFChannelHandler h,
1029 OFAsyncGetReply m) {
1030 unhandledMessageReceived(h, m);
1031 }
1032
1033 }
1034
1035
1036
1037 //*************************
1038 // Channel handler methods
1039 //*************************
1040
1041 @Override
1042 public void channelConnected(ChannelHandlerContext ctx,
1043 ChannelStateEvent e) throws Exception {
1044 channel = e.getChannel();
1045 log.info("New switch connection from {}",
1046 channel.getRemoteAddress());
alshabib70fc7fb2015-01-06 11:04:29 -08001047 /*
1048 hack to wait for the switch to tell us what it's
1049 max version is. This is not spec compliant and should
1050 be removed as soon as switches behave better.
1051 */
1052 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001053 setState(ChannelState.WAIT_HELLO);
1054 }
1055
1056 @Override
1057 public void channelDisconnected(ChannelHandlerContext ctx,
1058 ChannelStateEvent e) throws Exception {
1059 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
1060 getSwitchInfoString());
1061 if (thisdpid != 0) {
1062 if (!duplicateDpidFound) {
1063 // if the disconnected switch (on this ChannelHandler)
1064 // was not one with a duplicate-dpid, it is safe to remove all
1065 // state for it at the controller. Notice that if the disconnected
1066 // switch was a duplicate-dpid, calling the method below would clear
1067 // all state for the original switch (with the same dpid),
1068 // which we obviously don't want.
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001069 log.info("{}:removal called", getSwitchInfoString());
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001070 if (sw != null) {
1071 sw.removeConnectedSwitch();
1072 }
tom7ef8ff92014-09-17 13:08:06 -07001073 } else {
1074 // A duplicate was disconnected on this ChannelHandler,
1075 // this is the same switch reconnecting, but the original state was
1076 // not cleaned up - XXX check liveness of original ChannelHandler
Yuta HIGUCHI17679472014-10-09 21:53:14 -07001077 log.info("{}:duplicate found", getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001078 duplicateDpidFound = Boolean.FALSE;
1079 }
1080 } else {
1081 log.warn("no dpid in channelHandler registered for "
1082 + "disconnected switch {}", getSwitchInfoString());
1083 }
1084 }
1085
1086 @Override
1087 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
1088 throws Exception {
1089 if (e.getCause() instanceof ReadTimeoutException) {
1090 // switch timeout
1091 log.error("Disconnecting switch {} due to read timeout",
1092 getSwitchInfoString());
1093 ctx.getChannel().close();
1094 } else if (e.getCause() instanceof HandshakeTimeoutException) {
1095 log.error("Disconnecting switch {}: failed to complete handshake",
1096 getSwitchInfoString());
1097 ctx.getChannel().close();
1098 } else if (e.getCause() instanceof ClosedChannelException) {
1099 log.debug("Channel for sw {} already closed", getSwitchInfoString());
1100 } else if (e.getCause() instanceof IOException) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001101 if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
1102 !e.getCause().getMessage().equals(BROKEN_PIPE)) {
1103 log.error("Disconnecting switch {} due to IO Error: {}",
1104 getSwitchInfoString(), e.getCause().getMessage());
1105 if (log.isDebugEnabled()) {
1106 // still print stack trace if debug is enabled
1107 log.debug("StackTrace for previous Exception: ", e.getCause());
1108 }
tom7ef8ff92014-09-17 13:08:06 -07001109 }
1110 ctx.getChannel().close();
1111 } else if (e.getCause() instanceof SwitchStateException) {
1112 log.error("Disconnecting switch {} due to switch state error: {}",
1113 getSwitchInfoString(), e.getCause().getMessage());
1114 if (log.isDebugEnabled()) {
1115 // still print stack trace if debug is enabled
1116 log.debug("StackTrace for previous Exception: ", e.getCause());
1117 }
1118 ctx.getChannel().close();
1119 } else if (e.getCause() instanceof OFParseError) {
1120 log.error("Disconnecting switch "
1121 + getSwitchInfoString() +
1122 " due to message parse failure",
1123 e.getCause());
1124 ctx.getChannel().close();
1125 } else if (e.getCause() instanceof RejectedExecutionException) {
1126 log.warn("Could not process message: queue full");
1127 } else {
1128 log.error("Error while processing message from switch "
1129 + getSwitchInfoString()
1130 + "state " + this.state, e.getCause());
1131 ctx.getChannel().close();
1132 }
1133 }
1134
1135 @Override
1136 public String toString() {
1137 return getSwitchInfoString();
1138 }
1139
1140 @Override
1141 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
1142 throws Exception {
1143 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1144 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001145 log.debug("Sending Echo Request on idle channel: {}",
tom7ef8ff92014-09-17 13:08:06 -07001146 e.getChannel().getPipeline().getLast().toString());
1147 e.getChannel().write(Collections.singletonList(m));
1148 // XXX S some problems here -- echo request has no transaction id, and
1149 // echo reply is not correlated to the echo request.
1150 }
1151
1152 @Override
1153 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
1154 throws Exception {
1155 if (e.getMessage() instanceof List) {
1156 @SuppressWarnings("unchecked")
1157 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
1158
1159
1160 for (OFMessage ofm : msglist) {
1161 // Do the actual packet processing
1162 state.processOFMessage(this, ofm);
1163 }
1164 } else {
1165 state.processOFMessage(this, (OFMessage) e.getMessage());
1166 }
1167 }
1168
1169
1170
1171 //*************************
1172 // Channel utility methods
1173 //*************************
1174
1175 /**
1176 * Is this a state in which the handshake has completed?
1177 * @return true if the handshake is complete
1178 */
1179 public boolean isHandshakeComplete() {
1180 return this.state.isHandshakeComplete();
1181 }
1182
1183 private void dispatchMessage(OFMessage m) {
1184 sw.handleMessage(m);
1185 }
1186
1187 /**
1188 * Return a string describing this switch based on the already available
1189 * information (DPID and/or remote socket).
1190 * @return display string
1191 */
1192 private String getSwitchInfoString() {
1193 if (sw != null) {
1194 return sw.toString();
1195 }
1196 String channelString;
1197 if (channel == null || channel.getRemoteAddress() == null) {
1198 channelString = "?";
1199 } else {
1200 channelString = channel.getRemoteAddress().toString();
1201 }
1202 String dpidString;
1203 if (featuresReply == null) {
1204 dpidString = "?";
1205 } else {
1206 dpidString = featuresReply.getDatapathId().toString();
1207 }
1208 return String.format("[%s DPID[%s]]", channelString, dpidString);
1209 }
1210
1211 /**
1212 * Update the channels state. Only called from the state machine.
1213 * TODO: enforce restricted state transitions
1214 * @param state
1215 */
1216 private void setState(ChannelState state) {
1217 this.state = state;
1218 }
1219
1220 /**
1221 * Send hello message to the switch using the handshake transactions ids.
1222 * @throws IOException
1223 */
1224 private void sendHandshakeHelloMessage() throws IOException {
1225 // The OF protocol requires us to start things off by sending the highest
1226 // version of the protocol supported.
1227
1228 // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
1229 // see Sec. 7.5.1 of the OF1.3.4 spec
1230 U32 bitmap = U32.ofRaw(0x00000012);
1231 OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
1232 .setBitmaps(Collections.singletonList(bitmap))
1233 .build();
1234 OFMessage.Builder mb = factory13.buildHello()
1235 .setXid(this.handshakeTransactionIds--)
1236 .setElements(Collections.singletonList(hem));
1237 log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
1238 channel.write(Collections.singletonList(mb.build()));
1239 }
1240
1241 /**
1242 * Send featuresRequest msg to the switch using the handshake transactions ids.
1243 * @throws IOException
1244 */
1245 private void sendHandshakeFeaturesRequestMessage() throws IOException {
1246 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1247 OFMessage m = factory.buildFeaturesRequest()
1248 .setXid(this.handshakeTransactionIds--)
1249 .build();
1250 channel.write(Collections.singletonList(m));
1251 }
1252
1253 /**
1254 * Send the configuration requests to tell the switch we want full
1255 * packets.
1256 * @throws IOException
1257 */
1258 private void sendHandshakeSetConfig() throws IOException {
1259 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1260 //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
1261 List<OFMessage> msglist = new ArrayList<OFMessage>(3);
1262
1263 // Ensure we receive the full packet via PacketIn
1264 // FIXME: We don't set the reassembly flags.
1265 OFSetConfig sc = factory
1266 .buildSetConfig()
1267 .setMissSendLen((short) 0xffff)
1268 .setXid(this.handshakeTransactionIds--)
1269 .build();
1270 msglist.add(sc);
1271
1272 // Barrier
1273 OFBarrierRequest br = factory
1274 .buildBarrierRequest()
1275 .setXid(this.handshakeTransactionIds--)
1276 .build();
1277 msglist.add(br);
1278
1279 // Verify (need barrier?)
1280 OFGetConfigRequest gcr = factory
1281 .buildGetConfigRequest()
1282 .setXid(this.handshakeTransactionIds--)
1283 .build();
1284 msglist.add(gcr);
1285 channel.write(msglist);
1286 }
1287
1288 /**
1289 * send a description state request.
1290 * @throws IOException
1291 */
1292 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1293 // Get Description to set switch-specific flags
1294 OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
1295 OFDescStatsRequest dreq = factory
1296 .buildDescStatsRequest()
1297 .setXid(handshakeTransactionIds--)
1298 .build();
1299 channel.write(Collections.singletonList(dreq));
1300 }
1301
1302 private void sendHandshakeOFPortDescRequest() throws IOException {
1303 // Get port description for 1.3 switch
1304 OFPortDescStatsRequest preq = factory13
1305 .buildPortDescStatsRequest()
1306 .setXid(handshakeTransactionIds--)
1307 .build();
1308 channel.write(Collections.singletonList(preq));
1309 }
1310
1311 ChannelState getStateForTesting() {
1312 return state;
1313 }
1314
1315}