blob: 55ea65e799c174073a32460a7df51b97e643ac59 [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
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()) {
560 if (!h.sw.connectSwitch()) {
561 disconnectDuplicate(h);
562 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800563 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700564 h.setState(ACTIVE);
565 } else {
566 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
567 }
tom7ef8ff92014-09-17 13:08:06 -0700568
569 }
570
571 @Override
572 void processOFError(OFChannelHandler h, OFErrorMsg m) {
573 logErrorDisconnect(h, m);
574 }
575
576 @Override
577 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
578 throws IOException, SwitchStateException {
579 illegalMessageReceived(h, m);
580 }
581
582 @Override
583 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
584 throws IOException {
585 h.pendingPortStatusMsg.add(m);
586 }
587 },
588
589
590 /**
591 * We are waiting for the respective switch driver to complete its
592 * configuration. Notice that we do not consider this to be part of the main
593 * switch-controller handshake. But we do consider it as a step that comes
594 * before we declare the switch as available to the controller.
595 * Next State: depends on the role of this controller for this switch - either
596 * MASTER or EQUAL.
597 */
598 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
599
600 @Override
601 void processOFError(OFChannelHandler h, OFErrorMsg m)
602 throws IOException {
603 // will never be called. We override processOFMessage
604 }
605
alshabibd7963912014-10-20 14:52:04 -0700606
607
tom7ef8ff92014-09-17 13:08:06 -0700608 @Override
609 void processOFMessage(OFChannelHandler h, OFMessage m)
610 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700611
612 if (h.sw.isDriverHandshakeComplete()) {
613 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700614 h.state.processOFMessage(h, m);
615 return;
alshabibd7963912014-10-20 14:52:04 -0700616
617 }
618
tom7ef8ff92014-09-17 13:08:06 -0700619 if (m.getType() == OFType.ECHO_REQUEST) {
620 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700621 } else if (m.getType() == OFType.ECHO_REPLY) {
622 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700623 } else if (m.getType() == OFType.ROLE_REPLY) {
624 h.sw.handleRole(m);
625 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800626 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700627 h.sw.processDriverHandshakeMessage(m);
628 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700629 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700630 }
631 }
632 } else {
633 if (m.getType() == OFType.EXPERIMENTER &&
634 ((OFExperimenter) m).getExperimenter() ==
635 RoleManager.NICIRA_EXPERIMENTER) {
636 h.sw.handleNiciraRole(m);
637 } else {
638 h.sw.processDriverHandshakeMessage(m);
639 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700640 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700641 }
642 }
643 }
644 }
645
646 @Override
647 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
648 throws IOException, SwitchStateException {
649 h.pendingPortStatusMsg.add(m);
650 }
alshabibd7963912014-10-20 14:52:04 -0700651
652 private void moveToActive(OFChannelHandler h) {
653 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800654 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700655 h.setState(ACTIVE);
656 if (!success) {
657 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700658 }
659 }
660
tom7ef8ff92014-09-17 13:08:06 -0700661 },
662
Jordi Ortiz91477b82016-11-29 15:22:50 +0100663 /**
664 * We are expecting a OF Multi Part Meter Features Stats Reply.
665 * Notice that this information is only available for switches running
666 * OpenFlow 1.3
667 */
668 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800669
670 @Override
671 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
672 throws IOException {
673 super.processOFEchoRequest(h, m);
674 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
675 log.info("{} did not respond to MeterFeaturesRequest on time, " +
676 "moving on without it.",
677 h.getSwitchInfoString());
678 h.sendHandshakeDescriptionStatsRequest();
679 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
680 }
681 }
682
Jordi Ortiz91477b82016-11-29 15:22:50 +0100683 @Override
684 void processOFError(OFChannelHandler h, OFErrorMsg m)
685 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800686 // Hardware switches may reply OFErrorMsg if meter is not supported
687 log.warn("Received OFError {}. It seems {} does not support Meter.",
688 m.getErrType().name(), Dpid.uri(h.thisdpid));
689 log.debug("{}", m);
690 h.sendHandshakeDescriptionStatsRequest();
691 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100692 }
693
694 @Override
695 void processOFStatisticsReply(OFChannelHandler h,
696 OFStatsReply m)
697 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800698 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100699 case METER_FEATURES:
700
701 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800702 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100703 log.info("Received meter features from {} with max meters: {}",
704 h.getSwitchInfoString(),
705 ofmfsr.getFeatures().getMaxMeter());
706 h.meterFeaturesReply = ofmfsr;
707 h.sendHandshakeDescriptionStatsRequest();
708 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
709 break;
710 default:
711 log.error("Unexpected OF Multi Part stats reply");
712 illegalMessageReceived(h, m);
713 break;
714 }
715 }
716
717 @Override
718 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
719 throws IOException, SwitchStateException {
720 illegalMessageReceived(h, m);
721 }
722
723 @Override
724 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
725 throws IOException {
726 h.pendingPortStatusMsg.add(m);
727 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800728
729 @Override
730 void processIdle(OFChannelHandler h) throws IOException {
731 log.info("{} did not respond to MeterFeaturesRequest, " +
732 "moving on without it.",
733 h.getSwitchInfoString());
734 h.sendHandshakeDescriptionStatsRequest();
735 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
736 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100737 },
738
tom7ef8ff92014-09-17 13:08:06 -0700739
740 /**
741 * This controller is in MASTER role for this switch. We enter this state
742 * after requesting and winning control from the global registry.
743 * The main handshake as well as the switch-driver sub-handshake
744 * is complete at this point.
745 * // XXX S reconsider below
746 * In the (near) future we may deterministically assign controllers to
747 * switches at startup.
748 * We only leave this state if the switch disconnects or
749 * if we send a role request for SLAVE /and/ receive the role reply for
750 * SLAVE.
751 */
752 ACTIVE(true) {
753 @Override
754 void processOFError(OFChannelHandler h, OFErrorMsg m)
755 throws IOException, SwitchStateException {
756 // if we get here, then the error message is for something else
757 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700758 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700759 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700760 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700761 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700762 // We are the master controller and the switch returned
763 // a permission error. This is a likely indicator that
764 // the switch thinks we are slave. Reassert our
765 // role
766 // FIXME: this could be really bad during role transitions
767 // if two controllers are master (even if its only for
768 // a brief period). We might need to see if these errors
769 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700770
tom7ef8ff92014-09-17 13:08:06 -0700771 h.sw.reassertRole();
772 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
773 ((OFFlowModFailedErrorMsg) m).getCode() ==
774 OFFlowModFailedCode.ALL_TABLES_FULL) {
775 h.sw.setTableFull(true);
776 } else {
777 logError(h, m);
778 }
779 h.dispatchMessage(m);
780 }
781
782 @Override
783 void processOFStatisticsReply(OFChannelHandler h,
784 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700785 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
Saurav Das45f48152018-01-18 12:07:33 -0800786 log.debug("Received port desc message from {}: {}",
787 h.sw.getDpid(),
788 ((OFPortDescStatsReply) m).getEntries());
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700789 h.sw.setPortDescReply((OFPortDescStatsReply) m);
790 }
tom7ef8ff92014-09-17 13:08:06 -0700791 h.dispatchMessage(m);
792 }
793
794 @Override
795 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
796 throws SwitchStateException {
797 h.sw.handleNiciraRole(m);
798 }
799
800 @Override
801 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
802 throws SwitchStateException {
803 h.sw.handleRole(m);
804 }
805
806 @Override
807 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
808 throws SwitchStateException {
809 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800810 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700811 }
812
813 @Override
814 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700815// OFPacketOut out =
816// h.sw.factory().buildPacketOut()
817// .setXid(m.getXid())
818// .setBufferId(m.getBufferId()).build();
819// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700820 h.dispatchMessage(m);
821 }
822
823 @Override
824 void processOFFlowRemoved(OFChannelHandler h,
825 OFFlowRemoved m) {
826 h.dispatchMessage(m);
827 }
828
829 @Override
830 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
831 h.dispatchMessage(m);
832 }
833
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700834 @Override
835 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700836 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700837 h.dispatchMessage(m);
838 }
839
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800840 @Override
841 void processIdle(OFChannelHandler h) throws IOException {
842 log.info("{} idle", h.getSwitchInfoString());
843 }
844
tom7ef8ff92014-09-17 13:08:06 -0700845 };
846
847 private final boolean handshakeComplete;
848 ChannelState(boolean handshakeComplete) {
849 this.handshakeComplete = handshakeComplete;
850 }
851
852 /**
853 * Is this a state in which the handshake has completed?
854 * @return true if the handshake is complete
855 */
856 public boolean isHandshakeComplete() {
857 return handshakeComplete;
858 }
859
860 /**
861 * Get a string specifying the switch connection, state, and
862 * message received. To be used as message for SwitchStateException
863 * or log messages
864 * @param h The channel handler (to get switch information_
865 * @param m The OFMessage that has just been received
866 * @param details A string giving more details about the exact nature
867 * of the problem.
868 * @return display string
869 */
870 // needs to be protected because enum members are actually subclasses
871 protected String getSwitchStateMessage(OFChannelHandler h,
872 OFMessage m,
873 String details) {
874 return String.format("Switch: [%s], State: [%s], received: [%s]"
875 + ", details: %s",
876 h.getSwitchInfoString(),
877 this.toString(),
878 m.getType().toString(),
879 details);
880 }
881
882 /**
883 * We have an OFMessage we didn't expect given the current state and
884 * we want to treat this as an error.
885 * We currently throw an exception that will terminate the connection
886 * However, we could be more forgiving
887 * @param h the channel handler that received the message
888 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700889 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700890 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700891 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700892 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
893 throws SwitchStateException {
894 String msg = getSwitchStateMessage(h, m,
895 "Switch should never send this message in the current state");
896 throw new SwitchStateException(msg);
897
898 }
899
900 /**
901 * We have an OFMessage we didn't expect given the current state and
902 * we want to ignore the message.
903 * @param h the channel handler the received the message
904 * @param m the message
905 */
906 protected void unhandledMessageReceived(OFChannelHandler h,
907 OFMessage m) {
908 if (log.isDebugEnabled()) {
909 String msg = getSwitchStateMessage(h, m,
910 "Ignoring unexpected message");
911 log.debug(msg);
912 }
913 }
914
915 /**
916 * Log an OpenFlow error message from a switch.
917 * @param h The switch that sent the error
918 * @param error The error message
919 */
920 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700921 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700922 error,
923 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800924 this);
tom7ef8ff92014-09-17 13:08:06 -0700925 }
926
927 /**
928 * Log an OpenFlow error message from a switch and disconnect the
929 * channel.
930 *
931 * @param h the IO channel for this switch.
932 * @param error The error message
933 */
934 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
935 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700936 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700937 h.channel.disconnect();
938 }
939
940 /**
941 * log an error message for a duplicate dpid and disconnect this channel.
942 * @param h the IO channel for this switch.
943 */
944 protected void disconnectDuplicate(OFChannelHandler h) {
945 log.error("Duplicated dpid or incompleted cleanup - "
946 + "disconnecting channel {}", h.getSwitchInfoString());
947 h.duplicateDpidFound = Boolean.TRUE;
948 h.channel.disconnect();
949 }
950
951
952
953 /**
954 * Handles all pending port status messages before a switch is declared
955 * activated in MASTER or EQUAL role. Note that since this handling
956 * precedes the activation (and therefore notification to IOFSwitchListerners)
957 * the changes to ports will already be visible once the switch is
958 * activated. As a result, no notifications are sent out for these
959 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700960 *
961 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700962 */
963 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
964 try {
965 handlePendingPortStatusMessages(h, 0);
966 } catch (SwitchStateException e) {
967 log.error(e.getMessage());
968 }
969 }
970
971 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
972 throws SwitchStateException {
973 if (h.sw == null) {
974 String msg = "State machine error: switch is null. Should never " +
975 "happen";
976 throw new SwitchStateException(msg);
977 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800978 log.info("Processing {} pending port status messages for {}",
979 h.pendingPortStatusMsg.size(), h.sw.getStringId());
980
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800981 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700982 for (OFPortStatus ps: h.pendingPortStatusMsg) {
983 temp.add(ps);
984 handlePortStatusMessage(h, ps, false);
985 }
tom7ef8ff92014-09-17 13:08:06 -0700986 // expensive but ok - we don't expect too many port-status messages
987 // note that we cannot use clear(), because of the reasons below
988 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800989 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700990 // the iterator above takes a snapshot of the list - so while we were
991 // dealing with the pending port-status messages, we could have received
992 // newer ones. Handle them recursively, but break the recursion after
993 // five steps to avoid an attack.
994 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
995 handlePendingPortStatusMessages(h, index);
996 }
997 }
998
999 /**
1000 * Handle a port status message.
1001 *
1002 * Handle a port status message by updating the port maps in the
1003 * IOFSwitch instance and notifying Controller about the change so
1004 * it can dispatch a switch update.
1005 *
1006 * @param h The OFChannelHhandler that received the message
1007 * @param m The PortStatus message we received
1008 * @param doNotify if true switch port changed events will be
1009 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001010 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001011 *
1012 */
1013 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1014 boolean doNotify) throws SwitchStateException {
1015 if (h.sw == null) {
1016 String msg = getSwitchStateMessage(h, m,
1017 "State machine error: switch is null. Should never " +
1018 "happen");
1019 throw new SwitchStateException(msg);
1020 }
Saurav Dasbd071d82018-01-09 17:38:44 -08001021 log.info("Received port status message from {}/{}: {}",
1022 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001023
1024 h.sw.handleMessage(m);
1025 }
1026
1027
1028 /**
1029 * Process an OF message received on the channel and
1030 * update state accordingly.
1031 *
1032 * The main "event" of the state machine. Process the received message,
1033 * send follow up message if required and update state if required.
1034 *
1035 * Switches on the message type and calls more specific event handlers
1036 * for each individual OF message type. If we receive a message that
1037 * is supposed to be sent from a controller to a switch we throw
1038 * a SwitchStateExeption.
1039 *
1040 * The more specific handlers can also throw SwitchStateExceptions
1041 *
1042 * @param h The OFChannelHandler that received the message
1043 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001044 * @throws SwitchStateException if the switch is not bound to the channel
1045 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001046 */
1047 void processOFMessage(OFChannelHandler h, OFMessage m)
1048 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001049 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001050 case HELLO:
1051 processOFHello(h, (OFHello) m);
1052 break;
1053 case BARRIER_REPLY:
1054 processOFBarrierReply(h, (OFBarrierReply) m);
1055 break;
1056 case ECHO_REPLY:
1057 processOFEchoReply(h, (OFEchoReply) m);
1058 break;
1059 case ECHO_REQUEST:
1060 processOFEchoRequest(h, (OFEchoRequest) m);
1061 break;
1062 case ERROR:
1063 processOFError(h, (OFErrorMsg) m);
1064 break;
1065 case FEATURES_REPLY:
1066 processOFFeaturesReply(h, (OFFeaturesReply) m);
1067 break;
1068 case FLOW_REMOVED:
1069 processOFFlowRemoved(h, (OFFlowRemoved) m);
1070 break;
1071 case GET_CONFIG_REPLY:
1072 processOFGetConfigReply(h, (OFGetConfigReply) m);
1073 break;
1074 case PACKET_IN:
1075 processOFPacketIn(h, (OFPacketIn) m);
1076 break;
1077 case PORT_STATUS:
1078 processOFPortStatus(h, (OFPortStatus) m);
1079 break;
1080 case QUEUE_GET_CONFIG_REPLY:
1081 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1082 break;
1083 case STATS_REPLY: // multipart_reply in 1.3
1084 processOFStatisticsReply(h, (OFStatsReply) m);
1085 break;
1086 case EXPERIMENTER:
1087 processOFExperimenter(h, (OFExperimenter) m);
1088 break;
1089 case ROLE_REPLY:
1090 processOFRoleReply(h, (OFRoleReply) m);
1091 break;
1092 case GET_ASYNC_REPLY:
1093 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1094 break;
1095
1096 // The following messages are sent to switches. The controller
1097 // should never receive them
1098 case SET_CONFIG:
1099 case GET_CONFIG_REQUEST:
1100 case PACKET_OUT:
1101 case PORT_MOD:
1102 case QUEUE_GET_CONFIG_REQUEST:
1103 case BARRIER_REQUEST:
1104 case STATS_REQUEST: // multipart request in 1.3
1105 case FEATURES_REQUEST:
1106 case FLOW_MOD:
1107 case GROUP_MOD:
1108 case TABLE_MOD:
1109 case GET_ASYNC_REQUEST:
1110 case SET_ASYNC:
1111 case METER_MOD:
1112 default:
1113 illegalMessageReceived(h, m);
1114 break;
1115 }
1116 }
1117
1118 /*-----------------------------------------------------------------
1119 * Default implementation for message handlers in any state.
1120 *
1121 * Individual states must override these if they want a behavior
1122 * that differs from the default.
1123 *
1124 * In general, these handlers simply ignore the message and do
1125 * nothing.
1126 *
1127 * There are some exceptions though, since some messages really
1128 * are handled the same way in every state (e.g., ECHO_REQUST) or
1129 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1130 -----------------------------------------------------------------*/
1131
1132 void processOFHello(OFChannelHandler h, OFHello m)
1133 throws IOException, SwitchStateException {
1134 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001135 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001136 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001137 }
1138
1139 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1140 throws IOException {
1141 // Silently ignore.
1142 }
1143
1144 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1145 throws IOException {
1146 if (h.ofVersion == null) {
1147 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001148 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001149 return;
1150 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001151 OFEchoReply reply = h.factory
1152 .buildEchoReply()
1153 .setXid(m.getXid())
1154 .setData(m.getData())
1155 .build();
1156 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001157 }
1158
1159 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1160 throws IOException {
1161 // Do nothing with EchoReplies !!
1162 }
1163
1164 // no default implementation for OFError
1165 // every state must override it
1166 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1167 throws IOException, SwitchStateException;
1168
1169
1170 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1171 throws IOException, SwitchStateException {
1172 unhandledMessageReceived(h, m);
1173 }
1174
1175 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1176 throws IOException {
1177 unhandledMessageReceived(h, m);
1178 }
1179
1180 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1181 throws IOException, SwitchStateException {
1182 // we only expect config replies in the WAIT_CONFIG_REPLY state
1183 illegalMessageReceived(h, m);
1184 }
1185
1186 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1187 throws IOException {
1188 unhandledMessageReceived(h, m);
1189 }
1190
1191 // no default implementation. Every state needs to handle it.
1192 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1193 throws IOException, SwitchStateException;
1194
1195 void processOFQueueGetConfigReply(OFChannelHandler h,
1196 OFQueueGetConfigReply m)
1197 throws IOException {
1198 unhandledMessageReceived(h, m);
1199 }
1200
1201 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1202 throws IOException, SwitchStateException {
1203 unhandledMessageReceived(h, m);
1204 }
1205
1206 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1207 throws IOException, SwitchStateException {
1208 // TODO: it might make sense to parse the vendor message here
1209 // into the known vendor messages we support and then call more
1210 // specific event handlers
1211 unhandledMessageReceived(h, m);
1212 }
1213
1214 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1215 throws SwitchStateException, IOException {
1216 unhandledMessageReceived(h, m);
1217 }
1218
1219 void processOFGetAsyncReply(OFChannelHandler h,
1220 OFAsyncGetReply m) {
1221 unhandledMessageReceived(h, m);
1222 }
1223
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001224 void processIdle(OFChannelHandler h) throws IOException {
1225 // disconnect channel which did no complete handshake
1226 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1227 h.channel.disconnect();
1228 }
tom7ef8ff92014-09-17 13:08:06 -07001229 }
1230
1231
1232
1233 //*************************
1234 // Channel handler methods
1235 //*************************
1236
1237 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001238 public void channelActive(ChannelHandlerContext ctx)
1239 throws Exception {
1240
1241 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001242 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001243 channel.remoteAddress());
1244
1245 SocketAddress address = channel.remoteAddress();
1246 if (address instanceof InetSocketAddress) {
1247 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1248 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1249 if (ipAddress.isIp4()) {
1250 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1251 } else {
1252 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1253 }
1254 } else {
1255 channelId = channel.toString();
1256 }
1257
1258 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1259
alshabib70fc7fb2015-01-06 11:04:29 -08001260 /*
1261 hack to wait for the switch to tell us what it's
1262 max version is. This is not spec compliant and should
1263 be removed as soon as switches behave better.
1264 */
1265 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001266 setState(ChannelState.WAIT_HELLO);
1267 }
1268
1269 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001270 public void channelInactive(ChannelHandlerContext ctx)
1271 throws Exception {
1272
tom7ef8ff92014-09-17 13:08:06 -07001273 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001274 getSwitchInfoString());
1275
1276 if (dispatcher != null) {
Harold Huang828cd592017-11-04 10:46:04 +08001277 dispatcher.shutdownNow();
Thomas Vachuskad75684a2018-01-03 09:04:47 -08001278 dispatcher = null;
tom7ef8ff92014-09-17 13:08:06 -07001279 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001280
1281 if (thisdpid != 0) {
1282 if (!duplicateDpidFound) {
1283 // if the disconnected switch (on this ChannelHandler)
1284 // was not one with a duplicate-dpid, it is safe to remove all
1285 // state for it at the controller. Notice that if the disconnected
1286 // switch was a duplicate-dpid, calling the method below would clear
1287 // all state for the original switch (with the same dpid),
1288 // which we obviously don't want.
1289 log.info("{}:removal called", getSwitchInfoString());
1290 if (sw != null) {
1291 sw.removeConnectedSwitch();
1292 }
1293 } else {
1294 // A duplicate was disconnected on this ChannelHandler,
1295 // this is the same switch reconnecting, but the original state was
1296 // not cleaned up - XXX check liveness of original ChannelHandler
1297 log.info("{}:duplicate found", getSwitchInfoString());
1298 duplicateDpidFound = Boolean.FALSE;
1299 }
1300 } else {
1301 log.warn("no dpid in channelHandler registered for "
1302 + "disconnected switch {}", getSwitchInfoString());
1303 }
tom7ef8ff92014-09-17 13:08:06 -07001304 }
1305
1306 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001307 public void exceptionCaught(ChannelHandlerContext ctx,
1308 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001309 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001310
1311 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001312 // switch timeout
1313 log.error("Disconnecting switch {} due to read timeout",
1314 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001315 ctx.channel().close();
1316 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001317 log.error("Disconnecting switch {}: failed to complete handshake",
1318 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001319 ctx.channel().close();
1320 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001321 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001322 } else if (cause instanceof IOException) {
1323 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1324 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001325 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001326 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001327 if (log.isDebugEnabled()) {
1328 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001329 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001330 }
tom7ef8ff92014-09-17 13:08:06 -07001331 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001332 ctx.channel().close();
1333 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001334 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001335 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001336 if (log.isDebugEnabled()) {
1337 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001338 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001339 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001340 ctx.channel().close();
1341 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001342 log.error("Disconnecting switch "
1343 + getSwitchInfoString() +
1344 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001345 cause);
1346 ctx.channel().close();
1347 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001348 log.warn("Could not process message: queue full");
1349 } else {
1350 log.error("Error while processing message from switch "
1351 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001352 + "state " + this.state, cause);
1353 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001354 }
1355 }
1356
1357 @Override
1358 public String toString() {
1359 return getSwitchInfoString();
1360 }
1361
Ray Milkey986a47a2018-01-25 11:38:51 -08001362 private void channelIdle(ChannelHandlerContext ctx,
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001363 IdleStateEvent e)
Ray Milkey986a47a2018-01-25 11:38:51 -08001364 throws IOException {
Charles Chan982d3902018-03-21 14:58:53 -07001365 // Factory can be null if the channel goes idle during initial handshake. Since the switch
1366 // is not even initialized properly, we just skip this and disconnect the channel.
1367 if (factory != null) {
1368 OFMessage m = factory.buildEchoRequest().build();
1369 log.debug("Sending Echo Request on idle channel: {}", ctx.channel());
1370 ctx.write(Collections.singletonList(m), ctx.voidPromise());
1371 // XXX S some problems here -- echo request has no transaction id, and
1372 // echo reply is not correlated to the echo request.
1373 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001374 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001375 }
1376
1377 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001378 public void userEventTriggered(ChannelHandlerContext ctx,
1379 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001380 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001381
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001382 if (evt instanceof IdleStateEvent) {
1383 channelIdle(ctx, (IdleStateEvent) evt);
1384 }
tom7ef8ff92014-09-17 13:08:06 -07001385
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001386 super.userEventTriggered(ctx, evt);
1387 }
1388
1389 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1390 @Override
1391 public void channelRead(ChannelHandlerContext ctx,
1392 Object msg) throws Exception {
1393
1394 boolean release = true;
1395 try {
1396 if (msg instanceof OFMessage) {
1397 // channelRead0 inlined
1398 state.processOFMessage(this, (OFMessage) msg);
1399 } else {
1400 release = false;
1401 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001402 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001403 } finally {
1404 if (release) {
1405 ReferenceCountUtil.release(msg);
1406 }
tom7ef8ff92014-09-17 13:08:06 -07001407 }
1408 }
1409
1410
1411
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001412
tom7ef8ff92014-09-17 13:08:06 -07001413 //*************************
1414 // Channel utility methods
1415 //*************************
1416
1417 /**
1418 * Is this a state in which the handshake has completed?
1419 * @return true if the handshake is complete
1420 */
1421 public boolean isHandshakeComplete() {
1422 return this.state.isHandshakeComplete();
1423 }
1424
1425 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001426
1427 if (dispatchBacklog.isEmpty()) {
1428 if (!dispatchQueue.offer(m)) {
1429 // queue full
1430 channel.config().setAutoRead(false);
1431 // put it on the head of backlog
1432 dispatchBacklog.addFirst(m);
1433 return;
1434 }
1435 } else {
1436 dispatchBacklog.addLast(m);
1437 }
1438
1439 while (!dispatchBacklog.isEmpty()) {
1440 OFMessage msg = dispatchBacklog.pop();
1441
1442 if (!dispatchQueue.offer(msg)) {
1443 // queue full
1444 channel.config().setAutoRead(false);
1445 // put it back to the head of backlog
1446 dispatchBacklog.addFirst(msg);
1447 return;
1448 }
1449 }
1450
1451
1452 if (dispatcherHandle.isDone()) {
1453 // dispatcher terminated for some reason, restart
1454
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -08001455 dispatcherHandle = dispatcher.submit((Runnable) () -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001456 try {
1457 List<OFMessage> msgs = new ArrayList<>();
1458 for (;;) {
1459 // wait for new message
1460 OFMessage msg = dispatchQueue.take();
1461 sw.handleMessage(msg);
1462
1463 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1464 if (!channel.config().isAutoRead()) {
1465 channel.config().setAutoRead(true);
1466 }
1467 msgs.forEach(sw::handleMessage);
1468 msgs.clear();
1469 }
1470
1471 if (!channel.config().isAutoRead()) {
1472 channel.config().setAutoRead(true);
1473 }
1474 }
1475 } catch (InterruptedException e) {
1476 Thread.currentThread().interrupt();
1477 // interrupted. gracefully shutting down
1478 return;
1479 }
1480
1481 });
1482 }
tom7ef8ff92014-09-17 13:08:06 -07001483 }
1484
1485 /**
1486 * Return a string describing this switch based on the already available
1487 * information (DPID and/or remote socket).
1488 * @return display string
1489 */
1490 private String getSwitchInfoString() {
1491 if (sw != null) {
1492 return sw.toString();
1493 }
1494 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001495 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001496 channelString = "?";
1497 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001498 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001499 }
1500 String dpidString;
1501 if (featuresReply == null) {
1502 dpidString = "?";
1503 } else {
1504 dpidString = featuresReply.getDatapathId().toString();
1505 }
1506 return String.format("[%s DPID[%s]]", channelString, dpidString);
1507 }
1508
1509 /**
1510 * Update the channels state. Only called from the state machine.
1511 * TODO: enforce restricted state transitions
1512 * @param state
1513 */
1514 private void setState(ChannelState state) {
1515 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001516 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001517 }
1518
Brian O'Connorf69e3e32018-05-10 02:25:09 -07001519 private boolean setDpid(Long dpid, Channel channel) {
1520 ChannelHandlerContext sslContext = channel.pipeline().context(SslHandler.class);
1521 if (sslContext != null) {
1522 try {
1523 SslHandler sslHandler = (SslHandler) sslContext.handler();
1524 Certificate[] certs = sslHandler.engine().getSession().getPeerCertificates();
1525 Certificate cert = certs.length > 0 ? certs[0] : null;
1526 if (!controller.isValidCertificate(dpid, cert)) {
1527 return false;
1528 }
1529 } catch (SSLPeerUnverifiedException e) {
1530 log.info("Switch with dpid {} is an unverified SSL peer.", dpid, e);
1531 return false;
1532 }
1533 }
1534 this.thisdpid = dpid;
1535 return true;
1536 }
1537
tom7ef8ff92014-09-17 13:08:06 -07001538 /**
1539 * Send hello message to the switch using the handshake transactions ids.
1540 * @throws IOException
1541 */
1542 private void sendHandshakeHelloMessage() throws IOException {
1543 // The OF protocol requires us to start things off by sending the highest
1544 // version of the protocol supported.
1545
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001546 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001547 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001548 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1549 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001550 (0b1 << OFVersion.OF_14.getWireVersion()) |
1551 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001552 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1553 OFHelloElem hem = OFFactories.getFactory(version)
1554 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001555 .setBitmaps(Collections.singletonList(bitmap))
1556 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001557 OFMessage.Builder mb = OFFactories.getFactory(version)
1558 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001559 .setXid(this.handshakeTransactionIds--)
1560 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001561 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1562 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001563 }
1564
1565 /**
1566 * Send featuresRequest msg to the switch using the handshake transactions ids.
1567 * @throws IOException
1568 */
1569 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001570 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001571 OFMessage m = factory.buildFeaturesRequest()
1572 .setXid(this.handshakeTransactionIds--)
1573 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001574 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001575 }
1576
1577 /**
1578 * Send the configuration requests to tell the switch we want full
1579 * packets.
1580 * @throws IOException
1581 */
1582 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001583 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001584 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001585
1586 // Ensure we receive the full packet via PacketIn
1587 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001588 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001589 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001590 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001591 OFSetConfig sc = factory
1592 .buildSetConfig()
1593 .setMissSendLen((short) 0xffff)
1594 .setXid(this.handshakeTransactionIds--)
1595 .build();
1596 msglist.add(sc);
1597 }
tom7ef8ff92014-09-17 13:08:06 -07001598
1599 // Barrier
1600 OFBarrierRequest br = factory
1601 .buildBarrierRequest()
1602 .setXid(this.handshakeTransactionIds--)
1603 .build();
1604 msglist.add(br);
1605
1606 // Verify (need barrier?)
1607 OFGetConfigRequest gcr = factory
1608 .buildGetConfigRequest()
1609 .setXid(this.handshakeTransactionIds--)
1610 .build();
1611 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001612 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001613 }
1614
1615 /**
1616 * send a description state request.
1617 * @throws IOException
1618 */
1619 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1620 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001621 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001622 OFDescStatsRequest dreq = factory
1623 .buildDescStatsRequest()
1624 .setXid(handshakeTransactionIds--)
1625 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001626 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001627 }
1628
Jordi Ortiz91477b82016-11-29 15:22:50 +01001629 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001630 * send a meter features request.
1631 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001632 * @throws IOException
1633 */
1634 private void sendMeterFeaturesRequest() throws IOException {
1635 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001636 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001637 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001638 OFMeterFeaturesStatsRequest mfreq = factory
1639 .buildMeterFeaturesStatsRequest()
1640 .setXid(handshakeTransactionIds--)
1641 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001642 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001643 }
1644
tom7ef8ff92014-09-17 13:08:06 -07001645 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001646 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001647 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001648 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001649 .buildPortDescStatsRequest()
1650 .setXid(handshakeTransactionIds--)
1651 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001652 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001653 }
1654
1655 ChannelState getStateForTesting() {
1656 return state;
1657 }
1658
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001659
1660 @Override
1661 public boolean isActive() {
1662 if (channel != null) {
1663 return channel.isActive();
1664 }
1665 return false;
1666 }
1667
1668 @Override
1669 public void closeSession() {
1670 if (channel != null) {
1671 channel.close();
1672 }
1673 }
1674
1675 @Override
1676 public boolean sendMsg(Iterable<OFMessage> msgs) {
1677 if (channel.isActive()) {
Laszlo Pappb68fe7e2017-11-24 17:06:59 +00001678 if (log.isTraceEnabled()) {
1679 log.trace("Sending messages for switch {} via openflow channel: {}", getSwitchInfoString(), msgs);
1680 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001681 channel.writeAndFlush(msgs, channel.voidPromise());
1682 return true;
1683 } else {
1684 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1685 getSwitchInfoString(), msgs);
1686 return false;
1687 }
1688 }
1689
1690 @Override
1691 public CharSequence sessionInfo() {
1692 return channelId;
1693 }
1694
tom7ef8ff92014-09-17 13:08:06 -07001695}