blob: 6ef5b6d30846155de83b39f3f3b992226d092d81 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
16
Brian O'Connorabafb502014-12-02 22:26:20 -080017package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070018
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070019import static org.onlab.util.Tools.groupedThreads;
20
tom7ef8ff92014-09-17 13:08:06 -070021import java.io.IOException;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070022import java.net.InetSocketAddress;
23import java.net.SocketAddress;
tom7ef8ff92014-09-17 13:08:06 -070024import java.nio.channels.ClosedChannelException;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070025import java.util.ArrayDeque;
tom7ef8ff92014-09-17 13:08:06 -070026import java.util.ArrayList;
27import java.util.Collections;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070028import java.util.Deque;
tom7ef8ff92014-09-17 13:08:06 -070029import java.util.List;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080030import java.util.Optional;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070031import java.util.concurrent.BlockingQueue;
32import java.util.concurrent.CompletableFuture;
tom7ef8ff92014-09-17 13:08:06 -070033import java.util.concurrent.CopyOnWriteArrayList;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070034import java.util.concurrent.ExecutorService;
35import java.util.concurrent.Executors;
36import java.util.concurrent.Future;
37import java.util.concurrent.LinkedBlockingQueue;
tom7ef8ff92014-09-17 13:08:06 -070038import java.util.concurrent.RejectedExecutionException;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070039import org.onlab.packet.IpAddress;
Charles Chan34155e52016-11-30 18:28:11 -080040import org.onosproject.openflow.controller.Dpid;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070041import org.onosproject.openflow.controller.OpenFlowSession;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
43import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070044import org.projectfloodlight.openflow.exceptions.OFParseError;
45import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
46import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
47import org.projectfloodlight.openflow.protocol.OFBarrierReply;
48import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
49import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
50import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
51import org.projectfloodlight.openflow.protocol.OFEchoReply;
52import org.projectfloodlight.openflow.protocol.OFEchoRequest;
53import org.projectfloodlight.openflow.protocol.OFErrorMsg;
54import org.projectfloodlight.openflow.protocol.OFErrorType;
55import org.projectfloodlight.openflow.protocol.OFExperimenter;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080056import org.projectfloodlight.openflow.protocol.OFFactories;
tom7ef8ff92014-09-17 13:08:06 -070057import org.projectfloodlight.openflow.protocol.OFFactory;
58import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
59import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
60import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
61import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
62import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
63import org.projectfloodlight.openflow.protocol.OFHello;
64import org.projectfloodlight.openflow.protocol.OFHelloElem;
65import org.projectfloodlight.openflow.protocol.OFMessage;
Jordi Ortiz91477b82016-11-29 15:22:50 +010066import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
67import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsRequest;
tom7ef8ff92014-09-17 13:08:06 -070068import org.projectfloodlight.openflow.protocol.OFPacketIn;
69import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
70import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
71import org.projectfloodlight.openflow.protocol.OFPortStatus;
72import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
73import org.projectfloodlight.openflow.protocol.OFRoleReply;
74import org.projectfloodlight.openflow.protocol.OFSetConfig;
75import org.projectfloodlight.openflow.protocol.OFStatsReply;
76import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
77import org.projectfloodlight.openflow.protocol.OFStatsType;
78import org.projectfloodlight.openflow.protocol.OFType;
79import org.projectfloodlight.openflow.protocol.OFVersion;
80import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
81import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
82import org.projectfloodlight.openflow.types.U32;
83import org.slf4j.Logger;
84import org.slf4j.LoggerFactory;
85
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070086import io.netty.channel.Channel;
87import io.netty.channel.ChannelHandlerContext;
88import io.netty.channel.ChannelInboundHandlerAdapter;
89import io.netty.handler.timeout.IdleStateEvent;
90import io.netty.handler.timeout.ReadTimeoutException;
91import io.netty.util.ReferenceCountUtil;
92
tom7ef8ff92014-09-17 13:08:06 -070093/**
94 * Channel handler deals with the switch connection and dispatches
95 * switch messages to the appropriate locations.
96 */
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070097class OFChannelHandler extends ChannelInboundHandlerAdapter
98 implements OpenFlowSession {
99
tom7ef8ff92014-09-17 13:08:06 -0700100 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -0700101
102 private static final String RESET_BY_PEER = "Connection reset by peer";
103 private static final String BROKEN_PIPE = "Broken pipe";
104
tom7ef8ff92014-09-17 13:08:06 -0700105 private final Controller controller;
106 private OpenFlowSwitchDriver sw;
107 private long thisdpid; // channelHandler cached value of connected switch id
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700108
tom7ef8ff92014-09-17 13:08:06 -0700109 private Channel channel;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700110 private String channelId;
111
112
tom7ef8ff92014-09-17 13:08:06 -0700113 // State needs to be volatile because the HandshakeTimeoutHandler
114 // needs to check if the handshake is complete
115 private volatile ChannelState state;
116
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800117 /**
118 * Timeout in ms to wait for meter feature reply.
119 */
120 private static final long METER_TIMEOUT = 60_000;
121
122 private volatile long lastStateChange = System.currentTimeMillis();
123
tom7ef8ff92014-09-17 13:08:06 -0700124 // When a switch with a duplicate dpid is found (i.e we already have a
125 // connected switch with the same dpid), the new switch is immediately
126 // disconnected. At that point netty callsback channelDisconnected() which
127 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
128 // switch state for the older (still connected) switch
129 private volatile Boolean duplicateDpidFound;
130
131 // Temporary storage for switch-features and port-description
132 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700133 private List<OFPortDescStatsReply> portDescReplies;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100134 private OFMeterFeaturesStatsReply meterFeaturesReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700135 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700136 // a concurrent ArrayList to temporarily store port status messages
137 // before we are ready to deal with them
138 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
139
140 //Indicates the openflow version used by this switch
141 protected OFVersion ofVersion;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700142 protected OFFactory factory;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800143
144 // deprecated in 1.10.0
145 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700146 protected OFFactory factory13;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800147 // deprecated in 1.10.0
148 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700149 protected OFFactory factory10;
150
151 /** transaction Ids to use during handshake. Since only one thread
152 * calls into an OFChannelHandler instance, we don't need atomic.
153 * We will count down
154 */
155 private int handshakeTransactionIds = -1;
156
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700157
158
159 private static final int MSG_READ_BUFFER = 5000;
160
161 /**
162 * OFMessage dispatch queue.
163 */
164 private final BlockingQueue<OFMessage> dispatchQueue =
165 new LinkedBlockingQueue<>(MSG_READ_BUFFER);
166
167 /**
168 * Single thread executor for OFMessage dispatching.
169 *
170 * Gets initialized on channelActive, shutdown on channelInactive.
171 */
172 private ExecutorService dispatcher;
173
174 /**
175 * Handle for dispatcher thread.
176 * <p>
177 * Should only be touched from the Channel I/O thread
178 */
179 private Future<?> dispatcherHandle = CompletableFuture.completedFuture(null);
180
181 /**
182 * Dispatch backlog.
183 * <p>
184 * Should only be touched from the Channel I/O thread
185 */
186 private final Deque<OFMessage> dispatchBacklog = new ArrayDeque<>();
187
tom7ef8ff92014-09-17 13:08:06 -0700188 /**
189 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800190 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700191 */
192 OFChannelHandler(Controller controller) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700193
tom7ef8ff92014-09-17 13:08:06 -0700194 this.controller = controller;
195 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800196 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
197 this.portDescReplies = new ArrayList<>();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800198 factory13 = OFFactories.getFactory(OFVersion.OF_13);
199 factory10 = OFFactories.getFactory(OFVersion.OF_10);
tom7ef8ff92014-09-17 13:08:06 -0700200 duplicateDpidFound = Boolean.FALSE;
201 }
202
203
204
205 // XXX S consider if necessary
206 public void disconnectSwitch() {
207 sw.disconnectSwitch();
208 }
209
210
211
212 //*************************
213 // Channel State Machine
214 //*************************
215
216 /**
217 * The state machine for handling the switch/channel state. All state
218 * transitions should happen from within the state machine (and not from other
219 * parts of the code)
220 */
221 enum ChannelState {
222 /**
223 * Initial state before channel is connected.
224 */
225 INIT(false) {
226 @Override
227 void processOFMessage(OFChannelHandler h, OFMessage m)
228 throws IOException, SwitchStateException {
229 illegalMessageReceived(h, m);
230 }
231
232 @Override
233 void processOFError(OFChannelHandler h, OFErrorMsg m)
234 throws IOException {
235 // need to implement since its abstract but it will never
236 // be called
237 }
238
239 @Override
240 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
241 throws IOException {
242 unhandledMessageReceived(h, m);
243 }
244 },
245
246 /**
247 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
248 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
249 * protocol version is accepted.
250 * We send an OFFeaturesRequest depending on the protocol version selected
251 * Next state is WAIT_FEATURES_REPLY
252 */
253 WAIT_HELLO(false) {
254 @Override
255 void processOFHello(OFChannelHandler h, OFHello m)
256 throws IOException {
257 // TODO We could check for the optional bitmap, but for now
258 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500259 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
260 log.debug("Received {} Hello from {} - switching to OF "
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800261 + "version 1.3+", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700262 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800263 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700264 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800265 h.sendHandshakeHelloMessage();
Chip Boling68bc6562015-07-06 10:00:01 -0500266 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700267 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700268 + "version 1.0", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700269 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800270 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700271 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800272 OFHello hi =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700273 h.factory.buildHello()
alshabib70fc7fb2015-01-06 11:04:29 -0800274 .setXid(h.handshakeTransactionIds--)
275 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700276 h.channel.writeAndFlush(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700277 } else {
278 log.error("Received Hello of version {} from switch at {}. "
279 + "This controller works with OF1.0 and OF1.3 "
280 + "switches. Disconnecting switch ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700281 m.getVersion(), h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700282 h.channel.disconnect();
283 return;
284 }
285 h.sendHandshakeFeaturesRequestMessage();
286 h.setState(WAIT_FEATURES_REPLY);
287 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700288
tom7ef8ff92014-09-17 13:08:06 -0700289 @Override
290 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
291 throws IOException, SwitchStateException {
292 illegalMessageReceived(h, m);
293 }
294 @Override
295 void processOFStatisticsReply(OFChannelHandler h,
296 OFStatsReply m)
297 throws IOException, SwitchStateException {
298 illegalMessageReceived(h, m);
299 }
300 @Override
301 void processOFError(OFChannelHandler h, OFErrorMsg m) {
302 logErrorDisconnect(h, m);
303 }
304
305 @Override
306 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
307 throws IOException {
308 unhandledMessageReceived(h, m);
309 }
310 },
311
312
313 /**
314 * We are waiting for a features reply message. Once we receive it, the
315 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
316 * we send a SetConfig request, barrier, and GetConfig request and the
317 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
318 * request and the next state is WAIT_PORT_DESC_REPLY.
319 */
320 WAIT_FEATURES_REPLY(false) {
321 @Override
322 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
323 throws IOException {
324 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700325 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700326 h.getSwitchInfoString(), h.thisdpid);
327
328 h.featuresReply = m; //temp store
329 if (h.ofVersion == OFVersion.OF_10) {
330 h.sendHandshakeSetConfig();
331 h.setState(WAIT_CONFIG_REPLY);
332 } else {
333 //version is 1.3, must get switchport information
334 h.sendHandshakeOFPortDescRequest();
335 h.setState(WAIT_PORT_DESC_REPLY);
336 }
337 }
338 @Override
339 void processOFStatisticsReply(OFChannelHandler h,
340 OFStatsReply m)
341 throws IOException, SwitchStateException {
342 illegalMessageReceived(h, m);
343 }
344 @Override
345 void processOFError(OFChannelHandler h, OFErrorMsg m) {
346 logErrorDisconnect(h, m);
347 }
348
349 @Override
350 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
351 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800352 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700353 }
354 },
355
356 /**
357 * We are waiting for a description of the 1.3 switch ports.
358 * Once received, we send a SetConfig request
359 * Next State is WAIT_CONFIG_REPLY
360 */
361 WAIT_PORT_DESC_REPLY(false) {
362
363 @Override
364 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
365 throws SwitchStateException {
366 // Read port description
367 if (m.getStatsType() != OFStatsType.PORT_DESC) {
368 log.warn("Expecting port description stats but received stats "
369 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700370 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700371 return;
372 }
373 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700374 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700375 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700376 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800377 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700378 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800379 } else {
380 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700381 }
382 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
tom7ef8ff92014-09-17 13:08:06 -0700383 log.info("Received port desc reply for switch at {}",
384 h.getSwitchInfoString());
385 try {
386 h.sendHandshakeSetConfig();
387 } catch (IOException e) {
388 log.error("Unable to send setConfig after PortDescReply. "
389 + "Error: {}", e.getMessage());
390 }
391 h.setState(WAIT_CONFIG_REPLY);
392 }
393
394 @Override
395 void processOFError(OFChannelHandler h, OFErrorMsg m)
396 throws IOException, SwitchStateException {
397 logErrorDisconnect(h, m);
398
399 }
400
401 @Override
402 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
403 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800404 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700405
406 }
407 },
408
409 /**
410 * We are waiting for a config reply message. Once we receive it
411 * we send a DescriptionStatsRequest to the switch.
412 * Next state: WAIT_DESCRIPTION_STAT_REPLY
413 */
414 WAIT_CONFIG_REPLY(false) {
415 @Override
416 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
417 throws IOException {
418 if (m.getMissSendLen() == 0xffff) {
419 log.trace("Config Reply from switch {} confirms "
420 + "miss length set to 0xffff",
421 h.getSwitchInfoString());
422 } else {
423 // FIXME: we can't really deal with switches that don't send
424 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800425 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700426 + "miss length set to {}",
427 h.getSwitchInfoString(),
428 m.getMissSendLen());
429 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100430
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800431 nextState(h);
432 }
433
434 /**
435 * Transition to next state based on OF version.
436 *
437 * @param h current channel handler
438 * @throws IOException
439 */
440 private void nextState(OFChannelHandler h) throws IOException {
441 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100442 // Meters were introduced in OpenFlow 1.3
443 h.sendMeterFeaturesRequest();
444 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800445 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100446 h.sendHandshakeDescriptionStatsRequest();
447 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
448 }
tom7ef8ff92014-09-17 13:08:06 -0700449 }
450
451 @Override
452 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
453 // do nothing;
454 }
455
456 @Override
457 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
458 throws IOException, SwitchStateException {
459 illegalMessageReceived(h, m);
460 }
461 @Override
462 void processOFStatisticsReply(OFChannelHandler h,
463 OFStatsReply m)
464 throws IOException, SwitchStateException {
465 log.error("Received multipart(stats) message sub-type {}",
466 m.getStatsType());
467 illegalMessageReceived(h, m);
468 }
469
470 @Override
471 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800472 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
473 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
474 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
475 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
476 try {
477 nextState(h);
478 return;
479 } catch (IOException e) {
480 log.error("Exception thrown transitioning to next", e);
481 logErrorDisconnect(h, m);
482 }
483 }
484 }
tom7ef8ff92014-09-17 13:08:06 -0700485 logErrorDisconnect(h, m);
486 }
487
488 @Override
489 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
490 throws IOException {
491 h.pendingPortStatusMsg.add(m);
492 }
493 },
494
495
496 /**
497 * We are waiting for a OFDescriptionStat message from the switch.
498 * Once we receive any stat message we try to parse it. If it's not
499 * a description stats message we disconnect. If its the expected
500 * description stats message, we:
501 * - use the switch driver to bind the switch and get an IOFSwitch instance
502 * - setup the IOFSwitch instance
503 * - add switch controller and send the initial role
504 * request to the switch.
505 * Next state: WAIT_INITIAL_ROLE
506 * In the typical case, where switches support role request messages
507 * the next state is where we expect the role reply message.
508 * In the special case that where the switch does not support any kind
509 * of role request messages, we don't send a role message, but we do
510 * request mastership from the registry service. This controller
511 * should become master once we hear back from the registry service.
512 * All following states will have a h.sw instance!
513 */
514 WAIT_DESCRIPTION_STAT_REPLY(false) {
515 @Override
516 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
517 throws SwitchStateException {
518 // Read description, if it has been updated
519 if (m.getStatsType() != OFStatsType.DESC) {
520 log.warn("Expecting Description stats but received stats "
521 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700522 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700523 return;
524 }
tom7ef8ff92014-09-17 13:08:06 -0700525 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700526 log.info("Received switch description reply {} from switch at {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700527 drep, h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700528 // Here is where we differentiate between different kinds of switches
529 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
530
531 h.sw.setOFVersion(h.ofVersion);
532 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700533 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100534 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700535 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700536 h.sw.setChannel(h);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700537// boolean success = h.sw.connectSwitch();
538//
539// if (!success) {
540// disconnectDuplicate(h);
541// return;
542// }
tom7ef8ff92014-09-17 13:08:06 -0700543 // set switch information
544
545
546
alshabib09d48be2014-10-03 15:43:33 -0700547 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800548 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700549 //Put switch in EQUAL mode until we hear back from the global registry
550 //log.debug("Setting new switch {} to EQUAL and sending Role request",
551 // h.sw.getStringId());
552 //h.sw.activateEqualSwitch();
553 //h.setSwitchRole(RoleState.EQUAL);
554
555 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700556 if (h.sw.isDriverHandshakeComplete()) {
557 if (!h.sw.connectSwitch()) {
558 disconnectDuplicate(h);
559 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800560 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700561 h.setState(ACTIVE);
562 } else {
563 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
564 }
tom7ef8ff92014-09-17 13:08:06 -0700565
566 }
567
568 @Override
569 void processOFError(OFChannelHandler h, OFErrorMsg m) {
570 logErrorDisconnect(h, m);
571 }
572
573 @Override
574 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
575 throws IOException, SwitchStateException {
576 illegalMessageReceived(h, m);
577 }
578
579 @Override
580 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
581 throws IOException {
582 h.pendingPortStatusMsg.add(m);
583 }
584 },
585
586
587 /**
588 * We are waiting for the respective switch driver to complete its
589 * configuration. Notice that we do not consider this to be part of the main
590 * switch-controller handshake. But we do consider it as a step that comes
591 * before we declare the switch as available to the controller.
592 * Next State: depends on the role of this controller for this switch - either
593 * MASTER or EQUAL.
594 */
595 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
596
597 @Override
598 void processOFError(OFChannelHandler h, OFErrorMsg m)
599 throws IOException {
600 // will never be called. We override processOFMessage
601 }
602
alshabibd7963912014-10-20 14:52:04 -0700603
604
tom7ef8ff92014-09-17 13:08:06 -0700605 @Override
606 void processOFMessage(OFChannelHandler h, OFMessage m)
607 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700608
609 if (h.sw.isDriverHandshakeComplete()) {
610 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700611 h.state.processOFMessage(h, m);
612 return;
alshabibd7963912014-10-20 14:52:04 -0700613
614 }
615
tom7ef8ff92014-09-17 13:08:06 -0700616 if (m.getType() == OFType.ECHO_REQUEST) {
617 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700618 } else if (m.getType() == OFType.ECHO_REPLY) {
619 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700620 } else if (m.getType() == OFType.ROLE_REPLY) {
621 h.sw.handleRole(m);
622 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800623 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700624 h.sw.processDriverHandshakeMessage(m);
625 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700626 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700627 }
628 }
629 } else {
630 if (m.getType() == OFType.EXPERIMENTER &&
631 ((OFExperimenter) m).getExperimenter() ==
632 RoleManager.NICIRA_EXPERIMENTER) {
633 h.sw.handleNiciraRole(m);
634 } else {
635 h.sw.processDriverHandshakeMessage(m);
636 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700637 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700638 }
639 }
640 }
641 }
642
643 @Override
644 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
645 throws IOException, SwitchStateException {
646 h.pendingPortStatusMsg.add(m);
647 }
alshabibd7963912014-10-20 14:52:04 -0700648
649 private void moveToActive(OFChannelHandler h) {
650 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800651 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700652 h.setState(ACTIVE);
653 if (!success) {
654 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700655 }
656 }
657
tom7ef8ff92014-09-17 13:08:06 -0700658 },
659
Jordi Ortiz91477b82016-11-29 15:22:50 +0100660 /**
661 * We are expecting a OF Multi Part Meter Features Stats Reply.
662 * Notice that this information is only available for switches running
663 * OpenFlow 1.3
664 */
665 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800666
667 @Override
668 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
669 throws IOException {
670 super.processOFEchoRequest(h, m);
671 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
672 log.info("{} did not respond to MeterFeaturesRequest on time, " +
673 "moving on without it.",
674 h.getSwitchInfoString());
675 h.sendHandshakeDescriptionStatsRequest();
676 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
677 }
678 }
679
Jordi Ortiz91477b82016-11-29 15:22:50 +0100680 @Override
681 void processOFError(OFChannelHandler h, OFErrorMsg m)
682 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800683 // Hardware switches may reply OFErrorMsg if meter is not supported
684 log.warn("Received OFError {}. It seems {} does not support Meter.",
685 m.getErrType().name(), Dpid.uri(h.thisdpid));
686 log.debug("{}", m);
687 h.sendHandshakeDescriptionStatsRequest();
688 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100689 }
690
691 @Override
692 void processOFStatisticsReply(OFChannelHandler h,
693 OFStatsReply m)
694 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800695 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100696 case METER_FEATURES:
697
698 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800699 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100700 log.info("Received meter features from {} with max meters: {}",
701 h.getSwitchInfoString(),
702 ofmfsr.getFeatures().getMaxMeter());
703 h.meterFeaturesReply = ofmfsr;
704 h.sendHandshakeDescriptionStatsRequest();
705 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
706 break;
707 default:
708 log.error("Unexpected OF Multi Part stats reply");
709 illegalMessageReceived(h, m);
710 break;
711 }
712 }
713
714 @Override
715 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
716 throws IOException, SwitchStateException {
717 illegalMessageReceived(h, m);
718 }
719
720 @Override
721 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
722 throws IOException {
723 h.pendingPortStatusMsg.add(m);
724 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800725
726 @Override
727 void processIdle(OFChannelHandler h) throws IOException {
728 log.info("{} did not respond to MeterFeaturesRequest, " +
729 "moving on without it.",
730 h.getSwitchInfoString());
731 h.sendHandshakeDescriptionStatsRequest();
732 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
733 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100734 },
735
tom7ef8ff92014-09-17 13:08:06 -0700736
737 /**
738 * This controller is in MASTER role for this switch. We enter this state
739 * after requesting and winning control from the global registry.
740 * The main handshake as well as the switch-driver sub-handshake
741 * is complete at this point.
742 * // XXX S reconsider below
743 * In the (near) future we may deterministically assign controllers to
744 * switches at startup.
745 * We only leave this state if the switch disconnects or
746 * if we send a role request for SLAVE /and/ receive the role reply for
747 * SLAVE.
748 */
749 ACTIVE(true) {
750 @Override
751 void processOFError(OFChannelHandler h, OFErrorMsg m)
752 throws IOException, SwitchStateException {
753 // if we get here, then the error message is for something else
754 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700755 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700756 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700757 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700758 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700759 // We are the master controller and the switch returned
760 // a permission error. This is a likely indicator that
761 // the switch thinks we are slave. Reassert our
762 // role
763 // FIXME: this could be really bad during role transitions
764 // if two controllers are master (even if its only for
765 // a brief period). We might need to see if these errors
766 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700767
tom7ef8ff92014-09-17 13:08:06 -0700768 h.sw.reassertRole();
769 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
770 ((OFFlowModFailedErrorMsg) m).getCode() ==
771 OFFlowModFailedCode.ALL_TABLES_FULL) {
772 h.sw.setTableFull(true);
773 } else {
774 logError(h, m);
775 }
776 h.dispatchMessage(m);
777 }
778
779 @Override
780 void processOFStatisticsReply(OFChannelHandler h,
781 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700782 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
783 h.sw.setPortDescReply((OFPortDescStatsReply) m);
784 }
tom7ef8ff92014-09-17 13:08:06 -0700785 h.dispatchMessage(m);
786 }
787
788 @Override
789 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
790 throws SwitchStateException {
791 h.sw.handleNiciraRole(m);
792 }
793
794 @Override
795 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
796 throws SwitchStateException {
797 h.sw.handleRole(m);
798 }
799
800 @Override
801 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
802 throws SwitchStateException {
803 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800804 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700805 }
806
807 @Override
808 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700809// OFPacketOut out =
810// h.sw.factory().buildPacketOut()
811// .setXid(m.getXid())
812// .setBufferId(m.getBufferId()).build();
813// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700814 h.dispatchMessage(m);
815 }
816
817 @Override
818 void processOFFlowRemoved(OFChannelHandler h,
819 OFFlowRemoved m) {
820 h.dispatchMessage(m);
821 }
822
823 @Override
824 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
825 h.dispatchMessage(m);
826 }
827
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700828 @Override
829 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700830 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700831 h.dispatchMessage(m);
832 }
833
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800834 @Override
835 void processIdle(OFChannelHandler h) throws IOException {
836 log.info("{} idle", h.getSwitchInfoString());
837 }
838
tom7ef8ff92014-09-17 13:08:06 -0700839 };
840
841 private final boolean handshakeComplete;
842 ChannelState(boolean handshakeComplete) {
843 this.handshakeComplete = handshakeComplete;
844 }
845
846 /**
847 * Is this a state in which the handshake has completed?
848 * @return true if the handshake is complete
849 */
850 public boolean isHandshakeComplete() {
851 return handshakeComplete;
852 }
853
854 /**
855 * Get a string specifying the switch connection, state, and
856 * message received. To be used as message for SwitchStateException
857 * or log messages
858 * @param h The channel handler (to get switch information_
859 * @param m The OFMessage that has just been received
860 * @param details A string giving more details about the exact nature
861 * of the problem.
862 * @return display string
863 */
864 // needs to be protected because enum members are actually subclasses
865 protected String getSwitchStateMessage(OFChannelHandler h,
866 OFMessage m,
867 String details) {
868 return String.format("Switch: [%s], State: [%s], received: [%s]"
869 + ", details: %s",
870 h.getSwitchInfoString(),
871 this.toString(),
872 m.getType().toString(),
873 details);
874 }
875
876 /**
877 * We have an OFMessage we didn't expect given the current state and
878 * we want to treat this as an error.
879 * We currently throw an exception that will terminate the connection
880 * However, we could be more forgiving
881 * @param h the channel handler that received the message
882 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700883 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700884 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700885 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700886 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
887 throws SwitchStateException {
888 String msg = getSwitchStateMessage(h, m,
889 "Switch should never send this message in the current state");
890 throw new SwitchStateException(msg);
891
892 }
893
894 /**
895 * We have an OFMessage we didn't expect given the current state and
896 * we want to ignore the message.
897 * @param h the channel handler the received the message
898 * @param m the message
899 */
900 protected void unhandledMessageReceived(OFChannelHandler h,
901 OFMessage m) {
902 if (log.isDebugEnabled()) {
903 String msg = getSwitchStateMessage(h, m,
904 "Ignoring unexpected message");
905 log.debug(msg);
906 }
907 }
908
909 /**
910 * Log an OpenFlow error message from a switch.
911 * @param h The switch that sent the error
912 * @param error The error message
913 */
914 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700915 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700916 error,
917 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800918 this);
tom7ef8ff92014-09-17 13:08:06 -0700919 }
920
921 /**
922 * Log an OpenFlow error message from a switch and disconnect the
923 * channel.
924 *
925 * @param h the IO channel for this switch.
926 * @param error The error message
927 */
928 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
929 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700930 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700931 h.channel.disconnect();
932 }
933
934 /**
935 * log an error message for a duplicate dpid and disconnect this channel.
936 * @param h the IO channel for this switch.
937 */
938 protected void disconnectDuplicate(OFChannelHandler h) {
939 log.error("Duplicated dpid or incompleted cleanup - "
940 + "disconnecting channel {}", h.getSwitchInfoString());
941 h.duplicateDpidFound = Boolean.TRUE;
942 h.channel.disconnect();
943 }
944
945
946
947 /**
948 * Handles all pending port status messages before a switch is declared
949 * activated in MASTER or EQUAL role. Note that since this handling
950 * precedes the activation (and therefore notification to IOFSwitchListerners)
951 * the changes to ports will already be visible once the switch is
952 * activated. As a result, no notifications are sent out for these
953 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700954 *
955 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700956 */
957 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
958 try {
959 handlePendingPortStatusMessages(h, 0);
960 } catch (SwitchStateException e) {
961 log.error(e.getMessage());
962 }
963 }
964
965 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
966 throws SwitchStateException {
967 if (h.sw == null) {
968 String msg = "State machine error: switch is null. Should never " +
969 "happen";
970 throw new SwitchStateException(msg);
971 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800972 log.info("Processing {} pending port status messages for {}",
973 h.pendingPortStatusMsg.size(), h.sw.getStringId());
974
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800975 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700976 for (OFPortStatus ps: h.pendingPortStatusMsg) {
977 temp.add(ps);
978 handlePortStatusMessage(h, ps, false);
979 }
tom7ef8ff92014-09-17 13:08:06 -0700980 // expensive but ok - we don't expect too many port-status messages
981 // note that we cannot use clear(), because of the reasons below
982 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800983 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700984 // the iterator above takes a snapshot of the list - so while we were
985 // dealing with the pending port-status messages, we could have received
986 // newer ones. Handle them recursively, but break the recursion after
987 // five steps to avoid an attack.
988 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
989 handlePendingPortStatusMessages(h, index);
990 }
991 }
992
993 /**
994 * Handle a port status message.
995 *
996 * Handle a port status message by updating the port maps in the
997 * IOFSwitch instance and notifying Controller about the change so
998 * it can dispatch a switch update.
999 *
1000 * @param h The OFChannelHhandler that received the message
1001 * @param m The PortStatus message we received
1002 * @param doNotify if true switch port changed events will be
1003 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001004 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001005 *
1006 */
1007 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1008 boolean doNotify) throws SwitchStateException {
1009 if (h.sw == null) {
1010 String msg = getSwitchStateMessage(h, m,
1011 "State machine error: switch is null. Should never " +
1012 "happen");
1013 throw new SwitchStateException(msg);
1014 }
Saurav Das607c8982018-01-09 17:38:44 -08001015 log.info("Received port status message from {}/{}: {}",
1016 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001017
1018 h.sw.handleMessage(m);
1019 }
1020
1021
1022 /**
1023 * Process an OF message received on the channel and
1024 * update state accordingly.
1025 *
1026 * The main "event" of the state machine. Process the received message,
1027 * send follow up message if required and update state if required.
1028 *
1029 * Switches on the message type and calls more specific event handlers
1030 * for each individual OF message type. If we receive a message that
1031 * is supposed to be sent from a controller to a switch we throw
1032 * a SwitchStateExeption.
1033 *
1034 * The more specific handlers can also throw SwitchStateExceptions
1035 *
1036 * @param h The OFChannelHandler that received the message
1037 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001038 * @throws SwitchStateException if the switch is not bound to the channel
1039 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001040 */
1041 void processOFMessage(OFChannelHandler h, OFMessage m)
1042 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001043 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001044 case HELLO:
1045 processOFHello(h, (OFHello) m);
1046 break;
1047 case BARRIER_REPLY:
1048 processOFBarrierReply(h, (OFBarrierReply) m);
1049 break;
1050 case ECHO_REPLY:
1051 processOFEchoReply(h, (OFEchoReply) m);
1052 break;
1053 case ECHO_REQUEST:
1054 processOFEchoRequest(h, (OFEchoRequest) m);
1055 break;
1056 case ERROR:
1057 processOFError(h, (OFErrorMsg) m);
1058 break;
1059 case FEATURES_REPLY:
1060 processOFFeaturesReply(h, (OFFeaturesReply) m);
1061 break;
1062 case FLOW_REMOVED:
1063 processOFFlowRemoved(h, (OFFlowRemoved) m);
1064 break;
1065 case GET_CONFIG_REPLY:
1066 processOFGetConfigReply(h, (OFGetConfigReply) m);
1067 break;
1068 case PACKET_IN:
1069 processOFPacketIn(h, (OFPacketIn) m);
1070 break;
1071 case PORT_STATUS:
1072 processOFPortStatus(h, (OFPortStatus) m);
1073 break;
1074 case QUEUE_GET_CONFIG_REPLY:
1075 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1076 break;
1077 case STATS_REPLY: // multipart_reply in 1.3
1078 processOFStatisticsReply(h, (OFStatsReply) m);
1079 break;
1080 case EXPERIMENTER:
1081 processOFExperimenter(h, (OFExperimenter) m);
1082 break;
1083 case ROLE_REPLY:
1084 processOFRoleReply(h, (OFRoleReply) m);
1085 break;
1086 case GET_ASYNC_REPLY:
1087 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1088 break;
1089
1090 // The following messages are sent to switches. The controller
1091 // should never receive them
1092 case SET_CONFIG:
1093 case GET_CONFIG_REQUEST:
1094 case PACKET_OUT:
1095 case PORT_MOD:
1096 case QUEUE_GET_CONFIG_REQUEST:
1097 case BARRIER_REQUEST:
1098 case STATS_REQUEST: // multipart request in 1.3
1099 case FEATURES_REQUEST:
1100 case FLOW_MOD:
1101 case GROUP_MOD:
1102 case TABLE_MOD:
1103 case GET_ASYNC_REQUEST:
1104 case SET_ASYNC:
1105 case METER_MOD:
1106 default:
1107 illegalMessageReceived(h, m);
1108 break;
1109 }
1110 }
1111
1112 /*-----------------------------------------------------------------
1113 * Default implementation for message handlers in any state.
1114 *
1115 * Individual states must override these if they want a behavior
1116 * that differs from the default.
1117 *
1118 * In general, these handlers simply ignore the message and do
1119 * nothing.
1120 *
1121 * There are some exceptions though, since some messages really
1122 * are handled the same way in every state (e.g., ECHO_REQUST) or
1123 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1124 -----------------------------------------------------------------*/
1125
1126 void processOFHello(OFChannelHandler h, OFHello m)
1127 throws IOException, SwitchStateException {
1128 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001129 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001130 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001131 }
1132
1133 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1134 throws IOException {
1135 // Silently ignore.
1136 }
1137
1138 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1139 throws IOException {
1140 if (h.ofVersion == null) {
1141 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001142 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001143 return;
1144 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001145 OFEchoReply reply = h.factory
1146 .buildEchoReply()
1147 .setXid(m.getXid())
1148 .setData(m.getData())
1149 .build();
1150 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001151 }
1152
1153 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1154 throws IOException {
1155 // Do nothing with EchoReplies !!
1156 }
1157
1158 // no default implementation for OFError
1159 // every state must override it
1160 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1161 throws IOException, SwitchStateException;
1162
1163
1164 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1165 throws IOException, SwitchStateException {
1166 unhandledMessageReceived(h, m);
1167 }
1168
1169 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1170 throws IOException {
1171 unhandledMessageReceived(h, m);
1172 }
1173
1174 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1175 throws IOException, SwitchStateException {
1176 // we only expect config replies in the WAIT_CONFIG_REPLY state
1177 illegalMessageReceived(h, m);
1178 }
1179
1180 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1181 throws IOException {
1182 unhandledMessageReceived(h, m);
1183 }
1184
1185 // no default implementation. Every state needs to handle it.
1186 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1187 throws IOException, SwitchStateException;
1188
1189 void processOFQueueGetConfigReply(OFChannelHandler h,
1190 OFQueueGetConfigReply m)
1191 throws IOException {
1192 unhandledMessageReceived(h, m);
1193 }
1194
1195 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1196 throws IOException, SwitchStateException {
1197 unhandledMessageReceived(h, m);
1198 }
1199
1200 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1201 throws IOException, SwitchStateException {
1202 // TODO: it might make sense to parse the vendor message here
1203 // into the known vendor messages we support and then call more
1204 // specific event handlers
1205 unhandledMessageReceived(h, m);
1206 }
1207
1208 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1209 throws SwitchStateException, IOException {
1210 unhandledMessageReceived(h, m);
1211 }
1212
1213 void processOFGetAsyncReply(OFChannelHandler h,
1214 OFAsyncGetReply m) {
1215 unhandledMessageReceived(h, m);
1216 }
1217
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001218 void processIdle(OFChannelHandler h) throws IOException {
1219 // disconnect channel which did no complete handshake
1220 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1221 h.channel.disconnect();
1222 }
tom7ef8ff92014-09-17 13:08:06 -07001223 }
1224
1225
1226
1227 //*************************
1228 // Channel handler methods
1229 //*************************
1230
1231 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001232 public void channelActive(ChannelHandlerContext ctx)
1233 throws Exception {
1234
1235 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001236 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001237 channel.remoteAddress());
1238
1239 SocketAddress address = channel.remoteAddress();
1240 if (address instanceof InetSocketAddress) {
1241 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1242 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1243 if (ipAddress.isIp4()) {
1244 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1245 } else {
1246 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1247 }
1248 } else {
1249 channelId = channel.toString();
1250 }
1251
1252 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1253
alshabib70fc7fb2015-01-06 11:04:29 -08001254 /*
1255 hack to wait for the switch to tell us what it's
1256 max version is. This is not spec compliant and should
1257 be removed as soon as switches behave better.
1258 */
1259 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001260 setState(ChannelState.WAIT_HELLO);
1261 }
1262
1263 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001264 public void channelInactive(ChannelHandlerContext ctx)
1265 throws Exception {
1266
tom7ef8ff92014-09-17 13:08:06 -07001267 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001268 getSwitchInfoString());
1269
1270 if (dispatcher != null) {
1271 dispatcher.shutdown();
tom7ef8ff92014-09-17 13:08:06 -07001272 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001273
1274 if (thisdpid != 0) {
1275 if (!duplicateDpidFound) {
1276 // if the disconnected switch (on this ChannelHandler)
1277 // was not one with a duplicate-dpid, it is safe to remove all
1278 // state for it at the controller. Notice that if the disconnected
1279 // switch was a duplicate-dpid, calling the method below would clear
1280 // all state for the original switch (with the same dpid),
1281 // which we obviously don't want.
1282 log.info("{}:removal called", getSwitchInfoString());
1283 if (sw != null) {
1284 sw.removeConnectedSwitch();
1285 }
1286 } else {
1287 // A duplicate was disconnected on this ChannelHandler,
1288 // this is the same switch reconnecting, but the original state was
1289 // not cleaned up - XXX check liveness of original ChannelHandler
1290 log.info("{}:duplicate found", getSwitchInfoString());
1291 duplicateDpidFound = Boolean.FALSE;
1292 }
1293 } else {
1294 log.warn("no dpid in channelHandler registered for "
1295 + "disconnected switch {}", getSwitchInfoString());
1296 }
tom7ef8ff92014-09-17 13:08:06 -07001297 }
1298
1299 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001300 public void exceptionCaught(ChannelHandlerContext ctx,
1301 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001302 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001303
1304 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001305 // switch timeout
1306 log.error("Disconnecting switch {} due to read timeout",
1307 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001308 ctx.channel().close();
1309 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001310 log.error("Disconnecting switch {}: failed to complete handshake",
1311 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001312 ctx.channel().close();
1313 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001314 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001315 } else if (cause instanceof IOException) {
1316 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1317 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001318 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001319 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001320 if (log.isDebugEnabled()) {
1321 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001322 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001323 }
tom7ef8ff92014-09-17 13:08:06 -07001324 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001325 ctx.channel().close();
1326 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001327 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001328 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001329 if (log.isDebugEnabled()) {
1330 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001331 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001332 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001333 ctx.channel().close();
1334 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001335 log.error("Disconnecting switch "
1336 + getSwitchInfoString() +
1337 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001338 cause);
1339 ctx.channel().close();
1340 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001341 log.warn("Could not process message: queue full");
1342 } else {
1343 log.error("Error while processing message from switch "
1344 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001345 + "state " + this.state, cause);
1346 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001347 }
1348 }
1349
1350 @Override
1351 public String toString() {
1352 return getSwitchInfoString();
1353 }
1354
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001355 protected void channelIdle(ChannelHandlerContext ctx,
1356 IdleStateEvent e)
tom7ef8ff92014-09-17 13:08:06 -07001357 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001358 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001359 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001360 ctx.channel());
1361 ctx.write(Collections.singletonList(m), ctx.voidPromise());
tom7ef8ff92014-09-17 13:08:06 -07001362 // XXX S some problems here -- echo request has no transaction id, and
1363 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001364 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001365 }
1366
1367 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001368 public void userEventTriggered(ChannelHandlerContext ctx,
1369 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001370 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001371
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001372 if (evt instanceof IdleStateEvent) {
1373 channelIdle(ctx, (IdleStateEvent) evt);
1374 }
tom7ef8ff92014-09-17 13:08:06 -07001375
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001376 super.userEventTriggered(ctx, evt);
1377 }
1378
1379 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1380 @Override
1381 public void channelRead(ChannelHandlerContext ctx,
1382 Object msg) throws Exception {
1383
1384 boolean release = true;
1385 try {
1386 if (msg instanceof OFMessage) {
1387 // channelRead0 inlined
1388 state.processOFMessage(this, (OFMessage) msg);
1389 } else {
1390 release = false;
1391 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001392 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001393 } finally {
1394 if (release) {
1395 ReferenceCountUtil.release(msg);
1396 }
tom7ef8ff92014-09-17 13:08:06 -07001397 }
1398 }
1399
1400
1401
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001402
tom7ef8ff92014-09-17 13:08:06 -07001403 //*************************
1404 // Channel utility methods
1405 //*************************
1406
1407 /**
1408 * Is this a state in which the handshake has completed?
1409 * @return true if the handshake is complete
1410 */
1411 public boolean isHandshakeComplete() {
1412 return this.state.isHandshakeComplete();
1413 }
1414
1415 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001416
1417 if (dispatchBacklog.isEmpty()) {
1418 if (!dispatchQueue.offer(m)) {
1419 // queue full
1420 channel.config().setAutoRead(false);
1421 // put it on the head of backlog
1422 dispatchBacklog.addFirst(m);
1423 return;
1424 }
1425 } else {
1426 dispatchBacklog.addLast(m);
1427 }
1428
1429 while (!dispatchBacklog.isEmpty()) {
1430 OFMessage msg = dispatchBacklog.pop();
1431
1432 if (!dispatchQueue.offer(msg)) {
1433 // queue full
1434 channel.config().setAutoRead(false);
1435 // put it back to the head of backlog
1436 dispatchBacklog.addFirst(msg);
1437 return;
1438 }
1439 }
1440
1441
1442 if (dispatcherHandle.isDone()) {
1443 // dispatcher terminated for some reason, restart
1444
1445 dispatcherHandle = dispatcher.submit(() -> {
1446 try {
1447 List<OFMessage> msgs = new ArrayList<>();
1448 for (;;) {
1449 // wait for new message
1450 OFMessage msg = dispatchQueue.take();
1451 sw.handleMessage(msg);
1452
1453 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1454 if (!channel.config().isAutoRead()) {
1455 channel.config().setAutoRead(true);
1456 }
1457 msgs.forEach(sw::handleMessage);
1458 msgs.clear();
1459 }
1460
1461 if (!channel.config().isAutoRead()) {
1462 channel.config().setAutoRead(true);
1463 }
1464 }
1465 } catch (InterruptedException e) {
1466 Thread.currentThread().interrupt();
1467 // interrupted. gracefully shutting down
1468 return;
1469 }
1470
1471 });
1472 }
tom7ef8ff92014-09-17 13:08:06 -07001473 }
1474
1475 /**
1476 * Return a string describing this switch based on the already available
1477 * information (DPID and/or remote socket).
1478 * @return display string
1479 */
1480 private String getSwitchInfoString() {
1481 if (sw != null) {
1482 return sw.toString();
1483 }
1484 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001485 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001486 channelString = "?";
1487 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001488 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001489 }
1490 String dpidString;
1491 if (featuresReply == null) {
1492 dpidString = "?";
1493 } else {
1494 dpidString = featuresReply.getDatapathId().toString();
1495 }
1496 return String.format("[%s DPID[%s]]", channelString, dpidString);
1497 }
1498
1499 /**
1500 * Update the channels state. Only called from the state machine.
1501 * TODO: enforce restricted state transitions
1502 * @param state
1503 */
1504 private void setState(ChannelState state) {
1505 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001506 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001507 }
1508
1509 /**
1510 * Send hello message to the switch using the handshake transactions ids.
1511 * @throws IOException
1512 */
1513 private void sendHandshakeHelloMessage() throws IOException {
1514 // The OF protocol requires us to start things off by sending the highest
1515 // version of the protocol supported.
1516
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001517 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001518 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001519 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1520 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001521 (0b1 << OFVersion.OF_14.getWireVersion()) |
1522 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001523 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1524 OFHelloElem hem = OFFactories.getFactory(version)
1525 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001526 .setBitmaps(Collections.singletonList(bitmap))
1527 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001528 OFMessage.Builder mb = OFFactories.getFactory(version)
1529 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001530 .setXid(this.handshakeTransactionIds--)
1531 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001532 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1533 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001534 }
1535
1536 /**
1537 * Send featuresRequest msg to the switch using the handshake transactions ids.
1538 * @throws IOException
1539 */
1540 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001541 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001542 OFMessage m = factory.buildFeaturesRequest()
1543 .setXid(this.handshakeTransactionIds--)
1544 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001545 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001546 }
1547
1548 /**
1549 * Send the configuration requests to tell the switch we want full
1550 * packets.
1551 * @throws IOException
1552 */
1553 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001554 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001555 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001556
1557 // Ensure we receive the full packet via PacketIn
1558 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001559 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001560 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001561 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001562 OFSetConfig sc = factory
1563 .buildSetConfig()
1564 .setMissSendLen((short) 0xffff)
1565 .setXid(this.handshakeTransactionIds--)
1566 .build();
1567 msglist.add(sc);
1568 }
tom7ef8ff92014-09-17 13:08:06 -07001569
1570 // Barrier
1571 OFBarrierRequest br = factory
1572 .buildBarrierRequest()
1573 .setXid(this.handshakeTransactionIds--)
1574 .build();
1575 msglist.add(br);
1576
1577 // Verify (need barrier?)
1578 OFGetConfigRequest gcr = factory
1579 .buildGetConfigRequest()
1580 .setXid(this.handshakeTransactionIds--)
1581 .build();
1582 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001583 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001584 }
1585
1586 /**
1587 * send a description state request.
1588 * @throws IOException
1589 */
1590 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1591 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001592 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001593 OFDescStatsRequest dreq = factory
1594 .buildDescStatsRequest()
1595 .setXid(handshakeTransactionIds--)
1596 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001597 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001598 }
1599
Jordi Ortiz91477b82016-11-29 15:22:50 +01001600 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001601 * send a meter features request.
1602 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001603 * @throws IOException
1604 */
1605 private void sendMeterFeaturesRequest() throws IOException {
1606 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001607 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001608 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001609 OFMeterFeaturesStatsRequest mfreq = factory
1610 .buildMeterFeaturesStatsRequest()
1611 .setXid(handshakeTransactionIds--)
1612 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001613 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001614 }
1615
tom7ef8ff92014-09-17 13:08:06 -07001616 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001617 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001618 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001619 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001620 .buildPortDescStatsRequest()
1621 .setXid(handshakeTransactionIds--)
1622 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001623 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001624 }
1625
1626 ChannelState getStateForTesting() {
1627 return state;
1628 }
1629
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001630
1631 @Override
1632 public boolean isActive() {
1633 if (channel != null) {
1634 return channel.isActive();
1635 }
1636 return false;
1637 }
1638
1639 @Override
1640 public void closeSession() {
1641 if (channel != null) {
1642 channel.close();
1643 }
1644 }
1645
1646 @Override
1647 public boolean sendMsg(Iterable<OFMessage> msgs) {
1648 if (channel.isActive()) {
1649 channel.writeAndFlush(msgs, channel.voidPromise());
1650 return true;
1651 } else {
1652 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1653 getSwitchInfoString(), msgs);
1654 return false;
1655 }
1656 }
1657
1658 @Override
1659 public CharSequence sessionInfo() {
1660 return channelId;
1661 }
1662
tom7ef8ff92014-09-17 13:08:06 -07001663}