blob: e34c95870378171b250c03a9a843d06c1a0744cc [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
Ray Milkey269ffb92014-04-03 14:43:30 -07002 * Copyright 2011, Big Switch Networks, Inc.
3 * Originally created by David Erickson, Stanford University
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may
6 * not use this file except in compliance with the License. You may obtain
7 * a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations
15 * under the License.
16 **/
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080017
18package net.floodlightcontroller.core.internal;
19
20import java.io.FileInputStream;
21import java.io.IOException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080022import java.net.InetSocketAddress;
Jonathan Hartd10008d2013-02-23 17:04:08 -080023import java.net.UnknownHostException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080024import java.nio.channels.ClosedChannelException;
Jonathan Hartd10008d2013-02-23 17:04:08 -080025import java.util.ArrayList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080026import java.util.Collection;
27import java.util.Collections;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080028import java.util.HashMap;
29import java.util.HashSet;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080030import java.util.List;
31import java.util.Map;
32import java.util.Map.Entry;
33import java.util.Properties;
34import java.util.Set;
35import java.util.Stack;
36import java.util.concurrent.BlockingQueue;
37import java.util.concurrent.ConcurrentHashMap;
38import java.util.concurrent.ConcurrentMap;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070039import java.util.concurrent.CopyOnWriteArrayList;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080040import java.util.concurrent.CopyOnWriteArraySet;
41import java.util.concurrent.Executors;
42import java.util.concurrent.Future;
43import java.util.concurrent.LinkedBlockingQueue;
44import java.util.concurrent.RejectedExecutionException;
45import java.util.concurrent.TimeUnit;
46import java.util.concurrent.TimeoutException;
47
48import net.floodlightcontroller.core.FloodlightContext;
49import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080050import net.floodlightcontroller.core.IListener.Command;
Jonathan Hartd10008d2013-02-23 17:04:08 -080051import net.floodlightcontroller.core.IOFMessageListener;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080052import net.floodlightcontroller.core.IOFSwitch;
53import net.floodlightcontroller.core.IOFSwitchFilter;
54import net.floodlightcontroller.core.IOFSwitchListener;
Pankaj Berdedc73bb12013-08-14 13:46:38 -070055import net.floodlightcontroller.core.IUpdate;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080056import net.floodlightcontroller.core.annotations.LogMessageDoc;
57import net.floodlightcontroller.core.annotations.LogMessageDocs;
58import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState;
59import net.floodlightcontroller.core.util.ListenerDispatcher;
60import net.floodlightcontroller.core.web.CoreWebRoutable;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080061import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080062import net.floodlightcontroller.threadpool.IThreadPoolService;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070063import net.onrc.onos.api.registry.ILocalSwitchMastershipListener;
Jonathan Hart23701d12014-04-03 10:45:48 -070064import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070065import net.onrc.onos.core.main.IOFSwitchPortListener;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070066import net.onrc.onos.core.packet.Ethernet;
67import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070068import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070069import net.onrc.onos.core.registry.RegistryException;
Pavlin Radoslavov695f8952014-07-23 16:57:01 -070070import net.onrc.onos.core.util.Dpid;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080071
72import org.jboss.netty.bootstrap.ServerBootstrap;
73import org.jboss.netty.buffer.ChannelBuffer;
74import org.jboss.netty.buffer.ChannelBuffers;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080075import org.jboss.netty.channel.ChannelHandlerContext;
76import org.jboss.netty.channel.ChannelPipelineFactory;
77import org.jboss.netty.channel.ChannelStateEvent;
78import org.jboss.netty.channel.ChannelUpstreamHandler;
79import org.jboss.netty.channel.Channels;
80import org.jboss.netty.channel.ExceptionEvent;
81import org.jboss.netty.channel.MessageEvent;
82import org.jboss.netty.channel.group.ChannelGroup;
83import org.jboss.netty.channel.group.DefaultChannelGroup;
84import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
85import org.jboss.netty.handler.timeout.IdleStateAwareChannelUpstreamHandler;
86import org.jboss.netty.handler.timeout.IdleStateEvent;
87import org.jboss.netty.handler.timeout.ReadTimeoutException;
88import org.openflow.protocol.OFEchoReply;
89import org.openflow.protocol.OFError;
90import org.openflow.protocol.OFError.OFBadActionCode;
91import org.openflow.protocol.OFError.OFBadRequestCode;
92import org.openflow.protocol.OFError.OFErrorType;
93import org.openflow.protocol.OFError.OFFlowModFailedCode;
94import org.openflow.protocol.OFError.OFHelloFailedCode;
95import org.openflow.protocol.OFError.OFPortModFailedCode;
96import org.openflow.protocol.OFError.OFQueueOpFailedCode;
97import org.openflow.protocol.OFFeaturesReply;
98import org.openflow.protocol.OFGetConfigReply;
99import org.openflow.protocol.OFMessage;
100import org.openflow.protocol.OFPacketIn;
101import org.openflow.protocol.OFPhysicalPort;
Pankaj Berde6a4075d2013-01-22 16:42:54 -0800102import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
Pankaj Berde6debb042013-01-16 18:04:32 -0800103import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800104import org.openflow.protocol.OFPortStatus;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800105import org.openflow.protocol.OFPortStatus.OFPortReason;
106import org.openflow.protocol.OFSetConfig;
107import org.openflow.protocol.OFStatisticsRequest;
108import org.openflow.protocol.OFSwitchConfig;
109import org.openflow.protocol.OFType;
110import org.openflow.protocol.OFVendor;
111import org.openflow.protocol.factory.BasicFactory;
112import org.openflow.protocol.factory.MessageParseException;
113import org.openflow.protocol.statistics.OFDescriptionStatistics;
114import org.openflow.protocol.statistics.OFStatistics;
115import org.openflow.protocol.statistics.OFStatisticsType;
116import org.openflow.protocol.vendor.OFBasicVendorDataType;
117import org.openflow.protocol.vendor.OFBasicVendorId;
118import org.openflow.protocol.vendor.OFVendorId;
119import org.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800120import org.openflow.vendor.nicira.OFNiciraVendorData;
121import org.openflow.vendor.nicira.OFRoleReplyVendorData;
122import org.openflow.vendor.nicira.OFRoleRequestVendorData;
123import org.openflow.vendor.nicira.OFRoleVendorData;
124import org.slf4j.Logger;
125import org.slf4j.LoggerFactory;
126
127
128/**
129 * The main controller class. Handles all setup and network listeners
Ray Milkey269ffb92014-04-03 14:43:30 -0700130 * <p/>
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700131 * Extensions made by ONOS are:
Ray Milkey269ffb92014-04-03 14:43:30 -0700132 * - Detailed Port event: PORTCHANGED -> {PORTCHANGED, PORTADDED, PORTREMOVED}
133 * Available as net.onrc.onos.core.main.IOFSwitchPortListener
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700134 * - Distributed ownership control of switch through RegistryService(IControllerRegistryService)
Pavlin Radoslavova653e9f2013-10-16 03:08:52 -0700135 * - Register ONOS services. (IControllerRegistryService)
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700136 * - Additional DEBUG logs
137 * - Try using hostname as controller ID, when ID was not explicitly given.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800138 */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800139public class Controller implements IFloodlightProviderService {
Ray Milkey269ffb92014-04-03 14:43:30 -0700140
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700141 protected final static Logger log = LoggerFactory.getLogger(Controller.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800142
Ray Milkey269ffb92014-04-03 14:43:30 -0700143 private static final String ERROR_DATABASE =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800144 "The controller could not communicate with the system database.";
Ray Milkey269ffb92014-04-03 14:43:30 -0700145
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800146 protected BasicFactory factory;
147 protected ConcurrentMap<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -0700148 ListenerDispatcher<OFType, IOFMessageListener>>
149 messageListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800150 // The activeSwitches map contains only those switches that are actively
151 // being controlled by us -- it doesn't contain switches that are
152 // in the slave role
153 protected ConcurrentHashMap<Long, IOFSwitch> activeSwitches;
154 // connectedSwitches contains all connected switches, including ones where
155 // we're a slave controller. We need to keep track of them so that we can
156 // send role request messages to switches when our role changes to master
157 // We add a switch to this set after it successfully completes the
158 // handshake. Access to this Set needs to be synchronized with roleChanger
159 protected HashSet<OFSwitchImpl> connectedSwitches;
Ray Milkey269ffb92014-04-03 14:43:30 -0700160
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800161 // The controllerNodeIPsCache maps Controller IDs to their IP address.
162 // It's only used by handleControllerNodeIPsChanged
163 protected HashMap<String, String> controllerNodeIPsCache;
Ray Milkey269ffb92014-04-03 14:43:30 -0700164
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800165 protected Set<IOFSwitchListener> switchListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800166 protected BlockingQueue<IUpdate> updates;
Ray Milkey269ffb92014-04-03 14:43:30 -0700167
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700168 private CopyOnWriteArrayList<ILocalSwitchMastershipListener> localSwitchMastershipListeners =
169 new CopyOnWriteArrayList<>();
170
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800171 // Module dependencies
172 protected IRestApiService restApi;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800173 protected IThreadPoolService threadPool;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800174 protected IControllerRegistryService registryService;
Ray Milkey269ffb92014-04-03 14:43:30 -0700175
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800176 protected ILinkDiscoveryService linkDiscovery;
Ray Milkey269ffb92014-04-03 14:43:30 -0700177
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800178 // Configuration options
179 protected int openFlowPort = 6633;
180 protected int workerThreads = 0;
181 // The id for this controller node. Should be unique for each controller
182 // node in a controller cluster.
183 protected String controllerId = "localhost";
Ray Milkey269ffb92014-04-03 14:43:30 -0700184
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800185 // The current role of the controller.
186 // If the controller isn't configured to support roles, then this is null.
187 protected Role role;
188 // A helper that handles sending and timeout handling for role requests
189 protected RoleChanger roleChanger;
Ray Milkey269ffb92014-04-03 14:43:30 -0700190
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800191 // Start time of the controller
192 protected long systemStartTime;
Ray Milkey269ffb92014-04-03 14:43:30 -0700193
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800194 // Flag to always flush flow table on switch reconnect (HA or otherwise)
195 protected boolean alwaysClearFlowsOnSwAdd = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700196
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800197 // Perf. related configuration
198 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
199 protected static final int BATCH_MAX_SIZE = 100;
Ray Milkey269ffb92014-04-03 14:43:30 -0700200 protected static final boolean ALWAYS_DECODE_ETH = true;
201
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800202 public enum SwitchUpdateType {
203 ADDED,
204 REMOVED,
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700205 PORTCHANGED,
206 PORTADDED,
207 PORTREMOVED
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800208 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700209
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800210 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700211 * Update message indicating a switch was added or removed
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700212 * ONOS: This message extended to indicate Port add or removed event.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800213 */
214 protected class SwitchUpdate implements IUpdate {
215 public IOFSwitch sw;
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700216 public OFPhysicalPort port; // Added by ONOS
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800217 public SwitchUpdateType switchUpdateType;
Ray Milkey269ffb92014-04-03 14:43:30 -0700218
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800219 public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
220 this.sw = sw;
221 this.switchUpdateType = switchUpdateType;
222 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700223
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700224 public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
225 this.sw = sw;
226 this.port = port;
227 this.switchUpdateType = switchUpdateType;
228 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700229
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800230 public void dispatch() {
231 if (log.isTraceEnabled()) {
232 log.trace("Dispatching switch update {} {}",
233 sw, switchUpdateType);
234 }
235 if (switchListeners != null) {
236 for (IOFSwitchListener listener : switchListeners) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700237 switch (switchUpdateType) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800238 case ADDED:
239 listener.addedSwitch(sw);
240 break;
241 case REMOVED:
242 listener.removedSwitch(sw);
243 break;
244 case PORTCHANGED:
245 listener.switchPortChanged(sw.getId());
246 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700247 case PORTADDED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700248 if (listener instanceof IOFSwitchPortListener) {
249 ((IOFSwitchPortListener) listener).switchPortAdded(sw.getId(), port);
250 }
251 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700252 case PORTREMOVED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700253 if (listener instanceof IOFSwitchPortListener) {
254 ((IOFSwitchPortListener) listener).switchPortRemoved(sw.getId(), port);
255 }
256 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700257 default:
Ray Milkey269ffb92014-04-03 14:43:30 -0700258 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800259 }
260 }
261 }
262 }
263 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700264
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800265 // ***************
266 // Getters/Setters
Jonathan Hart20fab1a2013-12-12 11:06:50 -0800267 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700268
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800269 public void setRestApiService(IRestApiService restApi) {
270 this.restApi = restApi;
271 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700272
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800273 public void setThreadPoolService(IThreadPoolService tp) {
274 this.threadPool = tp;
275 }
276
Ray Milkey269ffb92014-04-03 14:43:30 -0700277 public void setMastershipService(IControllerRegistryService serviceImpl) {
278 this.registryService = serviceImpl;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700279 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700280
281 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
282 this.linkDiscovery = linkDiscovery;
283 }
284
285 public void publishUpdate(IUpdate update) {
286 try {
287 this.updates.put(update);
288 } catch (InterruptedException e) {
289 log.error("Failure adding update to queue", e);
290 }
291 }
292
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800293 // **********************
294 // ChannelUpstreamHandler
295 // **********************
Ray Milkey269ffb92014-04-03 14:43:30 -0700296
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800297 /**
298 * Return a new channel handler for processing a switch connections
Ray Milkey269ffb92014-04-03 14:43:30 -0700299 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800300 * @param state The channel state object for the connection
301 * @return the new channel handler
302 */
303 protected ChannelUpstreamHandler getChannelHandler(OFChannelState state) {
304 return new OFChannelHandler(state);
305 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700306
Jonathan Hartcc957a02013-02-26 10:39:04 -0800307 protected class RoleChangeCallback implements ControlChangeCallback {
Ray Milkey269ffb92014-04-03 14:43:30 -0700308 @Override
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700309 public void controlChanged(long dpidLong, boolean hasControl) {
310 Dpid dpid = new Dpid(dpidLong);
Ray Milkey269ffb92014-04-03 14:43:30 -0700311 log.info("Role change callback for switch {}, hasControl {}",
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700312 dpid, hasControl);
Pankaj Berde01939e92013-03-08 14:38:27 -0800313
Ray Milkey269ffb92014-04-03 14:43:30 -0700314 synchronized (roleChanger) {
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700315 Role role = null;
316 /*
317 * issue #229
318 * Cannot rely on sw.getRole() as it can be behind due to pending
319 * role changes in the queue. Just submit it and late the RoleChanger
320 * handle duplicates.
321 */
322 if (hasControl) {
323 role = Role.MASTER;
324 } else {
325 role = Role.SLAVE;
326 }
327 //
328 // Inform all listeners about the Controller role change.
329 //
330 // NOTE: The role change here is triggered by the mastership
331 // election mechanism, and reflects what the Controller itself
332 // believes its role should be. The role change request hasn't
333 // been accepted by the switch itself.
334 // If the semantics for informing the listeners is changed
335 // (i.e., the listeners should be informed after the switch
336 // has accepted the role change), then the code below should
337 // be moved to method handleRoleReplyMessage() or its
338 // equivalent.
339 //
340 for (ILocalSwitchMastershipListener listener : localSwitchMastershipListeners) {
341 listener.controllerRoleChanged(dpid, role);
342 }
343
Ray Milkey269ffb92014-04-03 14:43:30 -0700344 OFSwitchImpl sw = null;
345 for (OFSwitchImpl connectedSw : connectedSwitches) {
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700346 if (connectedSw.getId() == dpidLong) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700347 sw = connectedSw;
348 break;
349 }
350 }
351 if (sw == null) {
352 log.warn("Switch {} not found in connected switches",
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700353 dpid);
Ray Milkey269ffb92014-04-03 14:43:30 -0700354 return;
355 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800356
Ray Milkey269ffb92014-04-03 14:43:30 -0700357 log.debug("Sending role request {} msg to {}", role, sw);
358 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
359 swList.add(sw);
360 roleChanger.submitRequest(swList, role);
Ray Milkey269ffb92014-04-03 14:43:30 -0700361 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700362 }
Jonathan Hartcc957a02013-02-26 10:39:04 -0800363 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700364
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800365 /**
366 * Channel handler deals with the switch connection and dispatches
367 * switch messages to the appropriate locations.
Ray Milkey269ffb92014-04-03 14:43:30 -0700368 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800369 * @author readams
370 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700371 protected class OFChannelHandler
372 extends IdleStateAwareChannelUpstreamHandler {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800373 protected OFSwitchImpl sw;
374 protected OFChannelState state;
Ray Milkey269ffb92014-04-03 14:43:30 -0700375
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800376 public OFChannelHandler(OFChannelState state) {
377 this.state = state;
378 }
379
380 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700381 @LogMessageDoc(message = "New switch connection from {ip address}",
382 explanation = "A new switch has connected from the " +
383 "specified IP address")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800384 public void channelConnected(ChannelHandlerContext ctx,
385 ChannelStateEvent e) throws Exception {
386 log.info("New switch connection from {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700387 e.getChannel().getRemoteAddress());
388
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800389 sw = new OFSwitchImpl();
390 sw.setChannel(e.getChannel());
391 sw.setFloodlightProvider(Controller.this);
392 sw.setThreadPoolService(threadPool);
Ray Milkey269ffb92014-04-03 14:43:30 -0700393
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800394 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
395 msglist.add(factory.getMessage(OFType.HELLO));
396 e.getChannel().write(msglist);
397
398 }
399
400 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700401 @LogMessageDoc(message = "Disconnected switch {switch information}",
402 explanation = "The specified switch has disconnected.")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800403 public void channelDisconnected(ChannelHandlerContext ctx,
404 ChannelStateEvent e) throws Exception {
405 if (sw != null && state.hsState == HandshakeState.READY) {
406 if (activeSwitches.containsKey(sw.getId())) {
407 // It's safe to call removeSwitch even though the map might
408 // not contain this particular switch but another with the
409 // same DPID
410 removeSwitch(sw);
411 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700412 synchronized (roleChanger) {
413 if (controlRequested) {
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700414
415 //
416 // Inform all listeners about the switch disconnection
417 //
418 Dpid dpid = new Dpid(sw.getId());
419 for (ILocalSwitchMastershipListener listener :
420 localSwitchMastershipListeners) {
421 listener.switchDisconnected(dpid);
422 }
423
Ray Milkey269ffb92014-04-03 14:43:30 -0700424 registryService.releaseControl(sw.getId());
425 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800426 connectedSwitches.remove(sw);
427 }
428 sw.setConnected(false);
429 }
430 log.info("Disconnected switch {}", sw);
431 }
432
433 @Override
434 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700435 @LogMessageDoc(level = "ERROR",
436 message = "Disconnecting switch {switch} due to read timeout",
437 explanation = "The connected switch has failed to send any " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800438 "messages or respond to echo requests",
Ray Milkey269ffb92014-04-03 14:43:30 -0700439 recommendation = LogMessageDoc.CHECK_SWITCH),
440 @LogMessageDoc(level = "ERROR",
441 message = "Disconnecting switch {switch}: failed to " +
442 "complete handshake",
443 explanation = "The switch did not respond correctly " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800444 "to handshake messages",
Ray Milkey269ffb92014-04-03 14:43:30 -0700445 recommendation = LogMessageDoc.CHECK_SWITCH),
446 @LogMessageDoc(level = "ERROR",
447 message = "Disconnecting switch {switch} due to IO Error: {}",
448 explanation = "There was an error communicating with the switch",
449 recommendation = LogMessageDoc.CHECK_SWITCH),
450 @LogMessageDoc(level = "ERROR",
451 message = "Disconnecting switch {switch} due to switch " +
452 "state error: {error}",
453 explanation = "The switch sent an unexpected message",
454 recommendation = LogMessageDoc.CHECK_SWITCH),
455 @LogMessageDoc(level = "ERROR",
456 message = "Disconnecting switch {switch} due to " +
457 "message parse failure",
458 explanation = "Could not parse a message from the switch",
459 recommendation = LogMessageDoc.CHECK_SWITCH),
460 @LogMessageDoc(level = "ERROR",
461 message = "Could not process message: queue full",
462 explanation = "OpenFlow messages are arriving faster than " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800463 " the controller can process them.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700464 recommendation = LogMessageDoc.CHECK_CONTROLLER),
465 @LogMessageDoc(level = "ERROR",
466 message = "Error while processing message " +
467 "from switch {switch} {cause}",
468 explanation = "An error occurred processing the switch message",
469 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800470 })
471 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
472 throws Exception {
473 if (e.getCause() instanceof ReadTimeoutException) {
474 // switch timeout
475 log.error("Disconnecting switch {} due to read timeout", sw);
476 ctx.getChannel().close();
477 } else if (e.getCause() instanceof HandshakeTimeoutException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700478 log.error("Disconnecting switch {}: failed to complete handshake",
479 sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800480 ctx.getChannel().close();
481 } else if (e.getCause() instanceof ClosedChannelException) {
482 //log.warn("Channel for sw {} already closed", sw);
483 } else if (e.getCause() instanceof IOException) {
484 log.error("Disconnecting switch {} due to IO Error: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700485 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800486 ctx.getChannel().close();
487 } else if (e.getCause() instanceof SwitchStateException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700488 log.error("Disconnecting switch {} due to switch state error: {}",
489 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800490 ctx.getChannel().close();
491 } else if (e.getCause() instanceof MessageParseException) {
492 log.error("Disconnecting switch " + sw +
Ray Milkey269ffb92014-04-03 14:43:30 -0700493 " due to message parse failure",
494 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800495 ctx.getChannel().close();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800496 } else if (e.getCause() instanceof RejectedExecutionException) {
497 log.warn("Could not process message: queue full");
498 } else {
499 log.error("Error while processing message from switch " + sw,
Ray Milkey269ffb92014-04-03 14:43:30 -0700500 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800501 ctx.getChannel().close();
502 }
503 }
504
505 @Override
506 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
507 throws Exception {
508 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
509 msglist.add(factory.getMessage(OFType.ECHO_REQUEST));
510 e.getChannel().write(msglist);
511 }
512
513 @Override
514 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
515 throws Exception {
516 if (e.getMessage() instanceof List) {
517 @SuppressWarnings("unchecked")
Ray Milkey269ffb92014-04-03 14:43:30 -0700518 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800519
520 for (OFMessage ofm : msglist) {
521 try {
522 processOFMessage(ofm);
Ray Milkey269ffb92014-04-03 14:43:30 -0700523 } catch (Exception ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800524 // We are the last handler in the stream, so run the
525 // exception through the channel again by passing in
526 // ctx.getChannel().
527 Channels.fireExceptionCaught(ctx.getChannel(), ex);
528 }
529 }
530
531 // Flush all flow-mods/packet-out generated from this "train"
532 OFSwitchImpl.flush_all();
533 }
534 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700535
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800536 /**
537 * Process the request for the switch description
538 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700539 @LogMessageDoc(level = "ERROR",
540 message = "Exception in reading description " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800541 " during handshake {exception}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700542 explanation = "Could not process the switch description string",
543 recommendation = LogMessageDoc.CHECK_SWITCH)
Ray Milkeyff735142014-05-22 19:06:02 -0700544 @SuppressWarnings("unchecked")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800545 void processSwitchDescReply() {
546 try {
547 // Read description, if it has been updated
Ray Milkeyff735142014-05-22 19:06:02 -0700548 Future<?> future =
549 (Future<?>) sw.
Ray Milkey269ffb92014-04-03 14:43:30 -0700550 getAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
Ray Milkeyff735142014-05-22 19:06:02 -0700551 Future<List<OFStatistics>> desc_future =
552 (Future<List<OFStatistics>>) future;
Ray Milkey269ffb92014-04-03 14:43:30 -0700553 List<OFStatistics> values =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800554 desc_future.get(0, TimeUnit.MILLISECONDS);
555 if (values != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700556 OFDescriptionStatistics description =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800557 new OFDescriptionStatistics();
Ray Milkey269ffb92014-04-03 14:43:30 -0700558 ChannelBuffer data =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800559 ChannelBuffers.buffer(description.getLength());
560 for (OFStatistics f : values) {
561 f.writeTo(data);
562 description.readFrom(data);
563 break; // SHOULD be a list of length 1
564 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700565 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_DATA,
566 description);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800567 sw.setSwitchProperties(description);
568 data = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800569 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700570
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800571 sw.removeAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
572 state.hasDescription = true;
573 checkSwitchReady();
Ray Milkey269ffb92014-04-03 14:43:30 -0700574 } catch (InterruptedException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800575 // Ignore
Ray Milkey269ffb92014-04-03 14:43:30 -0700576 } catch (TimeoutException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800577 // Ignore
578 } catch (Exception ex) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700579 log.error("Exception in reading description " +
580 " during handshake", ex);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800581 }
582 }
583
584 /**
585 * Send initial switch setup information that we need before adding
586 * the switch
Ray Milkey269ffb92014-04-03 14:43:30 -0700587 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800588 * @throws IOException
589 */
590 void sendHelloConfiguration() throws IOException {
591 // Send initial Features Request
Ray Milkey269ffb92014-04-03 14:43:30 -0700592 log.debug("Sending FEATURES_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800593 sw.write(factory.getMessage(OFType.FEATURES_REQUEST), null);
594 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700595
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800596 /**
597 * Send the configuration requests we can only do after we have
598 * the features reply
Ray Milkey269ffb92014-04-03 14:43:30 -0700599 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800600 * @throws IOException
601 */
602 void sendFeatureReplyConfiguration() throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700603 log.debug("Sending CONFIG_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800604 // Ensure we receive the full packet via PacketIn
605 OFSetConfig config = (OFSetConfig) factory
606 .getMessage(OFType.SET_CONFIG);
607 config.setMissSendLength((short) 0xffff)
Ray Milkey269ffb92014-04-03 14:43:30 -0700608 .setLengthU(OFSwitchConfig.MINIMUM_LENGTH);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800609 sw.write(config, null);
610 sw.write(factory.getMessage(OFType.GET_CONFIG_REQUEST),
611 null);
612
613 // Get Description to set switch-specific flags
614 OFStatisticsRequest req = new OFStatisticsRequest();
615 req.setStatisticType(OFStatisticsType.DESC);
616 req.setLengthU(req.getLengthU());
Ray Milkey269ffb92014-04-03 14:43:30 -0700617 Future<List<OFStatistics>> dfuture =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800618 sw.getStatistics(req);
619 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE,
620 dfuture);
621
622 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700623
624 volatile Boolean controlRequested = Boolean.FALSE;
625
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800626 protected void checkSwitchReady() {
627 if (state.hsState == HandshakeState.FEATURES_REPLY &&
628 state.hasDescription && state.hasGetConfigReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700629
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800630 state.hsState = HandshakeState.READY;
Jonathan Hart9e92c512013-03-20 16:24:44 -0700631 log.debug("Handshake with {} complete", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700632
633 synchronized (roleChanger) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800634 // We need to keep track of all of the switches that are connected
Ray Milkeyff735142014-05-22 19:06:02 -0700635 // to the controller, in any role, so that we can later send the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800636 // role request messages when the controller role changes.
Ray Milkeyff735142014-05-22 19:06:02 -0700637 // We need to be synchronized while doing this: we must not
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800638 // send a another role request to the connectedSwitches until
Ray Milkeyff735142014-05-22 19:06:02 -0700639 // we were able to add this new switch to connectedSwitches
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800640 // *and* send the current role to the new switch.
641 connectedSwitches.add(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700642
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800643 if (role != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700644 //Put the switch in SLAVE mode until we know we have control
645 log.debug("Setting new switch {} to SLAVE", sw.getStringId());
646 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
647 swList.add(sw);
648 roleChanger.submitRequest(swList, Role.SLAVE);
649
650 //Request control of the switch from the global registry
651 try {
652 controlRequested = Boolean.TRUE;
653 registryService.requestControl(sw.getId(),
654 new RoleChangeCallback());
655 } catch (RegistryException e) {
656 log.debug("Registry error: {}", e.getMessage());
657 controlRequested = Boolean.FALSE;
658 }
659
660
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800661 // Send a role request if role support is enabled for the controller
662 // This is a probe that we'll use to determine if the switch
663 // actually supports the role request message. If it does we'll
664 // get back a role reply message. If it doesn't we'll get back an
Ray Milkeyff735142014-05-22 19:06:02 -0700665 // OFError message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800666 // If role is MASTER we will promote switch to active
667 // list when we receive the switch's role reply messages
Jonathan Hartcc957a02013-02-26 10:39:04 -0800668 /*
Ray Milkeyff735142014-05-22 19:06:02 -0700669 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800670 "sending initial role request msg to {}",
671 role, sw);
672 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
673 swList.add(sw);
674 roleChanger.submitRequest(swList, role);
Jonathan Hartcc957a02013-02-26 10:39:04 -0800675 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700676 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800677 // Role supported not enabled on controller (for now)
Ray Milkeyff735142014-05-22 19:06:02 -0700678 // automatically promote switch to active state.
Ray Milkey269ffb92014-04-03 14:43:30 -0700679 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800680 "not sending role request msg to {}",
681 role, sw);
682 // Need to clear FlowMods before we add the switch
683 // and dispatch updates otherwise we have a race condition.
684 sw.clearAllFlowMods();
685 addSwitch(sw);
686 state.firstRoleReplyReceived = true;
687 }
688 }
Pankaj Berde99fcee12013-03-18 09:41:53 -0700689 if (!controlRequested) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700690 // yield to allow other thread(s) to release control
691 try {
692 Thread.sleep(10);
693 } catch (InterruptedException e) {
694 // Ignore interruptions
695 }
696 // safer to bounce the switch to reconnect here than proceeding further
697 log.debug("Closing {} because we weren't able to request control " +
698 "successfully" + sw);
699 sw.channel.close();
Pankaj Berde99fcee12013-03-18 09:41:53 -0700700 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800701 }
702 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700703
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800704 /* Handle a role reply message we received from the switch. Since
Ray Milkeyff735142014-05-22 19:06:02 -0700705 * netty serializes message dispatch we don't need to synchronize
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800706 * against other receive operations from the same switch, so no need
707 * to synchronize addSwitch(), removeSwitch() operations from the same
Ray Milkeyff735142014-05-22 19:06:02 -0700708 * connection.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800709 * FIXME: However, when a switch with the same DPID connects we do
710 * need some synchronization. However, handling switches with same
711 * DPID needs to be revisited anyways (get rid of r/w-lock and synchronous
712 * removedSwitch notification):1
Ray Milkeyff735142014-05-22 19:06:02 -0700713 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800714 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700715 @LogMessageDoc(level = "ERROR",
716 message = "Invalid role value in role reply message",
717 explanation = "Was unable to set the HA role (master or slave) " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800718 "for the controller.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700719 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800720 protected void handleRoleReplyMessage(OFVendor vendorMessage,
Ray Milkey269ffb92014-04-03 14:43:30 -0700721 OFRoleReplyVendorData roleReplyVendorData) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800722 // Map from the role code in the message to our role enum
723 int nxRole = roleReplyVendorData.getRole();
724 Role role = null;
725 switch (nxRole) {
726 case OFRoleVendorData.NX_ROLE_OTHER:
727 role = Role.EQUAL;
728 break;
729 case OFRoleVendorData.NX_ROLE_MASTER:
730 role = Role.MASTER;
731 break;
732 case OFRoleVendorData.NX_ROLE_SLAVE:
733 role = Role.SLAVE;
734 break;
735 default:
736 log.error("Invalid role value in role reply message");
737 sw.getChannel().close();
738 return;
739 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700740
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800741 log.debug("Handling role reply for role {} from {}. " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700742 "Controller's role is {} ",
743 new Object[]{role, sw, Controller.this.role}
744 );
745
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800746 sw.deliverRoleReply(vendorMessage.getXid(), role);
Ray Milkey269ffb92014-04-03 14:43:30 -0700747
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800748 boolean isActive = activeSwitches.containsKey(sw.getId());
749 if (!isActive && sw.isActive()) {
750 // Transition from SLAVE to MASTER.
Ray Milkey269ffb92014-04-03 14:43:30 -0700751
752 if (!state.firstRoleReplyReceived ||
753 getAlwaysClearFlowsOnSwAdd()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800754 // This is the first role-reply message we receive from
755 // this switch or roles were disabled when the switch
Ray Milkeyff735142014-05-22 19:06:02 -0700756 // connected:
757 // Delete all pre-existing flows for new connections to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800758 // the master
759 //
Ray Milkeyff735142014-05-22 19:06:02 -0700760 // FIXME: Need to think more about what the test should
761 // be for when we flush the flow-table? For example,
762 // if all the controllers are temporarily in the backup
763 // role (e.g. right after a failure of the master
764 // controller) at the point the switch connects, then
765 // all of the controllers will initially connect as
766 // backup controllers and not flush the flow-table.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800767 // Then when one of them is promoted to master following
768 // the master controller election the flow-table
Ray Milkeyff735142014-05-22 19:06:02 -0700769 // will still not be flushed because that's treated as
770 // a failover event where we don't want to flush the
771 // flow-table. The end result would be that the flow
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800772 // table for a newly connected switch is never
773 // flushed. Not sure how to handle that case though...
774 sw.clearAllFlowMods();
775 log.debug("First role reply from master switch {}, " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700776 "clear FlowTable to active switch list",
777 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800778 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700779
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800780 // Some switches don't seem to update us with port
781 // status messages while in slave role.
Ray Milkeyff735142014-05-22 19:06:02 -0700782 //readSwitchPortStateFromStorage(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700783
Ray Milkeyff735142014-05-22 19:06:02 -0700784 // Only add the switch to the active switch list if
785 // we're not in the slave role. Note that if the role
786 // attribute is null, then that means that the switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800787 // doesn't support the role request messages, so in that
Ray Milkeyff735142014-05-22 19:06:02 -0700788 // case we're effectively in the EQUAL role and the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800789 // switch should be included in the active switch list.
790 addSwitch(sw);
791 log.debug("Added master switch {} to active switch list",
Ray Milkey269ffb92014-04-03 14:43:30 -0700792 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800793
Ray Milkey269ffb92014-04-03 14:43:30 -0700794 } else if (isActive && !sw.isActive()) {
Ray Milkeyff735142014-05-22 19:06:02 -0700795 // Transition from MASTER to SLAVE: remove switch
796 // from active switch list.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800797 log.debug("Removed slave switch {} from active switch" +
Ray Milkey269ffb92014-04-03 14:43:30 -0700798 " list", HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800799 removeSwitch(sw);
800 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700801
Ray Milkeyff735142014-05-22 19:06:02 -0700802 // Indicate that we have received a role reply message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800803 state.firstRoleReplyReceived = true;
804 }
805
806 protected boolean handleVendorMessage(OFVendor vendorMessage) {
807 boolean shouldHandleMessage = false;
808 int vendor = vendorMessage.getVendor();
809 switch (vendor) {
810 case OFNiciraVendorData.NX_VENDOR_ID:
811 OFNiciraVendorData niciraVendorData =
Ray Milkey269ffb92014-04-03 14:43:30 -0700812 (OFNiciraVendorData) vendorMessage.getVendorData();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800813 int dataType = niciraVendorData.getDataType();
814 switch (dataType) {
815 case OFRoleReplyVendorData.NXT_ROLE_REPLY:
816 OFRoleReplyVendorData roleReplyVendorData =
817 (OFRoleReplyVendorData) niciraVendorData;
Ray Milkey269ffb92014-04-03 14:43:30 -0700818 handleRoleReplyMessage(vendorMessage,
819 roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800820 break;
821 default:
822 log.warn("Unhandled Nicira VENDOR message; " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700823 "data type = {}", dataType);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800824 break;
825 }
826 break;
827 default:
828 log.warn("Unhandled VENDOR message; vendor id = {}", vendor);
829 break;
830 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700831
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800832 return shouldHandleMessage;
833 }
834
835 /**
836 * Dispatch an Openflow message from a switch to the appropriate
837 * handler.
Ray Milkey269ffb92014-04-03 14:43:30 -0700838 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800839 * @param m The message to process
840 * @throws IOException
Ray Milkey269ffb92014-04-03 14:43:30 -0700841 * @throws SwitchStateException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800842 */
843 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700844 @LogMessageDoc(level = "WARN",
845 message = "Config Reply from {switch} has " +
846 "miss length set to {length}",
847 explanation = "The controller requires that the switch " +
848 "use a miss length of 0xffff for correct " +
849 "function",
850 recommendation = "Use a different switch to ensure " +
851 "correct function"),
852 @LogMessageDoc(level = "WARN",
853 message = "Received ERROR from sw {switch} that "
854 + "indicates roles are not supported "
855 + "but we have received a valid "
856 + "role reply earlier",
857 explanation = "The switch sent a confusing message to the" +
858 "controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800859 })
860 protected void processOFMessage(OFMessage m)
861 throws IOException, SwitchStateException {
862 boolean shouldHandleMessage = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700863
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800864 switch (m.getType()) {
865 case HELLO:
866 if (log.isTraceEnabled())
867 log.trace("HELLO from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700868
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800869 if (state.hsState.equals(HandshakeState.START)) {
870 state.hsState = HandshakeState.HELLO;
871 sendHelloConfiguration();
872 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700873 throw new SwitchStateException("Unexpected HELLO from "
874 + sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800875 }
876 break;
877 case ECHO_REQUEST:
878 OFEchoReply reply =
Ray Milkey269ffb92014-04-03 14:43:30 -0700879 (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800880 reply.setXid(m.getXid());
881 sw.write(reply, null);
882 break;
883 case ECHO_REPLY:
884 break;
885 case FEATURES_REPLY:
886 if (log.isTraceEnabled())
887 log.trace("Features Reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700888
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800889 sw.setFeaturesReply((OFFeaturesReply) m);
890 if (state.hsState.equals(HandshakeState.HELLO)) {
891 sendFeatureReplyConfiguration();
892 state.hsState = HandshakeState.FEATURES_REPLY;
893 // uncomment to enable "dumb" switches like cbench
894 // state.hsState = HandshakeState.READY;
895 // addSwitch(sw);
896 } else {
897 // return results to rest api caller
898 sw.deliverOFFeaturesReply(m);
899 // update database */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800900 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800901 }
902 break;
903 case GET_CONFIG_REPLY:
904 if (log.isTraceEnabled())
905 log.trace("Get config reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700906
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800907 if (!state.hsState.equals(HandshakeState.FEATURES_REPLY)) {
908 String em = "Unexpected GET_CONFIG_REPLY from " + sw;
909 throw new SwitchStateException(em);
910 }
911 OFGetConfigReply cr = (OFGetConfigReply) m;
Ray Milkey269ffb92014-04-03 14:43:30 -0700912 if (cr.getMissSendLength() == (short) 0xffff) {
913 log.trace("Config Reply from {} confirms " +
914 "miss length set to 0xffff", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800915 } else {
916 log.warn("Config Reply from {} has " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700917 "miss length set to {}",
918 sw, cr.getMissSendLength() & 0xffff);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800919 }
920 state.hasGetConfigReply = true;
921 checkSwitchReady();
922 break;
923 case VENDOR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700924 shouldHandleMessage = handleVendorMessage((OFVendor) m);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800925 break;
926 case ERROR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700927 log.debug("Recieved ERROR message from switch {}: {}", sw, m);
Ray Milkeyff735142014-05-22 19:06:02 -0700928 // TODO: we need better error handling. Especially for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800929 // request/reply style message (stats, roles) we should have
Ray Milkeyff735142014-05-22 19:06:02 -0700930 // a unified way to lookup the xid in the error message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800931 // This will probable involve rewriting the way we handle
932 // request/reply style messages.
933 OFError error = (OFError) m;
934 boolean shouldLogError = true;
935 // TODO: should we check that firstRoleReplyReceived is false,
936 // i.e., check only whether the first request fails?
937 if (sw.checkFirstPendingRoleRequestXid(error.getXid())) {
938 boolean isBadVendorError =
Ray Milkey269ffb92014-04-03 14:43:30 -0700939 (error.getErrorType() == OFError.OFErrorType.
940 OFPET_BAD_REQUEST.getValue());
Ray Milkeyff735142014-05-22 19:06:02 -0700941 // We expect to receive a bad vendor error when
942 // we're connected to a switch that doesn't support
943 // the Nicira vendor extensions (i.e. not OVS or
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800944 // derived from OVS). By protocol, it should also be
945 // BAD_VENDOR, but too many switch implementations
946 // get it wrong and we can already check the xid()
947 // so we can ignore the type with confidence that this
948 // is not a spurious error
949 shouldLogError = !isBadVendorError;
950 if (isBadVendorError) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700951 log.debug("Handling bad vendor error for {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800952 if (state.firstRoleReplyReceived && (role != null)) {
953 log.warn("Received ERROR from sw {} that "
Ray Milkey269ffb92014-04-03 14:43:30 -0700954 + "indicates roles are not supported "
955 + "but we have received a valid "
956 + "role reply earlier", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800957 }
958 state.firstRoleReplyReceived = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700959 Role requestedRole =
960 sw.deliverRoleRequestNotSupportedEx(error.getXid());
961 synchronized (roleChanger) {
962 if (sw.role == null && Controller.this.role == Role.SLAVE) {
963 //This will now never happen. The Controller's role
964 //is now never SLAVE, always MASTER.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800965 // the switch doesn't understand role request
966 // messages and the current controller role is
Ray Milkeyff735142014-05-22 19:06:02 -0700967 // slave. We need to disconnect the switch.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800968 // @see RoleChanger for rationale
Ray Milkey269ffb92014-04-03 14:43:30 -0700969 log.warn("Closing {} channel because controller's role " +
970 "is SLAVE", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800971 sw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700972 } else if (sw.role == null && requestedRole == Role.MASTER) {
973 log.debug("Adding switch {} because we got an error" +
974 " returned from a MASTER role request", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800975 // Controller's role is master: add to
Ray Milkeyff735142014-05-22 19:06:02 -0700976 // active
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800977 // TODO: check if clearing flow table is
978 // right choice here.
979 // Need to clear FlowMods before we add the switch
980 // and dispatch updates otherwise we have a race condition.
981 // TODO: switch update is async. Won't we still have a potential
Ray Milkeyff735142014-05-22 19:06:02 -0700982 // race condition?
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800983 sw.clearAllFlowMods();
984 addSwitch(sw);
985 }
986 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700987 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800988 // TODO: Is this the right thing to do if we receive
Ray Milkeyff735142014-05-22 19:06:02 -0700989 // some other error besides a bad vendor error?
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800990 // Presumably that means the switch did actually
Ray Milkeyff735142014-05-22 19:06:02 -0700991 // understand the role request message, but there
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800992 // was some other error from processing the message.
993 // OF 1.2 specifies a OFPET_ROLE_REQUEST_FAILED
Ray Milkeyff735142014-05-22 19:06:02 -0700994 // error code, but it doesn't look like the Nicira
995 // role request has that. Should check OVS source
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800996 // code to see if it's possible for any other errors
997 // to be returned.
998 // If we received an error the switch is not
Ray Milkeyff735142014-05-22 19:06:02 -0700999 // in the correct role, so we need to disconnect it.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001000 // We could also resend the request but then we need to
1001 // check if there are other pending request in which
1002 // case we shouldn't resend. If we do resend we need
1003 // to make sure that the switch eventually accepts one
1004 // of our requests or disconnect the switch. This feels
Ray Milkeyff735142014-05-22 19:06:02 -07001005 // cumbersome.
Ray Milkey269ffb92014-04-03 14:43:30 -07001006 log.debug("Closing {} channel because we recieved an " +
1007 "error other than BAD_VENDOR", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001008 sw.getChannel().close();
1009 }
1010 }
1011 // Once we support OF 1.2, we'd add code to handle it here.
1012 //if (error.getXid() == state.ofRoleRequestXid) {
1013 //}
1014 if (shouldLogError)
1015 logError(sw, error);
1016 break;
1017 case STATS_REPLY:
Ray Milkey269ffb92014-04-03 14:43:30 -07001018 if (state.hsState.ordinal() <
1019 HandshakeState.FEATURES_REPLY.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001020 String em = "Unexpected STATS_REPLY from " + sw;
1021 throw new SwitchStateException(em);
1022 }
1023 sw.deliverStatisticsReply(m);
1024 if (sw.hasAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE)) {
1025 processSwitchDescReply();
1026 }
1027 break;
1028 case PORT_STATUS:
Ray Milkeyff735142014-05-22 19:06:02 -07001029 // We want to update our port state info even if we're in
1030 // the slave role, but we only want to update storage if
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001031 // we're the master (or equal).
1032 boolean updateStorage = state.hsState.
Ray Milkey269ffb92014-04-03 14:43:30 -07001033 equals(HandshakeState.READY) &&
1034 (sw.getRole() != Role.SLAVE);
1035 handlePortStatusMessage(sw, (OFPortStatus) m, updateStorage);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001036 shouldHandleMessage = true;
1037 break;
1038
1039 default:
1040 shouldHandleMessage = true;
1041 break;
1042 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001043
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001044 if (shouldHandleMessage) {
1045 sw.getListenerReadLock().lock();
1046 try {
1047 if (sw.isConnected()) {
1048 if (!state.hsState.equals(HandshakeState.READY)) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001049 log.debug("Ignoring message type {} received " +
1050 "from switch {} before switch is " +
1051 "fully configured.", m.getType(), sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001052 }
Ray Milkeyff735142014-05-22 19:06:02 -07001053 // Check if the controller is in the slave role for the
1054 // switch. If it is, then don't dispatch the message to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001055 // the listeners.
Ray Milkeyff735142014-05-22 19:06:02 -07001056 // TODO: Should we dispatch messages that we expect to
1057 // receive when we're in the slave role, e.g. port
1058 // status messages? Since we're "hiding" switches from
1059 // the listeners when we're in the slave role, then it
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001060 // seems a little weird to dispatch port status messages
Ray Milkeyff735142014-05-22 19:06:02 -07001061 // to them. On the other hand there might be special
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001062 // modules that care about all of the connected switches
1063 // and would like to receive port status notifications.
1064 else if (sw.getRole() == Role.SLAVE) {
Ray Milkeyff735142014-05-22 19:06:02 -07001065 // Don't log message if it's a port status message
1066 // since we expect to receive those from the switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001067 // and don't want to emit spurious messages.
1068 if (m.getType() != OFType.PORT_STATUS) {
1069 log.debug("Ignoring message type {} received " +
1070 "from switch {} while in the slave role.",
1071 m.getType(), sw);
1072 }
1073 } else {
1074 handleMessage(sw, m, null);
1075 }
1076 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001077 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001078 sw.getListenerReadLock().unlock();
1079 }
1080 }
1081 }
1082 }
1083
1084 // ****************
1085 // Message handlers
1086 // ****************
Ray Milkey269ffb92014-04-03 14:43:30 -07001087
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001088 protected void handlePortStatusMessage(IOFSwitch sw,
1089 OFPortStatus m,
1090 boolean updateStorage) {
1091 short portNumber = m.getDesc().getPortNumber();
1092 OFPhysicalPort port = m.getDesc();
Ray Milkey269ffb92014-04-03 14:43:30 -07001093 if (m.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
1094 boolean portDown = ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) ||
1095 ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001096 sw.setPort(port);
Ray Milkey269ffb92014-04-03 14:43:30 -07001097 if (!portDown) {
1098 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1099 try {
1100 this.updates.put(update);
1101 } catch (InterruptedException e) {
1102 log.error("Failure adding update to queue", e);
1103 }
1104 } else {
1105 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1106 try {
1107 this.updates.put(update);
1108 } catch (InterruptedException e) {
1109 log.error("Failure adding update to queue", e);
1110 }
1111 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001112 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001113 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001114 log.debug("Port #{} modified for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001115 } else if (m.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
1116 // XXX Workaround to prevent race condition where a link is detected
1117 // and attempted to be written to the database before the port is in
1118 // the database. We now suppress link discovery on ports until we're
1119 // sure they're in the database.
Jonathan Hart284e70f2014-07-05 12:32:51 -07001120 linkDiscovery.disableDiscoveryOnPort(sw.getId(), port.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -07001121
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001122 sw.setPort(port);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001123 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1124 try {
1125 this.updates.put(update);
1126 } catch (InterruptedException e) {
1127 log.error("Failure adding update to queue", e);
1128 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001129 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001130 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001131 log.debug("Port #{} added for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001132 } else if (m.getReason() ==
1133 (byte) OFPortReason.OFPPR_DELETE.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001134 sw.deletePort(portNumber);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001135 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1136 try {
1137 this.updates.put(update);
1138 } catch (InterruptedException e) {
1139 log.error("Failure adding update to queue", e);
1140 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001141 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001142 //removePortInfo(sw, portNumber);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001143 log.debug("Port #{} deleted for {}", portNumber, sw);
1144 }
1145 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED);
1146 try {
1147 this.updates.put(update);
1148 } catch (InterruptedException e) {
1149 log.error("Failure adding update to queue", e);
1150 }
1151 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001152
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001153 /**
1154 * flcontext_cache - Keep a thread local stack of contexts
1155 */
1156 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
Ray Milkey269ffb92014-04-03 14:43:30 -07001157 new ThreadLocal<Stack<FloodlightContext>>() {
1158 @Override
1159 protected Stack<FloodlightContext> initialValue() {
1160 return new Stack<FloodlightContext>();
1161 }
1162 };
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001163
1164 /**
1165 * flcontext_alloc - pop a context off the stack, if required create a new one
Ray Milkey269ffb92014-04-03 14:43:30 -07001166 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001167 * @return FloodlightContext
1168 */
1169 protected static FloodlightContext flcontext_alloc() {
1170 FloodlightContext flcontext = null;
1171
1172 if (flcontext_cache.get().empty()) {
1173 flcontext = new FloodlightContext();
Ray Milkey269ffb92014-04-03 14:43:30 -07001174 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001175 flcontext = flcontext_cache.get().pop();
1176 }
1177
1178 return flcontext;
1179 }
1180
1181 /**
1182 * flcontext_free - Free the context to the current thread
Ray Milkey269ffb92014-04-03 14:43:30 -07001183 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001184 * @param flcontext
1185 */
1186 protected void flcontext_free(FloodlightContext flcontext) {
1187 flcontext.getStorage().clear();
1188 flcontext_cache.get().push(flcontext);
1189 }
1190
1191 /**
1192 * Handle replies to certain OFMessages, and pass others off to listeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001193 *
1194 * @param sw The switch for the message
1195 * @param m The message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001196 * @param bContext The floodlight context. If null then floodlight context would
Ray Milkey269ffb92014-04-03 14:43:30 -07001197 * be allocated in this function
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001198 * @throws IOException
1199 */
1200 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001201 @LogMessageDoc(level = "ERROR",
1202 message = "Ignoring PacketIn (Xid = {xid}) because the data" +
1203 " field is empty.",
1204 explanation = "The switch sent an improperly-formatted PacketIn" +
1205 " message",
1206 recommendation = LogMessageDoc.CHECK_SWITCH),
1207 @LogMessageDoc(level = "WARN",
1208 message = "Unhandled OF Message: {} from {}",
1209 explanation = "The switch sent a message not handled by " +
1210 "the controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001211 })
Ray Milkeyff735142014-05-22 19:06:02 -07001212 @SuppressWarnings("fallthrough")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001213 protected void handleMessage(IOFSwitch sw, OFMessage m,
1214 FloodlightContext bContext)
1215 throws IOException {
1216 Ethernet eth = null;
1217
1218 switch (m.getType()) {
1219 case PACKET_IN:
Ray Milkey269ffb92014-04-03 14:43:30 -07001220 OFPacketIn pi = (OFPacketIn) m;
1221
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001222 if (pi.getPacketData().length <= 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001223 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
1224 ") because the data field is empty.");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001225 return;
1226 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001227
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001228 if (Controller.ALWAYS_DECODE_ETH) {
1229 eth = new Ethernet();
1230 eth.deserialize(pi.getPacketData(), 0,
1231 pi.getPacketData().length);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001232 }
1233 // fall through to default case...
1234
1235 default:
Ray Milkey269ffb92014-04-03 14:43:30 -07001236
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001237 List<IOFMessageListener> listeners = null;
1238 if (messageListeners.containsKey(m.getType())) {
1239 listeners = messageListeners.get(m.getType()).
1240 getOrderedListeners();
1241 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001242
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001243 FloodlightContext bc = null;
1244 if (listeners != null) {
1245 // Check if floodlight context is passed from the calling
1246 // function, if so use that floodlight context, otherwise
1247 // allocate one
1248 if (bContext == null) {
1249 bc = flcontext_alloc();
1250 } else {
1251 bc = bContext;
1252 }
1253 if (eth != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001254 IFloodlightProviderService.bcStore.put(bc,
1255 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001256 eth);
1257 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001258
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001259 // Get the starting time (overall and per-component) of
1260 // the processing chain for this packet if performance
1261 // monitoring is turned on
Ray Milkey269ffb92014-04-03 14:43:30 -07001262
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001263 Command cmd;
1264 for (IOFMessageListener listener : listeners) {
1265 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001266 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001267 continue;
1268 }
1269 }
1270
mininet73e7fb72013-12-03 14:25:53 -08001271
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001272 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -08001273
Ray Milkey269ffb92014-04-03 14:43:30 -07001274
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001275 if (Command.STOP.equals(cmd)) {
1276 break;
1277 }
1278 }
mininet73e7fb72013-12-03 14:25:53 -08001279
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001280 } else {
1281 log.warn("Unhandled OF Message: {} from {}", m, sw);
1282 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001283
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001284 if ((bContext == null) && (bc != null)) flcontext_free(bc);
1285 }
1286 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001287
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001288 /**
1289 * Log an OpenFlow error message from a switch
Ray Milkey269ffb92014-04-03 14:43:30 -07001290 *
1291 * @param sw The switch that sent the error
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001292 * @param error The error message
1293 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001294 @LogMessageDoc(level = "ERROR",
1295 message = "Error {error type} {error code} from {switch}",
1296 explanation = "The switch responded with an unexpected error" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001297 "to an OpenFlow message from the controller",
Ray Milkey269ffb92014-04-03 14:43:30 -07001298 recommendation = "This could indicate improper network operation. " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001299 "If the problem persists restarting the switch and " +
1300 "controller may help."
Ray Milkey269ffb92014-04-03 14:43:30 -07001301 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001302 protected void logError(IOFSwitch sw, OFError error) {
1303 int etint = 0xffff & error.getErrorType();
1304 if (etint < 0 || etint >= OFErrorType.values().length) {
1305 log.error("Unknown error code {} from sw {}", etint, sw);
1306 }
1307 OFErrorType et = OFErrorType.values()[etint];
1308 switch (et) {
1309 case OFPET_HELLO_FAILED:
Ray Milkey269ffb92014-04-03 14:43:30 -07001310 OFHelloFailedCode hfc =
1311 OFHelloFailedCode.values()[0xffff & error.getErrorCode()];
1312 log.error("Error {} {} from {}", new Object[]{et, hfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001313 break;
1314 case OFPET_BAD_REQUEST:
Ray Milkey269ffb92014-04-03 14:43:30 -07001315 OFBadRequestCode brc =
1316 OFBadRequestCode.values()[0xffff & error.getErrorCode()];
1317 log.error("Error {} {} from {}", new Object[]{et, brc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001318 break;
1319 case OFPET_BAD_ACTION:
1320 OFBadActionCode bac =
Ray Milkey269ffb92014-04-03 14:43:30 -07001321 OFBadActionCode.values()[0xffff & error.getErrorCode()];
1322 log.error("Error {} {} from {}", new Object[]{et, bac, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001323 break;
1324 case OFPET_FLOW_MOD_FAILED:
1325 OFFlowModFailedCode fmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001326 OFFlowModFailedCode.values()[0xffff & error.getErrorCode()];
1327 log.error("Error {} {} from {}", new Object[]{et, fmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001328 break;
1329 case OFPET_PORT_MOD_FAILED:
1330 OFPortModFailedCode pmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001331 OFPortModFailedCode.values()[0xffff & error.getErrorCode()];
1332 log.error("Error {} {} from {}", new Object[]{et, pmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001333 break;
1334 case OFPET_QUEUE_OP_FAILED:
1335 OFQueueOpFailedCode qofc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001336 OFQueueOpFailedCode.values()[0xffff & error.getErrorCode()];
1337 log.error("Error {} {} from {}", new Object[]{et, qofc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001338 break;
1339 default:
1340 break;
1341 }
1342 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001343
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001344 /**
1345 * Add a switch to the active switch list and call the switch listeners.
1346 * This happens either when a switch first connects (and the controller is
1347 * not in the slave role) or when the role of the controller changes from
1348 * slave to master.
Ray Milkey269ffb92014-04-03 14:43:30 -07001349 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001350 * @param sw the switch that has been added
1351 */
1352 // TODO: need to rethink locking and the synchronous switch update.
1353 // We can / should also handle duplicate DPIDs in connectedSwitches
Ray Milkey269ffb92014-04-03 14:43:30 -07001354 @LogMessageDoc(level = "ERROR",
1355 message = "New switch added {switch} for already-added switch {switch}",
1356 explanation = "A switch with the same DPID as another switch " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001357 "connected to the controller. This can be caused by " +
1358 "multiple switches configured with the same DPID, or " +
1359 "by a switch reconnected very quickly after " +
1360 "disconnecting.",
Ray Milkey269ffb92014-04-03 14:43:30 -07001361 recommendation = "If this happens repeatedly, it is likely there " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001362 "are switches with duplicate DPIDs on the network. " +
1363 "Reconfigure the appropriate switches. If it happens " +
1364 "very rarely, then it is likely this is a transient " +
1365 "network problem that can be ignored."
Ray Milkey269ffb92014-04-03 14:43:30 -07001366 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001367 protected void addSwitch(IOFSwitch sw) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001368 // XXX Workaround to prevent race condition where a link is detected
1369 // and attempted to be written to the database before the port is in
1370 // the database. We now suppress link discovery on ports until we're
1371 // sure they're in the database.
1372 for (OFPhysicalPort port : sw.getPorts()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -07001373 linkDiscovery.disableDiscoveryOnPort(sw.getId(), port.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -07001374 }
1375
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001376 // TODO: is it safe to modify the HashMap without holding
1377 // the old switch's lock?
1378 OFSwitchImpl oldSw = (OFSwitchImpl) this.activeSwitches.put(sw.getId(), sw);
1379 if (sw == oldSw) {
1380 // Note == for object equality, not .equals for value
1381 log.info("New add switch for pre-existing switch {}", sw);
1382 return;
1383 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001384
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001385 if (oldSw != null) {
1386 oldSw.getListenerWriteLock().lock();
1387 try {
1388 log.error("New switch added {} for already-added switch {}",
Ray Milkey269ffb92014-04-03 14:43:30 -07001389 sw, oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001390 // Set the connected flag to false to suppress calling
1391 // the listeners for this switch in processOFMessage
1392 oldSw.setConnected(false);
Ray Milkey269ffb92014-04-03 14:43:30 -07001393
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001394 oldSw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001395
Jonathan Hart2fa28062013-11-25 20:16:28 -08001396 //updateInactiveSwitchInfo(oldSw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001397
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001398 // we need to clean out old switch state definitively
1399 // before adding the new switch
1400 // FIXME: It seems not completely kosher to call the
1401 // switch listeners here. I thought one of the points of
1402 // having the asynchronous switch update mechanism was so
1403 // the addedSwitch and removedSwitch were always called
1404 // from a single thread to simplify concurrency issues
1405 // for the listener.
1406 if (switchListeners != null) {
1407 for (IOFSwitchListener listener : switchListeners) {
1408 listener.removedSwitch(oldSw);
1409 }
1410 }
1411 // will eventually trigger a removeSwitch(), which will cause
1412 // a "Not removing Switch ... already removed debug message.
1413 // TODO: Figure out a way to handle this that avoids the
1414 // spurious debug message.
Jonathan Hart9e92c512013-03-20 16:24:44 -07001415 log.debug("Closing {} because a new IOFSwitch got added " +
Ray Milkey269ffb92014-04-03 14:43:30 -07001416 "for this dpid", oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001417 oldSw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -07001418 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001419 oldSw.getListenerWriteLock().unlock();
1420 }
1421 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001422
Jonathan Hart2fa28062013-11-25 20:16:28 -08001423 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001424 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
1425 try {
1426 this.updates.put(update);
1427 } catch (InterruptedException e) {
1428 log.error("Failure adding update to queue", e);
1429 }
1430 }
1431
1432 /**
1433 * Remove a switch from the active switch list and call the switch listeners.
1434 * This happens either when the switch is disconnected or when the
1435 * controller's role for the switch changes from master to slave.
Ray Milkey269ffb92014-04-03 14:43:30 -07001436 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001437 * @param sw the switch that has been removed
1438 */
1439 protected void removeSwitch(IOFSwitch sw) {
1440 // No need to acquire the listener lock, since
1441 // this method is only called after netty has processed all
1442 // pending messages
1443 log.debug("removeSwitch: {}", sw);
1444 if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
1445 log.debug("Not removing switch {}; already removed", sw);
1446 return;
1447 }
1448 // We cancel all outstanding statistics replies if the switch transition
1449 // from active. In the future we might allow statistics requests
1450 // from slave controllers. Then we need to move this cancelation
1451 // to switch disconnect
1452 sw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001453
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001454 // FIXME: I think there's a race condition if we call updateInactiveSwitchInfo
1455 // here if role support is enabled. In that case if the switch is being
1456 // removed because we've been switched to being in the slave role, then I think
1457 // it's possible that the new master may have already been promoted to master
1458 // and written out the active switch state to storage. If we now execute
1459 // updateInactiveSwitchInfo we may wipe out all of the state that was
1460 // written out by the new master. Maybe need to revisit how we handle all
1461 // of the switch state that's written to storage.
Ray Milkey269ffb92014-04-03 14:43:30 -07001462
Jonathan Hart2fa28062013-11-25 20:16:28 -08001463 //updateInactiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001464 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.REMOVED);
1465 try {
1466 this.updates.put(update);
1467 } catch (InterruptedException e) {
1468 log.error("Failure adding update to queue", e);
1469 }
1470 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001471
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001472 // ***************
1473 // IFloodlightProvider
1474 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -07001475
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001476 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001477 public synchronized void addOFMessageListener(OFType type,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001478 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001479 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1480 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001481 if (ldd == null) {
1482 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
1483 messageListeners.put(type, ldd);
1484 }
1485 ldd.addListener(type, listener);
1486 }
1487
1488 @Override
1489 public synchronized void removeOFMessageListener(OFType type,
1490 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001491 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1492 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001493 if (ldd != null) {
1494 ldd.removeListener(listener);
1495 }
1496 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001497
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001498 private void logListeners() {
1499 for (Map.Entry<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -07001500 ListenerDispatcher<OFType,
1501 IOFMessageListener>> entry
1502 : messageListeners.entrySet()) {
1503
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001504 OFType type = entry.getKey();
Ray Milkey269ffb92014-04-03 14:43:30 -07001505 ListenerDispatcher<OFType, IOFMessageListener> ldd =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001506 entry.getValue();
Ray Milkey269ffb92014-04-03 14:43:30 -07001507
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001508 StringBuffer sb = new StringBuffer();
1509 sb.append("OFListeners for ");
1510 sb.append(type);
1511 sb.append(": ");
1512 for (IOFMessageListener l : ldd.getOrderedListeners()) {
1513 sb.append(l.getName());
1514 sb.append(",");
1515 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001516 log.debug(sb.toString());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001517 }
1518 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001519
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001520 public void removeOFMessageListeners(OFType type) {
1521 messageListeners.remove(type);
1522 }
1523
1524 @Override
1525 public Map<Long, IOFSwitch> getSwitches() {
1526 return Collections.unmodifiableMap(this.activeSwitches);
1527 }
1528
1529 @Override
1530 public void addOFSwitchListener(IOFSwitchListener listener) {
1531 this.switchListeners.add(listener);
1532 }
1533
1534 @Override
1535 public void removeOFSwitchListener(IOFSwitchListener listener) {
1536 this.switchListeners.remove(listener);
1537 }
1538
1539 @Override
1540 public Map<OFType, List<IOFMessageListener>> getListeners() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001541 Map<OFType, List<IOFMessageListener>> lers =
1542 new HashMap<OFType, List<IOFMessageListener>>();
1543 for (Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
1544 messageListeners.entrySet()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001545 lers.put(e.getKey(), e.getValue().getOrderedListeners());
1546 }
1547 return Collections.unmodifiableMap(lers);
1548 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001549
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001550 @Override
Pavlin Radoslavov695f8952014-07-23 16:57:01 -07001551 public void addLocalSwitchMastershipListener(
1552 ILocalSwitchMastershipListener listener) {
1553 this.localSwitchMastershipListeners.addIfAbsent(listener);
1554 }
1555
1556 @Override
1557 public void removeLocalSwitchMastershipListener(
1558 ILocalSwitchMastershipListener listener) {
1559 this.localSwitchMastershipListeners.remove(listener);
1560 }
1561
1562 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001563 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001564 @LogMessageDoc(message = "Failed to inject OFMessage {message} onto " +
1565 "a null switch",
1566 explanation = "Failed to process a message because the switch " +
1567 " is no longer connected."),
1568 @LogMessageDoc(level = "ERROR",
1569 message = "Error reinjecting OFMessage on switch {switch}",
1570 explanation = "An I/O error occured while attempting to " +
1571 "process an OpenFlow message",
1572 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001573 })
1574 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
1575 FloodlightContext bc) {
1576 if (sw == null) {
1577 log.info("Failed to inject OFMessage {} onto a null switch", msg);
1578 return false;
1579 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001580
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001581 // FIXME: Do we need to be able to inject messages to switches
1582 // where we're the slave controller (i.e. they're connected but
1583 // not active)?
1584 // FIXME: Don't we need synchronization logic here so we're holding
1585 // the listener read lock when we call handleMessage? After some
1586 // discussions it sounds like the right thing to do here would be to
1587 // inject the message as a netty upstream channel event so it goes
1588 // through the normal netty event processing, including being
1589 // handled
1590 if (!activeSwitches.containsKey(sw.getId())) return false;
Ray Milkey269ffb92014-04-03 14:43:30 -07001591
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001592 try {
1593 // Pass Floodlight context to the handleMessages()
1594 handleMessage(sw, msg, bc);
1595 } catch (IOException e) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001596 log.error("Error reinjecting OFMessage on switch {}",
1597 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001598 return false;
1599 }
1600 return true;
1601 }
1602
1603 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001604 @LogMessageDoc(message = "Calling System.exit",
1605 explanation = "The controller is terminating")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001606 public synchronized void terminate() {
1607 log.info("Calling System.exit");
1608 System.exit(1);
1609 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001610
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001611 @Override
1612 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
1613 // call the overloaded version with floodlight context set to null
1614 return injectOfMessage(sw, msg, null);
1615 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001616
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001617 @Override
1618 public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
1619 FloodlightContext bc) {
1620 if (log.isTraceEnabled()) {
1621 String str = OFMessage.getDataAsString(sw, m, bc);
1622 log.trace("{}", str);
1623 }
1624
1625 List<IOFMessageListener> listeners = null;
1626 if (messageListeners.containsKey(m.getType())) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001627 listeners =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001628 messageListeners.get(m.getType()).getOrderedListeners();
1629 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001630
1631 if (listeners != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001632 for (IOFMessageListener listener : listeners) {
1633 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001634 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001635 continue;
1636 }
1637 }
1638 if (Command.STOP.equals(listener.receive(sw, m, bc))) {
1639 break;
1640 }
1641 }
1642 }
1643 }
1644
1645 @Override
1646 public BasicFactory getOFMessageFactory() {
1647 return factory;
1648 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001649
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001650 @Override
1651 public String getControllerId() {
1652 return controllerId;
1653 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001654
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001655 // **************
1656 // Initialization
1657 // **************
1658
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001659 /**
1660 * Sets the initial role based on properties in the config params.
1661 * It looks for two different properties.
1662 * If the "role" property is specified then the value should be
1663 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1664 * controller is set to the specified value. If the "role" property
1665 * is not specified then it looks next for the "role.path" property.
1666 * In this case the value should be the path to a property file in
1667 * the file system that contains a property called "floodlight.role"
1668 * which can be one of the values listed above for the "role" property.
1669 * The idea behind the "role.path" mechanism is that you have some
1670 * separate heartbeat and master controller election algorithm that
1671 * determines the role of the controller. When a role transition happens,
1672 * it updates the current role in the file specified by the "role.path"
1673 * file. Then if floodlight restarts for some reason it can get the
1674 * correct current role of the controller from the file.
Ray Milkey269ffb92014-04-03 14:43:30 -07001675 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001676 * @param configParams The config params for the FloodlightProvider service
1677 * @return A valid role if role information is specified in the
Ray Milkey269ffb92014-04-03 14:43:30 -07001678 * config params, otherwise null
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001679 */
1680 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001681 @LogMessageDoc(message = "Controller role set to {role}",
1682 explanation = "Setting the initial HA role to "),
1683 @LogMessageDoc(level = "ERROR",
1684 message = "Invalid current role value: {role}",
1685 explanation = "An invalid HA role value was read from the " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001686 "properties file",
Ray Milkey269ffb92014-04-03 14:43:30 -07001687 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001688 })
1689 protected Role getInitialRole(Map<String, String> configParams) {
1690 Role role = null;
1691 String roleString = configParams.get("role");
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001692 FileInputStream fs = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001693 if (roleString == null) {
1694 String rolePath = configParams.get("rolepath");
1695 if (rolePath != null) {
1696 Properties properties = new Properties();
1697 try {
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001698 fs = new FileInputStream(rolePath);
1699 properties.load(fs);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001700 roleString = properties.getProperty("floodlight.role");
Ray Milkey269ffb92014-04-03 14:43:30 -07001701 } catch (IOException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001702 // Don't treat it as an error if the file specified by the
1703 // rolepath property doesn't exist. This lets us enable the
1704 // HA mechanism by just creating/setting the floodlight.role
1705 // property in that file without having to modify the
1706 // floodlight properties.
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001707 } finally {
1708 if (fs != null) {
1709 try {
1710 fs.close();
1711 } catch (IOException e) {
1712 log.error("Exception while closing resource ", e);
1713 }
1714 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001715 }
1716 }
1717 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001718
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001719 if (roleString != null) {
1720 // Canonicalize the string to the form used for the enum constants
1721 roleString = roleString.trim().toUpperCase();
1722 try {
1723 role = Role.valueOf(roleString);
Ray Milkey269ffb92014-04-03 14:43:30 -07001724 } catch (IllegalArgumentException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001725 log.error("Invalid current role value: {}", roleString);
1726 }
1727 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001728
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001729 log.info("Controller role set to {}", role);
Ray Milkey269ffb92014-04-03 14:43:30 -07001730
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001731 return role;
1732 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001733
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001734 /**
1735 * Tell controller that we're ready to accept switches loop
Ray Milkey269ffb92014-04-03 14:43:30 -07001736 *
1737 * @throws IOException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001738 */
1739 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001740 @LogMessageDoc(message = "Listening for switch connections on {address}",
1741 explanation = "The controller is ready and listening for new" +
1742 " switch connections"),
1743 @LogMessageDoc(message = "Storage exception in controller " +
1744 "updates loop; terminating process",
1745 explanation = ERROR_DATABASE,
1746 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1747 @LogMessageDoc(level = "ERROR",
1748 message = "Exception in controller updates loop",
1749 explanation = "Failed to dispatch controller event",
1750 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001751 })
1752 public void run() {
1753 if (log.isDebugEnabled()) {
1754 logListeners();
1755 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001756
1757 try {
1758 final ServerBootstrap bootstrap = createServerBootStrap();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001759
1760 bootstrap.setOption("reuseAddr", true);
1761 bootstrap.setOption("child.keepAlive", true);
1762 bootstrap.setOption("child.tcpNoDelay", true);
1763 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1764
Ray Milkey269ffb92014-04-03 14:43:30 -07001765 ChannelPipelineFactory pfact =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001766 new OpenflowPipelineFactory(this, null);
1767 bootstrap.setPipelineFactory(pfact);
1768 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1769 final ChannelGroup cg = new DefaultChannelGroup();
1770 cg.add(bootstrap.bind(sa));
Ray Milkey269ffb92014-04-03 14:43:30 -07001771
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001772 log.info("Listening for switch connections on {}", sa);
1773 } catch (Exception e) {
1774 throw new RuntimeException(e);
1775 }
1776
1777 // main loop
1778 while (true) {
1779 try {
1780 IUpdate update = updates.take();
1781 update.dispatch();
1782 } catch (InterruptedException e) {
1783 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001784 } catch (Exception e) {
1785 log.error("Exception in controller updates loop", e);
1786 }
1787 }
1788 }
1789
1790 private ServerBootstrap createServerBootStrap() {
1791 if (workerThreads == 0) {
1792 return new ServerBootstrap(
1793 new NioServerSocketChannelFactory(
1794 Executors.newCachedThreadPool(),
1795 Executors.newCachedThreadPool()));
1796 } else {
1797 return new ServerBootstrap(
1798 new NioServerSocketChannelFactory(
1799 Executors.newCachedThreadPool(),
1800 Executors.newCachedThreadPool(), workerThreads));
1801 }
1802 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001803
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001804 public void setConfigParams(Map<String, String> configParams) {
1805 String ofPort = configParams.get("openflowport");
1806 if (ofPort != null) {
1807 this.openFlowPort = Integer.parseInt(ofPort);
1808 }
1809 log.debug("OpenFlow port set to {}", this.openFlowPort);
1810 String threads = configParams.get("workerthreads");
1811 if (threads != null) {
1812 this.workerThreads = Integer.parseInt(threads);
1813 }
1814 log.debug("Number of worker threads set to {}", this.workerThreads);
1815 String controllerId = configParams.get("controllerid");
1816 if (controllerId != null) {
1817 this.controllerId = controllerId;
Ray Milkey269ffb92014-04-03 14:43:30 -07001818 } else {
1819 //Try to get the hostname of the machine and use that for controller ID
1820 try {
1821 String hostname = java.net.InetAddress.getLocalHost().getHostName();
1822 this.controllerId = hostname;
1823 } catch (UnknownHostException e) {
1824 // Can't get hostname, we'll just use the default
1825 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001826 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001827
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001828 log.debug("ControllerId set to {}", this.controllerId);
1829 }
1830
1831 private void initVendorMessages() {
1832 // Configure openflowj to be able to parse the role request/reply
1833 // vendor messages.
1834 OFBasicVendorId niciraVendorId = new OFBasicVendorId(
1835 OFNiciraVendorData.NX_VENDOR_ID, 4);
1836 OFVendorId.registerVendorId(niciraVendorId);
1837 OFBasicVendorDataType roleRequestVendorData =
1838 new OFBasicVendorDataType(
1839 OFRoleRequestVendorData.NXT_ROLE_REQUEST,
1840 OFRoleRequestVendorData.getInstantiable());
1841 niciraVendorId.registerVendorDataType(roleRequestVendorData);
1842 OFBasicVendorDataType roleReplyVendorData =
1843 new OFBasicVendorDataType(
1844 OFRoleReplyVendorData.NXT_ROLE_REPLY,
1845 OFRoleReplyVendorData.getInstantiable());
Ray Milkey269ffb92014-04-03 14:43:30 -07001846 niciraVendorId.registerVendorDataType(roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001847 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001848
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001849 /**
1850 * Initialize internal data structures
1851 */
1852 public void init(Map<String, String> configParams) {
1853 // These data structures are initialized here because other
1854 // module's startUp() might be called before ours
1855 this.messageListeners =
Ray Milkey269ffb92014-04-03 14:43:30 -07001856 new ConcurrentHashMap<OFType,
1857 ListenerDispatcher<OFType,
1858 IOFMessageListener>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001859 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001860 this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1861 this.connectedSwitches = new HashSet<OFSwitchImpl>();
1862 this.controllerNodeIPsCache = new HashMap<String, String>();
1863 this.updates = new LinkedBlockingQueue<IUpdate>();
1864 this.factory = new BasicFactory();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001865 setConfigParams(configParams);
Jonathan Hartcc957a02013-02-26 10:39:04 -08001866 //Set the controller's role to MASTER so it always tries to do role requests.
1867 this.role = Role.MASTER;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001868 this.roleChanger = new RoleChanger();
1869 initVendorMessages();
1870 this.systemStartTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001871 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001872
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001873 /**
1874 * Startup all of the controller's components
1875 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001876 @LogMessageDoc(message = "Waiting for storage source",
1877 explanation = "The system database is not yet ready",
1878 recommendation = "If this message persists, this indicates " +
1879 "that the system database has failed to start. " +
1880 LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001881 public void startupComponents() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001882 try {
1883 registryService.registerController(controllerId);
1884 } catch (RegistryException e) {
1885 log.warn("Registry service error: {}", e.getMessage());
1886 }
1887
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001888 // Add our REST API
1889 restApi.addRestletRoutable(new CoreWebRoutable());
1890 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001891
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001892 @Override
1893 public Map<String, String> getControllerNodeIPs() {
1894 // We return a copy of the mapping so we can guarantee that
1895 // the mapping return is the same as one that will be (or was)
1896 // dispatched to IHAListeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001897 HashMap<String, String> retval = new HashMap<String, String>();
1898 synchronized (controllerNodeIPsCache) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001899 retval.putAll(controllerNodeIPsCache);
1900 }
1901 return retval;
1902 }
1903
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001904 @Override
1905 public long getSystemStartTime() {
1906 return (this.systemStartTime);
1907 }
1908
1909 @Override
1910 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
1911 this.alwaysClearFlowsOnSwAdd = value;
1912 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001913
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001914 public boolean getAlwaysClearFlowsOnSwAdd() {
1915 return this.alwaysClearFlowsOnSwAdd;
1916 }
1917}