blob: 0eb2bec7cd0d2fb335300e73b615f350a55b73e6 [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;
Brian O'Connor47a16092018-05-10 02:25:09 -070025import java.security.cert.Certificate;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070026import java.util.ArrayDeque;
tom7ef8ff92014-09-17 13:08:06 -070027import java.util.ArrayList;
28import java.util.Collections;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070029import java.util.Deque;
tom7ef8ff92014-09-17 13:08:06 -070030import java.util.List;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080031import java.util.Optional;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070032import java.util.concurrent.BlockingQueue;
33import java.util.concurrent.CompletableFuture;
tom7ef8ff92014-09-17 13:08:06 -070034import java.util.concurrent.CopyOnWriteArrayList;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070035import java.util.concurrent.ExecutorService;
36import java.util.concurrent.Executors;
37import java.util.concurrent.Future;
38import java.util.concurrent.LinkedBlockingQueue;
tom7ef8ff92014-09-17 13:08:06 -070039import java.util.concurrent.RejectedExecutionException;
Brian O'Connor47a16092018-05-10 02:25:09 -070040
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070041import org.onlab.packet.IpAddress;
Charles Chan34155e52016-11-30 18:28:11 -080042import org.onosproject.openflow.controller.Dpid;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070043import org.onosproject.openflow.controller.OpenFlowSession;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
45import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070046import org.projectfloodlight.openflow.exceptions.OFParseError;
47import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
48import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
49import org.projectfloodlight.openflow.protocol.OFBarrierReply;
50import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
51import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
52import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
53import org.projectfloodlight.openflow.protocol.OFEchoReply;
54import org.projectfloodlight.openflow.protocol.OFEchoRequest;
55import org.projectfloodlight.openflow.protocol.OFErrorMsg;
56import org.projectfloodlight.openflow.protocol.OFErrorType;
57import org.projectfloodlight.openflow.protocol.OFExperimenter;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080058import org.projectfloodlight.openflow.protocol.OFFactories;
tom7ef8ff92014-09-17 13:08:06 -070059import org.projectfloodlight.openflow.protocol.OFFactory;
60import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
61import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
62import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
63import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
64import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
65import org.projectfloodlight.openflow.protocol.OFHello;
66import org.projectfloodlight.openflow.protocol.OFHelloElem;
67import org.projectfloodlight.openflow.protocol.OFMessage;
Jordi Ortiz91477b82016-11-29 15:22:50 +010068import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
69import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsRequest;
tom7ef8ff92014-09-17 13:08:06 -070070import org.projectfloodlight.openflow.protocol.OFPacketIn;
71import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
72import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
73import org.projectfloodlight.openflow.protocol.OFPortStatus;
74import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
75import org.projectfloodlight.openflow.protocol.OFRoleReply;
76import org.projectfloodlight.openflow.protocol.OFSetConfig;
77import org.projectfloodlight.openflow.protocol.OFStatsReply;
78import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
79import org.projectfloodlight.openflow.protocol.OFStatsType;
80import org.projectfloodlight.openflow.protocol.OFType;
81import org.projectfloodlight.openflow.protocol.OFVersion;
82import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
83import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
84import org.projectfloodlight.openflow.types.U32;
85import org.slf4j.Logger;
86import org.slf4j.LoggerFactory;
87
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070088import io.netty.channel.Channel;
89import io.netty.channel.ChannelHandlerContext;
90import io.netty.channel.ChannelInboundHandlerAdapter;
Brian O'Connor47a16092018-05-10 02:25:09 -070091import io.netty.handler.ssl.SslHandler;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070092import io.netty.handler.timeout.IdleStateEvent;
93import io.netty.handler.timeout.ReadTimeoutException;
94import io.netty.util.ReferenceCountUtil;
95
Brian O'Connor47a16092018-05-10 02:25:09 -070096import javax.net.ssl.SSLPeerUnverifiedException;
97
tom7ef8ff92014-09-17 13:08:06 -070098/**
99 * Channel handler deals with the switch connection and dispatches
100 * switch messages to the appropriate locations.
101 */
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700102class OFChannelHandler extends ChannelInboundHandlerAdapter
103 implements OpenFlowSession {
104
tom7ef8ff92014-09-17 13:08:06 -0700105 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -0700106
107 private static final String RESET_BY_PEER = "Connection reset by peer";
108 private static final String BROKEN_PIPE = "Broken pipe";
109
tom7ef8ff92014-09-17 13:08:06 -0700110 private final Controller controller;
111 private OpenFlowSwitchDriver sw;
112 private long thisdpid; // channelHandler cached value of connected switch id
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700113
tom7ef8ff92014-09-17 13:08:06 -0700114 private Channel channel;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700115 private String channelId;
116
117
tom7ef8ff92014-09-17 13:08:06 -0700118 // State needs to be volatile because the HandshakeTimeoutHandler
119 // needs to check if the handshake is complete
120 private volatile ChannelState state;
121
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800122 /**
123 * Timeout in ms to wait for meter feature reply.
124 */
125 private static final long METER_TIMEOUT = 60_000;
126
127 private volatile long lastStateChange = System.currentTimeMillis();
128
tom7ef8ff92014-09-17 13:08:06 -0700129 // When a switch with a duplicate dpid is found (i.e we already have a
130 // connected switch with the same dpid), the new switch is immediately
131 // disconnected. At that point netty callsback channelDisconnected() which
132 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
133 // switch state for the older (still connected) switch
134 private volatile Boolean duplicateDpidFound;
135
136 // Temporary storage for switch-features and port-description
137 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700138 private List<OFPortDescStatsReply> portDescReplies;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100139 private OFMeterFeaturesStatsReply meterFeaturesReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700140 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700141 // a concurrent ArrayList to temporarily store port status messages
142 // before we are ready to deal with them
143 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
144
145 //Indicates the openflow version used by this switch
146 protected OFVersion ofVersion;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700147 protected OFFactory factory;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800148
tom7ef8ff92014-09-17 13:08:06 -0700149 /** transaction Ids to use during handshake. Since only one thread
150 * calls into an OFChannelHandler instance, we don't need atomic.
151 * We will count down
152 */
153 private int handshakeTransactionIds = -1;
154
pier641f1c02019-11-22 20:51:26 +0100155 // Each IDLE_INTERVAL (see OFChannelInitializer) we will perform a sanity check
156 // Idle timeout actions will be performed each MAX_IDLE_RETRY * IDLE_INTERVAL
157 private static final int MAX_IDLE_RETRY = 4;
158 private int maxIdleRetry = MAX_IDLE_RETRY;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700159
pier641f1c02019-11-22 20:51:26 +0100160 // Dispatcher buffer/read size
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700161 private static final int MSG_READ_BUFFER = 5000;
162
163 /**
164 * OFMessage dispatch queue.
165 */
166 private final BlockingQueue<OFMessage> dispatchQueue =
167 new LinkedBlockingQueue<>(MSG_READ_BUFFER);
168
169 /**
170 * Single thread executor for OFMessage dispatching.
171 *
172 * Gets initialized on channelActive, shutdown on channelInactive.
173 */
174 private ExecutorService dispatcher;
175
176 /**
177 * Handle for dispatcher thread.
178 * <p>
179 * Should only be touched from the Channel I/O thread
180 */
181 private Future<?> dispatcherHandle = CompletableFuture.completedFuture(null);
182
183 /**
184 * Dispatch backlog.
185 * <p>
186 * Should only be touched from the Channel I/O thread
187 */
188 private final Deque<OFMessage> dispatchBacklog = new ArrayDeque<>();
189
tom7ef8ff92014-09-17 13:08:06 -0700190 /**
191 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800192 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700193 */
194 OFChannelHandler(Controller controller) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700195
tom7ef8ff92014-09-17 13:08:06 -0700196 this.controller = controller;
197 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800198 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
199 this.portDescReplies = new ArrayList<>();
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
Brian O'Connor47a16092018-05-10 02:25:09 -0700322 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
tom7ef8ff92014-09-17 13:08:06 -0700323 throws IOException {
Brian O'Connor47a16092018-05-10 02:25:09 -0700324 Long dpid = m.getDatapathId().getLong();
325 if (!h.setDpid(dpid, h.channel)) {
326 log.error("Switch presented invalid certificate for dpid {}. Disconnecting",
327 dpid);
328 h.channel.disconnect();
329 return;
330 }
alshabib09d48be2014-10-03 15:43:33 -0700331 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700332 h.getSwitchInfoString(), h.thisdpid);
333
334 h.featuresReply = m; //temp store
335 if (h.ofVersion == OFVersion.OF_10) {
336 h.sendHandshakeSetConfig();
337 h.setState(WAIT_CONFIG_REPLY);
338 } else {
339 //version is 1.3, must get switchport information
340 h.sendHandshakeOFPortDescRequest();
341 h.setState(WAIT_PORT_DESC_REPLY);
342 }
343 }
344 @Override
345 void processOFStatisticsReply(OFChannelHandler h,
346 OFStatsReply m)
347 throws IOException, SwitchStateException {
348 illegalMessageReceived(h, m);
349 }
350 @Override
351 void processOFError(OFChannelHandler h, OFErrorMsg m) {
352 logErrorDisconnect(h, m);
353 }
354
355 @Override
356 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
357 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800358 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700359 }
360 },
361
362 /**
363 * We are waiting for a description of the 1.3 switch ports.
364 * Once received, we send a SetConfig request
365 * Next State is WAIT_CONFIG_REPLY
366 */
367 WAIT_PORT_DESC_REPLY(false) {
368
369 @Override
370 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
371 throws SwitchStateException {
372 // Read port description
373 if (m.getStatsType() != OFStatsType.PORT_DESC) {
374 log.warn("Expecting port description stats but received stats "
375 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700376 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700377 return;
378 }
379 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700380 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700381 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700382 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800383 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700384 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800385 } else {
386 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700387 }
388 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
Saurav Das45f48152018-01-18 12:07:33 -0800389 log.debug("Received port desc reply for switch at {}: {}",
390 h.getSwitchInfoString(),
391 ((OFPortDescStatsReply) m).getEntries());
tom7ef8ff92014-09-17 13:08:06 -0700392 try {
393 h.sendHandshakeSetConfig();
394 } catch (IOException e) {
395 log.error("Unable to send setConfig after PortDescReply. "
396 + "Error: {}", e.getMessage());
397 }
398 h.setState(WAIT_CONFIG_REPLY);
399 }
400
401 @Override
402 void processOFError(OFChannelHandler h, OFErrorMsg m)
403 throws IOException, SwitchStateException {
404 logErrorDisconnect(h, m);
405
406 }
407
408 @Override
409 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
410 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800411 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700412
413 }
414 },
415
416 /**
417 * We are waiting for a config reply message. Once we receive it
418 * we send a DescriptionStatsRequest to the switch.
419 * Next state: WAIT_DESCRIPTION_STAT_REPLY
420 */
421 WAIT_CONFIG_REPLY(false) {
422 @Override
423 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
424 throws IOException {
425 if (m.getMissSendLen() == 0xffff) {
426 log.trace("Config Reply from switch {} confirms "
427 + "miss length set to 0xffff",
428 h.getSwitchInfoString());
429 } else {
430 // FIXME: we can't really deal with switches that don't send
431 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800432 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700433 + "miss length set to {}",
434 h.getSwitchInfoString(),
435 m.getMissSendLen());
436 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100437
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800438 nextState(h);
439 }
440
441 /**
442 * Transition to next state based on OF version.
443 *
444 * @param h current channel handler
445 * @throws IOException
446 */
447 private void nextState(OFChannelHandler h) throws IOException {
448 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100449 // Meters were introduced in OpenFlow 1.3
450 h.sendMeterFeaturesRequest();
451 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800452 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100453 h.sendHandshakeDescriptionStatsRequest();
454 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
455 }
tom7ef8ff92014-09-17 13:08:06 -0700456 }
457
458 @Override
459 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
460 // do nothing;
461 }
462
463 @Override
464 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
465 throws IOException, SwitchStateException {
466 illegalMessageReceived(h, m);
467 }
468 @Override
469 void processOFStatisticsReply(OFChannelHandler h,
470 OFStatsReply m)
471 throws IOException, SwitchStateException {
472 log.error("Received multipart(stats) message sub-type {}",
473 m.getStatsType());
474 illegalMessageReceived(h, m);
475 }
476
477 @Override
478 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800479 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
480 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
481 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
482 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
483 try {
484 nextState(h);
485 return;
486 } catch (IOException e) {
487 log.error("Exception thrown transitioning to next", e);
488 logErrorDisconnect(h, m);
489 }
490 }
491 }
tom7ef8ff92014-09-17 13:08:06 -0700492 logErrorDisconnect(h, m);
493 }
494
495 @Override
496 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
497 throws IOException {
498 h.pendingPortStatusMsg.add(m);
499 }
500 },
501
502
503 /**
504 * We are waiting for a OFDescriptionStat message from the switch.
505 * Once we receive any stat message we try to parse it. If it's not
506 * a description stats message we disconnect. If its the expected
507 * description stats message, we:
508 * - use the switch driver to bind the switch and get an IOFSwitch instance
509 * - setup the IOFSwitch instance
510 * - add switch controller and send the initial role
511 * request to the switch.
512 * Next state: WAIT_INITIAL_ROLE
513 * In the typical case, where switches support role request messages
514 * the next state is where we expect the role reply message.
515 * In the special case that where the switch does not support any kind
516 * of role request messages, we don't send a role message, but we do
517 * request mastership from the registry service. This controller
518 * should become master once we hear back from the registry service.
519 * All following states will have a h.sw instance!
520 */
521 WAIT_DESCRIPTION_STAT_REPLY(false) {
522 @Override
523 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
524 throws SwitchStateException {
525 // Read description, if it has been updated
526 if (m.getStatsType() != OFStatsType.DESC) {
527 log.warn("Expecting Description stats but received stats "
528 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700529 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700530 return;
531 }
tom7ef8ff92014-09-17 13:08:06 -0700532 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700533 log.info("Received switch description reply {} from switch at {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700534 drep, h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700535 // Here is where we differentiate between different kinds of switches
536 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
537
538 h.sw.setOFVersion(h.ofVersion);
539 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700540 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100541 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700542 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700543 h.sw.setChannel(h);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700544// boolean success = h.sw.connectSwitch();
545//
546// if (!success) {
547// disconnectDuplicate(h);
548// return;
549// }
tom7ef8ff92014-09-17 13:08:06 -0700550 // set switch information
551
552
553
alshabib09d48be2014-10-03 15:43:33 -0700554 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800555 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700556 //Put switch in EQUAL mode until we hear back from the global registry
557 //log.debug("Setting new switch {} to EQUAL and sending Role request",
558 // h.sw.getStringId());
559 //h.sw.activateEqualSwitch();
560 //h.setSwitchRole(RoleState.EQUAL);
561
562 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700563 if (h.sw.isDriverHandshakeComplete()) {
pier7f292e72019-07-16 15:52:50 +0200564 // We are not able to complete the connection for a dpid collision.
565 // Same device reconnecting or different device configured with
566 // the same dpid.
alshabib9eab22f2014-10-20 17:17:31 -0700567 if (!h.sw.connectSwitch()) {
pier7f292e72019-07-16 15:52:50 +0200568 // Disconnect from the device and return
alshabib9eab22f2014-10-20 17:17:31 -0700569 disconnectDuplicate(h);
pier7f292e72019-07-16 15:52:50 +0200570 return;
alshabib9eab22f2014-10-20 17:17:31 -0700571 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800572 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700573 h.setState(ACTIVE);
574 } else {
575 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
576 }
tom7ef8ff92014-09-17 13:08:06 -0700577
578 }
579
580 @Override
581 void processOFError(OFChannelHandler h, OFErrorMsg m) {
582 logErrorDisconnect(h, m);
583 }
584
585 @Override
586 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
587 throws IOException, SwitchStateException {
588 illegalMessageReceived(h, m);
589 }
590
591 @Override
592 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
593 throws IOException {
594 h.pendingPortStatusMsg.add(m);
595 }
596 },
597
598
599 /**
600 * We are waiting for the respective switch driver to complete its
601 * configuration. Notice that we do not consider this to be part of the main
602 * switch-controller handshake. But we do consider it as a step that comes
603 * before we declare the switch as available to the controller.
604 * Next State: depends on the role of this controller for this switch - either
605 * MASTER or EQUAL.
606 */
607 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
608
609 @Override
610 void processOFError(OFChannelHandler h, OFErrorMsg m)
611 throws IOException {
612 // will never be called. We override processOFMessage
613 }
614
alshabibd7963912014-10-20 14:52:04 -0700615
616
tom7ef8ff92014-09-17 13:08:06 -0700617 @Override
618 void processOFMessage(OFChannelHandler h, OFMessage m)
619 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700620
621 if (h.sw.isDriverHandshakeComplete()) {
622 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700623 h.state.processOFMessage(h, m);
624 return;
alshabibd7963912014-10-20 14:52:04 -0700625
626 }
627
tom7ef8ff92014-09-17 13:08:06 -0700628 if (m.getType() == OFType.ECHO_REQUEST) {
629 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700630 } else if (m.getType() == OFType.ECHO_REPLY) {
631 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700632 } else if (m.getType() == OFType.ROLE_REPLY) {
633 h.sw.handleRole(m);
634 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800635 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700636 h.sw.processDriverHandshakeMessage(m);
637 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700638 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700639 }
640 }
641 } else {
642 if (m.getType() == OFType.EXPERIMENTER &&
643 ((OFExperimenter) m).getExperimenter() ==
644 RoleManager.NICIRA_EXPERIMENTER) {
645 h.sw.handleNiciraRole(m);
646 } else {
647 h.sw.processDriverHandshakeMessage(m);
648 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700649 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700650 }
651 }
652 }
653 }
654
655 @Override
656 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
657 throws IOException, SwitchStateException {
658 h.pendingPortStatusMsg.add(m);
659 }
alshabibd7963912014-10-20 14:52:04 -0700660
661 private void moveToActive(OFChannelHandler h) {
662 boolean success = h.sw.connectSwitch();
pier7f292e72019-07-16 15:52:50 +0200663 // Disconnect from the device and return
alshabibd7963912014-10-20 14:52:04 -0700664 if (!success) {
665 disconnectDuplicate(h);
pier7f292e72019-07-16 15:52:50 +0200666 return;
alshabibd7963912014-10-20 14:52:04 -0700667 }
pier7f292e72019-07-16 15:52:50 +0200668 handlePendingPortStatusMessages(h);
669 h.setState(ACTIVE);
alshabibd7963912014-10-20 14:52:04 -0700670 }
671
tom7ef8ff92014-09-17 13:08:06 -0700672 },
673
Jordi Ortiz91477b82016-11-29 15:22:50 +0100674 /**
675 * We are expecting a OF Multi Part Meter Features Stats Reply.
676 * Notice that this information is only available for switches running
677 * OpenFlow 1.3
678 */
679 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800680
681 @Override
682 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
683 throws IOException {
684 super.processOFEchoRequest(h, m);
685 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
686 log.info("{} did not respond to MeterFeaturesRequest on time, " +
687 "moving on without it.",
688 h.getSwitchInfoString());
689 h.sendHandshakeDescriptionStatsRequest();
690 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
691 }
692 }
693
Jordi Ortiz91477b82016-11-29 15:22:50 +0100694 @Override
695 void processOFError(OFChannelHandler h, OFErrorMsg m)
696 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800697 // Hardware switches may reply OFErrorMsg if meter is not supported
698 log.warn("Received OFError {}. It seems {} does not support Meter.",
699 m.getErrType().name(), Dpid.uri(h.thisdpid));
700 log.debug("{}", m);
701 h.sendHandshakeDescriptionStatsRequest();
702 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100703 }
704
705 @Override
706 void processOFStatisticsReply(OFChannelHandler h,
707 OFStatsReply m)
708 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800709 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100710 case METER_FEATURES:
711
712 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800713 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100714 log.info("Received meter features from {} with max meters: {}",
715 h.getSwitchInfoString(),
716 ofmfsr.getFeatures().getMaxMeter());
717 h.meterFeaturesReply = ofmfsr;
718 h.sendHandshakeDescriptionStatsRequest();
719 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
720 break;
721 default:
722 log.error("Unexpected OF Multi Part stats reply");
723 illegalMessageReceived(h, m);
724 break;
725 }
726 }
727
728 @Override
729 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
730 throws IOException, SwitchStateException {
731 illegalMessageReceived(h, m);
732 }
733
734 @Override
735 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
736 throws IOException {
737 h.pendingPortStatusMsg.add(m);
738 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800739
740 @Override
741 void processIdle(OFChannelHandler h) throws IOException {
742 log.info("{} did not respond to MeterFeaturesRequest, " +
743 "moving on without it.",
744 h.getSwitchInfoString());
745 h.sendHandshakeDescriptionStatsRequest();
746 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
747 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100748 },
749
tom7ef8ff92014-09-17 13:08:06 -0700750
751 /**
752 * This controller is in MASTER role for this switch. We enter this state
753 * after requesting and winning control from the global registry.
754 * The main handshake as well as the switch-driver sub-handshake
755 * is complete at this point.
756 * // XXX S reconsider below
757 * In the (near) future we may deterministically assign controllers to
758 * switches at startup.
759 * We only leave this state if the switch disconnects or
760 * if we send a role request for SLAVE /and/ receive the role reply for
761 * SLAVE.
762 */
763 ACTIVE(true) {
764 @Override
765 void processOFError(OFChannelHandler h, OFErrorMsg m)
766 throws IOException, SwitchStateException {
767 // if we get here, then the error message is for something else
768 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700769 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700770 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700771 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700772 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700773 // We are the master controller and the switch returned
774 // a permission error. This is a likely indicator that
775 // the switch thinks we are slave. Reassert our
776 // role
777 // FIXME: this could be really bad during role transitions
778 // if two controllers are master (even if its only for
779 // a brief period). We might need to see if these errors
780 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700781
tom7ef8ff92014-09-17 13:08:06 -0700782 h.sw.reassertRole();
783 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
784 ((OFFlowModFailedErrorMsg) m).getCode() ==
785 OFFlowModFailedCode.ALL_TABLES_FULL) {
786 h.sw.setTableFull(true);
787 } else {
788 logError(h, m);
789 }
790 h.dispatchMessage(m);
791 }
792
793 @Override
794 void processOFStatisticsReply(OFChannelHandler h,
795 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700796 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
Saurav Das45f48152018-01-18 12:07:33 -0800797 log.debug("Received port desc message from {}: {}",
798 h.sw.getDpid(),
799 ((OFPortDescStatsReply) m).getEntries());
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700800 h.sw.setPortDescReply((OFPortDescStatsReply) m);
801 }
tom7ef8ff92014-09-17 13:08:06 -0700802 h.dispatchMessage(m);
803 }
804
805 @Override
806 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
807 throws SwitchStateException {
808 h.sw.handleNiciraRole(m);
809 }
810
811 @Override
812 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
813 throws SwitchStateException {
814 h.sw.handleRole(m);
815 }
816
817 @Override
818 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
819 throws SwitchStateException {
820 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800821 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700822 }
823
824 @Override
825 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700826// OFPacketOut out =
827// h.sw.factory().buildPacketOut()
828// .setXid(m.getXid())
829// .setBufferId(m.getBufferId()).build();
830// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700831 h.dispatchMessage(m);
832 }
833
834 @Override
835 void processOFFlowRemoved(OFChannelHandler h,
836 OFFlowRemoved m) {
837 h.dispatchMessage(m);
838 }
839
840 @Override
841 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
842 h.dispatchMessage(m);
843 }
844
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700845 @Override
846 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700847 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700848 h.dispatchMessage(m);
849 }
850
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800851 @Override
852 void processIdle(OFChannelHandler h) throws IOException {
853 log.info("{} idle", h.getSwitchInfoString());
854 }
855
tom7ef8ff92014-09-17 13:08:06 -0700856 };
857
858 private final boolean handshakeComplete;
859 ChannelState(boolean handshakeComplete) {
860 this.handshakeComplete = handshakeComplete;
861 }
862
863 /**
864 * Is this a state in which the handshake has completed?
865 * @return true if the handshake is complete
866 */
867 public boolean isHandshakeComplete() {
868 return handshakeComplete;
869 }
870
871 /**
872 * Get a string specifying the switch connection, state, and
873 * message received. To be used as message for SwitchStateException
874 * or log messages
875 * @param h The channel handler (to get switch information_
876 * @param m The OFMessage that has just been received
877 * @param details A string giving more details about the exact nature
878 * of the problem.
879 * @return display string
880 */
881 // needs to be protected because enum members are actually subclasses
882 protected String getSwitchStateMessage(OFChannelHandler h,
883 OFMessage m,
884 String details) {
885 return String.format("Switch: [%s], State: [%s], received: [%s]"
886 + ", details: %s",
887 h.getSwitchInfoString(),
888 this.toString(),
889 m.getType().toString(),
890 details);
891 }
892
893 /**
894 * We have an OFMessage we didn't expect given the current state and
895 * we want to treat this as an error.
896 * We currently throw an exception that will terminate the connection
897 * However, we could be more forgiving
898 * @param h the channel handler that received the message
899 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700900 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700901 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700902 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700903 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
904 throws SwitchStateException {
905 String msg = getSwitchStateMessage(h, m,
906 "Switch should never send this message in the current state");
907 throw new SwitchStateException(msg);
908
909 }
910
911 /**
912 * We have an OFMessage we didn't expect given the current state and
913 * we want to ignore the message.
914 * @param h the channel handler the received the message
915 * @param m the message
916 */
917 protected void unhandledMessageReceived(OFChannelHandler h,
918 OFMessage m) {
919 if (log.isDebugEnabled()) {
920 String msg = getSwitchStateMessage(h, m,
921 "Ignoring unexpected message");
922 log.debug(msg);
923 }
924 }
925
926 /**
927 * Log an OpenFlow error message from a switch.
928 * @param h The switch that sent the error
929 * @param error The error message
930 */
931 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700932 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700933 error,
934 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800935 this);
tom7ef8ff92014-09-17 13:08:06 -0700936 }
937
938 /**
939 * Log an OpenFlow error message from a switch and disconnect the
940 * channel.
941 *
942 * @param h the IO channel for this switch.
943 * @param error The error message
944 */
945 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
946 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700947 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700948 h.channel.disconnect();
949 }
950
951 /**
952 * log an error message for a duplicate dpid and disconnect this channel.
953 * @param h the IO channel for this switch.
954 */
955 protected void disconnectDuplicate(OFChannelHandler h) {
956 log.error("Duplicated dpid or incompleted cleanup - "
957 + "disconnecting channel {}", h.getSwitchInfoString());
958 h.duplicateDpidFound = Boolean.TRUE;
959 h.channel.disconnect();
960 }
961
962
963
964 /**
965 * Handles all pending port status messages before a switch is declared
966 * activated in MASTER or EQUAL role. Note that since this handling
967 * precedes the activation (and therefore notification to IOFSwitchListerners)
968 * the changes to ports will already be visible once the switch is
969 * activated. As a result, no notifications are sent out for these
970 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700971 *
972 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700973 */
974 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
975 try {
976 handlePendingPortStatusMessages(h, 0);
977 } catch (SwitchStateException e) {
978 log.error(e.getMessage());
979 }
980 }
981
982 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
983 throws SwitchStateException {
984 if (h.sw == null) {
985 String msg = "State machine error: switch is null. Should never " +
986 "happen";
987 throw new SwitchStateException(msg);
988 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800989 log.info("Processing {} pending port status messages for {}",
990 h.pendingPortStatusMsg.size(), h.sw.getStringId());
991
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800992 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700993 for (OFPortStatus ps: h.pendingPortStatusMsg) {
994 temp.add(ps);
995 handlePortStatusMessage(h, ps, false);
996 }
tom7ef8ff92014-09-17 13:08:06 -0700997 // expensive but ok - we don't expect too many port-status messages
998 // note that we cannot use clear(), because of the reasons below
999 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -08001000 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -07001001 // the iterator above takes a snapshot of the list - so while we were
1002 // dealing with the pending port-status messages, we could have received
1003 // newer ones. Handle them recursively, but break the recursion after
1004 // five steps to avoid an attack.
1005 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1006 handlePendingPortStatusMessages(h, index);
1007 }
1008 }
1009
1010 /**
1011 * Handle a port status message.
1012 *
1013 * Handle a port status message by updating the port maps in the
1014 * IOFSwitch instance and notifying Controller about the change so
1015 * it can dispatch a switch update.
1016 *
1017 * @param h The OFChannelHhandler that received the message
1018 * @param m The PortStatus message we received
1019 * @param doNotify if true switch port changed events will be
1020 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001021 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001022 *
1023 */
1024 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1025 boolean doNotify) throws SwitchStateException {
1026 if (h.sw == null) {
1027 String msg = getSwitchStateMessage(h, m,
1028 "State machine error: switch is null. Should never " +
1029 "happen");
1030 throw new SwitchStateException(msg);
1031 }
Saurav Dasbd071d82018-01-09 17:38:44 -08001032 log.info("Received port status message from {}/{}: {}",
1033 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001034
1035 h.sw.handleMessage(m);
1036 }
1037
1038
1039 /**
1040 * Process an OF message received on the channel and
1041 * update state accordingly.
1042 *
1043 * The main "event" of the state machine. Process the received message,
1044 * send follow up message if required and update state if required.
1045 *
1046 * Switches on the message type and calls more specific event handlers
1047 * for each individual OF message type. If we receive a message that
1048 * is supposed to be sent from a controller to a switch we throw
1049 * a SwitchStateExeption.
1050 *
1051 * The more specific handlers can also throw SwitchStateExceptions
1052 *
1053 * @param h The OFChannelHandler that received the message
1054 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001055 * @throws SwitchStateException if the switch is not bound to the channel
1056 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001057 */
1058 void processOFMessage(OFChannelHandler h, OFMessage m)
1059 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001060 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001061 case HELLO:
1062 processOFHello(h, (OFHello) m);
1063 break;
1064 case BARRIER_REPLY:
1065 processOFBarrierReply(h, (OFBarrierReply) m);
1066 break;
1067 case ECHO_REPLY:
1068 processOFEchoReply(h, (OFEchoReply) m);
1069 break;
1070 case ECHO_REQUEST:
1071 processOFEchoRequest(h, (OFEchoRequest) m);
1072 break;
1073 case ERROR:
1074 processOFError(h, (OFErrorMsg) m);
1075 break;
1076 case FEATURES_REPLY:
1077 processOFFeaturesReply(h, (OFFeaturesReply) m);
1078 break;
1079 case FLOW_REMOVED:
1080 processOFFlowRemoved(h, (OFFlowRemoved) m);
1081 break;
1082 case GET_CONFIG_REPLY:
1083 processOFGetConfigReply(h, (OFGetConfigReply) m);
1084 break;
1085 case PACKET_IN:
1086 processOFPacketIn(h, (OFPacketIn) m);
1087 break;
1088 case PORT_STATUS:
1089 processOFPortStatus(h, (OFPortStatus) m);
1090 break;
1091 case QUEUE_GET_CONFIG_REPLY:
1092 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1093 break;
1094 case STATS_REPLY: // multipart_reply in 1.3
1095 processOFStatisticsReply(h, (OFStatsReply) m);
1096 break;
1097 case EXPERIMENTER:
1098 processOFExperimenter(h, (OFExperimenter) m);
1099 break;
1100 case ROLE_REPLY:
1101 processOFRoleReply(h, (OFRoleReply) m);
1102 break;
1103 case GET_ASYNC_REPLY:
1104 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1105 break;
1106
1107 // The following messages are sent to switches. The controller
1108 // should never receive them
1109 case SET_CONFIG:
1110 case GET_CONFIG_REQUEST:
1111 case PACKET_OUT:
1112 case PORT_MOD:
1113 case QUEUE_GET_CONFIG_REQUEST:
1114 case BARRIER_REQUEST:
1115 case STATS_REQUEST: // multipart request in 1.3
1116 case FEATURES_REQUEST:
1117 case FLOW_MOD:
1118 case GROUP_MOD:
1119 case TABLE_MOD:
1120 case GET_ASYNC_REQUEST:
1121 case SET_ASYNC:
1122 case METER_MOD:
1123 default:
1124 illegalMessageReceived(h, m);
1125 break;
1126 }
1127 }
1128
1129 /*-----------------------------------------------------------------
1130 * Default implementation for message handlers in any state.
1131 *
1132 * Individual states must override these if they want a behavior
1133 * that differs from the default.
1134 *
1135 * In general, these handlers simply ignore the message and do
1136 * nothing.
1137 *
1138 * There are some exceptions though, since some messages really
1139 * are handled the same way in every state (e.g., ECHO_REQUST) or
1140 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1141 -----------------------------------------------------------------*/
1142
1143 void processOFHello(OFChannelHandler h, OFHello m)
1144 throws IOException, SwitchStateException {
1145 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001146 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001147 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001148 }
1149
1150 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1151 throws IOException {
1152 // Silently ignore.
1153 }
1154
1155 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1156 throws IOException {
1157 if (h.ofVersion == null) {
1158 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001159 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001160 return;
1161 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001162 OFEchoReply reply = h.factory
1163 .buildEchoReply()
1164 .setXid(m.getXid())
1165 .setData(m.getData())
1166 .build();
1167 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001168 }
1169
1170 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1171 throws IOException {
1172 // Do nothing with EchoReplies !!
1173 }
1174
1175 // no default implementation for OFError
1176 // every state must override it
1177 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1178 throws IOException, SwitchStateException;
1179
1180
1181 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1182 throws IOException, SwitchStateException {
1183 unhandledMessageReceived(h, m);
1184 }
1185
1186 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1187 throws IOException {
1188 unhandledMessageReceived(h, m);
1189 }
1190
1191 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1192 throws IOException, SwitchStateException {
1193 // we only expect config replies in the WAIT_CONFIG_REPLY state
1194 illegalMessageReceived(h, m);
1195 }
1196
1197 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1198 throws IOException {
1199 unhandledMessageReceived(h, m);
1200 }
1201
1202 // no default implementation. Every state needs to handle it.
1203 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1204 throws IOException, SwitchStateException;
1205
1206 void processOFQueueGetConfigReply(OFChannelHandler h,
1207 OFQueueGetConfigReply m)
1208 throws IOException {
1209 unhandledMessageReceived(h, m);
1210 }
1211
1212 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1213 throws IOException, SwitchStateException {
1214 unhandledMessageReceived(h, m);
1215 }
1216
1217 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1218 throws IOException, SwitchStateException {
1219 // TODO: it might make sense to parse the vendor message here
1220 // into the known vendor messages we support and then call more
1221 // specific event handlers
1222 unhandledMessageReceived(h, m);
1223 }
1224
1225 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1226 throws SwitchStateException, IOException {
1227 unhandledMessageReceived(h, m);
1228 }
1229
1230 void processOFGetAsyncReply(OFChannelHandler h,
1231 OFAsyncGetReply m) {
1232 unhandledMessageReceived(h, m);
1233 }
1234
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001235 void processIdle(OFChannelHandler h) throws IOException {
1236 // disconnect channel which did no complete handshake
1237 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1238 h.channel.disconnect();
1239 }
tom7ef8ff92014-09-17 13:08:06 -07001240 }
1241
1242
1243
1244 //*************************
1245 // Channel handler methods
1246 //*************************
1247
1248 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001249 public void channelActive(ChannelHandlerContext ctx)
1250 throws Exception {
1251
1252 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001253 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001254 channel.remoteAddress());
1255
1256 SocketAddress address = channel.remoteAddress();
1257 if (address instanceof InetSocketAddress) {
1258 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1259 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1260 if (ipAddress.isIp4()) {
1261 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1262 } else {
1263 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1264 }
1265 } else {
1266 channelId = channel.toString();
1267 }
1268
1269 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1270
alshabib70fc7fb2015-01-06 11:04:29 -08001271 /*
1272 hack to wait for the switch to tell us what it's
1273 max version is. This is not spec compliant and should
1274 be removed as soon as switches behave better.
1275 */
1276 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001277 setState(ChannelState.WAIT_HELLO);
1278 }
1279
1280 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001281 public void channelInactive(ChannelHandlerContext ctx)
1282 throws Exception {
1283
tom7ef8ff92014-09-17 13:08:06 -07001284 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001285 getSwitchInfoString());
1286
1287 if (dispatcher != null) {
Harold Huang828cd592017-11-04 10:46:04 +08001288 dispatcher.shutdownNow();
Thomas Vachuskad75684a2018-01-03 09:04:47 -08001289 dispatcher = null;
tom7ef8ff92014-09-17 13:08:06 -07001290 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001291
1292 if (thisdpid != 0) {
1293 if (!duplicateDpidFound) {
1294 // if the disconnected switch (on this ChannelHandler)
1295 // was not one with a duplicate-dpid, it is safe to remove all
1296 // state for it at the controller. Notice that if the disconnected
1297 // switch was a duplicate-dpid, calling the method below would clear
1298 // all state for the original switch (with the same dpid),
1299 // which we obviously don't want.
1300 log.info("{}:removal called", getSwitchInfoString());
1301 if (sw != null) {
1302 sw.removeConnectedSwitch();
1303 }
1304 } else {
1305 // A duplicate was disconnected on this ChannelHandler,
1306 // this is the same switch reconnecting, but the original state was
1307 // not cleaned up - XXX check liveness of original ChannelHandler
1308 log.info("{}:duplicate found", getSwitchInfoString());
1309 duplicateDpidFound = Boolean.FALSE;
1310 }
1311 } else {
1312 log.warn("no dpid in channelHandler registered for "
1313 + "disconnected switch {}", getSwitchInfoString());
1314 }
tom7ef8ff92014-09-17 13:08:06 -07001315 }
1316
1317 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001318 public void exceptionCaught(ChannelHandlerContext ctx,
1319 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001320 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001321
1322 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001323 // switch timeout
1324 log.error("Disconnecting switch {} due to read timeout",
1325 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001326 ctx.channel().close();
1327 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001328 log.error("Disconnecting switch {}: failed to complete handshake",
1329 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001330 ctx.channel().close();
1331 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001332 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001333 } else if (cause instanceof IOException) {
1334 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1335 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001336 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001337 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001338 if (log.isDebugEnabled()) {
1339 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001340 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001341 }
tom7ef8ff92014-09-17 13:08:06 -07001342 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001343 ctx.channel().close();
1344 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001345 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001346 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001347 if (log.isDebugEnabled()) {
1348 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001349 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001350 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001351 ctx.channel().close();
1352 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001353 log.error("Disconnecting switch "
1354 + getSwitchInfoString() +
1355 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001356 cause);
1357 ctx.channel().close();
1358 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001359 log.warn("Could not process message: queue full");
1360 } else {
1361 log.error("Error while processing message from switch "
1362 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001363 + "state " + this.state, cause);
1364 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001365 }
1366 }
1367
1368 @Override
1369 public String toString() {
1370 return getSwitchInfoString();
1371 }
1372
pier641f1c02019-11-22 20:51:26 +01001373 // We have reduced the idle period, the idea is to use
1374 // the IdleHandler to perform also some sanity checks.
1375 // Previous code is still executed with the same frequency
1376 // which is IDLE_INTERVAL * MAX_IDLE_RETRY of inactivity
Ray Milkey986a47a2018-01-25 11:38:51 -08001377 private void channelIdle(ChannelHandlerContext ctx,
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001378 IdleStateEvent e)
Ray Milkey986a47a2018-01-25 11:38:51 -08001379 throws IOException {
pier641f1c02019-11-22 20:51:26 +01001380 // dispatcher terminated for some reason, restart
1381 if (dispatcherHandle.isDone()) {
1382 dispatcherHandle = dispatcher.submit(new Dispatcher());
Charles Chan982d3902018-03-21 14:58:53 -07001383 }
pier641f1c02019-11-22 20:51:26 +01001384 // drain the backlog
1385 while (!dispatchBacklog.isEmpty()) {
1386 OFMessage msg = dispatchBacklog.pop();
1387 // move packet to the dispatcher queue
1388 if (!dispatchQueue.offer(msg)) {
1389 // queue full
1390 channel.config().setAutoRead(false);
1391 // put it back to the head of backlog
1392 dispatchBacklog.addFirst(msg);
1393 break;
1394 }
1395 }
1396 // Original timeout reached
1397 if (--maxIdleRetry == 0) {
1398 maxIdleRetry = MAX_IDLE_RETRY;
1399 // Factory can be null if the channel goes idle during initial handshake. Since the switch
1400 // is not even initialized properly, we just skip this and disconnect the channel.
1401 if (factory != null) {
1402 // send an echo request each time idle_timeout * TICK
1403 OFMessage m = factory.buildEchoRequest().build();
1404 log.info("Sending Echo Request on idle channel: {}", ctx.channel());
1405 // XXX S some problems here -- echo request has no transaction id, and
1406 // echo reply is not correlated to the echo request.
1407 ctx.writeAndFlush(Collections.singletonList(m), ctx.voidPromise());
1408 }
1409 state.processIdle(this);
1410 }
tom7ef8ff92014-09-17 13:08:06 -07001411 }
1412
1413 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001414 public void userEventTriggered(ChannelHandlerContext ctx,
1415 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001416 throws Exception {
pier6fe5a802019-10-24 16:53:25 +02001417 // If the connection is READER/WRITER idle try to send an echo request
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001418 if (evt instanceof IdleStateEvent) {
pier641f1c02019-11-22 20:51:26 +01001419 log.debug("Channel {} is {}", ctx.channel(), ((IdleStateEvent) evt).state());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001420 channelIdle(ctx, (IdleStateEvent) evt);
pier6fe5a802019-10-24 16:53:25 +02001421 } else {
1422 super.userEventTriggered(ctx, evt);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001423 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001424 }
1425
1426 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1427 @Override
1428 public void channelRead(ChannelHandlerContext ctx,
1429 Object msg) throws Exception {
1430
1431 boolean release = true;
pier641f1c02019-11-22 20:51:26 +01001432 maxIdleRetry = MAX_IDLE_RETRY;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001433 try {
1434 if (msg instanceof OFMessage) {
1435 // channelRead0 inlined
1436 state.processOFMessage(this, (OFMessage) msg);
1437 } else {
1438 release = false;
1439 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001440 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001441 } finally {
1442 if (release) {
1443 ReferenceCountUtil.release(msg);
1444 }
tom7ef8ff92014-09-17 13:08:06 -07001445 }
1446 }
1447
1448
1449
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001450
tom7ef8ff92014-09-17 13:08:06 -07001451 //*************************
1452 // Channel utility methods
1453 //*************************
1454
1455 /**
1456 * Is this a state in which the handshake has completed?
1457 * @return true if the handshake is complete
1458 */
1459 public boolean isHandshakeComplete() {
1460 return this.state.isHandshakeComplete();
1461 }
1462
1463 private void dispatchMessage(OFMessage m) {
pier641f1c02019-11-22 20:51:26 +01001464 // backlog is empty
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001465 if (dispatchBacklog.isEmpty()) {
1466 if (!dispatchQueue.offer(m)) {
1467 // queue full
1468 channel.config().setAutoRead(false);
1469 // put it on the head of backlog
1470 dispatchBacklog.addFirst(m);
pier641f1c02019-11-22 20:51:26 +01001471 // dispatcher terminated for some reason, restart
1472 if (dispatcherHandle.isDone()) {
1473 dispatcherHandle = dispatcher.submit(new Dispatcher());
1474 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001475 return;
1476 }
1477 } else {
1478 dispatchBacklog.addLast(m);
1479 }
pier641f1c02019-11-22 20:51:26 +01001480 // drain the backlog
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001481 while (!dispatchBacklog.isEmpty()) {
1482 OFMessage msg = dispatchBacklog.pop();
pier641f1c02019-11-22 20:51:26 +01001483 // move packet to the dispatcher queue
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001484 if (!dispatchQueue.offer(msg)) {
1485 // queue full
1486 channel.config().setAutoRead(false);
1487 // put it back to the head of backlog
1488 dispatchBacklog.addFirst(msg);
pier641f1c02019-11-22 20:51:26 +01001489 // dispatcher terminated for some reason, restart
1490 if (dispatcherHandle.isDone()) {
1491 dispatcherHandle = dispatcher.submit(new Dispatcher());
1492 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001493 return;
1494 }
1495 }
pier641f1c02019-11-22 20:51:26 +01001496 // dispatcher terminated for some reason, restart
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001497 if (dispatcherHandle.isDone()) {
pier641f1c02019-11-22 20:51:26 +01001498 dispatcherHandle = dispatcher.submit(new Dispatcher());
1499 }
1500 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001501
pier641f1c02019-11-22 20:51:26 +01001502 private final class Dispatcher implements Runnable {
1503 // dispatch loop
1504 @Override
1505 public void run() {
1506 try {
1507 List<OFMessage> msgs = new ArrayList<>();
1508 for (;;) {
1509 // wait for new message
1510 OFMessage msg = dispatchQueue.take();
1511 sw.handleMessage(msg);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001512
pier641f1c02019-11-22 20:51:26 +01001513 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001514 if (!channel.config().isAutoRead()) {
1515 channel.config().setAutoRead(true);
1516 }
pier641f1c02019-11-22 20:51:26 +01001517 msgs.forEach(sw::handleMessage);
1518 msgs.clear();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001519 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001520
pier641f1c02019-11-22 20:51:26 +01001521 if (!channel.config().isAutoRead()) {
1522 channel.config().setAutoRead(true);
1523 }
1524 }
1525 } catch (InterruptedException e) {
1526 Thread.currentThread().interrupt();
1527 // interrupted. gracefully shutting down
1528 log.warn("Dispatcher thread InterruptedException:", e);
1529
1530 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001531 }
tom7ef8ff92014-09-17 13:08:06 -07001532 }
1533
1534 /**
1535 * Return a string describing this switch based on the already available
1536 * information (DPID and/or remote socket).
1537 * @return display string
1538 */
1539 private String getSwitchInfoString() {
1540 if (sw != null) {
1541 return sw.toString();
1542 }
1543 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001544 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001545 channelString = "?";
1546 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001547 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001548 }
1549 String dpidString;
1550 if (featuresReply == null) {
1551 dpidString = "?";
1552 } else {
1553 dpidString = featuresReply.getDatapathId().toString();
1554 }
1555 return String.format("[%s DPID[%s]]", channelString, dpidString);
1556 }
1557
1558 /**
1559 * Update the channels state. Only called from the state machine.
1560 * TODO: enforce restricted state transitions
pier7f292e72019-07-16 15:52:50 +02001561 * @param state new state
tom7ef8ff92014-09-17 13:08:06 -07001562 */
pier7f292e72019-07-16 15:52:50 +02001563 void setState(ChannelState state) {
tom7ef8ff92014-09-17 13:08:06 -07001564 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001565 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001566 }
1567
Brian O'Connor47a16092018-05-10 02:25:09 -07001568 private boolean setDpid(Long dpid, Channel channel) {
1569 ChannelHandlerContext sslContext = channel.pipeline().context(SslHandler.class);
1570 if (sslContext != null) {
1571 try {
1572 SslHandler sslHandler = (SslHandler) sslContext.handler();
1573 Certificate[] certs = sslHandler.engine().getSession().getPeerCertificates();
1574 Certificate cert = certs.length > 0 ? certs[0] : null;
1575 if (!controller.isValidCertificate(dpid, cert)) {
1576 return false;
1577 }
1578 } catch (SSLPeerUnverifiedException e) {
1579 log.info("Switch with dpid {} is an unverified SSL peer.", dpid, e);
1580 return false;
1581 }
1582 }
1583 this.thisdpid = dpid;
1584 return true;
1585 }
1586
tom7ef8ff92014-09-17 13:08:06 -07001587 /**
1588 * Send hello message to the switch using the handshake transactions ids.
1589 * @throws IOException
1590 */
1591 private void sendHandshakeHelloMessage() throws IOException {
1592 // The OF protocol requires us to start things off by sending the highest
1593 // version of the protocol supported.
1594
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001595 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001596 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001597 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1598 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001599 (0b1 << OFVersion.OF_14.getWireVersion()) |
1600 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001601 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1602 OFHelloElem hem = OFFactories.getFactory(version)
1603 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001604 .setBitmaps(Collections.singletonList(bitmap))
1605 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001606 OFMessage.Builder mb = OFFactories.getFactory(version)
1607 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001608 .setXid(this.handshakeTransactionIds--)
1609 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001610 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1611 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001612 }
1613
1614 /**
1615 * Send featuresRequest msg to the switch using the handshake transactions ids.
1616 * @throws IOException
1617 */
1618 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001619 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001620 OFMessage m = factory.buildFeaturesRequest()
1621 .setXid(this.handshakeTransactionIds--)
1622 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001623 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001624 }
1625
1626 /**
1627 * Send the configuration requests to tell the switch we want full
1628 * packets.
1629 * @throws IOException
1630 */
1631 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001632 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001633 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001634
1635 // Ensure we receive the full packet via PacketIn
1636 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001637 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001638 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001639 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001640 OFSetConfig sc = factory
1641 .buildSetConfig()
1642 .setMissSendLen((short) 0xffff)
1643 .setXid(this.handshakeTransactionIds--)
1644 .build();
1645 msglist.add(sc);
1646 }
tom7ef8ff92014-09-17 13:08:06 -07001647
1648 // Barrier
1649 OFBarrierRequest br = factory
1650 .buildBarrierRequest()
1651 .setXid(this.handshakeTransactionIds--)
1652 .build();
1653 msglist.add(br);
1654
1655 // Verify (need barrier?)
1656 OFGetConfigRequest gcr = factory
1657 .buildGetConfigRequest()
1658 .setXid(this.handshakeTransactionIds--)
1659 .build();
1660 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001661 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001662 }
1663
1664 /**
1665 * send a description state request.
1666 * @throws IOException
1667 */
1668 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1669 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001670 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001671 OFDescStatsRequest dreq = factory
1672 .buildDescStatsRequest()
1673 .setXid(handshakeTransactionIds--)
1674 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001675 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001676 }
1677
Jordi Ortiz91477b82016-11-29 15:22:50 +01001678 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001679 * send a meter features request.
1680 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001681 * @throws IOException
1682 */
1683 private void sendMeterFeaturesRequest() throws IOException {
1684 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001685 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001686 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001687 OFMeterFeaturesStatsRequest mfreq = factory
1688 .buildMeterFeaturesStatsRequest()
1689 .setXid(handshakeTransactionIds--)
1690 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001691 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001692 }
1693
tom7ef8ff92014-09-17 13:08:06 -07001694 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001695 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001696 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001697 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001698 .buildPortDescStatsRequest()
1699 .setXid(handshakeTransactionIds--)
1700 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001701 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001702 }
1703
1704 ChannelState getStateForTesting() {
1705 return state;
1706 }
1707
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001708
1709 @Override
1710 public boolean isActive() {
1711 if (channel != null) {
1712 return channel.isActive();
1713 }
1714 return false;
1715 }
1716
1717 @Override
1718 public void closeSession() {
1719 if (channel != null) {
1720 channel.close();
1721 }
1722 }
1723
1724 @Override
1725 public boolean sendMsg(Iterable<OFMessage> msgs) {
1726 if (channel.isActive()) {
Laszlo Pappb68fe7e2017-11-24 17:06:59 +00001727 if (log.isTraceEnabled()) {
1728 log.trace("Sending messages for switch {} via openflow channel: {}", getSwitchInfoString(), msgs);
1729 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001730 channel.writeAndFlush(msgs, channel.voidPromise());
1731 return true;
1732 } else {
1733 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1734 getSwitchInfoString(), msgs);
1735 return false;
1736 }
1737 }
1738
1739 @Override
1740 public CharSequence sessionInfo() {
1741 return channelId;
1742 }
1743
tom7ef8ff92014-09-17 13:08:06 -07001744}