blob: 97d91974ba22c8f745e9e846634d42e8102b8f46 [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 Dase9c8971e2018-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 Dase9c8971e2018-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 Das607c8982018-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) {
1275 dispatcher.shutdown();
tom7ef8ff92014-09-17 13:08:06 -07001276 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001277
1278 if (thisdpid != 0) {
1279 if (!duplicateDpidFound) {
1280 // if the disconnected switch (on this ChannelHandler)
1281 // was not one with a duplicate-dpid, it is safe to remove all
1282 // state for it at the controller. Notice that if the disconnected
1283 // switch was a duplicate-dpid, calling the method below would clear
1284 // all state for the original switch (with the same dpid),
1285 // which we obviously don't want.
1286 log.info("{}:removal called", getSwitchInfoString());
1287 if (sw != null) {
1288 sw.removeConnectedSwitch();
1289 }
1290 } else {
1291 // A duplicate was disconnected on this ChannelHandler,
1292 // this is the same switch reconnecting, but the original state was
1293 // not cleaned up - XXX check liveness of original ChannelHandler
1294 log.info("{}:duplicate found", getSwitchInfoString());
1295 duplicateDpidFound = Boolean.FALSE;
1296 }
1297 } else {
1298 log.warn("no dpid in channelHandler registered for "
1299 + "disconnected switch {}", getSwitchInfoString());
1300 }
tom7ef8ff92014-09-17 13:08:06 -07001301 }
1302
1303 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001304 public void exceptionCaught(ChannelHandlerContext ctx,
1305 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001306 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001307
1308 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001309 // switch timeout
1310 log.error("Disconnecting switch {} due to read timeout",
1311 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001312 ctx.channel().close();
1313 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001314 log.error("Disconnecting switch {}: failed to complete handshake",
1315 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001316 ctx.channel().close();
1317 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001318 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001319 } else if (cause instanceof IOException) {
1320 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1321 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001322 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001323 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001324 if (log.isDebugEnabled()) {
1325 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001326 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001327 }
tom7ef8ff92014-09-17 13:08:06 -07001328 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001329 ctx.channel().close();
1330 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001331 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001332 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001333 if (log.isDebugEnabled()) {
1334 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001335 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001336 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001337 ctx.channel().close();
1338 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001339 log.error("Disconnecting switch "
1340 + getSwitchInfoString() +
1341 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001342 cause);
1343 ctx.channel().close();
1344 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001345 log.warn("Could not process message: queue full");
1346 } else {
1347 log.error("Error while processing message from switch "
1348 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001349 + "state " + this.state, cause);
1350 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001351 }
1352 }
1353
1354 @Override
1355 public String toString() {
1356 return getSwitchInfoString();
1357 }
1358
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001359 protected void channelIdle(ChannelHandlerContext ctx,
1360 IdleStateEvent e)
tom7ef8ff92014-09-17 13:08:06 -07001361 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001362 OFMessage m = factory.buildEchoRequest().build();
alshabib09d48be2014-10-03 15:43:33 -07001363 log.debug("Sending Echo Request on idle channel: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001364 ctx.channel());
1365 ctx.write(Collections.singletonList(m), ctx.voidPromise());
tom7ef8ff92014-09-17 13:08:06 -07001366 // XXX S some problems here -- echo request has no transaction id, and
1367 // echo reply is not correlated to the echo request.
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001368 state.processIdle(this);
tom7ef8ff92014-09-17 13:08:06 -07001369 }
1370
1371 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001372 public void userEventTriggered(ChannelHandlerContext ctx,
1373 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001374 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001375
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001376 if (evt instanceof IdleStateEvent) {
1377 channelIdle(ctx, (IdleStateEvent) evt);
1378 }
tom7ef8ff92014-09-17 13:08:06 -07001379
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001380 super.userEventTriggered(ctx, evt);
1381 }
1382
1383 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1384 @Override
1385 public void channelRead(ChannelHandlerContext ctx,
1386 Object msg) throws Exception {
1387
1388 boolean release = true;
1389 try {
1390 if (msg instanceof OFMessage) {
1391 // channelRead0 inlined
1392 state.processOFMessage(this, (OFMessage) msg);
1393 } else {
1394 release = false;
1395 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001396 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001397 } finally {
1398 if (release) {
1399 ReferenceCountUtil.release(msg);
1400 }
tom7ef8ff92014-09-17 13:08:06 -07001401 }
1402 }
1403
1404
1405
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001406
tom7ef8ff92014-09-17 13:08:06 -07001407 //*************************
1408 // Channel utility methods
1409 //*************************
1410
1411 /**
1412 * Is this a state in which the handshake has completed?
1413 * @return true if the handshake is complete
1414 */
1415 public boolean isHandshakeComplete() {
1416 return this.state.isHandshakeComplete();
1417 }
1418
1419 private void dispatchMessage(OFMessage m) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001420
1421 if (dispatchBacklog.isEmpty()) {
1422 if (!dispatchQueue.offer(m)) {
1423 // queue full
1424 channel.config().setAutoRead(false);
1425 // put it on the head of backlog
1426 dispatchBacklog.addFirst(m);
1427 return;
1428 }
1429 } else {
1430 dispatchBacklog.addLast(m);
1431 }
1432
1433 while (!dispatchBacklog.isEmpty()) {
1434 OFMessage msg = dispatchBacklog.pop();
1435
1436 if (!dispatchQueue.offer(msg)) {
1437 // queue full
1438 channel.config().setAutoRead(false);
1439 // put it back to the head of backlog
1440 dispatchBacklog.addFirst(msg);
1441 return;
1442 }
1443 }
1444
1445
1446 if (dispatcherHandle.isDone()) {
1447 // dispatcher terminated for some reason, restart
1448
Yuta HIGUCHI1edc36b2018-01-24 23:39:06 -08001449 dispatcherHandle = dispatcher.submit((Runnable) () -> {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001450 try {
1451 List<OFMessage> msgs = new ArrayList<>();
1452 for (;;) {
1453 // wait for new message
1454 OFMessage msg = dispatchQueue.take();
1455 sw.handleMessage(msg);
1456
1457 while (dispatchQueue.drainTo(msgs, MSG_READ_BUFFER) > 0) {
1458 if (!channel.config().isAutoRead()) {
1459 channel.config().setAutoRead(true);
1460 }
1461 msgs.forEach(sw::handleMessage);
1462 msgs.clear();
1463 }
1464
1465 if (!channel.config().isAutoRead()) {
1466 channel.config().setAutoRead(true);
1467 }
1468 }
1469 } catch (InterruptedException e) {
1470 Thread.currentThread().interrupt();
1471 // interrupted. gracefully shutting down
1472 return;
1473 }
1474
1475 });
1476 }
tom7ef8ff92014-09-17 13:08:06 -07001477 }
1478
1479 /**
1480 * Return a string describing this switch based on the already available
1481 * information (DPID and/or remote socket).
1482 * @return display string
1483 */
1484 private String getSwitchInfoString() {
1485 if (sw != null) {
1486 return sw.toString();
1487 }
1488 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001489 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001490 channelString = "?";
1491 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001492 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001493 }
1494 String dpidString;
1495 if (featuresReply == null) {
1496 dpidString = "?";
1497 } else {
1498 dpidString = featuresReply.getDatapathId().toString();
1499 }
1500 return String.format("[%s DPID[%s]]", channelString, dpidString);
1501 }
1502
1503 /**
1504 * Update the channels state. Only called from the state machine.
1505 * TODO: enforce restricted state transitions
1506 * @param state
1507 */
1508 private void setState(ChannelState state) {
1509 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001510 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001511 }
1512
1513 /**
1514 * Send hello message to the switch using the handshake transactions ids.
1515 * @throws IOException
1516 */
1517 private void sendHandshakeHelloMessage() throws IOException {
1518 // The OF protocol requires us to start things off by sending the highest
1519 // version of the protocol supported.
1520
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001521 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001522 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001523 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1524 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001525 (0b1 << OFVersion.OF_14.getWireVersion()) |
1526 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001527 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1528 OFHelloElem hem = OFFactories.getFactory(version)
1529 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001530 .setBitmaps(Collections.singletonList(bitmap))
1531 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001532 OFMessage.Builder mb = OFFactories.getFactory(version)
1533 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001534 .setXid(this.handshakeTransactionIds--)
1535 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001536 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1537 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001538 }
1539
1540 /**
1541 * Send featuresRequest msg to the switch using the handshake transactions ids.
1542 * @throws IOException
1543 */
1544 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001545 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001546 OFMessage m = factory.buildFeaturesRequest()
1547 .setXid(this.handshakeTransactionIds--)
1548 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001549 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001550 }
1551
1552 /**
1553 * Send the configuration requests to tell the switch we want full
1554 * packets.
1555 * @throws IOException
1556 */
1557 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001558 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001559 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001560
1561 // Ensure we receive the full packet via PacketIn
1562 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001563 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001564 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001565 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001566 OFSetConfig sc = factory
1567 .buildSetConfig()
1568 .setMissSendLen((short) 0xffff)
1569 .setXid(this.handshakeTransactionIds--)
1570 .build();
1571 msglist.add(sc);
1572 }
tom7ef8ff92014-09-17 13:08:06 -07001573
1574 // Barrier
1575 OFBarrierRequest br = factory
1576 .buildBarrierRequest()
1577 .setXid(this.handshakeTransactionIds--)
1578 .build();
1579 msglist.add(br);
1580
1581 // Verify (need barrier?)
1582 OFGetConfigRequest gcr = factory
1583 .buildGetConfigRequest()
1584 .setXid(this.handshakeTransactionIds--)
1585 .build();
1586 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001587 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001588 }
1589
1590 /**
1591 * send a description state request.
1592 * @throws IOException
1593 */
1594 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1595 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001596 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001597 OFDescStatsRequest dreq = factory
1598 .buildDescStatsRequest()
1599 .setXid(handshakeTransactionIds--)
1600 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001601 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001602 }
1603
Jordi Ortiz91477b82016-11-29 15:22:50 +01001604 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001605 * send a meter features request.
1606 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001607 * @throws IOException
1608 */
1609 private void sendMeterFeaturesRequest() throws IOException {
1610 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001611 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001612 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001613 OFMeterFeaturesStatsRequest mfreq = factory
1614 .buildMeterFeaturesStatsRequest()
1615 .setXid(handshakeTransactionIds--)
1616 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001617 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001618 }
1619
tom7ef8ff92014-09-17 13:08:06 -07001620 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001621 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001622 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001623 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001624 .buildPortDescStatsRequest()
1625 .setXid(handshakeTransactionIds--)
1626 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001627 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001628 }
1629
1630 ChannelState getStateForTesting() {
1631 return state;
1632 }
1633
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001634
1635 @Override
1636 public boolean isActive() {
1637 if (channel != null) {
1638 return channel.isActive();
1639 }
1640 return false;
1641 }
1642
1643 @Override
1644 public void closeSession() {
1645 if (channel != null) {
1646 channel.close();
1647 }
1648 }
1649
1650 @Override
1651 public boolean sendMsg(Iterable<OFMessage> msgs) {
1652 if (channel.isActive()) {
1653 channel.writeAndFlush(msgs, channel.voidPromise());
1654 return true;
1655 } else {
1656 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1657 getSwitchInfoString(), msgs);
1658 return false;
1659 }
1660 }
1661
1662 @Override
1663 public CharSequence sessionInfo() {
1664 return channelId;
1665 }
1666
tom7ef8ff92014-09-17 13:08:06 -07001667}