blob: 264629e574028dca901081403088a0016aa8306a [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'Connorf69e3e32018-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'Connorf69e3e32018-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'Connorf69e3e32018-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'Connorf69e3e32018-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'Connorf69e3e32018-05-10 02:25:09 -0700318 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
tom7ef8ff92014-09-17 13:08:06 -0700319 throws IOException {
Brian O'Connorf69e3e32018-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
Ray Milkey31b00482019-02-07 08:06:28 -0800534 if (h.sw == null) {
535 log.info("Switch not found for {}", h.thisdpid);
536 return;
537 }
538
tom7ef8ff92014-09-17 13:08:06 -0700539 h.sw.setOFVersion(h.ofVersion);
540 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700541 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100542 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700543 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700544 h.sw.setChannel(h);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700545// boolean success = h.sw.connectSwitch();
546//
547// if (!success) {
548// disconnectDuplicate(h);
549// return;
550// }
tom7ef8ff92014-09-17 13:08:06 -0700551 // set switch information
552
553
554
alshabib09d48be2014-10-03 15:43:33 -0700555 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800556 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700557 //Put switch in EQUAL mode until we hear back from the global registry
558 //log.debug("Setting new switch {} to EQUAL and sending Role request",
559 // h.sw.getStringId());
560 //h.sw.activateEqualSwitch();
561 //h.setSwitchRole(RoleState.EQUAL);
562
563 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700564 if (h.sw.isDriverHandshakeComplete()) {
565 if (!h.sw.connectSwitch()) {
566 disconnectDuplicate(h);
567 }
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();
Thomas Vachuska39274462014-12-02 13:23:50 -0800659 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700660 h.setState(ACTIVE);
661 if (!success) {
662 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700663 }
664 }
665
tom7ef8ff92014-09-17 13:08:06 -0700666 },
667
Jordi Ortiz91477b82016-11-29 15:22:50 +0100668 /**
669 * We are expecting a OF Multi Part Meter Features Stats Reply.
670 * Notice that this information is only available for switches running
671 * OpenFlow 1.3
672 */
673 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800674
675 @Override
676 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
677 throws IOException {
678 super.processOFEchoRequest(h, m);
679 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
680 log.info("{} did not respond to MeterFeaturesRequest on time, " +
681 "moving on without it.",
682 h.getSwitchInfoString());
683 h.sendHandshakeDescriptionStatsRequest();
684 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
685 }
686 }
687
Jordi Ortiz91477b82016-11-29 15:22:50 +0100688 @Override
689 void processOFError(OFChannelHandler h, OFErrorMsg m)
690 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800691 // Hardware switches may reply OFErrorMsg if meter is not supported
692 log.warn("Received OFError {}. It seems {} does not support Meter.",
693 m.getErrType().name(), Dpid.uri(h.thisdpid));
694 log.debug("{}", m);
695 h.sendHandshakeDescriptionStatsRequest();
696 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100697 }
698
699 @Override
700 void processOFStatisticsReply(OFChannelHandler h,
701 OFStatsReply m)
702 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800703 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100704 case METER_FEATURES:
705
706 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800707 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100708 log.info("Received meter features from {} with max meters: {}",
709 h.getSwitchInfoString(),
710 ofmfsr.getFeatures().getMaxMeter());
711 h.meterFeaturesReply = ofmfsr;
712 h.sendHandshakeDescriptionStatsRequest();
713 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
714 break;
715 default:
716 log.error("Unexpected OF Multi Part stats reply");
717 illegalMessageReceived(h, m);
718 break;
719 }
720 }
721
722 @Override
723 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
724 throws IOException, SwitchStateException {
725 illegalMessageReceived(h, m);
726 }
727
728 @Override
729 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
730 throws IOException {
731 h.pendingPortStatusMsg.add(m);
732 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800733
734 @Override
735 void processIdle(OFChannelHandler h) throws IOException {
736 log.info("{} did not respond to MeterFeaturesRequest, " +
737 "moving on without it.",
738 h.getSwitchInfoString());
739 h.sendHandshakeDescriptionStatsRequest();
740 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
741 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100742 },
743
tom7ef8ff92014-09-17 13:08:06 -0700744
745 /**
746 * This controller is in MASTER role for this switch. We enter this state
747 * after requesting and winning control from the global registry.
748 * The main handshake as well as the switch-driver sub-handshake
749 * is complete at this point.
750 * // XXX S reconsider below
751 * In the (near) future we may deterministically assign controllers to
752 * switches at startup.
753 * We only leave this state if the switch disconnects or
754 * if we send a role request for SLAVE /and/ receive the role reply for
755 * SLAVE.
756 */
757 ACTIVE(true) {
758 @Override
759 void processOFError(OFChannelHandler h, OFErrorMsg m)
760 throws IOException, SwitchStateException {
761 // if we get here, then the error message is for something else
762 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700763 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700764 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700765 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700766 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700767 // We are the master controller and the switch returned
768 // a permission error. This is a likely indicator that
769 // the switch thinks we are slave. Reassert our
770 // role
771 // FIXME: this could be really bad during role transitions
772 // if two controllers are master (even if its only for
773 // a brief period). We might need to see if these errors
774 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700775
tom7ef8ff92014-09-17 13:08:06 -0700776 h.sw.reassertRole();
777 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
778 ((OFFlowModFailedErrorMsg) m).getCode() ==
779 OFFlowModFailedCode.ALL_TABLES_FULL) {
780 h.sw.setTableFull(true);
781 } else {
782 logError(h, m);
783 }
784 h.dispatchMessage(m);
785 }
786
787 @Override
788 void processOFStatisticsReply(OFChannelHandler h,
789 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700790 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
Saurav Das45f48152018-01-18 12:07:33 -0800791 log.debug("Received port desc message from {}: {}",
792 h.sw.getDpid(),
793 ((OFPortDescStatsReply) m).getEntries());
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700794 h.sw.setPortDescReply((OFPortDescStatsReply) m);
795 }
tom7ef8ff92014-09-17 13:08:06 -0700796 h.dispatchMessage(m);
797 }
798
799 @Override
800 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
801 throws SwitchStateException {
802 h.sw.handleNiciraRole(m);
803 }
804
805 @Override
806 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
807 throws SwitchStateException {
808 h.sw.handleRole(m);
809 }
810
811 @Override
812 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
813 throws SwitchStateException {
814 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800815 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700816 }
817
818 @Override
819 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700820// OFPacketOut out =
821// h.sw.factory().buildPacketOut()
822// .setXid(m.getXid())
823// .setBufferId(m.getBufferId()).build();
824// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700825 h.dispatchMessage(m);
826 }
827
828 @Override
829 void processOFFlowRemoved(OFChannelHandler h,
830 OFFlowRemoved m) {
831 h.dispatchMessage(m);
832 }
833
834 @Override
835 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
836 h.dispatchMessage(m);
837 }
838
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700839 @Override
840 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700841 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700842 h.dispatchMessage(m);
843 }
844
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800845 @Override
846 void processIdle(OFChannelHandler h) throws IOException {
847 log.info("{} idle", h.getSwitchInfoString());
848 }
849
tom7ef8ff92014-09-17 13:08:06 -0700850 };
851
852 private final boolean handshakeComplete;
853 ChannelState(boolean handshakeComplete) {
854 this.handshakeComplete = handshakeComplete;
855 }
856
857 /**
858 * Is this a state in which the handshake has completed?
859 * @return true if the handshake is complete
860 */
861 public boolean isHandshakeComplete() {
862 return handshakeComplete;
863 }
864
865 /**
866 * Get a string specifying the switch connection, state, and
867 * message received. To be used as message for SwitchStateException
868 * or log messages
869 * @param h The channel handler (to get switch information_
870 * @param m The OFMessage that has just been received
871 * @param details A string giving more details about the exact nature
872 * of the problem.
873 * @return display string
874 */
875 // needs to be protected because enum members are actually subclasses
876 protected String getSwitchStateMessage(OFChannelHandler h,
877 OFMessage m,
878 String details) {
879 return String.format("Switch: [%s], State: [%s], received: [%s]"
880 + ", details: %s",
881 h.getSwitchInfoString(),
882 this.toString(),
883 m.getType().toString(),
884 details);
885 }
886
887 /**
888 * We have an OFMessage we didn't expect given the current state and
889 * we want to treat this as an error.
890 * We currently throw an exception that will terminate the connection
891 * However, we could be more forgiving
892 * @param h the channel handler that received the message
893 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700894 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700895 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700896 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700897 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
898 throws SwitchStateException {
899 String msg = getSwitchStateMessage(h, m,
900 "Switch should never send this message in the current state");
901 throw new SwitchStateException(msg);
902
903 }
904
905 /**
906 * We have an OFMessage we didn't expect given the current state and
907 * we want to ignore the message.
908 * @param h the channel handler the received the message
909 * @param m the message
910 */
911 protected void unhandledMessageReceived(OFChannelHandler h,
912 OFMessage m) {
913 if (log.isDebugEnabled()) {
914 String msg = getSwitchStateMessage(h, m,
915 "Ignoring unexpected message");
916 log.debug(msg);
917 }
918 }
919
920 /**
921 * Log an OpenFlow error message from a switch.
922 * @param h The switch that sent the error
923 * @param error The error message
924 */
925 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700926 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700927 error,
928 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800929 this);
tom7ef8ff92014-09-17 13:08:06 -0700930 }
931
932 /**
933 * Log an OpenFlow error message from a switch and disconnect the
934 * channel.
935 *
936 * @param h the IO channel for this switch.
937 * @param error The error message
938 */
939 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
940 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700941 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700942 h.channel.disconnect();
943 }
944
945 /**
946 * log an error message for a duplicate dpid and disconnect this channel.
947 * @param h the IO channel for this switch.
948 */
949 protected void disconnectDuplicate(OFChannelHandler h) {
950 log.error("Duplicated dpid or incompleted cleanup - "
951 + "disconnecting channel {}", h.getSwitchInfoString());
952 h.duplicateDpidFound = Boolean.TRUE;
953 h.channel.disconnect();
954 }
955
956
957
958 /**
959 * Handles all pending port status messages before a switch is declared
960 * activated in MASTER or EQUAL role. Note that since this handling
961 * precedes the activation (and therefore notification to IOFSwitchListerners)
962 * the changes to ports will already be visible once the switch is
963 * activated. As a result, no notifications are sent out for these
964 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700965 *
966 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700967 */
968 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
969 try {
970 handlePendingPortStatusMessages(h, 0);
971 } catch (SwitchStateException e) {
972 log.error(e.getMessage());
973 }
974 }
975
976 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
977 throws SwitchStateException {
978 if (h.sw == null) {
979 String msg = "State machine error: switch is null. Should never " +
980 "happen";
981 throw new SwitchStateException(msg);
982 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800983 log.info("Processing {} pending port status messages for {}",
984 h.pendingPortStatusMsg.size(), h.sw.getStringId());
985
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800986 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700987 for (OFPortStatus ps: h.pendingPortStatusMsg) {
988 temp.add(ps);
989 handlePortStatusMessage(h, ps, false);
990 }
tom7ef8ff92014-09-17 13:08:06 -0700991 // expensive but ok - we don't expect too many port-status messages
992 // note that we cannot use clear(), because of the reasons below
993 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800994 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700995 // the iterator above takes a snapshot of the list - so while we were
996 // dealing with the pending port-status messages, we could have received
997 // newer ones. Handle them recursively, but break the recursion after
998 // five steps to avoid an attack.
999 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1000 handlePendingPortStatusMessages(h, index);
1001 }
1002 }
1003
1004 /**
1005 * Handle a port status message.
1006 *
1007 * Handle a port status message by updating the port maps in the
1008 * IOFSwitch instance and notifying Controller about the change so
1009 * it can dispatch a switch update.
1010 *
1011 * @param h The OFChannelHhandler that received the message
1012 * @param m The PortStatus message we received
1013 * @param doNotify if true switch port changed events will be
1014 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001015 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001016 *
1017 */
1018 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1019 boolean doNotify) throws SwitchStateException {
1020 if (h.sw == null) {
1021 String msg = getSwitchStateMessage(h, m,
1022 "State machine error: switch is null. Should never " +
1023 "happen");
1024 throw new SwitchStateException(msg);
1025 }
Saurav Dasbd071d82018-01-09 17:38:44 -08001026 log.info("Received port status message from {}/{}: {}",
1027 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001028
1029 h.sw.handleMessage(m);
1030 }
1031
1032
1033 /**
1034 * Process an OF message received on the channel and
1035 * update state accordingly.
1036 *
1037 * The main "event" of the state machine. Process the received message,
1038 * send follow up message if required and update state if required.
1039 *
1040 * Switches on the message type and calls more specific event handlers
1041 * for each individual OF message type. If we receive a message that
1042 * is supposed to be sent from a controller to a switch we throw
1043 * a SwitchStateExeption.
1044 *
1045 * The more specific handlers can also throw SwitchStateExceptions
1046 *
1047 * @param h The OFChannelHandler that received the message
1048 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001049 * @throws SwitchStateException if the switch is not bound to the channel
1050 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001051 */
1052 void processOFMessage(OFChannelHandler h, OFMessage m)
1053 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001054 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001055 case HELLO:
1056 processOFHello(h, (OFHello) m);
1057 break;
1058 case BARRIER_REPLY:
1059 processOFBarrierReply(h, (OFBarrierReply) m);
1060 break;
1061 case ECHO_REPLY:
1062 processOFEchoReply(h, (OFEchoReply) m);
1063 break;
1064 case ECHO_REQUEST:
1065 processOFEchoRequest(h, (OFEchoRequest) m);
1066 break;
1067 case ERROR:
1068 processOFError(h, (OFErrorMsg) m);
1069 break;
1070 case FEATURES_REPLY:
1071 processOFFeaturesReply(h, (OFFeaturesReply) m);
1072 break;
1073 case FLOW_REMOVED:
1074 processOFFlowRemoved(h, (OFFlowRemoved) m);
1075 break;
1076 case GET_CONFIG_REPLY:
1077 processOFGetConfigReply(h, (OFGetConfigReply) m);
1078 break;
1079 case PACKET_IN:
1080 processOFPacketIn(h, (OFPacketIn) m);
1081 break;
1082 case PORT_STATUS:
1083 processOFPortStatus(h, (OFPortStatus) m);
1084 break;
1085 case QUEUE_GET_CONFIG_REPLY:
1086 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1087 break;
1088 case STATS_REPLY: // multipart_reply in 1.3
1089 processOFStatisticsReply(h, (OFStatsReply) m);
1090 break;
1091 case EXPERIMENTER:
1092 processOFExperimenter(h, (OFExperimenter) m);
1093 break;
1094 case ROLE_REPLY:
1095 processOFRoleReply(h, (OFRoleReply) m);
1096 break;
1097 case GET_ASYNC_REPLY:
1098 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1099 break;
1100
1101 // The following messages are sent to switches. The controller
1102 // should never receive them
1103 case SET_CONFIG:
1104 case GET_CONFIG_REQUEST:
1105 case PACKET_OUT:
1106 case PORT_MOD:
1107 case QUEUE_GET_CONFIG_REQUEST:
1108 case BARRIER_REQUEST:
1109 case STATS_REQUEST: // multipart request in 1.3
1110 case FEATURES_REQUEST:
1111 case FLOW_MOD:
1112 case GROUP_MOD:
1113 case TABLE_MOD:
1114 case GET_ASYNC_REQUEST:
1115 case SET_ASYNC:
1116 case METER_MOD:
1117 default:
1118 illegalMessageReceived(h, m);
1119 break;
1120 }
1121 }
1122
1123 /*-----------------------------------------------------------------
1124 * Default implementation for message handlers in any state.
1125 *
1126 * Individual states must override these if they want a behavior
1127 * that differs from the default.
1128 *
1129 * In general, these handlers simply ignore the message and do
1130 * nothing.
1131 *
1132 * There are some exceptions though, since some messages really
1133 * are handled the same way in every state (e.g., ECHO_REQUST) or
1134 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1135 -----------------------------------------------------------------*/
1136
1137 void processOFHello(OFChannelHandler h, OFHello m)
1138 throws IOException, SwitchStateException {
1139 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001140 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001141 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001142 }
1143
1144 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1145 throws IOException {
1146 // Silently ignore.
1147 }
1148
1149 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1150 throws IOException {
1151 if (h.ofVersion == null) {
1152 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001153 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001154 return;
1155 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001156 OFEchoReply reply = h.factory
1157 .buildEchoReply()
1158 .setXid(m.getXid())
1159 .setData(m.getData())
1160 .build();
1161 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001162 }
1163
1164 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1165 throws IOException {
1166 // Do nothing with EchoReplies !!
1167 }
1168
1169 // no default implementation for OFError
1170 // every state must override it
1171 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1172 throws IOException, SwitchStateException;
1173
1174
1175 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1176 throws IOException, SwitchStateException {
1177 unhandledMessageReceived(h, m);
1178 }
1179
1180 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1181 throws IOException {
1182 unhandledMessageReceived(h, m);
1183 }
1184
1185 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1186 throws IOException, SwitchStateException {
1187 // we only expect config replies in the WAIT_CONFIG_REPLY state
1188 illegalMessageReceived(h, m);
1189 }
1190
1191 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1192 throws IOException {
1193 unhandledMessageReceived(h, m);
1194 }
1195
1196 // no default implementation. Every state needs to handle it.
1197 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1198 throws IOException, SwitchStateException;
1199
1200 void processOFQueueGetConfigReply(OFChannelHandler h,
1201 OFQueueGetConfigReply m)
1202 throws IOException {
1203 unhandledMessageReceived(h, m);
1204 }
1205
1206 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1207 throws IOException, SwitchStateException {
1208 unhandledMessageReceived(h, m);
1209 }
1210
1211 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1212 throws IOException, SwitchStateException {
1213 // TODO: it might make sense to parse the vendor message here
1214 // into the known vendor messages we support and then call more
1215 // specific event handlers
1216 unhandledMessageReceived(h, m);
1217 }
1218
1219 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1220 throws SwitchStateException, IOException {
1221 unhandledMessageReceived(h, m);
1222 }
1223
1224 void processOFGetAsyncReply(OFChannelHandler h,
1225 OFAsyncGetReply m) {
1226 unhandledMessageReceived(h, m);
1227 }
1228
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001229 void processIdle(OFChannelHandler h) throws IOException {
1230 // disconnect channel which did no complete handshake
1231 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1232 h.channel.disconnect();
1233 }
tom7ef8ff92014-09-17 13:08:06 -07001234 }
1235
1236
1237
1238 //*************************
1239 // Channel handler methods
1240 //*************************
1241
1242 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001243 public void channelActive(ChannelHandlerContext ctx)
1244 throws Exception {
1245
1246 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001247 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001248 channel.remoteAddress());
1249
1250 SocketAddress address = channel.remoteAddress();
1251 if (address instanceof InetSocketAddress) {
1252 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1253 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1254 if (ipAddress.isIp4()) {
1255 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1256 } else {
1257 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1258 }
1259 } else {
1260 channelId = channel.toString();
1261 }
1262
1263 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1264
alshabib70fc7fb2015-01-06 11:04:29 -08001265 /*
1266 hack to wait for the switch to tell us what it's
1267 max version is. This is not spec compliant and should
1268 be removed as soon as switches behave better.
1269 */
1270 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001271 setState(ChannelState.WAIT_HELLO);
1272 }
1273
1274 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001275 public void channelInactive(ChannelHandlerContext ctx)
1276 throws Exception {
1277
tom7ef8ff92014-09-17 13:08:06 -07001278 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001279 getSwitchInfoString());
1280
1281 if (dispatcher != null) {
Harold Huang828cd592017-11-04 10:46:04 +08001282 dispatcher.shutdownNow();
Thomas Vachuskad75684a2018-01-03 09:04:47 -08001283 dispatcher = null;
tom7ef8ff92014-09-17 13:08:06 -07001284 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001285
1286 if (thisdpid != 0) {
1287 if (!duplicateDpidFound) {
1288 // if the disconnected switch (on this ChannelHandler)
1289 // was not one with a duplicate-dpid, it is safe to remove all
1290 // state for it at the controller. Notice that if the disconnected
1291 // switch was a duplicate-dpid, calling the method below would clear
1292 // all state for the original switch (with the same dpid),
1293 // which we obviously don't want.
1294 log.info("{}:removal called", getSwitchInfoString());
1295 if (sw != null) {
1296 sw.removeConnectedSwitch();
1297 }
1298 } else {
1299 // A duplicate was disconnected on this ChannelHandler,
1300 // this is the same switch reconnecting, but the original state was
1301 // not cleaned up - XXX check liveness of original ChannelHandler
1302 log.info("{}:duplicate found", getSwitchInfoString());
1303 duplicateDpidFound = Boolean.FALSE;
1304 }
1305 } else {
1306 log.warn("no dpid in channelHandler registered for "
1307 + "disconnected switch {}", getSwitchInfoString());
1308 }
tom7ef8ff92014-09-17 13:08:06 -07001309 }
1310
1311 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001312 public void exceptionCaught(ChannelHandlerContext ctx,
1313 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001314 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001315
1316 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001317 // switch timeout
1318 log.error("Disconnecting switch {} due to read timeout",
1319 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001320 ctx.channel().close();
1321 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001322 log.error("Disconnecting switch {}: failed to complete handshake",
1323 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001324 ctx.channel().close();
1325 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001326 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001327 } else if (cause instanceof IOException) {
1328 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1329 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001330 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001331 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001332 if (log.isDebugEnabled()) {
1333 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001334 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001335 }
tom7ef8ff92014-09-17 13:08:06 -07001336 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001337 ctx.channel().close();
1338 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001339 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001340 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001341 if (log.isDebugEnabled()) {
1342 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001343 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001344 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001345 ctx.channel().close();
1346 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001347 log.error("Disconnecting switch "
1348 + getSwitchInfoString() +
1349 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001350 cause);
1351 ctx.channel().close();
1352 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001353 log.warn("Could not process message: queue full");
1354 } else {
1355 log.error("Error while processing message from switch "
1356 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001357 + "state " + this.state, cause);
1358 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001359 }
1360 }
1361
1362 @Override
1363 public String toString() {
1364 return getSwitchInfoString();
1365 }
1366
Ray Milkey986a47a2018-01-25 11:38:51 -08001367 private void channelIdle(ChannelHandlerContext ctx,
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001368 IdleStateEvent e)
Ray Milkey986a47a2018-01-25 11:38:51 -08001369 throws IOException {
Charles Chan982d3902018-03-21 14:58:53 -07001370 // Factory can be null if the channel goes idle during initial handshake. Since the switch
1371 // is not even initialized properly, we just skip this and disconnect the channel.
1372 if (factory != null) {
1373 OFMessage m = factory.buildEchoRequest().build();
1374 log.debug("Sending Echo Request on idle channel: {}", ctx.channel());
1375 ctx.write(Collections.singletonList(m), ctx.voidPromise());
1376 // XXX S some problems here -- echo request has no transaction id, and
1377 // echo reply is not correlated to the echo request.
1378 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001379 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001380 }
1381
1382 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001383 public void userEventTriggered(ChannelHandlerContext ctx,
1384 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001385 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001386
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001387 if (evt instanceof IdleStateEvent) {
1388 channelIdle(ctx, (IdleStateEvent) evt);
1389 }
tom7ef8ff92014-09-17 13:08:06 -07001390
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001391 super.userEventTriggered(ctx, evt);
1392 }
1393
1394 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1395 @Override
1396 public void channelRead(ChannelHandlerContext ctx,
1397 Object msg) throws Exception {
1398
1399 boolean release = true;
1400 try {
1401 if (msg instanceof OFMessage) {
1402 // channelRead0 inlined
1403 state.processOFMessage(this, (OFMessage) msg);
1404 } else {
1405 release = false;
1406 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001407 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001408 } finally {
1409 if (release) {
1410 ReferenceCountUtil.release(msg);
1411 }
tom7ef8ff92014-09-17 13:08:06 -07001412 }
1413 }
1414
1415
1416
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001417
tom7ef8ff92014-09-17 13:08:06 -07001418 //*************************
1419 // Channel utility methods
1420 //*************************
1421
1422 /**
1423 * Is this a state in which the handshake has completed?
1424 * @return true if the handshake is complete
1425 */
1426 public boolean isHandshakeComplete() {
1427 return this.state.isHandshakeComplete();
1428 }
1429
1430 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001431
1432 if (dispatchBacklog.isEmpty()) {
1433 if (!dispatchQueue.offer(m)) {
1434 // queue full
1435 channel.config().setAutoRead(false);
1436 // put it on the head of backlog
1437 dispatchBacklog.addFirst(m);
1438 return;
1439 }
1440 } else {
1441 dispatchBacklog.addLast(m);
1442 }
1443
1444 while (!dispatchBacklog.isEmpty()) {
1445 OFMessage msg = dispatchBacklog.pop();
1446
1447 if (!dispatchQueue.offer(msg)) {
1448 // queue full
1449 channel.config().setAutoRead(false);
1450 // put it back to the head of backlog
1451 dispatchBacklog.addFirst(msg);
1452 return;
1453 }
1454 }
1455
1456
1457 if (dispatcherHandle.isDone()) {
1458 // dispatcher terminated for some reason, restart
1459
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -08001460 dispatcherHandle = dispatcher.submit((Runnable) () -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001461 try {
1462 List<OFMessage> msgs = new ArrayList<>();
1463 for (;;) {
1464 // wait for new message
1465 OFMessage msg = dispatchQueue.take();
1466 sw.handleMessage(msg);
1467
1468 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1469 if (!channel.config().isAutoRead()) {
1470 channel.config().setAutoRead(true);
1471 }
1472 msgs.forEach(sw::handleMessage);
1473 msgs.clear();
1474 }
1475
1476 if (!channel.config().isAutoRead()) {
1477 channel.config().setAutoRead(true);
1478 }
1479 }
1480 } catch (InterruptedException e) {
1481 Thread.currentThread().interrupt();
1482 // interrupted. gracefully shutting down
1483 return;
1484 }
1485
1486 });
1487 }
tom7ef8ff92014-09-17 13:08:06 -07001488 }
1489
1490 /**
1491 * Return a string describing this switch based on the already available
1492 * information (DPID and/or remote socket).
1493 * @return display string
1494 */
1495 private String getSwitchInfoString() {
1496 if (sw != null) {
1497 return sw.toString();
1498 }
1499 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001500 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001501 channelString = "?";
1502 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001503 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001504 }
1505 String dpidString;
1506 if (featuresReply == null) {
1507 dpidString = "?";
1508 } else {
1509 dpidString = featuresReply.getDatapathId().toString();
1510 }
1511 return String.format("[%s DPID[%s]]", channelString, dpidString);
1512 }
1513
1514 /**
1515 * Update the channels state. Only called from the state machine.
1516 * TODO: enforce restricted state transitions
1517 * @param state
1518 */
1519 private void setState(ChannelState state) {
1520 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001521 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001522 }
1523
Brian O'Connorf69e3e32018-05-10 02:25:09 -07001524 private boolean setDpid(Long dpid, Channel channel) {
1525 ChannelHandlerContext sslContext = channel.pipeline().context(SslHandler.class);
1526 if (sslContext != null) {
1527 try {
1528 SslHandler sslHandler = (SslHandler) sslContext.handler();
1529 Certificate[] certs = sslHandler.engine().getSession().getPeerCertificates();
1530 Certificate cert = certs.length > 0 ? certs[0] : null;
1531 if (!controller.isValidCertificate(dpid, cert)) {
1532 return false;
1533 }
1534 } catch (SSLPeerUnverifiedException e) {
1535 log.info("Switch with dpid {} is an unverified SSL peer.", dpid, e);
1536 return false;
1537 }
1538 }
1539 this.thisdpid = dpid;
1540 return true;
1541 }
1542
tom7ef8ff92014-09-17 13:08:06 -07001543 /**
1544 * Send hello message to the switch using the handshake transactions ids.
1545 * @throws IOException
1546 */
1547 private void sendHandshakeHelloMessage() throws IOException {
1548 // The OF protocol requires us to start things off by sending the highest
1549 // version of the protocol supported.
1550
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001551 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001552 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001553 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1554 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001555 (0b1 << OFVersion.OF_14.getWireVersion()) |
1556 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001557 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1558 OFHelloElem hem = OFFactories.getFactory(version)
1559 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001560 .setBitmaps(Collections.singletonList(bitmap))
1561 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001562 OFMessage.Builder mb = OFFactories.getFactory(version)
1563 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001564 .setXid(this.handshakeTransactionIds--)
1565 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001566 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1567 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001568 }
1569
1570 /**
1571 * Send featuresRequest msg to the switch using the handshake transactions ids.
1572 * @throws IOException
1573 */
1574 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001575 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001576 OFMessage m = factory.buildFeaturesRequest()
1577 .setXid(this.handshakeTransactionIds--)
1578 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001579 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001580 }
1581
1582 /**
1583 * Send the configuration requests to tell the switch we want full
1584 * packets.
1585 * @throws IOException
1586 */
1587 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001588 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001589 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001590
1591 // Ensure we receive the full packet via PacketIn
1592 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001593 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001594 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001595 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001596 OFSetConfig sc = factory
1597 .buildSetConfig()
1598 .setMissSendLen((short) 0xffff)
1599 .setXid(this.handshakeTransactionIds--)
1600 .build();
1601 msglist.add(sc);
1602 }
tom7ef8ff92014-09-17 13:08:06 -07001603
1604 // Barrier
1605 OFBarrierRequest br = factory
1606 .buildBarrierRequest()
1607 .setXid(this.handshakeTransactionIds--)
1608 .build();
1609 msglist.add(br);
1610
1611 // Verify (need barrier?)
1612 OFGetConfigRequest gcr = factory
1613 .buildGetConfigRequest()
1614 .setXid(this.handshakeTransactionIds--)
1615 .build();
1616 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001617 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001618 }
1619
1620 /**
1621 * send a description state request.
1622 * @throws IOException
1623 */
1624 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1625 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001626 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001627 OFDescStatsRequest dreq = factory
1628 .buildDescStatsRequest()
1629 .setXid(handshakeTransactionIds--)
1630 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001631 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001632 }
1633
Jordi Ortiz91477b82016-11-29 15:22:50 +01001634 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001635 * send a meter features request.
1636 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001637 * @throws IOException
1638 */
1639 private void sendMeterFeaturesRequest() throws IOException {
1640 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001641 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001642 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001643 OFMeterFeaturesStatsRequest mfreq = factory
1644 .buildMeterFeaturesStatsRequest()
1645 .setXid(handshakeTransactionIds--)
1646 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001647 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001648 }
1649
tom7ef8ff92014-09-17 13:08:06 -07001650 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001651 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001652 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001653 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001654 .buildPortDescStatsRequest()
1655 .setXid(handshakeTransactionIds--)
1656 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001657 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001658 }
1659
1660 ChannelState getStateForTesting() {
1661 return state;
1662 }
1663
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001664
1665 @Override
1666 public boolean isActive() {
1667 if (channel != null) {
1668 return channel.isActive();
1669 }
1670 return false;
1671 }
1672
1673 @Override
1674 public void closeSession() {
1675 if (channel != null) {
1676 channel.close();
1677 }
1678 }
1679
1680 @Override
1681 public boolean sendMsg(Iterable<OFMessage> msgs) {
1682 if (channel.isActive()) {
Laszlo Pappb68fe7e2017-11-24 17:06:59 +00001683 if (log.isTraceEnabled()) {
1684 log.trace("Sending messages for switch {} via openflow channel: {}", getSwitchInfoString(), msgs);
1685 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001686 channel.writeAndFlush(msgs, channel.voidPromise());
1687 return true;
1688 } else {
1689 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1690 getSwitchInfoString(), msgs);
1691 return false;
1692 }
1693 }
1694
1695 @Override
1696 public CharSequence sessionInfo() {
1697 return channelId;
1698 }
1699
tom7ef8ff92014-09-17 13:08:06 -07001700}