blob: 1b0a449baf26fe697f59813fe8e2e941e171b955 [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
Andrea Campanella9ef930a2020-11-06 21:41:01 +010019import static java.util.concurrent.Executors.newSingleThreadExecutor;
Anton Chigrinbf14b372019-01-14 17:29:56 +020020import static org.onlab.packet.Ethernet.TYPE_BSN;
21import static org.onlab.packet.Ethernet.TYPE_LLDP;
Andrea Campanellab0b93ac2021-09-13 12:37:36 +020022import static org.onlab.util.GroupedThreadFactory.groupedThreadFactory;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070023import static org.onlab.util.Tools.groupedThreads;
Anton Chigrinbf14b372019-01-14 17:29:56 +020024import static org.onosproject.openflow.controller.Dpid.uri;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070025
tom7ef8ff92014-09-17 13:08:06 -070026import java.io.IOException;
Andrea Campanellab0b93ac2021-09-13 12:37:36 +020027import java.io.UncheckedIOException;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070028import java.net.InetSocketAddress;
29import java.net.SocketAddress;
Anton Chigrinbf14b372019-01-14 17:29:56 +020030import java.nio.ByteBuffer;
tom7ef8ff92014-09-17 13:08:06 -070031import java.nio.channels.ClosedChannelException;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070032import java.security.cert.Certificate;
tom7ef8ff92014-09-17 13:08:06 -070033import java.util.ArrayList;
34import java.util.Collections;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070035import java.util.Deque;
tom7ef8ff92014-09-17 13:08:06 -070036import java.util.List;
Anton Chigrinbf14b372019-01-14 17:29:56 +020037import java.util.Map;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080038import java.util.Optional;
Anton Chigrinbf14b372019-01-14 17:29:56 +020039import java.util.Set;
40import java.util.concurrent.LinkedBlockingDeque;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070041import java.util.concurrent.CompletableFuture;
Anton Chigrinbf14b372019-01-14 17:29:56 +020042import java.util.concurrent.ConcurrentHashMap;
tom7ef8ff92014-09-17 13:08:06 -070043import java.util.concurrent.CopyOnWriteArrayList;
Anton Chigrinbf14b372019-01-14 17:29:56 +020044import java.util.concurrent.CopyOnWriteArraySet;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070045import java.util.concurrent.ExecutorService;
46import java.util.concurrent.Executors;
47import java.util.concurrent.Future;
tom7ef8ff92014-09-17 13:08:06 -070048import java.util.concurrent.RejectedExecutionException;
Andrea Campanellab0b93ac2021-09-13 12:37:36 +020049import java.util.concurrent.ThreadFactory;
Anton Chigrinbf14b372019-01-14 17:29:56 +020050import java.util.concurrent.atomic.AtomicInteger;
51import java.util.concurrent.locks.Condition;
52import java.util.concurrent.locks.ReentrantLock;
Brian O'Connorf69e3e32018-05-10 02:25:09 -070053
Andrea Campanellab0b93ac2021-09-13 12:37:36 +020054import com.google.common.util.concurrent.ThreadFactoryBuilder;
55import org.onlab.util.GroupedThreadFactory;
Anton Chigrinbf14b372019-01-14 17:29:56 +020056import org.osgi.service.component.annotations.Reference;
57import org.osgi.service.component.annotations.ReferenceCardinality;
58import org.onlab.osgi.DefaultServiceDirectory;
59import org.onlab.packet.Ethernet;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070060import org.onlab.packet.IpAddress;
Anton Chigrinbf14b372019-01-14 17:29:56 +020061import org.onosproject.net.ConnectPoint;
62import org.onosproject.net.DeviceId;
63import org.onosproject.net.PortNumber;
64import org.onosproject.net.packet.DefaultInboundPacket;
65import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
Charles Chan34155e52016-11-30 18:28:11 -080066import org.onosproject.openflow.controller.Dpid;
Anton Chigrinbf14b372019-01-14 17:29:56 +020067import org.onosproject.openflow.controller.OpenFlowClassifier;
68import org.onosproject.openflow.controller.OpenFlowPacketContext;
69import org.onosproject.openflow.controller.OpenFlowService;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -070070import org.onosproject.openflow.controller.OpenFlowSession;
Brian O'Connorabafb502014-12-02 22:26:20 -080071import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
72import org.onosproject.openflow.controller.driver.SwitchStateException;
tom7ef8ff92014-09-17 13:08:06 -070073import org.projectfloodlight.openflow.exceptions.OFParseError;
74import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
75import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
76import org.projectfloodlight.openflow.protocol.OFBarrierReply;
77import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
78import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
79import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
80import org.projectfloodlight.openflow.protocol.OFEchoReply;
81import org.projectfloodlight.openflow.protocol.OFEchoRequest;
82import org.projectfloodlight.openflow.protocol.OFErrorMsg;
83import org.projectfloodlight.openflow.protocol.OFErrorType;
84import org.projectfloodlight.openflow.protocol.OFExperimenter;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -080085import org.projectfloodlight.openflow.protocol.OFFactories;
tom7ef8ff92014-09-17 13:08:06 -070086import org.projectfloodlight.openflow.protocol.OFFactory;
87import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
88import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
89import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
90import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
91import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
92import org.projectfloodlight.openflow.protocol.OFHello;
93import org.projectfloodlight.openflow.protocol.OFHelloElem;
94import org.projectfloodlight.openflow.protocol.OFMessage;
Jordi Ortiz91477b82016-11-29 15:22:50 +010095import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsReply;
96import org.projectfloodlight.openflow.protocol.OFMeterFeaturesStatsRequest;
tom7ef8ff92014-09-17 13:08:06 -070097import org.projectfloodlight.openflow.protocol.OFPacketIn;
98import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
99import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
100import org.projectfloodlight.openflow.protocol.OFPortStatus;
101import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
102import org.projectfloodlight.openflow.protocol.OFRoleReply;
103import org.projectfloodlight.openflow.protocol.OFSetConfig;
104import org.projectfloodlight.openflow.protocol.OFStatsReply;
105import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
106import org.projectfloodlight.openflow.protocol.OFStatsType;
107import org.projectfloodlight.openflow.protocol.OFType;
108import org.projectfloodlight.openflow.protocol.OFVersion;
109import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
110import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
111import org.projectfloodlight.openflow.types.U32;
112import org.slf4j.Logger;
113import org.slf4j.LoggerFactory;
114
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700115import io.netty.channel.Channel;
116import io.netty.channel.ChannelHandlerContext;
117import io.netty.channel.ChannelInboundHandlerAdapter;
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700118import io.netty.handler.ssl.SslHandler;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700119import io.netty.handler.timeout.IdleStateEvent;
120import io.netty.handler.timeout.ReadTimeoutException;
121import io.netty.util.ReferenceCountUtil;
122
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700123import javax.net.ssl.SSLPeerUnverifiedException;
124
tom7ef8ff92014-09-17 13:08:06 -0700125/**
126 * Channel handler deals with the switch connection and dispatches
127 * switch messages to the appropriate locations.
128 */
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700129class OFChannelHandler extends ChannelInboundHandlerAdapter
130 implements OpenFlowSession {
131
tom7ef8ff92014-09-17 13:08:06 -0700132 private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -0700133
134 private static final String RESET_BY_PEER = "Connection reset by peer";
135 private static final String BROKEN_PIPE = "Broken pipe";
pierc684ee12019-07-16 15:52:50 +0200136 static final int NUM_OF_QUEUES = 8;
Thomas Vachuskae9af1f42015-07-06 08:42:18 -0700137
tom7ef8ff92014-09-17 13:08:06 -0700138 private final Controller controller;
139 private OpenFlowSwitchDriver sw;
140 private long thisdpid; // channelHandler cached value of connected switch id
Anton Chigrinbf14b372019-01-14 17:29:56 +0200141 private DeviceId deviceId;
tom7ef8ff92014-09-17 13:08:06 -0700142 private Channel channel;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700143 private String channelId;
144
145
tom7ef8ff92014-09-17 13:08:06 -0700146 // State needs to be volatile because the HandshakeTimeoutHandler
147 // needs to check if the handshake is complete
148 private volatile ChannelState state;
149
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800150 /**
151 * Timeout in ms to wait for meter feature reply.
152 */
153 private static final long METER_TIMEOUT = 60_000;
154
155 private volatile long lastStateChange = System.currentTimeMillis();
156
tom7ef8ff92014-09-17 13:08:06 -0700157 // When a switch with a duplicate dpid is found (i.e we already have a
158 // connected switch with the same dpid), the new switch is immediately
159 // disconnected. At that point netty callsback channelDisconnected() which
160 // proceeds to cleaup switch state - we need to ensure that it does not cleanup
161 // switch state for the older (still connected) switch
162 private volatile Boolean duplicateDpidFound;
163
164 // Temporary storage for switch-features and port-description
165 private OFFeaturesReply featuresReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700166 private List<OFPortDescStatsReply> portDescReplies;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100167 private OFMeterFeaturesStatsReply meterFeaturesReply;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700168 //private OFPortDescStatsReply portDescReply;
tom7ef8ff92014-09-17 13:08:06 -0700169 // a concurrent ArrayList to temporarily store port status messages
170 // before we are ready to deal with them
171 private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
172
173 //Indicates the openflow version used by this switch
174 protected OFVersion ofVersion;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700175 protected OFFactory factory;
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800176
tom7ef8ff92014-09-17 13:08:06 -0700177 /** transaction Ids to use during handshake. Since only one thread
178 * calls into an OFChannelHandler instance, we don't need atomic.
179 * We will count down
180 */
181 private int handshakeTransactionIds = -1;
182
Anton Chigrinbf14b372019-01-14 17:29:56 +0200183 @Reference(cardinality = ReferenceCardinality.MANDATORY)
184 private OpenFlowService openFlowManager;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700185
pierf528eff2019-11-22 20:51:26 +0100186 // Each IDLE_INTERVAL (see OFChannelInitializer) we will perform a sanity check
187 // Idle timeout actions will be performed each MAX_IDLE_RETRY * IDLE_INTERVAL
188 private static final int MAX_IDLE_RETRY = 4;
189 private int maxIdleRetry = MAX_IDLE_RETRY;
190
191 // Dispatcher buffer/read size
Anton Chigrinbf14b372019-01-14 17:29:56 +0200192 private static final int BACKLOG_READ_BUFFER_DEFAULT = 1000;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700193
194 /**
Anton Chigrinbf14b372019-01-14 17:29:56 +0200195 * Map with all LinkedBlockingMessagesQueue queues which contains OFMessages.
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700196 */
Anton Chigrinbf14b372019-01-14 17:29:56 +0200197 private Map<Integer, LinkedBlockingMessagesQueue<OFMessage>> dispatchQueuesMapProducer = new ConcurrentHashMap<>();
198
199 /**
200 * OFMessage classifiers map.
201 */
202 private List<Set<OpenFlowClassifier>> messageClassifiersMapProducer =
203 new CopyOnWriteArrayList<Set<OpenFlowClassifier>>();
204
Anton Chigrinbf14b372019-01-14 17:29:56 +0200205 /**
206 * Lock held by take, poll, etc.
207 */
208 private final ReentrantLock takeLock = new ReentrantLock();
209
210 /**
211 * Wait queue for waiting takes.
212 */
213 private final Condition notEmpty = takeLock.newCondition();
214
215 /**
216 * Current number of elements in enabled sub-queues.
217 */
218 private final AtomicInteger totalCount = new AtomicInteger();
219
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700220 /**
221 * Single thread executor for OFMessage dispatching.
222 *
223 * Gets initialized on channelActive, shutdown on channelInactive.
224 */
225 private ExecutorService dispatcher;
226
227 /**
228 * Handle for dispatcher thread.
229 * <p>
230 * Should only be touched from the Channel I/O thread
231 */
232 private Future<?> dispatcherHandle = CompletableFuture.completedFuture(null);
233
234 /**
235 * Dispatch backlog.
236 * <p>
237 * Should only be touched from the Channel I/O thread
238 */
Anton Chigrinbf14b372019-01-14 17:29:56 +0200239 private final Deque<OFMessage> dispatchBacklog;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700240
tom7ef8ff92014-09-17 13:08:06 -0700241 /**
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200242 * Executor for runtime status events to offload from the main thread the
243 * processing of port status, mastership and connection OF messages.
244 * Executor is instantiated as a single thread executor guaranteeing processing
245 * of device status messages in order.
Andrea Campanella9ef930a2020-11-06 21:41:01 +0100246 */
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200247 protected ExecutorService runtimeStatusExecutor;
Andrea Campanella9ef930a2020-11-06 21:41:01 +0100248
249 /**
tom7ef8ff92014-09-17 13:08:06 -0700250 * Create a new unconnected OFChannelHandler.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -0800251 * @param controller parent controller
tom7ef8ff92014-09-17 13:08:06 -0700252 */
253 OFChannelHandler(Controller controller) {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700254
tom7ef8ff92014-09-17 13:08:06 -0700255 this.controller = controller;
256 this.state = ChannelState.INIT;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800257 this.pendingPortStatusMsg = new CopyOnWriteArrayList<>();
258 this.portDescReplies = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -0700259 duplicateDpidFound = Boolean.FALSE;
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200260 String groupName = "onos/of-channel-handler";
261 String pattern = "runtime-status-%d";
262 ThreadFactory factory = new ThreadFactoryBuilder()
263 .setThreadFactory(groupedThreadFactory(groupName))
264 .setNameFormat(groupName.replace(GroupedThreadFactory.DELIMITER, "-") + "-" + pattern)
265 .setUncaughtExceptionHandler((t, e) -> {
266 log.error("Exception on " + t.getName(), e);
267 throw new UncheckedIOException(new IOException(e));
268 }).build();
269 runtimeStatusExecutor = newSingleThreadExecutor(
270 factory);
Anton Chigrinbf14b372019-01-14 17:29:56 +0200271 //Initialize queues and classifiers
272 dispatchBacklog = new LinkedBlockingDeque<>(BACKLOG_READ_BUFFER_DEFAULT);
273 for (int i = 0; i < NUM_OF_QUEUES; i++) {
274 if (controller.getQueueSize(i) > 0) {
275 dispatchQueuesMapProducer.put(i,
276 new LinkedBlockingMessagesQueue<>(i, controller.getQueueSize(i), controller.getBulkSize(i)));
277 }
278 if (i != NUM_OF_QUEUES) {
279 messageClassifiersMapProducer.add(i, new CopyOnWriteArraySet<>());
280 }
281 }
tom7ef8ff92014-09-17 13:08:06 -0700282 }
283
tom7ef8ff92014-09-17 13:08:06 -0700284 // XXX S consider if necessary
285 public void disconnectSwitch() {
286 sw.disconnectSwitch();
287 }
288
tom7ef8ff92014-09-17 13:08:06 -0700289 //*************************
290 // Channel State Machine
291 //*************************
292
293 /**
294 * The state machine for handling the switch/channel state. All state
295 * transitions should happen from within the state machine (and not from other
296 * parts of the code)
297 */
298 enum ChannelState {
299 /**
300 * Initial state before channel is connected.
301 */
302 INIT(false) {
303 @Override
304 void processOFMessage(OFChannelHandler h, OFMessage m)
305 throws IOException, SwitchStateException {
306 illegalMessageReceived(h, m);
307 }
308
309 @Override
310 void processOFError(OFChannelHandler h, OFErrorMsg m)
311 throws IOException {
312 // need to implement since its abstract but it will never
313 // be called
314 }
315
316 @Override
317 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
318 throws IOException {
319 unhandledMessageReceived(h, m);
320 }
321 },
322
323 /**
324 * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
325 * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
326 * protocol version is accepted.
327 * We send an OFFeaturesRequest depending on the protocol version selected
328 * Next state is WAIT_FEATURES_REPLY
329 */
330 WAIT_HELLO(false) {
331 @Override
332 void processOFHello(OFChannelHandler h, OFHello m)
333 throws IOException {
334 // TODO We could check for the optional bitmap, but for now
335 // we are just checking the version number.
Chip Boling68bc6562015-07-06 10:00:01 -0500336 if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
337 log.debug("Received {} Hello from {} - switching to OF "
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800338 + "version 1.3+", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700339 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800340 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700341 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800342 h.sendHandshakeHelloMessage();
Chip Boling68bc6562015-07-06 10:00:01 -0500343 } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
alshabib09d48be2014-10-03 15:43:33 -0700344 log.debug("Received {} Hello from {} - switching to OF "
tom7ef8ff92014-09-17 13:08:06 -0700345 + "version 1.0", m.getVersion(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700346 h.channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800347 h.ofVersion = m.getVersion();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700348 h.factory = OFFactories.getFactory(h.ofVersion);
alshabib70fc7fb2015-01-06 11:04:29 -0800349 OFHello hi =
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700350 h.factory.buildHello()
alshabib70fc7fb2015-01-06 11:04:29 -0800351 .setXid(h.handshakeTransactionIds--)
352 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700353 h.channel.writeAndFlush(Collections.singletonList(hi));
tom7ef8ff92014-09-17 13:08:06 -0700354 } else {
355 log.error("Received Hello of version {} from switch at {}. "
356 + "This controller works with OF1.0 and OF1.3 "
357 + "switches. Disconnecting switch ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700358 m.getVersion(), h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700359 h.channel.disconnect();
360 return;
361 }
362 h.sendHandshakeFeaturesRequestMessage();
363 h.setState(WAIT_FEATURES_REPLY);
364 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700365
tom7ef8ff92014-09-17 13:08:06 -0700366 @Override
367 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
368 throws IOException, SwitchStateException {
369 illegalMessageReceived(h, m);
370 }
371 @Override
372 void processOFStatisticsReply(OFChannelHandler h,
373 OFStatsReply m)
374 throws IOException, SwitchStateException {
375 illegalMessageReceived(h, m);
376 }
377 @Override
378 void processOFError(OFChannelHandler h, OFErrorMsg m) {
379 logErrorDisconnect(h, m);
380 }
381
382 @Override
383 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
384 throws IOException {
385 unhandledMessageReceived(h, m);
386 }
387 },
388
tom7ef8ff92014-09-17 13:08:06 -0700389 /**
390 * We are waiting for a features reply message. Once we receive it, the
391 * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
392 * we send a SetConfig request, barrier, and GetConfig request and the
393 * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
394 * request and the next state is WAIT_PORT_DESC_REPLY.
395 */
396 WAIT_FEATURES_REPLY(false) {
397 @Override
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700398 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
tom7ef8ff92014-09-17 13:08:06 -0700399 throws IOException {
Brian O'Connorf69e3e32018-05-10 02:25:09 -0700400 Long dpid = m.getDatapathId().getLong();
401 if (!h.setDpid(dpid, h.channel)) {
402 log.error("Switch presented invalid certificate for dpid {}. Disconnecting",
403 dpid);
404 h.channel.disconnect();
405 return;
406 }
Anton Chigrinbf14b372019-01-14 17:29:56 +0200407 h.deviceId = DeviceId.deviceId(uri(h.thisdpid));
alshabib09d48be2014-10-03 15:43:33 -0700408 log.debug("Received features reply for switch at {} with dpid {}",
tom7ef8ff92014-09-17 13:08:06 -0700409 h.getSwitchInfoString(), h.thisdpid);
410
411 h.featuresReply = m; //temp store
412 if (h.ofVersion == OFVersion.OF_10) {
413 h.sendHandshakeSetConfig();
414 h.setState(WAIT_CONFIG_REPLY);
415 } else {
416 //version is 1.3, must get switchport information
417 h.sendHandshakeOFPortDescRequest();
418 h.setState(WAIT_PORT_DESC_REPLY);
419 }
420 }
421 @Override
422 void processOFStatisticsReply(OFChannelHandler h,
423 OFStatsReply m)
424 throws IOException, SwitchStateException {
425 illegalMessageReceived(h, m);
426 }
427 @Override
428 void processOFError(OFChannelHandler h, OFErrorMsg m) {
429 logErrorDisconnect(h, m);
430 }
431
432 @Override
433 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
434 throws IOException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800435 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700436 }
437 },
438
439 /**
440 * We are waiting for a description of the 1.3 switch ports.
441 * Once received, we send a SetConfig request
442 * Next State is WAIT_CONFIG_REPLY
443 */
444 WAIT_PORT_DESC_REPLY(false) {
445
446 @Override
447 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
448 throws SwitchStateException {
449 // Read port description
450 if (m.getStatsType() != OFStatsType.PORT_DESC) {
451 log.warn("Expecting port description stats but received stats "
452 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700453 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700454 return;
455 }
456 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700457 log.debug("Stats reply indicates more stats from sw {} for "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700458 + "port description",
tom7ef8ff92014-09-17 13:08:06 -0700459 h.getSwitchInfoString());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800460 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700461 return;
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800462 } else {
463 h.portDescReplies.add((OFPortDescStatsReply) m);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700464 }
465 //h.portDescReply = (OFPortDescStatsReply) m; // temp store
Andrea Campanellafbec0992020-05-22 12:42:12 +0200466 if (log.isDebugEnabled()) {
467 log.debug("Received port desc reply for switch at {}: {}",
468 h.getSwitchInfoString(),
469 ((OFPortDescStatsReply) m).getEntries());
470 }
tom7ef8ff92014-09-17 13:08:06 -0700471 try {
472 h.sendHandshakeSetConfig();
473 } catch (IOException e) {
474 log.error("Unable to send setConfig after PortDescReply. "
475 + "Error: {}", e.getMessage());
476 }
477 h.setState(WAIT_CONFIG_REPLY);
478 }
479
480 @Override
481 void processOFError(OFChannelHandler h, OFErrorMsg m)
482 throws IOException, SwitchStateException {
483 logErrorDisconnect(h, m);
484
485 }
486
487 @Override
488 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
489 throws IOException, SwitchStateException {
Thomas Vachuska39274462014-12-02 13:23:50 -0800490 h.pendingPortStatusMsg.add(m);
tom7ef8ff92014-09-17 13:08:06 -0700491
492 }
493 },
494
495 /**
496 * We are waiting for a config reply message. Once we receive it
497 * we send a DescriptionStatsRequest to the switch.
498 * Next state: WAIT_DESCRIPTION_STAT_REPLY
499 */
500 WAIT_CONFIG_REPLY(false) {
501 @Override
502 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
503 throws IOException {
504 if (m.getMissSendLen() == 0xffff) {
505 log.trace("Config Reply from switch {} confirms "
506 + "miss length set to 0xffff",
507 h.getSwitchInfoString());
508 } else {
509 // FIXME: we can't really deal with switches that don't send
510 // full packets. Shouldn't we drop the connection here?
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800511 log.warn("Config Reply from switch {} has "
tom7ef8ff92014-09-17 13:08:06 -0700512 + "miss length set to {}",
513 h.getSwitchInfoString(),
514 m.getMissSendLen());
515 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100516
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800517 nextState(h);
518 }
519
520 /**
521 * Transition to next state based on OF version.
522 *
523 * @param h current channel handler
524 * @throws IOException
525 */
526 private void nextState(OFChannelHandler h) throws IOException {
527 if (h.ofVersion.getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100528 // Meters were introduced in OpenFlow 1.3
529 h.sendMeterFeaturesRequest();
530 h.setState(WAIT_METER_FEATURES_REPLY);
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800531 } else {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100532 h.sendHandshakeDescriptionStatsRequest();
533 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
534 }
tom7ef8ff92014-09-17 13:08:06 -0700535 }
536
537 @Override
538 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
539 // do nothing;
540 }
541
542 @Override
543 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
544 throws IOException, SwitchStateException {
545 illegalMessageReceived(h, m);
546 }
Anton Chigrinbf14b372019-01-14 17:29:56 +0200547
tom7ef8ff92014-09-17 13:08:06 -0700548 @Override
549 void processOFStatisticsReply(OFChannelHandler h,
550 OFStatsReply m)
551 throws IOException, SwitchStateException {
552 log.error("Received multipart(stats) message sub-type {}",
553 m.getStatsType());
554 illegalMessageReceived(h, m);
555 }
556
557 @Override
558 void processOFError(OFChannelHandler h, OFErrorMsg m) {
Yuta HIGUCHI2341e602017-03-08 20:10:08 -0800559 if (m.getErrType() == OFErrorType.BAD_REQUEST) {
560 OFBadRequestErrorMsg badRequest = (OFBadRequestErrorMsg) m;
561 if (badRequest.getCode() == OFBadRequestCode.BAD_TYPE) {
562 log.debug("{} does not support GetConfig, moving on", h.getSwitchInfoString());
563 try {
564 nextState(h);
565 return;
566 } catch (IOException e) {
567 log.error("Exception thrown transitioning to next", e);
568 logErrorDisconnect(h, m);
569 }
570 }
571 }
tom7ef8ff92014-09-17 13:08:06 -0700572 logErrorDisconnect(h, m);
573 }
574
575 @Override
576 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
577 throws IOException {
578 h.pendingPortStatusMsg.add(m);
579 }
580 },
581
tom7ef8ff92014-09-17 13:08:06 -0700582 /**
583 * We are waiting for a OFDescriptionStat message from the switch.
584 * Once we receive any stat message we try to parse it. If it's not
585 * a description stats message we disconnect. If its the expected
586 * description stats message, we:
587 * - use the switch driver to bind the switch and get an IOFSwitch instance
588 * - setup the IOFSwitch instance
589 * - add switch controller and send the initial role
590 * request to the switch.
591 * Next state: WAIT_INITIAL_ROLE
592 * In the typical case, where switches support role request messages
593 * the next state is where we expect the role reply message.
594 * In the special case that where the switch does not support any kind
595 * of role request messages, we don't send a role message, but we do
596 * request mastership from the registry service. This controller
597 * should become master once we hear back from the registry service.
598 * All following states will have a h.sw instance!
599 */
600 WAIT_DESCRIPTION_STAT_REPLY(false) {
601 @Override
602 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
603 throws SwitchStateException {
604 // Read description, if it has been updated
605 if (m.getStatsType() != OFStatsType.DESC) {
606 log.warn("Expecting Description stats but received stats "
607 + "type {} from {}. Ignoring ...", m.getStatsType(),
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700608 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700609 return;
610 }
tom7ef8ff92014-09-17 13:08:06 -0700611 OFDescStatsReply drep = (OFDescStatsReply) m;
Saurav Dasf9ba4222015-05-07 17:13:59 -0700612 log.info("Received switch description reply {} from switch at {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700613 drep, h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -0700614 // Here is where we differentiate between different kinds of switches
615 h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
616
Ray Milkey31b00482019-02-07 08:06:28 -0800617 if (h.sw == null) {
618 log.info("Switch not found for {}", h.thisdpid);
619 return;
620 }
621
Andrea Campanellafbec0992020-05-22 12:42:12 +0200622 // set switch information
tom7ef8ff92014-09-17 13:08:06 -0700623 h.sw.setOFVersion(h.ofVersion);
624 h.sw.setFeaturesReply(h.featuresReply);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700625 h.sw.setPortDescReplies(h.portDescReplies);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100626 h.sw.setMeterFeaturesReply(h.meterFeaturesReply);
tom7ef8ff92014-09-17 13:08:06 -0700627 h.sw.setConnected(true);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -0700628 h.sw.setChannel(h);
Andrea Campanellafbec0992020-05-22 12:42:12 +0200629
630 //Port Description List has served its purpose, clearing.
631 h.portDescReplies.clear();
632
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700633// boolean success = h.sw.connectSwitch();
634//
635// if (!success) {
636// disconnectDuplicate(h);
637// return;
638// }
tom7ef8ff92014-09-17 13:08:06 -0700639
alshabib09d48be2014-10-03 15:43:33 -0700640 log.debug("Switch {} bound to class {}, description {}",
Ray Milkey6bc43c22015-11-06 13:22:38 -0800641 h.sw, h.sw.getClass(), drep);
tom7ef8ff92014-09-17 13:08:06 -0700642 //Put switch in EQUAL mode until we hear back from the global registry
643 //log.debug("Setting new switch {} to EQUAL and sending Role request",
644 // h.sw.getStringId());
645 //h.sw.activateEqualSwitch();
646 //h.setSwitchRole(RoleState.EQUAL);
647
648 h.sw.startDriverHandshake();
alshabib9eab22f2014-10-20 17:17:31 -0700649 if (h.sw.isDriverHandshakeComplete()) {
pierc684ee12019-07-16 15:52:50 +0200650 // We are not able to complete the connection for a dpid collision.
651 // Same device reconnecting or different device configured with
652 // the same dpid.
alshabib9eab22f2014-10-20 17:17:31 -0700653 if (!h.sw.connectSwitch()) {
pierc684ee12019-07-16 15:52:50 +0200654 // Disconnect from the device and return
alshabib9eab22f2014-10-20 17:17:31 -0700655 disconnectDuplicate(h);
pierc684ee12019-07-16 15:52:50 +0200656 return;
Anton Chigrinbf14b372019-01-14 17:29:56 +0200657 } else {
658 h.initClassifiers();
alshabib9eab22f2014-10-20 17:17:31 -0700659 }
Thomas Vachuska39274462014-12-02 13:23:50 -0800660 handlePendingPortStatusMessages(h);
alshabib9eab22f2014-10-20 17:17:31 -0700661 h.setState(ACTIVE);
662 } else {
663 h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
664 }
tom7ef8ff92014-09-17 13:08:06 -0700665
666 }
667
668 @Override
669 void processOFError(OFChannelHandler h, OFErrorMsg m) {
670 logErrorDisconnect(h, m);
671 }
672
673 @Override
674 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
675 throws IOException, SwitchStateException {
676 illegalMessageReceived(h, m);
677 }
678
679 @Override
680 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
681 throws IOException {
682 h.pendingPortStatusMsg.add(m);
683 }
684 },
685
tom7ef8ff92014-09-17 13:08:06 -0700686 /**
687 * We are waiting for the respective switch driver to complete its
688 * configuration. Notice that we do not consider this to be part of the main
689 * switch-controller handshake. But we do consider it as a step that comes
690 * before we declare the switch as available to the controller.
691 * Next State: depends on the role of this controller for this switch - either
692 * MASTER or EQUAL.
693 */
694 WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
695
696 @Override
697 void processOFError(OFChannelHandler h, OFErrorMsg m)
698 throws IOException {
699 // will never be called. We override processOFMessage
700 }
701
702 @Override
703 void processOFMessage(OFChannelHandler h, OFMessage m)
704 throws IOException, SwitchStateException {
alshabibd7963912014-10-20 14:52:04 -0700705
706 if (h.sw.isDriverHandshakeComplete()) {
707 moveToActive(h);
alshabib9eab22f2014-10-20 17:17:31 -0700708 h.state.processOFMessage(h, m);
709 return;
alshabibd7963912014-10-20 14:52:04 -0700710
711 }
712
tom7ef8ff92014-09-17 13:08:06 -0700713 if (m.getType() == OFType.ECHO_REQUEST) {
714 processOFEchoRequest(h, (OFEchoRequest) m);
Praseed Balakrishnana22eadf2014-10-20 14:21:45 -0700715 } else if (m.getType() == OFType.ECHO_REPLY) {
716 processOFEchoReply(h, (OFEchoReply) m);
tom7ef8ff92014-09-17 13:08:06 -0700717 } else if (m.getType() == OFType.ROLE_REPLY) {
718 h.sw.handleRole(m);
719 } else if (m.getType() == OFType.ERROR) {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800720 if (!h.sw.handleRoleError((OFErrorMsg) m)) {
tom7ef8ff92014-09-17 13:08:06 -0700721 h.sw.processDriverHandshakeMessage(m);
722 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700723 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700724 }
725 }
726 } else {
727 if (m.getType() == OFType.EXPERIMENTER &&
728 ((OFExperimenter) m).getExperimenter() ==
729 RoleManager.NICIRA_EXPERIMENTER) {
730 h.sw.handleNiciraRole(m);
731 } else {
732 h.sw.processDriverHandshakeMessage(m);
733 if (h.sw.isDriverHandshakeComplete()) {
alshabibd7963912014-10-20 14:52:04 -0700734 moveToActive(h);
tom7ef8ff92014-09-17 13:08:06 -0700735 }
736 }
737 }
738 }
739
740 @Override
741 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
742 throws IOException, SwitchStateException {
743 h.pendingPortStatusMsg.add(m);
744 }
alshabibd7963912014-10-20 14:52:04 -0700745
746 private void moveToActive(OFChannelHandler h) {
747 boolean success = h.sw.connectSwitch();
pierc684ee12019-07-16 15:52:50 +0200748 // Disconnect from the device and return
alshabibd7963912014-10-20 14:52:04 -0700749 if (!success) {
750 disconnectDuplicate(h);
pierc684ee12019-07-16 15:52:50 +0200751 return;
alshabibd7963912014-10-20 14:52:04 -0700752 }
pierc684ee12019-07-16 15:52:50 +0200753 handlePendingPortStatusMessages(h);
754 h.setState(ACTIVE);
alshabibd7963912014-10-20 14:52:04 -0700755 }
756
tom7ef8ff92014-09-17 13:08:06 -0700757 },
758
Jordi Ortiz91477b82016-11-29 15:22:50 +0100759 /**
760 * We are expecting a OF Multi Part Meter Features Stats Reply.
761 * Notice that this information is only available for switches running
762 * OpenFlow 1.3
763 */
764 WAIT_METER_FEATURES_REPLY(true) {
Yuta HIGUCHI10f45132017-03-01 17:09:32 -0800765
766 @Override
767 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
768 throws IOException {
769 super.processOFEchoRequest(h, m);
770 if (System.currentTimeMillis() - h.lastStateChange > METER_TIMEOUT) {
771 log.info("{} did not respond to MeterFeaturesRequest on time, " +
772 "moving on without it.",
773 h.getSwitchInfoString());
774 h.sendHandshakeDescriptionStatsRequest();
775 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
776 }
777 }
778
Jordi Ortiz91477b82016-11-29 15:22:50 +0100779 @Override
780 void processOFError(OFChannelHandler h, OFErrorMsg m)
781 throws IOException {
Charles Chan34155e52016-11-30 18:28:11 -0800782 // Hardware switches may reply OFErrorMsg if meter is not supported
783 log.warn("Received OFError {}. It seems {} does not support Meter.",
784 m.getErrType().name(), Dpid.uri(h.thisdpid));
785 log.debug("{}", m);
786 h.sendHandshakeDescriptionStatsRequest();
787 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
Jordi Ortiz91477b82016-11-29 15:22:50 +0100788 }
789
790 @Override
791 void processOFStatisticsReply(OFChannelHandler h,
792 OFStatsReply m)
793 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800794 switch (m.getStatsType()) {
Jordi Ortiz91477b82016-11-29 15:22:50 +0100795 case METER_FEATURES:
796
797 log.debug("Received Meter Features");
Yuta HIGUCHI605758e2017-01-13 20:26:44 -0800798 OFMeterFeaturesStatsReply ofmfsr = (OFMeterFeaturesStatsReply) m;
Jordi Ortiz91477b82016-11-29 15:22:50 +0100799 log.info("Received meter features from {} with max meters: {}",
800 h.getSwitchInfoString(),
801 ofmfsr.getFeatures().getMaxMeter());
802 h.meterFeaturesReply = ofmfsr;
803 h.sendHandshakeDescriptionStatsRequest();
804 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
805 break;
806 default:
807 log.error("Unexpected OF Multi Part stats reply");
808 illegalMessageReceived(h, m);
809 break;
810 }
811 }
812
813 @Override
814 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
815 throws IOException, SwitchStateException {
816 illegalMessageReceived(h, m);
817 }
818
819 @Override
820 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
821 throws IOException {
822 h.pendingPortStatusMsg.add(m);
823 }
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800824
825 @Override
826 void processIdle(OFChannelHandler h) throws IOException {
827 log.info("{} did not respond to MeterFeaturesRequest, " +
828 "moving on without it.",
829 h.getSwitchInfoString());
830 h.sendHandshakeDescriptionStatsRequest();
831 h.setState(WAIT_DESCRIPTION_STAT_REPLY);
832 }
Jordi Ortiz91477b82016-11-29 15:22:50 +0100833 },
834
tom7ef8ff92014-09-17 13:08:06 -0700835 /**
836 * This controller is in MASTER role for this switch. We enter this state
837 * after requesting and winning control from the global registry.
838 * The main handshake as well as the switch-driver sub-handshake
839 * is complete at this point.
840 * // XXX S reconsider below
841 * In the (near) future we may deterministically assign controllers to
842 * switches at startup.
843 * We only leave this state if the switch disconnects or
844 * if we send a role request for SLAVE /and/ receive the role reply for
845 * SLAVE.
846 */
847 ACTIVE(true) {
848 @Override
849 void processOFError(OFChannelHandler h, OFErrorMsg m)
850 throws IOException, SwitchStateException {
851 // if we get here, then the error message is for something else
852 if (m.getErrType() == OFErrorType.BAD_REQUEST &&
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200853 (((OFBadRequestErrorMsg) m).getCode() == OFBadRequestCode.EPERM ||
854 ((OFBadRequestErrorMsg) m).getCode() == OFBadRequestCode.IS_SLAVE)) {
tom7ef8ff92014-09-17 13:08:06 -0700855 // We are the master controller and the switch returned
856 // a permission error. This is a likely indicator that
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200857 // the switch thinks we are slave. Reassert our role
tom7ef8ff92014-09-17 13:08:06 -0700858 // FIXME: this could be really bad during role transitions
859 // if two controllers are master (even if its only for
860 // a brief period). We might need to see if these errors
861 // persist before we reassert
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200862 // Scheduling in the executor to keep in line with other status events.
863 h.runtimeStatusExecutor.submit(() -> {
864 h.sw.reassertRole();
865 });
tom7ef8ff92014-09-17 13:08:06 -0700866 } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
867 ((OFFlowModFailedErrorMsg) m).getCode() ==
868 OFFlowModFailedCode.ALL_TABLES_FULL) {
869 h.sw.setTableFull(true);
870 } else {
871 logError(h, m);
872 }
873 h.dispatchMessage(m);
874 }
875
876 @Override
877 void processOFStatisticsReply(OFChannelHandler h,
878 OFStatsReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700879 if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
Andrea Campanellafbec0992020-05-22 12:42:12 +0200880 if (log.isDebugEnabled()) {
881 log.debug("Received port desc message from {}: {}",
882 h.sw.getDpid(),
883 ((OFPortDescStatsReply) m).getEntries());
884 }
885 if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
886 log.debug("Active Stats reply indicates more stats from sw {} for "
887 + "port description",
888 h.getSwitchInfoString());
889 h.portDescReplies.add((OFPortDescStatsReply) m);
890 h.dispatchMessage(m);
891 return;
892 }
893
894 h.portDescReplies.add((OFPortDescStatsReply) m);
895 if (log.isDebugEnabled()) {
896 log.debug("Adding all Port Desc Active Replies to {}: {}",
897 h.sw.getDpid(),
898 h.portDescReplies);
899 }
900 h.sw.setPortDescReplies(h.portDescReplies);
901 //clearing to wait for next full response
902 h.portDescReplies.clear();
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700903 }
tom7ef8ff92014-09-17 13:08:06 -0700904 h.dispatchMessage(m);
905 }
906
907 @Override
908 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
909 throws SwitchStateException {
910 h.sw.handleNiciraRole(m);
911 }
912
913 @Override
914 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
915 throws SwitchStateException {
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200916 h.runtimeStatusExecutor.execute(() -> {
917 try {
918 h.sw.handleRole(m);
919 } catch (SwitchStateException e) {
920 log.error("SwitchStateException while processing " +
921 "role reply message {}", m, e);
922 log.error("Disconnecting switch {} due to switch state error: {}",
923 h.getSwitchInfoString(), e.getMessage());
924 h.channel.close();
925 }
926 });
tom7ef8ff92014-09-17 13:08:06 -0700927 }
928
929 @Override
930 void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
931 throws SwitchStateException {
Andrea Campanella9ef930a2020-11-06 21:41:01 +0100932 // Handing over processing of port status messages to a thread to avoid
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200933 // getting blocked on the main thread and resulting other OF message being delayed.
934 // Ordering of the status messages is guaranteed by runtimeStatsExecutor being a single
935 // threaded executor. This executor will execute concurrently to the netty thread; meaning
936 // that the order is no more guaranteed like it was in the past between different
937 // status handling messages and other messages: statistics (port, flows, meters, groups)
938 // barriers, idle, features, packet-ins handled inline to the netty thread. This executor
939 // will only apply to messages during the ACTIVE state of the connection.
940 h.runtimeStatusExecutor.execute(() -> {
Andrea Campanella9ef930a2020-11-06 21:41:01 +0100941 try {
942 handlePortStatusMessage(h, m, true);
943 } catch (SwitchStateException e) {
944 log.error("SwitchStateException while processing " +
945 "port status message {}", m, e);
Andrea Campanellab0b93ac2021-09-13 12:37:36 +0200946 log.error("Disconnecting switch {} due to switch state error: {}",
947 h.getSwitchInfoString(), e.getMessage());
948 h.channel.close();
Andrea Campanella9ef930a2020-11-06 21:41:01 +0100949 }
950 });
Thomas Vachuska39274462014-12-02 13:23:50 -0800951 //h.dispatchMessage(m);
tom7ef8ff92014-09-17 13:08:06 -0700952 }
953
954 @Override
955 void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
alshabib9eab22f2014-10-20 17:17:31 -0700956// OFPacketOut out =
957// h.sw.factory().buildPacketOut()
958// .setXid(m.getXid())
959// .setBufferId(m.getBufferId()).build();
960// h.sw.sendMsg(out);
tom7ef8ff92014-09-17 13:08:06 -0700961 h.dispatchMessage(m);
962 }
963
964 @Override
965 void processOFFlowRemoved(OFChannelHandler h,
966 OFFlowRemoved m) {
967 h.dispatchMessage(m);
968 }
969
970 @Override
971 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
972 h.dispatchMessage(m);
973 }
974
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700975 @Override
976 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m) {
Ayaka Koshibe38594c22014-10-22 13:36:12 -0700977 h.sw.setFeaturesReply(m);
Ayaka Koshibee8708e32014-10-22 13:40:18 -0700978 h.dispatchMessage(m);
979 }
980
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -0800981 @Override
982 void processIdle(OFChannelHandler h) throws IOException {
983 log.info("{} idle", h.getSwitchInfoString());
984 }
985
tom7ef8ff92014-09-17 13:08:06 -0700986 };
987
988 private final boolean handshakeComplete;
989 ChannelState(boolean handshakeComplete) {
990 this.handshakeComplete = handshakeComplete;
991 }
992
993 /**
994 * Is this a state in which the handshake has completed?
995 * @return true if the handshake is complete
996 */
997 public boolean isHandshakeComplete() {
998 return handshakeComplete;
999 }
1000
1001 /**
1002 * Get a string specifying the switch connection, state, and
1003 * message received. To be used as message for SwitchStateException
1004 * or log messages
1005 * @param h The channel handler (to get switch information_
1006 * @param m The OFMessage that has just been received
1007 * @param details A string giving more details about the exact nature
1008 * of the problem.
1009 * @return display string
1010 */
1011 // needs to be protected because enum members are actually subclasses
1012 protected String getSwitchStateMessage(OFChannelHandler h,
1013 OFMessage m,
1014 String details) {
1015 return String.format("Switch: [%s], State: [%s], received: [%s]"
1016 + ", details: %s",
1017 h.getSwitchInfoString(),
1018 this.toString(),
1019 m.getType().toString(),
1020 details);
1021 }
1022
1023 /**
1024 * We have an OFMessage we didn't expect given the current state and
1025 * we want to treat this as an error.
1026 * We currently throw an exception that will terminate the connection
1027 * However, we could be more forgiving
1028 * @param h the channel handler that received the message
1029 * @param m the message
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001030 * @throws SwitchStateException we always throw the exception
tom7ef8ff92014-09-17 13:08:06 -07001031 */
Jonathan Hart147b2ac2014-10-23 10:03:52 -07001032 // needs to be protected because enum members are actually subclasses
tom7ef8ff92014-09-17 13:08:06 -07001033 protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
1034 throws SwitchStateException {
1035 String msg = getSwitchStateMessage(h, m,
1036 "Switch should never send this message in the current state");
1037 throw new SwitchStateException(msg);
1038
1039 }
1040
1041 /**
1042 * We have an OFMessage we didn't expect given the current state and
1043 * we want to ignore the message.
1044 * @param h the channel handler the received the message
1045 * @param m the message
1046 */
1047 protected void unhandledMessageReceived(OFChannelHandler h,
1048 OFMessage m) {
1049 if (log.isDebugEnabled()) {
1050 String msg = getSwitchStateMessage(h, m,
1051 "Ignoring unexpected message");
1052 log.debug(msg);
1053 }
1054 }
1055
1056 /**
1057 * Log an OpenFlow error message from a switch.
1058 * @param h The switch that sent the error
1059 * @param error The error message
1060 */
1061 protected void logError(OFChannelHandler h, OFErrorMsg error) {
alshabib09d48be2014-10-03 15:43:33 -07001062 log.error("{} from switch {} in state {}",
tom7ef8ff92014-09-17 13:08:06 -07001063 error,
1064 h.getSwitchInfoString(),
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001065 this);
tom7ef8ff92014-09-17 13:08:06 -07001066 }
1067
1068 /**
1069 * Log an OpenFlow error message from a switch and disconnect the
1070 * channel.
1071 *
1072 * @param h the IO channel for this switch.
1073 * @param error The error message
1074 */
1075 protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
1076 logError(h, error);
HIGUCHI Yutadc5cf8a2016-04-29 15:17:06 -07001077 log.error("Disconnecting switch {}", h.getSwitchInfoString());
tom7ef8ff92014-09-17 13:08:06 -07001078 h.channel.disconnect();
1079 }
1080
1081 /**
1082 * log an error message for a duplicate dpid and disconnect this channel.
1083 * @param h the IO channel for this switch.
1084 */
1085 protected void disconnectDuplicate(OFChannelHandler h) {
1086 log.error("Duplicated dpid or incompleted cleanup - "
1087 + "disconnecting channel {}", h.getSwitchInfoString());
1088 h.duplicateDpidFound = Boolean.TRUE;
1089 h.channel.disconnect();
1090 }
1091
tom7ef8ff92014-09-17 13:08:06 -07001092 /**
1093 * Handles all pending port status messages before a switch is declared
1094 * activated in MASTER or EQUAL role. Note that since this handling
1095 * precedes the activation (and therefore notification to IOFSwitchListerners)
1096 * the changes to ports will already be visible once the switch is
1097 * activated. As a result, no notifications are sent out for these
1098 * pending portStatus messages.
Thomas Vachuska4b420772014-10-30 16:46:17 -07001099 *
1100 * @param h the channel handler that received the message
tom7ef8ff92014-09-17 13:08:06 -07001101 */
1102 protected void handlePendingPortStatusMessages(OFChannelHandler h) {
1103 try {
1104 handlePendingPortStatusMessages(h, 0);
1105 } catch (SwitchStateException e) {
1106 log.error(e.getMessage());
1107 }
1108 }
1109
1110 private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
1111 throws SwitchStateException {
1112 if (h.sw == null) {
1113 String msg = "State machine error: switch is null. Should never " +
1114 "happen";
1115 throw new SwitchStateException(msg);
1116 }
Thomas Vachuska39274462014-12-02 13:23:50 -08001117 log.info("Processing {} pending port status messages for {}",
1118 h.pendingPortStatusMsg.size(), h.sw.getStringId());
1119
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001120 ArrayList<OFPortStatus> temp = new ArrayList<>();
tom7ef8ff92014-09-17 13:08:06 -07001121 for (OFPortStatus ps: h.pendingPortStatusMsg) {
1122 temp.add(ps);
1123 handlePortStatusMessage(h, ps, false);
1124 }
tom7ef8ff92014-09-17 13:08:06 -07001125 // expensive but ok - we don't expect too many port-status messages
1126 // note that we cannot use clear(), because of the reasons below
1127 h.pendingPortStatusMsg.removeAll(temp);
Thomas Vachuska39274462014-12-02 13:23:50 -08001128 temp.clear();
tom7ef8ff92014-09-17 13:08:06 -07001129 // the iterator above takes a snapshot of the list - so while we were
1130 // dealing with the pending port-status messages, we could have received
1131 // newer ones. Handle them recursively, but break the recursion after
1132 // five steps to avoid an attack.
1133 if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
1134 handlePendingPortStatusMessages(h, index);
1135 }
1136 }
1137
1138 /**
1139 * Handle a port status message.
1140 *
1141 * Handle a port status message by updating the port maps in the
1142 * IOFSwitch instance and notifying Controller about the change so
1143 * it can dispatch a switch update.
1144 *
1145 * @param h The OFChannelHhandler that received the message
1146 * @param m The PortStatus message we received
1147 * @param doNotify if true switch port changed events will be
1148 * dispatched
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001149 * @throws SwitchStateException if the switch is not bound to the channel
tom7ef8ff92014-09-17 13:08:06 -07001150 *
1151 */
1152 protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
1153 boolean doNotify) throws SwitchStateException {
1154 if (h.sw == null) {
1155 String msg = getSwitchStateMessage(h, m,
1156 "State machine error: switch is null. Should never " +
1157 "happen");
1158 throw new SwitchStateException(msg);
1159 }
Saurav Dasbd071d82018-01-09 17:38:44 -08001160 log.info("Received port status message from {}/{}: {}",
1161 h.sw.getDpid(), m.getDesc().getPortNo(), m);
tom7ef8ff92014-09-17 13:08:06 -07001162
1163 h.sw.handleMessage(m);
1164 }
1165
tom7ef8ff92014-09-17 13:08:06 -07001166 /**
1167 * Process an OF message received on the channel and
1168 * update state accordingly.
1169 *
1170 * The main "event" of the state machine. Process the received message,
1171 * send follow up message if required and update state if required.
1172 *
1173 * Switches on the message type and calls more specific event handlers
1174 * for each individual OF message type. If we receive a message that
1175 * is supposed to be sent from a controller to a switch we throw
1176 * a SwitchStateExeption.
1177 *
1178 * The more specific handlers can also throw SwitchStateExceptions
1179 *
1180 * @param h The OFChannelHandler that received the message
1181 * @param m The message we received.
Thomas Vachuskab14c77a2014-11-04 18:08:01 -08001182 * @throws SwitchStateException if the switch is not bound to the channel
1183 * @throws IOException if unable to send message back to the switch
tom7ef8ff92014-09-17 13:08:06 -07001184 */
1185 void processOFMessage(OFChannelHandler h, OFMessage m)
1186 throws IOException, SwitchStateException {
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001187 switch (m.getType()) {
tom7ef8ff92014-09-17 13:08:06 -07001188 case HELLO:
1189 processOFHello(h, (OFHello) m);
1190 break;
1191 case BARRIER_REPLY:
1192 processOFBarrierReply(h, (OFBarrierReply) m);
1193 break;
1194 case ECHO_REPLY:
1195 processOFEchoReply(h, (OFEchoReply) m);
1196 break;
1197 case ECHO_REQUEST:
1198 processOFEchoRequest(h, (OFEchoRequest) m);
1199 break;
1200 case ERROR:
1201 processOFError(h, (OFErrorMsg) m);
1202 break;
1203 case FEATURES_REPLY:
1204 processOFFeaturesReply(h, (OFFeaturesReply) m);
1205 break;
1206 case FLOW_REMOVED:
1207 processOFFlowRemoved(h, (OFFlowRemoved) m);
1208 break;
1209 case GET_CONFIG_REPLY:
1210 processOFGetConfigReply(h, (OFGetConfigReply) m);
1211 break;
1212 case PACKET_IN:
1213 processOFPacketIn(h, (OFPacketIn) m);
1214 break;
1215 case PORT_STATUS:
1216 processOFPortStatus(h, (OFPortStatus) m);
1217 break;
1218 case QUEUE_GET_CONFIG_REPLY:
1219 processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
1220 break;
1221 case STATS_REPLY: // multipart_reply in 1.3
1222 processOFStatisticsReply(h, (OFStatsReply) m);
1223 break;
1224 case EXPERIMENTER:
1225 processOFExperimenter(h, (OFExperimenter) m);
1226 break;
1227 case ROLE_REPLY:
1228 processOFRoleReply(h, (OFRoleReply) m);
1229 break;
1230 case GET_ASYNC_REPLY:
1231 processOFGetAsyncReply(h, (OFAsyncGetReply) m);
1232 break;
1233
1234 // The following messages are sent to switches. The controller
1235 // should never receive them
1236 case SET_CONFIG:
1237 case GET_CONFIG_REQUEST:
1238 case PACKET_OUT:
1239 case PORT_MOD:
1240 case QUEUE_GET_CONFIG_REQUEST:
1241 case BARRIER_REQUEST:
1242 case STATS_REQUEST: // multipart request in 1.3
1243 case FEATURES_REQUEST:
1244 case FLOW_MOD:
1245 case GROUP_MOD:
1246 case TABLE_MOD:
1247 case GET_ASYNC_REQUEST:
1248 case SET_ASYNC:
1249 case METER_MOD:
1250 default:
1251 illegalMessageReceived(h, m);
1252 break;
1253 }
1254 }
1255
1256 /*-----------------------------------------------------------------
1257 * Default implementation for message handlers in any state.
1258 *
1259 * Individual states must override these if they want a behavior
1260 * that differs from the default.
1261 *
1262 * In general, these handlers simply ignore the message and do
1263 * nothing.
1264 *
1265 * There are some exceptions though, since some messages really
1266 * are handled the same way in every state (e.g., ECHO_REQUST) or
1267 * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
1268 -----------------------------------------------------------------*/
1269
1270 void processOFHello(OFChannelHandler h, OFHello m)
1271 throws IOException, SwitchStateException {
1272 // we only expect hello in the WAIT_HELLO state
alshabib45fd88a2015-09-24 17:34:35 -07001273 log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001274 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001275 }
1276
1277 void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
1278 throws IOException {
1279 // Silently ignore.
1280 }
1281
1282 void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
1283 throws IOException {
1284 if (h.ofVersion == null) {
1285 log.error("No OF version set for {}. Not sending Echo REPLY",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001286 h.channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001287 return;
1288 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001289 OFEchoReply reply = h.factory
1290 .buildEchoReply()
1291 .setXid(m.getXid())
1292 .setData(m.getData())
1293 .build();
1294 h.channel.writeAndFlush(Collections.singletonList(reply));
tom7ef8ff92014-09-17 13:08:06 -07001295 }
1296
1297 void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
1298 throws IOException {
1299 // Do nothing with EchoReplies !!
1300 }
1301
1302 // no default implementation for OFError
1303 // every state must override it
1304 abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
1305 throws IOException, SwitchStateException;
1306
1307
1308 void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply m)
1309 throws IOException, SwitchStateException {
1310 unhandledMessageReceived(h, m);
1311 }
1312
1313 void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
1314 throws IOException {
1315 unhandledMessageReceived(h, m);
1316 }
1317
1318 void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
1319 throws IOException, SwitchStateException {
1320 // we only expect config replies in the WAIT_CONFIG_REPLY state
1321 illegalMessageReceived(h, m);
1322 }
1323
1324 void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
1325 throws IOException {
1326 unhandledMessageReceived(h, m);
1327 }
1328
1329 // no default implementation. Every state needs to handle it.
1330 abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
1331 throws IOException, SwitchStateException;
1332
1333 void processOFQueueGetConfigReply(OFChannelHandler h,
1334 OFQueueGetConfigReply m)
1335 throws IOException {
1336 unhandledMessageReceived(h, m);
1337 }
1338
1339 void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
1340 throws IOException, SwitchStateException {
1341 unhandledMessageReceived(h, m);
1342 }
1343
1344 void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
1345 throws IOException, SwitchStateException {
1346 // TODO: it might make sense to parse the vendor message here
1347 // into the known vendor messages we support and then call more
1348 // specific event handlers
1349 unhandledMessageReceived(h, m);
1350 }
1351
1352 void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
1353 throws SwitchStateException, IOException {
1354 unhandledMessageReceived(h, m);
1355 }
1356
1357 void processOFGetAsyncReply(OFChannelHandler h,
1358 OFAsyncGetReply m) {
1359 unhandledMessageReceived(h, m);
1360 }
1361
Yuta HIGUCHI1745e5a2017-01-15 21:43:02 -08001362 void processIdle(OFChannelHandler h) throws IOException {
1363 // disconnect channel which did no complete handshake
1364 log.error("{} idle in state {}, disconnecting", h.getSwitchInfoString(), this);
1365 h.channel.disconnect();
1366 }
tom7ef8ff92014-09-17 13:08:06 -07001367 }
1368
tom7ef8ff92014-09-17 13:08:06 -07001369 //*************************
1370 // Channel handler methods
1371 //*************************
1372
1373 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001374 public void channelActive(ChannelHandlerContext ctx)
1375 throws Exception {
1376
1377 channel = ctx.channel();
tom7ef8ff92014-09-17 13:08:06 -07001378 log.info("New switch connection from {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001379 channel.remoteAddress());
1380
1381 SocketAddress address = channel.remoteAddress();
1382 if (address instanceof InetSocketAddress) {
1383 final InetSocketAddress inetAddress = (InetSocketAddress) address;
1384 final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
1385 if (ipAddress.isIp4()) {
1386 channelId = ipAddress.toString() + ':' + inetAddress.getPort();
1387 } else {
1388 channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
1389 }
1390 } else {
1391 channelId = channel.toString();
1392 }
1393
1394 dispatcher = Executors.newSingleThreadExecutor(groupedThreads("onos/of/dispatcher", channelId, log));
1395
alshabib70fc7fb2015-01-06 11:04:29 -08001396 /*
1397 hack to wait for the switch to tell us what it's
1398 max version is. This is not spec compliant and should
1399 be removed as soon as switches behave better.
1400 */
1401 //sendHandshakeHelloMessage();
tom7ef8ff92014-09-17 13:08:06 -07001402 setState(ChannelState.WAIT_HELLO);
1403 }
1404
1405 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001406 public void channelInactive(ChannelHandlerContext ctx)
1407 throws Exception {
tom7ef8ff92014-09-17 13:08:06 -07001408 log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001409 getSwitchInfoString());
1410
1411 if (dispatcher != null) {
Harold Huang828cd592017-11-04 10:46:04 +08001412 dispatcher.shutdownNow();
Thomas Vachuskad75684a2018-01-03 09:04:47 -08001413 dispatcher = null;
tom7ef8ff92014-09-17 13:08:06 -07001414 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001415
Andrea Campanellab0b93ac2021-09-13 12:37:36 +02001416 if (thisdpid != 0) {
1417 if (!duplicateDpidFound) {
1418 // if the disconnected switch (on this ChannelHandler)
1419 // was not one with a duplicate-dpid, it is safe to remove all
1420 // state for it at the controller. Notice that if the disconnected
1421 // switch was a duplicate-dpid, calling the method below would clear
1422 // all state for the original switch (with the same dpid),
1423 // which we obviously don't want.
1424 runtimeStatusExecutor.submit(() -> {
1425 log.info("{}:removal called", getSwitchInfoString());
1426 if (sw != null) {
1427 sw.removeConnectedSwitch();
1428 }
1429 });
1430 } else {
1431 // A duplicate was disconnected on this ChannelHandler,
1432 // this is the same switch reconnecting, but the original state was
1433 // not cleaned up - XXX check liveness of original ChannelHandler
1434 log.info("{}:duplicate found", getSwitchInfoString());
1435 duplicateDpidFound = Boolean.FALSE;
1436 }
1437 } else {
1438 log.warn("no dpid in channelHandler registered for "
1439 + "disconnected switch {}", getSwitchInfoString());
1440 }
tom7ef8ff92014-09-17 13:08:06 -07001441 }
1442
1443 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001444 public void exceptionCaught(ChannelHandlerContext ctx,
1445 Throwable cause)
tom7ef8ff92014-09-17 13:08:06 -07001446 throws Exception {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001447
1448 if (cause instanceof ReadTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001449 // switch timeout
1450 log.error("Disconnecting switch {} due to read timeout",
1451 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001452 ctx.channel().close();
1453 } else if (cause instanceof HandshakeTimeoutException) {
tom7ef8ff92014-09-17 13:08:06 -07001454 log.error("Disconnecting switch {}: failed to complete handshake",
1455 getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001456 ctx.channel().close();
1457 } else if (cause instanceof ClosedChannelException) {
tom7ef8ff92014-09-17 13:08:06 -07001458 log.debug("Channel for sw {} already closed", getSwitchInfoString());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001459 } else if (cause instanceof IOException) {
1460 if (!cause.getMessage().equals(RESET_BY_PEER) &&
1461 !cause.getMessage().equals(BROKEN_PIPE)) {
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001462 log.error("Disconnecting switch {} due to IO Error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001463 getSwitchInfoString(), cause.getMessage());
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001464 if (log.isDebugEnabled()) {
1465 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001466 log.debug("StackTrace for previous Exception: ", cause);
Thomas Vachuskae9af1f42015-07-06 08:42:18 -07001467 }
tom7ef8ff92014-09-17 13:08:06 -07001468 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001469 ctx.channel().close();
1470 } else if (cause instanceof SwitchStateException) {
tom7ef8ff92014-09-17 13:08:06 -07001471 log.error("Disconnecting switch {} due to switch state error: {}",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001472 getSwitchInfoString(), cause.getMessage());
tom7ef8ff92014-09-17 13:08:06 -07001473 if (log.isDebugEnabled()) {
1474 // still print stack trace if debug is enabled
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001475 log.debug("StackTrace for previous Exception: ", cause);
tom7ef8ff92014-09-17 13:08:06 -07001476 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001477 ctx.channel().close();
1478 } else if (cause instanceof OFParseError) {
tom7ef8ff92014-09-17 13:08:06 -07001479 log.error("Disconnecting switch "
1480 + getSwitchInfoString() +
1481 " due to message parse failure",
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001482 cause);
1483 ctx.channel().close();
1484 } else if (cause instanceof RejectedExecutionException) {
tom7ef8ff92014-09-17 13:08:06 -07001485 log.warn("Could not process message: queue full");
1486 } else {
1487 log.error("Error while processing message from switch "
1488 + getSwitchInfoString()
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001489 + "state " + this.state, cause);
1490 ctx.channel().close();
tom7ef8ff92014-09-17 13:08:06 -07001491 }
1492 }
1493
1494 @Override
1495 public String toString() {
1496 return getSwitchInfoString();
1497 }
1498
pierf528eff2019-11-22 20:51:26 +01001499 // We have reduced the idle period, the idea is to use
1500 // the IdleHandler to perform also some sanity checks.
1501 // Previous code is still executed with the same frequency
1502 // which is IDLE_INTERVAL * MAX_IDLE_RETRY of inactivity
Ray Milkey986a47a2018-01-25 11:38:51 -08001503 private void channelIdle(ChannelHandlerContext ctx,
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001504 IdleStateEvent e)
Ray Milkey986a47a2018-01-25 11:38:51 -08001505 throws IOException {
pierf528eff2019-11-22 20:51:26 +01001506 // dispatcher terminated for some reason, restart
1507 if (dispatcherHandle.isDone()) {
1508 dispatcherHandle = dispatcher.submit(new Dispatcher());
Charles Chan982d3902018-03-21 14:58:53 -07001509 }
pierf528eff2019-11-22 20:51:26 +01001510 // drain the backlog
1511 processDispatchBacklogQueue();
1512 // Original timeout reached
1513 if (--maxIdleRetry == 0) {
1514 maxIdleRetry = MAX_IDLE_RETRY;
1515 // Factory can be null if the channel goes idle during initial handshake. Since the switch
1516 // is not even initialized properly, we just skip this and disconnect the channel.
1517 if (factory != null) {
1518 // send an echo request each time idle_timeout * TICK
1519 OFMessage m = factory.buildEchoRequest().build();
1520 log.info("Sending Echo Request on idle channel: {}", ctx.channel());
1521 // XXX S some problems here -- echo request has no transaction id, and
1522 // echo reply is not correlated to the echo request.
1523 ctx.writeAndFlush(Collections.singletonList(m), ctx.voidPromise());
1524 }
1525 state.processIdle(this);
1526 }
tom7ef8ff92014-09-17 13:08:06 -07001527 }
1528
1529 @Override
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001530 public void userEventTriggered(ChannelHandlerContext ctx,
1531 Object evt)
tom7ef8ff92014-09-17 13:08:06 -07001532 throws Exception {
pieraeb7dbc2019-10-24 16:53:25 +02001533 // If the connection is READER/WRITER idle try to send an echo request
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001534 if (evt instanceof IdleStateEvent) {
pierf528eff2019-11-22 20:51:26 +01001535 log.debug("Channel {} is {}", ctx.channel(), ((IdleStateEvent) evt).state());
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001536 channelIdle(ctx, (IdleStateEvent) evt);
pieraeb7dbc2019-10-24 16:53:25 +02001537 } else {
1538 super.userEventTriggered(ctx, evt);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001539 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001540 }
1541
1542 // SimpleChannelInboundHandler without dependency to TypeParameterMatcher
1543 @Override
1544 public void channelRead(ChannelHandlerContext ctx,
1545 Object msg) throws Exception {
1546
1547 boolean release = true;
pierf528eff2019-11-22 20:51:26 +01001548 maxIdleRetry = MAX_IDLE_RETRY;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001549 try {
1550 if (msg instanceof OFMessage) {
1551 // channelRead0 inlined
1552 state.processOFMessage(this, (OFMessage) msg);
1553 } else {
1554 release = false;
1555 ctx.fireChannelRead(msg);
tom7ef8ff92014-09-17 13:08:06 -07001556 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001557 } finally {
1558 if (release) {
1559 ReferenceCountUtil.release(msg);
1560 }
tom7ef8ff92014-09-17 13:08:06 -07001561 }
1562 }
1563
1564
1565
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001566
tom7ef8ff92014-09-17 13:08:06 -07001567 //*************************
1568 // Channel utility methods
1569 //*************************
1570
1571 /**
1572 * Is this a state in which the handshake has completed?
Anton Chigrinbf14b372019-01-14 17:29:56 +02001573 *
tom7ef8ff92014-09-17 13:08:06 -07001574 * @return true if the handshake is complete
1575 */
1576 public boolean isHandshakeComplete() {
1577 return this.state.isHandshakeComplete();
1578 }
1579
Anton Chigrinbf14b372019-01-14 17:29:56 +02001580 /**
1581 * Increment totalCount variable and send signal to executor.
1582 */
1583 private void incrementAndSignal() {
1584 try {
1585 totalCount.incrementAndGet();
1586 takeLock.lockInterruptibly();
1587 try {
1588 notEmpty.signal();
1589 } finally {
1590 takeLock.unlock();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001591 }
Anton Chigrinbf14b372019-01-14 17:29:56 +02001592 } catch (InterruptedException e) {
1593 e.printStackTrace();
1594 }
1595 }
1596
1597 /**
1598 * Try to push OpenFlow message to queue.
1599 *
1600 * @param message OpenFlow message
1601 * @param idQueue id of Queue
1602 * @return true if message was successful added to queue
1603 */
1604 private boolean pushMessageToQueue(OFMessage message, int idQueue) {
1605 if (!dispatchQueuesMapProducer.get(idQueue).offer(message)) {
1606 return false;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001607 } else {
Anton Chigrinbf14b372019-01-14 17:29:56 +02001608 incrementAndSignal();
1609 return true;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001610 }
Anton Chigrinbf14b372019-01-14 17:29:56 +02001611 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001612
Anton Chigrinbf14b372019-01-14 17:29:56 +02001613 /**
1614 * Process backlog - move messages from backlog to default queue.
1615 *
1616 * @return true if whole backlog was processed, otherwise false
1617 */
1618 private boolean processDispatchBacklogQueue() {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001619 while (!dispatchBacklog.isEmpty()) {
Anton Chigrinbf14b372019-01-14 17:29:56 +02001620 OFMessage msgFromBacklog = dispatchBacklog.removeFirst();
1621 if (!pushMessageToQueue(msgFromBacklog, NUM_OF_QUEUES - 1)) {
1622 dispatchBacklog.addFirst(msgFromBacklog);
1623 return false;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001624 }
1625 }
Anton Chigrinbf14b372019-01-14 17:29:56 +02001626 return true;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001627
Anton Chigrinbf14b372019-01-14 17:29:56 +02001628 }
1629
1630 /**
1631 * Parse OpenFlow message context for get Ethernet packet.
1632 *
1633 * @param message OpenFlow message
1634 * @return parsed Ethernet packet
1635 */
1636 private Ethernet parsePacketInMessage(OFMessage message) {
1637 OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
1638 .packetContextFromPacketIn(sw, (OFPacketIn) message);
1639 DeviceId id = DeviceId.deviceId(Dpid.uri(pktCtx.dpid().value()));
1640 DefaultInboundPacket inPkt = new DefaultInboundPacket(
1641 new ConnectPoint(id, PortNumber.portNumber(pktCtx.inPort())),
1642 pktCtx.parsed(), ByteBuffer.wrap(pktCtx.unparsed()),
1643 pktCtx.cookie());
1644 return inPkt.parsed();
1645 }
1646
1647 /**
1648 * Classify the Ethernet packet for membership on one of the queues.
1649 *
1650 * @param packet ethernet packet
1651 * @return Id of destination Queue
1652 */
1653 private int classifyEthernetPacket(Ethernet packet) {
1654 for (Set<OpenFlowClassifier> classifiers : this.messageClassifiersMapProducer) {
1655 for (OpenFlowClassifier classifier : classifiers) {
1656 if (classifier.ethernetType() == packet.getEtherType()) {
1657 return classifier.idQueue();
1658 }
1659 }
1660 }
1661 return NUM_OF_QUEUES - 1;
1662 }
1663
1664 /**
1665 * Process messages from dispatch queues.
1666 *
1667 * @param queuesSize count of messages in all queues
1668 */
1669 private void processMessages(int queuesSize) {
1670 List<OFMessage> msgs = new ArrayList<>();
1671 int processed;
1672 do {
1673 processed = 0;
1674 while (processed < queuesSize) {
1675 for (LinkedBlockingMessagesQueue<OFMessage> queue :
1676 dispatchQueuesMapProducer.values()) {
1677 processed += queue.drainTo(msgs);
1678 }
1679 }
1680
1681 msgs.forEach(sw::handleMessage);
1682 msgs.clear();
1683 /* Decrement conditional variable */
1684 queuesSize = totalCount.addAndGet(-1 * processed);
1685 } while (queuesSize > 0);
1686 }
1687
1688 private void dispatchMessage(OFMessage m) {
1689 log.debug("Begin dispatch OpenFlow Message");
1690 boolean backlogEmpty = processDispatchBacklogQueue();
1691 if (m.getType() == OFType.PACKET_IN) {
1692 Ethernet pkt = parsePacketInMessage(m);
1693 pushMessageToQueue(m, classifyEthernetPacket(pkt));
1694 } else {
1695 if (!backlogEmpty || !pushMessageToQueue(m, NUM_OF_QUEUES - 1)) {
1696 dispatchBacklog.offer(m);
1697 }
1698 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001699
1700 if (dispatcherHandle.isDone()) {
1701 // dispatcher terminated for some reason, restart
pierf528eff2019-11-22 20:51:26 +01001702 dispatcherHandle = dispatcher.submit(new Dispatcher());
1703 }
1704 }
1705
1706 private final class Dispatcher implements Runnable {
1707 // dispatch loop
1708 @Override
1709 public void run() {
1710 try {
1711 for (;;) {
1712 int tc = 0;
1713 takeLock.lockInterruptibly();
1714 try {
1715 while ((tc = totalCount.get()) == 0) {
1716 notEmpty.await();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001717 }
pierf528eff2019-11-22 20:51:26 +01001718 } finally {
1719 takeLock.unlock();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001720 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001721
pierf528eff2019-11-22 20:51:26 +01001722 processMessages(tc);
1723 }
1724 } catch (InterruptedException e) {
1725 log.warn("Dispatcher interrupted");
1726 Thread.currentThread().interrupt();
1727 // interrupted. gracefully shutting down
1728 return;
1729 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001730 }
tom7ef8ff92014-09-17 13:08:06 -07001731 }
1732
1733 /**
1734 * Return a string describing this switch based on the already available
1735 * information (DPID and/or remote socket).
1736 * @return display string
1737 */
1738 private String getSwitchInfoString() {
1739 if (sw != null) {
1740 return sw.toString();
1741 }
1742 String channelString;
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001743 if (channel == null || channel.remoteAddress() == null) {
tom7ef8ff92014-09-17 13:08:06 -07001744 channelString = "?";
1745 } else {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001746 channelString = channel.remoteAddress().toString();
tom7ef8ff92014-09-17 13:08:06 -07001747 }
1748 String dpidString;
1749 if (featuresReply == null) {
1750 dpidString = "?";
1751 } else {
1752 dpidString = featuresReply.getDatapathId().toString();
1753 }
1754 return String.format("[%s DPID[%s]]", channelString, dpidString);
1755 }
1756
1757 /**
1758 * Update the channels state. Only called from the state machine.
1759 * TODO: enforce restricted state transitions
pierc684ee12019-07-16 15:52:50 +02001760 * @param state new state
tom7ef8ff92014-09-17 13:08:06 -07001761 */
pierc684ee12019-07-16 15:52:50 +02001762 void setState(ChannelState state) {
tom7ef8ff92014-09-17 13:08:06 -07001763 this.state = state;
Yuta HIGUCHI10f45132017-03-01 17:09:32 -08001764 this.lastStateChange = System.currentTimeMillis();
tom7ef8ff92014-09-17 13:08:06 -07001765 }
1766
Brian O'Connorf69e3e32018-05-10 02:25:09 -07001767 private boolean setDpid(Long dpid, Channel channel) {
1768 ChannelHandlerContext sslContext = channel.pipeline().context(SslHandler.class);
1769 if (sslContext != null) {
1770 try {
1771 SslHandler sslHandler = (SslHandler) sslContext.handler();
1772 Certificate[] certs = sslHandler.engine().getSession().getPeerCertificates();
1773 Certificate cert = certs.length > 0 ? certs[0] : null;
1774 if (!controller.isValidCertificate(dpid, cert)) {
1775 return false;
1776 }
1777 } catch (SSLPeerUnverifiedException e) {
1778 log.info("Switch with dpid {} is an unverified SSL peer.", dpid, e);
1779 return false;
1780 }
1781 }
1782 this.thisdpid = dpid;
1783 return true;
1784 }
1785
tom7ef8ff92014-09-17 13:08:06 -07001786 /**
1787 * Send hello message to the switch using the handshake transactions ids.
1788 * @throws IOException
1789 */
1790 private void sendHandshakeHelloMessage() throws IOException {
1791 // The OF protocol requires us to start things off by sending the highest
1792 // version of the protocol supported.
1793
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001794 // bitmap represents OF1.0, OF1.3, OF1.4, and OF1.5
tom7ef8ff92014-09-17 13:08:06 -07001795 // see Sec. 7.5.1 of the OF1.3.4 spec
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001796 U32 bitmap = U32.ofRaw((0b1 << OFVersion.OF_10.getWireVersion()) |
1797 (0b1 << OFVersion.OF_13.getWireVersion()) |
Yuta HIGUCHI6512f3e2017-05-18 17:21:24 -07001798 (0b1 << OFVersion.OF_14.getWireVersion()) |
1799 (0b1 << OFVersion.OF_15.getWireVersion()));
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001800 OFVersion version = Optional.ofNullable(ofVersion).orElse(OFVersion.OF_13);
1801 OFHelloElem hem = OFFactories.getFactory(version)
1802 .buildHelloElemVersionbitmap()
tom7ef8ff92014-09-17 13:08:06 -07001803 .setBitmaps(Collections.singletonList(bitmap))
1804 .build();
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001805 OFMessage.Builder mb = OFFactories.getFactory(version)
1806 .buildHello()
tom7ef8ff92014-09-17 13:08:06 -07001807 .setXid(this.handshakeTransactionIds--)
1808 .setElements(Collections.singletonList(hem));
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001809 log.info("Sending {} Hello to {}", version, channel.remoteAddress());
1810 channel.writeAndFlush(Collections.singletonList(mb.build()));
tom7ef8ff92014-09-17 13:08:06 -07001811 }
1812
1813 /**
1814 * Send featuresRequest msg to the switch using the handshake transactions ids.
1815 * @throws IOException
1816 */
1817 private void sendHandshakeFeaturesRequestMessage() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001818 log.debug("Sending FEATURES_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001819 OFMessage m = factory.buildFeaturesRequest()
1820 .setXid(this.handshakeTransactionIds--)
1821 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001822 channel.writeAndFlush(Collections.singletonList(m));
tom7ef8ff92014-09-17 13:08:06 -07001823 }
1824
1825 /**
1826 * Send the configuration requests to tell the switch we want full
1827 * packets.
1828 * @throws IOException
1829 */
1830 private void sendHandshakeSetConfig() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001831 log.debug("Sending CONFIG_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001832 List<OFMessage> msglist = new ArrayList<>(3);
tom7ef8ff92014-09-17 13:08:06 -07001833
1834 // Ensure we receive the full packet via PacketIn
1835 // FIXME: We don't set the reassembly flags.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001836 // Only send config to switches to send full packets, if they have a buffer.
Michael Jarschel7f521a32015-08-12 16:31:07 +02001837 // Saves a packet & OFSetConfig can't be handled by certain switches.
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001838 if (this.featuresReply.getNBuffers() > 0) {
Michael Jarschel7f521a32015-08-12 16:31:07 +02001839 OFSetConfig sc = factory
1840 .buildSetConfig()
1841 .setMissSendLen((short) 0xffff)
1842 .setXid(this.handshakeTransactionIds--)
1843 .build();
1844 msglist.add(sc);
1845 }
tom7ef8ff92014-09-17 13:08:06 -07001846
1847 // Barrier
1848 OFBarrierRequest br = factory
1849 .buildBarrierRequest()
1850 .setXid(this.handshakeTransactionIds--)
1851 .build();
1852 msglist.add(br);
1853
1854 // Verify (need barrier?)
1855 OFGetConfigRequest gcr = factory
1856 .buildGetConfigRequest()
1857 .setXid(this.handshakeTransactionIds--)
1858 .build();
1859 msglist.add(gcr);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001860 channel.writeAndFlush(msglist);
tom7ef8ff92014-09-17 13:08:06 -07001861 }
1862
1863 /**
1864 * send a description state request.
1865 * @throws IOException
1866 */
1867 private void sendHandshakeDescriptionStatsRequest() throws IOException {
1868 // Get Description to set switch-specific flags
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001869 log.debug("Sending DESC_STATS_REQUEST to {}", channel.remoteAddress());
tom7ef8ff92014-09-17 13:08:06 -07001870 OFDescStatsRequest dreq = factory
1871 .buildDescStatsRequest()
1872 .setXid(handshakeTransactionIds--)
1873 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001874 channel.writeAndFlush(Collections.singletonList(dreq));
tom7ef8ff92014-09-17 13:08:06 -07001875 }
1876
Jordi Ortiz91477b82016-11-29 15:22:50 +01001877 /**
Yuta HIGUCHI605758e2017-01-13 20:26:44 -08001878 * send a meter features request.
1879 *
Jordi Ortiz91477b82016-11-29 15:22:50 +01001880 * @throws IOException
1881 */
1882 private void sendMeterFeaturesRequest() throws IOException {
1883 // Get meter features including the MaxMeters value available for the device
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001884 OFFactory factory = OFFactories.getFactory(ofVersion);
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001885 log.debug("Sending METER_FEATURES_REQUEST to {}", channel.remoteAddress());
Jordi Ortiz91477b82016-11-29 15:22:50 +01001886 OFMeterFeaturesStatsRequest mfreq = factory
1887 .buildMeterFeaturesStatsRequest()
1888 .setXid(handshakeTransactionIds--)
1889 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001890 channel.writeAndFlush(Collections.singletonList(mfreq));
Jordi Ortiz91477b82016-11-29 15:22:50 +01001891 }
1892
tom7ef8ff92014-09-17 13:08:06 -07001893 private void sendHandshakeOFPortDescRequest() throws IOException {
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001894 log.debug("Sending OF_PORT_DESC_REQUEST to {}", channel.remoteAddress());
Yuta HIGUCHI2341e602017-03-08 20:10:08 -08001895 // Get port description for 1.3+ switch
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001896 OFPortDescStatsRequest preq = factory
tom7ef8ff92014-09-17 13:08:06 -07001897 .buildPortDescStatsRequest()
1898 .setXid(handshakeTransactionIds--)
1899 .build();
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001900 channel.writeAndFlush(Collections.singletonList(preq));
tom7ef8ff92014-09-17 13:08:06 -07001901 }
1902
1903 ChannelState getStateForTesting() {
1904 return state;
1905 }
1906
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001907
1908 @Override
1909 public boolean isActive() {
1910 if (channel != null) {
1911 return channel.isActive();
1912 }
1913 return false;
1914 }
1915
1916 @Override
1917 public void closeSession() {
1918 if (channel != null) {
1919 channel.close();
1920 }
1921 }
1922
1923 @Override
1924 public boolean sendMsg(Iterable<OFMessage> msgs) {
1925 if (channel.isActive()) {
Laszlo Pappb68fe7e2017-11-24 17:06:59 +00001926 if (log.isTraceEnabled()) {
1927 log.trace("Sending messages for switch {} via openflow channel: {}", getSwitchInfoString(), msgs);
1928 }
Yuta HIGUCHI6ee6b8c2017-05-09 14:44:30 -07001929 channel.writeAndFlush(msgs, channel.voidPromise());
1930 return true;
1931 } else {
1932 log.warn("Dropping messages for switch {} because channel is not connected: {}",
1933 getSwitchInfoString(), msgs);
1934 return false;
1935 }
1936 }
1937
1938 @Override
1939 public CharSequence sessionInfo() {
1940 return channelId;
1941 }
1942
Anton Chigrinbf14b372019-01-14 17:29:56 +02001943 @Override
1944 public void addClassifier(OpenFlowClassifier classifier) {
1945 if (this.deviceId.equals(classifier.deviceId())) {
1946 log.debug("Add OpenFlow Classifier for switch {} to queue {} with type {}",
1947 classifier.deviceId().toString(), classifier.idQueue(), classifier.ethernetType());
1948 this.messageClassifiersMapProducer.get(classifier.idQueue()).add(classifier);
1949 }
1950 }
1951
1952 @Override
1953 public void removeClassifier(OpenFlowClassifier classifier) {
1954 if (this.deviceId.equals(classifier.deviceId())) {
1955 log.debug("Remove OpenFlow Classifier for switch {} from queue {} with type {}",
1956 classifier.deviceId().toString(), classifier.idQueue(), classifier.ethernetType());
1957 this.messageClassifiersMapProducer.get(classifier.idQueue()).remove(classifier);
1958 }
1959 }
1960
1961 /**
1962 * Init classifier configuration for the switch. Use stored configuration if exist.
1963 * Otherwise add LLDP and BDDP classifiers for Queue N0.
1964 */
1965 private void initClassifiers() {
1966 try {
1967 openFlowManager = DefaultServiceDirectory.getService(OpenFlowService.class);
1968 DeviceId did = DeviceId.deviceId(uri(thisdpid));
1969 Set<OpenFlowClassifier> classifiers = openFlowManager.getClassifiersByDeviceId(did);
1970 if (classifiers == null) {
1971 OpenFlowClassifier classifier =
1972 new OpenFlowClassifier.Builder(did, 0).ethernetType(TYPE_LLDP).build();
1973 openFlowManager.add(classifier);
1974 classifier = new OpenFlowClassifier.Builder(did, 0).ethernetType(TYPE_BSN).build();
1975 openFlowManager.add(classifier);
1976 } else {
1977 this.messageClassifiersMapProducer.forEach((v) -> {
1978 v.clear();
1979 });
1980 classifiers.forEach((c) -> {
1981 messageClassifiersMapProducer.get(c.idQueue()).add(c);
1982 });
1983 }
1984 } catch (Exception e) {
1985 log.error("Initialize default classifier failed: {}", e.toString());
1986 e.printStackTrace();
1987 }
1988 }
tom7ef8ff92014-09-17 13:08:06 -07001989}