blob: b2d77cf9db50c9d5ef813867007de894dbce8587 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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
tom7ef8ff92014-09-17 13:08:06 -0700383 log.info("Received port desc reply for switch at {}",
384 h.getSwitchInfoString());
385 try {
386 h.sendHandshakeSetConfig();
387 } catch (IOException e) {
388 log.error("Unable to send setConfig after PortDescReply. "
389 + "Error: {}", e.getMessage());
390 }
391 h.setState(WAIT_CONFIG_REPLY);
392 }
393
394 @Override
395 void processOFError(OFChannelHandler h, OFErrorMsg m)
396 throws IOException, SwitchStateException {
397 logErrorDisconnect(h, m);
398
399 }
400
401 @Override
402 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
403 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800404 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700405
406 }
407 },
408
409 /**
410 * We are waiting for a config reply message. Once we receive it
411 * we send a DescriptionStatsRequest to the switch.
412 * Next state: WAIT_DESCRIPTION_STAT_REPLY
413 */
414 WAIT_CONFIG_REPLY(false) {
415 @Override
416 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
417 throws IOException {
418 if (m.getMissSendLen() == 0xffff) {
419 log.trace("Config Reply from switch {} confirms "
420 + "miss length set to 0xffff",
421 h.getSwitchInfoString());
422 } else {
423 // FIXME: we can't really deal with switches that don't send
424 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800425 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700426 + "miss length set to {}",
427 h.getSwitchInfoString(),
428 m.getMissSendLen());
429 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100430
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800431 nextState(h);
432 }
433
434 /**
435 * Transition to next state based on OF version.
436 *
437 * @param h current channel handler
438 * @throws IOException
439 */
440 private void nextState(OFChannelHandler h) throws IOException {
441 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100442 // Meters were introduced in OpenFlow 1.3
443 h.sendMeterFeaturesRequest();
444 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800445 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100446 h.sendHandshakeDescriptionStatsRequest();
447 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
448 }
tom7ef8ff92014-09-17 13:08:06 -0700449 }
450
451 @Override
452 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
453 // do nothing;
454 }
455
456 @Override
457 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
458 throws IOException, SwitchStateException {
459 illegalMessageReceived(h, m);
460 }
461 @Override
462 void processOFStatisticsReply(OFChannelHandler h,
463 OFStatsReply m)
464 throws IOException, SwitchStateException {
465 log.error("Received multipart(stats) message sub-type {}",
466 m.getStatsType());
467 illegalMessageReceived(h, m);
468 }
469
470 @Override
471 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800472 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
473 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
474 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
475 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
476 try {
477 nextState(h);
478 return;
479 } catch (IOException e) {
480 log.error("Exception thrown transitioning to next", e);
481 logErrorDisconnect(h, m);
482 }
483 }
484 }
tom7ef8ff92014-09-17 13:08:06 -0700485 logErrorDisconnect(h, m);
486 }
487
488 @Override
489 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
490 throws IOException {
491 h.pendingPortStatusMsg.add(m);
492 }
493 },
494
495
496 /**
497 * We are waiting for a OFDescriptionStat message from the switch.
498 * Once we receive any stat message we try to parse it. If it's not
499 * a description stats message we disconnect. If its the expected
500 * description stats message, we:
501 * - use the switch driver to bind the switch and get an IOFSwitch instance
502 * - setup the IOFSwitch instance
503 * - add switch controller and send the initial role
504 * request to the switch.
505 * Next state: WAIT_INITIAL_ROLE
506 * In the typical case, where switches support role request messages
507 * the next state is where we expect the role reply message.
508 * In the special case that where the switch does not support any kind
509 * of role request messages, we don't send a role message, but we do
510 * request mastership from the registry service. This controller
511 * should become master once we hear back from the registry service.
512 * All following states will have a h.sw instance!
513 */
514 WAIT_DESCRIPTION_STAT_REPLY(false) {
515 @Override
516 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
517 throws SwitchStateException {
518 // Read description, if it has been updated
519 if (m.getStatsType() != OFStatsType.DESC) {
520 log.warn("Expecting Description stats but received stats "
521 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700522 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700523 return;
524 }
tom7ef8ff92014-09-17 13:08:06 -0700525 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700526 log.info("Received switch description reply {} from switch at {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700527 drep, h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700528 // Here is where we differentiate between different kinds of switches
529 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
530
531 h.sw.setOFVersion(h.ofVersion);
532 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700533 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100534 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700535 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700536 h.sw.setChannel(h);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700537// boolean success = h.sw.connectSwitch();
538//
539// if (!success) {
540// disconnectDuplicate(h);
541// return;
542// }
tom7ef8ff92014-09-17 13:08:06 -0700543 // set switch information
544
545
546
alshabib09d48be2014-10-03 15:43:33 -0700547 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800548 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700549 //Put switch in EQUAL mode until we hear back from the global registry
550 //log.debug("Setting new switch {} to EQUAL and sending Role request",
551 // h.sw.getStringId());
552 //h.sw.activateEqualSwitch();
553 //h.setSwitchRole(RoleState.EQUAL);
554
555 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700556 if (h.sw.isDriverHandshakeComplete()) {
557 if (!h.sw.connectSwitch()) {
558 disconnectDuplicate(h);
559 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800560 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700561 h.setState(ACTIVE);
562 } else {
563 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
564 }
tom7ef8ff92014-09-17 13:08:06 -0700565
566 }
567
568 @Override
569 void processOFError(OFChannelHandler h, OFErrorMsg m) {
570 logErrorDisconnect(h, m);
571 }
572
573 @Override
574 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
575 throws IOException, SwitchStateException {
576 illegalMessageReceived(h, m);
577 }
578
579 @Override
580 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
581 throws IOException {
582 h.pendingPortStatusMsg.add(m);
583 }
584 },
585
586
587 /**
588 * We are waiting for the respective switch driver to complete its
589 * configuration. Notice that we do not consider this to be part of the main
590 * switch-controller handshake. But we do consider it as a step that comes
591 * before we declare the switch as available to the controller.
592 * Next State: depends on the role of this controller for this switch - either
593 * MASTER or EQUAL.
594 */
595 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
596
597 @Override
598 void processOFError(OFChannelHandler h, OFErrorMsg m)
599 throws IOException {
600 // will never be called. We override processOFMessage
601 }
602
alshabibd7963912014-10-20 14:52:04 -0700603
604
tom7ef8ff92014-09-17 13:08:06 -0700605 @Override
606 void processOFMessage(OFChannelHandler h, OFMessage m)
607 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700608
609 if (h.sw.isDriverHandshakeComplete()) {
610 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700611 h.state.processOFMessage(h, m);
612 return;
alshabibd7963912014-10-20 14:52:04 -0700613
614 }
615
tom7ef8ff92014-09-17 13:08:06 -0700616 if (m.getType() == OFType.ECHO_REQUEST) {
617 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700618 } else if (m.getType() == OFType.ECHO_REPLY) {
619 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700620 } else if (m.getType() == OFType.ROLE_REPLY) {
621 h.sw.handleRole(m);
622 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800623 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700624 h.sw.processDriverHandshakeMessage(m);
625 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700626 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700627 }
628 }
629 } else {
630 if (m.getType() == OFType.EXPERIMENTER &&
631 ((OFExperimenter) m).getExperimenter() ==
632 RoleManager.NICIRA_EXPERIMENTER) {
633 h.sw.handleNiciraRole(m);
634 } else {
635 h.sw.processDriverHandshakeMessage(m);
636 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700637 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700638 }
639 }
640 }
641 }
642
643 @Override
644 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
645 throws IOException, SwitchStateException {
646 h.pendingPortStatusMsg.add(m);
647 }
alshabibd7963912014-10-20 14:52:04 -0700648
649 private void moveToActive(OFChannelHandler h) {
650 boolean success = h.sw.connectSwitch();
Thomas Vachuska39274462014-12-02 13:23:50 -0800651 handlePendingPortStatusMessages(h);
alshabibd7963912014-10-20 14:52:04 -0700652 h.setState(ACTIVE);
653 if (!success) {
654 disconnectDuplicate(h);
alshabibd7963912014-10-20 14:52:04 -0700655 }
656 }
657
tom7ef8ff92014-09-17 13:08:06 -0700658 },
659
Jordi Ortiz91477b82016-11-29 15:22:50 +0100660 /**
661 * We are expecting a OF Multi Part Meter Features Stats Reply.
662 * Notice that this information is only available for switches running
663 * OpenFlow 1.3
664 */
665 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800666
667 @Override
668 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
669 throws IOException {
670 super.processOFEchoRequest(h, m);
671 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
672 log.info("{} did not respond to MeterFeaturesRequest on time, " +
673 "moving on without it.",
674 h.getSwitchInfoString());
675 h.sendHandshakeDescriptionStatsRequest();
676 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
677 }
678 }
679
Jordi Ortiz91477b82016-11-29 15:22:50 +0100680 @Override
681 void processOFError(OFChannelHandler h, OFErrorMsg m)
682 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800683 // Hardware switches may reply OFErrorMsg if meter is not supported
684 log.warn("Received OFError {}. It seems {} does not support Meter.",
685 m.getErrType().name(), Dpid.uri(h.thisdpid));
686 log.debug("{}", m);
687 h.sendHandshakeDescriptionStatsRequest();
688 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100689 }
690
691 @Override
692 void processOFStatisticsReply(OFChannelHandler h,
693 OFStatsReply m)
694 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800695 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100696 case METER_FEATURES:
697
698 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800699 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100700 log.info("Received meter features from {} with max meters: {}",
701 h.getSwitchInfoString(),
702 ofmfsr.getFeatures().getMaxMeter());
703 h.meterFeaturesReply = ofmfsr;
704 h.sendHandshakeDescriptionStatsRequest();
705 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
706 break;
707 default:
708 log.error("Unexpected OF Multi Part stats reply");
709 illegalMessageReceived(h, m);
710 break;
711 }
712 }
713
714 @Override
715 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
716 throws IOException, SwitchStateException {
717 illegalMessageReceived(h, m);
718 }
719
720 @Override
721 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
722 throws IOException {
723 h.pendingPortStatusMsg.add(m);
724 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800725
726 @Override
727 void processIdle(OFChannelHandler h) throws IOException {
728 log.info("{} did not respond to MeterFeaturesRequest, " +
729 "moving on without it.",
730 h.getSwitchInfoString());
731 h.sendHandshakeDescriptionStatsRequest();
732 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
733 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100734 },
735
tom7ef8ff92014-09-17 13:08:06 -0700736
737 /**
738 * This controller is in MASTER role for this switch. We enter this state
739 * after requesting and winning control from the global registry.
740 * The main handshake as well as the switch-driver sub-handshake
741 * is complete at this point.
742 * // XXX S reconsider below
743 * In the (near) future we may deterministically assign controllers to
744 * switches at startup.
745 * We only leave this state if the switch disconnects or
746 * if we send a role request for SLAVE /and/ receive the role reply for
747 * SLAVE.
748 */
749 ACTIVE(true) {
750 @Override
751 void processOFError(OFChannelHandler h, OFErrorMsg m)
752 throws IOException, SwitchStateException {
753 // if we get here, then the error message is for something else
754 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Ray Milkey30d19652016-09-06 12:09:46 -0700755 (((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700756 OFBadRequestCode.EPERM ||
tom7ef8ff92014-09-17 13:08:06 -0700757 ((OFBadRequestErrorMsg) m).getCode() ==
Charles Chan9d7465e2016-09-09 19:02:34 -0700758 OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700759 // We are the master controller and the switch returned
760 // a permission error. This is a likely indicator that
761 // the switch thinks we are slave. Reassert our
762 // role
763 // FIXME: this could be really bad during role transitions
764 // if two controllers are master (even if its only for
765 // a brief period). We might need to see if these errors
766 // persist before we reassert
alshabib339a3d92014-09-26 17:54:32 -0700767
tom7ef8ff92014-09-17 13:08:06 -0700768 h.sw.reassertRole();
769 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
770 ((OFFlowModFailedErrorMsg) m).getCode() ==
771 OFFlowModFailedCode.ALL_TABLES_FULL) {
772 h.sw.setTableFull(true);
773 } else {
774 logError(h, m);
775 }
776 h.dispatchMessage(m);
777 }
778
779 @Override
780 void processOFStatisticsReply(OFChannelHandler h,
781 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700782 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
783 h.sw.setPortDescReply((OFPortDescStatsReply) m);
784 }
tom7ef8ff92014-09-17 13:08:06 -0700785 h.dispatchMessage(m);
786 }
787
788 @Override
789 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
790 throws SwitchStateException {
791 h.sw.handleNiciraRole(m);
792 }
793
794 @Override
795 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
796 throws SwitchStateException {
797 h.sw.handleRole(m);
798 }
799
800 @Override
801 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
802 throws SwitchStateException {
803 handlePortStatusMessage(h, m, true);
Thomas Vachuska39274462014-12-02 13:23:50 -0800804 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700805 }
806
807 @Override
808 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700809// OFPacketOut out =
810// h.sw.factory().buildPacketOut()
811// .setXid(m.getXid())
812// .setBufferId(m.getBufferId()).build();
813// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700814 h.dispatchMessage(m);
815 }
816
817 @Override
818 void processOFFlowRemoved(OFChannelHandler h,
819 OFFlowRemoved m) {
820 h.dispatchMessage(m);
821 }
822
823 @Override
824 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
825 h.dispatchMessage(m);
826 }
827
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700828 @Override
829 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700830 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700831 h.dispatchMessage(m);
832 }
833
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800834 @Override
835 void processIdle(OFChannelHandler h) throws IOException {
836 log.info("{} idle", h.getSwitchInfoString());
837 }
838
tom7ef8ff92014-09-17 13:08:06 -0700839 };
840
841 private final boolean handshakeComplete;
842 ChannelState(boolean handshakeComplete) {
843 this.handshakeComplete = handshakeComplete;
844 }
845
846 /**
847 * Is this a state in which the handshake has completed?
848 * @return true if the handshake is complete
849 */
850 public boolean isHandshakeComplete() {
851 return handshakeComplete;
852 }
853
854 /**
855 * Get a string specifying the switch connection, state, and
856 * message received. To be used as message for SwitchStateException
857 * or log messages
858 * @param h The channel handler (to get switch information_
859 * @param m The OFMessage that has just been received
860 * @param details A string giving more details about the exact nature
861 * of the problem.
862 * @return display string
863 */
864 // needs to be protected because enum members are actually subclasses
865 protected String getSwitchStateMessage(OFChannelHandler h,
866 OFMessage m,
867 String details) {
868 return String.format("Switch: [%s], State: [%s], received: [%s]"
869 + ", details: %s",
870 h.getSwitchInfoString(),
871 this.toString(),
872 m.getType().toString(),
873 details);
874 }
875
876 /**
877 * We have an OFMessage we didn't expect given the current state and
878 * we want to treat this as an error.
879 * We currently throw an exception that will terminate the connection
880 * However, we could be more forgiving
881 * @param h the channel handler that received the message
882 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700883 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -0700884 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -0700885 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -0700886 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
887 throws SwitchStateException {
888 String msg = getSwitchStateMessage(h, m,
889 "Switch should never send this message in the current state");
890 throw new SwitchStateException(msg);
891
892 }
893
894 /**
895 * We have an OFMessage we didn't expect given the current state and
896 * we want to ignore the message.
897 * @param h the channel handler the received the message
898 * @param m the message
899 */
900 protected void unhandledMessageReceived(OFChannelHandler h,
901 OFMessage m) {
902 if (log.isDebugEnabled()) {
903 String msg = getSwitchStateMessage(h, m,
904 "Ignoring unexpected message");
905 log.debug(msg);
906 }
907 }
908
909 /**
910 * Log an OpenFlow error message from a switch.
911 * @param h The switch that sent the error
912 * @param error The error message
913 */
914 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -0700915 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -0700916 error,
917 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800918 this);
tom7ef8ff92014-09-17 13:08:06 -0700919 }
920
921 /**
922 * Log an OpenFlow error message from a switch and disconnect the
923 * channel.
924 *
925 * @param h the IO channel for this switch.
926 * @param error The error message
927 */
928 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
929 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -0700930 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -0700931 h.channel.disconnect();
932 }
933
934 /**
935 * log an error message for a duplicate dpid and disconnect this channel.
936 * @param h the IO channel for this switch.
937 */
938 protected void disconnectDuplicate(OFChannelHandler h) {
939 log.error("Duplicated dpid or incompleted cleanup - "
940 + "disconnecting channel {}", h.getSwitchInfoString());
941 h.duplicateDpidFound = Boolean.TRUE;
942 h.channel.disconnect();
943 }
944
945
946
947 /**
948 * Handles all pending port status messages before a switch is declared
949 * activated in MASTER or EQUAL role. Note that since this handling
950 * precedes the activation (and therefore notification to IOFSwitchListerners)
951 * the changes to ports will already be visible once the switch is
952 * activated. As a result, no notifications are sent out for these
953 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -0700954 *
955 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -0700956 */
957 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
958 try {
959 handlePendingPortStatusMessages(h, 0);
960 } catch (SwitchStateException e) {
961 log.error(e.getMessage());
962 }
963 }
964
965 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
966 throws SwitchStateException {
967 if (h.sw == null) {
968 String msg = "State machine error: switch is null. Should never " +
969 "happen";
970 throw new SwitchStateException(msg);
971 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800972 log.info("Processing {} pending port status messages for {}",
973 h.pendingPortStatusMsg.size(), h.sw.getStringId());
974
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800975 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700976 for (OFPortStatus ps: h.pendingPortStatusMsg) {
977 temp.add(ps);
978 handlePortStatusMessage(h, ps, false);
979 }
tom7ef8ff92014-09-17 13:08:06 -0700980 // expensive but ok - we don't expect too many port-status messages
981 // note that we cannot use clear(), because of the reasons below
982 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -0800983 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -0700984 // the iterator above takes a snapshot of the list - so while we were
985 // dealing with the pending port-status messages, we could have received
986 // newer ones. Handle them recursively, but break the recursion after
987 // five steps to avoid an attack.
988 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
989 handlePendingPortStatusMessages(h, index);
990 }
991 }
992
993 /**
994 * Handle a port status message.
995 *
996 * Handle a port status message by updating the port maps in the
997 * IOFSwitch instance and notifying Controller about the change so
998 * it can dispatch a switch update.
999 *
1000 * @param h The OFChannelHhandler that received the message
1001 * @param m The PortStatus message we received
1002 * @param doNotify if true switch port changed events will be
1003 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001004 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001005 *
1006 */
1007 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1008 boolean doNotify) throws SwitchStateException {
1009 if (h.sw == null) {
1010 String msg = getSwitchStateMessage(h, m,
1011 "State machine error: switch is null. Should never " +
1012 "happen");
1013 throw new SwitchStateException(msg);
1014 }
1015
1016 h.sw.handleMessage(m);
1017 }
1018
1019
1020 /**
1021 * Process an OF message received on the channel and
1022 * update state accordingly.
1023 *
1024 * The main "event" of the state machine. Process the received message,
1025 * send follow up message if required and update state if required.
1026 *
1027 * Switches on the message type and calls more specific event handlers
1028 * for each individual OF message type. If we receive a message that
1029 * is supposed to be sent from a controller to a switch we throw
1030 * a SwitchStateExeption.
1031 *
1032 * The more specific handlers can also throw SwitchStateExceptions
1033 *
1034 * @param h The OFChannelHandler that received the message
1035 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001036 * @throws SwitchStateException if the switch is not bound to the channel
1037 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001038 */
1039 void processOFMessage(OFChannelHandler h, OFMessage m)
1040 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001041 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001042 case HELLO:
1043 processOFHello(h, (OFHello) m);
1044 break;
1045 case BARRIER_REPLY:
1046 processOFBarrierReply(h, (OFBarrierReply) m);
1047 break;
1048 case ECHO_REPLY:
1049 processOFEchoReply(h, (OFEchoReply) m);
1050 break;
1051 case ECHO_REQUEST:
1052 processOFEchoRequest(h, (OFEchoRequest) m);
1053 break;
1054 case ERROR:
1055 processOFError(h, (OFErrorMsg) m);
1056 break;
1057 case FEATURES_REPLY:
1058 processOFFeaturesReply(h, (OFFeaturesReply) m);
1059 break;
1060 case FLOW_REMOVED:
1061 processOFFlowRemoved(h, (OFFlowRemoved) m);
1062 break;
1063 case GET_CONFIG_REPLY:
1064 processOFGetConfigReply(h, (OFGetConfigReply) m);
1065 break;
1066 case PACKET_IN:
1067 processOFPacketIn(h, (OFPacketIn) m);
1068 break;
1069 case PORT_STATUS:
1070 processOFPortStatus(h, (OFPortStatus) m);
1071 break;
1072 case QUEUE_GET_CONFIG_REPLY:
1073 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1074 break;
1075 case STATS_REPLY: // multipart_reply in 1.3
1076 processOFStatisticsReply(h, (OFStatsReply) m);
1077 break;
1078 case EXPERIMENTER:
1079 processOFExperimenter(h, (OFExperimenter) m);
1080 break;
1081 case ROLE_REPLY:
1082 processOFRoleReply(h, (OFRoleReply) m);
1083 break;
1084 case GET_ASYNC_REPLY:
1085 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1086 break;
1087
1088 // The following messages are sent to switches. The controller
1089 // should never receive them
1090 case SET_CONFIG:
1091 case GET_CONFIG_REQUEST:
1092 case PACKET_OUT:
1093 case PORT_MOD:
1094 case QUEUE_GET_CONFIG_REQUEST:
1095 case BARRIER_REQUEST:
1096 case STATS_REQUEST: // multipart request in 1.3
1097 case FEATURES_REQUEST:
1098 case FLOW_MOD:
1099 case GROUP_MOD:
1100 case TABLE_MOD:
1101 case GET_ASYNC_REQUEST:
1102 case SET_ASYNC:
1103 case METER_MOD:
1104 default:
1105 illegalMessageReceived(h, m);
1106 break;
1107 }
1108 }
1109
1110 /*-----------------------------------------------------------------
1111 * Default implementation for message handlers in any state.
1112 *
1113 * Individual states must override these if they want a behavior
1114 * that differs from the default.
1115 *
1116 * In general, these handlers simply ignore the message and do
1117 * nothing.
1118 *
1119 * There are some exceptions though, since some messages really
1120 * are handled the same way in every state (e.g., ECHO_REQUST) or
1121 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1122 -----------------------------------------------------------------*/
1123
1124 void processOFHello(OFChannelHandler h, OFHello m)
1125 throws IOException, SwitchStateException {
1126 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001127 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001128 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001129 }
1130
1131 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1132 throws IOException {
1133 // Silently ignore.
1134 }
1135
1136 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1137 throws IOException {
1138 if (h.ofVersion == null) {
1139 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001140 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001141 return;
1142 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001143 OFEchoReply reply = h.factory
1144 .buildEchoReply()
1145 .setXid(m.getXid())
1146 .setData(m.getData())
1147 .build();
1148 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001149 }
1150
1151 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1152 throws IOException {
1153 // Do nothing with EchoReplies !!
1154 }
1155
1156 // no default implementation for OFError
1157 // every state must override it
1158 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1159 throws IOException, SwitchStateException;
1160
1161
1162 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1163 throws IOException, SwitchStateException {
1164 unhandledMessageReceived(h, m);
1165 }
1166
1167 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1168 throws IOException {
1169 unhandledMessageReceived(h, m);
1170 }
1171
1172 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1173 throws IOException, SwitchStateException {
1174 // we only expect config replies in the WAIT_CONFIG_REPLY state
1175 illegalMessageReceived(h, m);
1176 }
1177
1178 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1179 throws IOException {
1180 unhandledMessageReceived(h, m);
1181 }
1182
1183 // no default implementation. Every state needs to handle it.
1184 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1185 throws IOException, SwitchStateException;
1186
1187 void processOFQueueGetConfigReply(OFChannelHandler h,
1188 OFQueueGetConfigReply m)
1189 throws IOException {
1190 unhandledMessageReceived(h, m);
1191 }
1192
1193 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1194 throws IOException, SwitchStateException {
1195 unhandledMessageReceived(h, m);
1196 }
1197
1198 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1199 throws IOException, SwitchStateException {
1200 // TODO: it might make sense to parse the vendor message here
1201 // into the known vendor messages we support and then call more
1202 // specific event handlers
1203 unhandledMessageReceived(h, m);
1204 }
1205
1206 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1207 throws SwitchStateException, IOException {
1208 unhandledMessageReceived(h, m);
1209 }
1210
1211 void processOFGetAsyncReply(OFChannelHandler h,
1212 OFAsyncGetReply m) {
1213 unhandledMessageReceived(h, m);
1214 }
1215
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001216 void processIdle(OFChannelHandler h) throws IOException {
1217 // disconnect channel which did no complete handshake
1218 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1219 h.channel.disconnect();
1220 }
tom7ef8ff92014-09-17 13:08:06 -07001221 }
1222
1223
1224
1225 //*************************
1226 // Channel handler methods
1227 //*************************
1228
1229 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001230 public void channelActive(ChannelHandlerContext ctx)
1231 throws Exception {
1232
1233 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001234 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001235 channel.remoteAddress());
1236
1237 SocketAddress address = channel.remoteAddress();
1238 if (address instanceof InetSocketAddress) {
1239 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1240 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1241 if (ipAddress.isIp4()) {
1242 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1243 } else {
1244 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1245 }
1246 } else {
1247 channelId = channel.toString();
1248 }
1249
1250 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1251
alshabib70fc7fb2015-01-06 11:04:29 -08001252 /*
1253 hack to wait for the switch to tell us what it's
1254 max version is. This is not spec compliant and should
1255 be removed as soon as switches behave better.
1256 */
1257 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001258 setState(ChannelState.WAIT_HELLO);
1259 }
1260
1261 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001262 public void channelInactive(ChannelHandlerContext ctx)
1263 throws Exception {
1264
tom7ef8ff92014-09-17 13:08:06 -07001265 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001266 getSwitchInfoString());
1267
1268 if (dispatcher != null) {
1269 dispatcher.shutdown();
tom7ef8ff92014-09-17 13:08:06 -07001270 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001271
1272 if (thisdpid != 0) {
1273 if (!duplicateDpidFound) {
1274 // if the disconnected switch (on this ChannelHandler)
1275 // was not one with a duplicate-dpid, it is safe to remove all
1276 // state for it at the controller. Notice that if the disconnected
1277 // switch was a duplicate-dpid, calling the method below would clear
1278 // all state for the original switch (with the same dpid),
1279 // which we obviously don't want.
1280 log.info("{}:removal called", getSwitchInfoString());
1281 if (sw != null) {
1282 sw.removeConnectedSwitch();
1283 }
1284 } else {
1285 // A duplicate was disconnected on this ChannelHandler,
1286 // this is the same switch reconnecting, but the original state was
1287 // not cleaned up - XXX check liveness of original ChannelHandler
1288 log.info("{}:duplicate found", getSwitchInfoString());
1289 duplicateDpidFound = Boolean.FALSE;
1290 }
1291 } else {
1292 log.warn("no dpid in channelHandler registered for "
1293 + "disconnected switch {}", getSwitchInfoString());
1294 }
tom7ef8ff92014-09-17 13:08:06 -07001295 }
1296
1297 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001298 public void exceptionCaught(ChannelHandlerContext ctx,
1299 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001300 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001301
1302 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001303 // switch timeout
1304 log.error("Disconnecting switch {} due to read timeout",
1305 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001306 ctx.channel().close();
1307 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001308 log.error("Disconnecting switch {}: failed to complete handshake",
1309 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001310 ctx.channel().close();
1311 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001312 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001313 } else if (cause instanceof IOException) {
1314 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1315 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001316 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001317 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001318 if (log.isDebugEnabled()) {
1319 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001320 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001321 }
tom7ef8ff92014-09-17 13:08:06 -07001322 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001323 ctx.channel().close();
1324 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001325 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001326 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -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);
tom7ef8ff92014-09-17 13:08:06 -07001330 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001331 ctx.channel().close();
1332 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001333 log.error("Disconnecting switch "
1334 + getSwitchInfoString() +
1335 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001336 cause);
1337 ctx.channel().close();
1338 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001339 log.warn("Could not process message: queue full");
1340 } else {
1341 log.error("Error while processing message from switch "
1342 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001343 + "state " + this.state, cause);
1344 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001345 }
1346 }
1347
1348 @Override
1349 public String toString() {
1350 return getSwitchInfoString();
1351 }
1352
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001353 protected void channelIdle(ChannelHandlerContext ctx,
1354 IdleStateEvent e)
tom7ef8ff92014-09-17 13:08:06 -07001355 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001356 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001357 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001358 ctx.channel());
1359 ctx.write(Collections.singletonList(m), ctx.voidPromise());
tom7ef8ff92014-09-17 13:08:06 -07001360 // XXX S some problems here -- echo request has no transaction id, and
1361 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001362 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001363 }
1364
1365 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001366 public void userEventTriggered(ChannelHandlerContext ctx,
1367 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001368 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001369
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001370 if (evt instanceof IdleStateEvent) {
1371 channelIdle(ctx, (IdleStateEvent) evt);
1372 }
tom7ef8ff92014-09-17 13:08:06 -07001373
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001374 super.userEventTriggered(ctx, evt);
1375 }
1376
1377 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1378 @Override
1379 public void channelRead(ChannelHandlerContext ctx,
1380 Object msg) throws Exception {
1381
1382 boolean release = true;
1383 try {
1384 if (msg instanceof OFMessage) {
1385 // channelRead0 inlined
1386 state.processOFMessage(this, (OFMessage) msg);
1387 } else {
1388 release = false;
1389 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001390 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001391 } finally {
1392 if (release) {
1393 ReferenceCountUtil.release(msg);
1394 }
tom7ef8ff92014-09-17 13:08:06 -07001395 }
1396 }
1397
1398
1399
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001400
tom7ef8ff92014-09-17 13:08:06 -07001401 //*************************
1402 // Channel utility methods
1403 //*************************
1404
1405 /**
1406 * Is this a state in which the handshake has completed?
1407 * @return true if the handshake is complete
1408 */
1409 public boolean isHandshakeComplete() {
1410 return this.state.isHandshakeComplete();
1411 }
1412
1413 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001414
1415 if (dispatchBacklog.isEmpty()) {
1416 if (!dispatchQueue.offer(m)) {
1417 // queue full
1418 channel.config().setAutoRead(false);
1419 // put it on the head of backlog
1420 dispatchBacklog.addFirst(m);
1421 return;
1422 }
1423 } else {
1424 dispatchBacklog.addLast(m);
1425 }
1426
1427 while (!dispatchBacklog.isEmpty()) {
1428 OFMessage msg = dispatchBacklog.pop();
1429
1430 if (!dispatchQueue.offer(msg)) {
1431 // queue full
1432 channel.config().setAutoRead(false);
1433 // put it back to the head of backlog
1434 dispatchBacklog.addFirst(msg);
1435 return;
1436 }
1437 }
1438
1439
1440 if (dispatcherHandle.isDone()) {
1441 // dispatcher terminated for some reason, restart
1442
1443 dispatcherHandle = dispatcher.submit(() -> {
1444 try {
1445 List<OFMessage> msgs = new ArrayList<>();
1446 for (;;) {
1447 // wait for new message
1448 OFMessage msg = dispatchQueue.take();
1449 sw.handleMessage(msg);
1450
1451 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1452 if (!channel.config().isAutoRead()) {
1453 channel.config().setAutoRead(true);
1454 }
1455 msgs.forEach(sw::handleMessage);
1456 msgs.clear();
1457 }
1458
1459 if (!channel.config().isAutoRead()) {
1460 channel.config().setAutoRead(true);
1461 }
1462 }
1463 } catch (InterruptedException e) {
1464 Thread.currentThread().interrupt();
1465 // interrupted. gracefully shutting down
1466 return;
1467 }
1468
1469 });
1470 }
tom7ef8ff92014-09-17 13:08:06 -07001471 }
1472
1473 /**
1474 * Return a string describing this switch based on the already available
1475 * information (DPID and/or remote socket).
1476 * @return display string
1477 */
1478 private String getSwitchInfoString() {
1479 if (sw != null) {
1480 return sw.toString();
1481 }
1482 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001483 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001484 channelString = "?";
1485 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001486 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001487 }
1488 String dpidString;
1489 if (featuresReply == null) {
1490 dpidString = "?";
1491 } else {
1492 dpidString = featuresReply.getDatapathId().toString();
1493 }
1494 return String.format("[%s DPID[%s]]", channelString, dpidString);
1495 }
1496
1497 /**
1498 * Update the channels state. Only called from the state machine.
1499 * TODO: enforce restricted state transitions
1500 * @param state
1501 */
1502 private void setState(ChannelState state) {
1503 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001504 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001505 }
1506
1507 /**
1508 * Send hello message to the switch using the handshake transactions ids.
1509 * @throws IOException
1510 */
1511 private void sendHandshakeHelloMessage() throws IOException {
1512 // The OF protocol requires us to start things off by sending the highest
1513 // version of the protocol supported.
1514
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001515 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001516 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001517 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1518 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001519 (0b1 << OFVersion.OF_14.getWireVersion()) |
1520 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001521 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1522 OFHelloElem hem = OFFactories.getFactory(version)
1523 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001524 .setBitmaps(Collections.singletonList(bitmap))
1525 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001526 OFMessage.Builder mb = OFFactories.getFactory(version)
1527 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001528 .setXid(this.handshakeTransactionIds--)
1529 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001530 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1531 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001532 }
1533
1534 /**
1535 * Send featuresRequest msg to the switch using the handshake transactions ids.
1536 * @throws IOException
1537 */
1538 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001539 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001540 OFMessage m = factory.buildFeaturesRequest()
1541 .setXid(this.handshakeTransactionIds--)
1542 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001543 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001544 }
1545
1546 /**
1547 * Send the configuration requests to tell the switch we want full
1548 * packets.
1549 * @throws IOException
1550 */
1551 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001552 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001553 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001554
1555 // Ensure we receive the full packet via PacketIn
1556 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001557 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001558 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001559 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001560 OFSetConfig sc = factory
1561 .buildSetConfig()
1562 .setMissSendLen((short) 0xffff)
1563 .setXid(this.handshakeTransactionIds--)
1564 .build();
1565 msglist.add(sc);
1566 }
tom7ef8ff92014-09-17 13:08:06 -07001567
1568 // Barrier
1569 OFBarrierRequest br = factory
1570 .buildBarrierRequest()
1571 .setXid(this.handshakeTransactionIds--)
1572 .build();
1573 msglist.add(br);
1574
1575 // Verify (need barrier?)
1576 OFGetConfigRequest gcr = factory
1577 .buildGetConfigRequest()
1578 .setXid(this.handshakeTransactionIds--)
1579 .build();
1580 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001581 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001582 }
1583
1584 /**
1585 * send a description state request.
1586 * @throws IOException
1587 */
1588 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1589 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001590 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001591 OFDescStatsRequest dreq = factory
1592 .buildDescStatsRequest()
1593 .setXid(handshakeTransactionIds--)
1594 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001595 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001596 }
1597
Jordi Ortiz91477b82016-11-29 15:22:50 +01001598 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001599 * send a meter features request.
1600 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001601 * @throws IOException
1602 */
1603 private void sendMeterFeaturesRequest() throws IOException {
1604 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001605 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001606 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001607 OFMeterFeaturesStatsRequest mfreq = factory
1608 .buildMeterFeaturesStatsRequest()
1609 .setXid(handshakeTransactionIds--)
1610 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001611 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001612 }
1613
tom7ef8ff92014-09-17 13:08:06 -07001614 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001615 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001616 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001617 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001618 .buildPortDescStatsRequest()
1619 .setXid(handshakeTransactionIds--)
1620 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001621 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001622 }
1623
1624 ChannelState getStateForTesting() {
1625 return state;
1626 }
1627
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001628
1629 @Override
1630 public boolean isActive() {
1631 if (channel != null) {
1632 return channel.isActive();
1633 }
1634 return false;
1635 }
1636
1637 @Override
1638 public void closeSession() {
1639 if (channel != null) {
1640 channel.close();
1641 }
1642 }
1643
1644 @Override
1645 public boolean sendMsg(Iterable<OFMessage> msgs) {
1646 if (channel.isActive()) {
1647 channel.writeAndFlush(msgs, channel.voidPromise());
1648 return true;
1649 } else {
1650 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1651 getSwitchInfoString(), msgs);
1652 return false;
1653 }
1654 }
1655
1656 @Override
1657 public CharSequence sessionInfo() {
1658 return channelId;
1659 }
1660
tom7ef8ff92014-09-17 13:08:06 -07001661}