blob: 414bd89538f4c6ee3ec07d35a1af85aad695c15d [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
16
Brian O'Connorabafb502014-12-02 22:26:20 -080017package org.onosproject.openflow.controller.impl;
tom7ef8ff92014-09-17 13:08:06 -070018
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070019import static org.onlab.util.Tools.groupedThreads;
20
tom7ef8ff92014-09-17 13:08:06 -070021import java.io.IOException;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070022import java.net.InetSocketAddress;
23import java.net.SocketAddress;
tom7ef8ff92014-09-17 13:08:06 -070024import java.nio.channels.ClosedChannelException;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070025import java.util.ArrayDeque;
tom7ef8ff92014-09-17 13:08:06 -070026import java.util.ArrayList;
27import java.util.Collections;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070028import java.util.Deque;
tom7ef8ff92014-09-17 13:08:06 -070029import java.util.List;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080030import java.util.Optional;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070031import java.util.concurrent.BlockingQueue;
32import java.util.concurrent.CompletableFuture;
tom7ef8ff92014-09-17 13:08:06 -070033import java.util.concurrent.CopyOnWriteArrayList;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070034import java.util.concurrent.ExecutorService;
35import java.util.concurrent.Executors;
36import java.util.concurrent.Future;
37import java.util.concurrent.LinkedBlockingQueue;
tom7ef8ff92014-09-17 13:08:06 -070038import java.util.concurrent.RejectedExecutionException;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070039import org.onlab.packet.IpAddress;
Charles Chan34155e52016-11-30 18:28:11 -080040import org.onosproject.openflow.controller.Dpid;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070041import org.onosproject.openflow.controller.OpenFlowSession;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
43import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070044import org.projectfloodlight.openflow.exceptions.OFParseError;
45import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
46import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
47import org.projectfloodlight.openflow.protocol.OFBarrierReply;
48import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
49import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
50import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
51import org.projectfloodlight.openflow.protocol.OFEchoReply;
52import org.projectfloodlight.openflow.protocol.OFEchoRequest;
53import org.projectfloodlight.openflow.protocol.OFErrorMsg;
54import org.projectfloodlight.openflow.protocol.OFErrorType;
55import org.projectfloodlight.openflow.protocol.OFExperimenter;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080056import org.projectfloodlight.openflow.protocol.OFFactories;
tom7ef8ff92014-09-17 13:08:06 -070057import org.projectfloodlight.openflow.protocol.OFFactory;
58import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
59import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
60import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
61import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
62import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
63import org.projectfloodlight.openflow.protocol.OFHello;
64import org.projectfloodlight.openflow.protocol.OFHelloElem;
65import org.projectfloodlight.openflow.protocol.OFMessage;
Jordi Ortiz91477b82016-11-29 15:22:50 +010066import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
67import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsRequest;
tom7ef8ff92014-09-17 13:08:06 -070068import org.projectfloodlight.openflow.protocol.OFPacketIn;
69import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
70import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
71import org.projectfloodlight.openflow.protocol.OFPortStatus;
72import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
73import org.projectfloodlight.openflow.protocol.OFRoleReply;
74import org.projectfloodlight.openflow.protocol.OFSetConfig;
75import org.projectfloodlight.openflow.protocol.OFStatsReply;
76import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
77import org.projectfloodlight.openflow.protocol.OFStatsType;
78import org.projectfloodlight.openflow.protocol.OFType;
79import org.projectfloodlight.openflow.protocol.OFVersion;
80import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
81import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
82import org.projectfloodlight.openflow.types.U32;
83import org.slf4j.Logger;
84import org.slf4j.LoggerFactory;
85
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070086import io.netty.channel.Channel;
87import io.netty.channel.ChannelHandlerContext;
88import io.netty.channel.ChannelInboundHandlerAdapter;
89import io.netty.handler.timeout.IdleStateEvent;
90import io.netty.handler.timeout.ReadTimeoutException;
91import io.netty.util.ReferenceCountUtil;
92
tom7ef8ff92014-09-17 13:08:06 -070093/**
94 * Channel handler deals with the switch connection and dispatches
95 * switch messages to the appropriate locations.
96 */
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070097class OFChannelHandler extends ChannelInboundHandlerAdapter
98 implements OpenFlowSession {
99
tom7ef8ff92014-09-17 13:08:06 -0700100 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -0700101
102 private static final String RESET_BY_PEER = "Connection reset by peer";
103 private static final String BROKEN_PIPE = "Broken pipe";
104
tom7ef8ff92014-09-17 13:08:06 -0700105 private final Controller controller;
106 private OpenFlowSwitchDriver sw;
107 private long thisdpid; // channelHandler cached value of connected switch id
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700108
tom7ef8ff92014-09-17 13:08:06 -0700109 private Channel channel;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700110 private String channelId;
111
112
tom7ef8ff92014-09-17 13:08:06 -0700113 // State needs to be volatile because the HandshakeTimeoutHandler
114 // needs to check if the handshake is complete
115 private volatile ChannelState state;
116
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800117 /**
118 * Timeout in ms to wait for meter feature reply.
119 */
120 private static final long METER_TIMEOUT = 60_000;
121
122 private volatile long lastStateChange = System.currentTimeMillis();
123
tom7ef8ff92014-09-17 13:08:06 -0700124 // When a switch with a duplicate dpid is found (i.e we already have a
125 // connected switch with the same dpid), the new switch is immediately
126 // disconnected. At that point netty callsback channelDisconnected() which
127 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
128 // switch state for the older (still connected) switch
129 private volatile Boolean duplicateDpidFound;
130
131 // Temporary storage for switch-features and port-description
132 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700133 private List<OFPortDescStatsReply> portDescReplies;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100134 private OFMeterFeaturesStatsReply meterFeaturesReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700135 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700136 // a concurrent ArrayList to temporarily store port status messages
137 // before we are ready to deal with them
138 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
139
140 //Indicates the openflow version used by this switch
141 protected OFVersion ofVersion;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700142 protected OFFactory factory;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800143
144 // deprecated in 1.10.0
145 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700146 protected OFFactory factory13;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800147 // deprecated in 1.10.0
148 @Deprecated
tom7ef8ff92014-09-17 13:08:06 -0700149 protected OFFactory factory10;
150
151 /** transaction Ids to use during handshake. Since only one thread
152 * calls into an OFChannelHandler instance, we don't need atomic.
153 * We will count down
154 */
155 private int handshakeTransactionIds = -1;
156
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700157
158
159 private static final int MSG_READ_BUFFER = 5000;
160
161 /**
162 * OFMessage dispatch queue.
163 */
164 private final BlockingQueue<OFMessage> dispatchQueue =
165 new LinkedBlockingQueue<>(MSG_READ_BUFFER);
166
167 /**
168 * Single thread executor for OFMessage dispatching.
169 *
170 * Gets initialized on channelActive, shutdown on channelInactive.
171 */
172 private ExecutorService dispatcher;
173
174 /**
175 * Handle for dispatcher thread.
176 * <p>
177 * Should only be touched from the Channel I/O thread
178 */
179 private Future<?> dispatcherHandle = CompletableFuture.completedFuture(null);
180
181 /**
182 * Dispatch backlog.
183 * <p>
184 * Should only be touched from the Channel I/O thread
185 */
186 private final Deque<OFMessage> dispatchBacklog = new ArrayDeque<>();
187
tom7ef8ff92014-09-17 13:08:06 -0700188 /**
189 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800190 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700191 */
192 OFChannelHandler(Controller controller) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700193
tom7ef8ff92014-09-17 13:08:06 -0700194 this.controller = controller;
195 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800196 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
197 this.portDescReplies = new ArrayList<>();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800198 factory13 = OFFactories.getFactory(OFVersion.OF_13);
199 factory10 = OFFactories.getFactory(OFVersion.OF_10);
tom7ef8ff92014-09-17 13:08:06 -0700200 duplicateDpidFound = Boolean.FALSE;
201 }
202
203
204
205 // XXX S consider if necessary
206 public void disconnectSwitch() {
207 sw.disconnectSwitch();
208 }
209
210
211
212 //*************************
213 // Channel State Machine
214 //*************************
215
216 /**
217 * The state machine for handling the switch/channel state. All state
218 * transitions should happen from within the state machine (and not from other
219 * parts of the code)
220 */
221 enum ChannelState {
222 /**
223 * Initial state before channel is connected.
224 */
225 INIT(false) {
226 @Override
227 void processOFMessage(OFChannelHandler h, OFMessage m)
228 throws IOException, SwitchStateException {
229 illegalMessageReceived(h, m);
230 }
231
232 @Override
233 void processOFError(OFChannelHandler h, OFErrorMsg m)
234 throws IOException {
235 // need to implement since its abstract but it will never
236 // be called
237 }
238
239 @Override
240 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
241 throws IOException {
242 unhandledMessageReceived(h, m);
243 }
244 },
245
246 /**
247 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
248 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
249 * protocol version is accepted.
250 * We send an OFFeaturesRequest depending on the protocol version selected
251 * Next state is WAIT_FEATURES_REPLY
252 */
253 WAIT_HELLO(false) {
254 @Override
255 void processOFHello(OFChannelHandler h, OFHello m)
256 throws IOException {
257 // TODO We could check for the optional bitmap, but for now
258 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500259 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
260 log.debug("Received {} Hello from {} - switching to OF "
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800261 + "version 1.3+", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700262 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800263 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700264 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800265 h.sendHandshakeHelloMessage();
Chip Boling68bc6562015-07-06 10:00:01 -0500266 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700267 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700268 + "version 1.0", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700269 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800270 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700271 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800272 OFHello hi =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700273 h.factory.buildHello()
alshabib70fc7fb2015-01-06 11:04:29 -0800274 .setXid(h.handshakeTransactionIds--)
275 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700276 h.channel.writeAndFlush(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700277 } else {
278 log.error("Received Hello of version {} from switch at {}. "
279 + "This controller works with OF1.0 and OF1.3 "
280 + "switches. Disconnecting switch ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700281 m.getVersion(), h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700282 h.channel.disconnect();
283 return;
284 }
285 h.sendHandshakeFeaturesRequestMessage();
286 h.setState(WAIT_FEATURES_REPLY);
287 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700288
tom7ef8ff92014-09-17 13:08:06 -0700289 @Override
290 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
291 throws IOException, SwitchStateException {
292 illegalMessageReceived(h, m);
293 }
294 @Override
295 void processOFStatisticsReply(OFChannelHandler h,
296 OFStatsReply m)
297 throws IOException, SwitchStateException {
298 illegalMessageReceived(h, m);
299 }
300 @Override
301 void processOFError(OFChannelHandler h, OFErrorMsg m) {
302 logErrorDisconnect(h, m);
303 }
304
305 @Override
306 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
307 throws IOException {
308 unhandledMessageReceived(h, m);
309 }
310 },
311
312
313 /**
314 * We are waiting for a features reply message. Once we receive it, the
315 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
316 * we send a SetConfig request, barrier, and GetConfig request and the
317 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
318 * request and the next state is WAIT_PORT_DESC_REPLY.
319 */
320 WAIT_FEATURES_REPLY(false) {
321 @Override
322 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
323 throws IOException {
324 h.thisdpid = m.getDatapathId().getLong();
alshabib09d48be2014-10-03 15:43:33 -0700325 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700326 h.getSwitchInfoString(), h.thisdpid);
327
328 h.featuresReply = m; //temp store
329 if (h.ofVersion == OFVersion.OF_10) {
330 h.sendHandshakeSetConfig();
331 h.setState(WAIT_CONFIG_REPLY);
332 } else {
333 //version is 1.3, must get switchport information
334 h.sendHandshakeOFPortDescRequest();
335 h.setState(WAIT_PORT_DESC_REPLY);
336 }
337 }
338 @Override
339 void processOFStatisticsReply(OFChannelHandler h,
340 OFStatsReply m)
341 throws IOException, SwitchStateException {
342 illegalMessageReceived(h, m);
343 }
344 @Override
345 void processOFError(OFChannelHandler h, OFErrorMsg m) {
346 logErrorDisconnect(h, m);
347 }
348
349 @Override
350 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
351 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800352 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700353 }
354 },
355
356 /**
357 * We are waiting for a description of the 1.3 switch ports.
358 * Once received, we send a SetConfig request
359 * Next State is WAIT_CONFIG_REPLY
360 */
361 WAIT_PORT_DESC_REPLY(false) {
362
363 @Override
364 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
365 throws SwitchStateException {
366 // Read port description
367 if (m.getStatsType() != OFStatsType.PORT_DESC) {
368 log.warn("Expecting port description stats but received stats "
369 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700370 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700371 return;
372 }
373 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700374 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700375 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700376 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800377 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700378 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800379 } else {
380 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700381 }
382 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
Saurav Das45f48152018-01-18 12:07:33 -0800383 log.debug("Received port desc reply for switch at {}: {}",
384 h.getSwitchInfoString(),
385 ((OFPortDescStatsReply) m).getEntries());
tom7ef8ff92014-09-17 13:08:06 -0700386 try {
387 h.sendHandshakeSetConfig();
388 } catch (IOException e) {
389 log.error("Unable to send setConfig after PortDescReply. "
390 + "Error: {}", e.getMessage());
391 }
392 h.setState(WAIT_CONFIG_REPLY);
393 }
394
395 @Override
396 void processOFError(OFChannelHandler h, OFErrorMsg m)
397 throws IOException, SwitchStateException {
398 logErrorDisconnect(h, m);
399
400 }
401
402 @Override
403 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
404 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800405 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700406
407 }
408 },
409
410 /**
411 * We are waiting for a config reply message. Once we receive it
412 * we send a DescriptionStatsRequest to the switch.
413 * Next state: WAIT_DESCRIPTION_STAT_REPLY
414 */
415 WAIT_CONFIG_REPLY(false) {
416 @Override
417 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
418 throws IOException {
419 if (m.getMissSendLen() == 0xffff) {
420 log.trace("Config Reply from switch {} confirms "
421 + "miss length set to 0xffff",
422 h.getSwitchInfoString());
423 } else {
424 // FIXME: we can't really deal with switches that don't send
425 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800426 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700427 + "miss length set to {}",
428 h.getSwitchInfoString(),
429 m.getMissSendLen());
430 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100431
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800432 nextState(h);
433 }
434
435 /**
436 * Transition to next state based on OF version.
437 *
438 * @param h current channel handler
439 * @throws IOException
440 */
441 private void nextState(OFChannelHandler h) throws IOException {
442 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100443 // Meters were introduced in OpenFlow 1.3
444 h.sendMeterFeaturesRequest();
445 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800446 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100447 h.sendHandshakeDescriptionStatsRequest();
448 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
449 }
tom7ef8ff92014-09-17 13:08:06 -0700450 }
451
452 @Override
453 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
454 // do nothing;
455 }
456
457 @Override
458 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
459 throws IOException, SwitchStateException {
460 illegalMessageReceived(h, m);
461 }
462 @Override
463 void processOFStatisticsReply(OFChannelHandler h,
464 OFStatsReply m)
465 throws IOException, SwitchStateException {
466 log.error("Received multipart(stats) message sub-type {}",
467 m.getStatsType());
468 illegalMessageReceived(h, m);
469 }
470
471 @Override
472 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800473 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
474 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
475 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
476 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
477 try {
478 nextState(h);
479 return;
480 } catch (IOException e) {
481 log.error("Exception thrown transitioning to next", e);
482 logErrorDisconnect(h, m);
483 }
484 }
485 }
tom7ef8ff92014-09-17 13:08:06 -0700486 logErrorDisconnect(h, m);
487 }
488
489 @Override
490 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
491 throws IOException {
492 h.pendingPortStatusMsg.add(m);
493 }
494 },
495
496
497 /**
498 * We are waiting for a OFDescriptionStat message from the switch.
499 * Once we receive any stat message we try to parse it. If it's not
500 * a description stats message we disconnect. If its the expected
501 * description stats message, we:
502 * - use the switch driver to bind the switch and get an IOFSwitch instance
503 * - setup the IOFSwitch instance
504 * - add switch controller and send the initial role
505 * request to the switch.
506 * Next state: WAIT_INITIAL_ROLE
507 * In the typical case, where switches support role request messages
508 * the next state is where we expect the role reply message.
509 * In the special case that where the switch does not support any kind
510 * of role request messages, we don't send a role message, but we do
511 * request mastership from the registry service. This controller
512 * should become master once we hear back from the registry service.
513 * All following states will have a h.sw instance!
514 */
515 WAIT_DESCRIPTION_STAT_REPLY(false) {
516 @Override
517 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
518 throws SwitchStateException {
519 // Read description, if it has been updated
520 if (m.getStatsType() != OFStatsType.DESC) {
521 log.warn("Expecting Description stats but received stats "
522 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700523 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700524 return;
525 }
tom7ef8ff92014-09-17 13:08:06 -0700526 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700527 log.info("Received switch description reply {} from switch at {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700528 drep, h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700529 // Here is where we differentiate between different kinds of switches
530 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
531
532 h.sw.setOFVersion(h.ofVersion);
533 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700534 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100535 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700536 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700537 h.sw.setChannel(h);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700538// boolean success = h.sw.connectSwitch();
539//
540// if (!success) {
541// disconnectDuplicate(h);
542// return;
543// }
tom7ef8ff92014-09-17 13:08:06 -0700544 // set switch information
545
546
547
alshabib09d48be2014-10-03 15:43:33 -0700548 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800549 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700550 //Put switch in EQUAL mode until we hear back from the global registry
551 //log.debug("Setting new switch {} to EQUAL and sending Role request",
552 // h.sw.getStringId());
553 //h.sw.activateEqualSwitch();
554 //h.setSwitchRole(RoleState.EQUAL);
555
556 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700557 if (h.sw.isDriverHandshakeComplete()) {
558 if (!h.sw.connectSwitch()) {
559 disconnectDuplicate(h);
560 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800561 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700562 h.setState(ACTIVE);
563 } else {
564 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
565 }
tom7ef8ff92014-09-17 13:08:06 -0700566
567 }
568
569 @Override
570 void processOFError(OFChannelHandler h, OFErrorMsg m) {
571 logErrorDisconnect(h, m);
572 }
573
574 @Override
575 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
576 throws IOException, SwitchStateException {
577 illegalMessageReceived(h, m);
578 }
579
580 @Override
581 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
582 throws IOException {
583 h.pendingPortStatusMsg.add(m);
584 }
585 },
586
587
588 /**
589 * We are waiting for the respective switch driver to complete its
590 * configuration. Notice that we do not consider this to be part of the main
591 * switch-controller handshake. But we do consider it as a step that comes
592 * before we declare the switch as available to the controller.
593 * Next State: depends on the role of this controller for this switch - either
594 * MASTER or EQUAL.
595 */
596 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
597
598 @Override
599 void processOFError(OFChannelHandler h, OFErrorMsg m)
600 throws IOException {
601 // will never be called. We override processOFMessage
602 }
603
alshabibd7963912014-10-20 14:52:04 -0700604
605
tom7ef8ff92014-09-17 13:08:06 -0700606 @Override
607 void processOFMessage(OFChannelHandler h, OFMessage m)
608 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700609
610 if (h.sw.isDriverHandshakeComplete()) {
611 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700612 h.state.processOFMessage(h, m);
613 return;
alshabibd7963912014-10-20 14:52:04 -0700614
615 }
616
tom7ef8ff92014-09-17 13:08:06 -0700617 if (m.getType() == OFType.ECHO_REQUEST) {
618 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700619 } else if (m.getType() == OFType.ECHO_REPLY) {
620 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700621 } else if (m.getType() == OFType.ROLE_REPLY) {
622 h.sw.handleRole(m);
623 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800624 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700625 h.sw.processDriverHandshakeMessage(m);
626 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700627 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700628 }
629 }
630 } else {
631 if (m.getType() == OFType.EXPERIMENTER &&
632 ((OFExperimenter) m).getExperimenter() ==
633 RoleManager.NICIRA_EXPERIMENTER) {
634 h.sw.handleNiciraRole(m);
635 } else {
636 h.sw.processDriverHandshakeMessage(m);
637 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700638 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700639 }
640 }
641 }
642 }
643
644 @Override
645 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
646 throws IOException, SwitchStateException {
647 h.pendingPortStatusMsg.add(m);
648 }
alshabibd7963912014-10-20 14:52:04 -0700649
650 private void moveToActive(OFChannelHandler h) {
651 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800652 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700653 h.setState(ACTIVE);
654 if (!success) {
655 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700656 }
657 }
658
tom7ef8ff92014-09-17 13:08:06 -0700659 },
660
Jordi Ortiz91477b82016-11-29 15:22:50 +0100661 /**
662 * We are expecting a OF Multi Part Meter Features Stats Reply.
663 * Notice that this information is only available for switches running
664 * OpenFlow 1.3
665 */
666 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800667
668 @Override
669 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
670 throws IOException {
671 super.processOFEchoRequest(h, m);
672 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
673 log.info("{} did not respond to MeterFeaturesRequest on time, " +
674 "moving on without it.",
675 h.getSwitchInfoString());
676 h.sendHandshakeDescriptionStatsRequest();
677 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
678 }
679 }
680
Jordi Ortiz91477b82016-11-29 15:22:50 +0100681 @Override
682 void processOFError(OFChannelHandler h, OFErrorMsg m)
683 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800684 // Hardware switches may reply OFErrorMsg if meter is not supported
685 log.warn("Received OFError {}. It seems {} does not support Meter.",
686 m.getErrType().name(), Dpid.uri(h.thisdpid));
687 log.debug("{}", m);
688 h.sendHandshakeDescriptionStatsRequest();
689 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100690 }
691
692 @Override
693 void processOFStatisticsReply(OFChannelHandler h,
694 OFStatsReply m)
695 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800696 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100697 case METER_FEATURES:
698
699 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800700 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100701 log.info("Received meter features from {} with max meters: {}",
702 h.getSwitchInfoString(),
703 ofmfsr.getFeatures().getMaxMeter());
704 h.meterFeaturesReply = ofmfsr;
705 h.sendHandshakeDescriptionStatsRequest();
706 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
707 break;
708 default:
709 log.error("Unexpected OF Multi Part stats reply");
710 illegalMessageReceived(h, m);
711 break;
712 }
713 }
714
715 @Override
716 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
717 throws IOException, SwitchStateException {
718 illegalMessageReceived(h, m);
719 }
720
721 @Override
722 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
723 throws IOException {
724 h.pendingPortStatusMsg.add(m);
725 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800726
727 @Override
728 void processIdle(OFChannelHandler h) throws IOException {
729 log.info("{} did not respond to MeterFeaturesRequest, " +
730 "moving on without it.",
731 h.getSwitchInfoString());
732 h.sendHandshakeDescriptionStatsRequest();
733 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
734 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100735 },
736
tom7ef8ff92014-09-17 13:08:06 -0700737
738 /**
739 * This controller is in MASTER role for this switch. We enter this state
740 * after requesting and winning control from the global registry.
741 * The main handshake as well as the switch-driver sub-handshake
742 * is complete at this point.
743 * // XXX S reconsider below
744 * In the (near) future we may deterministically assign controllers to
745 * switches at startup.
746 * We only leave this state if the switch disconnects or
747 * if we send a role request for SLAVE /and/ receive the role reply for
748 * SLAVE.
749 */
750 ACTIVE(true) {
751 @Override
752 void processOFError(OFChannelHandler h, OFErrorMsg m)
753 throws IOException, SwitchStateException {
754 // if we get here, then the error message is for something else
755 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700756 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700757 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700758 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700759 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700760 // We are the master controller and the switch returned
761 // a permission error. This is a likely indicator that
762 // the switch thinks we are slave. Reassert our
763 // role
764 // FIXME: this could be really bad during role transitions
765 // if two controllers are master (even if its only for
766 // a brief period). We might need to see if these errors
767 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700768
tom7ef8ff92014-09-17 13:08:06 -0700769 h.sw.reassertRole();
770 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
771 ((OFFlowModFailedErrorMsg) m).getCode() ==
772 OFFlowModFailedCode.ALL_TABLES_FULL) {
773 h.sw.setTableFull(true);
774 } else {
775 logError(h, m);
776 }
777 h.dispatchMessage(m);
778 }
779
780 @Override
781 void processOFStatisticsReply(OFChannelHandler h,
782 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700783 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
Saurav Das45f48152018-01-18 12:07:33 -0800784 log.debug("Received port desc message from {}: {}",
785 h.sw.getDpid(),
786 ((OFPortDescStatsReply) m).getEntries());
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700787 h.sw.setPortDescReply((OFPortDescStatsReply) m);
788 }
tom7ef8ff92014-09-17 13:08:06 -0700789 h.dispatchMessage(m);
790 }
791
792 @Override
793 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
794 throws SwitchStateException {
795 h.sw.handleNiciraRole(m);
796 }
797
798 @Override
799 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
800 throws SwitchStateException {
801 h.sw.handleRole(m);
802 }
803
804 @Override
805 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
806 throws SwitchStateException {
807 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800808 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700809 }
810
811 @Override
812 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700813// OFPacketOut out =
814// h.sw.factory().buildPacketOut()
815// .setXid(m.getXid())
816// .setBufferId(m.getBufferId()).build();
817// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700818 h.dispatchMessage(m);
819 }
820
821 @Override
822 void processOFFlowRemoved(OFChannelHandler h,
823 OFFlowRemoved m) {
824 h.dispatchMessage(m);
825 }
826
827 @Override
828 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
829 h.dispatchMessage(m);
830 }
831
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700832 @Override
833 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700834 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700835 h.dispatchMessage(m);
836 }
837
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800838 @Override
839 void processIdle(OFChannelHandler h) throws IOException {
840 log.info("{} idle", h.getSwitchInfoString());
841 }
842
tom7ef8ff92014-09-17 13:08:06 -0700843 };
844
845 private final boolean handshakeComplete;
846 ChannelState(boolean handshakeComplete) {
847 this.handshakeComplete = handshakeComplete;
848 }
849
850 /**
851 * Is this a state in which the handshake has completed?
852 * @return true if the handshake is complete
853 */
854 public boolean isHandshakeComplete() {
855 return handshakeComplete;
856 }
857
858 /**
859 * Get a string specifying the switch connection, state, and
860 * message received. To be used as message for SwitchStateException
861 * or log messages
862 * @param h The channel handler (to get switch information_
863 * @param m The OFMessage that has just been received
864 * @param details A string giving more details about the exact nature
865 * of the problem.
866 * @return display string
867 */
868 // needs to be protected because enum members are actually subclasses
869 protected String getSwitchStateMessage(OFChannelHandler h,
870 OFMessage m,
871 String details) {
872 return String.format("Switch: [%s], State: [%s], received: [%s]"
873 + ", details: %s",
874 h.getSwitchInfoString(),
875 this.toString(),
876 m.getType().toString(),
877 details);
878 }
879
880 /**
881 * We have an OFMessage we didn't expect given the current state and
882 * we want to treat this as an error.
883 * We currently throw an exception that will terminate the connection
884 * However, we could be more forgiving
885 * @param h the channel handler that received the message
886 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700887 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700888 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700889 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700890 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
891 throws SwitchStateException {
892 String msg = getSwitchStateMessage(h, m,
893 "Switch should never send this message in the current state");
894 throw new SwitchStateException(msg);
895
896 }
897
898 /**
899 * We have an OFMessage we didn't expect given the current state and
900 * we want to ignore the message.
901 * @param h the channel handler the received the message
902 * @param m the message
903 */
904 protected void unhandledMessageReceived(OFChannelHandler h,
905 OFMessage m) {
906 if (log.isDebugEnabled()) {
907 String msg = getSwitchStateMessage(h, m,
908 "Ignoring unexpected message");
909 log.debug(msg);
910 }
911 }
912
913 /**
914 * Log an OpenFlow error message from a switch.
915 * @param h The switch that sent the error
916 * @param error The error message
917 */
918 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700919 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700920 error,
921 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800922 this);
tom7ef8ff92014-09-17 13:08:06 -0700923 }
924
925 /**
926 * Log an OpenFlow error message from a switch and disconnect the
927 * channel.
928 *
929 * @param h the IO channel for this switch.
930 * @param error The error message
931 */
932 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
933 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700934 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700935 h.channel.disconnect();
936 }
937
938 /**
939 * log an error message for a duplicate dpid and disconnect this channel.
940 * @param h the IO channel for this switch.
941 */
942 protected void disconnectDuplicate(OFChannelHandler h) {
943 log.error("Duplicated dpid or incompleted cleanup - "
944 + "disconnecting channel {}", h.getSwitchInfoString());
945 h.duplicateDpidFound = Boolean.TRUE;
946 h.channel.disconnect();
947 }
948
949
950
951 /**
952 * Handles all pending port status messages before a switch is declared
953 * activated in MASTER or EQUAL role. Note that since this handling
954 * precedes the activation (and therefore notification to IOFSwitchListerners)
955 * the changes to ports will already be visible once the switch is
956 * activated. As a result, no notifications are sent out for these
957 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700958 *
959 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700960 */
961 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
962 try {
963 handlePendingPortStatusMessages(h, 0);
964 } catch (SwitchStateException e) {
965 log.error(e.getMessage());
966 }
967 }
968
969 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
970 throws SwitchStateException {
971 if (h.sw == null) {
972 String msg = "State machine error: switch is null. Should never " +
973 "happen";
974 throw new SwitchStateException(msg);
975 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800976 log.info("Processing {} pending port status messages for {}",
977 h.pendingPortStatusMsg.size(), h.sw.getStringId());
978
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800979 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700980 for (OFPortStatus ps: h.pendingPortStatusMsg) {
981 temp.add(ps);
982 handlePortStatusMessage(h, ps, false);
983 }
tom7ef8ff92014-09-17 13:08:06 -0700984 // expensive but ok - we don't expect too many port-status messages
985 // note that we cannot use clear(), because of the reasons below
986 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800987 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700988 // the iterator above takes a snapshot of the list - so while we were
989 // dealing with the pending port-status messages, we could have received
990 // newer ones. Handle them recursively, but break the recursion after
991 // five steps to avoid an attack.
992 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
993 handlePendingPortStatusMessages(h, index);
994 }
995 }
996
997 /**
998 * Handle a port status message.
999 *
1000 * Handle a port status message by updating the port maps in the
1001 * IOFSwitch instance and notifying Controller about the change so
1002 * it can dispatch a switch update.
1003 *
1004 * @param h The OFChannelHhandler that received the message
1005 * @param m The PortStatus message we received
1006 * @param doNotify if true switch port changed events will be
1007 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001008 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001009 *
1010 */
1011 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1012 boolean doNotify) throws SwitchStateException {
1013 if (h.sw == null) {
1014 String msg = getSwitchStateMessage(h, m,
1015 "State machine error: switch is null. Should never " +
1016 "happen");
1017 throw new SwitchStateException(msg);
1018 }
Saurav Dasbd071d82018-01-09 17:38:44 -08001019 log.info("Received port status message from {}/{}: {}",
1020 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001021
1022 h.sw.handleMessage(m);
1023 }
1024
1025
1026 /**
1027 * Process an OF message received on the channel and
1028 * update state accordingly.
1029 *
1030 * The main "event" of the state machine. Process the received message,
1031 * send follow up message if required and update state if required.
1032 *
1033 * Switches on the message type and calls more specific event handlers
1034 * for each individual OF message type. If we receive a message that
1035 * is supposed to be sent from a controller to a switch we throw
1036 * a SwitchStateExeption.
1037 *
1038 * The more specific handlers can also throw SwitchStateExceptions
1039 *
1040 * @param h The OFChannelHandler that received the message
1041 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001042 * @throws SwitchStateException if the switch is not bound to the channel
1043 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001044 */
1045 void processOFMessage(OFChannelHandler h, OFMessage m)
1046 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001047 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001048 case HELLO:
1049 processOFHello(h, (OFHello) m);
1050 break;
1051 case BARRIER_REPLY:
1052 processOFBarrierReply(h, (OFBarrierReply) m);
1053 break;
1054 case ECHO_REPLY:
1055 processOFEchoReply(h, (OFEchoReply) m);
1056 break;
1057 case ECHO_REQUEST:
1058 processOFEchoRequest(h, (OFEchoRequest) m);
1059 break;
1060 case ERROR:
1061 processOFError(h, (OFErrorMsg) m);
1062 break;
1063 case FEATURES_REPLY:
1064 processOFFeaturesReply(h, (OFFeaturesReply) m);
1065 break;
1066 case FLOW_REMOVED:
1067 processOFFlowRemoved(h, (OFFlowRemoved) m);
1068 break;
1069 case GET_CONFIG_REPLY:
1070 processOFGetConfigReply(h, (OFGetConfigReply) m);
1071 break;
1072 case PACKET_IN:
1073 processOFPacketIn(h, (OFPacketIn) m);
1074 break;
1075 case PORT_STATUS:
1076 processOFPortStatus(h, (OFPortStatus) m);
1077 break;
1078 case QUEUE_GET_CONFIG_REPLY:
1079 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1080 break;
1081 case STATS_REPLY: // multipart_reply in 1.3
1082 processOFStatisticsReply(h, (OFStatsReply) m);
1083 break;
1084 case EXPERIMENTER:
1085 processOFExperimenter(h, (OFExperimenter) m);
1086 break;
1087 case ROLE_REPLY:
1088 processOFRoleReply(h, (OFRoleReply) m);
1089 break;
1090 case GET_ASYNC_REPLY:
1091 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1092 break;
1093
1094 // The following messages are sent to switches. The controller
1095 // should never receive them
1096 case SET_CONFIG:
1097 case GET_CONFIG_REQUEST:
1098 case PACKET_OUT:
1099 case PORT_MOD:
1100 case QUEUE_GET_CONFIG_REQUEST:
1101 case BARRIER_REQUEST:
1102 case STATS_REQUEST: // multipart request in 1.3
1103 case FEATURES_REQUEST:
1104 case FLOW_MOD:
1105 case GROUP_MOD:
1106 case TABLE_MOD:
1107 case GET_ASYNC_REQUEST:
1108 case SET_ASYNC:
1109 case METER_MOD:
1110 default:
1111 illegalMessageReceived(h, m);
1112 break;
1113 }
1114 }
1115
1116 /*-----------------------------------------------------------------
1117 * Default implementation for message handlers in any state.
1118 *
1119 * Individual states must override these if they want a behavior
1120 * that differs from the default.
1121 *
1122 * In general, these handlers simply ignore the message and do
1123 * nothing.
1124 *
1125 * There are some exceptions though, since some messages really
1126 * are handled the same way in every state (e.g., ECHO_REQUST) or
1127 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1128 -----------------------------------------------------------------*/
1129
1130 void processOFHello(OFChannelHandler h, OFHello m)
1131 throws IOException, SwitchStateException {
1132 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001133 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001134 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001135 }
1136
1137 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1138 throws IOException {
1139 // Silently ignore.
1140 }
1141
1142 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1143 throws IOException {
1144 if (h.ofVersion == null) {
1145 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001146 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001147 return;
1148 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001149 OFEchoReply reply = h.factory
1150 .buildEchoReply()
1151 .setXid(m.getXid())
1152 .setData(m.getData())
1153 .build();
1154 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001155 }
1156
1157 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1158 throws IOException {
1159 // Do nothing with EchoReplies !!
1160 }
1161
1162 // no default implementation for OFError
1163 // every state must override it
1164 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1165 throws IOException, SwitchStateException;
1166
1167
1168 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1169 throws IOException, SwitchStateException {
1170 unhandledMessageReceived(h, m);
1171 }
1172
1173 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1174 throws IOException {
1175 unhandledMessageReceived(h, m);
1176 }
1177
1178 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1179 throws IOException, SwitchStateException {
1180 // we only expect config replies in the WAIT_CONFIG_REPLY state
1181 illegalMessageReceived(h, m);
1182 }
1183
1184 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1185 throws IOException {
1186 unhandledMessageReceived(h, m);
1187 }
1188
1189 // no default implementation. Every state needs to handle it.
1190 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1191 throws IOException, SwitchStateException;
1192
1193 void processOFQueueGetConfigReply(OFChannelHandler h,
1194 OFQueueGetConfigReply m)
1195 throws IOException {
1196 unhandledMessageReceived(h, m);
1197 }
1198
1199 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1200 throws IOException, SwitchStateException {
1201 unhandledMessageReceived(h, m);
1202 }
1203
1204 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1205 throws IOException, SwitchStateException {
1206 // TODO: it might make sense to parse the vendor message here
1207 // into the known vendor messages we support and then call more
1208 // specific event handlers
1209 unhandledMessageReceived(h, m);
1210 }
1211
1212 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1213 throws SwitchStateException, IOException {
1214 unhandledMessageReceived(h, m);
1215 }
1216
1217 void processOFGetAsyncReply(OFChannelHandler h,
1218 OFAsyncGetReply m) {
1219 unhandledMessageReceived(h, m);
1220 }
1221
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001222 void processIdle(OFChannelHandler h) throws IOException {
1223 // disconnect channel which did no complete handshake
1224 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1225 h.channel.disconnect();
1226 }
tom7ef8ff92014-09-17 13:08:06 -07001227 }
1228
1229
1230
1231 //*************************
1232 // Channel handler methods
1233 //*************************
1234
1235 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001236 public void channelActive(ChannelHandlerContext ctx)
1237 throws Exception {
1238
1239 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001240 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001241 channel.remoteAddress());
1242
1243 SocketAddress address = channel.remoteAddress();
1244 if (address instanceof InetSocketAddress) {
1245 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1246 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1247 if (ipAddress.isIp4()) {
1248 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1249 } else {
1250 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1251 }
1252 } else {
1253 channelId = channel.toString();
1254 }
1255
1256 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1257
alshabib70fc7fb2015-01-06 11:04:29 -08001258 /*
1259 hack to wait for the switch to tell us what it's
1260 max version is. This is not spec compliant and should
1261 be removed as soon as switches behave better.
1262 */
1263 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001264 setState(ChannelState.WAIT_HELLO);
1265 }
1266
1267 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001268 public void channelInactive(ChannelHandlerContext ctx)
1269 throws Exception {
1270
tom7ef8ff92014-09-17 13:08:06 -07001271 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001272 getSwitchInfoString());
1273
1274 if (dispatcher != null) {
Harold Huang828cd592017-11-04 10:46:04 +08001275 dispatcher.shutdownNow();
Thomas Vachuskad75684a2018-01-03 09:04:47 -08001276 dispatcher = null;
tom7ef8ff92014-09-17 13:08:06 -07001277 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001278
1279 if (thisdpid != 0) {
1280 if (!duplicateDpidFound) {
1281 // if the disconnected switch (on this ChannelHandler)
1282 // was not one with a duplicate-dpid, it is safe to remove all
1283 // state for it at the controller. Notice that if the disconnected
1284 // switch was a duplicate-dpid, calling the method below would clear
1285 // all state for the original switch (with the same dpid),
1286 // which we obviously don't want.
1287 log.info("{}:removal called", getSwitchInfoString());
1288 if (sw != null) {
1289 sw.removeConnectedSwitch();
1290 }
1291 } else {
1292 // A duplicate was disconnected on this ChannelHandler,
1293 // this is the same switch reconnecting, but the original state was
1294 // not cleaned up - XXX check liveness of original ChannelHandler
1295 log.info("{}:duplicate found", getSwitchInfoString());
1296 duplicateDpidFound = Boolean.FALSE;
1297 }
1298 } else {
1299 log.warn("no dpid in channelHandler registered for "
1300 + "disconnected switch {}", getSwitchInfoString());
1301 }
tom7ef8ff92014-09-17 13:08:06 -07001302 }
1303
1304 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001305 public void exceptionCaught(ChannelHandlerContext ctx,
1306 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001307 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001308
1309 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001310 // switch timeout
1311 log.error("Disconnecting switch {} due to read timeout",
1312 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001313 ctx.channel().close();
1314 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001315 log.error("Disconnecting switch {}: failed to complete handshake",
1316 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001317 ctx.channel().close();
1318 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001319 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001320 } else if (cause instanceof IOException) {
1321 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1322 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001323 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001324 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001325 if (log.isDebugEnabled()) {
1326 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001327 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001328 }
tom7ef8ff92014-09-17 13:08:06 -07001329 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001330 ctx.channel().close();
1331 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001332 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001333 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001334 if (log.isDebugEnabled()) {
1335 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001336 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001337 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001338 ctx.channel().close();
1339 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001340 log.error("Disconnecting switch "
1341 + getSwitchInfoString() +
1342 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001343 cause);
1344 ctx.channel().close();
1345 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001346 log.warn("Could not process message: queue full");
1347 } else {
1348 log.error("Error while processing message from switch "
1349 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001350 + "state " + this.state, cause);
1351 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001352 }
1353 }
1354
1355 @Override
1356 public String toString() {
1357 return getSwitchInfoString();
1358 }
1359
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001360 protected void channelIdle(ChannelHandlerContext ctx,
1361 IdleStateEvent e)
tom7ef8ff92014-09-17 13:08:06 -07001362 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001363 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001364 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001365 ctx.channel());
1366 ctx.write(Collections.singletonList(m), ctx.voidPromise());
tom7ef8ff92014-09-17 13:08:06 -07001367 // XXX S some problems here -- echo request has no transaction id, and
1368 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001369 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001370 }
1371
1372 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001373 public void userEventTriggered(ChannelHandlerContext ctx,
1374 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001375 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001376
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001377 if (evt instanceof IdleStateEvent) {
1378 channelIdle(ctx, (IdleStateEvent) evt);
1379 }
tom7ef8ff92014-09-17 13:08:06 -07001380
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001381 super.userEventTriggered(ctx, evt);
1382 }
1383
1384 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1385 @Override
1386 public void channelRead(ChannelHandlerContext ctx,
1387 Object msg) throws Exception {
1388
1389 boolean release = true;
1390 try {
1391 if (msg instanceof OFMessage) {
1392 // channelRead0 inlined
1393 state.processOFMessage(this, (OFMessage) msg);
1394 } else {
1395 release = false;
1396 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001397 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001398 } finally {
1399 if (release) {
1400 ReferenceCountUtil.release(msg);
1401 }
tom7ef8ff92014-09-17 13:08:06 -07001402 }
1403 }
1404
1405
1406
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001407
tom7ef8ff92014-09-17 13:08:06 -07001408 //*************************
1409 // Channel utility methods
1410 //*************************
1411
1412 /**
1413 * Is this a state in which the handshake has completed?
1414 * @return true if the handshake is complete
1415 */
1416 public boolean isHandshakeComplete() {
1417 return this.state.isHandshakeComplete();
1418 }
1419
1420 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001421
1422 if (dispatchBacklog.isEmpty()) {
1423 if (!dispatchQueue.offer(m)) {
1424 // queue full
1425 channel.config().setAutoRead(false);
1426 // put it on the head of backlog
1427 dispatchBacklog.addFirst(m);
1428 return;
1429 }
1430 } else {
1431 dispatchBacklog.addLast(m);
1432 }
1433
1434 while (!dispatchBacklog.isEmpty()) {
1435 OFMessage msg = dispatchBacklog.pop();
1436
1437 if (!dispatchQueue.offer(msg)) {
1438 // queue full
1439 channel.config().setAutoRead(false);
1440 // put it back to the head of backlog
1441 dispatchBacklog.addFirst(msg);
1442 return;
1443 }
1444 }
1445
1446
1447 if (dispatcherHandle.isDone()) {
1448 // dispatcher terminated for some reason, restart
1449
Yuta HIGUCHIfbd9ae92018-01-24 23:39:06 -08001450 dispatcherHandle = dispatcher.submit((Runnable) () -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001451 try {
1452 List<OFMessage> msgs = new ArrayList<>();
1453 for (;;) {
1454 // wait for new message
1455 OFMessage msg = dispatchQueue.take();
1456 sw.handleMessage(msg);
1457
1458 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1459 if (!channel.config().isAutoRead()) {
1460 channel.config().setAutoRead(true);
1461 }
1462 msgs.forEach(sw::handleMessage);
1463 msgs.clear();
1464 }
1465
1466 if (!channel.config().isAutoRead()) {
1467 channel.config().setAutoRead(true);
1468 }
1469 }
1470 } catch (InterruptedException e) {
1471 Thread.currentThread().interrupt();
1472 // interrupted. gracefully shutting down
1473 return;
1474 }
1475
1476 });
1477 }
tom7ef8ff92014-09-17 13:08:06 -07001478 }
1479
1480 /**
1481 * Return a string describing this switch based on the already available
1482 * information (DPID and/or remote socket).
1483 * @return display string
1484 */
1485 private String getSwitchInfoString() {
1486 if (sw != null) {
1487 return sw.toString();
1488 }
1489 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001490 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001491 channelString = "?";
1492 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001493 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001494 }
1495 String dpidString;
1496 if (featuresReply == null) {
1497 dpidString = "?";
1498 } else {
1499 dpidString = featuresReply.getDatapathId().toString();
1500 }
1501 return String.format("[%s DPID[%s]]", channelString, dpidString);
1502 }
1503
1504 /**
1505 * Update the channels state. Only called from the state machine.
1506 * TODO: enforce restricted state transitions
1507 * @param state
1508 */
1509 private void setState(ChannelState state) {
1510 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001511 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001512 }
1513
1514 /**
1515 * Send hello message to the switch using the handshake transactions ids.
1516 * @throws IOException
1517 */
1518 private void sendHandshakeHelloMessage() throws IOException {
1519 // The OF protocol requires us to start things off by sending the highest
1520 // version of the protocol supported.
1521
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001522 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001523 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001524 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1525 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001526 (0b1 << OFVersion.OF_14.getWireVersion()) |
1527 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001528 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1529 OFHelloElem hem = OFFactories.getFactory(version)
1530 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001531 .setBitmaps(Collections.singletonList(bitmap))
1532 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001533 OFMessage.Builder mb = OFFactories.getFactory(version)
1534 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001535 .setXid(this.handshakeTransactionIds--)
1536 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001537 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1538 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001539 }
1540
1541 /**
1542 * Send featuresRequest msg to the switch using the handshake transactions ids.
1543 * @throws IOException
1544 */
1545 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001546 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001547 OFMessage m = factory.buildFeaturesRequest()
1548 .setXid(this.handshakeTransactionIds--)
1549 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001550 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001551 }
1552
1553 /**
1554 * Send the configuration requests to tell the switch we want full
1555 * packets.
1556 * @throws IOException
1557 */
1558 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001559 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001560 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001561
1562 // Ensure we receive the full packet via PacketIn
1563 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001564 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001565 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001566 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001567 OFSetConfig sc = factory
1568 .buildSetConfig()
1569 .setMissSendLen((short) 0xffff)
1570 .setXid(this.handshakeTransactionIds--)
1571 .build();
1572 msglist.add(sc);
1573 }
tom7ef8ff92014-09-17 13:08:06 -07001574
1575 // Barrier
1576 OFBarrierRequest br = factory
1577 .buildBarrierRequest()
1578 .setXid(this.handshakeTransactionIds--)
1579 .build();
1580 msglist.add(br);
1581
1582 // Verify (need barrier?)
1583 OFGetConfigRequest gcr = factory
1584 .buildGetConfigRequest()
1585 .setXid(this.handshakeTransactionIds--)
1586 .build();
1587 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001588 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001589 }
1590
1591 /**
1592 * send a description state request.
1593 * @throws IOException
1594 */
1595 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1596 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001597 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001598 OFDescStatsRequest dreq = factory
1599 .buildDescStatsRequest()
1600 .setXid(handshakeTransactionIds--)
1601 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001602 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001603 }
1604
Jordi Ortiz91477b82016-11-29 15:22:50 +01001605 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001606 * send a meter features request.
1607 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001608 * @throws IOException
1609 */
1610 private void sendMeterFeaturesRequest() throws IOException {
1611 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001612 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001613 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001614 OFMeterFeaturesStatsRequest mfreq = factory
1615 .buildMeterFeaturesStatsRequest()
1616 .setXid(handshakeTransactionIds--)
1617 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001618 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001619 }
1620
tom7ef8ff92014-09-17 13:08:06 -07001621 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001622 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001623 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001624 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001625 .buildPortDescStatsRequest()
1626 .setXid(handshakeTransactionIds--)
1627 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001628 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001629 }
1630
1631 ChannelState getStateForTesting() {
1632 return state;
1633 }
1634
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001635
1636 @Override
1637 public boolean isActive() {
1638 if (channel != null) {
1639 return channel.isActive();
1640 }
1641 return false;
1642 }
1643
1644 @Override
1645 public void closeSession() {
1646 if (channel != null) {
1647 channel.close();
1648 }
1649 }
1650
1651 @Override
1652 public boolean sendMsg(Iterable<OFMessage> msgs) {
1653 if (channel.isActive()) {
Laszlo Pappb68fe7e2017-11-24 17:06:59 +00001654 if (log.isTraceEnabled()) {
1655 log.trace("Sending messages for switch {} via openflow channel: {}", getSwitchInfoString(), msgs);
1656 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001657 channel.writeAndFlush(msgs, channel.voidPromise());
1658 return true;
1659 } else {
1660 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1661 getSwitchInfoString(), msgs);
1662 return false;
1663 }
1664 }
1665
1666 @Override
1667 public CharSequence sessionInfo() {
1668 return channelId;
1669 }
1670
tom7ef8ff92014-09-17 13:08:06 -07001671}