blob: 96ddc7c66cf3bfee9265d0a4cf2ed2e3eeef3f41 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
Ray Milkey269ffb92014-04-03 14:43:30 -07002 * Copyright 2012, 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.IOException;
21import java.net.SocketAddress;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.Collections;
25import java.util.Date;
26import java.util.HashMap;
27import java.util.LinkedList;
28import java.util.List;
29import java.util.Map;
30import java.util.concurrent.ConcurrentHashMap;
31import java.util.concurrent.ConcurrentMap;
32import java.util.concurrent.Future;
33import java.util.concurrent.atomic.AtomicInteger;
34import java.util.concurrent.locks.Lock;
35import java.util.concurrent.locks.ReentrantReadWriteLock;
36
37import net.floodlightcontroller.core.FloodlightContext;
38import net.floodlightcontroller.core.IFloodlightProviderService;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080039import net.floodlightcontroller.core.IFloodlightProviderService.Role;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080040import net.floodlightcontroller.core.IOFMessageListener;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080041import net.floodlightcontroller.core.IOFSwitch;
42import net.floodlightcontroller.core.annotations.LogMessageDoc;
43import net.floodlightcontroller.core.annotations.LogMessageDocs;
44import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
45import net.floodlightcontroller.threadpool.IThreadPoolService;
46import net.floodlightcontroller.util.TimedCache;
Jonathan Hart51f6f5b2014-04-03 10:32:10 -070047import net.onrc.onos.core.main.IOnosRemoteSwitch;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080048
49import org.codehaus.jackson.annotate.JsonIgnore;
50import org.codehaus.jackson.annotate.JsonProperty;
51import org.codehaus.jackson.map.annotate.JsonSerialize;
Pankaj Berde98478422013-09-10 13:53:26 -070052import org.codehaus.jackson.map.ser.ToStringSerializer;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080053import org.jboss.netty.channel.Channel;
54import org.openflow.protocol.OFFeaturesReply;
55import org.openflow.protocol.OFFeaturesRequest;
56import org.openflow.protocol.OFFlowMod;
57import org.openflow.protocol.OFMatch;
58import org.openflow.protocol.OFMessage;
59import org.openflow.protocol.OFPhysicalPort;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080060import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
61import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080062import org.openflow.protocol.OFPort;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080063import org.openflow.protocol.OFStatisticsRequest;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080064import org.openflow.protocol.OFType;
65import org.openflow.protocol.OFVendor;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080066import org.openflow.protocol.statistics.OFDescriptionStatistics;
67import org.openflow.protocol.statistics.OFStatistics;
68import org.openflow.util.HexString;
69import org.openflow.util.U16;
70import org.openflow.vendor.nicira.OFNiciraVendorData;
71import org.openflow.vendor.nicira.OFRoleRequestVendorData;
72import org.openflow.vendor.nicira.OFRoleVendorData;
73import org.slf4j.Logger;
74import org.slf4j.LoggerFactory;
75
Pankaj Berde98478422013-09-10 13:53:26 -070076
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080077/**
78 * This is the internal representation of an openflow switch.
79 */
HIGUCHI Yuta7677a6f2013-06-14 14:13:35 -070080public class OFSwitchImpl implements IOFSwitch, IOnosRemoteSwitch {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080081 // TODO: should we really do logging in the class or should we throw
82 // exception that can then be handled by callers?
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -070083 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImpl.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080084
Ray Milkey269ffb92014-04-03 14:43:30 -070085 private static final String HA_CHECK_SWITCH =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080086 "Check the health of the indicated switch. If the problem " +
Ray Milkey269ffb92014-04-03 14:43:30 -070087 "persists or occurs repeatedly, it likely indicates a defect " +
88 "in the switch HA implementation.";
89
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080090 protected ConcurrentMap<Object, Object> attributes;
91 protected IFloodlightProviderService floodlightProvider;
92 protected IThreadPoolService threadPool;
93 protected Date connectedSince;
94 protected String stringId;
95 protected Channel channel;
96 protected AtomicInteger transactionIdSource;
97 // Lock to protect modification of the port maps. We only need to
98 // synchronize on modifications. For read operations we are fine since
99 // we rely on ConcurrentMaps which works for our use case.
100 private Object portLock;
101 // Map port numbers to the appropriate OFPhysicalPort
102 protected ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber;
103 // Map port names to the appropriate OFPhyiscalPort
104 // XXX: The OF spec doesn't specify if port names need to be unique but
105 // according it's always the case in practice.
106 protected ConcurrentHashMap<String, OFPhysicalPort> portsByName;
Ray Milkey269ffb92014-04-03 14:43:30 -0700107 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800108 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
Ray Milkey269ffb92014-04-03 14:43:30 -0700109 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800110 protected boolean connected;
111 protected Role role;
112 protected TimedCache<Long> timedCache;
113 protected ReentrantReadWriteLock listenerLock;
114 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
115 /**
116 * When sending a role request message, the role request is added
Ray Milkey269ffb92014-04-03 14:43:30 -0700117 * to this queue. If a role reply is received this queue is checked to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800118 * verify that the reply matches the expected reply. We require in order
Ray Milkey269ffb92014-04-03 14:43:30 -0700119 * delivery of replies. That's why we use a Queue.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800120 * The RoleChanger uses a timeout to ensure we receive a timely reply.
Ray Milkey269ffb92014-04-03 14:43:30 -0700121 * <p/>
122 * Need to synchronize on this instance if a request is sent, received,
123 * checked.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800124 */
125 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
Ray Milkey269ffb92014-04-03 14:43:30 -0700126
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800127 /* Switch features from initial featuresReply */
128 protected int capabilities;
129 protected int buffers;
130 protected int actions;
131 protected byte tables;
132 protected long datapathId;
133
134 public static IOFSwitchFeatures switchFeatures;
Ray Milkey269ffb92014-04-03 14:43:30 -0700135 protected static final ThreadLocal<Map<OFSwitchImpl, List<OFMessage>>> local_msg_buffer =
136 new ThreadLocal<Map<OFSwitchImpl, List<OFMessage>>>() {
137 @Override
138 protected Map<OFSwitchImpl, List<OFMessage>> initialValue() {
139 return new HashMap<OFSwitchImpl, List<OFMessage>>();
140 }
141 };
142
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800143 // for managing our map sizes
Ray Milkey269ffb92014-04-03 14:43:30 -0700144 protected static final int MAX_MACS_PER_SWITCH = 1000;
145
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800146 protected static class PendingRoleRequestEntry {
147 protected int xid;
148 protected Role role;
149 // cookie is used to identify the role "generation". roleChanger uses
150 protected long cookie;
Ray Milkey269ffb92014-04-03 14:43:30 -0700151
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800152 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
153 this.xid = xid;
154 this.role = role;
155 this.cookie = cookie;
156 }
157 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700158
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800159 public OFSwitchImpl() {
160 this.stringId = null;
161 this.attributes = new ConcurrentHashMap<Object, Object>();
162 this.connectedSince = new Date();
163 this.transactionIdSource = new AtomicInteger();
164 this.portLock = new Object();
165 this.portsByNumber = new ConcurrentHashMap<Short, OFPhysicalPort>();
166 this.portsByName = new ConcurrentHashMap<String, OFPhysicalPort>();
167 this.connected = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700168 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
169 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
170 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800171 this.role = null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700172 this.timedCache = new TimedCache<Long>(100, 5 * 1000); // 5 seconds interval
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800173 this.listenerLock = new ReentrantReadWriteLock();
174 this.portBroadcastCacheHitMap = new ConcurrentHashMap<Short, Long>();
175 this.pendingRoleRequests = new LinkedList<OFSwitchImpl.PendingRoleRequestEntry>();
Ray Milkey269ffb92014-04-03 14:43:30 -0700176
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800177 // Defaults properties for an ideal switch
178 this.setAttribute(PROP_FASTWILDCARDS, OFMatch.OFPFW_ALL);
179 this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, new Boolean(true));
180 this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, new Boolean(true));
181 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700182
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800183
184 @Override
185 public Object getAttribute(String name) {
186 if (this.attributes.containsKey(name)) {
187 return this.attributes.get(name);
188 }
189 return null;
190 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700191
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800192 @Override
193 public void setAttribute(String name, Object value) {
194 this.attributes.put(name, value);
195 return;
196 }
197
198 @Override
199 public Object removeAttribute(String name) {
200 return this.attributes.remove(name);
201 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700202
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800203 @Override
204 public boolean hasAttribute(String name) {
205 return this.attributes.containsKey(name);
206 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700207
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800208 @Override
209 @JsonIgnore
210 public Channel getChannel() {
211 return this.channel;
212 }
213
214 @JsonIgnore
215 public void setChannel(Channel channel) {
216 this.channel = channel;
217 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700218
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800219 @Override
220 public void write(OFMessage m, FloodlightContext bc) throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700221 Map<OFSwitchImpl, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800222 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
223 if (msg_buffer == null) {
224 msg_buffer = new ArrayList<OFMessage>();
225 msg_buffer_map.put(this, msg_buffer);
226 }
227
228 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
229 msg_buffer.add(m);
230
231 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
Ray Milkey269ffb92014-04-03 14:43:30 -0700232 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800233 this.write(msg_buffer);
234 msg_buffer.clear();
235 }
236 }
237
238 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700239 @LogMessageDoc(level = "WARN",
240 message = "Sending OF message that modifies switch " +
241 "state while in the slave role: {switch}",
242 explanation = "An application has sent a message to a switch " +
243 "that is not valid when the switch is in a slave role",
244 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
245 public void write(List<OFMessage> msglist,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800246 FloodlightContext bc) throws IOException {
247 for (OFMessage m : msglist) {
248 if (role == Role.SLAVE) {
249 switch (m.getType()) {
250 case PACKET_OUT:
251 case FLOW_MOD:
252 case PORT_MOD:
253 log.warn("Sending OF message that modifies switch " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700254 "state while in the slave role: {}",
255 m.getType().name());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800256 break;
257 default:
258 break;
259 }
260 }
261 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
262 }
263 this.write(msglist);
264 }
265
266 public void write(List<OFMessage> msglist) throws IOException {
267 this.channel.write(msglist);
268 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700269
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800270 @Override
271 public void disconnectOutputStream() {
272 channel.close();
273 }
HIGUCHI Yuta2995eb52013-06-14 12:50:10 -0700274
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800275 @Override
276 @JsonIgnore
277 public void setFeaturesReply(OFFeaturesReply featuresReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700278 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800279 if (stringId == null) {
280 /* ports are updated via port status message, so we
281 * only fill in ports on initial connection.
282 */
283 for (OFPhysicalPort port : featuresReply.getPorts()) {
284 setPort(port);
285 }
286 }
287 this.datapathId = featuresReply.getDatapathId();
288 this.capabilities = featuresReply.getCapabilities();
289 this.buffers = featuresReply.getBuffers();
290 this.actions = featuresReply.getActions();
291 this.tables = featuresReply.getTables();
292 this.stringId = HexString.toHexString(this.datapathId);
293 }
294 }
295
296 @Override
297 @JsonIgnore
298 public Collection<OFPhysicalPort> getEnabledPorts() {
299 List<OFPhysicalPort> result = new ArrayList<OFPhysicalPort>();
300 for (OFPhysicalPort port : portsByNumber.values()) {
301 if (portEnabled(port)) {
302 result.add(port);
303 }
304 }
305 return result;
306 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700307
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800308 @Override
309 @JsonIgnore
310 public Collection<Short> getEnabledPortNumbers() {
311 List<Short> result = new ArrayList<Short>();
312 for (OFPhysicalPort port : portsByNumber.values()) {
313 if (portEnabled(port)) {
314 result.add(port.getPortNumber());
315 }
316 }
317 return result;
318 }
319
320 @Override
321 public OFPhysicalPort getPort(short portNumber) {
322 return portsByNumber.get(portNumber);
323 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700324
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800325 @Override
326 public OFPhysicalPort getPort(String portName) {
327 return portsByName.get(portName);
328 }
329
330 @Override
331 @JsonIgnore
332 public void setPort(OFPhysicalPort port) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700333 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800334 portsByNumber.put(port.getPortNumber(), port);
335 portsByName.put(port.getName(), port);
336 }
337 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700338
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800339 @Override
340 @JsonProperty("ports")
341 public Collection<OFPhysicalPort> getPorts() {
342 return Collections.unmodifiableCollection(portsByNumber.values());
343 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700344
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800345 @Override
346 public void deletePort(short portNumber) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700347 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800348 portsByName.remove(portsByNumber.get(portNumber).getName());
349 portsByNumber.remove(portNumber);
350 }
351 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700352
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800353 @Override
354 public void deletePort(String portName) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700355 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800356 portsByNumber.remove(portsByName.get(portName).getPortNumber());
357 portsByName.remove(portName);
358 }
359 }
360
361 @Override
362 public boolean portEnabled(short portNumber) {
363 if (portsByNumber.get(portNumber) == null) return false;
364 return portEnabled(portsByNumber.get(portNumber));
365 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700366
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800367 @Override
368 public boolean portEnabled(String portName) {
369 if (portsByName.get(portName) == null) return false;
370 return portEnabled(portsByName.get(portName));
371 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700372
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800373 @Override
374 public boolean portEnabled(OFPhysicalPort port) {
375 if (port == null)
376 return false;
377 if ((port.getConfig() & OFPortConfig.OFPPC_PORT_DOWN.getValue()) > 0)
378 return false;
379 if ((port.getState() & OFPortState.OFPPS_LINK_DOWN.getValue()) > 0)
380 return false;
381 // Port STP state doesn't work with multiple VLANs, so ignore it for now
382 //if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
383 // return false;
384 return true;
385 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700386
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800387 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700388 @JsonSerialize(using = DPIDSerializer.class)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800389 @JsonProperty("dpid")
390 public long getId() {
391 if (this.stringId == null)
392 throw new RuntimeException("Features reply has not yet been set");
393 return this.datapathId;
394 }
395
396 @JsonIgnore
397 @Override
398 public String getStringId() {
399 return stringId;
400 }
401
402 /* (non-Javadoc)
403 * @see java.lang.Object#toString()
404 */
405 @Override
406 public String toString() {
HIGUCHI Yuta535f21b2013-06-21 10:02:16 -0700407 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?") + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800408 }
409
410 @Override
411 public ConcurrentMap<Object, Object> getAttributes() {
412 return this.attributes;
413 }
414
415 @Override
416 public Date getConnectedSince() {
417 return connectedSince;
418 }
419
420 @JsonIgnore
421 @Override
422 public int getNextTransactionId() {
423 return this.transactionIdSource.incrementAndGet();
424 }
425
426 @Override
427 public void sendStatsQuery(OFStatisticsRequest request, int xid,
Ray Milkey269ffb92014-04-03 14:43:30 -0700428 IOFMessageListener caller) throws IOException {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800429 request.setXid(xid);
430 this.iofMsgListenersMap.put(xid, caller);
431 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
432 msglist.add(request);
433 this.channel.write(msglist);
434 return;
435 }
436
437 @Override
438 public Future<List<OFStatistics>> getStatistics(OFStatisticsRequest request) throws IOException {
439 request.setXid(getNextTransactionId());
440 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this, request.getXid());
441 this.statsFutureMap.put(request.getXid(), future);
442 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
443 msglist.add(request);
444 this.channel.write(msglist);
445 return future;
446 }
447
448 @Override
449 public void deliverStatisticsReply(OFMessage reply) {
450 OFStatisticsFuture future = this.statsFutureMap.get(reply.getXid());
451 if (future != null) {
452 future.deliverFuture(this, reply);
453 // The future will ultimately unregister itself and call
454 // cancelStatisticsReply
455 return;
456 }
457 /* Transaction id was not found in statsFutureMap.check the other map */
458 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
459 if (caller != null) {
460 caller.receive(this, reply, null);
461 }
462 }
463
464 @Override
465 public void cancelStatisticsReply(int transactionId) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700466 if (null == this.statsFutureMap.remove(transactionId)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800467 this.iofMsgListenersMap.remove(transactionId);
468 }
469 }
470
471 @Override
472 public void cancelAllStatisticsReplies() {
473 /* we don't need to be synchronized here. Even if another thread
474 * modifies the map while we're cleaning up the future will eventuall
475 * timeout */
476 for (OFStatisticsFuture f : statsFutureMap.values()) {
477 f.cancel(true);
478 }
479 statsFutureMap.clear();
480 iofMsgListenersMap.clear();
481 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700482
483
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800484 /**
485 * @param floodlightProvider the floodlightProvider to set
486 */
487 @JsonIgnore
488 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
489 this.floodlightProvider = floodlightProvider;
490 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700491
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800492 @JsonIgnore
493 public void setThreadPoolService(IThreadPoolService tp) {
494 this.threadPool = tp;
495 }
496
497 @JsonIgnore
498 @Override
499 public synchronized boolean isConnected() {
500 return connected;
501 }
502
503 @Override
504 @JsonIgnore
505 public synchronized void setConnected(boolean connected) {
506 this.connected = connected;
507 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700508
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800509 @Override
510 public Role getRole() {
511 return role;
512 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700513
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800514 @JsonIgnore
515 @Override
516 public boolean isActive() {
517 return (role != Role.SLAVE);
518 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700519
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800520 @Override
521 @JsonIgnore
522 public void setSwitchProperties(OFDescriptionStatistics description) {
523 if (switchFeatures != null) {
524 switchFeatures.setFromDescription(this, description);
525 }
526 }
527
528 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700529 @LogMessageDoc(level = "ERROR",
530 message = "Failed to clear all flows on switch {switch}",
531 explanation = "An I/O error occured while trying to clear " +
532 "flows on the switch.",
533 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800534 public void clearAllFlowMods() {
535 // Delete all pre-existing flows
536 OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
537 OFMessage fm = ((OFFlowMod) floodlightProvider.getOFMessageFactory()
Ray Milkey269ffb92014-04-03 14:43:30 -0700538 .getMessage(OFType.FLOW_MOD))
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800539 .setMatch(match)
Ray Milkey269ffb92014-04-03 14:43:30 -0700540 .setCommand(OFFlowMod.OFPFC_DELETE)
541 .setOutPort(OFPort.OFPP_NONE)
542 .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800543 try {
544 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
545 msglist.add(fm);
546 channel.write(msglist);
547 } catch (Exception e) {
548 log.error("Failed to clear all flows on switch " + this, e);
549 }
550 }
551
552 @Override
553 public boolean updateBroadcastCache(Long entry, Short port) {
554 if (timedCache.update(entry)) {
555 Long count = portBroadcastCacheHitMap.putIfAbsent(port, new Long(1));
556 if (count != null) {
557 count++;
558 }
559 return true;
560 } else {
561 return false;
562 }
563 }
564
565 @Override
566 @JsonIgnore
567 public Map<Short, Long> getPortBroadcastHits() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700568 return this.portBroadcastCacheHitMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800569 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700570
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800571
572 @Override
573 public void flush() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700574 Map<OFSwitchImpl, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800575 List<OFMessage> msglist = msg_buffer_map.get(this);
576 if ((msglist != null) && (msglist.size() > 0)) {
577 try {
578 this.write(msglist);
579 } catch (IOException e) {
580 // TODO: log exception
581 e.printStackTrace();
582 }
583 msglist.clear();
584 }
585 }
586
587 public static void flush_all() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700588 Map<OFSwitchImpl, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800589 for (OFSwitchImpl sw : msg_buffer_map.keySet()) {
590 sw.flush();
591 }
592 }
593
594 /**
595 * Return a read lock that must be held while calling the listeners for
596 * messages from the switch. Holding the read lock prevents the active
597 * switch list from being modified out from under the listeners.
Ray Milkey269ffb92014-04-03 14:43:30 -0700598 *
599 * @return
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800600 */
601 @JsonIgnore
602 public Lock getListenerReadLock() {
603 return listenerLock.readLock();
604 }
605
606 /**
607 * Return a write lock that must be held when the controllers modifies the
608 * list of active switches. This is to ensure that the active switch list
609 * doesn't change out from under the listeners as they are handling a
610 * message from the switch.
Ray Milkey269ffb92014-04-03 14:43:30 -0700611 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800612 * @return
613 */
614 @JsonIgnore
615 public Lock getListenerWriteLock() {
616 return listenerLock.writeLock();
617 }
618
619 /**
620 * Get the IP Address for the switch
Ray Milkey269ffb92014-04-03 14:43:30 -0700621 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800622 * @return the inet address
623 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700624 @JsonSerialize(using = ToStringSerializer.class)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800625 public SocketAddress getInetAddress() {
626 return channel.getRemoteAddress();
627 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700628
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800629 /**
630 * Send NX role request message to the switch requesting the specified role.
Ray Milkey269ffb92014-04-03 14:43:30 -0700631 * <p/>
632 * This method should ONLY be called by @see RoleChanger.submitRequest().
633 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800634 * After sending the request add it to the queue of pending request. We
635 * use the queue to later verify that we indeed receive the correct reply.
Ray Milkey269ffb92014-04-03 14:43:30 -0700636 *
637 * @param sw switch to send the role request message to
638 * @param role role to request
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800639 * @param cookie an opaque value that will be stored in the pending queue so
Ray Milkey269ffb92014-04-03 14:43:30 -0700640 * RoleChanger can check for timeouts.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800641 * @return transaction id of the role request message that was sent
642 */
643 protected int sendNxRoleRequest(Role role, long cookie)
644 throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700645 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800646 // Convert the role enum to the appropriate integer constant used
647 // in the NX role request message
648 int nxRole = 0;
649 switch (role) {
650 case EQUAL:
651 nxRole = OFRoleVendorData.NX_ROLE_OTHER;
652 break;
653 case MASTER:
654 nxRole = OFRoleVendorData.NX_ROLE_MASTER;
655 break;
656 case SLAVE:
657 nxRole = OFRoleVendorData.NX_ROLE_SLAVE;
658 break;
659 default:
660 log.error("Invalid Role specified for switch {}."
Ray Milkey269ffb92014-04-03 14:43:30 -0700661 + " Disconnecting.", this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800662 // TODO: should throw an error
663 return 0;
664 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700665
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800666 // Construct the role request message
Ray Milkey269ffb92014-04-03 14:43:30 -0700667 OFVendor roleRequest = (OFVendor) floodlightProvider.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800668 getOFMessageFactory().getMessage(OFType.VENDOR);
669 int xid = this.getNextTransactionId();
670 roleRequest.setXid(xid);
671 roleRequest.setVendor(OFNiciraVendorData.NX_VENDOR_ID);
672 OFRoleRequestVendorData roleRequestData = new OFRoleRequestVendorData();
673 roleRequestData.setRole(nxRole);
674 roleRequest.setVendorData(roleRequestData);
Ray Milkey269ffb92014-04-03 14:43:30 -0700675 roleRequest.setLengthU(OFVendor.MINIMUM_LENGTH +
676 roleRequestData.getLength());
677
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800678 // Send it to the switch
679 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
680 msglist.add(roleRequest);
681 // FIXME: should this use this.write() in order for messages to
682 // be processed by handleOutgoingMessage()
683 this.channel.write(msglist);
Ray Milkey269ffb92014-04-03 14:43:30 -0700684
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800685 pendingRoleRequests.add(new PendingRoleRequestEntry(xid, role, cookie));
686 return xid;
687 }
688 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700689
690 /**
691 * Deliver a RoleReply message to this switch. Checks if the reply
692 * message matches the expected reply (head of the pending request queue).
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800693 * We require in-order delivery of replies. If there's any deviation from
Ray Milkey269ffb92014-04-03 14:43:30 -0700694 * our expectations we disconnect the switch.
695 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800696 * We must not check the received role against the controller's current
697 * role because there's no synchronization but that's fine @see RoleChanger
Ray Milkey269ffb92014-04-03 14:43:30 -0700698 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800699 * Will be called by the OFChannelHandler's receive loop
Ray Milkey269ffb92014-04-03 14:43:30 -0700700 *
701 * @param xid Xid of the reply message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800702 * @param role The Role in the the reply message
703 */
704 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700705 @LogMessageDoc(level = "ERROR",
706 message = "Switch {switch}: received unexpected role reply for " +
707 "Role {role}" +
708 " Disconnecting switch",
709 explanation = "The switch sent an unexpected HA role reply",
710 recommendation = HA_CHECK_SWITCH),
711 @LogMessageDoc(level = "ERROR",
712 message = "Switch {switch}: expected role reply with " +
713 "Xid {xid}, got {xid}. Disconnecting switch",
714 explanation = "The switch sent an unexpected HA role reply",
715 recommendation = HA_CHECK_SWITCH),
716 @LogMessageDoc(level = "ERROR",
717 message = "Switch {switch}: expected role reply with " +
718 "Role {role}, got {role}. Disconnecting switch",
719 explanation = "The switch sent an unexpected HA role reply",
720 recommendation = HA_CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800721 })
722 protected void deliverRoleReply(int xid, Role role) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700723 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800724 PendingRoleRequestEntry head = pendingRoleRequests.poll();
725 if (head == null) {
726 // Maybe don't disconnect if the role reply we received is
727 // for the same role we are already in.
Ray Milkey269ffb92014-04-03 14:43:30 -0700728 log.error("Switch {}: received unexpected role reply for Role {}" +
729 " Disconnecting switch", this, role);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800730 this.channel.close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700731 } else if (head.xid != xid) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800732 // check xid before role!!
733 log.error("Switch {}: expected role reply with " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700734 "Xid {}, got {}. Disconnecting switch",
735 new Object[]{this, head.xid, xid});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800736 this.channel.close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700737 } else if (head.role != role) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800738 log.error("Switch {}: expected role reply with " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700739 "Role {}, got {}. Disconnecting switch",
740 new Object[]{this, head.role, role});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800741 this.channel.close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700742 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800743 log.debug("Received role reply message from {}, setting role to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700744 this, role);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800745 if (this.role == null && getAttribute(SWITCH_SUPPORTS_NX_ROLE) == null) {
746 // The first role reply we received. Set the attribute
747 // that the switch supports roles
748 setAttribute(SWITCH_SUPPORTS_NX_ROLE, true);
749 }
750 this.role = role;
751 }
752 }
753 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700754
755 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800756 * Checks whether the given xid matches the xid of the first pending
Ray Milkey269ffb92014-04-03 14:43:30 -0700757 * role request.
758 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800759 * @param xid
Ray Milkey269ffb92014-04-03 14:43:30 -0700760 * @return
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800761 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700762 protected boolean checkFirstPendingRoleRequestXid(int xid) {
763 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800764 PendingRoleRequestEntry head = pendingRoleRequests.peek();
765 if (head == null)
766 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700767 else
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800768 return head.xid == xid;
769 }
770 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700771
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800772 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700773 * Checks whether the given request cookie matches the cookie of the first
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800774 * pending request
Ray Milkey269ffb92014-04-03 14:43:30 -0700775 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800776 * @param cookie
777 * @return
778 */
779 protected boolean checkFirstPendingRoleRequestCookie(long cookie) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700780 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800781 PendingRoleRequestEntry head = pendingRoleRequests.peek();
782 if (head == null)
783 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700784 else
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800785 return head.cookie == cookie;
786 }
787 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700788
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800789 /**
790 * Called if we receive a vendor error message indicating that roles
791 * are not supported by the switch. If the xid matches the first pending
792 * one, we'll mark the switch as not supporting roles and remove the head.
793 * Otherwise we ignore it.
Ray Milkey269ffb92014-04-03 14:43:30 -0700794 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800795 * @param xid
796 */
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700797 protected void deliverRoleRequestNotSupported(int xid) {
798 deliverRoleRequestNotSupportedEx(xid);
799 }
800
801 /**
802 * ONOS Extension to deliverRoleRequestNotSupported().
803 * This version return the Roll request made.
Ray Milkey269ffb92014-04-03 14:43:30 -0700804 *
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700805 * @param xid
806 * @return Role of attempted RoleRequest.
Ray Milkey269ffb92014-04-03 14:43:30 -0700807 * @see deliverRoleRequestNotSupported
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700808 */
809 protected Role deliverRoleRequestNotSupportedEx(int xid) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700810 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800811 PendingRoleRequestEntry head = pendingRoleRequests.poll();
812 this.role = null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700813 if (head != null && head.xid == xid) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800814 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
Jonathan Harta95c6d92013-03-18 16:12:27 -0700815 return head.role;
Ray Milkey269ffb92014-04-03 14:43:30 -0700816 } else {
817 log.debug("Closing {} because a role request error didn't match " +
818 "head of pendingRoleRequests queue", this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800819 this.channel.close();
Jonathan Harta95c6d92013-03-18 16:12:27 -0700820 return null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800821 }
822 }
823 }
824
825 @Override
826 public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
827 throws IOException {
828 OFMessage request = new OFFeaturesRequest();
829 request.setXid(getNextTransactionId());
830 OFFeaturesReplyFuture future =
831 new OFFeaturesReplyFuture(threadPool, this, request.getXid());
832 this.featuresFutureMap.put(request.getXid(), future);
833 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
834 msglist.add(request);
835 this.channel.write(msglist);
836 return future;
837 }
838
839 @Override
840 public void deliverOFFeaturesReply(OFMessage reply) {
841 OFFeaturesReplyFuture future = this.featuresFutureMap.get(reply.getXid());
842 if (future != null) {
843 future.deliverFuture(this, reply);
844 // The future will ultimately unregister itself and call
845 // cancelFeaturesReply
846 return;
847 }
848 log.error("Switch {}: received unexpected featureReply", this);
849 }
850
851 @Override
852 public void cancelFeaturesReply(int transactionId) {
853 this.featuresFutureMap.remove(transactionId);
854 }
855
856
857 @Override
858 public int getBuffers() {
859 return buffers;
860 }
861
862
863 @Override
864 public int getActions() {
865 return actions;
866 }
867
868
869 @Override
870 public int getCapabilities() {
871 return capabilities;
872 }
873
874
875 @Override
876 public byte getTables() {
877 return tables;
878 }
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800879
880
Ray Milkey269ffb92014-04-03 14:43:30 -0700881 @Override
882 public void setupRemoteSwitch(Long dpid) {
883 this.datapathId = dpid;
884 this.stringId = HexString.toHexString(this.datapathId);
885 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800886}