blob: a502711355542779e968031aa351d8b481482173 [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;
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -070071import net.onrc.onos.core.util.OnosInstanceId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080072
73import org.jboss.netty.bootstrap.ServerBootstrap;
74import org.jboss.netty.buffer.ChannelBuffer;
75import org.jboss.netty.buffer.ChannelBuffers;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080076import org.jboss.netty.channel.ChannelHandlerContext;
77import org.jboss.netty.channel.ChannelPipelineFactory;
78import org.jboss.netty.channel.ChannelStateEvent;
79import org.jboss.netty.channel.ChannelUpstreamHandler;
80import org.jboss.netty.channel.Channels;
81import org.jboss.netty.channel.ExceptionEvent;
82import org.jboss.netty.channel.MessageEvent;
83import org.jboss.netty.channel.group.ChannelGroup;
84import org.jboss.netty.channel.group.DefaultChannelGroup;
85import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
86import org.jboss.netty.handler.timeout.IdleStateAwareChannelUpstreamHandler;
87import org.jboss.netty.handler.timeout.IdleStateEvent;
88import org.jboss.netty.handler.timeout.ReadTimeoutException;
89import org.openflow.protocol.OFEchoReply;
90import org.openflow.protocol.OFError;
91import org.openflow.protocol.OFError.OFBadActionCode;
92import org.openflow.protocol.OFError.OFBadRequestCode;
93import org.openflow.protocol.OFError.OFErrorType;
94import org.openflow.protocol.OFError.OFFlowModFailedCode;
95import org.openflow.protocol.OFError.OFHelloFailedCode;
96import org.openflow.protocol.OFError.OFPortModFailedCode;
97import org.openflow.protocol.OFError.OFQueueOpFailedCode;
98import org.openflow.protocol.OFFeaturesReply;
99import org.openflow.protocol.OFGetConfigReply;
100import org.openflow.protocol.OFMessage;
101import org.openflow.protocol.OFPacketIn;
102import org.openflow.protocol.OFPhysicalPort;
Pankaj Berde6a4075d2013-01-22 16:42:54 -0800103import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
Pankaj Berde6debb042013-01-16 18:04:32 -0800104import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800105import org.openflow.protocol.OFPortStatus;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800106import org.openflow.protocol.OFPortStatus.OFPortReason;
107import org.openflow.protocol.OFSetConfig;
108import org.openflow.protocol.OFStatisticsRequest;
109import org.openflow.protocol.OFSwitchConfig;
110import org.openflow.protocol.OFType;
111import org.openflow.protocol.OFVendor;
112import org.openflow.protocol.factory.BasicFactory;
113import org.openflow.protocol.factory.MessageParseException;
114import org.openflow.protocol.statistics.OFDescriptionStatistics;
115import org.openflow.protocol.statistics.OFStatistics;
116import org.openflow.protocol.statistics.OFStatisticsType;
117import org.openflow.protocol.vendor.OFBasicVendorDataType;
118import org.openflow.protocol.vendor.OFBasicVendorId;
119import org.openflow.protocol.vendor.OFVendorId;
120import org.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800121import org.openflow.vendor.nicira.OFNiciraVendorData;
122import org.openflow.vendor.nicira.OFRoleReplyVendorData;
123import org.openflow.vendor.nicira.OFRoleRequestVendorData;
124import org.openflow.vendor.nicira.OFRoleVendorData;
125import org.slf4j.Logger;
126import org.slf4j.LoggerFactory;
127
128
129/**
130 * The main controller class. Handles all setup and network listeners
Ray Milkey269ffb92014-04-03 14:43:30 -0700131 * <p/>
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700132 * Extensions made by ONOS are:
Ray Milkey269ffb92014-04-03 14:43:30 -0700133 * - Detailed Port event: PORTCHANGED -> {PORTCHANGED, PORTADDED, PORTREMOVED}
134 * Available as net.onrc.onos.core.main.IOFSwitchPortListener
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700135 * - Distributed ownership control of switch through RegistryService(IControllerRegistryService)
Pavlin Radoslavova653e9f2013-10-16 03:08:52 -0700136 * - Register ONOS services. (IControllerRegistryService)
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700137 * - Additional DEBUG logs
138 * - Try using hostname as controller ID, when ID was not explicitly given.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800139 */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800140public class Controller implements IFloodlightProviderService {
Ray Milkey269ffb92014-04-03 14:43:30 -0700141
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700142 protected final static Logger log = LoggerFactory.getLogger(Controller.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800143
Ray Milkey269ffb92014-04-03 14:43:30 -0700144 private static final String ERROR_DATABASE =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800145 "The controller could not communicate with the system database.";
Ray Milkey269ffb92014-04-03 14:43:30 -0700146
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800147 protected BasicFactory factory;
148 protected ConcurrentMap<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -0700149 ListenerDispatcher<OFType, IOFMessageListener>>
150 messageListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800151 // The activeSwitches map contains only those switches that are actively
152 // being controlled by us -- it doesn't contain switches that are
153 // in the slave role
154 protected ConcurrentHashMap<Long, IOFSwitch> activeSwitches;
155 // connectedSwitches contains all connected switches, including ones where
156 // we're a slave controller. We need to keep track of them so that we can
157 // send role request messages to switches when our role changes to master
158 // We add a switch to this set after it successfully completes the
159 // handshake. Access to this Set needs to be synchronized with roleChanger
160 protected HashSet<OFSwitchImpl> connectedSwitches;
Ray Milkey269ffb92014-04-03 14:43:30 -0700161
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800162 // The controllerNodeIPsCache maps Controller IDs to their IP address.
163 // It's only used by handleControllerNodeIPsChanged
164 protected HashMap<String, String> controllerNodeIPsCache;
Ray Milkey269ffb92014-04-03 14:43:30 -0700165
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800166 protected Set<IOFSwitchListener> switchListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800167 protected BlockingQueue<IUpdate> updates;
Ray Milkey269ffb92014-04-03 14:43:30 -0700168
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700169 private CopyOnWriteArrayList<ILocalSwitchMastershipListener> localSwitchMastershipListeners =
170 new CopyOnWriteArrayList<>();
171
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800172 // Module dependencies
173 protected IRestApiService restApi;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800174 protected IThreadPoolService threadPool;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800175 protected IControllerRegistryService registryService;
Ray Milkey269ffb92014-04-03 14:43:30 -0700176
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800177 protected ILinkDiscoveryService linkDiscovery;
Ray Milkey269ffb92014-04-03 14:43:30 -0700178
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800179 // Configuration options
180 protected int openFlowPort = 6633;
181 protected int workerThreads = 0;
182 // The id for this controller node. Should be unique for each controller
183 // node in a controller cluster.
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -0700184 private OnosInstanceId onosInstanceId = new OnosInstanceId("localhost");
Ray Milkey269ffb92014-04-03 14:43:30 -0700185
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800186 // The current role of the controller.
187 // If the controller isn't configured to support roles, then this is null.
188 protected Role role;
189 // A helper that handles sending and timeout handling for role requests
190 protected RoleChanger roleChanger;
Ray Milkey269ffb92014-04-03 14:43:30 -0700191
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800192 // Start time of the controller
193 protected long systemStartTime;
Ray Milkey269ffb92014-04-03 14:43:30 -0700194
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800195 // Flag to always flush flow table on switch reconnect (HA or otherwise)
196 protected boolean alwaysClearFlowsOnSwAdd = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700197
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800198 // Perf. related configuration
199 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
200 protected static final int BATCH_MAX_SIZE = 100;
Ray Milkey269ffb92014-04-03 14:43:30 -0700201 protected static final boolean ALWAYS_DECODE_ETH = true;
202
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800203 public enum SwitchUpdateType {
204 ADDED,
205 REMOVED,
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700206 PORTCHANGED,
207 PORTADDED,
208 PORTREMOVED
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800209 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700210
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800211 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700212 * Update message indicating a switch was added or removed
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700213 * ONOS: This message extended to indicate Port add or removed event.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800214 */
215 protected class SwitchUpdate implements IUpdate {
216 public IOFSwitch sw;
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700217 public OFPhysicalPort port; // Added by ONOS
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800218 public SwitchUpdateType switchUpdateType;
Ray Milkey269ffb92014-04-03 14:43:30 -0700219
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800220 public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
221 this.sw = sw;
222 this.switchUpdateType = switchUpdateType;
223 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700224
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700225 public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
226 this.sw = sw;
227 this.port = port;
228 this.switchUpdateType = switchUpdateType;
229 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700230
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800231 public void dispatch() {
232 if (log.isTraceEnabled()) {
233 log.trace("Dispatching switch update {} {}",
234 sw, switchUpdateType);
235 }
236 if (switchListeners != null) {
237 for (IOFSwitchListener listener : switchListeners) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700238 switch (switchUpdateType) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800239 case ADDED:
240 listener.addedSwitch(sw);
241 break;
242 case REMOVED:
243 listener.removedSwitch(sw);
244 break;
245 case PORTCHANGED:
246 listener.switchPortChanged(sw.getId());
247 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700248 case PORTADDED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700249 if (listener instanceof IOFSwitchPortListener) {
250 ((IOFSwitchPortListener) listener).switchPortAdded(sw.getId(), port);
251 }
252 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700253 case PORTREMOVED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700254 if (listener instanceof IOFSwitchPortListener) {
255 ((IOFSwitchPortListener) listener).switchPortRemoved(sw.getId(), port);
256 }
257 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700258 default:
Ray Milkey269ffb92014-04-03 14:43:30 -0700259 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800260 }
261 }
262 }
263 }
264 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700265
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800266 // ***************
267 // Getters/Setters
Jonathan Hart20fab1a2013-12-12 11:06:50 -0800268 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700269
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800270 public void setRestApiService(IRestApiService restApi) {
271 this.restApi = restApi;
272 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700273
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800274 public void setThreadPoolService(IThreadPoolService tp) {
275 this.threadPool = tp;
276 }
277
Ray Milkey269ffb92014-04-03 14:43:30 -0700278 public void setMastershipService(IControllerRegistryService serviceImpl) {
279 this.registryService = serviceImpl;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700280 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700281
282 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
283 this.linkDiscovery = linkDiscovery;
284 }
285
286 public void publishUpdate(IUpdate update) {
287 try {
288 this.updates.put(update);
289 } catch (InterruptedException e) {
290 log.error("Failure adding update to queue", e);
291 }
292 }
293
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800294 // **********************
295 // ChannelUpstreamHandler
296 // **********************
Ray Milkey269ffb92014-04-03 14:43:30 -0700297
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800298 /**
299 * Return a new channel handler for processing a switch connections
Ray Milkey269ffb92014-04-03 14:43:30 -0700300 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800301 * @param state The channel state object for the connection
302 * @return the new channel handler
303 */
304 protected ChannelUpstreamHandler getChannelHandler(OFChannelState state) {
305 return new OFChannelHandler(state);
306 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700307
Jonathan Hartcc957a02013-02-26 10:39:04 -0800308 protected class RoleChangeCallback implements ControlChangeCallback {
Ray Milkey269ffb92014-04-03 14:43:30 -0700309 @Override
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700310 public void controlChanged(long dpidLong, boolean hasControl) {
311 Dpid dpid = new Dpid(dpidLong);
Ray Milkey269ffb92014-04-03 14:43:30 -0700312 log.info("Role change callback for switch {}, hasControl {}",
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700313 dpid, hasControl);
Pankaj Berde01939e92013-03-08 14:38:27 -0800314
Ray Milkey269ffb92014-04-03 14:43:30 -0700315 synchronized (roleChanger) {
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700316 Role role = null;
317 /*
318 * issue #229
319 * Cannot rely on sw.getRole() as it can be behind due to pending
320 * role changes in the queue. Just submit it and late the RoleChanger
321 * handle duplicates.
322 */
323 if (hasControl) {
324 role = Role.MASTER;
325 } else {
326 role = Role.SLAVE;
327 }
328 //
329 // Inform all listeners about the Controller role change.
330 //
331 // NOTE: The role change here is triggered by the mastership
332 // election mechanism, and reflects what the Controller itself
333 // believes its role should be. The role change request hasn't
334 // been accepted by the switch itself.
335 // If the semantics for informing the listeners is changed
336 // (i.e., the listeners should be informed after the switch
337 // has accepted the role change), then the code below should
338 // be moved to method handleRoleReplyMessage() or its
339 // equivalent.
340 //
341 for (ILocalSwitchMastershipListener listener : localSwitchMastershipListeners) {
342 listener.controllerRoleChanged(dpid, role);
343 }
344
Ray Milkey269ffb92014-04-03 14:43:30 -0700345 OFSwitchImpl sw = null;
346 for (OFSwitchImpl connectedSw : connectedSwitches) {
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700347 if (connectedSw.getId() == dpidLong) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700348 sw = connectedSw;
349 break;
350 }
351 }
352 if (sw == null) {
353 log.warn("Switch {} not found in connected switches",
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700354 dpid);
Ray Milkey269ffb92014-04-03 14:43:30 -0700355 return;
356 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800357
Ray Milkey269ffb92014-04-03 14:43:30 -0700358 log.debug("Sending role request {} msg to {}", role, sw);
359 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
360 swList.add(sw);
361 roleChanger.submitRequest(swList, role);
Ray Milkey269ffb92014-04-03 14:43:30 -0700362 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700363 }
Jonathan Hartcc957a02013-02-26 10:39:04 -0800364 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700365
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800366 /**
367 * Channel handler deals with the switch connection and dispatches
368 * switch messages to the appropriate locations.
Ray Milkey269ffb92014-04-03 14:43:30 -0700369 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800370 * @author readams
371 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700372 protected class OFChannelHandler
373 extends IdleStateAwareChannelUpstreamHandler {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800374 protected OFSwitchImpl sw;
375 protected OFChannelState state;
Ray Milkey269ffb92014-04-03 14:43:30 -0700376
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800377 public OFChannelHandler(OFChannelState state) {
378 this.state = state;
379 }
380
381 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700382 @LogMessageDoc(message = "New switch connection from {ip address}",
383 explanation = "A new switch has connected from the " +
384 "specified IP address")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800385 public void channelConnected(ChannelHandlerContext ctx,
386 ChannelStateEvent e) throws Exception {
387 log.info("New switch connection from {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700388 e.getChannel().getRemoteAddress());
389
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800390 sw = new OFSwitchImpl();
391 sw.setChannel(e.getChannel());
392 sw.setFloodlightProvider(Controller.this);
393 sw.setThreadPoolService(threadPool);
Ray Milkey269ffb92014-04-03 14:43:30 -0700394
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800395 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
396 msglist.add(factory.getMessage(OFType.HELLO));
397 e.getChannel().write(msglist);
398
399 }
400
401 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700402 @LogMessageDoc(message = "Disconnected switch {switch information}",
403 explanation = "The specified switch has disconnected.")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800404 public void channelDisconnected(ChannelHandlerContext ctx,
405 ChannelStateEvent e) throws Exception {
406 if (sw != null && state.hsState == HandshakeState.READY) {
407 if (activeSwitches.containsKey(sw.getId())) {
408 // It's safe to call removeSwitch even though the map might
409 // not contain this particular switch but another with the
410 // same DPID
411 removeSwitch(sw);
412 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700413 synchronized (roleChanger) {
414 if (controlRequested) {
Pavlin Radoslavov695f8952014-07-23 16:57:01 -0700415
416 //
417 // Inform all listeners about the switch disconnection
418 //
419 Dpid dpid = new Dpid(sw.getId());
420 for (ILocalSwitchMastershipListener listener :
421 localSwitchMastershipListeners) {
422 listener.switchDisconnected(dpid);
423 }
424
Ray Milkey269ffb92014-04-03 14:43:30 -0700425 registryService.releaseControl(sw.getId());
426 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800427 connectedSwitches.remove(sw);
428 }
429 sw.setConnected(false);
430 }
431 log.info("Disconnected switch {}", sw);
432 }
433
434 @Override
435 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700436 @LogMessageDoc(level = "ERROR",
437 message = "Disconnecting switch {switch} due to read timeout",
438 explanation = "The connected switch has failed to send any " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800439 "messages or respond to echo requests",
Ray Milkey269ffb92014-04-03 14:43:30 -0700440 recommendation = LogMessageDoc.CHECK_SWITCH),
441 @LogMessageDoc(level = "ERROR",
442 message = "Disconnecting switch {switch}: failed to " +
443 "complete handshake",
444 explanation = "The switch did not respond correctly " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800445 "to handshake messages",
Ray Milkey269ffb92014-04-03 14:43:30 -0700446 recommendation = LogMessageDoc.CHECK_SWITCH),
447 @LogMessageDoc(level = "ERROR",
448 message = "Disconnecting switch {switch} due to IO Error: {}",
449 explanation = "There was an error communicating with the switch",
450 recommendation = LogMessageDoc.CHECK_SWITCH),
451 @LogMessageDoc(level = "ERROR",
452 message = "Disconnecting switch {switch} due to switch " +
453 "state error: {error}",
454 explanation = "The switch sent an unexpected message",
455 recommendation = LogMessageDoc.CHECK_SWITCH),
456 @LogMessageDoc(level = "ERROR",
457 message = "Disconnecting switch {switch} due to " +
458 "message parse failure",
459 explanation = "Could not parse a message from the switch",
460 recommendation = LogMessageDoc.CHECK_SWITCH),
461 @LogMessageDoc(level = "ERROR",
462 message = "Could not process message: queue full",
463 explanation = "OpenFlow messages are arriving faster than " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800464 " the controller can process them.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700465 recommendation = LogMessageDoc.CHECK_CONTROLLER),
466 @LogMessageDoc(level = "ERROR",
467 message = "Error while processing message " +
468 "from switch {switch} {cause}",
469 explanation = "An error occurred processing the switch message",
470 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800471 })
472 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
473 throws Exception {
474 if (e.getCause() instanceof ReadTimeoutException) {
475 // switch timeout
476 log.error("Disconnecting switch {} due to read timeout", sw);
477 ctx.getChannel().close();
478 } else if (e.getCause() instanceof HandshakeTimeoutException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700479 log.error("Disconnecting switch {}: failed to complete handshake",
480 sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800481 ctx.getChannel().close();
482 } else if (e.getCause() instanceof ClosedChannelException) {
483 //log.warn("Channel for sw {} already closed", sw);
484 } else if (e.getCause() instanceof IOException) {
485 log.error("Disconnecting switch {} due to IO Error: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700486 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800487 ctx.getChannel().close();
488 } else if (e.getCause() instanceof SwitchStateException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 log.error("Disconnecting switch {} due to switch state error: {}",
490 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800491 ctx.getChannel().close();
492 } else if (e.getCause() instanceof MessageParseException) {
493 log.error("Disconnecting switch " + sw +
Ray Milkey269ffb92014-04-03 14:43:30 -0700494 " due to message parse failure",
495 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800496 ctx.getChannel().close();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800497 } else if (e.getCause() instanceof RejectedExecutionException) {
498 log.warn("Could not process message: queue full");
499 } else {
500 log.error("Error while processing message from switch " + sw,
Ray Milkey269ffb92014-04-03 14:43:30 -0700501 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800502 ctx.getChannel().close();
503 }
504 }
505
506 @Override
507 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
508 throws Exception {
509 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
510 msglist.add(factory.getMessage(OFType.ECHO_REQUEST));
511 e.getChannel().write(msglist);
512 }
513
514 @Override
515 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
516 throws Exception {
517 if (e.getMessage() instanceof List) {
518 @SuppressWarnings("unchecked")
Ray Milkey269ffb92014-04-03 14:43:30 -0700519 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800520
521 for (OFMessage ofm : msglist) {
522 try {
523 processOFMessage(ofm);
Ray Milkey269ffb92014-04-03 14:43:30 -0700524 } catch (Exception ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800525 // We are the last handler in the stream, so run the
526 // exception through the channel again by passing in
527 // ctx.getChannel().
528 Channels.fireExceptionCaught(ctx.getChannel(), ex);
529 }
530 }
531
532 // Flush all flow-mods/packet-out generated from this "train"
533 OFSwitchImpl.flush_all();
534 }
535 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700536
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800537 /**
538 * Process the request for the switch description
539 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700540 @LogMessageDoc(level = "ERROR",
541 message = "Exception in reading description " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800542 " during handshake {exception}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700543 explanation = "Could not process the switch description string",
544 recommendation = LogMessageDoc.CHECK_SWITCH)
Ray Milkeyff735142014-05-22 19:06:02 -0700545 @SuppressWarnings("unchecked")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800546 void processSwitchDescReply() {
547 try {
548 // Read description, if it has been updated
Ray Milkeyff735142014-05-22 19:06:02 -0700549 Future<?> future =
550 (Future<?>) sw.
Ray Milkey269ffb92014-04-03 14:43:30 -0700551 getAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
Ray Milkeyff735142014-05-22 19:06:02 -0700552 Future<List<OFStatistics>> desc_future =
553 (Future<List<OFStatistics>>) future;
Ray Milkey269ffb92014-04-03 14:43:30 -0700554 List<OFStatistics> values =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800555 desc_future.get(0, TimeUnit.MILLISECONDS);
556 if (values != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700557 OFDescriptionStatistics description =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800558 new OFDescriptionStatistics();
Ray Milkey269ffb92014-04-03 14:43:30 -0700559 ChannelBuffer data =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800560 ChannelBuffers.buffer(description.getLength());
561 for (OFStatistics f : values) {
562 f.writeTo(data);
563 description.readFrom(data);
564 break; // SHOULD be a list of length 1
565 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700566 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_DATA,
567 description);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800568 sw.setSwitchProperties(description);
569 data = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800570 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700571
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800572 sw.removeAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
573 state.hasDescription = true;
574 checkSwitchReady();
Ray Milkey269ffb92014-04-03 14:43:30 -0700575 } catch (InterruptedException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800576 // Ignore
Ray Milkey269ffb92014-04-03 14:43:30 -0700577 } catch (TimeoutException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800578 // Ignore
579 } catch (Exception ex) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700580 log.error("Exception in reading description " +
581 " during handshake", ex);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800582 }
583 }
584
585 /**
586 * Send initial switch setup information that we need before adding
587 * the switch
Ray Milkey269ffb92014-04-03 14:43:30 -0700588 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800589 * @throws IOException
590 */
591 void sendHelloConfiguration() throws IOException {
592 // Send initial Features Request
Ray Milkey269ffb92014-04-03 14:43:30 -0700593 log.debug("Sending FEATURES_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800594 sw.write(factory.getMessage(OFType.FEATURES_REQUEST), null);
595 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700596
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800597 /**
598 * Send the configuration requests we can only do after we have
599 * the features reply
Ray Milkey269ffb92014-04-03 14:43:30 -0700600 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800601 * @throws IOException
602 */
603 void sendFeatureReplyConfiguration() throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700604 log.debug("Sending CONFIG_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800605 // Ensure we receive the full packet via PacketIn
606 OFSetConfig config = (OFSetConfig) factory
607 .getMessage(OFType.SET_CONFIG);
608 config.setMissSendLength((short) 0xffff)
Ray Milkey269ffb92014-04-03 14:43:30 -0700609 .setLengthU(OFSwitchConfig.MINIMUM_LENGTH);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800610 sw.write(config, null);
611 sw.write(factory.getMessage(OFType.GET_CONFIG_REQUEST),
612 null);
613
614 // Get Description to set switch-specific flags
615 OFStatisticsRequest req = new OFStatisticsRequest();
616 req.setStatisticType(OFStatisticsType.DESC);
617 req.setLengthU(req.getLengthU());
Ray Milkey269ffb92014-04-03 14:43:30 -0700618 Future<List<OFStatistics>> dfuture =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800619 sw.getStatistics(req);
620 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE,
621 dfuture);
622
623 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700624
625 volatile Boolean controlRequested = Boolean.FALSE;
626
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800627 protected void checkSwitchReady() {
628 if (state.hsState == HandshakeState.FEATURES_REPLY &&
629 state.hasDescription && state.hasGetConfigReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700630
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800631 state.hsState = HandshakeState.READY;
Jonathan Hart9e92c512013-03-20 16:24:44 -0700632 log.debug("Handshake with {} complete", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700633
634 synchronized (roleChanger) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800635 // We need to keep track of all of the switches that are connected
Ray Milkeyff735142014-05-22 19:06:02 -0700636 // to the controller, in any role, so that we can later send the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800637 // role request messages when the controller role changes.
Ray Milkeyff735142014-05-22 19:06:02 -0700638 // We need to be synchronized while doing this: we must not
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800639 // send a another role request to the connectedSwitches until
Ray Milkeyff735142014-05-22 19:06:02 -0700640 // we were able to add this new switch to connectedSwitches
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800641 // *and* send the current role to the new switch.
642 connectedSwitches.add(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700643
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800644 if (role != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700645 //Put the switch in SLAVE mode until we know we have control
646 log.debug("Setting new switch {} to SLAVE", sw.getStringId());
647 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
648 swList.add(sw);
649 roleChanger.submitRequest(swList, Role.SLAVE);
650
651 //Request control of the switch from the global registry
652 try {
653 controlRequested = Boolean.TRUE;
654 registryService.requestControl(sw.getId(),
655 new RoleChangeCallback());
656 } catch (RegistryException e) {
657 log.debug("Registry error: {}", e.getMessage());
658 controlRequested = Boolean.FALSE;
659 }
660
661
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800662 // Send a role request if role support is enabled for the controller
663 // This is a probe that we'll use to determine if the switch
664 // actually supports the role request message. If it does we'll
665 // get back a role reply message. If it doesn't we'll get back an
Ray Milkeyff735142014-05-22 19:06:02 -0700666 // OFError message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800667 // If role is MASTER we will promote switch to active
668 // list when we receive the switch's role reply messages
Jonathan Hartcc957a02013-02-26 10:39:04 -0800669 /*
Ray Milkeyff735142014-05-22 19:06:02 -0700670 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800671 "sending initial role request msg to {}",
672 role, sw);
673 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
674 swList.add(sw);
675 roleChanger.submitRequest(swList, role);
Jonathan Hartcc957a02013-02-26 10:39:04 -0800676 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700677 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800678 // Role supported not enabled on controller (for now)
Ray Milkeyff735142014-05-22 19:06:02 -0700679 // automatically promote switch to active state.
Ray Milkey269ffb92014-04-03 14:43:30 -0700680 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800681 "not sending role request msg to {}",
682 role, sw);
683 // Need to clear FlowMods before we add the switch
684 // and dispatch updates otherwise we have a race condition.
685 sw.clearAllFlowMods();
686 addSwitch(sw);
687 state.firstRoleReplyReceived = true;
688 }
689 }
Pankaj Berde99fcee12013-03-18 09:41:53 -0700690 if (!controlRequested) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700691 // yield to allow other thread(s) to release control
692 try {
693 Thread.sleep(10);
694 } catch (InterruptedException e) {
695 // Ignore interruptions
696 }
697 // safer to bounce the switch to reconnect here than proceeding further
698 log.debug("Closing {} because we weren't able to request control " +
699 "successfully" + sw);
700 sw.channel.close();
Pankaj Berde99fcee12013-03-18 09:41:53 -0700701 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800702 }
703 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700704
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800705 /* Handle a role reply message we received from the switch. Since
Ray Milkeyff735142014-05-22 19:06:02 -0700706 * netty serializes message dispatch we don't need to synchronize
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800707 * against other receive operations from the same switch, so no need
708 * to synchronize addSwitch(), removeSwitch() operations from the same
Ray Milkeyff735142014-05-22 19:06:02 -0700709 * connection.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800710 * FIXME: However, when a switch with the same DPID connects we do
711 * need some synchronization. However, handling switches with same
712 * DPID needs to be revisited anyways (get rid of r/w-lock and synchronous
713 * removedSwitch notification):1
Ray Milkeyff735142014-05-22 19:06:02 -0700714 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800715 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700716 @LogMessageDoc(level = "ERROR",
717 message = "Invalid role value in role reply message",
718 explanation = "Was unable to set the HA role (master or slave) " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800719 "for the controller.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700720 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800721 protected void handleRoleReplyMessage(OFVendor vendorMessage,
Ray Milkey269ffb92014-04-03 14:43:30 -0700722 OFRoleReplyVendorData roleReplyVendorData) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800723 // Map from the role code in the message to our role enum
724 int nxRole = roleReplyVendorData.getRole();
725 Role role = null;
726 switch (nxRole) {
727 case OFRoleVendorData.NX_ROLE_OTHER:
728 role = Role.EQUAL;
729 break;
730 case OFRoleVendorData.NX_ROLE_MASTER:
731 role = Role.MASTER;
732 break;
733 case OFRoleVendorData.NX_ROLE_SLAVE:
734 role = Role.SLAVE;
735 break;
736 default:
737 log.error("Invalid role value in role reply message");
738 sw.getChannel().close();
739 return;
740 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700741
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800742 log.debug("Handling role reply for role {} from {}. " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700743 "Controller's role is {} ",
744 new Object[]{role, sw, Controller.this.role}
745 );
746
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800747 sw.deliverRoleReply(vendorMessage.getXid(), role);
Ray Milkey269ffb92014-04-03 14:43:30 -0700748
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800749 boolean isActive = activeSwitches.containsKey(sw.getId());
750 if (!isActive && sw.isActive()) {
751 // Transition from SLAVE to MASTER.
Ray Milkey269ffb92014-04-03 14:43:30 -0700752
753 if (!state.firstRoleReplyReceived ||
754 getAlwaysClearFlowsOnSwAdd()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800755 // This is the first role-reply message we receive from
756 // this switch or roles were disabled when the switch
Ray Milkeyff735142014-05-22 19:06:02 -0700757 // connected:
758 // Delete all pre-existing flows for new connections to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800759 // the master
760 //
Ray Milkeyff735142014-05-22 19:06:02 -0700761 // FIXME: Need to think more about what the test should
762 // be for when we flush the flow-table? For example,
763 // if all the controllers are temporarily in the backup
764 // role (e.g. right after a failure of the master
765 // controller) at the point the switch connects, then
766 // all of the controllers will initially connect as
767 // backup controllers and not flush the flow-table.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800768 // Then when one of them is promoted to master following
769 // the master controller election the flow-table
Ray Milkeyff735142014-05-22 19:06:02 -0700770 // will still not be flushed because that's treated as
771 // a failover event where we don't want to flush the
772 // flow-table. The end result would be that the flow
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800773 // table for a newly connected switch is never
774 // flushed. Not sure how to handle that case though...
775 sw.clearAllFlowMods();
776 log.debug("First role reply from master switch {}, " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700777 "clear FlowTable to active switch list",
778 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800779 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700780
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800781 // Some switches don't seem to update us with port
782 // status messages while in slave role.
Ray Milkeyff735142014-05-22 19:06:02 -0700783 //readSwitchPortStateFromStorage(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700784
Ray Milkeyff735142014-05-22 19:06:02 -0700785 // Only add the switch to the active switch list if
786 // we're not in the slave role. Note that if the role
787 // attribute is null, then that means that the switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800788 // doesn't support the role request messages, so in that
Ray Milkeyff735142014-05-22 19:06:02 -0700789 // case we're effectively in the EQUAL role and the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800790 // switch should be included in the active switch list.
791 addSwitch(sw);
792 log.debug("Added master switch {} to active switch list",
Ray Milkey269ffb92014-04-03 14:43:30 -0700793 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800794
Ray Milkey269ffb92014-04-03 14:43:30 -0700795 } else if (isActive && !sw.isActive()) {
Ray Milkeyff735142014-05-22 19:06:02 -0700796 // Transition from MASTER to SLAVE: remove switch
797 // from active switch list.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800798 log.debug("Removed slave switch {} from active switch" +
Ray Milkey269ffb92014-04-03 14:43:30 -0700799 " list", HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800800 removeSwitch(sw);
801 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700802
Ray Milkeyff735142014-05-22 19:06:02 -0700803 // Indicate that we have received a role reply message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800804 state.firstRoleReplyReceived = true;
805 }
806
807 protected boolean handleVendorMessage(OFVendor vendorMessage) {
808 boolean shouldHandleMessage = false;
809 int vendor = vendorMessage.getVendor();
810 switch (vendor) {
811 case OFNiciraVendorData.NX_VENDOR_ID:
812 OFNiciraVendorData niciraVendorData =
Ray Milkey269ffb92014-04-03 14:43:30 -0700813 (OFNiciraVendorData) vendorMessage.getVendorData();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800814 int dataType = niciraVendorData.getDataType();
815 switch (dataType) {
816 case OFRoleReplyVendorData.NXT_ROLE_REPLY:
817 OFRoleReplyVendorData roleReplyVendorData =
818 (OFRoleReplyVendorData) niciraVendorData;
Ray Milkey269ffb92014-04-03 14:43:30 -0700819 handleRoleReplyMessage(vendorMessage,
820 roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800821 break;
822 default:
823 log.warn("Unhandled Nicira VENDOR message; " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700824 "data type = {}", dataType);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800825 break;
826 }
827 break;
828 default:
829 log.warn("Unhandled VENDOR message; vendor id = {}", vendor);
830 break;
831 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700832
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800833 return shouldHandleMessage;
834 }
835
836 /**
837 * Dispatch an Openflow message from a switch to the appropriate
838 * handler.
Ray Milkey269ffb92014-04-03 14:43:30 -0700839 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800840 * @param m The message to process
841 * @throws IOException
Ray Milkey269ffb92014-04-03 14:43:30 -0700842 * @throws SwitchStateException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800843 */
844 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700845 @LogMessageDoc(level = "WARN",
846 message = "Config Reply from {switch} has " +
847 "miss length set to {length}",
848 explanation = "The controller requires that the switch " +
849 "use a miss length of 0xffff for correct " +
850 "function",
851 recommendation = "Use a different switch to ensure " +
852 "correct function"),
853 @LogMessageDoc(level = "WARN",
854 message = "Received ERROR from sw {switch} that "
855 + "indicates roles are not supported "
856 + "but we have received a valid "
857 + "role reply earlier",
858 explanation = "The switch sent a confusing message to the" +
859 "controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800860 })
861 protected void processOFMessage(OFMessage m)
862 throws IOException, SwitchStateException {
863 boolean shouldHandleMessage = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700864
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800865 switch (m.getType()) {
866 case HELLO:
867 if (log.isTraceEnabled())
868 log.trace("HELLO from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700869
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800870 if (state.hsState.equals(HandshakeState.START)) {
871 state.hsState = HandshakeState.HELLO;
872 sendHelloConfiguration();
873 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700874 throw new SwitchStateException("Unexpected HELLO from "
875 + sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800876 }
877 break;
878 case ECHO_REQUEST:
879 OFEchoReply reply =
Ray Milkey269ffb92014-04-03 14:43:30 -0700880 (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800881 reply.setXid(m.getXid());
882 sw.write(reply, null);
883 break;
884 case ECHO_REPLY:
885 break;
886 case FEATURES_REPLY:
887 if (log.isTraceEnabled())
888 log.trace("Features Reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700889
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800890 sw.setFeaturesReply((OFFeaturesReply) m);
891 if (state.hsState.equals(HandshakeState.HELLO)) {
892 sendFeatureReplyConfiguration();
893 state.hsState = HandshakeState.FEATURES_REPLY;
894 // uncomment to enable "dumb" switches like cbench
895 // state.hsState = HandshakeState.READY;
896 // addSwitch(sw);
897 } else {
898 // return results to rest api caller
899 sw.deliverOFFeaturesReply(m);
900 // update database */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800901 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800902 }
903 break;
904 case GET_CONFIG_REPLY:
905 if (log.isTraceEnabled())
906 log.trace("Get config reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700907
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800908 if (!state.hsState.equals(HandshakeState.FEATURES_REPLY)) {
909 String em = "Unexpected GET_CONFIG_REPLY from " + sw;
910 throw new SwitchStateException(em);
911 }
912 OFGetConfigReply cr = (OFGetConfigReply) m;
Ray Milkey269ffb92014-04-03 14:43:30 -0700913 if (cr.getMissSendLength() == (short) 0xffff) {
914 log.trace("Config Reply from {} confirms " +
915 "miss length set to 0xffff", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800916 } else {
917 log.warn("Config Reply from {} has " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700918 "miss length set to {}",
919 sw, cr.getMissSendLength() & 0xffff);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800920 }
921 state.hasGetConfigReply = true;
922 checkSwitchReady();
923 break;
924 case VENDOR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700925 shouldHandleMessage = handleVendorMessage((OFVendor) m);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800926 break;
927 case ERROR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700928 log.debug("Recieved ERROR message from switch {}: {}", sw, m);
Ray Milkeyff735142014-05-22 19:06:02 -0700929 // TODO: we need better error handling. Especially for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800930 // request/reply style message (stats, roles) we should have
Ray Milkeyff735142014-05-22 19:06:02 -0700931 // a unified way to lookup the xid in the error message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800932 // This will probable involve rewriting the way we handle
933 // request/reply style messages.
934 OFError error = (OFError) m;
935 boolean shouldLogError = true;
936 // TODO: should we check that firstRoleReplyReceived is false,
937 // i.e., check only whether the first request fails?
938 if (sw.checkFirstPendingRoleRequestXid(error.getXid())) {
939 boolean isBadVendorError =
Ray Milkey269ffb92014-04-03 14:43:30 -0700940 (error.getErrorType() == OFError.OFErrorType.
941 OFPET_BAD_REQUEST.getValue());
Ray Milkeyff735142014-05-22 19:06:02 -0700942 // We expect to receive a bad vendor error when
943 // we're connected to a switch that doesn't support
944 // the Nicira vendor extensions (i.e. not OVS or
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800945 // derived from OVS). By protocol, it should also be
946 // BAD_VENDOR, but too many switch implementations
947 // get it wrong and we can already check the xid()
948 // so we can ignore the type with confidence that this
949 // is not a spurious error
950 shouldLogError = !isBadVendorError;
951 if (isBadVendorError) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700952 log.debug("Handling bad vendor error for {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800953 if (state.firstRoleReplyReceived && (role != null)) {
954 log.warn("Received ERROR from sw {} that "
Ray Milkey269ffb92014-04-03 14:43:30 -0700955 + "indicates roles are not supported "
956 + "but we have received a valid "
957 + "role reply earlier", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800958 }
959 state.firstRoleReplyReceived = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700960 Role requestedRole =
961 sw.deliverRoleRequestNotSupportedEx(error.getXid());
962 synchronized (roleChanger) {
963 if (sw.role == null && Controller.this.role == Role.SLAVE) {
964 //This will now never happen. The Controller's role
965 //is now never SLAVE, always MASTER.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800966 // the switch doesn't understand role request
967 // messages and the current controller role is
Ray Milkeyff735142014-05-22 19:06:02 -0700968 // slave. We need to disconnect the switch.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800969 // @see RoleChanger for rationale
Ray Milkey269ffb92014-04-03 14:43:30 -0700970 log.warn("Closing {} channel because controller's role " +
971 "is SLAVE", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800972 sw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700973 } else if (sw.role == null && requestedRole == Role.MASTER) {
974 log.debug("Adding switch {} because we got an error" +
975 " returned from a MASTER role request", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800976 // Controller's role is master: add to
Ray Milkeyff735142014-05-22 19:06:02 -0700977 // active
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800978 // TODO: check if clearing flow table is
979 // right choice here.
980 // Need to clear FlowMods before we add the switch
981 // and dispatch updates otherwise we have a race condition.
982 // TODO: switch update is async. Won't we still have a potential
Ray Milkeyff735142014-05-22 19:06:02 -0700983 // race condition?
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800984 sw.clearAllFlowMods();
985 addSwitch(sw);
986 }
987 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700988 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800989 // TODO: Is this the right thing to do if we receive
Ray Milkeyff735142014-05-22 19:06:02 -0700990 // some other error besides a bad vendor error?
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800991 // Presumably that means the switch did actually
Ray Milkeyff735142014-05-22 19:06:02 -0700992 // understand the role request message, but there
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800993 // was some other error from processing the message.
994 // OF 1.2 specifies a OFPET_ROLE_REQUEST_FAILED
Ray Milkeyff735142014-05-22 19:06:02 -0700995 // error code, but it doesn't look like the Nicira
996 // role request has that. Should check OVS source
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800997 // code to see if it's possible for any other errors
998 // to be returned.
999 // If we received an error the switch is not
Ray Milkeyff735142014-05-22 19:06:02 -07001000 // in the correct role, so we need to disconnect it.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001001 // We could also resend the request but then we need to
1002 // check if there are other pending request in which
1003 // case we shouldn't resend. If we do resend we need
1004 // to make sure that the switch eventually accepts one
1005 // of our requests or disconnect the switch. This feels
Ray Milkeyff735142014-05-22 19:06:02 -07001006 // cumbersome.
Ray Milkey269ffb92014-04-03 14:43:30 -07001007 log.debug("Closing {} channel because we recieved an " +
1008 "error other than BAD_VENDOR", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001009 sw.getChannel().close();
1010 }
1011 }
1012 // Once we support OF 1.2, we'd add code to handle it here.
1013 //if (error.getXid() == state.ofRoleRequestXid) {
1014 //}
1015 if (shouldLogError)
1016 logError(sw, error);
1017 break;
1018 case STATS_REPLY:
Ray Milkey269ffb92014-04-03 14:43:30 -07001019 if (state.hsState.ordinal() <
1020 HandshakeState.FEATURES_REPLY.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001021 String em = "Unexpected STATS_REPLY from " + sw;
1022 throw new SwitchStateException(em);
1023 }
1024 sw.deliverStatisticsReply(m);
1025 if (sw.hasAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE)) {
1026 processSwitchDescReply();
1027 }
1028 break;
1029 case PORT_STATUS:
Ray Milkeyff735142014-05-22 19:06:02 -07001030 // We want to update our port state info even if we're in
1031 // the slave role, but we only want to update storage if
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001032 // we're the master (or equal).
1033 boolean updateStorage = state.hsState.
Ray Milkey269ffb92014-04-03 14:43:30 -07001034 equals(HandshakeState.READY) &&
1035 (sw.getRole() != Role.SLAVE);
1036 handlePortStatusMessage(sw, (OFPortStatus) m, updateStorage);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001037 shouldHandleMessage = true;
1038 break;
1039
1040 default:
1041 shouldHandleMessage = true;
1042 break;
1043 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001044
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001045 if (shouldHandleMessage) {
1046 sw.getListenerReadLock().lock();
1047 try {
1048 if (sw.isConnected()) {
1049 if (!state.hsState.equals(HandshakeState.READY)) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001050 log.debug("Ignoring message type {} received " +
1051 "from switch {} before switch is " +
1052 "fully configured.", m.getType(), sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001053 }
Ray Milkeyff735142014-05-22 19:06:02 -07001054 // Check if the controller is in the slave role for the
1055 // switch. If it is, then don't dispatch the message to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001056 // the listeners.
Ray Milkeyff735142014-05-22 19:06:02 -07001057 // TODO: Should we dispatch messages that we expect to
1058 // receive when we're in the slave role, e.g. port
1059 // status messages? Since we're "hiding" switches from
1060 // the listeners when we're in the slave role, then it
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001061 // seems a little weird to dispatch port status messages
Ray Milkeyff735142014-05-22 19:06:02 -07001062 // to them. On the other hand there might be special
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001063 // modules that care about all of the connected switches
1064 // and would like to receive port status notifications.
1065 else if (sw.getRole() == Role.SLAVE) {
Ray Milkeyff735142014-05-22 19:06:02 -07001066 // Don't log message if it's a port status message
1067 // since we expect to receive those from the switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001068 // and don't want to emit spurious messages.
1069 if (m.getType() != OFType.PORT_STATUS) {
1070 log.debug("Ignoring message type {} received " +
1071 "from switch {} while in the slave role.",
1072 m.getType(), sw);
1073 }
1074 } else {
1075 handleMessage(sw, m, null);
1076 }
1077 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001078 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001079 sw.getListenerReadLock().unlock();
1080 }
1081 }
1082 }
1083 }
1084
1085 // ****************
1086 // Message handlers
1087 // ****************
Ray Milkey269ffb92014-04-03 14:43:30 -07001088
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001089 protected void handlePortStatusMessage(IOFSwitch sw,
1090 OFPortStatus m,
1091 boolean updateStorage) {
1092 short portNumber = m.getDesc().getPortNumber();
1093 OFPhysicalPort port = m.getDesc();
Ray Milkey269ffb92014-04-03 14:43:30 -07001094 if (m.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
1095 boolean portDown = ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) ||
1096 ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001097 sw.setPort(port);
Ray Milkey269ffb92014-04-03 14:43:30 -07001098 if (!portDown) {
1099 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1100 try {
1101 this.updates.put(update);
1102 } catch (InterruptedException e) {
1103 log.error("Failure adding update to queue", e);
1104 }
1105 } else {
1106 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1107 try {
1108 this.updates.put(update);
1109 } catch (InterruptedException e) {
1110 log.error("Failure adding update to queue", e);
1111 }
1112 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001113 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001114 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001115 log.debug("Port #{} modified for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001116 } else if (m.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
1117 // XXX Workaround to prevent race condition where a link is detected
1118 // and attempted to be written to the database before the port is in
1119 // the database. We now suppress link discovery on ports until we're
1120 // sure they're in the database.
Jonathan Hart284e70f2014-07-05 12:32:51 -07001121 linkDiscovery.disableDiscoveryOnPort(sw.getId(), port.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -07001122
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001123 sw.setPort(port);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001124 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1125 try {
1126 this.updates.put(update);
1127 } catch (InterruptedException e) {
1128 log.error("Failure adding update to queue", e);
1129 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001130 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001131 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001132 log.debug("Port #{} added for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001133 } else if (m.getReason() ==
1134 (byte) OFPortReason.OFPPR_DELETE.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001135 sw.deletePort(portNumber);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001136 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1137 try {
1138 this.updates.put(update);
1139 } catch (InterruptedException e) {
1140 log.error("Failure adding update to queue", e);
1141 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001142 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001143 //removePortInfo(sw, portNumber);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001144 log.debug("Port #{} deleted for {}", portNumber, sw);
1145 }
1146 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED);
1147 try {
1148 this.updates.put(update);
1149 } catch (InterruptedException e) {
1150 log.error("Failure adding update to queue", e);
1151 }
1152 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001153
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001154 /**
1155 * flcontext_cache - Keep a thread local stack of contexts
1156 */
1157 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
Ray Milkey269ffb92014-04-03 14:43:30 -07001158 new ThreadLocal<Stack<FloodlightContext>>() {
1159 @Override
1160 protected Stack<FloodlightContext> initialValue() {
1161 return new Stack<FloodlightContext>();
1162 }
1163 };
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001164
1165 /**
1166 * flcontext_alloc - pop a context off the stack, if required create a new one
Ray Milkey269ffb92014-04-03 14:43:30 -07001167 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001168 * @return FloodlightContext
1169 */
1170 protected static FloodlightContext flcontext_alloc() {
1171 FloodlightContext flcontext = null;
1172
1173 if (flcontext_cache.get().empty()) {
1174 flcontext = new FloodlightContext();
Ray Milkey269ffb92014-04-03 14:43:30 -07001175 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001176 flcontext = flcontext_cache.get().pop();
1177 }
1178
1179 return flcontext;
1180 }
1181
1182 /**
1183 * flcontext_free - Free the context to the current thread
Ray Milkey269ffb92014-04-03 14:43:30 -07001184 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001185 * @param flcontext
1186 */
1187 protected void flcontext_free(FloodlightContext flcontext) {
1188 flcontext.getStorage().clear();
1189 flcontext_cache.get().push(flcontext);
1190 }
1191
1192 /**
1193 * Handle replies to certain OFMessages, and pass others off to listeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001194 *
1195 * @param sw The switch for the message
1196 * @param m The message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001197 * @param bContext The floodlight context. If null then floodlight context would
Ray Milkey269ffb92014-04-03 14:43:30 -07001198 * be allocated in this function
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001199 * @throws IOException
1200 */
1201 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001202 @LogMessageDoc(level = "ERROR",
1203 message = "Ignoring PacketIn (Xid = {xid}) because the data" +
1204 " field is empty.",
1205 explanation = "The switch sent an improperly-formatted PacketIn" +
1206 " message",
1207 recommendation = LogMessageDoc.CHECK_SWITCH),
1208 @LogMessageDoc(level = "WARN",
1209 message = "Unhandled OF Message: {} from {}",
1210 explanation = "The switch sent a message not handled by " +
1211 "the controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001212 })
Ray Milkeyff735142014-05-22 19:06:02 -07001213 @SuppressWarnings("fallthrough")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001214 protected void handleMessage(IOFSwitch sw, OFMessage m,
1215 FloodlightContext bContext)
1216 throws IOException {
1217 Ethernet eth = null;
1218
1219 switch (m.getType()) {
1220 case PACKET_IN:
Ray Milkey269ffb92014-04-03 14:43:30 -07001221 OFPacketIn pi = (OFPacketIn) m;
1222
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001223 if (pi.getPacketData().length <= 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001224 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
1225 ") because the data field is empty.");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001226 return;
1227 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001228
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001229 if (Controller.ALWAYS_DECODE_ETH) {
1230 eth = new Ethernet();
1231 eth.deserialize(pi.getPacketData(), 0,
1232 pi.getPacketData().length);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001233 }
1234 // fall through to default case...
1235
1236 default:
Ray Milkey269ffb92014-04-03 14:43:30 -07001237
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001238 List<IOFMessageListener> listeners = null;
1239 if (messageListeners.containsKey(m.getType())) {
1240 listeners = messageListeners.get(m.getType()).
1241 getOrderedListeners();
1242 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001243
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001244 FloodlightContext bc = null;
1245 if (listeners != null) {
1246 // Check if floodlight context is passed from the calling
1247 // function, if so use that floodlight context, otherwise
1248 // allocate one
1249 if (bContext == null) {
1250 bc = flcontext_alloc();
1251 } else {
1252 bc = bContext;
1253 }
1254 if (eth != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001255 IFloodlightProviderService.bcStore.put(bc,
1256 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001257 eth);
1258 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001259
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001260 // Get the starting time (overall and per-component) of
1261 // the processing chain for this packet if performance
1262 // monitoring is turned on
Ray Milkey269ffb92014-04-03 14:43:30 -07001263
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001264 Command cmd;
1265 for (IOFMessageListener listener : listeners) {
1266 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001267 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001268 continue;
1269 }
1270 }
1271
mininet73e7fb72013-12-03 14:25:53 -08001272
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001273 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -08001274
Ray Milkey269ffb92014-04-03 14:43:30 -07001275
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001276 if (Command.STOP.equals(cmd)) {
1277 break;
1278 }
1279 }
mininet73e7fb72013-12-03 14:25:53 -08001280
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001281 } else {
1282 log.warn("Unhandled OF Message: {} from {}", m, sw);
1283 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001284
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001285 if ((bContext == null) && (bc != null)) flcontext_free(bc);
1286 }
1287 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001288
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001289 /**
1290 * Log an OpenFlow error message from a switch
Ray Milkey269ffb92014-04-03 14:43:30 -07001291 *
1292 * @param sw The switch that sent the error
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001293 * @param error The error message
1294 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001295 @LogMessageDoc(level = "ERROR",
1296 message = "Error {error type} {error code} from {switch}",
1297 explanation = "The switch responded with an unexpected error" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001298 "to an OpenFlow message from the controller",
Ray Milkey269ffb92014-04-03 14:43:30 -07001299 recommendation = "This could indicate improper network operation. " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001300 "If the problem persists restarting the switch and " +
1301 "controller may help."
Ray Milkey269ffb92014-04-03 14:43:30 -07001302 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001303 protected void logError(IOFSwitch sw, OFError error) {
1304 int etint = 0xffff & error.getErrorType();
1305 if (etint < 0 || etint >= OFErrorType.values().length) {
1306 log.error("Unknown error code {} from sw {}", etint, sw);
1307 }
1308 OFErrorType et = OFErrorType.values()[etint];
1309 switch (et) {
1310 case OFPET_HELLO_FAILED:
Ray Milkey269ffb92014-04-03 14:43:30 -07001311 OFHelloFailedCode hfc =
1312 OFHelloFailedCode.values()[0xffff & error.getErrorCode()];
1313 log.error("Error {} {} from {}", new Object[]{et, hfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001314 break;
1315 case OFPET_BAD_REQUEST:
Ray Milkey269ffb92014-04-03 14:43:30 -07001316 OFBadRequestCode brc =
1317 OFBadRequestCode.values()[0xffff & error.getErrorCode()];
1318 log.error("Error {} {} from {}", new Object[]{et, brc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001319 break;
1320 case OFPET_BAD_ACTION:
1321 OFBadActionCode bac =
Ray Milkey269ffb92014-04-03 14:43:30 -07001322 OFBadActionCode.values()[0xffff & error.getErrorCode()];
1323 log.error("Error {} {} from {}", new Object[]{et, bac, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001324 break;
1325 case OFPET_FLOW_MOD_FAILED:
1326 OFFlowModFailedCode fmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001327 OFFlowModFailedCode.values()[0xffff & error.getErrorCode()];
1328 log.error("Error {} {} from {}", new Object[]{et, fmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001329 break;
1330 case OFPET_PORT_MOD_FAILED:
1331 OFPortModFailedCode pmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001332 OFPortModFailedCode.values()[0xffff & error.getErrorCode()];
1333 log.error("Error {} {} from {}", new Object[]{et, pmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001334 break;
1335 case OFPET_QUEUE_OP_FAILED:
1336 OFQueueOpFailedCode qofc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001337 OFQueueOpFailedCode.values()[0xffff & error.getErrorCode()];
1338 log.error("Error {} {} from {}", new Object[]{et, qofc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001339 break;
1340 default:
1341 break;
1342 }
1343 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001344
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001345 /**
1346 * Add a switch to the active switch list and call the switch listeners.
1347 * This happens either when a switch first connects (and the controller is
1348 * not in the slave role) or when the role of the controller changes from
1349 * slave to master.
Ray Milkey269ffb92014-04-03 14:43:30 -07001350 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001351 * @param sw the switch that has been added
1352 */
1353 // TODO: need to rethink locking and the synchronous switch update.
1354 // We can / should also handle duplicate DPIDs in connectedSwitches
Ray Milkey269ffb92014-04-03 14:43:30 -07001355 @LogMessageDoc(level = "ERROR",
1356 message = "New switch added {switch} for already-added switch {switch}",
1357 explanation = "A switch with the same DPID as another switch " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001358 "connected to the controller. This can be caused by " +
1359 "multiple switches configured with the same DPID, or " +
1360 "by a switch reconnected very quickly after " +
1361 "disconnecting.",
Ray Milkey269ffb92014-04-03 14:43:30 -07001362 recommendation = "If this happens repeatedly, it is likely there " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001363 "are switches with duplicate DPIDs on the network. " +
1364 "Reconfigure the appropriate switches. If it happens " +
1365 "very rarely, then it is likely this is a transient " +
1366 "network problem that can be ignored."
Ray Milkey269ffb92014-04-03 14:43:30 -07001367 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001368 protected void addSwitch(IOFSwitch sw) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001369 // XXX Workaround to prevent race condition where a link is detected
1370 // and attempted to be written to the database before the port is in
1371 // the database. We now suppress link discovery on ports until we're
1372 // sure they're in the database.
1373 for (OFPhysicalPort port : sw.getPorts()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -07001374 linkDiscovery.disableDiscoveryOnPort(sw.getId(), port.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -07001375 }
1376
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001377 // TODO: is it safe to modify the HashMap without holding
1378 // the old switch's lock?
1379 OFSwitchImpl oldSw = (OFSwitchImpl) this.activeSwitches.put(sw.getId(), sw);
1380 if (sw == oldSw) {
1381 // Note == for object equality, not .equals for value
1382 log.info("New add switch for pre-existing switch {}", sw);
1383 return;
1384 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001385
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001386 if (oldSw != null) {
1387 oldSw.getListenerWriteLock().lock();
1388 try {
1389 log.error("New switch added {} for already-added switch {}",
Ray Milkey269ffb92014-04-03 14:43:30 -07001390 sw, oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001391 // Set the connected flag to false to suppress calling
1392 // the listeners for this switch in processOFMessage
1393 oldSw.setConnected(false);
Ray Milkey269ffb92014-04-03 14:43:30 -07001394
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001395 oldSw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001396
Jonathan Hart2fa28062013-11-25 20:16:28 -08001397 //updateInactiveSwitchInfo(oldSw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001398
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001399 // we need to clean out old switch state definitively
1400 // before adding the new switch
1401 // FIXME: It seems not completely kosher to call the
1402 // switch listeners here. I thought one of the points of
1403 // having the asynchronous switch update mechanism was so
1404 // the addedSwitch and removedSwitch were always called
1405 // from a single thread to simplify concurrency issues
1406 // for the listener.
1407 if (switchListeners != null) {
1408 for (IOFSwitchListener listener : switchListeners) {
1409 listener.removedSwitch(oldSw);
1410 }
1411 }
1412 // will eventually trigger a removeSwitch(), which will cause
1413 // a "Not removing Switch ... already removed debug message.
1414 // TODO: Figure out a way to handle this that avoids the
1415 // spurious debug message.
Jonathan Hart9e92c512013-03-20 16:24:44 -07001416 log.debug("Closing {} because a new IOFSwitch got added " +
Ray Milkey269ffb92014-04-03 14:43:30 -07001417 "for this dpid", oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001418 oldSw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -07001419 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001420 oldSw.getListenerWriteLock().unlock();
1421 }
1422 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001423
Jonathan Hart2fa28062013-11-25 20:16:28 -08001424 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001425 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
1426 try {
1427 this.updates.put(update);
1428 } catch (InterruptedException e) {
1429 log.error("Failure adding update to queue", e);
1430 }
1431 }
1432
1433 /**
1434 * Remove a switch from the active switch list and call the switch listeners.
1435 * This happens either when the switch is disconnected or when the
1436 * controller's role for the switch changes from master to slave.
Ray Milkey269ffb92014-04-03 14:43:30 -07001437 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001438 * @param sw the switch that has been removed
1439 */
1440 protected void removeSwitch(IOFSwitch sw) {
1441 // No need to acquire the listener lock, since
1442 // this method is only called after netty has processed all
1443 // pending messages
1444 log.debug("removeSwitch: {}", sw);
1445 if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
1446 log.debug("Not removing switch {}; already removed", sw);
1447 return;
1448 }
1449 // We cancel all outstanding statistics replies if the switch transition
1450 // from active. In the future we might allow statistics requests
1451 // from slave controllers. Then we need to move this cancelation
1452 // to switch disconnect
1453 sw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001454
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001455 // FIXME: I think there's a race condition if we call updateInactiveSwitchInfo
1456 // here if role support is enabled. In that case if the switch is being
1457 // removed because we've been switched to being in the slave role, then I think
1458 // it's possible that the new master may have already been promoted to master
1459 // and written out the active switch state to storage. If we now execute
1460 // updateInactiveSwitchInfo we may wipe out all of the state that was
1461 // written out by the new master. Maybe need to revisit how we handle all
1462 // of the switch state that's written to storage.
Ray Milkey269ffb92014-04-03 14:43:30 -07001463
Jonathan Hart2fa28062013-11-25 20:16:28 -08001464 //updateInactiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001465 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.REMOVED);
1466 try {
1467 this.updates.put(update);
1468 } catch (InterruptedException e) {
1469 log.error("Failure adding update to queue", e);
1470 }
1471 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001472
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001473 // ***************
1474 // IFloodlightProvider
1475 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -07001476
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001477 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001478 public synchronized void addOFMessageListener(OFType type,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001479 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001480 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1481 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001482 if (ldd == null) {
1483 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
1484 messageListeners.put(type, ldd);
1485 }
1486 ldd.addListener(type, listener);
1487 }
1488
1489 @Override
1490 public synchronized void removeOFMessageListener(OFType type,
1491 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001492 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1493 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001494 if (ldd != null) {
1495 ldd.removeListener(listener);
1496 }
1497 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001498
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001499 private void logListeners() {
1500 for (Map.Entry<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -07001501 ListenerDispatcher<OFType,
1502 IOFMessageListener>> entry
1503 : messageListeners.entrySet()) {
1504
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001505 OFType type = entry.getKey();
Ray Milkey269ffb92014-04-03 14:43:30 -07001506 ListenerDispatcher<OFType, IOFMessageListener> ldd =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001507 entry.getValue();
Ray Milkey269ffb92014-04-03 14:43:30 -07001508
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001509 StringBuffer sb = new StringBuffer();
1510 sb.append("OFListeners for ");
1511 sb.append(type);
1512 sb.append(": ");
1513 for (IOFMessageListener l : ldd.getOrderedListeners()) {
1514 sb.append(l.getName());
1515 sb.append(",");
1516 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001517 log.debug(sb.toString());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001518 }
1519 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001520
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001521 public void removeOFMessageListeners(OFType type) {
1522 messageListeners.remove(type);
1523 }
1524
1525 @Override
1526 public Map<Long, IOFSwitch> getSwitches() {
1527 return Collections.unmodifiableMap(this.activeSwitches);
1528 }
1529
1530 @Override
1531 public void addOFSwitchListener(IOFSwitchListener listener) {
1532 this.switchListeners.add(listener);
1533 }
1534
1535 @Override
1536 public void removeOFSwitchListener(IOFSwitchListener listener) {
1537 this.switchListeners.remove(listener);
1538 }
1539
1540 @Override
1541 public Map<OFType, List<IOFMessageListener>> getListeners() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001542 Map<OFType, List<IOFMessageListener>> lers =
1543 new HashMap<OFType, List<IOFMessageListener>>();
1544 for (Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
1545 messageListeners.entrySet()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001546 lers.put(e.getKey(), e.getValue().getOrderedListeners());
1547 }
1548 return Collections.unmodifiableMap(lers);
1549 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001550
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001551 @Override
Pavlin Radoslavov695f8952014-07-23 16:57:01 -07001552 public void addLocalSwitchMastershipListener(
1553 ILocalSwitchMastershipListener listener) {
1554 this.localSwitchMastershipListeners.addIfAbsent(listener);
1555 }
1556
1557 @Override
1558 public void removeLocalSwitchMastershipListener(
1559 ILocalSwitchMastershipListener listener) {
1560 this.localSwitchMastershipListeners.remove(listener);
1561 }
1562
1563 @Override
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001564 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001565 @LogMessageDoc(message = "Failed to inject OFMessage {message} onto " +
1566 "a null switch",
1567 explanation = "Failed to process a message because the switch " +
1568 " is no longer connected."),
1569 @LogMessageDoc(level = "ERROR",
1570 message = "Error reinjecting OFMessage on switch {switch}",
1571 explanation = "An I/O error occured while attempting to " +
1572 "process an OpenFlow message",
1573 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001574 })
1575 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
1576 FloodlightContext bc) {
1577 if (sw == null) {
1578 log.info("Failed to inject OFMessage {} onto a null switch", msg);
1579 return false;
1580 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001581
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001582 // FIXME: Do we need to be able to inject messages to switches
1583 // where we're the slave controller (i.e. they're connected but
1584 // not active)?
1585 // FIXME: Don't we need synchronization logic here so we're holding
1586 // the listener read lock when we call handleMessage? After some
1587 // discussions it sounds like the right thing to do here would be to
1588 // inject the message as a netty upstream channel event so it goes
1589 // through the normal netty event processing, including being
1590 // handled
1591 if (!activeSwitches.containsKey(sw.getId())) return false;
Ray Milkey269ffb92014-04-03 14:43:30 -07001592
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001593 try {
1594 // Pass Floodlight context to the handleMessages()
1595 handleMessage(sw, msg, bc);
1596 } catch (IOException e) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001597 log.error("Error reinjecting OFMessage on switch {}",
1598 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001599 return false;
1600 }
1601 return true;
1602 }
1603
1604 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001605 @LogMessageDoc(message = "Calling System.exit",
1606 explanation = "The controller is terminating")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001607 public synchronized void terminate() {
1608 log.info("Calling System.exit");
1609 System.exit(1);
1610 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001611
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001612 @Override
1613 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
1614 // call the overloaded version with floodlight context set to null
1615 return injectOfMessage(sw, msg, null);
1616 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001617
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001618 @Override
1619 public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
1620 FloodlightContext bc) {
1621 if (log.isTraceEnabled()) {
1622 String str = OFMessage.getDataAsString(sw, m, bc);
1623 log.trace("{}", str);
1624 }
1625
1626 List<IOFMessageListener> listeners = null;
1627 if (messageListeners.containsKey(m.getType())) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001628 listeners =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001629 messageListeners.get(m.getType()).getOrderedListeners();
1630 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001631
1632 if (listeners != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001633 for (IOFMessageListener listener : listeners) {
1634 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001635 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001636 continue;
1637 }
1638 }
1639 if (Command.STOP.equals(listener.receive(sw, m, bc))) {
1640 break;
1641 }
1642 }
1643 }
1644 }
1645
1646 @Override
1647 public BasicFactory getOFMessageFactory() {
1648 return factory;
1649 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001650
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001651 @Override
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001652 public OnosInstanceId getOnosInstanceId() {
1653 return onosInstanceId;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001654 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001655
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001656 // **************
1657 // Initialization
1658 // **************
1659
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001660 /**
1661 * Sets the initial role based on properties in the config params.
1662 * It looks for two different properties.
1663 * If the "role" property is specified then the value should be
1664 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1665 * controller is set to the specified value. If the "role" property
1666 * is not specified then it looks next for the "role.path" property.
1667 * In this case the value should be the path to a property file in
1668 * the file system that contains a property called "floodlight.role"
1669 * which can be one of the values listed above for the "role" property.
1670 * The idea behind the "role.path" mechanism is that you have some
1671 * separate heartbeat and master controller election algorithm that
1672 * determines the role of the controller. When a role transition happens,
1673 * it updates the current role in the file specified by the "role.path"
1674 * file. Then if floodlight restarts for some reason it can get the
1675 * correct current role of the controller from the file.
Ray Milkey269ffb92014-04-03 14:43:30 -07001676 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001677 * @param configParams The config params for the FloodlightProvider service
1678 * @return A valid role if role information is specified in the
Ray Milkey269ffb92014-04-03 14:43:30 -07001679 * config params, otherwise null
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001680 */
1681 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001682 @LogMessageDoc(message = "Controller role set to {role}",
1683 explanation = "Setting the initial HA role to "),
1684 @LogMessageDoc(level = "ERROR",
1685 message = "Invalid current role value: {role}",
1686 explanation = "An invalid HA role value was read from the " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001687 "properties file",
Ray Milkey269ffb92014-04-03 14:43:30 -07001688 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001689 })
1690 protected Role getInitialRole(Map<String, String> configParams) {
1691 Role role = null;
1692 String roleString = configParams.get("role");
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001693 FileInputStream fs = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001694 if (roleString == null) {
1695 String rolePath = configParams.get("rolepath");
1696 if (rolePath != null) {
1697 Properties properties = new Properties();
1698 try {
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001699 fs = new FileInputStream(rolePath);
1700 properties.load(fs);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001701 roleString = properties.getProperty("floodlight.role");
Ray Milkey269ffb92014-04-03 14:43:30 -07001702 } catch (IOException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001703 // Don't treat it as an error if the file specified by the
1704 // rolepath property doesn't exist. This lets us enable the
1705 // HA mechanism by just creating/setting the floodlight.role
1706 // property in that file without having to modify the
1707 // floodlight properties.
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001708 } finally {
1709 if (fs != null) {
1710 try {
1711 fs.close();
1712 } catch (IOException e) {
1713 log.error("Exception while closing resource ", e);
1714 }
1715 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001716 }
1717 }
1718 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001719
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001720 if (roleString != null) {
1721 // Canonicalize the string to the form used for the enum constants
1722 roleString = roleString.trim().toUpperCase();
1723 try {
1724 role = Role.valueOf(roleString);
Ray Milkey269ffb92014-04-03 14:43:30 -07001725 } catch (IllegalArgumentException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001726 log.error("Invalid current role value: {}", roleString);
1727 }
1728 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001729
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001730 log.info("Controller role set to {}", role);
Ray Milkey269ffb92014-04-03 14:43:30 -07001731
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001732 return role;
1733 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001734
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001735 /**
1736 * Tell controller that we're ready to accept switches loop
Ray Milkey269ffb92014-04-03 14:43:30 -07001737 *
1738 * @throws IOException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001739 */
1740 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001741 @LogMessageDoc(message = "Listening for switch connections on {address}",
1742 explanation = "The controller is ready and listening for new" +
1743 " switch connections"),
1744 @LogMessageDoc(message = "Storage exception in controller " +
1745 "updates loop; terminating process",
1746 explanation = ERROR_DATABASE,
1747 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1748 @LogMessageDoc(level = "ERROR",
1749 message = "Exception in controller updates loop",
1750 explanation = "Failed to dispatch controller event",
1751 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001752 })
1753 public void run() {
1754 if (log.isDebugEnabled()) {
1755 logListeners();
1756 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001757
1758 try {
1759 final ServerBootstrap bootstrap = createServerBootStrap();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001760
1761 bootstrap.setOption("reuseAddr", true);
1762 bootstrap.setOption("child.keepAlive", true);
1763 bootstrap.setOption("child.tcpNoDelay", true);
1764 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1765
Ray Milkey269ffb92014-04-03 14:43:30 -07001766 ChannelPipelineFactory pfact =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001767 new OpenflowPipelineFactory(this, null);
1768 bootstrap.setPipelineFactory(pfact);
1769 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1770 final ChannelGroup cg = new DefaultChannelGroup();
1771 cg.add(bootstrap.bind(sa));
Ray Milkey269ffb92014-04-03 14:43:30 -07001772
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001773 log.info("Listening for switch connections on {}", sa);
1774 } catch (Exception e) {
1775 throw new RuntimeException(e);
1776 }
1777
1778 // main loop
1779 while (true) {
1780 try {
1781 IUpdate update = updates.take();
1782 update.dispatch();
1783 } catch (InterruptedException e) {
1784 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001785 } catch (Exception e) {
1786 log.error("Exception in controller updates loop", e);
1787 }
1788 }
1789 }
1790
1791 private ServerBootstrap createServerBootStrap() {
1792 if (workerThreads == 0) {
1793 return new ServerBootstrap(
1794 new NioServerSocketChannelFactory(
1795 Executors.newCachedThreadPool(),
1796 Executors.newCachedThreadPool()));
1797 } else {
1798 return new ServerBootstrap(
1799 new NioServerSocketChannelFactory(
1800 Executors.newCachedThreadPool(),
1801 Executors.newCachedThreadPool(), workerThreads));
1802 }
1803 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001804
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001805 public void setConfigParams(Map<String, String> configParams) {
1806 String ofPort = configParams.get("openflowport");
1807 if (ofPort != null) {
1808 this.openFlowPort = Integer.parseInt(ofPort);
1809 }
1810 log.debug("OpenFlow port set to {}", this.openFlowPort);
1811 String threads = configParams.get("workerthreads");
1812 if (threads != null) {
1813 this.workerThreads = Integer.parseInt(threads);
1814 }
1815 log.debug("Number of worker threads set to {}", this.workerThreads);
1816 String controllerId = configParams.get("controllerid");
1817 if (controllerId != null) {
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001818 this.onosInstanceId = new OnosInstanceId(controllerId);
Ray Milkey269ffb92014-04-03 14:43:30 -07001819 } else {
1820 //Try to get the hostname of the machine and use that for controller ID
1821 try {
1822 String hostname = java.net.InetAddress.getLocalHost().getHostName();
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001823 this.onosInstanceId = new OnosInstanceId(hostname);
Ray Milkey269ffb92014-04-03 14:43:30 -07001824 } catch (UnknownHostException e) {
1825 // Can't get hostname, we'll just use the default
1826 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001827 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001828
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001829 log.debug("ControllerId set to {}", this.onosInstanceId);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001830 }
1831
1832 private void initVendorMessages() {
1833 // Configure openflowj to be able to parse the role request/reply
1834 // vendor messages.
1835 OFBasicVendorId niciraVendorId = new OFBasicVendorId(
1836 OFNiciraVendorData.NX_VENDOR_ID, 4);
1837 OFVendorId.registerVendorId(niciraVendorId);
1838 OFBasicVendorDataType roleRequestVendorData =
1839 new OFBasicVendorDataType(
1840 OFRoleRequestVendorData.NXT_ROLE_REQUEST,
1841 OFRoleRequestVendorData.getInstantiable());
1842 niciraVendorId.registerVendorDataType(roleRequestVendorData);
1843 OFBasicVendorDataType roleReplyVendorData =
1844 new OFBasicVendorDataType(
1845 OFRoleReplyVendorData.NXT_ROLE_REPLY,
1846 OFRoleReplyVendorData.getInstantiable());
Ray Milkey269ffb92014-04-03 14:43:30 -07001847 niciraVendorId.registerVendorDataType(roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001848 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001849
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001850 /**
1851 * Initialize internal data structures
1852 */
1853 public void init(Map<String, String> configParams) {
1854 // These data structures are initialized here because other
1855 // module's startUp() might be called before ours
1856 this.messageListeners =
Ray Milkey269ffb92014-04-03 14:43:30 -07001857 new ConcurrentHashMap<OFType,
1858 ListenerDispatcher<OFType,
1859 IOFMessageListener>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001860 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001861 this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1862 this.connectedSwitches = new HashSet<OFSwitchImpl>();
1863 this.controllerNodeIPsCache = new HashMap<String, String>();
1864 this.updates = new LinkedBlockingQueue<IUpdate>();
1865 this.factory = new BasicFactory();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001866 setConfigParams(configParams);
Jonathan Hartcc957a02013-02-26 10:39:04 -08001867 //Set the controller's role to MASTER so it always tries to do role requests.
1868 this.role = Role.MASTER;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001869 this.roleChanger = new RoleChanger();
1870 initVendorMessages();
1871 this.systemStartTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001872 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001873
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001874 /**
1875 * Startup all of the controller's components
1876 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001877 @LogMessageDoc(message = "Waiting for storage source",
1878 explanation = "The system database is not yet ready",
1879 recommendation = "If this message persists, this indicates " +
1880 "that the system database has failed to start. " +
1881 LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001882 public void startupComponents() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001883 try {
Pavlin Radoslavov53b208a2014-07-28 13:16:11 -07001884 registryService.registerController(onosInstanceId.toString());
Ray Milkey269ffb92014-04-03 14:43:30 -07001885 } catch (RegistryException e) {
1886 log.warn("Registry service error: {}", e.getMessage());
1887 }
1888
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001889 // Add our REST API
1890 restApi.addRestletRoutable(new CoreWebRoutable());
1891 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001892
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001893 @Override
1894 public Map<String, String> getControllerNodeIPs() {
1895 // We return a copy of the mapping so we can guarantee that
1896 // the mapping return is the same as one that will be (or was)
1897 // dispatched to IHAListeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001898 HashMap<String, String> retval = new HashMap<String, String>();
1899 synchronized (controllerNodeIPsCache) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001900 retval.putAll(controllerNodeIPsCache);
1901 }
1902 return retval;
1903 }
1904
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001905 @Override
1906 public long getSystemStartTime() {
1907 return (this.systemStartTime);
1908 }
1909
1910 @Override
1911 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
1912 this.alwaysClearFlowsOnSwAdd = value;
1913 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001914
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001915 public boolean getAlwaysClearFlowsOnSwAdd() {
1916 return this.alwaysClearFlowsOnSwAdd;
1917 }
1918}