blob: ae362a2037d0d724e179a874f56e4ec5650e09e9 [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;
39import java.util.concurrent.CopyOnWriteArraySet;
40import java.util.concurrent.Executors;
41import java.util.concurrent.Future;
42import java.util.concurrent.LinkedBlockingQueue;
43import java.util.concurrent.RejectedExecutionException;
44import java.util.concurrent.TimeUnit;
45import java.util.concurrent.TimeoutException;
46
47import net.floodlightcontroller.core.FloodlightContext;
48import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080049import net.floodlightcontroller.core.IListener.Command;
Jonathan Hartd10008d2013-02-23 17:04:08 -080050import net.floodlightcontroller.core.IOFMessageListener;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080051import net.floodlightcontroller.core.IOFSwitch;
52import net.floodlightcontroller.core.IOFSwitchFilter;
53import net.floodlightcontroller.core.IOFSwitchListener;
Pankaj Berdedc73bb12013-08-14 13:46:38 -070054import net.floodlightcontroller.core.IUpdate;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080055import net.floodlightcontroller.core.annotations.LogMessageDoc;
56import net.floodlightcontroller.core.annotations.LogMessageDocs;
57import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState;
58import net.floodlightcontroller.core.util.ListenerDispatcher;
59import net.floodlightcontroller.core.web.CoreWebRoutable;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080060import net.floodlightcontroller.restserver.IRestApiService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080061import net.floodlightcontroller.threadpool.IThreadPoolService;
Jonathan Hart23701d12014-04-03 10:45:48 -070062import net.onrc.onos.core.linkdiscovery.ILinkDiscoveryService;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070063import net.onrc.onos.core.main.IOFSwitchPortListener;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070064import net.onrc.onos.core.packet.Ethernet;
65import net.onrc.onos.core.registry.IControllerRegistryService;
Jonathan Hartdeda0ba2014-04-03 11:14:12 -070066import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
Jonathan Harta99ec672014-04-03 11:30:34 -070067import net.onrc.onos.core.registry.RegistryException;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080068
69import org.jboss.netty.bootstrap.ServerBootstrap;
70import org.jboss.netty.buffer.ChannelBuffer;
71import org.jboss.netty.buffer.ChannelBuffers;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080072import org.jboss.netty.channel.ChannelHandlerContext;
73import org.jboss.netty.channel.ChannelPipelineFactory;
74import org.jboss.netty.channel.ChannelStateEvent;
75import org.jboss.netty.channel.ChannelUpstreamHandler;
76import org.jboss.netty.channel.Channels;
77import org.jboss.netty.channel.ExceptionEvent;
78import org.jboss.netty.channel.MessageEvent;
79import org.jboss.netty.channel.group.ChannelGroup;
80import org.jboss.netty.channel.group.DefaultChannelGroup;
81import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
82import org.jboss.netty.handler.timeout.IdleStateAwareChannelUpstreamHandler;
83import org.jboss.netty.handler.timeout.IdleStateEvent;
84import org.jboss.netty.handler.timeout.ReadTimeoutException;
85import org.openflow.protocol.OFEchoReply;
86import org.openflow.protocol.OFError;
87import org.openflow.protocol.OFError.OFBadActionCode;
88import org.openflow.protocol.OFError.OFBadRequestCode;
89import org.openflow.protocol.OFError.OFErrorType;
90import org.openflow.protocol.OFError.OFFlowModFailedCode;
91import org.openflow.protocol.OFError.OFHelloFailedCode;
92import org.openflow.protocol.OFError.OFPortModFailedCode;
93import org.openflow.protocol.OFError.OFQueueOpFailedCode;
94import org.openflow.protocol.OFFeaturesReply;
95import org.openflow.protocol.OFGetConfigReply;
96import org.openflow.protocol.OFMessage;
97import org.openflow.protocol.OFPacketIn;
98import org.openflow.protocol.OFPhysicalPort;
Pankaj Berde6a4075d2013-01-22 16:42:54 -080099import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
Pankaj Berde6debb042013-01-16 18:04:32 -0800100import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800101import org.openflow.protocol.OFPortStatus;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800102import org.openflow.protocol.OFPortStatus.OFPortReason;
103import org.openflow.protocol.OFSetConfig;
104import org.openflow.protocol.OFStatisticsRequest;
105import org.openflow.protocol.OFSwitchConfig;
106import org.openflow.protocol.OFType;
107import org.openflow.protocol.OFVendor;
108import org.openflow.protocol.factory.BasicFactory;
109import org.openflow.protocol.factory.MessageParseException;
110import org.openflow.protocol.statistics.OFDescriptionStatistics;
111import org.openflow.protocol.statistics.OFStatistics;
112import org.openflow.protocol.statistics.OFStatisticsType;
113import org.openflow.protocol.vendor.OFBasicVendorDataType;
114import org.openflow.protocol.vendor.OFBasicVendorId;
115import org.openflow.protocol.vendor.OFVendorId;
116import org.openflow.util.HexString;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800117import org.openflow.vendor.nicira.OFNiciraVendorData;
118import org.openflow.vendor.nicira.OFRoleReplyVendorData;
119import org.openflow.vendor.nicira.OFRoleRequestVendorData;
120import org.openflow.vendor.nicira.OFRoleVendorData;
121import org.slf4j.Logger;
122import org.slf4j.LoggerFactory;
123
124
125/**
126 * The main controller class. Handles all setup and network listeners
Ray Milkey269ffb92014-04-03 14:43:30 -0700127 * <p/>
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700128 * Extensions made by ONOS are:
Ray Milkey269ffb92014-04-03 14:43:30 -0700129 * - Detailed Port event: PORTCHANGED -> {PORTCHANGED, PORTADDED, PORTREMOVED}
130 * Available as net.onrc.onos.core.main.IOFSwitchPortListener
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700131 * - Distributed ownership control of switch through RegistryService(IControllerRegistryService)
Pavlin Radoslavova653e9f2013-10-16 03:08:52 -0700132 * - Register ONOS services. (IControllerRegistryService)
HIGUCHI Yuta11360702013-06-17 10:28:06 -0700133 * - Additional DEBUG logs
134 * - Try using hostname as controller ID, when ID was not explicitly given.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800135 */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800136public class Controller implements IFloodlightProviderService {
Ray Milkey269ffb92014-04-03 14:43:30 -0700137
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -0700138 protected final static Logger log = LoggerFactory.getLogger(Controller.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800139
Ray Milkey269ffb92014-04-03 14:43:30 -0700140 private static final String ERROR_DATABASE =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800141 "The controller could not communicate with the system database.";
Ray Milkey269ffb92014-04-03 14:43:30 -0700142
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800143 protected BasicFactory factory;
144 protected ConcurrentMap<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -0700145 ListenerDispatcher<OFType, IOFMessageListener>>
146 messageListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800147 // The activeSwitches map contains only those switches that are actively
148 // being controlled by us -- it doesn't contain switches that are
149 // in the slave role
150 protected ConcurrentHashMap<Long, IOFSwitch> activeSwitches;
151 // connectedSwitches contains all connected switches, including ones where
152 // we're a slave controller. We need to keep track of them so that we can
153 // send role request messages to switches when our role changes to master
154 // We add a switch to this set after it successfully completes the
155 // handshake. Access to this Set needs to be synchronized with roleChanger
156 protected HashSet<OFSwitchImpl> connectedSwitches;
Ray Milkey269ffb92014-04-03 14:43:30 -0700157
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800158 // The controllerNodeIPsCache maps Controller IDs to their IP address.
159 // It's only used by handleControllerNodeIPsChanged
160 protected HashMap<String, String> controllerNodeIPsCache;
Ray Milkey269ffb92014-04-03 14:43:30 -0700161
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800162 protected Set<IOFSwitchListener> switchListeners;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800163 protected BlockingQueue<IUpdate> updates;
Ray Milkey269ffb92014-04-03 14:43:30 -0700164
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800165 // Module dependencies
166 protected IRestApiService restApi;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800167 protected IThreadPoolService threadPool;
Jonathan Hartd10008d2013-02-23 17:04:08 -0800168 protected IControllerRegistryService registryService;
Ray Milkey269ffb92014-04-03 14:43:30 -0700169
Jonathan Hart8a5d0972013-12-04 10:02:44 -0800170 protected ILinkDiscoveryService linkDiscovery;
Ray Milkey269ffb92014-04-03 14:43:30 -0700171
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800172 // Configuration options
173 protected int openFlowPort = 6633;
174 protected int workerThreads = 0;
175 // The id for this controller node. Should be unique for each controller
176 // node in a controller cluster.
177 protected String controllerId = "localhost";
Ray Milkey269ffb92014-04-03 14:43:30 -0700178
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800179 // The current role of the controller.
180 // If the controller isn't configured to support roles, then this is null.
181 protected Role role;
182 // A helper that handles sending and timeout handling for role requests
183 protected RoleChanger roleChanger;
Ray Milkey269ffb92014-04-03 14:43:30 -0700184
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800185 // Start time of the controller
186 protected long systemStartTime;
Ray Milkey269ffb92014-04-03 14:43:30 -0700187
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800188 // Flag to always flush flow table on switch reconnect (HA or otherwise)
189 protected boolean alwaysClearFlowsOnSwAdd = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700190
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800191 // Perf. related configuration
192 protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
193 protected static final int BATCH_MAX_SIZE = 100;
Ray Milkey269ffb92014-04-03 14:43:30 -0700194 protected static final boolean ALWAYS_DECODE_ETH = true;
195
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800196 public enum SwitchUpdateType {
197 ADDED,
198 REMOVED,
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700199 PORTCHANGED,
200 PORTADDED,
201 PORTREMOVED
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800202 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700203
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800204 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700205 * Update message indicating a switch was added or removed
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700206 * ONOS: This message extended to indicate Port add or removed event.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800207 */
208 protected class SwitchUpdate implements IUpdate {
209 public IOFSwitch sw;
HIGUCHI Yutaec4bff82013-06-17 11:49:31 -0700210 public OFPhysicalPort port; // Added by ONOS
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800211 public SwitchUpdateType switchUpdateType;
Ray Milkey269ffb92014-04-03 14:43:30 -0700212
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800213 public SwitchUpdate(IOFSwitch sw, SwitchUpdateType switchUpdateType) {
214 this.sw = sw;
215 this.switchUpdateType = switchUpdateType;
216 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700217
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700218 public SwitchUpdate(IOFSwitch sw, OFPhysicalPort port, SwitchUpdateType switchUpdateType) {
219 this.sw = sw;
220 this.port = port;
221 this.switchUpdateType = switchUpdateType;
222 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700223
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800224 public void dispatch() {
225 if (log.isTraceEnabled()) {
226 log.trace("Dispatching switch update {} {}",
227 sw, switchUpdateType);
228 }
229 if (switchListeners != null) {
230 for (IOFSwitchListener listener : switchListeners) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700231 switch (switchUpdateType) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800232 case ADDED:
233 listener.addedSwitch(sw);
234 break;
235 case REMOVED:
236 listener.removedSwitch(sw);
237 break;
238 case PORTCHANGED:
239 listener.switchPortChanged(sw.getId());
240 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700241 case PORTADDED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700242 if (listener instanceof IOFSwitchPortListener) {
243 ((IOFSwitchPortListener) listener).switchPortAdded(sw.getId(), port);
244 }
245 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700246 case PORTREMOVED:
Ray Milkey269ffb92014-04-03 14:43:30 -0700247 if (listener instanceof IOFSwitchPortListener) {
248 ((IOFSwitchPortListener) listener).switchPortRemoved(sw.getId(), port);
249 }
250 break;
Pankaj Berde465ac7c2013-05-23 13:47:49 -0700251 default:
Ray Milkey269ffb92014-04-03 14:43:30 -0700252 break;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800253 }
254 }
255 }
256 }
257 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700258
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800259 // ***************
260 // Getters/Setters
Jonathan Hart20fab1a2013-12-12 11:06:50 -0800261 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -0700262
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800263 public void setRestApiService(IRestApiService restApi) {
264 this.restApi = restApi;
265 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700266
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800267 public void setThreadPoolService(IThreadPoolService tp) {
268 this.threadPool = tp;
269 }
270
Ray Milkey269ffb92014-04-03 14:43:30 -0700271 public void setMastershipService(IControllerRegistryService serviceImpl) {
272 this.registryService = serviceImpl;
Pankaj Berdedc73bb12013-08-14 13:46:38 -0700273 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700274
275 public void setLinkDiscoveryService(ILinkDiscoveryService linkDiscovery) {
276 this.linkDiscovery = linkDiscovery;
277 }
278
279 public void publishUpdate(IUpdate update) {
280 try {
281 this.updates.put(update);
282 } catch (InterruptedException e) {
283 log.error("Failure adding update to queue", e);
284 }
285 }
286
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800287 // **********************
288 // ChannelUpstreamHandler
289 // **********************
Ray Milkey269ffb92014-04-03 14:43:30 -0700290
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800291 /**
292 * Return a new channel handler for processing a switch connections
Ray Milkey269ffb92014-04-03 14:43:30 -0700293 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800294 * @param state The channel state object for the connection
295 * @return the new channel handler
296 */
297 protected ChannelUpstreamHandler getChannelHandler(OFChannelState state) {
298 return new OFChannelHandler(state);
299 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700300
Jonathan Hartcc957a02013-02-26 10:39:04 -0800301 protected class RoleChangeCallback implements ControlChangeCallback {
Ray Milkey269ffb92014-04-03 14:43:30 -0700302 @Override
303 public void controlChanged(long dpid, boolean hasControl) {
304 log.info("Role change callback for switch {}, hasControl {}",
305 HexString.toHexString(dpid), hasControl);
Pankaj Berde01939e92013-03-08 14:38:27 -0800306
Ray Milkey269ffb92014-04-03 14:43:30 -0700307 synchronized (roleChanger) {
308 OFSwitchImpl sw = null;
309 for (OFSwitchImpl connectedSw : connectedSwitches) {
310 if (connectedSw.getId() == dpid) {
311 sw = connectedSw;
312 break;
313 }
314 }
315 if (sw == null) {
316 log.warn("Switch {} not found in connected switches",
317 HexString.toHexString(dpid));
318 return;
319 }
Pankaj Berde01939e92013-03-08 14:38:27 -0800320
Ray Milkey269ffb92014-04-03 14:43:30 -0700321 Role role = null;
322
323 /*
324 * issue #229
325 * Cannot rely on sw.getRole() as it can be behind due to pending
326 * role changes in the queue. Just submit it and late the RoleChanger
327 * handle duplicates.
328 */
Pankaj Berde01939e92013-03-08 14:38:27 -0800329
Ray Milkey269ffb92014-04-03 14:43:30 -0700330 if (hasControl) {
331 role = Role.MASTER;
332 } else {
333 role = Role.SLAVE;
334 }
335
336 log.debug("Sending role request {} msg to {}", role, sw);
337 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
338 swList.add(sw);
339 roleChanger.submitRequest(swList, role);
340
341 }
342
343 }
Jonathan Hartcc957a02013-02-26 10:39:04 -0800344 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700345
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800346 /**
347 * Channel handler deals with the switch connection and dispatches
348 * switch messages to the appropriate locations.
Ray Milkey269ffb92014-04-03 14:43:30 -0700349 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800350 * @author readams
351 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700352 protected class OFChannelHandler
353 extends IdleStateAwareChannelUpstreamHandler {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800354 protected OFSwitchImpl sw;
355 protected OFChannelState state;
Ray Milkey269ffb92014-04-03 14:43:30 -0700356
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800357 public OFChannelHandler(OFChannelState state) {
358 this.state = state;
359 }
360
361 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700362 @LogMessageDoc(message = "New switch connection from {ip address}",
363 explanation = "A new switch has connected from the " +
364 "specified IP address")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800365 public void channelConnected(ChannelHandlerContext ctx,
366 ChannelStateEvent e) throws Exception {
367 log.info("New switch connection from {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700368 e.getChannel().getRemoteAddress());
369
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800370 sw = new OFSwitchImpl();
371 sw.setChannel(e.getChannel());
372 sw.setFloodlightProvider(Controller.this);
373 sw.setThreadPoolService(threadPool);
Ray Milkey269ffb92014-04-03 14:43:30 -0700374
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800375 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
376 msglist.add(factory.getMessage(OFType.HELLO));
377 e.getChannel().write(msglist);
378
379 }
380
381 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700382 @LogMessageDoc(message = "Disconnected switch {switch information}",
383 explanation = "The specified switch has disconnected.")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800384 public void channelDisconnected(ChannelHandlerContext ctx,
385 ChannelStateEvent e) throws Exception {
386 if (sw != null && state.hsState == HandshakeState.READY) {
387 if (activeSwitches.containsKey(sw.getId())) {
388 // It's safe to call removeSwitch even though the map might
389 // not contain this particular switch but another with the
390 // same DPID
391 removeSwitch(sw);
392 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700393 synchronized (roleChanger) {
394 if (controlRequested) {
395 registryService.releaseControl(sw.getId());
396 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800397 connectedSwitches.remove(sw);
398 }
399 sw.setConnected(false);
400 }
401 log.info("Disconnected switch {}", sw);
402 }
403
404 @Override
405 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700406 @LogMessageDoc(level = "ERROR",
407 message = "Disconnecting switch {switch} due to read timeout",
408 explanation = "The connected switch has failed to send any " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800409 "messages or respond to echo requests",
Ray Milkey269ffb92014-04-03 14:43:30 -0700410 recommendation = LogMessageDoc.CHECK_SWITCH),
411 @LogMessageDoc(level = "ERROR",
412 message = "Disconnecting switch {switch}: failed to " +
413 "complete handshake",
414 explanation = "The switch did not respond correctly " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800415 "to handshake messages",
Ray Milkey269ffb92014-04-03 14:43:30 -0700416 recommendation = LogMessageDoc.CHECK_SWITCH),
417 @LogMessageDoc(level = "ERROR",
418 message = "Disconnecting switch {switch} due to IO Error: {}",
419 explanation = "There was an error communicating with the switch",
420 recommendation = LogMessageDoc.CHECK_SWITCH),
421 @LogMessageDoc(level = "ERROR",
422 message = "Disconnecting switch {switch} due to switch " +
423 "state error: {error}",
424 explanation = "The switch sent an unexpected message",
425 recommendation = LogMessageDoc.CHECK_SWITCH),
426 @LogMessageDoc(level = "ERROR",
427 message = "Disconnecting switch {switch} due to " +
428 "message parse failure",
429 explanation = "Could not parse a message from the switch",
430 recommendation = LogMessageDoc.CHECK_SWITCH),
431 @LogMessageDoc(level = "ERROR",
432 message = "Could not process message: queue full",
433 explanation = "OpenFlow messages are arriving faster than " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800434 " the controller can process them.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700435 recommendation = LogMessageDoc.CHECK_CONTROLLER),
436 @LogMessageDoc(level = "ERROR",
437 message = "Error while processing message " +
438 "from switch {switch} {cause}",
439 explanation = "An error occurred processing the switch message",
440 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800441 })
442 public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
443 throws Exception {
444 if (e.getCause() instanceof ReadTimeoutException) {
445 // switch timeout
446 log.error("Disconnecting switch {} due to read timeout", sw);
447 ctx.getChannel().close();
448 } else if (e.getCause() instanceof HandshakeTimeoutException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700449 log.error("Disconnecting switch {}: failed to complete handshake",
450 sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800451 ctx.getChannel().close();
452 } else if (e.getCause() instanceof ClosedChannelException) {
453 //log.warn("Channel for sw {} already closed", sw);
454 } else if (e.getCause() instanceof IOException) {
455 log.error("Disconnecting switch {} due to IO Error: {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700456 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800457 ctx.getChannel().close();
458 } else if (e.getCause() instanceof SwitchStateException) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700459 log.error("Disconnecting switch {} due to switch state error: {}",
460 sw, e.getCause().getMessage());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800461 ctx.getChannel().close();
462 } else if (e.getCause() instanceof MessageParseException) {
463 log.error("Disconnecting switch " + sw +
Ray Milkey269ffb92014-04-03 14:43:30 -0700464 " due to message parse failure",
465 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800466 ctx.getChannel().close();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800467 } else if (e.getCause() instanceof RejectedExecutionException) {
468 log.warn("Could not process message: queue full");
469 } else {
470 log.error("Error while processing message from switch " + sw,
Ray Milkey269ffb92014-04-03 14:43:30 -0700471 e.getCause());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800472 ctx.getChannel().close();
473 }
474 }
475
476 @Override
477 public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
478 throws Exception {
479 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
480 msglist.add(factory.getMessage(OFType.ECHO_REQUEST));
481 e.getChannel().write(msglist);
482 }
483
484 @Override
485 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
486 throws Exception {
487 if (e.getMessage() instanceof List) {
488 @SuppressWarnings("unchecked")
Ray Milkey269ffb92014-04-03 14:43:30 -0700489 List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800490
491 for (OFMessage ofm : msglist) {
492 try {
493 processOFMessage(ofm);
Ray Milkey269ffb92014-04-03 14:43:30 -0700494 } catch (Exception ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800495 // We are the last handler in the stream, so run the
496 // exception through the channel again by passing in
497 // ctx.getChannel().
498 Channels.fireExceptionCaught(ctx.getChannel(), ex);
499 }
500 }
501
502 // Flush all flow-mods/packet-out generated from this "train"
503 OFSwitchImpl.flush_all();
504 }
505 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700506
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800507 /**
508 * Process the request for the switch description
509 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700510 @LogMessageDoc(level = "ERROR",
511 message = "Exception in reading description " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800512 " during handshake {exception}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700513 explanation = "Could not process the switch description string",
514 recommendation = LogMessageDoc.CHECK_SWITCH)
Ray Milkeyff735142014-05-22 19:06:02 -0700515 @SuppressWarnings("unchecked")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800516 void processSwitchDescReply() {
517 try {
518 // Read description, if it has been updated
Ray Milkeyff735142014-05-22 19:06:02 -0700519 Future<?> future =
520 (Future<?>) sw.
Ray Milkey269ffb92014-04-03 14:43:30 -0700521 getAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
Ray Milkeyff735142014-05-22 19:06:02 -0700522 Future<List<OFStatistics>> desc_future =
523 (Future<List<OFStatistics>>) future;
Ray Milkey269ffb92014-04-03 14:43:30 -0700524 List<OFStatistics> values =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800525 desc_future.get(0, TimeUnit.MILLISECONDS);
526 if (values != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700527 OFDescriptionStatistics description =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800528 new OFDescriptionStatistics();
Ray Milkey269ffb92014-04-03 14:43:30 -0700529 ChannelBuffer data =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800530 ChannelBuffers.buffer(description.getLength());
531 for (OFStatistics f : values) {
532 f.writeTo(data);
533 description.readFrom(data);
534 break; // SHOULD be a list of length 1
535 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700536 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_DATA,
537 description);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800538 sw.setSwitchProperties(description);
539 data = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800540 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700541
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800542 sw.removeAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE);
543 state.hasDescription = true;
544 checkSwitchReady();
Ray Milkey269ffb92014-04-03 14:43:30 -0700545 } catch (InterruptedException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800546 // Ignore
Ray Milkey269ffb92014-04-03 14:43:30 -0700547 } catch (TimeoutException ex) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800548 // Ignore
549 } catch (Exception ex) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700550 log.error("Exception in reading description " +
551 " during handshake", ex);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800552 }
553 }
554
555 /**
556 * Send initial switch setup information that we need before adding
557 * the switch
Ray Milkey269ffb92014-04-03 14:43:30 -0700558 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800559 * @throws IOException
560 */
561 void sendHelloConfiguration() throws IOException {
562 // Send initial Features Request
Ray Milkey269ffb92014-04-03 14:43:30 -0700563 log.debug("Sending FEATURES_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800564 sw.write(factory.getMessage(OFType.FEATURES_REQUEST), null);
565 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700566
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800567 /**
568 * Send the configuration requests we can only do after we have
569 * the features reply
Ray Milkey269ffb92014-04-03 14:43:30 -0700570 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800571 * @throws IOException
572 */
573 void sendFeatureReplyConfiguration() throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700574 log.debug("Sending CONFIG_REQUEST to {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800575 // Ensure we receive the full packet via PacketIn
576 OFSetConfig config = (OFSetConfig) factory
577 .getMessage(OFType.SET_CONFIG);
578 config.setMissSendLength((short) 0xffff)
Ray Milkey269ffb92014-04-03 14:43:30 -0700579 .setLengthU(OFSwitchConfig.MINIMUM_LENGTH);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800580 sw.write(config, null);
581 sw.write(factory.getMessage(OFType.GET_CONFIG_REQUEST),
582 null);
583
584 // Get Description to set switch-specific flags
585 OFStatisticsRequest req = new OFStatisticsRequest();
586 req.setStatisticType(OFStatisticsType.DESC);
587 req.setLengthU(req.getLengthU());
Ray Milkey269ffb92014-04-03 14:43:30 -0700588 Future<List<OFStatistics>> dfuture =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800589 sw.getStatistics(req);
590 sw.setAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE,
591 dfuture);
592
593 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700594
595 volatile Boolean controlRequested = Boolean.FALSE;
596
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800597 protected void checkSwitchReady() {
598 if (state.hsState == HandshakeState.FEATURES_REPLY &&
599 state.hasDescription && state.hasGetConfigReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700600
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800601 state.hsState = HandshakeState.READY;
Jonathan Hart9e92c512013-03-20 16:24:44 -0700602 log.debug("Handshake with {} complete", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700603
604 synchronized (roleChanger) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800605 // We need to keep track of all of the switches that are connected
Ray Milkeyff735142014-05-22 19:06:02 -0700606 // to the controller, in any role, so that we can later send the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800607 // role request messages when the controller role changes.
Ray Milkeyff735142014-05-22 19:06:02 -0700608 // We need to be synchronized while doing this: we must not
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800609 // send a another role request to the connectedSwitches until
Ray Milkeyff735142014-05-22 19:06:02 -0700610 // we were able to add this new switch to connectedSwitches
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800611 // *and* send the current role to the new switch.
612 connectedSwitches.add(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700613
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800614 if (role != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700615 //Put the switch in SLAVE mode until we know we have control
616 log.debug("Setting new switch {} to SLAVE", sw.getStringId());
617 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
618 swList.add(sw);
619 roleChanger.submitRequest(swList, Role.SLAVE);
620
621 //Request control of the switch from the global registry
622 try {
623 controlRequested = Boolean.TRUE;
624 registryService.requestControl(sw.getId(),
625 new RoleChangeCallback());
626 } catch (RegistryException e) {
627 log.debug("Registry error: {}", e.getMessage());
628 controlRequested = Boolean.FALSE;
629 }
630
631
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800632 // Send a role request if role support is enabled for the controller
633 // This is a probe that we'll use to determine if the switch
634 // actually supports the role request message. If it does we'll
635 // get back a role reply message. If it doesn't we'll get back an
Ray Milkeyff735142014-05-22 19:06:02 -0700636 // OFError message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800637 // If role is MASTER we will promote switch to active
638 // list when we receive the switch's role reply messages
Jonathan Hartcc957a02013-02-26 10:39:04 -0800639 /*
Ray Milkeyff735142014-05-22 19:06:02 -0700640 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800641 "sending initial role request msg to {}",
642 role, sw);
643 Collection<OFSwitchImpl> swList = new ArrayList<OFSwitchImpl>(1);
644 swList.add(sw);
645 roleChanger.submitRequest(swList, role);
Jonathan Hartcc957a02013-02-26 10:39:04 -0800646 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700647 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800648 // Role supported not enabled on controller (for now)
Ray Milkeyff735142014-05-22 19:06:02 -0700649 // automatically promote switch to active state.
Ray Milkey269ffb92014-04-03 14:43:30 -0700650 log.debug("This controller's role is {}, " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800651 "not sending role request msg to {}",
652 role, sw);
653 // Need to clear FlowMods before we add the switch
654 // and dispatch updates otherwise we have a race condition.
655 sw.clearAllFlowMods();
656 addSwitch(sw);
657 state.firstRoleReplyReceived = true;
658 }
659 }
Pankaj Berde99fcee12013-03-18 09:41:53 -0700660 if (!controlRequested) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700661 // yield to allow other thread(s) to release control
662 try {
663 Thread.sleep(10);
664 } catch (InterruptedException e) {
665 // Ignore interruptions
666 }
667 // safer to bounce the switch to reconnect here than proceeding further
668 log.debug("Closing {} because we weren't able to request control " +
669 "successfully" + sw);
670 sw.channel.close();
Pankaj Berde99fcee12013-03-18 09:41:53 -0700671 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800672 }
673 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700674
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800675 /* Handle a role reply message we received from the switch. Since
Ray Milkeyff735142014-05-22 19:06:02 -0700676 * netty serializes message dispatch we don't need to synchronize
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800677 * against other receive operations from the same switch, so no need
678 * to synchronize addSwitch(), removeSwitch() operations from the same
Ray Milkeyff735142014-05-22 19:06:02 -0700679 * connection.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800680 * FIXME: However, when a switch with the same DPID connects we do
681 * need some synchronization. However, handling switches with same
682 * DPID needs to be revisited anyways (get rid of r/w-lock and synchronous
683 * removedSwitch notification):1
Ray Milkeyff735142014-05-22 19:06:02 -0700684 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800685 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700686 @LogMessageDoc(level = "ERROR",
687 message = "Invalid role value in role reply message",
688 explanation = "Was unable to set the HA role (master or slave) " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800689 "for the controller.",
Ray Milkey269ffb92014-04-03 14:43:30 -0700690 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800691 protected void handleRoleReplyMessage(OFVendor vendorMessage,
Ray Milkey269ffb92014-04-03 14:43:30 -0700692 OFRoleReplyVendorData roleReplyVendorData) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800693 // Map from the role code in the message to our role enum
694 int nxRole = roleReplyVendorData.getRole();
695 Role role = null;
696 switch (nxRole) {
697 case OFRoleVendorData.NX_ROLE_OTHER:
698 role = Role.EQUAL;
699 break;
700 case OFRoleVendorData.NX_ROLE_MASTER:
701 role = Role.MASTER;
702 break;
703 case OFRoleVendorData.NX_ROLE_SLAVE:
704 role = Role.SLAVE;
705 break;
706 default:
707 log.error("Invalid role value in role reply message");
708 sw.getChannel().close();
709 return;
710 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700711
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800712 log.debug("Handling role reply for role {} from {}. " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700713 "Controller's role is {} ",
714 new Object[]{role, sw, Controller.this.role}
715 );
716
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800717 sw.deliverRoleReply(vendorMessage.getXid(), role);
Ray Milkey269ffb92014-04-03 14:43:30 -0700718
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800719 boolean isActive = activeSwitches.containsKey(sw.getId());
720 if (!isActive && sw.isActive()) {
721 // Transition from SLAVE to MASTER.
Ray Milkey269ffb92014-04-03 14:43:30 -0700722
723 if (!state.firstRoleReplyReceived ||
724 getAlwaysClearFlowsOnSwAdd()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800725 // This is the first role-reply message we receive from
726 // this switch or roles were disabled when the switch
Ray Milkeyff735142014-05-22 19:06:02 -0700727 // connected:
728 // Delete all pre-existing flows for new connections to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800729 // the master
730 //
Ray Milkeyff735142014-05-22 19:06:02 -0700731 // FIXME: Need to think more about what the test should
732 // be for when we flush the flow-table? For example,
733 // if all the controllers are temporarily in the backup
734 // role (e.g. right after a failure of the master
735 // controller) at the point the switch connects, then
736 // all of the controllers will initially connect as
737 // backup controllers and not flush the flow-table.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800738 // Then when one of them is promoted to master following
739 // the master controller election the flow-table
Ray Milkeyff735142014-05-22 19:06:02 -0700740 // will still not be flushed because that's treated as
741 // a failover event where we don't want to flush the
742 // flow-table. The end result would be that the flow
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800743 // table for a newly connected switch is never
744 // flushed. Not sure how to handle that case though...
745 sw.clearAllFlowMods();
746 log.debug("First role reply from master switch {}, " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700747 "clear FlowTable to active switch list",
748 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800749 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700750
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800751 // Some switches don't seem to update us with port
752 // status messages while in slave role.
Ray Milkeyff735142014-05-22 19:06:02 -0700753 //readSwitchPortStateFromStorage(sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700754
Ray Milkeyff735142014-05-22 19:06:02 -0700755 // Only add the switch to the active switch list if
756 // we're not in the slave role. Note that if the role
757 // attribute is null, then that means that the switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800758 // doesn't support the role request messages, so in that
Ray Milkeyff735142014-05-22 19:06:02 -0700759 // case we're effectively in the EQUAL role and the
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800760 // switch should be included in the active switch list.
761 addSwitch(sw);
762 log.debug("Added master switch {} to active switch list",
Ray Milkey269ffb92014-04-03 14:43:30 -0700763 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800764
Ray Milkey269ffb92014-04-03 14:43:30 -0700765 } else if (isActive && !sw.isActive()) {
Ray Milkeyff735142014-05-22 19:06:02 -0700766 // Transition from MASTER to SLAVE: remove switch
767 // from active switch list.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800768 log.debug("Removed slave switch {} from active switch" +
Ray Milkey269ffb92014-04-03 14:43:30 -0700769 " list", HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800770 removeSwitch(sw);
771 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700772
Ray Milkeyff735142014-05-22 19:06:02 -0700773 // Indicate that we have received a role reply message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800774 state.firstRoleReplyReceived = true;
775 }
776
777 protected boolean handleVendorMessage(OFVendor vendorMessage) {
778 boolean shouldHandleMessage = false;
779 int vendor = vendorMessage.getVendor();
780 switch (vendor) {
781 case OFNiciraVendorData.NX_VENDOR_ID:
782 OFNiciraVendorData niciraVendorData =
Ray Milkey269ffb92014-04-03 14:43:30 -0700783 (OFNiciraVendorData) vendorMessage.getVendorData();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800784 int dataType = niciraVendorData.getDataType();
785 switch (dataType) {
786 case OFRoleReplyVendorData.NXT_ROLE_REPLY:
787 OFRoleReplyVendorData roleReplyVendorData =
788 (OFRoleReplyVendorData) niciraVendorData;
Ray Milkey269ffb92014-04-03 14:43:30 -0700789 handleRoleReplyMessage(vendorMessage,
790 roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800791 break;
792 default:
793 log.warn("Unhandled Nicira VENDOR message; " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700794 "data type = {}", dataType);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800795 break;
796 }
797 break;
798 default:
799 log.warn("Unhandled VENDOR message; vendor id = {}", vendor);
800 break;
801 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700802
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800803 return shouldHandleMessage;
804 }
805
806 /**
807 * Dispatch an Openflow message from a switch to the appropriate
808 * handler.
Ray Milkey269ffb92014-04-03 14:43:30 -0700809 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800810 * @param m The message to process
811 * @throws IOException
Ray Milkey269ffb92014-04-03 14:43:30 -0700812 * @throws SwitchStateException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800813 */
814 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700815 @LogMessageDoc(level = "WARN",
816 message = "Config Reply from {switch} has " +
817 "miss length set to {length}",
818 explanation = "The controller requires that the switch " +
819 "use a miss length of 0xffff for correct " +
820 "function",
821 recommendation = "Use a different switch to ensure " +
822 "correct function"),
823 @LogMessageDoc(level = "WARN",
824 message = "Received ERROR from sw {switch} that "
825 + "indicates roles are not supported "
826 + "but we have received a valid "
827 + "role reply earlier",
828 explanation = "The switch sent a confusing message to the" +
829 "controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800830 })
831 protected void processOFMessage(OFMessage m)
832 throws IOException, SwitchStateException {
833 boolean shouldHandleMessage = false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700834
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800835 switch (m.getType()) {
836 case HELLO:
837 if (log.isTraceEnabled())
838 log.trace("HELLO from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700839
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800840 if (state.hsState.equals(HandshakeState.START)) {
841 state.hsState = HandshakeState.HELLO;
842 sendHelloConfiguration();
843 } else {
Ray Milkey269ffb92014-04-03 14:43:30 -0700844 throw new SwitchStateException("Unexpected HELLO from "
845 + sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800846 }
847 break;
848 case ECHO_REQUEST:
849 OFEchoReply reply =
Ray Milkey269ffb92014-04-03 14:43:30 -0700850 (OFEchoReply) factory.getMessage(OFType.ECHO_REPLY);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800851 reply.setXid(m.getXid());
852 sw.write(reply, null);
853 break;
854 case ECHO_REPLY:
855 break;
856 case FEATURES_REPLY:
857 if (log.isTraceEnabled())
858 log.trace("Features Reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700859
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800860 sw.setFeaturesReply((OFFeaturesReply) m);
861 if (state.hsState.equals(HandshakeState.HELLO)) {
862 sendFeatureReplyConfiguration();
863 state.hsState = HandshakeState.FEATURES_REPLY;
864 // uncomment to enable "dumb" switches like cbench
865 // state.hsState = HandshakeState.READY;
866 // addSwitch(sw);
867 } else {
868 // return results to rest api caller
869 sw.deliverOFFeaturesReply(m);
870 // update database */
Jonathan Hart2fa28062013-11-25 20:16:28 -0800871 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800872 }
873 break;
874 case GET_CONFIG_REPLY:
875 if (log.isTraceEnabled())
876 log.trace("Get config reply from {}", sw);
Ray Milkey269ffb92014-04-03 14:43:30 -0700877
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800878 if (!state.hsState.equals(HandshakeState.FEATURES_REPLY)) {
879 String em = "Unexpected GET_CONFIG_REPLY from " + sw;
880 throw new SwitchStateException(em);
881 }
882 OFGetConfigReply cr = (OFGetConfigReply) m;
Ray Milkey269ffb92014-04-03 14:43:30 -0700883 if (cr.getMissSendLength() == (short) 0xffff) {
884 log.trace("Config Reply from {} confirms " +
885 "miss length set to 0xffff", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800886 } else {
887 log.warn("Config Reply from {} has " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700888 "miss length set to {}",
889 sw, cr.getMissSendLength() & 0xffff);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800890 }
891 state.hasGetConfigReply = true;
892 checkSwitchReady();
893 break;
894 case VENDOR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700895 shouldHandleMessage = handleVendorMessage((OFVendor) m);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800896 break;
897 case ERROR:
Ray Milkey269ffb92014-04-03 14:43:30 -0700898 log.debug("Recieved ERROR message from switch {}: {}", sw, m);
Ray Milkeyff735142014-05-22 19:06:02 -0700899 // TODO: we need better error handling. Especially for
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800900 // request/reply style message (stats, roles) we should have
Ray Milkeyff735142014-05-22 19:06:02 -0700901 // a unified way to lookup the xid in the error message.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800902 // This will probable involve rewriting the way we handle
903 // request/reply style messages.
904 OFError error = (OFError) m;
905 boolean shouldLogError = true;
906 // TODO: should we check that firstRoleReplyReceived is false,
907 // i.e., check only whether the first request fails?
908 if (sw.checkFirstPendingRoleRequestXid(error.getXid())) {
909 boolean isBadVendorError =
Ray Milkey269ffb92014-04-03 14:43:30 -0700910 (error.getErrorType() == OFError.OFErrorType.
911 OFPET_BAD_REQUEST.getValue());
Ray Milkeyff735142014-05-22 19:06:02 -0700912 // We expect to receive a bad vendor error when
913 // we're connected to a switch that doesn't support
914 // the Nicira vendor extensions (i.e. not OVS or
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800915 // derived from OVS). By protocol, it should also be
916 // BAD_VENDOR, but too many switch implementations
917 // get it wrong and we can already check the xid()
918 // so we can ignore the type with confidence that this
919 // is not a spurious error
920 shouldLogError = !isBadVendorError;
921 if (isBadVendorError) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700922 log.debug("Handling bad vendor error for {}", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800923 if (state.firstRoleReplyReceived && (role != null)) {
924 log.warn("Received ERROR from sw {} that "
Ray Milkey269ffb92014-04-03 14:43:30 -0700925 + "indicates roles are not supported "
926 + "but we have received a valid "
927 + "role reply earlier", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800928 }
929 state.firstRoleReplyReceived = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700930 Role requestedRole =
931 sw.deliverRoleRequestNotSupportedEx(error.getXid());
932 synchronized (roleChanger) {
933 if (sw.role == null && Controller.this.role == Role.SLAVE) {
934 //This will now never happen. The Controller's role
935 //is now never SLAVE, always MASTER.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800936 // the switch doesn't understand role request
937 // messages and the current controller role is
Ray Milkeyff735142014-05-22 19:06:02 -0700938 // slave. We need to disconnect the switch.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800939 // @see RoleChanger for rationale
Ray Milkey269ffb92014-04-03 14:43:30 -0700940 log.warn("Closing {} channel because controller's role " +
941 "is SLAVE", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800942 sw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700943 } else if (sw.role == null && requestedRole == Role.MASTER) {
944 log.debug("Adding switch {} because we got an error" +
945 " returned from a MASTER role request", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800946 // Controller's role is master: add to
Ray Milkeyff735142014-05-22 19:06:02 -0700947 // active
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800948 // TODO: check if clearing flow table is
949 // right choice here.
950 // Need to clear FlowMods before we add the switch
951 // and dispatch updates otherwise we have a race condition.
952 // TODO: switch update is async. Won't we still have a potential
Ray Milkeyff735142014-05-22 19:06:02 -0700953 // race condition?
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800954 sw.clearAllFlowMods();
955 addSwitch(sw);
956 }
957 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700958 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800959 // TODO: Is this the right thing to do if we receive
Ray Milkeyff735142014-05-22 19:06:02 -0700960 // some other error besides a bad vendor error?
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800961 // Presumably that means the switch did actually
Ray Milkeyff735142014-05-22 19:06:02 -0700962 // understand the role request message, but there
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800963 // was some other error from processing the message.
964 // OF 1.2 specifies a OFPET_ROLE_REQUEST_FAILED
Ray Milkeyff735142014-05-22 19:06:02 -0700965 // error code, but it doesn't look like the Nicira
966 // role request has that. Should check OVS source
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800967 // code to see if it's possible for any other errors
968 // to be returned.
969 // If we received an error the switch is not
Ray Milkeyff735142014-05-22 19:06:02 -0700970 // in the correct role, so we need to disconnect it.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800971 // We could also resend the request but then we need to
972 // check if there are other pending request in which
973 // case we shouldn't resend. If we do resend we need
974 // to make sure that the switch eventually accepts one
975 // of our requests or disconnect the switch. This feels
Ray Milkeyff735142014-05-22 19:06:02 -0700976 // cumbersome.
Ray Milkey269ffb92014-04-03 14:43:30 -0700977 log.debug("Closing {} channel because we recieved an " +
978 "error other than BAD_VENDOR", sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800979 sw.getChannel().close();
980 }
981 }
982 // Once we support OF 1.2, we'd add code to handle it here.
983 //if (error.getXid() == state.ofRoleRequestXid) {
984 //}
985 if (shouldLogError)
986 logError(sw, error);
987 break;
988 case STATS_REPLY:
Ray Milkey269ffb92014-04-03 14:43:30 -0700989 if (state.hsState.ordinal() <
990 HandshakeState.FEATURES_REPLY.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800991 String em = "Unexpected STATS_REPLY from " + sw;
992 throw new SwitchStateException(em);
993 }
994 sw.deliverStatisticsReply(m);
995 if (sw.hasAttribute(IOFSwitch.SWITCH_DESCRIPTION_FUTURE)) {
996 processSwitchDescReply();
997 }
998 break;
999 case PORT_STATUS:
Ray Milkeyff735142014-05-22 19:06:02 -07001000 // We want to update our port state info even if we're in
1001 // the slave role, but we only want to update storage if
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001002 // we're the master (or equal).
1003 boolean updateStorage = state.hsState.
Ray Milkey269ffb92014-04-03 14:43:30 -07001004 equals(HandshakeState.READY) &&
1005 (sw.getRole() != Role.SLAVE);
1006 handlePortStatusMessage(sw, (OFPortStatus) m, updateStorage);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001007 shouldHandleMessage = true;
1008 break;
1009
1010 default:
1011 shouldHandleMessage = true;
1012 break;
1013 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001014
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001015 if (shouldHandleMessage) {
1016 sw.getListenerReadLock().lock();
1017 try {
1018 if (sw.isConnected()) {
1019 if (!state.hsState.equals(HandshakeState.READY)) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001020 log.debug("Ignoring message type {} received " +
1021 "from switch {} before switch is " +
1022 "fully configured.", m.getType(), sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001023 }
Ray Milkeyff735142014-05-22 19:06:02 -07001024 // Check if the controller is in the slave role for the
1025 // switch. If it is, then don't dispatch the message to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001026 // the listeners.
Ray Milkeyff735142014-05-22 19:06:02 -07001027 // TODO: Should we dispatch messages that we expect to
1028 // receive when we're in the slave role, e.g. port
1029 // status messages? Since we're "hiding" switches from
1030 // the listeners when we're in the slave role, then it
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001031 // seems a little weird to dispatch port status messages
Ray Milkeyff735142014-05-22 19:06:02 -07001032 // to them. On the other hand there might be special
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001033 // modules that care about all of the connected switches
1034 // and would like to receive port status notifications.
1035 else if (sw.getRole() == Role.SLAVE) {
Ray Milkeyff735142014-05-22 19:06:02 -07001036 // Don't log message if it's a port status message
1037 // since we expect to receive those from the switch
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001038 // and don't want to emit spurious messages.
1039 if (m.getType() != OFType.PORT_STATUS) {
1040 log.debug("Ignoring message type {} received " +
1041 "from switch {} while in the slave role.",
1042 m.getType(), sw);
1043 }
1044 } else {
1045 handleMessage(sw, m, null);
1046 }
1047 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001048 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001049 sw.getListenerReadLock().unlock();
1050 }
1051 }
1052 }
1053 }
1054
1055 // ****************
1056 // Message handlers
1057 // ****************
Ray Milkey269ffb92014-04-03 14:43:30 -07001058
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001059 protected void handlePortStatusMessage(IOFSwitch sw,
1060 OFPortStatus m,
1061 boolean updateStorage) {
1062 short portNumber = m.getDesc().getPortNumber();
1063 OFPhysicalPort port = m.getDesc();
Ray Milkey269ffb92014-04-03 14:43:30 -07001064 if (m.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) {
1065 boolean portDown = ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) ||
1066 ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001067 sw.setPort(port);
Ray Milkey269ffb92014-04-03 14:43:30 -07001068 if (!portDown) {
1069 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1070 try {
1071 this.updates.put(update);
1072 } catch (InterruptedException e) {
1073 log.error("Failure adding update to queue", e);
1074 }
1075 } else {
1076 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTREMOVED);
1077 try {
1078 this.updates.put(update);
1079 } catch (InterruptedException e) {
1080 log.error("Failure adding update to queue", e);
1081 }
1082 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001083 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001084 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001085 log.debug("Port #{} modified for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001086 } else if (m.getReason() == (byte) OFPortReason.OFPPR_ADD.ordinal()) {
1087 // XXX Workaround to prevent race condition where a link is detected
1088 // and attempted to be written to the database before the port is in
1089 // the database. We now suppress link discovery on ports until we're
1090 // sure they're in the database.
Jonathan Hart284e70f2014-07-05 12:32:51 -07001091 linkDiscovery.disableDiscoveryOnPort(sw.getId(), port.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -07001092
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001093 sw.setPort(port);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001094 SwitchUpdate update = new SwitchUpdate(sw, port, SwitchUpdateType.PORTADDED);
1095 try {
1096 this.updates.put(update);
1097 } catch (InterruptedException e) {
1098 log.error("Failure adding update to queue", e);
1099 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001100 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001101 //updatePortInfo(sw, port);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001102 log.debug("Port #{} added for {}", portNumber, sw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001103 } else if (m.getReason() ==
1104 (byte) OFPortReason.OFPPR_DELETE.ordinal()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001105 sw.deletePort(portNumber);
Pankaj Berde465ac7c2013-05-23 13:47:49 -07001106 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 }
Jonathan Hart2fa28062013-11-25 20:16:28 -08001112 //if (updateStorage)
Ray Milkey269ffb92014-04-03 14:43:30 -07001113 //removePortInfo(sw, portNumber);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001114 log.debug("Port #{} deleted for {}", portNumber, sw);
1115 }
1116 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.PORTCHANGED);
1117 try {
1118 this.updates.put(update);
1119 } catch (InterruptedException e) {
1120 log.error("Failure adding update to queue", e);
1121 }
1122 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001123
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001124 /**
1125 * flcontext_cache - Keep a thread local stack of contexts
1126 */
1127 protected static final ThreadLocal<Stack<FloodlightContext>> flcontext_cache =
Ray Milkey269ffb92014-04-03 14:43:30 -07001128 new ThreadLocal<Stack<FloodlightContext>>() {
1129 @Override
1130 protected Stack<FloodlightContext> initialValue() {
1131 return new Stack<FloodlightContext>();
1132 }
1133 };
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001134
1135 /**
1136 * flcontext_alloc - pop a context off the stack, if required create a new one
Ray Milkey269ffb92014-04-03 14:43:30 -07001137 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001138 * @return FloodlightContext
1139 */
1140 protected static FloodlightContext flcontext_alloc() {
1141 FloodlightContext flcontext = null;
1142
1143 if (flcontext_cache.get().empty()) {
1144 flcontext = new FloodlightContext();
Ray Milkey269ffb92014-04-03 14:43:30 -07001145 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001146 flcontext = flcontext_cache.get().pop();
1147 }
1148
1149 return flcontext;
1150 }
1151
1152 /**
1153 * flcontext_free - Free the context to the current thread
Ray Milkey269ffb92014-04-03 14:43:30 -07001154 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001155 * @param flcontext
1156 */
1157 protected void flcontext_free(FloodlightContext flcontext) {
1158 flcontext.getStorage().clear();
1159 flcontext_cache.get().push(flcontext);
1160 }
1161
1162 /**
1163 * Handle replies to certain OFMessages, and pass others off to listeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001164 *
1165 * @param sw The switch for the message
1166 * @param m The message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001167 * @param bContext The floodlight context. If null then floodlight context would
Ray Milkey269ffb92014-04-03 14:43:30 -07001168 * be allocated in this function
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001169 * @throws IOException
1170 */
1171 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001172 @LogMessageDoc(level = "ERROR",
1173 message = "Ignoring PacketIn (Xid = {xid}) because the data" +
1174 " field is empty.",
1175 explanation = "The switch sent an improperly-formatted PacketIn" +
1176 " message",
1177 recommendation = LogMessageDoc.CHECK_SWITCH),
1178 @LogMessageDoc(level = "WARN",
1179 message = "Unhandled OF Message: {} from {}",
1180 explanation = "The switch sent a message not handled by " +
1181 "the controller")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001182 })
Ray Milkeyff735142014-05-22 19:06:02 -07001183 @SuppressWarnings("fallthrough")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001184 protected void handleMessage(IOFSwitch sw, OFMessage m,
1185 FloodlightContext bContext)
1186 throws IOException {
1187 Ethernet eth = null;
1188
1189 switch (m.getType()) {
1190 case PACKET_IN:
Ray Milkey269ffb92014-04-03 14:43:30 -07001191 OFPacketIn pi = (OFPacketIn) m;
1192
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001193 if (pi.getPacketData().length <= 0) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001194 log.error("Ignoring PacketIn (Xid = " + pi.getXid() +
1195 ") because the data field is empty.");
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001196 return;
1197 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001198
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001199 if (Controller.ALWAYS_DECODE_ETH) {
1200 eth = new Ethernet();
1201 eth.deserialize(pi.getPacketData(), 0,
1202 pi.getPacketData().length);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001203 }
1204 // fall through to default case...
1205
1206 default:
Ray Milkey269ffb92014-04-03 14:43:30 -07001207
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001208 List<IOFMessageListener> listeners = null;
1209 if (messageListeners.containsKey(m.getType())) {
1210 listeners = messageListeners.get(m.getType()).
1211 getOrderedListeners();
1212 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001213
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001214 FloodlightContext bc = null;
1215 if (listeners != null) {
1216 // Check if floodlight context is passed from the calling
1217 // function, if so use that floodlight context, otherwise
1218 // allocate one
1219 if (bContext == null) {
1220 bc = flcontext_alloc();
1221 } else {
1222 bc = bContext;
1223 }
1224 if (eth != null) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001225 IFloodlightProviderService.bcStore.put(bc,
1226 IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001227 eth);
1228 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001229
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001230 // Get the starting time (overall and per-component) of
1231 // the processing chain for this packet if performance
1232 // monitoring is turned on
Ray Milkey269ffb92014-04-03 14:43:30 -07001233
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001234 Command cmd;
1235 for (IOFMessageListener listener : listeners) {
1236 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001237 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001238 continue;
1239 }
1240 }
1241
mininet73e7fb72013-12-03 14:25:53 -08001242
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001243 cmd = listener.receive(sw, m, bc);
mininet73e7fb72013-12-03 14:25:53 -08001244
Ray Milkey269ffb92014-04-03 14:43:30 -07001245
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001246 if (Command.STOP.equals(cmd)) {
1247 break;
1248 }
1249 }
mininet73e7fb72013-12-03 14:25:53 -08001250
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001251 } else {
1252 log.warn("Unhandled OF Message: {} from {}", m, sw);
1253 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001254
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001255 if ((bContext == null) && (bc != null)) flcontext_free(bc);
1256 }
1257 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001258
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001259 /**
1260 * Log an OpenFlow error message from a switch
Ray Milkey269ffb92014-04-03 14:43:30 -07001261 *
1262 * @param sw The switch that sent the error
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001263 * @param error The error message
1264 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001265 @LogMessageDoc(level = "ERROR",
1266 message = "Error {error type} {error code} from {switch}",
1267 explanation = "The switch responded with an unexpected error" +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001268 "to an OpenFlow message from the controller",
Ray Milkey269ffb92014-04-03 14:43:30 -07001269 recommendation = "This could indicate improper network operation. " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001270 "If the problem persists restarting the switch and " +
1271 "controller may help."
Ray Milkey269ffb92014-04-03 14:43:30 -07001272 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001273 protected void logError(IOFSwitch sw, OFError error) {
1274 int etint = 0xffff & error.getErrorType();
1275 if (etint < 0 || etint >= OFErrorType.values().length) {
1276 log.error("Unknown error code {} from sw {}", etint, sw);
1277 }
1278 OFErrorType et = OFErrorType.values()[etint];
1279 switch (et) {
1280 case OFPET_HELLO_FAILED:
Ray Milkey269ffb92014-04-03 14:43:30 -07001281 OFHelloFailedCode hfc =
1282 OFHelloFailedCode.values()[0xffff & error.getErrorCode()];
1283 log.error("Error {} {} from {}", new Object[]{et, hfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001284 break;
1285 case OFPET_BAD_REQUEST:
Ray Milkey269ffb92014-04-03 14:43:30 -07001286 OFBadRequestCode brc =
1287 OFBadRequestCode.values()[0xffff & error.getErrorCode()];
1288 log.error("Error {} {} from {}", new Object[]{et, brc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001289 break;
1290 case OFPET_BAD_ACTION:
1291 OFBadActionCode bac =
Ray Milkey269ffb92014-04-03 14:43:30 -07001292 OFBadActionCode.values()[0xffff & error.getErrorCode()];
1293 log.error("Error {} {} from {}", new Object[]{et, bac, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001294 break;
1295 case OFPET_FLOW_MOD_FAILED:
1296 OFFlowModFailedCode fmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001297 OFFlowModFailedCode.values()[0xffff & error.getErrorCode()];
1298 log.error("Error {} {} from {}", new Object[]{et, fmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001299 break;
1300 case OFPET_PORT_MOD_FAILED:
1301 OFPortModFailedCode pmfc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001302 OFPortModFailedCode.values()[0xffff & error.getErrorCode()];
1303 log.error("Error {} {} from {}", new Object[]{et, pmfc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001304 break;
1305 case OFPET_QUEUE_OP_FAILED:
1306 OFQueueOpFailedCode qofc =
Ray Milkey269ffb92014-04-03 14:43:30 -07001307 OFQueueOpFailedCode.values()[0xffff & error.getErrorCode()];
1308 log.error("Error {} {} from {}", new Object[]{et, qofc, sw});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001309 break;
1310 default:
1311 break;
1312 }
1313 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001314
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001315 /**
1316 * Add a switch to the active switch list and call the switch listeners.
1317 * This happens either when a switch first connects (and the controller is
1318 * not in the slave role) or when the role of the controller changes from
1319 * slave to master.
Ray Milkey269ffb92014-04-03 14:43:30 -07001320 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001321 * @param sw the switch that has been added
1322 */
1323 // TODO: need to rethink locking and the synchronous switch update.
1324 // We can / should also handle duplicate DPIDs in connectedSwitches
Ray Milkey269ffb92014-04-03 14:43:30 -07001325 @LogMessageDoc(level = "ERROR",
1326 message = "New switch added {switch} for already-added switch {switch}",
1327 explanation = "A switch with the same DPID as another switch " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001328 "connected to the controller. This can be caused by " +
1329 "multiple switches configured with the same DPID, or " +
1330 "by a switch reconnected very quickly after " +
1331 "disconnecting.",
Ray Milkey269ffb92014-04-03 14:43:30 -07001332 recommendation = "If this happens repeatedly, it is likely there " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001333 "are switches with duplicate DPIDs on the network. " +
1334 "Reconfigure the appropriate switches. If it happens " +
1335 "very rarely, then it is likely this is a transient " +
1336 "network problem that can be ignored."
Ray Milkey269ffb92014-04-03 14:43:30 -07001337 )
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001338 protected void addSwitch(IOFSwitch sw) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001339 // XXX Workaround to prevent race condition where a link is detected
1340 // and attempted to be written to the database before the port is in
1341 // the database. We now suppress link discovery on ports until we're
1342 // sure they're in the database.
1343 for (OFPhysicalPort port : sw.getPorts()) {
Jonathan Hart284e70f2014-07-05 12:32:51 -07001344 linkDiscovery.disableDiscoveryOnPort(sw.getId(), port.getPortNumber());
Ray Milkey269ffb92014-04-03 14:43:30 -07001345 }
1346
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001347 // TODO: is it safe to modify the HashMap without holding
1348 // the old switch's lock?
1349 OFSwitchImpl oldSw = (OFSwitchImpl) this.activeSwitches.put(sw.getId(), sw);
1350 if (sw == oldSw) {
1351 // Note == for object equality, not .equals for value
1352 log.info("New add switch for pre-existing switch {}", sw);
1353 return;
1354 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001355
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001356 if (oldSw != null) {
1357 oldSw.getListenerWriteLock().lock();
1358 try {
1359 log.error("New switch added {} for already-added switch {}",
Ray Milkey269ffb92014-04-03 14:43:30 -07001360 sw, oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001361 // Set the connected flag to false to suppress calling
1362 // the listeners for this switch in processOFMessage
1363 oldSw.setConnected(false);
Ray Milkey269ffb92014-04-03 14:43:30 -07001364
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001365 oldSw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001366
Jonathan Hart2fa28062013-11-25 20:16:28 -08001367 //updateInactiveSwitchInfo(oldSw);
Ray Milkey269ffb92014-04-03 14:43:30 -07001368
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001369 // we need to clean out old switch state definitively
1370 // before adding the new switch
1371 // FIXME: It seems not completely kosher to call the
1372 // switch listeners here. I thought one of the points of
1373 // having the asynchronous switch update mechanism was so
1374 // the addedSwitch and removedSwitch were always called
1375 // from a single thread to simplify concurrency issues
1376 // for the listener.
1377 if (switchListeners != null) {
1378 for (IOFSwitchListener listener : switchListeners) {
1379 listener.removedSwitch(oldSw);
1380 }
1381 }
1382 // will eventually trigger a removeSwitch(), which will cause
1383 // a "Not removing Switch ... already removed debug message.
1384 // TODO: Figure out a way to handle this that avoids the
1385 // spurious debug message.
Jonathan Hart9e92c512013-03-20 16:24:44 -07001386 log.debug("Closing {} because a new IOFSwitch got added " +
Ray Milkey269ffb92014-04-03 14:43:30 -07001387 "for this dpid", oldSw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001388 oldSw.getChannel().close();
Ray Milkey269ffb92014-04-03 14:43:30 -07001389 } finally {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001390 oldSw.getListenerWriteLock().unlock();
1391 }
1392 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001393
Jonathan Hart2fa28062013-11-25 20:16:28 -08001394 //updateActiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001395 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.ADDED);
1396 try {
1397 this.updates.put(update);
1398 } catch (InterruptedException e) {
1399 log.error("Failure adding update to queue", e);
1400 }
1401 }
1402
1403 /**
1404 * Remove a switch from the active switch list and call the switch listeners.
1405 * This happens either when the switch is disconnected or when the
1406 * controller's role for the switch changes from master to slave.
Ray Milkey269ffb92014-04-03 14:43:30 -07001407 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001408 * @param sw the switch that has been removed
1409 */
1410 protected void removeSwitch(IOFSwitch sw) {
1411 // No need to acquire the listener lock, since
1412 // this method is only called after netty has processed all
1413 // pending messages
1414 log.debug("removeSwitch: {}", sw);
1415 if (!this.activeSwitches.remove(sw.getId(), sw) || !sw.isConnected()) {
1416 log.debug("Not removing switch {}; already removed", sw);
1417 return;
1418 }
1419 // We cancel all outstanding statistics replies if the switch transition
1420 // from active. In the future we might allow statistics requests
1421 // from slave controllers. Then we need to move this cancelation
1422 // to switch disconnect
1423 sw.cancelAllStatisticsReplies();
Ray Milkey269ffb92014-04-03 14:43:30 -07001424
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001425 // FIXME: I think there's a race condition if we call updateInactiveSwitchInfo
1426 // here if role support is enabled. In that case if the switch is being
1427 // removed because we've been switched to being in the slave role, then I think
1428 // it's possible that the new master may have already been promoted to master
1429 // and written out the active switch state to storage. If we now execute
1430 // updateInactiveSwitchInfo we may wipe out all of the state that was
1431 // written out by the new master. Maybe need to revisit how we handle all
1432 // of the switch state that's written to storage.
Ray Milkey269ffb92014-04-03 14:43:30 -07001433
Jonathan Hart2fa28062013-11-25 20:16:28 -08001434 //updateInactiveSwitchInfo(sw);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001435 SwitchUpdate update = new SwitchUpdate(sw, SwitchUpdateType.REMOVED);
1436 try {
1437 this.updates.put(update);
1438 } catch (InterruptedException e) {
1439 log.error("Failure adding update to queue", e);
1440 }
1441 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001442
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001443 // ***************
1444 // IFloodlightProvider
1445 // ***************
Ray Milkey269ffb92014-04-03 14:43:30 -07001446
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001447 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001448 public synchronized void addOFMessageListener(OFType type,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001449 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001450 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1451 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001452 if (ldd == null) {
1453 ldd = new ListenerDispatcher<OFType, IOFMessageListener>();
1454 messageListeners.put(type, ldd);
1455 }
1456 ldd.addListener(type, listener);
1457 }
1458
1459 @Override
1460 public synchronized void removeOFMessageListener(OFType type,
1461 IOFMessageListener listener) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001462 ListenerDispatcher<OFType, IOFMessageListener> ldd =
1463 messageListeners.get(type);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001464 if (ldd != null) {
1465 ldd.removeListener(listener);
1466 }
1467 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001468
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001469 private void logListeners() {
1470 for (Map.Entry<OFType,
Ray Milkey269ffb92014-04-03 14:43:30 -07001471 ListenerDispatcher<OFType,
1472 IOFMessageListener>> entry
1473 : messageListeners.entrySet()) {
1474
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001475 OFType type = entry.getKey();
Ray Milkey269ffb92014-04-03 14:43:30 -07001476 ListenerDispatcher<OFType, IOFMessageListener> ldd =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001477 entry.getValue();
Ray Milkey269ffb92014-04-03 14:43:30 -07001478
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001479 StringBuffer sb = new StringBuffer();
1480 sb.append("OFListeners for ");
1481 sb.append(type);
1482 sb.append(": ");
1483 for (IOFMessageListener l : ldd.getOrderedListeners()) {
1484 sb.append(l.getName());
1485 sb.append(",");
1486 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001487 log.debug(sb.toString());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001488 }
1489 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001490
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001491 public void removeOFMessageListeners(OFType type) {
1492 messageListeners.remove(type);
1493 }
1494
1495 @Override
1496 public Map<Long, IOFSwitch> getSwitches() {
1497 return Collections.unmodifiableMap(this.activeSwitches);
1498 }
1499
1500 @Override
1501 public void addOFSwitchListener(IOFSwitchListener listener) {
1502 this.switchListeners.add(listener);
1503 }
1504
1505 @Override
1506 public void removeOFSwitchListener(IOFSwitchListener listener) {
1507 this.switchListeners.remove(listener);
1508 }
1509
1510 @Override
1511 public Map<OFType, List<IOFMessageListener>> getListeners() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001512 Map<OFType, List<IOFMessageListener>> lers =
1513 new HashMap<OFType, List<IOFMessageListener>>();
1514 for (Entry<OFType, ListenerDispatcher<OFType, IOFMessageListener>> e :
1515 messageListeners.entrySet()) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001516 lers.put(e.getKey(), e.getValue().getOrderedListeners());
1517 }
1518 return Collections.unmodifiableMap(lers);
1519 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001520
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001521 @Override
1522 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001523 @LogMessageDoc(message = "Failed to inject OFMessage {message} onto " +
1524 "a null switch",
1525 explanation = "Failed to process a message because the switch " +
1526 " is no longer connected."),
1527 @LogMessageDoc(level = "ERROR",
1528 message = "Error reinjecting OFMessage on switch {switch}",
1529 explanation = "An I/O error occured while attempting to " +
1530 "process an OpenFlow message",
1531 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001532 })
1533 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg,
1534 FloodlightContext bc) {
1535 if (sw == null) {
1536 log.info("Failed to inject OFMessage {} onto a null switch", msg);
1537 return false;
1538 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001539
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001540 // FIXME: Do we need to be able to inject messages to switches
1541 // where we're the slave controller (i.e. they're connected but
1542 // not active)?
1543 // FIXME: Don't we need synchronization logic here so we're holding
1544 // the listener read lock when we call handleMessage? After some
1545 // discussions it sounds like the right thing to do here would be to
1546 // inject the message as a netty upstream channel event so it goes
1547 // through the normal netty event processing, including being
1548 // handled
1549 if (!activeSwitches.containsKey(sw.getId())) return false;
Ray Milkey269ffb92014-04-03 14:43:30 -07001550
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001551 try {
1552 // Pass Floodlight context to the handleMessages()
1553 handleMessage(sw, msg, bc);
1554 } catch (IOException e) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001555 log.error("Error reinjecting OFMessage on switch {}",
1556 HexString.toHexString(sw.getId()));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001557 return false;
1558 }
1559 return true;
1560 }
1561
1562 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -07001563 @LogMessageDoc(message = "Calling System.exit",
1564 explanation = "The controller is terminating")
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001565 public synchronized void terminate() {
1566 log.info("Calling System.exit");
1567 System.exit(1);
1568 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001569
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001570 @Override
1571 public boolean injectOfMessage(IOFSwitch sw, OFMessage msg) {
1572 // call the overloaded version with floodlight context set to null
1573 return injectOfMessage(sw, msg, null);
1574 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001575
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001576 @Override
1577 public void handleOutgoingMessage(IOFSwitch sw, OFMessage m,
1578 FloodlightContext bc) {
1579 if (log.isTraceEnabled()) {
1580 String str = OFMessage.getDataAsString(sw, m, bc);
1581 log.trace("{}", str);
1582 }
1583
1584 List<IOFMessageListener> listeners = null;
1585 if (messageListeners.containsKey(m.getType())) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001586 listeners =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001587 messageListeners.get(m.getType()).getOrderedListeners();
1588 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001589
1590 if (listeners != null) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001591 for (IOFMessageListener listener : listeners) {
1592 if (listener instanceof IOFSwitchFilter) {
Ray Milkey269ffb92014-04-03 14:43:30 -07001593 if (!((IOFSwitchFilter) listener).isInterested(sw)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001594 continue;
1595 }
1596 }
1597 if (Command.STOP.equals(listener.receive(sw, m, bc))) {
1598 break;
1599 }
1600 }
1601 }
1602 }
1603
1604 @Override
1605 public BasicFactory getOFMessageFactory() {
1606 return factory;
1607 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001608
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001609 @Override
1610 public String getControllerId() {
1611 return controllerId;
1612 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001613
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001614 // **************
1615 // Initialization
1616 // **************
1617
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001618 /**
1619 * Sets the initial role based on properties in the config params.
1620 * It looks for two different properties.
1621 * If the "role" property is specified then the value should be
1622 * either "EQUAL", "MASTER", or "SLAVE" and the role of the
1623 * controller is set to the specified value. If the "role" property
1624 * is not specified then it looks next for the "role.path" property.
1625 * In this case the value should be the path to a property file in
1626 * the file system that contains a property called "floodlight.role"
1627 * which can be one of the values listed above for the "role" property.
1628 * The idea behind the "role.path" mechanism is that you have some
1629 * separate heartbeat and master controller election algorithm that
1630 * determines the role of the controller. When a role transition happens,
1631 * it updates the current role in the file specified by the "role.path"
1632 * file. Then if floodlight restarts for some reason it can get the
1633 * correct current role of the controller from the file.
Ray Milkey269ffb92014-04-03 14:43:30 -07001634 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001635 * @param configParams The config params for the FloodlightProvider service
1636 * @return A valid role if role information is specified in the
Ray Milkey269ffb92014-04-03 14:43:30 -07001637 * config params, otherwise null
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001638 */
1639 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001640 @LogMessageDoc(message = "Controller role set to {role}",
1641 explanation = "Setting the initial HA role to "),
1642 @LogMessageDoc(level = "ERROR",
1643 message = "Invalid current role value: {role}",
1644 explanation = "An invalid HA role value was read from the " +
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001645 "properties file",
Ray Milkey269ffb92014-04-03 14:43:30 -07001646 recommendation = LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001647 })
1648 protected Role getInitialRole(Map<String, String> configParams) {
1649 Role role = null;
1650 String roleString = configParams.get("role");
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001651 FileInputStream fs = null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001652 if (roleString == null) {
1653 String rolePath = configParams.get("rolepath");
1654 if (rolePath != null) {
1655 Properties properties = new Properties();
1656 try {
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001657 fs = new FileInputStream(rolePath);
1658 properties.load(fs);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001659 roleString = properties.getProperty("floodlight.role");
Ray Milkey269ffb92014-04-03 14:43:30 -07001660 } catch (IOException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001661 // Don't treat it as an error if the file specified by the
1662 // rolepath property doesn't exist. This lets us enable the
1663 // HA mechanism by just creating/setting the floodlight.role
1664 // property in that file without having to modify the
1665 // floodlight properties.
Praseed Balakrishnan59469e92014-06-20 11:55:13 -07001666 } finally {
1667 if (fs != null) {
1668 try {
1669 fs.close();
1670 } catch (IOException e) {
1671 log.error("Exception while closing resource ", e);
1672 }
1673 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001674 }
1675 }
1676 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001677
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001678 if (roleString != null) {
1679 // Canonicalize the string to the form used for the enum constants
1680 roleString = roleString.trim().toUpperCase();
1681 try {
1682 role = Role.valueOf(roleString);
Ray Milkey269ffb92014-04-03 14:43:30 -07001683 } catch (IllegalArgumentException exc) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001684 log.error("Invalid current role value: {}", roleString);
1685 }
1686 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001687
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001688 log.info("Controller role set to {}", role);
Ray Milkey269ffb92014-04-03 14:43:30 -07001689
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001690 return role;
1691 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001692
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001693 /**
1694 * Tell controller that we're ready to accept switches loop
Ray Milkey269ffb92014-04-03 14:43:30 -07001695 *
1696 * @throws IOException
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001697 */
1698 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -07001699 @LogMessageDoc(message = "Listening for switch connections on {address}",
1700 explanation = "The controller is ready and listening for new" +
1701 " switch connections"),
1702 @LogMessageDoc(message = "Storage exception in controller " +
1703 "updates loop; terminating process",
1704 explanation = ERROR_DATABASE,
1705 recommendation = LogMessageDoc.CHECK_CONTROLLER),
1706 @LogMessageDoc(level = "ERROR",
1707 message = "Exception in controller updates loop",
1708 explanation = "Failed to dispatch controller event",
1709 recommendation = LogMessageDoc.GENERIC_ACTION)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001710 })
1711 public void run() {
1712 if (log.isDebugEnabled()) {
1713 logListeners();
1714 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001715
1716 try {
1717 final ServerBootstrap bootstrap = createServerBootStrap();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001718
1719 bootstrap.setOption("reuseAddr", true);
1720 bootstrap.setOption("child.keepAlive", true);
1721 bootstrap.setOption("child.tcpNoDelay", true);
1722 bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
1723
Ray Milkey269ffb92014-04-03 14:43:30 -07001724 ChannelPipelineFactory pfact =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001725 new OpenflowPipelineFactory(this, null);
1726 bootstrap.setPipelineFactory(pfact);
1727 InetSocketAddress sa = new InetSocketAddress(openFlowPort);
1728 final ChannelGroup cg = new DefaultChannelGroup();
1729 cg.add(bootstrap.bind(sa));
Ray Milkey269ffb92014-04-03 14:43:30 -07001730
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001731 log.info("Listening for switch connections on {}", sa);
1732 } catch (Exception e) {
1733 throw new RuntimeException(e);
1734 }
1735
1736 // main loop
1737 while (true) {
1738 try {
1739 IUpdate update = updates.take();
1740 update.dispatch();
1741 } catch (InterruptedException e) {
1742 return;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001743 } catch (Exception e) {
1744 log.error("Exception in controller updates loop", e);
1745 }
1746 }
1747 }
1748
1749 private ServerBootstrap createServerBootStrap() {
1750 if (workerThreads == 0) {
1751 return new ServerBootstrap(
1752 new NioServerSocketChannelFactory(
1753 Executors.newCachedThreadPool(),
1754 Executors.newCachedThreadPool()));
1755 } else {
1756 return new ServerBootstrap(
1757 new NioServerSocketChannelFactory(
1758 Executors.newCachedThreadPool(),
1759 Executors.newCachedThreadPool(), workerThreads));
1760 }
1761 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001762
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001763 public void setConfigParams(Map<String, String> configParams) {
1764 String ofPort = configParams.get("openflowport");
1765 if (ofPort != null) {
1766 this.openFlowPort = Integer.parseInt(ofPort);
1767 }
1768 log.debug("OpenFlow port set to {}", this.openFlowPort);
1769 String threads = configParams.get("workerthreads");
1770 if (threads != null) {
1771 this.workerThreads = Integer.parseInt(threads);
1772 }
1773 log.debug("Number of worker threads set to {}", this.workerThreads);
1774 String controllerId = configParams.get("controllerid");
1775 if (controllerId != null) {
1776 this.controllerId = controllerId;
Ray Milkey269ffb92014-04-03 14:43:30 -07001777 } else {
1778 //Try to get the hostname of the machine and use that for controller ID
1779 try {
1780 String hostname = java.net.InetAddress.getLocalHost().getHostName();
1781 this.controllerId = hostname;
1782 } catch (UnknownHostException e) {
1783 // Can't get hostname, we'll just use the default
1784 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001785 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001786
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001787 log.debug("ControllerId set to {}", this.controllerId);
1788 }
1789
1790 private void initVendorMessages() {
1791 // Configure openflowj to be able to parse the role request/reply
1792 // vendor messages.
1793 OFBasicVendorId niciraVendorId = new OFBasicVendorId(
1794 OFNiciraVendorData.NX_VENDOR_ID, 4);
1795 OFVendorId.registerVendorId(niciraVendorId);
1796 OFBasicVendorDataType roleRequestVendorData =
1797 new OFBasicVendorDataType(
1798 OFRoleRequestVendorData.NXT_ROLE_REQUEST,
1799 OFRoleRequestVendorData.getInstantiable());
1800 niciraVendorId.registerVendorDataType(roleRequestVendorData);
1801 OFBasicVendorDataType roleReplyVendorData =
1802 new OFBasicVendorDataType(
1803 OFRoleReplyVendorData.NXT_ROLE_REPLY,
1804 OFRoleReplyVendorData.getInstantiable());
Ray Milkey269ffb92014-04-03 14:43:30 -07001805 niciraVendorId.registerVendorDataType(roleReplyVendorData);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001806 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001807
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001808 /**
1809 * Initialize internal data structures
1810 */
1811 public void init(Map<String, String> configParams) {
1812 // These data structures are initialized here because other
1813 // module's startUp() might be called before ours
1814 this.messageListeners =
Ray Milkey269ffb92014-04-03 14:43:30 -07001815 new ConcurrentHashMap<OFType,
1816 ListenerDispatcher<OFType,
1817 IOFMessageListener>>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001818 this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001819 this.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>();
1820 this.connectedSwitches = new HashSet<OFSwitchImpl>();
1821 this.controllerNodeIPsCache = new HashMap<String, String>();
1822 this.updates = new LinkedBlockingQueue<IUpdate>();
1823 this.factory = new BasicFactory();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001824 setConfigParams(configParams);
Jonathan Hartcc957a02013-02-26 10:39:04 -08001825 //Set the controller's role to MASTER so it always tries to do role requests.
1826 this.role = Role.MASTER;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001827 this.roleChanger = new RoleChanger();
1828 initVendorMessages();
1829 this.systemStartTime = System.currentTimeMillis();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001830 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001831
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001832 /**
1833 * Startup all of the controller's components
1834 */
Ray Milkey269ffb92014-04-03 14:43:30 -07001835 @LogMessageDoc(message = "Waiting for storage source",
1836 explanation = "The system database is not yet ready",
1837 recommendation = "If this message persists, this indicates " +
1838 "that the system database has failed to start. " +
1839 LogMessageDoc.CHECK_CONTROLLER)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001840 public void startupComponents() {
Ray Milkey269ffb92014-04-03 14:43:30 -07001841 try {
1842 registryService.registerController(controllerId);
1843 } catch (RegistryException e) {
1844 log.warn("Registry service error: {}", e.getMessage());
1845 }
1846
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001847 // Add our REST API
1848 restApi.addRestletRoutable(new CoreWebRoutable());
1849 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001850
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001851 @Override
1852 public Map<String, String> getControllerNodeIPs() {
1853 // We return a copy of the mapping so we can guarantee that
1854 // the mapping return is the same as one that will be (or was)
1855 // dispatched to IHAListeners
Ray Milkey269ffb92014-04-03 14:43:30 -07001856 HashMap<String, String> retval = new HashMap<String, String>();
1857 synchronized (controllerNodeIPsCache) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001858 retval.putAll(controllerNodeIPsCache);
1859 }
1860 return retval;
1861 }
1862
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001863 @Override
1864 public long getSystemStartTime() {
1865 return (this.systemStartTime);
1866 }
1867
1868 @Override
1869 public void setAlwaysClearFlowsOnSwAdd(boolean value) {
1870 this.alwaysClearFlowsOnSwAdd = value;
1871 }
Ray Milkey269ffb92014-04-03 14:43:30 -07001872
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001873 public boolean getAlwaysClearFlowsOnSwAdd() {
1874 return this.alwaysClearFlowsOnSwAdd;
1875 }
1876}