blob: e04bd41eed7c4d689c745957d1264a4900f1a71d [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
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700155
156
157 private static final int MSG_READ_BUFFER = 5000;
158
159 /**
160 * OFMessage dispatch queue.
161 */
162 private final BlockingQueue<OFMessage> dispatchQueue =
163 new LinkedBlockingQueue<>(MSG_READ_BUFFER);
164
165 /**
166 * Single thread executor for OFMessage dispatching.
167 *
168 * Gets initialized on channelActive, shutdown on channelInactive.
169 */
170 private ExecutorService dispatcher;
171
172 /**
173 * Handle for dispatcher thread.
174 * <p>
175 * Should only be touched from the Channel I/O thread
176 */
177 private Future<?> dispatcherHandle = CompletableFuture.completedFuture(null);
178
179 /**
180 * Dispatch backlog.
181 * <p>
182 * Should only be touched from the Channel I/O thread
183 */
184 private final Deque<OFMessage> dispatchBacklog = new ArrayDeque<>();
185
tom7ef8ff92014-09-17 13:08:06 -0700186 /**
187 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800188 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700189 */
190 OFChannelHandler(Controller controller) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700191
tom7ef8ff92014-09-17 13:08:06 -0700192 this.controller = controller;
193 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800194 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
195 this.portDescReplies = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700196 duplicateDpidFound = Boolean.FALSE;
197 }
198
199
200
201 // XXX S consider if necessary
202 public void disconnectSwitch() {
203 sw.disconnectSwitch();
204 }
205
206
207
208 //*************************
209 // Channel State Machine
210 //*************************
211
212 /**
213 * The state machine for handling the switch/channel state. All state
214 * transitions should happen from within the state machine (and not from other
215 * parts of the code)
216 */
217 enum ChannelState {
218 /**
219 * Initial state before channel is connected.
220 */
221 INIT(false) {
222 @Override
223 void processOFMessage(OFChannelHandler h, OFMessage m)
224 throws IOException, SwitchStateException {
225 illegalMessageReceived(h, m);
226 }
227
228 @Override
229 void processOFError(OFChannelHandler h, OFErrorMsg m)
230 throws IOException {
231 // need to implement since its abstract but it will never
232 // be called
233 }
234
235 @Override
236 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
237 throws IOException {
238 unhandledMessageReceived(h, m);
239 }
240 },
241
242 /**
243 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
244 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
245 * protocol version is accepted.
246 * We send an OFFeaturesRequest depending on the protocol version selected
247 * Next state is WAIT_FEATURES_REPLY
248 */
249 WAIT_HELLO(false) {
250 @Override
251 void processOFHello(OFChannelHandler h, OFHello m)
252 throws IOException {
253 // TODO We could check for the optional bitmap, but for now
254 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500255 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
256 log.debug("Received {} Hello from {} - switching to OF "
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800257 + "version 1.3+", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700258 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800259 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700260 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800261 h.sendHandshakeHelloMessage();
Chip Boling68bc6562015-07-06 10:00:01 -0500262 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700263 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700264 + "version 1.0", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700265 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800266 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700267 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800268 OFHello hi =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700269 h.factory.buildHello()
alshabib70fc7fb2015-01-06 11:04:29 -0800270 .setXid(h.handshakeTransactionIds--)
271 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700272 h.channel.writeAndFlush(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700273 } else {
274 log.error("Received Hello of version {} from switch at {}. "
275 + "This controller works with OF1.0 and OF1.3 "
276 + "switches. Disconnecting switch ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700277 m.getVersion(), h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700278 h.channel.disconnect();
279 return;
280 }
281 h.sendHandshakeFeaturesRequestMessage();
282 h.setState(WAIT_FEATURES_REPLY);
283 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700284
tom7ef8ff92014-09-17 13:08:06 -0700285 @Override
286 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
287 throws IOException, SwitchStateException {
288 illegalMessageReceived(h, m);
289 }
290 @Override
291 void processOFStatisticsReply(OFChannelHandler h,
292 OFStatsReply m)
293 throws IOException, SwitchStateException {
294 illegalMessageReceived(h, m);
295 }
296 @Override
297 void processOFError(OFChannelHandler h, OFErrorMsg m) {
298 logErrorDisconnect(h, m);
299 }
300
301 @Override
302 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
303 throws IOException {
304 unhandledMessageReceived(h, m);
305 }
306 },
307
308
309 /**
310 * We are waiting for a features reply message. Once we receive it, the
311 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
312 * we send a SetConfig request, barrier, and GetConfig request and the
313 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
314 * request and the next state is WAIT_PORT_DESC_REPLY.
315 */
316 WAIT_FEATURES_REPLY(false) {
317 @Override
Brian O'Connor47a16092018-05-10 02:25:09 -0700318 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
tom7ef8ff92014-09-17 13:08:06 -0700319 throws IOException {
Brian O'Connor47a16092018-05-10 02:25:09 -0700320 Long dpid = m.getDatapathId().getLong();
321 if (!h.setDpid(dpid, h.channel)) {
322 log.error("Switch presented invalid certificate for dpid {}. Disconnecting",
323 dpid);
324 h.channel.disconnect();
325 return;
326 }
alshabib09d48be2014-10-03 15:43:33 -0700327 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700328 h.getSwitchInfoString(), h.thisdpid);
329
330 h.featuresReply = m; //temp store
331 if (h.ofVersion == OFVersion.OF_10) {
332 h.sendHandshakeSetConfig();
333 h.setState(WAIT_CONFIG_REPLY);
334 } else {
335 //version is 1.3, must get switchport information
336 h.sendHandshakeOFPortDescRequest();
337 h.setState(WAIT_PORT_DESC_REPLY);
338 }
339 }
340 @Override
341 void processOFStatisticsReply(OFChannelHandler h,
342 OFStatsReply m)
343 throws IOException, SwitchStateException {
344 illegalMessageReceived(h, m);
345 }
346 @Override
347 void processOFError(OFChannelHandler h, OFErrorMsg m) {
348 logErrorDisconnect(h, m);
349 }
350
351 @Override
352 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
353 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800354 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700355 }
356 },
357
358 /**
359 * We are waiting for a description of the 1.3 switch ports.
360 * Once received, we send a SetConfig request
361 * Next State is WAIT_CONFIG_REPLY
362 */
363 WAIT_PORT_DESC_REPLY(false) {
364
365 @Override
366 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
367 throws SwitchStateException {
368 // Read port description
369 if (m.getStatsType() != OFStatsType.PORT_DESC) {
370 log.warn("Expecting port description stats but received stats "
371 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700372 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700373 return;
374 }
375 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700376 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700377 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700378 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800379 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700380 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800381 } else {
382 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700383 }
384 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
Saurav Das45f48152018-01-18 12:07:33 -0800385 log.debug("Received port desc reply for switch at {}: {}",
386 h.getSwitchInfoString(),
387 ((OFPortDescStatsReply) m).getEntries());
tom7ef8ff92014-09-17 13:08:06 -0700388 try {
389 h.sendHandshakeSetConfig();
390 } catch (IOException e) {
391 log.error("Unable to send setConfig after PortDescReply. "
392 + "Error: {}", e.getMessage());
393 }
394 h.setState(WAIT_CONFIG_REPLY);
395 }
396
397 @Override
398 void processOFError(OFChannelHandler h, OFErrorMsg m)
399 throws IOException, SwitchStateException {
400 logErrorDisconnect(h, m);
401
402 }
403
404 @Override
405 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
406 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800407 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700408
409 }
410 },
411
412 /**
413 * We are waiting for a config reply message. Once we receive it
414 * we send a DescriptionStatsRequest to the switch.
415 * Next state: WAIT_DESCRIPTION_STAT_REPLY
416 */
417 WAIT_CONFIG_REPLY(false) {
418 @Override
419 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
420 throws IOException {
421 if (m.getMissSendLen() == 0xffff) {
422 log.trace("Config Reply from switch {} confirms "
423 + "miss length set to 0xffff",
424 h.getSwitchInfoString());
425 } else {
426 // FIXME: we can't really deal with switches that don't send
427 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800428 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700429 + "miss length set to {}",
430 h.getSwitchInfoString(),
431 m.getMissSendLen());
432 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100433
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800434 nextState(h);
435 }
436
437 /**
438 * Transition to next state based on OF version.
439 *
440 * @param h current channel handler
441 * @throws IOException
442 */
443 private void nextState(OFChannelHandler h) throws IOException {
444 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100445 // Meters were introduced in OpenFlow 1.3
446 h.sendMeterFeaturesRequest();
447 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800448 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100449 h.sendHandshakeDescriptionStatsRequest();
450 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
451 }
tom7ef8ff92014-09-17 13:08:06 -0700452 }
453
454 @Override
455 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
456 // do nothing;
457 }
458
459 @Override
460 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
461 throws IOException, SwitchStateException {
462 illegalMessageReceived(h, m);
463 }
464 @Override
465 void processOFStatisticsReply(OFChannelHandler h,
466 OFStatsReply m)
467 throws IOException, SwitchStateException {
468 log.error("Received multipart(stats) message sub-type {}",
469 m.getStatsType());
470 illegalMessageReceived(h, m);
471 }
472
473 @Override
474 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800475 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
476 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
477 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
478 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
479 try {
480 nextState(h);
481 return;
482 } catch (IOException e) {
483 log.error("Exception thrown transitioning to next", e);
484 logErrorDisconnect(h, m);
485 }
486 }
487 }
tom7ef8ff92014-09-17 13:08:06 -0700488 logErrorDisconnect(h, m);
489 }
490
491 @Override
492 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
493 throws IOException {
494 h.pendingPortStatusMsg.add(m);
495 }
496 },
497
498
499 /**
500 * We are waiting for a OFDescriptionStat message from the switch.
501 * Once we receive any stat message we try to parse it. If it's not
502 * a description stats message we disconnect. If its the expected
503 * description stats message, we:
504 * - use the switch driver to bind the switch and get an IOFSwitch instance
505 * - setup the IOFSwitch instance
506 * - add switch controller and send the initial role
507 * request to the switch.
508 * Next state: WAIT_INITIAL_ROLE
509 * In the typical case, where switches support role request messages
510 * the next state is where we expect the role reply message.
511 * In the special case that where the switch does not support any kind
512 * of role request messages, we don't send a role message, but we do
513 * request mastership from the registry service. This controller
514 * should become master once we hear back from the registry service.
515 * All following states will have a h.sw instance!
516 */
517 WAIT_DESCRIPTION_STAT_REPLY(false) {
518 @Override
519 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
520 throws SwitchStateException {
521 // Read description, if it has been updated
522 if (m.getStatsType() != OFStatsType.DESC) {
523 log.warn("Expecting Description stats but received stats "
524 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700525 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700526 return;
527 }
tom7ef8ff92014-09-17 13:08:06 -0700528 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700529 log.info("Received switch description reply {} from switch at {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700530 drep, h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700531 // Here is where we differentiate between different kinds of switches
532 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
533
534 h.sw.setOFVersion(h.ofVersion);
535 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700536 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100537 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700538 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700539 h.sw.setChannel(h);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700540// boolean success = h.sw.connectSwitch();
541//
542// if (!success) {
543// disconnectDuplicate(h);
544// return;
545// }
tom7ef8ff92014-09-17 13:08:06 -0700546 // set switch information
547
548
549
alshabib09d48be2014-10-03 15:43:33 -0700550 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800551 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700552 //Put switch in EQUAL mode until we hear back from the global registry
553 //log.debug("Setting new switch {} to EQUAL and sending Role request",
554 // h.sw.getStringId());
555 //h.sw.activateEqualSwitch();
556 //h.setSwitchRole(RoleState.EQUAL);
557
558 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700559 if (h.sw.isDriverHandshakeComplete()) {
pier7f292e72019-07-16 15:52:50 +0200560 // We are not able to complete the connection for a dpid collision.
561 // Same device reconnecting or different device configured with
562 // the same dpid.
alshabib9eab22f2014-10-20 17:17:31 -0700563 if (!h.sw.connectSwitch()) {
pier7f292e72019-07-16 15:52:50 +0200564 // Disconnect from the device and return
alshabib9eab22f2014-10-20 17:17:31 -0700565 disconnectDuplicate(h);
pier7f292e72019-07-16 15:52:50 +0200566 return;
alshabib9eab22f2014-10-20 17:17:31 -0700567 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800568 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700569 h.setState(ACTIVE);
570 } else {
571 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
572 }
tom7ef8ff92014-09-17 13:08:06 -0700573
574 }
575
576 @Override
577 void processOFError(OFChannelHandler h, OFErrorMsg m) {
578 logErrorDisconnect(h, m);
579 }
580
581 @Override
582 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
583 throws IOException, SwitchStateException {
584 illegalMessageReceived(h, m);
585 }
586
587 @Override
588 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
589 throws IOException {
590 h.pendingPortStatusMsg.add(m);
591 }
592 },
593
594
595 /**
596 * We are waiting for the respective switch driver to complete its
597 * configuration. Notice that we do not consider this to be part of the main
598 * switch-controller handshake. But we do consider it as a step that comes
599 * before we declare the switch as available to the controller.
600 * Next State: depends on the role of this controller for this switch - either
601 * MASTER or EQUAL.
602 */
603 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
604
605 @Override
606 void processOFError(OFChannelHandler h, OFErrorMsg m)
607 throws IOException {
608 // will never be called. We override processOFMessage
609 }
610
alshabibd7963912014-10-20 14:52:04 -0700611
612
tom7ef8ff92014-09-17 13:08:06 -0700613 @Override
614 void processOFMessage(OFChannelHandler h, OFMessage m)
615 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700616
617 if (h.sw.isDriverHandshakeComplete()) {
618 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700619 h.state.processOFMessage(h, m);
620 return;
alshabibd7963912014-10-20 14:52:04 -0700621
622 }
623
tom7ef8ff92014-09-17 13:08:06 -0700624 if (m.getType() == OFType.ECHO_REQUEST) {
625 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700626 } else if (m.getType() == OFType.ECHO_REPLY) {
627 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700628 } else if (m.getType() == OFType.ROLE_REPLY) {
629 h.sw.handleRole(m);
630 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800631 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700632 h.sw.processDriverHandshakeMessage(m);
633 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700634 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700635 }
636 }
637 } else {
638 if (m.getType() == OFType.EXPERIMENTER &&
639 ((OFExperimenter) m).getExperimenter() ==
640 RoleManager.NICIRA_EXPERIMENTER) {
641 h.sw.handleNiciraRole(m);
642 } else {
643 h.sw.processDriverHandshakeMessage(m);
644 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700645 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700646 }
647 }
648 }
649 }
650
651 @Override
652 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
653 throws IOException, SwitchStateException {
654 h.pendingPortStatusMsg.add(m);
655 }
alshabibd7963912014-10-20 14:52:04 -0700656
657 private void moveToActive(OFChannelHandler h) {
658 boolean success = h.sw.connectSwitch();
pier7f292e72019-07-16 15:52:50 +0200659 // Disconnect from the device and return
alshabibd7963912014-10-20 14:52:04 -0700660 if (!success) {
661 disconnectDuplicate(h);
pier7f292e72019-07-16 15:52:50 +0200662 return;
alshabibd7963912014-10-20 14:52:04 -0700663 }
pier7f292e72019-07-16 15:52:50 +0200664 handlePendingPortStatusMessages(h);
665 h.setState(ACTIVE);
alshabibd7963912014-10-20 14:52:04 -0700666 }
667
tom7ef8ff92014-09-17 13:08:06 -0700668 },
669
Jordi Ortiz91477b82016-11-29 15:22:50 +0100670 /**
671 * We are expecting a OF Multi Part Meter Features Stats Reply.
672 * Notice that this information is only available for switches running
673 * OpenFlow 1.3
674 */
675 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800676
677 @Override
678 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
679 throws IOException {
680 super.processOFEchoRequest(h, m);
681 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
682 log.info("{} did not respond to MeterFeaturesRequest on time, " +
683 "moving on without it.",
684 h.getSwitchInfoString());
685 h.sendHandshakeDescriptionStatsRequest();
686 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
687 }
688 }
689
Jordi Ortiz91477b82016-11-29 15:22:50 +0100690 @Override
691 void processOFError(OFChannelHandler h, OFErrorMsg m)
692 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800693 // Hardware switches may reply OFErrorMsg if meter is not supported
694 log.warn("Received OFError {}. It seems {} does not support Meter.",
695 m.getErrType().name(), Dpid.uri(h.thisdpid));
696 log.debug("{}", m);
697 h.sendHandshakeDescriptionStatsRequest();
698 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100699 }
700
701 @Override
702 void processOFStatisticsReply(OFChannelHandler h,
703 OFStatsReply m)
704 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800705 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100706 case METER_FEATURES:
707
708 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800709 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100710 log.info("Received meter features from {} with max meters: {}",
711 h.getSwitchInfoString(),
712 ofmfsr.getFeatures().getMaxMeter());
713 h.meterFeaturesReply = ofmfsr;
714 h.sendHandshakeDescriptionStatsRequest();
715 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
716 break;
717 default:
718 log.error("Unexpected OF Multi Part stats reply");
719 illegalMessageReceived(h, m);
720 break;
721 }
722 }
723
724 @Override
725 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
726 throws IOException, SwitchStateException {
727 illegalMessageReceived(h, m);
728 }
729
730 @Override
731 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
732 throws IOException {
733 h.pendingPortStatusMsg.add(m);
734 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800735
736 @Override
737 void processIdle(OFChannelHandler h) throws IOException {
738 log.info("{} did not respond to MeterFeaturesRequest, " +
739 "moving on without it.",
740 h.getSwitchInfoString());
741 h.sendHandshakeDescriptionStatsRequest();
742 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
743 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100744 },
745
tom7ef8ff92014-09-17 13:08:06 -0700746
747 /**
748 * This controller is in MASTER role for this switch. We enter this state
749 * after requesting and winning control from the global registry.
750 * The main handshake as well as the switch-driver sub-handshake
751 * is complete at this point.
752 * // XXX S reconsider below
753 * In the (near) future we may deterministically assign controllers to
754 * switches at startup.
755 * We only leave this state if the switch disconnects or
756 * if we send a role request for SLAVE /and/ receive the role reply for
757 * SLAVE.
758 */
759 ACTIVE(true) {
760 @Override
761 void processOFError(OFChannelHandler h, OFErrorMsg m)
762 throws IOException, SwitchStateException {
763 // if we get here, then the error message is for something else
764 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700765 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700766 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700767 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700768 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700769 // We are the master controller and the switch returned
770 // a permission error. This is a likely indicator that
771 // the switch thinks we are slave. Reassert our
772 // role
773 // FIXME: this could be really bad during role transitions
774 // if two controllers are master (even if its only for
775 // a brief period). We might need to see if these errors
776 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700777
tom7ef8ff92014-09-17 13:08:06 -0700778 h.sw.reassertRole();
779 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
780 ((OFFlowModFailedErrorMsg) m).getCode() ==
781 OFFlowModFailedCode.ALL_TABLES_FULL) {
782 h.sw.setTableFull(true);
783 } else {
784 logError(h, m);
785 }
786 h.dispatchMessage(m);
787 }
788
789 @Override
790 void processOFStatisticsReply(OFChannelHandler h,
791 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700792 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
Saurav Das45f48152018-01-18 12:07:33 -0800793 log.debug("Received port desc message from {}: {}",
794 h.sw.getDpid(),
795 ((OFPortDescStatsReply) m).getEntries());
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700796 h.sw.setPortDescReply((OFPortDescStatsReply) m);
797 }
tom7ef8ff92014-09-17 13:08:06 -0700798 h.dispatchMessage(m);
799 }
800
801 @Override
802 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
803 throws SwitchStateException {
804 h.sw.handleNiciraRole(m);
805 }
806
807 @Override
808 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
809 throws SwitchStateException {
810 h.sw.handleRole(m);
811 }
812
813 @Override
814 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
815 throws SwitchStateException {
816 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800817 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700818 }
819
820 @Override
821 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700822// OFPacketOut out =
823// h.sw.factory().buildPacketOut()
824// .setXid(m.getXid())
825// .setBufferId(m.getBufferId()).build();
826// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700827 h.dispatchMessage(m);
828 }
829
830 @Override
831 void processOFFlowRemoved(OFChannelHandler h,
832 OFFlowRemoved m) {
833 h.dispatchMessage(m);
834 }
835
836 @Override
837 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
838 h.dispatchMessage(m);
839 }
840
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700841 @Override
842 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700843 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700844 h.dispatchMessage(m);
845 }
846
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800847 @Override
848 void processIdle(OFChannelHandler h) throws IOException {
849 log.info("{} idle", h.getSwitchInfoString());
850 }
851
tom7ef8ff92014-09-17 13:08:06 -0700852 };
853
854 private final boolean handshakeComplete;
855 ChannelState(boolean handshakeComplete) {
856 this.handshakeComplete = handshakeComplete;
857 }
858
859 /**
860 * Is this a state in which the handshake has completed?
861 * @return true if the handshake is complete
862 */
863 public boolean isHandshakeComplete() {
864 return handshakeComplete;
865 }
866
867 /**
868 * Get a string specifying the switch connection, state, and
869 * message received. To be used as message for SwitchStateException
870 * or log messages
871 * @param h The channel handler (to get switch information_
872 * @param m The OFMessage that has just been received
873 * @param details A string giving more details about the exact nature
874 * of the problem.
875 * @return display string
876 */
877 // needs to be protected because enum members are actually subclasses
878 protected String getSwitchStateMessage(OFChannelHandler h,
879 OFMessage m,
880 String details) {
881 return String.format("Switch: [%s], State: [%s], received: [%s]"
882 + ", details: %s",
883 h.getSwitchInfoString(),
884 this.toString(),
885 m.getType().toString(),
886 details);
887 }
888
889 /**
890 * We have an OFMessage we didn't expect given the current state and
891 * we want to treat this as an error.
892 * We currently throw an exception that will terminate the connection
893 * However, we could be more forgiving
894 * @param h the channel handler that received the message
895 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700896 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700897 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700898 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700899 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
900 throws SwitchStateException {
901 String msg = getSwitchStateMessage(h, m,
902 "Switch should never send this message in the current state");
903 throw new SwitchStateException(msg);
904
905 }
906
907 /**
908 * We have an OFMessage we didn't expect given the current state and
909 * we want to ignore the message.
910 * @param h the channel handler the received the message
911 * @param m the message
912 */
913 protected void unhandledMessageReceived(OFChannelHandler h,
914 OFMessage m) {
915 if (log.isDebugEnabled()) {
916 String msg = getSwitchStateMessage(h, m,
917 "Ignoring unexpected message");
918 log.debug(msg);
919 }
920 }
921
922 /**
923 * Log an OpenFlow error message from a switch.
924 * @param h The switch that sent the error
925 * @param error The error message
926 */
927 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700928 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700929 error,
930 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800931 this);
tom7ef8ff92014-09-17 13:08:06 -0700932 }
933
934 /**
935 * Log an OpenFlow error message from a switch and disconnect the
936 * channel.
937 *
938 * @param h the IO channel for this switch.
939 * @param error The error message
940 */
941 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
942 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700943 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700944 h.channel.disconnect();
945 }
946
947 /**
948 * log an error message for a duplicate dpid and disconnect this channel.
949 * @param h the IO channel for this switch.
950 */
951 protected void disconnectDuplicate(OFChannelHandler h) {
952 log.error("Duplicated dpid or incompleted cleanup - "
953 + "disconnecting channel {}", h.getSwitchInfoString());
954 h.duplicateDpidFound = Boolean.TRUE;
955 h.channel.disconnect();
956 }
957
958
959
960 /**
961 * Handles all pending port status messages before a switch is declared
962 * activated in MASTER or EQUAL role. Note that since this handling
963 * precedes the activation (and therefore notification to IOFSwitchListerners)
964 * the changes to ports will already be visible once the switch is
965 * activated. As a result, no notifications are sent out for these
966 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700967 *
968 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700969 */
970 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
971 try {
972 handlePendingPortStatusMessages(h, 0);
973 } catch (SwitchStateException e) {
974 log.error(e.getMessage());
975 }
976 }
977
978 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
979 throws SwitchStateException {
980 if (h.sw == null) {
981 String msg = "State machine error: switch is null. Should never " +
982 "happen";
983 throw new SwitchStateException(msg);
984 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800985 log.info("Processing {} pending port status messages for {}",
986 h.pendingPortStatusMsg.size(), h.sw.getStringId());
987
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800988 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700989 for (OFPortStatus ps: h.pendingPortStatusMsg) {
990 temp.add(ps);
991 handlePortStatusMessage(h, ps, false);
992 }
tom7ef8ff92014-09-17 13:08:06 -0700993 // expensive but ok - we don't expect too many port-status messages
994 // note that we cannot use clear(), because of the reasons below
995 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800996 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700997 // the iterator above takes a snapshot of the list - so while we were
998 // dealing with the pending port-status messages, we could have received
999 // newer ones. Handle them recursively, but break the recursion after
1000 // five steps to avoid an attack.
1001 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1002 handlePendingPortStatusMessages(h, index);
1003 }
1004 }
1005
1006 /**
1007 * Handle a port status message.
1008 *
1009 * Handle a port status message by updating the port maps in the
1010 * IOFSwitch instance and notifying Controller about the change so
1011 * it can dispatch a switch update.
1012 *
1013 * @param h The OFChannelHhandler that received the message
1014 * @param m The PortStatus message we received
1015 * @param doNotify if true switch port changed events will be
1016 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001017 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001018 *
1019 */
1020 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1021 boolean doNotify) throws SwitchStateException {
1022 if (h.sw == null) {
1023 String msg = getSwitchStateMessage(h, m,
1024 "State machine error: switch is null. Should never " +
1025 "happen");
1026 throw new SwitchStateException(msg);
1027 }
Saurav Dasbd071d82018-01-09 17:38:44 -08001028 log.info("Received port status message from {}/{}: {}",
1029 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001030
1031 h.sw.handleMessage(m);
1032 }
1033
1034
1035 /**
1036 * Process an OF message received on the channel and
1037 * update state accordingly.
1038 *
1039 * The main "event" of the state machine. Process the received message,
1040 * send follow up message if required and update state if required.
1041 *
1042 * Switches on the message type and calls more specific event handlers
1043 * for each individual OF message type. If we receive a message that
1044 * is supposed to be sent from a controller to a switch we throw
1045 * a SwitchStateExeption.
1046 *
1047 * The more specific handlers can also throw SwitchStateExceptions
1048 *
1049 * @param h The OFChannelHandler that received the message
1050 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001051 * @throws SwitchStateException if the switch is not bound to the channel
1052 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001053 */
1054 void processOFMessage(OFChannelHandler h, OFMessage m)
1055 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001056 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001057 case HELLO:
1058 processOFHello(h, (OFHello) m);
1059 break;
1060 case BARRIER_REPLY:
1061 processOFBarrierReply(h, (OFBarrierReply) m);
1062 break;
1063 case ECHO_REPLY:
1064 processOFEchoReply(h, (OFEchoReply) m);
1065 break;
1066 case ECHO_REQUEST:
1067 processOFEchoRequest(h, (OFEchoRequest) m);
1068 break;
1069 case ERROR:
1070 processOFError(h, (OFErrorMsg) m);
1071 break;
1072 case FEATURES_REPLY:
1073 processOFFeaturesReply(h, (OFFeaturesReply) m);
1074 break;
1075 case FLOW_REMOVED:
1076 processOFFlowRemoved(h, (OFFlowRemoved) m);
1077 break;
1078 case GET_CONFIG_REPLY:
1079 processOFGetConfigReply(h, (OFGetConfigReply) m);
1080 break;
1081 case PACKET_IN:
1082 processOFPacketIn(h, (OFPacketIn) m);
1083 break;
1084 case PORT_STATUS:
1085 processOFPortStatus(h, (OFPortStatus) m);
1086 break;
1087 case QUEUE_GET_CONFIG_REPLY:
1088 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1089 break;
1090 case STATS_REPLY: // multipart_reply in 1.3
1091 processOFStatisticsReply(h, (OFStatsReply) m);
1092 break;
1093 case EXPERIMENTER:
1094 processOFExperimenter(h, (OFExperimenter) m);
1095 break;
1096 case ROLE_REPLY:
1097 processOFRoleReply(h, (OFRoleReply) m);
1098 break;
1099 case GET_ASYNC_REPLY:
1100 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1101 break;
1102
1103 // The following messages are sent to switches. The controller
1104 // should never receive them
1105 case SET_CONFIG:
1106 case GET_CONFIG_REQUEST:
1107 case PACKET_OUT:
1108 case PORT_MOD:
1109 case QUEUE_GET_CONFIG_REQUEST:
1110 case BARRIER_REQUEST:
1111 case STATS_REQUEST: // multipart request in 1.3
1112 case FEATURES_REQUEST:
1113 case FLOW_MOD:
1114 case GROUP_MOD:
1115 case TABLE_MOD:
1116 case GET_ASYNC_REQUEST:
1117 case SET_ASYNC:
1118 case METER_MOD:
1119 default:
1120 illegalMessageReceived(h, m);
1121 break;
1122 }
1123 }
1124
1125 /*-----------------------------------------------------------------
1126 * Default implementation for message handlers in any state.
1127 *
1128 * Individual states must override these if they want a behavior
1129 * that differs from the default.
1130 *
1131 * In general, these handlers simply ignore the message and do
1132 * nothing.
1133 *
1134 * There are some exceptions though, since some messages really
1135 * are handled the same way in every state (e.g., ECHO_REQUST) or
1136 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1137 -----------------------------------------------------------------*/
1138
1139 void processOFHello(OFChannelHandler h, OFHello m)
1140 throws IOException, SwitchStateException {
1141 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001142 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001143 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001144 }
1145
1146 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1147 throws IOException {
1148 // Silently ignore.
1149 }
1150
1151 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1152 throws IOException {
1153 if (h.ofVersion == null) {
1154 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001155 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001156 return;
1157 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001158 OFEchoReply reply = h.factory
1159 .buildEchoReply()
1160 .setXid(m.getXid())
1161 .setData(m.getData())
1162 .build();
1163 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001164 }
1165
1166 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1167 throws IOException {
1168 // Do nothing with EchoReplies !!
1169 }
1170
1171 // no default implementation for OFError
1172 // every state must override it
1173 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1174 throws IOException, SwitchStateException;
1175
1176
1177 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1178 throws IOException, SwitchStateException {
1179 unhandledMessageReceived(h, m);
1180 }
1181
1182 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1183 throws IOException {
1184 unhandledMessageReceived(h, m);
1185 }
1186
1187 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1188 throws IOException, SwitchStateException {
1189 // we only expect config replies in the WAIT_CONFIG_REPLY state
1190 illegalMessageReceived(h, m);
1191 }
1192
1193 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1194 throws IOException {
1195 unhandledMessageReceived(h, m);
1196 }
1197
1198 // no default implementation. Every state needs to handle it.
1199 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1200 throws IOException, SwitchStateException;
1201
1202 void processOFQueueGetConfigReply(OFChannelHandler h,
1203 OFQueueGetConfigReply m)
1204 throws IOException {
1205 unhandledMessageReceived(h, m);
1206 }
1207
1208 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1209 throws IOException, SwitchStateException {
1210 unhandledMessageReceived(h, m);
1211 }
1212
1213 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1214 throws IOException, SwitchStateException {
1215 // TODO: it might make sense to parse the vendor message here
1216 // into the known vendor messages we support and then call more
1217 // specific event handlers
1218 unhandledMessageReceived(h, m);
1219 }
1220
1221 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1222 throws SwitchStateException, IOException {
1223 unhandledMessageReceived(h, m);
1224 }
1225
1226 void processOFGetAsyncReply(OFChannelHandler h,
1227 OFAsyncGetReply m) {
1228 unhandledMessageReceived(h, m);
1229 }
1230
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001231 void processIdle(OFChannelHandler h) throws IOException {
1232 // disconnect channel which did no complete handshake
1233 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1234 h.channel.disconnect();
1235 }
tom7ef8ff92014-09-17 13:08:06 -07001236 }
1237
1238
1239
1240 //*************************
1241 // Channel handler methods
1242 //*************************
1243
1244 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001245 public void channelActive(ChannelHandlerContext ctx)
1246 throws Exception {
1247
1248 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001249 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001250 channel.remoteAddress());
1251
1252 SocketAddress address = channel.remoteAddress();
1253 if (address instanceof InetSocketAddress) {
1254 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1255 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1256 if (ipAddress.isIp4()) {
1257 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1258 } else {
1259 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1260 }
1261 } else {
1262 channelId = channel.toString();
1263 }
1264
1265 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1266
alshabib70fc7fb2015-01-06 11:04:29 -08001267 /*
1268 hack to wait for the switch to tell us what it's
1269 max version is. This is not spec compliant and should
1270 be removed as soon as switches behave better.
1271 */
1272 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001273 setState(ChannelState.WAIT_HELLO);
1274 }
1275
1276 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001277 public void channelInactive(ChannelHandlerContext ctx)
1278 throws Exception {
1279
tom7ef8ff92014-09-17 13:08:06 -07001280 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001281 getSwitchInfoString());
1282
1283 if (dispatcher != null) {
Harold Huang828cd592017-11-04 10:46:04 +08001284 dispatcher.shutdownNow();
Thomas Vachuskad75684a2018-01-03 09:04:47 -08001285 dispatcher = null;
tom7ef8ff92014-09-17 13:08:06 -07001286 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001287
1288 if (thisdpid != 0) {
1289 if (!duplicateDpidFound) {
1290 // if the disconnected switch (on this ChannelHandler)
1291 // was not one with a duplicate-dpid, it is safe to remove all
1292 // state for it at the controller. Notice that if the disconnected
1293 // switch was a duplicate-dpid, calling the method below would clear
1294 // all state for the original switch (with the same dpid),
1295 // which we obviously don't want.
1296 log.info("{}:removal called", getSwitchInfoString());
1297 if (sw != null) {
1298 sw.removeConnectedSwitch();
1299 }
1300 } else {
1301 // A duplicate was disconnected on this ChannelHandler,
1302 // this is the same switch reconnecting, but the original state was
1303 // not cleaned up - XXX check liveness of original ChannelHandler
1304 log.info("{}:duplicate found", getSwitchInfoString());
1305 duplicateDpidFound = Boolean.FALSE;
1306 }
1307 } else {
1308 log.warn("no dpid in channelHandler registered for "
1309 + "disconnected switch {}", getSwitchInfoString());
1310 }
tom7ef8ff92014-09-17 13:08:06 -07001311 }
1312
1313 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001314 public void exceptionCaught(ChannelHandlerContext ctx,
1315 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001316 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001317
1318 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001319 // switch timeout
1320 log.error("Disconnecting switch {} due to read timeout",
1321 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001322 ctx.channel().close();
1323 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001324 log.error("Disconnecting switch {}: failed to complete handshake",
1325 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001326 ctx.channel().close();
1327 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001328 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001329 } else if (cause instanceof IOException) {
1330 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1331 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001332 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001333 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001334 if (log.isDebugEnabled()) {
1335 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001336 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001337 }
tom7ef8ff92014-09-17 13:08:06 -07001338 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001339 ctx.channel().close();
1340 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001341 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001342 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001343 if (log.isDebugEnabled()) {
1344 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001345 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001346 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001347 ctx.channel().close();
1348 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001349 log.error("Disconnecting switch "
1350 + getSwitchInfoString() +
1351 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001352 cause);
1353 ctx.channel().close();
1354 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001355 log.warn("Could not process message: queue full");
1356 } else {
1357 log.error("Error while processing message from switch "
1358 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001359 + "state " + this.state, cause);
1360 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001361 }
1362 }
1363
1364 @Override
1365 public String toString() {
1366 return getSwitchInfoString();
1367 }
1368
Ray Milkey986a47a2018-01-25 11:38:51 -08001369 private void channelIdle(ChannelHandlerContext ctx,
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001370 IdleStateEvent e)
Ray Milkey986a47a2018-01-25 11:38:51 -08001371 throws IOException {
Charles Chan982d3902018-03-21 14:58:53 -07001372 // Factory can be null if the channel goes idle during initial handshake. Since the switch
1373 // is not even initialized properly, we just skip this and disconnect the channel.
1374 if (factory != null) {
1375 OFMessage m = factory.buildEchoRequest().build();
1376 log.debug("Sending Echo Request on idle channel: {}", ctx.channel());
1377 ctx.write(Collections.singletonList(m), ctx.voidPromise());
1378 // XXX S some problems here -- echo request has no transaction id, and
1379 // echo reply is not correlated to the echo request.
1380 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001381 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001382 }
1383
1384 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001385 public void userEventTriggered(ChannelHandlerContext ctx,
1386 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001387 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001388
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001389 if (evt instanceof IdleStateEvent) {
1390 channelIdle(ctx, (IdleStateEvent) evt);
1391 }
tom7ef8ff92014-09-17 13:08:06 -07001392
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001393 super.userEventTriggered(ctx, evt);
1394 }
1395
1396 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1397 @Override
1398 public void channelRead(ChannelHandlerContext ctx,
1399 Object msg) throws Exception {
1400
1401 boolean release = true;
1402 try {
1403 if (msg instanceof OFMessage) {
1404 // channelRead0 inlined
1405 state.processOFMessage(this, (OFMessage) msg);
1406 } else {
1407 release = false;
1408 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001409 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001410 } finally {
1411 if (release) {
1412 ReferenceCountUtil.release(msg);
1413 }
tom7ef8ff92014-09-17 13:08:06 -07001414 }
1415 }
1416
1417
1418
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001419
tom7ef8ff92014-09-17 13:08:06 -07001420 //*************************
1421 // Channel utility methods
1422 //*************************
1423
1424 /**
1425 * Is this a state in which the handshake has completed?
1426 * @return true if the handshake is complete
1427 */
1428 public boolean isHandshakeComplete() {
1429 return this.state.isHandshakeComplete();
1430 }
1431
1432 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001433
1434 if (dispatchBacklog.isEmpty()) {
1435 if (!dispatchQueue.offer(m)) {
1436 // queue full
1437 channel.config().setAutoRead(false);
1438 // put it on the head of backlog
1439 dispatchBacklog.addFirst(m);
1440 return;
1441 }
1442 } else {
1443 dispatchBacklog.addLast(m);
1444 }
1445
1446 while (!dispatchBacklog.isEmpty()) {
1447 OFMessage msg = dispatchBacklog.pop();
1448
1449 if (!dispatchQueue.offer(msg)) {
1450 // queue full
1451 channel.config().setAutoRead(false);
1452 // put it back to the head of backlog
1453 dispatchBacklog.addFirst(msg);
1454 return;
1455 }
1456 }
1457
1458
1459 if (dispatcherHandle.isDone()) {
1460 // dispatcher terminated for some reason, restart
1461
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -08001462 dispatcherHandle = dispatcher.submit((Runnable) () -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001463 try {
1464 List<OFMessage> msgs = new ArrayList<>();
1465 for (;;) {
1466 // wait for new message
1467 OFMessage msg = dispatchQueue.take();
1468 sw.handleMessage(msg);
1469
1470 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1471 if (!channel.config().isAutoRead()) {
1472 channel.config().setAutoRead(true);
1473 }
1474 msgs.forEach(sw::handleMessage);
1475 msgs.clear();
1476 }
1477
1478 if (!channel.config().isAutoRead()) {
1479 channel.config().setAutoRead(true);
1480 }
1481 }
1482 } catch (InterruptedException e) {
1483 Thread.currentThread().interrupt();
1484 // interrupted. gracefully shutting down
1485 return;
1486 }
1487
1488 });
1489 }
tom7ef8ff92014-09-17 13:08:06 -07001490 }
1491
1492 /**
1493 * Return a string describing this switch based on the already available
1494 * information (DPID and/or remote socket).
1495 * @return display string
1496 */
1497 private String getSwitchInfoString() {
1498 if (sw != null) {
1499 return sw.toString();
1500 }
1501 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001502 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001503 channelString = "?";
1504 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001505 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001506 }
1507 String dpidString;
1508 if (featuresReply == null) {
1509 dpidString = "?";
1510 } else {
1511 dpidString = featuresReply.getDatapathId().toString();
1512 }
1513 return String.format("[%s DPID[%s]]", channelString, dpidString);
1514 }
1515
1516 /**
1517 * Update the channels state. Only called from the state machine.
1518 * TODO: enforce restricted state transitions
pier7f292e72019-07-16 15:52:50 +02001519 * @param state new state
tom7ef8ff92014-09-17 13:08:06 -07001520 */
pier7f292e72019-07-16 15:52:50 +02001521 void setState(ChannelState state) {
tom7ef8ff92014-09-17 13:08:06 -07001522 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001523 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001524 }
1525
Brian O'Connor47a16092018-05-10 02:25:09 -07001526 private boolean setDpid(Long dpid, Channel channel) {
1527 ChannelHandlerContext sslContext = channel.pipeline().context(SslHandler.class);
1528 if (sslContext != null) {
1529 try {
1530 SslHandler sslHandler = (SslHandler) sslContext.handler();
1531 Certificate[] certs = sslHandler.engine().getSession().getPeerCertificates();
1532 Certificate cert = certs.length > 0 ? certs[0] : null;
1533 if (!controller.isValidCertificate(dpid, cert)) {
1534 return false;
1535 }
1536 } catch (SSLPeerUnverifiedException e) {
1537 log.info("Switch with dpid {} is an unverified SSL peer.", dpid, e);
1538 return false;
1539 }
1540 }
1541 this.thisdpid = dpid;
1542 return true;
1543 }
1544
tom7ef8ff92014-09-17 13:08:06 -07001545 /**
1546 * Send hello message to the switch using the handshake transactions ids.
1547 * @throws IOException
1548 */
1549 private void sendHandshakeHelloMessage() throws IOException {
1550 // The OF protocol requires us to start things off by sending the highest
1551 // version of the protocol supported.
1552
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001553 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001554 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001555 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1556 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001557 (0b1 << OFVersion.OF_14.getWireVersion()) |
1558 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001559 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1560 OFHelloElem hem = OFFactories.getFactory(version)
1561 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001562 .setBitmaps(Collections.singletonList(bitmap))
1563 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001564 OFMessage.Builder mb = OFFactories.getFactory(version)
1565 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001566 .setXid(this.handshakeTransactionIds--)
1567 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001568 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1569 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001570 }
1571
1572 /**
1573 * Send featuresRequest msg to the switch using the handshake transactions ids.
1574 * @throws IOException
1575 */
1576 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001577 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001578 OFMessage m = factory.buildFeaturesRequest()
1579 .setXid(this.handshakeTransactionIds--)
1580 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001581 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001582 }
1583
1584 /**
1585 * Send the configuration requests to tell the switch we want full
1586 * packets.
1587 * @throws IOException
1588 */
1589 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001590 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001591 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001592
1593 // Ensure we receive the full packet via PacketIn
1594 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001595 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001596 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001597 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001598 OFSetConfig sc = factory
1599 .buildSetConfig()
1600 .setMissSendLen((short) 0xffff)
1601 .setXid(this.handshakeTransactionIds--)
1602 .build();
1603 msglist.add(sc);
1604 }
tom7ef8ff92014-09-17 13:08:06 -07001605
1606 // Barrier
1607 OFBarrierRequest br = factory
1608 .buildBarrierRequest()
1609 .setXid(this.handshakeTransactionIds--)
1610 .build();
1611 msglist.add(br);
1612
1613 // Verify (need barrier?)
1614 OFGetConfigRequest gcr = factory
1615 .buildGetConfigRequest()
1616 .setXid(this.handshakeTransactionIds--)
1617 .build();
1618 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001619 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001620 }
1621
1622 /**
1623 * send a description state request.
1624 * @throws IOException
1625 */
1626 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1627 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001628 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001629 OFDescStatsRequest dreq = factory
1630 .buildDescStatsRequest()
1631 .setXid(handshakeTransactionIds--)
1632 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001633 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001634 }
1635
Jordi Ortiz91477b82016-11-29 15:22:50 +01001636 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001637 * send a meter features request.
1638 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001639 * @throws IOException
1640 */
1641 private void sendMeterFeaturesRequest() throws IOException {
1642 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001643 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001644 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001645 OFMeterFeaturesStatsRequest mfreq = factory
1646 .buildMeterFeaturesStatsRequest()
1647 .setXid(handshakeTransactionIds--)
1648 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001649 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001650 }
1651
tom7ef8ff92014-09-17 13:08:06 -07001652 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001653 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001654 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001655 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001656 .buildPortDescStatsRequest()
1657 .setXid(handshakeTransactionIds--)
1658 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001659 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001660 }
1661
1662 ChannelState getStateForTesting() {
1663 return state;
1664 }
1665
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001666
1667 @Override
1668 public boolean isActive() {
1669 if (channel != null) {
1670 return channel.isActive();
1671 }
1672 return false;
1673 }
1674
1675 @Override
1676 public void closeSession() {
1677 if (channel != null) {
1678 channel.close();
1679 }
1680 }
1681
1682 @Override
1683 public boolean sendMsg(Iterable<OFMessage> msgs) {
1684 if (channel.isActive()) {
Laszlo Pappb68fe7e2017-11-24 17:06:59 +00001685 if (log.isTraceEnabled()) {
1686 log.trace("Sending messages for switch {} via openflow channel: {}", getSwitchInfoString(), msgs);
1687 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001688 channel.writeAndFlush(msgs, channel.voidPromise());
1689 return true;
1690 } else {
1691 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1692 getSwitchInfoString(), msgs);
1693 return false;
1694 }
1695 }
1696
1697 @Override
1698 public CharSequence sessionInfo() {
1699 return channelId;
1700 }
1701
tom7ef8ff92014-09-17 13:08:06 -07001702}