blob: 5f2c13f783915ac03d80faee3172e6124220e66b [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
tom7ef8ff92014-09-17 13:08:06 -0700144 /** transaction Ids to use during handshake. Since only one thread
145 * calls into an OFChannelHandler instance, we don't need atomic.
146 * We will count down
147 */
148 private int handshakeTransactionIds = -1;
149
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700150
151
152 private static final int MSG_READ_BUFFER = 5000;
153
154 /**
155 * OFMessage dispatch queue.
156 */
157 private final BlockingQueue<OFMessage> dispatchQueue =
158 new LinkedBlockingQueue<>(MSG_READ_BUFFER);
159
160 /**
161 * Single thread executor for OFMessage dispatching.
162 *
163 * Gets initialized on channelActive, shutdown on channelInactive.
164 */
165 private ExecutorService dispatcher;
166
167 /**
168 * Handle for dispatcher thread.
169 * <p>
170 * Should only be touched from the Channel I/O thread
171 */
172 private Future<?> dispatcherHandle = CompletableFuture.completedFuture(null);
173
174 /**
175 * Dispatch backlog.
176 * <p>
177 * Should only be touched from the Channel I/O thread
178 */
179 private final Deque<OFMessage> dispatchBacklog = new ArrayDeque<>();
180
tom7ef8ff92014-09-17 13:08:06 -0700181 /**
182 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800183 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700184 */
185 OFChannelHandler(Controller controller) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700186
tom7ef8ff92014-09-17 13:08:06 -0700187 this.controller = controller;
188 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800189 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
190 this.portDescReplies = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700191 duplicateDpidFound = Boolean.FALSE;
192 }
193
194
195
196 // XXX S consider if necessary
197 public void disconnectSwitch() {
198 sw.disconnectSwitch();
199 }
200
201
202
203 //*************************
204 // Channel State Machine
205 //*************************
206
207 /**
208 * The state machine for handling the switch/channel state. All state
209 * transitions should happen from within the state machine (and not from other
210 * parts of the code)
211 */
212 enum ChannelState {
213 /**
214 * Initial state before channel is connected.
215 */
216 INIT(false) {
217 @Override
218 void processOFMessage(OFChannelHandler h, OFMessage m)
219 throws IOException, SwitchStateException {
220 illegalMessageReceived(h, m);
221 }
222
223 @Override
224 void processOFError(OFChannelHandler h, OFErrorMsg m)
225 throws IOException {
226 // need to implement since its abstract but it will never
227 // be called
228 }
229
230 @Override
231 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
232 throws IOException {
233 unhandledMessageReceived(h, m);
234 }
235 },
236
237 /**
238 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
239 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
240 * protocol version is accepted.
241 * We send an OFFeaturesRequest depending on the protocol version selected
242 * Next state is WAIT_FEATURES_REPLY
243 */
244 WAIT_HELLO(false) {
245 @Override
246 void processOFHello(OFChannelHandler h, OFHello m)
247 throws IOException {
248 // TODO We could check for the optional bitmap, but for now
249 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500250 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
251 log.debug("Received {} Hello from {} - switching to OF "
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800252 + "version 1.3+", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700253 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800254 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700255 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800256 h.sendHandshakeHelloMessage();
Chip Boling68bc6562015-07-06 10:00:01 -0500257 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700258 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700259 + "version 1.0", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700260 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800261 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700262 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800263 OFHello hi =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700264 h.factory.buildHello()
alshabib70fc7fb2015-01-06 11:04:29 -0800265 .setXid(h.handshakeTransactionIds--)
266 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700267 h.channel.writeAndFlush(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700268 } else {
269 log.error("Received Hello of version {} from switch at {}. "
270 + "This controller works with OF1.0 and OF1.3 "
271 + "switches. Disconnecting switch ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700272 m.getVersion(), h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700273 h.channel.disconnect();
274 return;
275 }
276 h.sendHandshakeFeaturesRequestMessage();
277 h.setState(WAIT_FEATURES_REPLY);
278 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700279
tom7ef8ff92014-09-17 13:08:06 -0700280 @Override
281 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
282 throws IOException, SwitchStateException {
283 illegalMessageReceived(h, m);
284 }
285 @Override
286 void processOFStatisticsReply(OFChannelHandler h,
287 OFStatsReply m)
288 throws IOException, SwitchStateException {
289 illegalMessageReceived(h, m);
290 }
291 @Override
292 void processOFError(OFChannelHandler h, OFErrorMsg m) {
293 logErrorDisconnect(h, m);
294 }
295
296 @Override
297 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
298 throws IOException {
299 unhandledMessageReceived(h, m);
300 }
301 },
302
303
304 /**
305 * We are waiting for a features reply message. Once we receive it, the
306 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
307 * we send a SetConfig request, barrier, and GetConfig request and the
308 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
309 * request and the next state is WAIT_PORT_DESC_REPLY.
310 */
311 WAIT_FEATURES_REPLY(false) {
312 @Override
313 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
314 throws IOException {
315 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700316 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700317 h.getSwitchInfoString(), h.thisdpid);
318
319 h.featuresReply = m; //temp store
320 if (h.ofVersion == OFVersion.OF_10) {
321 h.sendHandshakeSetConfig();
322 h.setState(WAIT_CONFIG_REPLY);
323 } else {
324 //version is 1.3, must get switchport information
325 h.sendHandshakeOFPortDescRequest();
326 h.setState(WAIT_PORT_DESC_REPLY);
327 }
328 }
329 @Override
330 void processOFStatisticsReply(OFChannelHandler h,
331 OFStatsReply m)
332 throws IOException, SwitchStateException {
333 illegalMessageReceived(h, m);
334 }
335 @Override
336 void processOFError(OFChannelHandler h, OFErrorMsg m) {
337 logErrorDisconnect(h, m);
338 }
339
340 @Override
341 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
342 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800343 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700344 }
345 },
346
347 /**
348 * We are waiting for a description of the 1.3 switch ports.
349 * Once received, we send a SetConfig request
350 * Next State is WAIT_CONFIG_REPLY
351 */
352 WAIT_PORT_DESC_REPLY(false) {
353
354 @Override
355 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
356 throws SwitchStateException {
357 // Read port description
358 if (m.getStatsType() != OFStatsType.PORT_DESC) {
359 log.warn("Expecting port description stats but received stats "
360 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700361 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700362 return;
363 }
364 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700365 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700366 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700367 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800368 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700369 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800370 } else {
371 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700372 }
373 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
Saurav Das45f48152018-01-18 12:07:33 -0800374 log.debug("Received port desc reply for switch at {}: {}",
375 h.getSwitchInfoString(),
376 ((OFPortDescStatsReply) m).getEntries());
tom7ef8ff92014-09-17 13:08:06 -0700377 try {
378 h.sendHandshakeSetConfig();
379 } catch (IOException e) {
380 log.error("Unable to send setConfig after PortDescReply. "
381 + "Error: {}", e.getMessage());
382 }
383 h.setState(WAIT_CONFIG_REPLY);
384 }
385
386 @Override
387 void processOFError(OFChannelHandler h, OFErrorMsg m)
388 throws IOException, SwitchStateException {
389 logErrorDisconnect(h, m);
390
391 }
392
393 @Override
394 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
395 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800396 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700397
398 }
399 },
400
401 /**
402 * We are waiting for a config reply message. Once we receive it
403 * we send a DescriptionStatsRequest to the switch.
404 * Next state: WAIT_DESCRIPTION_STAT_REPLY
405 */
406 WAIT_CONFIG_REPLY(false) {
407 @Override
408 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
409 throws IOException {
410 if (m.getMissSendLen() == 0xffff) {
411 log.trace("Config Reply from switch {} confirms "
412 + "miss length set to 0xffff",
413 h.getSwitchInfoString());
414 } else {
415 // FIXME: we can't really deal with switches that don't send
416 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800417 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700418 + "miss length set to {}",
419 h.getSwitchInfoString(),
420 m.getMissSendLen());
421 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100422
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800423 nextState(h);
424 }
425
426 /**
427 * Transition to next state based on OF version.
428 *
429 * @param h current channel handler
430 * @throws IOException
431 */
432 private void nextState(OFChannelHandler h) throws IOException {
433 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100434 // Meters were introduced in OpenFlow 1.3
435 h.sendMeterFeaturesRequest();
436 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800437 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100438 h.sendHandshakeDescriptionStatsRequest();
439 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
440 }
tom7ef8ff92014-09-17 13:08:06 -0700441 }
442
443 @Override
444 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
445 // do nothing;
446 }
447
448 @Override
449 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
450 throws IOException, SwitchStateException {
451 illegalMessageReceived(h, m);
452 }
453 @Override
454 void processOFStatisticsReply(OFChannelHandler h,
455 OFStatsReply m)
456 throws IOException, SwitchStateException {
457 log.error("Received multipart(stats) message sub-type {}",
458 m.getStatsType());
459 illegalMessageReceived(h, m);
460 }
461
462 @Override
463 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800464 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
465 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
466 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
467 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
468 try {
469 nextState(h);
470 return;
471 } catch (IOException e) {
472 log.error("Exception thrown transitioning to next", e);
473 logErrorDisconnect(h, m);
474 }
475 }
476 }
tom7ef8ff92014-09-17 13:08:06 -0700477 logErrorDisconnect(h, m);
478 }
479
480 @Override
481 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
482 throws IOException {
483 h.pendingPortStatusMsg.add(m);
484 }
485 },
486
487
488 /**
489 * We are waiting for a OFDescriptionStat message from the switch.
490 * Once we receive any stat message we try to parse it. If it's not
491 * a description stats message we disconnect. If its the expected
492 * description stats message, we:
493 * - use the switch driver to bind the switch and get an IOFSwitch instance
494 * - setup the IOFSwitch instance
495 * - add switch controller and send the initial role
496 * request to the switch.
497 * Next state: WAIT_INITIAL_ROLE
498 * In the typical case, where switches support role request messages
499 * the next state is where we expect the role reply message.
500 * In the special case that where the switch does not support any kind
501 * of role request messages, we don't send a role message, but we do
502 * request mastership from the registry service. This controller
503 * should become master once we hear back from the registry service.
504 * All following states will have a h.sw instance!
505 */
506 WAIT_DESCRIPTION_STAT_REPLY(false) {
507 @Override
508 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
509 throws SwitchStateException {
510 // Read description, if it has been updated
511 if (m.getStatsType() != OFStatsType.DESC) {
512 log.warn("Expecting Description stats but received stats "
513 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700514 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700515 return;
516 }
tom7ef8ff92014-09-17 13:08:06 -0700517 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700518 log.info("Received switch description reply {} from switch at {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700519 drep, h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700520 // Here is where we differentiate between different kinds of switches
521 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
522
523 h.sw.setOFVersion(h.ofVersion);
524 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700525 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100526 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700527 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700528 h.sw.setChannel(h);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700529// boolean success = h.sw.connectSwitch();
530//
531// if (!success) {
532// disconnectDuplicate(h);
533// return;
534// }
tom7ef8ff92014-09-17 13:08:06 -0700535 // set switch information
536
537
538
alshabib09d48be2014-10-03 15:43:33 -0700539 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800540 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700541 //Put switch in EQUAL mode until we hear back from the global registry
542 //log.debug("Setting new switch {} to EQUAL and sending Role request",
543 // h.sw.getStringId());
544 //h.sw.activateEqualSwitch();
545 //h.setSwitchRole(RoleState.EQUAL);
546
547 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700548 if (h.sw.isDriverHandshakeComplete()) {
549 if (!h.sw.connectSwitch()) {
550 disconnectDuplicate(h);
551 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800552 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700553 h.setState(ACTIVE);
554 } else {
555 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
556 }
tom7ef8ff92014-09-17 13:08:06 -0700557
558 }
559
560 @Override
561 void processOFError(OFChannelHandler h, OFErrorMsg m) {
562 logErrorDisconnect(h, m);
563 }
564
565 @Override
566 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
567 throws IOException, SwitchStateException {
568 illegalMessageReceived(h, m);
569 }
570
571 @Override
572 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
573 throws IOException {
574 h.pendingPortStatusMsg.add(m);
575 }
576 },
577
578
579 /**
580 * We are waiting for the respective switch driver to complete its
581 * configuration. Notice that we do not consider this to be part of the main
582 * switch-controller handshake. But we do consider it as a step that comes
583 * before we declare the switch as available to the controller.
584 * Next State: depends on the role of this controller for this switch - either
585 * MASTER or EQUAL.
586 */
587 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
588
589 @Override
590 void processOFError(OFChannelHandler h, OFErrorMsg m)
591 throws IOException {
592 // will never be called. We override processOFMessage
593 }
594
alshabibd7963912014-10-20 14:52:04 -0700595
596
tom7ef8ff92014-09-17 13:08:06 -0700597 @Override
598 void processOFMessage(OFChannelHandler h, OFMessage m)
599 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700600
601 if (h.sw.isDriverHandshakeComplete()) {
602 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700603 h.state.processOFMessage(h, m);
604 return;
alshabibd7963912014-10-20 14:52:04 -0700605
606 }
607
tom7ef8ff92014-09-17 13:08:06 -0700608 if (m.getType() == OFType.ECHO_REQUEST) {
609 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700610 } else if (m.getType() == OFType.ECHO_REPLY) {
611 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700612 } else if (m.getType() == OFType.ROLE_REPLY) {
613 h.sw.handleRole(m);
614 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800615 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700616 h.sw.processDriverHandshakeMessage(m);
617 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700618 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700619 }
620 }
621 } else {
622 if (m.getType() == OFType.EXPERIMENTER &&
623 ((OFExperimenter) m).getExperimenter() ==
624 RoleManager.NICIRA_EXPERIMENTER) {
625 h.sw.handleNiciraRole(m);
626 } else {
627 h.sw.processDriverHandshakeMessage(m);
628 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700629 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700630 }
631 }
632 }
633 }
634
635 @Override
636 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
637 throws IOException, SwitchStateException {
638 h.pendingPortStatusMsg.add(m);
639 }
alshabibd7963912014-10-20 14:52:04 -0700640
641 private void moveToActive(OFChannelHandler h) {
642 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800643 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700644 h.setState(ACTIVE);
645 if (!success) {
646 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700647 }
648 }
649
tom7ef8ff92014-09-17 13:08:06 -0700650 },
651
Jordi Ortiz91477b82016-11-29 15:22:50 +0100652 /**
653 * We are expecting a OF Multi Part Meter Features Stats Reply.
654 * Notice that this information is only available for switches running
655 * OpenFlow 1.3
656 */
657 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800658
659 @Override
660 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
661 throws IOException {
662 super.processOFEchoRequest(h, m);
663 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
664 log.info("{} did not respond to MeterFeaturesRequest on time, " +
665 "moving on without it.",
666 h.getSwitchInfoString());
667 h.sendHandshakeDescriptionStatsRequest();
668 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
669 }
670 }
671
Jordi Ortiz91477b82016-11-29 15:22:50 +0100672 @Override
673 void processOFError(OFChannelHandler h, OFErrorMsg m)
674 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800675 // Hardware switches may reply OFErrorMsg if meter is not supported
676 log.warn("Received OFError {}. It seems {} does not support Meter.",
677 m.getErrType().name(), Dpid.uri(h.thisdpid));
678 log.debug("{}", m);
679 h.sendHandshakeDescriptionStatsRequest();
680 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100681 }
682
683 @Override
684 void processOFStatisticsReply(OFChannelHandler h,
685 OFStatsReply m)
686 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800687 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100688 case METER_FEATURES:
689
690 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800691 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100692 log.info("Received meter features from {} with max meters: {}",
693 h.getSwitchInfoString(),
694 ofmfsr.getFeatures().getMaxMeter());
695 h.meterFeaturesReply = ofmfsr;
696 h.sendHandshakeDescriptionStatsRequest();
697 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
698 break;
699 default:
700 log.error("Unexpected OF Multi Part stats reply");
701 illegalMessageReceived(h, m);
702 break;
703 }
704 }
705
706 @Override
707 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
708 throws IOException, SwitchStateException {
709 illegalMessageReceived(h, m);
710 }
711
712 @Override
713 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
714 throws IOException {
715 h.pendingPortStatusMsg.add(m);
716 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800717
718 @Override
719 void processIdle(OFChannelHandler h) throws IOException {
720 log.info("{} did not respond to MeterFeaturesRequest, " +
721 "moving on without it.",
722 h.getSwitchInfoString());
723 h.sendHandshakeDescriptionStatsRequest();
724 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
725 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100726 },
727
tom7ef8ff92014-09-17 13:08:06 -0700728
729 /**
730 * This controller is in MASTER role for this switch. We enter this state
731 * after requesting and winning control from the global registry.
732 * The main handshake as well as the switch-driver sub-handshake
733 * is complete at this point.
734 * // XXX S reconsider below
735 * In the (near) future we may deterministically assign controllers to
736 * switches at startup.
737 * We only leave this state if the switch disconnects or
738 * if we send a role request for SLAVE /and/ receive the role reply for
739 * SLAVE.
740 */
741 ACTIVE(true) {
742 @Override
743 void processOFError(OFChannelHandler h, OFErrorMsg m)
744 throws IOException, SwitchStateException {
745 // if we get here, then the error message is for something else
746 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700747 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700748 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700749 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700750 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700751 // We are the master controller and the switch returned
752 // a permission error. This is a likely indicator that
753 // the switch thinks we are slave. Reassert our
754 // role
755 // FIXME: this could be really bad during role transitions
756 // if two controllers are master (even if its only for
757 // a brief period). We might need to see if these errors
758 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700759
tom7ef8ff92014-09-17 13:08:06 -0700760 h.sw.reassertRole();
761 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
762 ((OFFlowModFailedErrorMsg) m).getCode() ==
763 OFFlowModFailedCode.ALL_TABLES_FULL) {
764 h.sw.setTableFull(true);
765 } else {
766 logError(h, m);
767 }
768 h.dispatchMessage(m);
769 }
770
771 @Override
772 void processOFStatisticsReply(OFChannelHandler h,
773 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700774 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
Saurav Das45f48152018-01-18 12:07:33 -0800775 log.debug("Received port desc message from {}: {}",
776 h.sw.getDpid(),
777 ((OFPortDescStatsReply) m).getEntries());
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700778 h.sw.setPortDescReply((OFPortDescStatsReply) m);
779 }
tom7ef8ff92014-09-17 13:08:06 -0700780 h.dispatchMessage(m);
781 }
782
783 @Override
784 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
785 throws SwitchStateException {
786 h.sw.handleNiciraRole(m);
787 }
788
789 @Override
790 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
791 throws SwitchStateException {
792 h.sw.handleRole(m);
793 }
794
795 @Override
796 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
797 throws SwitchStateException {
798 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800799 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700800 }
801
802 @Override
803 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700804// OFPacketOut out =
805// h.sw.factory().buildPacketOut()
806// .setXid(m.getXid())
807// .setBufferId(m.getBufferId()).build();
808// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700809 h.dispatchMessage(m);
810 }
811
812 @Override
813 void processOFFlowRemoved(OFChannelHandler h,
814 OFFlowRemoved m) {
815 h.dispatchMessage(m);
816 }
817
818 @Override
819 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
820 h.dispatchMessage(m);
821 }
822
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700823 @Override
824 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700825 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700826 h.dispatchMessage(m);
827 }
828
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800829 @Override
830 void processIdle(OFChannelHandler h) throws IOException {
831 log.info("{} idle", h.getSwitchInfoString());
832 }
833
tom7ef8ff92014-09-17 13:08:06 -0700834 };
835
836 private final boolean handshakeComplete;
837 ChannelState(boolean handshakeComplete) {
838 this.handshakeComplete = handshakeComplete;
839 }
840
841 /**
842 * Is this a state in which the handshake has completed?
843 * @return true if the handshake is complete
844 */
845 public boolean isHandshakeComplete() {
846 return handshakeComplete;
847 }
848
849 /**
850 * Get a string specifying the switch connection, state, and
851 * message received. To be used as message for SwitchStateException
852 * or log messages
853 * @param h The channel handler (to get switch information_
854 * @param m The OFMessage that has just been received
855 * @param details A string giving more details about the exact nature
856 * of the problem.
857 * @return display string
858 */
859 // needs to be protected because enum members are actually subclasses
860 protected String getSwitchStateMessage(OFChannelHandler h,
861 OFMessage m,
862 String details) {
863 return String.format("Switch: [%s], State: [%s], received: [%s]"
864 + ", details: %s",
865 h.getSwitchInfoString(),
866 this.toString(),
867 m.getType().toString(),
868 details);
869 }
870
871 /**
872 * We have an OFMessage we didn't expect given the current state and
873 * we want to treat this as an error.
874 * We currently throw an exception that will terminate the connection
875 * However, we could be more forgiving
876 * @param h the channel handler that received the message
877 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700878 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700879 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700880 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700881 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
882 throws SwitchStateException {
883 String msg = getSwitchStateMessage(h, m,
884 "Switch should never send this message in the current state");
885 throw new SwitchStateException(msg);
886
887 }
888
889 /**
890 * We have an OFMessage we didn't expect given the current state and
891 * we want to ignore the message.
892 * @param h the channel handler the received the message
893 * @param m the message
894 */
895 protected void unhandledMessageReceived(OFChannelHandler h,
896 OFMessage m) {
897 if (log.isDebugEnabled()) {
898 String msg = getSwitchStateMessage(h, m,
899 "Ignoring unexpected message");
900 log.debug(msg);
901 }
902 }
903
904 /**
905 * Log an OpenFlow error message from a switch.
906 * @param h The switch that sent the error
907 * @param error The error message
908 */
909 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700910 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700911 error,
912 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800913 this);
tom7ef8ff92014-09-17 13:08:06 -0700914 }
915
916 /**
917 * Log an OpenFlow error message from a switch and disconnect the
918 * channel.
919 *
920 * @param h the IO channel for this switch.
921 * @param error The error message
922 */
923 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
924 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700925 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700926 h.channel.disconnect();
927 }
928
929 /**
930 * log an error message for a duplicate dpid and disconnect this channel.
931 * @param h the IO channel for this switch.
932 */
933 protected void disconnectDuplicate(OFChannelHandler h) {
934 log.error("Duplicated dpid or incompleted cleanup - "
935 + "disconnecting channel {}", h.getSwitchInfoString());
936 h.duplicateDpidFound = Boolean.TRUE;
937 h.channel.disconnect();
938 }
939
940
941
942 /**
943 * Handles all pending port status messages before a switch is declared
944 * activated in MASTER or EQUAL role. Note that since this handling
945 * precedes the activation (and therefore notification to IOFSwitchListerners)
946 * the changes to ports will already be visible once the switch is
947 * activated. As a result, no notifications are sent out for these
948 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700949 *
950 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700951 */
952 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
953 try {
954 handlePendingPortStatusMessages(h, 0);
955 } catch (SwitchStateException e) {
956 log.error(e.getMessage());
957 }
958 }
959
960 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
961 throws SwitchStateException {
962 if (h.sw == null) {
963 String msg = "State machine error: switch is null. Should never " +
964 "happen";
965 throw new SwitchStateException(msg);
966 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800967 log.info("Processing {} pending port status messages for {}",
968 h.pendingPortStatusMsg.size(), h.sw.getStringId());
969
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800970 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700971 for (OFPortStatus ps: h.pendingPortStatusMsg) {
972 temp.add(ps);
973 handlePortStatusMessage(h, ps, false);
974 }
tom7ef8ff92014-09-17 13:08:06 -0700975 // expensive but ok - we don't expect too many port-status messages
976 // note that we cannot use clear(), because of the reasons below
977 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800978 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700979 // the iterator above takes a snapshot of the list - so while we were
980 // dealing with the pending port-status messages, we could have received
981 // newer ones. Handle them recursively, but break the recursion after
982 // five steps to avoid an attack.
983 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
984 handlePendingPortStatusMessages(h, index);
985 }
986 }
987
988 /**
989 * Handle a port status message.
990 *
991 * Handle a port status message by updating the port maps in the
992 * IOFSwitch instance and notifying Controller about the change so
993 * it can dispatch a switch update.
994 *
995 * @param h The OFChannelHhandler that received the message
996 * @param m The PortStatus message we received
997 * @param doNotify if true switch port changed events will be
998 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800999 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001000 *
1001 */
1002 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1003 boolean doNotify) throws SwitchStateException {
1004 if (h.sw == null) {
1005 String msg = getSwitchStateMessage(h, m,
1006 "State machine error: switch is null. Should never " +
1007 "happen");
1008 throw new SwitchStateException(msg);
1009 }
Saurav Dasbd071d82018-01-09 17:38:44 -08001010 log.info("Received port status message from {}/{}: {}",
1011 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001012
1013 h.sw.handleMessage(m);
1014 }
1015
1016
1017 /**
1018 * Process an OF message received on the channel and
1019 * update state accordingly.
1020 *
1021 * The main "event" of the state machine. Process the received message,
1022 * send follow up message if required and update state if required.
1023 *
1024 * Switches on the message type and calls more specific event handlers
1025 * for each individual OF message type. If we receive a message that
1026 * is supposed to be sent from a controller to a switch we throw
1027 * a SwitchStateExeption.
1028 *
1029 * The more specific handlers can also throw SwitchStateExceptions
1030 *
1031 * @param h The OFChannelHandler that received the message
1032 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001033 * @throws SwitchStateException if the switch is not bound to the channel
1034 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001035 */
1036 void processOFMessage(OFChannelHandler h, OFMessage m)
1037 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001038 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001039 case HELLO:
1040 processOFHello(h, (OFHello) m);
1041 break;
1042 case BARRIER_REPLY:
1043 processOFBarrierReply(h, (OFBarrierReply) m);
1044 break;
1045 case ECHO_REPLY:
1046 processOFEchoReply(h, (OFEchoReply) m);
1047 break;
1048 case ECHO_REQUEST:
1049 processOFEchoRequest(h, (OFEchoRequest) m);
1050 break;
1051 case ERROR:
1052 processOFError(h, (OFErrorMsg) m);
1053 break;
1054 case FEATURES_REPLY:
1055 processOFFeaturesReply(h, (OFFeaturesReply) m);
1056 break;
1057 case FLOW_REMOVED:
1058 processOFFlowRemoved(h, (OFFlowRemoved) m);
1059 break;
1060 case GET_CONFIG_REPLY:
1061 processOFGetConfigReply(h, (OFGetConfigReply) m);
1062 break;
1063 case PACKET_IN:
1064 processOFPacketIn(h, (OFPacketIn) m);
1065 break;
1066 case PORT_STATUS:
1067 processOFPortStatus(h, (OFPortStatus) m);
1068 break;
1069 case QUEUE_GET_CONFIG_REPLY:
1070 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1071 break;
1072 case STATS_REPLY: // multipart_reply in 1.3
1073 processOFStatisticsReply(h, (OFStatsReply) m);
1074 break;
1075 case EXPERIMENTER:
1076 processOFExperimenter(h, (OFExperimenter) m);
1077 break;
1078 case ROLE_REPLY:
1079 processOFRoleReply(h, (OFRoleReply) m);
1080 break;
1081 case GET_ASYNC_REPLY:
1082 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1083 break;
1084
1085 // The following messages are sent to switches. The controller
1086 // should never receive them
1087 case SET_CONFIG:
1088 case GET_CONFIG_REQUEST:
1089 case PACKET_OUT:
1090 case PORT_MOD:
1091 case QUEUE_GET_CONFIG_REQUEST:
1092 case BARRIER_REQUEST:
1093 case STATS_REQUEST: // multipart request in 1.3
1094 case FEATURES_REQUEST:
1095 case FLOW_MOD:
1096 case GROUP_MOD:
1097 case TABLE_MOD:
1098 case GET_ASYNC_REQUEST:
1099 case SET_ASYNC:
1100 case METER_MOD:
1101 default:
1102 illegalMessageReceived(h, m);
1103 break;
1104 }
1105 }
1106
1107 /*-----------------------------------------------------------------
1108 * Default implementation for message handlers in any state.
1109 *
1110 * Individual states must override these if they want a behavior
1111 * that differs from the default.
1112 *
1113 * In general, these handlers simply ignore the message and do
1114 * nothing.
1115 *
1116 * There are some exceptions though, since some messages really
1117 * are handled the same way in every state (e.g., ECHO_REQUST) or
1118 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1119 -----------------------------------------------------------------*/
1120
1121 void processOFHello(OFChannelHandler h, OFHello m)
1122 throws IOException, SwitchStateException {
1123 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001124 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001125 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001126 }
1127
1128 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1129 throws IOException {
1130 // Silently ignore.
1131 }
1132
1133 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1134 throws IOException {
1135 if (h.ofVersion == null) {
1136 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001137 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001138 return;
1139 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001140 OFEchoReply reply = h.factory
1141 .buildEchoReply()
1142 .setXid(m.getXid())
1143 .setData(m.getData())
1144 .build();
1145 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001146 }
1147
1148 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1149 throws IOException {
1150 // Do nothing with EchoReplies !!
1151 }
1152
1153 // no default implementation for OFError
1154 // every state must override it
1155 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1156 throws IOException, SwitchStateException;
1157
1158
1159 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1160 throws IOException, SwitchStateException {
1161 unhandledMessageReceived(h, m);
1162 }
1163
1164 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1165 throws IOException {
1166 unhandledMessageReceived(h, m);
1167 }
1168
1169 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1170 throws IOException, SwitchStateException {
1171 // we only expect config replies in the WAIT_CONFIG_REPLY state
1172 illegalMessageReceived(h, m);
1173 }
1174
1175 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1176 throws IOException {
1177 unhandledMessageReceived(h, m);
1178 }
1179
1180 // no default implementation. Every state needs to handle it.
1181 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1182 throws IOException, SwitchStateException;
1183
1184 void processOFQueueGetConfigReply(OFChannelHandler h,
1185 OFQueueGetConfigReply m)
1186 throws IOException {
1187 unhandledMessageReceived(h, m);
1188 }
1189
1190 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1191 throws IOException, SwitchStateException {
1192 unhandledMessageReceived(h, m);
1193 }
1194
1195 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1196 throws IOException, SwitchStateException {
1197 // TODO: it might make sense to parse the vendor message here
1198 // into the known vendor messages we support and then call more
1199 // specific event handlers
1200 unhandledMessageReceived(h, m);
1201 }
1202
1203 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1204 throws SwitchStateException, IOException {
1205 unhandledMessageReceived(h, m);
1206 }
1207
1208 void processOFGetAsyncReply(OFChannelHandler h,
1209 OFAsyncGetReply m) {
1210 unhandledMessageReceived(h, m);
1211 }
1212
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001213 void processIdle(OFChannelHandler h) throws IOException {
1214 // disconnect channel which did no complete handshake
1215 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1216 h.channel.disconnect();
1217 }
tom7ef8ff92014-09-17 13:08:06 -07001218 }
1219
1220
1221
1222 //*************************
1223 // Channel handler methods
1224 //*************************
1225
1226 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001227 public void channelActive(ChannelHandlerContext ctx)
1228 throws Exception {
1229
1230 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001231 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001232 channel.remoteAddress());
1233
1234 SocketAddress address = channel.remoteAddress();
1235 if (address instanceof InetSocketAddress) {
1236 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1237 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1238 if (ipAddress.isIp4()) {
1239 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1240 } else {
1241 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1242 }
1243 } else {
1244 channelId = channel.toString();
1245 }
1246
1247 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1248
alshabib70fc7fb2015-01-06 11:04:29 -08001249 /*
1250 hack to wait for the switch to tell us what it's
1251 max version is. This is not spec compliant and should
1252 be removed as soon as switches behave better.
1253 */
1254 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001255 setState(ChannelState.WAIT_HELLO);
1256 }
1257
1258 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001259 public void channelInactive(ChannelHandlerContext ctx)
1260 throws Exception {
1261
tom7ef8ff92014-09-17 13:08:06 -07001262 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001263 getSwitchInfoString());
1264
1265 if (dispatcher != null) {
Harold Huang828cd592017-11-04 10:46:04 +08001266 dispatcher.shutdownNow();
Thomas Vachuskad75684a2018-01-03 09:04:47 -08001267 dispatcher = null;
tom7ef8ff92014-09-17 13:08:06 -07001268 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001269
1270 if (thisdpid != 0) {
1271 if (!duplicateDpidFound) {
1272 // if the disconnected switch (on this ChannelHandler)
1273 // was not one with a duplicate-dpid, it is safe to remove all
1274 // state for it at the controller. Notice that if the disconnected
1275 // switch was a duplicate-dpid, calling the method below would clear
1276 // all state for the original switch (with the same dpid),
1277 // which we obviously don't want.
1278 log.info("{}:removal called", getSwitchInfoString());
1279 if (sw != null) {
1280 sw.removeConnectedSwitch();
1281 }
1282 } else {
1283 // A duplicate was disconnected on this ChannelHandler,
1284 // this is the same switch reconnecting, but the original state was
1285 // not cleaned up - XXX check liveness of original ChannelHandler
1286 log.info("{}:duplicate found", getSwitchInfoString());
1287 duplicateDpidFound = Boolean.FALSE;
1288 }
1289 } else {
1290 log.warn("no dpid in channelHandler registered for "
1291 + "disconnected switch {}", getSwitchInfoString());
1292 }
tom7ef8ff92014-09-17 13:08:06 -07001293 }
1294
1295 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001296 public void exceptionCaught(ChannelHandlerContext ctx,
1297 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001298 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001299
1300 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001301 // switch timeout
1302 log.error("Disconnecting switch {} due to read timeout",
1303 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001304 ctx.channel().close();
1305 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001306 log.error("Disconnecting switch {}: failed to complete handshake",
1307 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001308 ctx.channel().close();
1309 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001310 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001311 } else if (cause instanceof IOException) {
1312 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1313 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001314 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001315 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001316 if (log.isDebugEnabled()) {
1317 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001318 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001319 }
tom7ef8ff92014-09-17 13:08:06 -07001320 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001321 ctx.channel().close();
1322 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001323 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001324 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001325 if (log.isDebugEnabled()) {
1326 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001327 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001328 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001329 ctx.channel().close();
1330 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001331 log.error("Disconnecting switch "
1332 + getSwitchInfoString() +
1333 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001334 cause);
1335 ctx.channel().close();
1336 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001337 log.warn("Could not process message: queue full");
1338 } else {
1339 log.error("Error while processing message from switch "
1340 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001341 + "state " + this.state, cause);
1342 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001343 }
1344 }
1345
1346 @Override
1347 public String toString() {
1348 return getSwitchInfoString();
1349 }
1350
Ray Milkey986a47a2018-01-25 11:38:51 -08001351 private void channelIdle(ChannelHandlerContext ctx,
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001352 IdleStateEvent e)
Ray Milkey986a47a2018-01-25 11:38:51 -08001353 throws IOException {
tom7ef8ff92014-09-17 13:08:06 -07001354 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001355 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001356 ctx.channel());
1357 ctx.write(Collections.singletonList(m), ctx.voidPromise());
tom7ef8ff92014-09-17 13:08:06 -07001358 // XXX S some problems here -- echo request has no transaction id, and
1359 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001360 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001361 }
1362
1363 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001364 public void userEventTriggered(ChannelHandlerContext ctx,
1365 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001366 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001367
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001368 if (evt instanceof IdleStateEvent) {
1369 channelIdle(ctx, (IdleStateEvent) evt);
1370 }
tom7ef8ff92014-09-17 13:08:06 -07001371
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001372 super.userEventTriggered(ctx, evt);
1373 }
1374
1375 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1376 @Override
1377 public void channelRead(ChannelHandlerContext ctx,
1378 Object msg) throws Exception {
1379
1380 boolean release = true;
1381 try {
1382 if (msg instanceof OFMessage) {
1383 // channelRead0 inlined
1384 state.processOFMessage(this, (OFMessage) msg);
1385 } else {
1386 release = false;
1387 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001388 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001389 } finally {
1390 if (release) {
1391 ReferenceCountUtil.release(msg);
1392 }
tom7ef8ff92014-09-17 13:08:06 -07001393 }
1394 }
1395
1396
1397
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001398
tom7ef8ff92014-09-17 13:08:06 -07001399 //*************************
1400 // Channel utility methods
1401 //*************************
1402
1403 /**
1404 * Is this a state in which the handshake has completed?
1405 * @return true if the handshake is complete
1406 */
1407 public boolean isHandshakeComplete() {
1408 return this.state.isHandshakeComplete();
1409 }
1410
1411 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001412
1413 if (dispatchBacklog.isEmpty()) {
1414 if (!dispatchQueue.offer(m)) {
1415 // queue full
1416 channel.config().setAutoRead(false);
1417 // put it on the head of backlog
1418 dispatchBacklog.addFirst(m);
1419 return;
1420 }
1421 } else {
1422 dispatchBacklog.addLast(m);
1423 }
1424
1425 while (!dispatchBacklog.isEmpty()) {
1426 OFMessage msg = dispatchBacklog.pop();
1427
1428 if (!dispatchQueue.offer(msg)) {
1429 // queue full
1430 channel.config().setAutoRead(false);
1431 // put it back to the head of backlog
1432 dispatchBacklog.addFirst(msg);
1433 return;
1434 }
1435 }
1436
1437
1438 if (dispatcherHandle.isDone()) {
1439 // dispatcher terminated for some reason, restart
1440
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -08001441 dispatcherHandle = dispatcher.submit((Runnable) () -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001442 try {
1443 List<OFMessage> msgs = new ArrayList<>();
1444 for (;;) {
1445 // wait for new message
1446 OFMessage msg = dispatchQueue.take();
1447 sw.handleMessage(msg);
1448
1449 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1450 if (!channel.config().isAutoRead()) {
1451 channel.config().setAutoRead(true);
1452 }
1453 msgs.forEach(sw::handleMessage);
1454 msgs.clear();
1455 }
1456
1457 if (!channel.config().isAutoRead()) {
1458 channel.config().setAutoRead(true);
1459 }
1460 }
1461 } catch (InterruptedException e) {
1462 Thread.currentThread().interrupt();
1463 // interrupted. gracefully shutting down
1464 return;
1465 }
1466
1467 });
1468 }
tom7ef8ff92014-09-17 13:08:06 -07001469 }
1470
1471 /**
1472 * Return a string describing this switch based on the already available
1473 * information (DPID and/or remote socket).
1474 * @return display string
1475 */
1476 private String getSwitchInfoString() {
1477 if (sw != null) {
1478 return sw.toString();
1479 }
1480 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001481 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001482 channelString = "?";
1483 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001484 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001485 }
1486 String dpidString;
1487 if (featuresReply == null) {
1488 dpidString = "?";
1489 } else {
1490 dpidString = featuresReply.getDatapathId().toString();
1491 }
1492 return String.format("[%s DPID[%s]]", channelString, dpidString);
1493 }
1494
1495 /**
1496 * Update the channels state. Only called from the state machine.
1497 * TODO: enforce restricted state transitions
1498 * @param state
1499 */
1500 private void setState(ChannelState state) {
1501 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001502 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001503 }
1504
1505 /**
1506 * Send hello message to the switch using the handshake transactions ids.
1507 * @throws IOException
1508 */
1509 private void sendHandshakeHelloMessage() throws IOException {
1510 // The OF protocol requires us to start things off by sending the highest
1511 // version of the protocol supported.
1512
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001513 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001514 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001515 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1516 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001517 (0b1 << OFVersion.OF_14.getWireVersion()) |
1518 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001519 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1520 OFHelloElem hem = OFFactories.getFactory(version)
1521 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001522 .setBitmaps(Collections.singletonList(bitmap))
1523 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001524 OFMessage.Builder mb = OFFactories.getFactory(version)
1525 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001526 .setXid(this.handshakeTransactionIds--)
1527 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001528 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1529 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001530 }
1531
1532 /**
1533 * Send featuresRequest msg to the switch using the handshake transactions ids.
1534 * @throws IOException
1535 */
1536 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001537 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001538 OFMessage m = factory.buildFeaturesRequest()
1539 .setXid(this.handshakeTransactionIds--)
1540 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001541 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001542 }
1543
1544 /**
1545 * Send the configuration requests to tell the switch we want full
1546 * packets.
1547 * @throws IOException
1548 */
1549 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001550 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001551 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001552
1553 // Ensure we receive the full packet via PacketIn
1554 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001555 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001556 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001557 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001558 OFSetConfig sc = factory
1559 .buildSetConfig()
1560 .setMissSendLen((short) 0xffff)
1561 .setXid(this.handshakeTransactionIds--)
1562 .build();
1563 msglist.add(sc);
1564 }
tom7ef8ff92014-09-17 13:08:06 -07001565
1566 // Barrier
1567 OFBarrierRequest br = factory
1568 .buildBarrierRequest()
1569 .setXid(this.handshakeTransactionIds--)
1570 .build();
1571 msglist.add(br);
1572
1573 // Verify (need barrier?)
1574 OFGetConfigRequest gcr = factory
1575 .buildGetConfigRequest()
1576 .setXid(this.handshakeTransactionIds--)
1577 .build();
1578 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001579 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001580 }
1581
1582 /**
1583 * send a description state request.
1584 * @throws IOException
1585 */
1586 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1587 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001588 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001589 OFDescStatsRequest dreq = factory
1590 .buildDescStatsRequest()
1591 .setXid(handshakeTransactionIds--)
1592 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001593 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001594 }
1595
Jordi Ortiz91477b82016-11-29 15:22:50 +01001596 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001597 * send a meter features request.
1598 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001599 * @throws IOException
1600 */
1601 private void sendMeterFeaturesRequest() throws IOException {
1602 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001603 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001604 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001605 OFMeterFeaturesStatsRequest mfreq = factory
1606 .buildMeterFeaturesStatsRequest()
1607 .setXid(handshakeTransactionIds--)
1608 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001609 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001610 }
1611
tom7ef8ff92014-09-17 13:08:06 -07001612 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001613 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001614 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001615 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001616 .buildPortDescStatsRequest()
1617 .setXid(handshakeTransactionIds--)
1618 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001619 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001620 }
1621
1622 ChannelState getStateForTesting() {
1623 return state;
1624 }
1625
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001626
1627 @Override
1628 public boolean isActive() {
1629 if (channel != null) {
1630 return channel.isActive();
1631 }
1632 return false;
1633 }
1634
1635 @Override
1636 public void closeSession() {
1637 if (channel != null) {
1638 channel.close();
1639 }
1640 }
1641
1642 @Override
1643 public boolean sendMsg(Iterable<OFMessage> msgs) {
1644 if (channel.isActive()) {
Laszlo Pappb68fe7e2017-11-24 17:06:59 +00001645 if (log.isTraceEnabled()) {
1646 log.trace("Sending messages for switch {} via openflow channel: {}", getSwitchInfoString(), msgs);
1647 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001648 channel.writeAndFlush(msgs, channel.voidPromise());
1649 return true;
1650 } else {
1651 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1652 getSwitchInfoString(), msgs);
1653 return false;
1654 }
1655 }
1656
1657 @Override
1658 public CharSequence sessionInfo() {
1659 return channelId;
1660 }
1661
tom7ef8ff92014-09-17 13:08:06 -07001662}