blob: 0bb9f2f1b2182b1c6c3599d7a330c4cbcdb42976 [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;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080026import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
Yuta HIGUCHIafadeda2014-07-24 17:11:07 -070029import java.util.WeakHashMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080030import 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;
47
48import org.codehaus.jackson.annotate.JsonIgnore;
49import org.codehaus.jackson.annotate.JsonProperty;
50import org.codehaus.jackson.map.annotate.JsonSerialize;
Yuta HIGUCHI128c23b2014-04-21 10:45:09 -070051import org.codehaus.jackson.map.ser.std.ToStringSerializer;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080052import org.jboss.netty.channel.Channel;
53import org.openflow.protocol.OFFeaturesReply;
54import org.openflow.protocol.OFFeaturesRequest;
55import org.openflow.protocol.OFFlowMod;
56import org.openflow.protocol.OFMatch;
57import org.openflow.protocol.OFMessage;
58import org.openflow.protocol.OFPhysicalPort;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080059import org.openflow.protocol.OFPhysicalPort.OFPortConfig;
60import org.openflow.protocol.OFPhysicalPort.OFPortState;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080061import org.openflow.protocol.OFPort;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080062import org.openflow.protocol.OFStatisticsRequest;
Jonathan Hartec4f14e2013-12-12 10:46:38 -080063import org.openflow.protocol.OFType;
64import org.openflow.protocol.OFVendor;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080065import org.openflow.protocol.statistics.OFDescriptionStatistics;
66import org.openflow.protocol.statistics.OFStatistics;
67import org.openflow.util.HexString;
68import org.openflow.util.U16;
69import org.openflow.vendor.nicira.OFNiciraVendorData;
70import org.openflow.vendor.nicira.OFRoleRequestVendorData;
71import org.openflow.vendor.nicira.OFRoleVendorData;
72import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
Pankaj Berde98478422013-09-10 13:53:26 -070075
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080076/**
77 * This is the internal representation of an openflow switch.
78 */
Jonathan Harte4022b92014-07-09 14:10:21 -070079public class OFSwitchImpl implements IOFSwitch {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080080 // TODO: should we really do logging in the class or should we throw
81 // exception that can then be handled by callers?
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -070082 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImpl.class);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080083
Ray Milkey269ffb92014-04-03 14:43:30 -070084 private static final String HA_CHECK_SWITCH =
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080085 "Check the health of the indicated switch. If the problem " +
Ray Milkey269ffb92014-04-03 14:43:30 -070086 "persists or occurs repeatedly, it likely indicates a defect " +
87 "in the switch HA implementation.";
88
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080089 protected ConcurrentMap<Object, Object> attributes;
90 protected IFloodlightProviderService floodlightProvider;
91 protected IThreadPoolService threadPool;
92 protected Date connectedSince;
93 protected String stringId;
94 protected Channel channel;
95 protected AtomicInteger transactionIdSource;
Jonathan Harte4022b92014-07-09 14:10:21 -070096 // Lock to protect modification of the port maps. We only need to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -080097 // synchronize on modifications. For read operations we are fine since
98 // we rely on ConcurrentMaps which works for our use case.
99 private Object portLock;
100 // Map port numbers to the appropriate OFPhysicalPort
101 protected ConcurrentHashMap<Short, OFPhysicalPort> portsByNumber;
102 // Map port names to the appropriate OFPhyiscalPort
103 // XXX: The OF spec doesn't specify if port names need to be unique but
Jonathan Harte4022b92014-07-09 14:10:21 -0700104 // according it's always the case in practice.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800105 protected ConcurrentHashMap<String, OFPhysicalPort> portsByName;
Ray Milkey269ffb92014-04-03 14:43:30 -0700106 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800107 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
Ray Milkey269ffb92014-04-03 14:43:30 -0700108 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800109 protected boolean connected;
110 protected Role role;
111 protected TimedCache<Long> timedCache;
112 protected ReentrantReadWriteLock listenerLock;
113 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
114 /**
115 * When sending a role request message, the role request is added
Ray Milkey269ffb92014-04-03 14:43:30 -0700116 * to this queue. If a role reply is received this queue is checked to
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800117 * verify that the reply matches the expected reply. We require in order
Ray Milkey269ffb92014-04-03 14:43:30 -0700118 * delivery of replies. That's why we use a Queue.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800119 * The RoleChanger uses a timeout to ensure we receive a timely reply.
Ray Milkey269ffb92014-04-03 14:43:30 -0700120 * <p/>
121 * Need to synchronize on this instance if a request is sent, received,
122 * checked.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800123 */
124 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
Ray Milkey269ffb92014-04-03 14:43:30 -0700125
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800126 /* Switch features from initial featuresReply */
127 protected int capabilities;
128 protected int buffers;
129 protected int actions;
130 protected byte tables;
131 protected long datapathId;
132
133 public static IOFSwitchFeatures switchFeatures;
Ray Milkey269ffb92014-04-03 14:43:30 -0700134 protected static final ThreadLocal<Map<OFSwitchImpl, List<OFMessage>>> local_msg_buffer =
135 new ThreadLocal<Map<OFSwitchImpl, List<OFMessage>>>() {
136 @Override
137 protected Map<OFSwitchImpl, List<OFMessage>> initialValue() {
Yuta HIGUCHIafadeda2014-07-24 17:11:07 -0700138 return new WeakHashMap<OFSwitchImpl, List<OFMessage>>();
Ray Milkey269ffb92014-04-03 14:43:30 -0700139 }
140 };
141
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800142 // for managing our map sizes
Ray Milkey269ffb92014-04-03 14:43:30 -0700143 protected static final int MAX_MACS_PER_SWITCH = 1000;
144
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800145 protected static class PendingRoleRequestEntry {
146 protected int xid;
147 protected Role role;
148 // cookie is used to identify the role "generation". roleChanger uses
149 protected long cookie;
Ray Milkey269ffb92014-04-03 14:43:30 -0700150
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800151 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
152 this.xid = xid;
153 this.role = role;
154 this.cookie = cookie;
155 }
156 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700157
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800158 public OFSwitchImpl() {
159 this.stringId = null;
160 this.attributes = new ConcurrentHashMap<Object, Object>();
161 this.connectedSince = new Date();
162 this.transactionIdSource = new AtomicInteger();
163 this.portLock = new Object();
164 this.portsByNumber = new ConcurrentHashMap<Short, OFPhysicalPort>();
165 this.portsByName = new ConcurrentHashMap<String, OFPhysicalPort>();
166 this.connected = true;
Ray Milkey269ffb92014-04-03 14:43:30 -0700167 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
168 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
169 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800170 this.role = null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700171 this.timedCache = new TimedCache<Long>(100, 5 * 1000); // 5 seconds interval
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800172 this.listenerLock = new ReentrantReadWriteLock();
173 this.portBroadcastCacheHitMap = new ConcurrentHashMap<Short, Long>();
174 this.pendingRoleRequests = new LinkedList<OFSwitchImpl.PendingRoleRequestEntry>();
Ray Milkey269ffb92014-04-03 14:43:30 -0700175
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800176 // Defaults properties for an ideal switch
177 this.setAttribute(PROP_FASTWILDCARDS, OFMatch.OFPFW_ALL);
178 this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, new Boolean(true));
179 this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, new Boolean(true));
180 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700181
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800182
183 @Override
184 public Object getAttribute(String name) {
185 if (this.attributes.containsKey(name)) {
186 return this.attributes.get(name);
187 }
188 return null;
189 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700190
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800191 @Override
192 public void setAttribute(String name, Object value) {
193 this.attributes.put(name, value);
194 return;
195 }
196
197 @Override
198 public Object removeAttribute(String name) {
199 return this.attributes.remove(name);
200 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700201
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800202 @Override
203 public boolean hasAttribute(String name) {
204 return this.attributes.containsKey(name);
205 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700206
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800207 @Override
208 @JsonIgnore
209 public Channel getChannel() {
210 return this.channel;
211 }
212
213 @JsonIgnore
214 public void setChannel(Channel channel) {
215 this.channel = channel;
216 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700217
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800218 @Override
219 public void write(OFMessage m, FloodlightContext bc) throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700220 Map<OFSwitchImpl, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800221 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
222 if (msg_buffer == null) {
223 msg_buffer = new ArrayList<OFMessage>();
224 msg_buffer_map.put(this, msg_buffer);
225 }
226
227 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
228 msg_buffer.add(m);
229
230 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
Ray Milkey269ffb92014-04-03 14:43:30 -0700231 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800232 this.write(msg_buffer);
233 msg_buffer.clear();
234 }
235 }
236
237 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700238 @LogMessageDoc(level = "WARN",
239 message = "Sending OF message that modifies switch " +
240 "state while in the slave role: {switch}",
241 explanation = "An application has sent a message to a switch " +
242 "that is not valid when the switch is in a slave role",
243 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
244 public void write(List<OFMessage> msglist,
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800245 FloodlightContext bc) throws IOException {
246 for (OFMessage m : msglist) {
247 if (role == Role.SLAVE) {
248 switch (m.getType()) {
249 case PACKET_OUT:
250 case FLOW_MOD:
251 case PORT_MOD:
252 log.warn("Sending OF message that modifies switch " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700253 "state while in the slave role: {}",
254 m.getType().name());
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800255 break;
256 default:
257 break;
258 }
259 }
260 this.floodlightProvider.handleOutgoingMessage(this, m, bc);
261 }
262 this.write(msglist);
263 }
264
265 public void write(List<OFMessage> msglist) throws IOException {
266 this.channel.write(msglist);
267 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700268
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800269 @Override
270 public void disconnectOutputStream() {
271 channel.close();
272 }
HIGUCHI Yuta2995eb52013-06-14 12:50:10 -0700273
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800274 @Override
275 @JsonIgnore
276 public void setFeaturesReply(OFFeaturesReply featuresReply) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700277 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800278 if (stringId == null) {
279 /* ports are updated via port status message, so we
280 * only fill in ports on initial connection.
281 */
282 for (OFPhysicalPort port : featuresReply.getPorts()) {
283 setPort(port);
284 }
285 }
286 this.datapathId = featuresReply.getDatapathId();
287 this.capabilities = featuresReply.getCapabilities();
288 this.buffers = featuresReply.getBuffers();
289 this.actions = featuresReply.getActions();
290 this.tables = featuresReply.getTables();
291 this.stringId = HexString.toHexString(this.datapathId);
292 }
293 }
294
295 @Override
296 @JsonIgnore
297 public Collection<OFPhysicalPort> getEnabledPorts() {
298 List<OFPhysicalPort> result = new ArrayList<OFPhysicalPort>();
299 for (OFPhysicalPort port : portsByNumber.values()) {
300 if (portEnabled(port)) {
301 result.add(port);
302 }
303 }
304 return result;
305 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700306
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800307 @Override
308 @JsonIgnore
309 public Collection<Short> getEnabledPortNumbers() {
310 List<Short> result = new ArrayList<Short>();
311 for (OFPhysicalPort port : portsByNumber.values()) {
312 if (portEnabled(port)) {
313 result.add(port.getPortNumber());
314 }
315 }
316 return result;
317 }
318
319 @Override
320 public OFPhysicalPort getPort(short portNumber) {
321 return portsByNumber.get(portNumber);
322 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700323
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800324 @Override
325 public OFPhysicalPort getPort(String portName) {
326 return portsByName.get(portName);
327 }
328
329 @Override
330 @JsonIgnore
331 public void setPort(OFPhysicalPort port) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700332 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800333 portsByNumber.put(port.getPortNumber(), port);
334 portsByName.put(port.getName(), port);
335 }
336 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700337
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800338 @Override
339 @JsonProperty("ports")
340 public Collection<OFPhysicalPort> getPorts() {
341 return Collections.unmodifiableCollection(portsByNumber.values());
342 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700343
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800344 @Override
345 public void deletePort(short portNumber) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700346 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800347 portsByName.remove(portsByNumber.get(portNumber).getName());
348 portsByNumber.remove(portNumber);
349 }
350 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700351
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800352 @Override
353 public void deletePort(String portName) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700354 synchronized (portLock) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800355 portsByNumber.remove(portsByName.get(portName).getPortNumber());
356 portsByName.remove(portName);
357 }
358 }
359
360 @Override
361 public boolean portEnabled(short portNumber) {
362 if (portsByNumber.get(portNumber) == null) return false;
363 return portEnabled(portsByNumber.get(portNumber));
364 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700365
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800366 @Override
367 public boolean portEnabled(String portName) {
368 if (portsByName.get(portName) == null) return false;
369 return portEnabled(portsByName.get(portName));
370 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700371
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800372 @Override
373 public boolean portEnabled(OFPhysicalPort port) {
374 if (port == null)
375 return false;
376 if ((port.getConfig() & OFPortConfig.OFPPC_PORT_DOWN.getValue()) > 0)
377 return false;
378 if ((port.getState() & OFPortState.OFPPS_LINK_DOWN.getValue()) > 0)
379 return false;
380 // Port STP state doesn't work with multiple VLANs, so ignore it for now
381 //if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue())
382 // return false;
383 return true;
384 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700385
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800386 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700387 @JsonSerialize(using = DPIDSerializer.class)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800388 @JsonProperty("dpid")
389 public long getId() {
390 if (this.stringId == null)
391 throw new RuntimeException("Features reply has not yet been set");
392 return this.datapathId;
393 }
394
395 @JsonIgnore
396 @Override
397 public String getStringId() {
398 return stringId;
399 }
400
401 /* (non-Javadoc)
402 * @see java.lang.Object#toString()
403 */
404 @Override
405 public String toString() {
HIGUCHI Yuta535f21b2013-06-21 10:02:16 -0700406 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?") + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800407 }
408
409 @Override
410 public ConcurrentMap<Object, Object> getAttributes() {
411 return this.attributes;
412 }
413
414 @Override
415 public Date getConnectedSince() {
416 return connectedSince;
417 }
418
419 @JsonIgnore
420 @Override
421 public int getNextTransactionId() {
422 return this.transactionIdSource.incrementAndGet();
423 }
424
425 @Override
426 public void sendStatsQuery(OFStatisticsRequest request, int xid,
Ray Milkey269ffb92014-04-03 14:43:30 -0700427 IOFMessageListener caller) throws IOException {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800428 request.setXid(xid);
429 this.iofMsgListenersMap.put(xid, caller);
430 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
431 msglist.add(request);
432 this.channel.write(msglist);
433 return;
434 }
435
436 @Override
437 public Future<List<OFStatistics>> getStatistics(OFStatisticsRequest request) throws IOException {
438 request.setXid(getNextTransactionId());
439 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this, request.getXid());
440 this.statsFutureMap.put(request.getXid(), future);
441 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
442 msglist.add(request);
443 this.channel.write(msglist);
444 return future;
445 }
446
447 @Override
448 public void deliverStatisticsReply(OFMessage reply) {
449 OFStatisticsFuture future = this.statsFutureMap.get(reply.getXid());
450 if (future != null) {
451 future.deliverFuture(this, reply);
452 // The future will ultimately unregister itself and call
453 // cancelStatisticsReply
454 return;
455 }
456 /* Transaction id was not found in statsFutureMap.check the other map */
457 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
458 if (caller != null) {
459 caller.receive(this, reply, null);
460 }
461 }
462
463 @Override
464 public void cancelStatisticsReply(int transactionId) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700465 if (null == this.statsFutureMap.remove(transactionId)) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800466 this.iofMsgListenersMap.remove(transactionId);
467 }
468 }
469
470 @Override
471 public void cancelAllStatisticsReplies() {
472 /* we don't need to be synchronized here. Even if another thread
473 * modifies the map while we're cleaning up the future will eventuall
474 * timeout */
475 for (OFStatisticsFuture f : statsFutureMap.values()) {
476 f.cancel(true);
477 }
478 statsFutureMap.clear();
479 iofMsgListenersMap.clear();
480 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700481
482
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800483 /**
484 * @param floodlightProvider the floodlightProvider to set
485 */
486 @JsonIgnore
487 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
488 this.floodlightProvider = floodlightProvider;
489 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700490
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800491 @JsonIgnore
492 public void setThreadPoolService(IThreadPoolService tp) {
493 this.threadPool = tp;
494 }
495
496 @JsonIgnore
497 @Override
498 public synchronized boolean isConnected() {
499 return connected;
500 }
501
502 @Override
503 @JsonIgnore
504 public synchronized void setConnected(boolean connected) {
505 this.connected = connected;
506 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700507
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800508 @Override
509 public Role getRole() {
510 return role;
511 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700512
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800513 @JsonIgnore
514 @Override
515 public boolean isActive() {
516 return (role != Role.SLAVE);
517 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700518
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800519 @Override
520 @JsonIgnore
521 public void setSwitchProperties(OFDescriptionStatistics description) {
522 if (switchFeatures != null) {
523 switchFeatures.setFromDescription(this, description);
524 }
525 }
526
527 @Override
Ray Milkey269ffb92014-04-03 14:43:30 -0700528 @LogMessageDoc(level = "ERROR",
529 message = "Failed to clear all flows on switch {switch}",
530 explanation = "An I/O error occured while trying to clear " +
531 "flows on the switch.",
532 recommendation = LogMessageDoc.CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800533 public void clearAllFlowMods() {
534 // Delete all pre-existing flows
535 OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
536 OFMessage fm = ((OFFlowMod) floodlightProvider.getOFMessageFactory()
Ray Milkey269ffb92014-04-03 14:43:30 -0700537 .getMessage(OFType.FLOW_MOD))
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800538 .setMatch(match)
Ray Milkey269ffb92014-04-03 14:43:30 -0700539 .setCommand(OFFlowMod.OFPFC_DELETE)
540 .setOutPort(OFPort.OFPP_NONE)
541 .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH));
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800542 try {
543 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
544 msglist.add(fm);
545 channel.write(msglist);
546 } catch (Exception e) {
547 log.error("Failed to clear all flows on switch " + this, e);
548 }
549 }
550
551 @Override
552 public boolean updateBroadcastCache(Long entry, Short port) {
553 if (timedCache.update(entry)) {
554 Long count = portBroadcastCacheHitMap.putIfAbsent(port, new Long(1));
555 if (count != null) {
556 count++;
557 }
558 return true;
559 } else {
560 return false;
561 }
562 }
563
564 @Override
565 @JsonIgnore
566 public Map<Short, Long> getPortBroadcastHits() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700567 return this.portBroadcastCacheHitMap;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800568 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700569
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800570
571 @Override
572 public void flush() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700573 Map<OFSwitchImpl, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800574 List<OFMessage> msglist = msg_buffer_map.get(this);
575 if ((msglist != null) && (msglist.size() > 0)) {
576 try {
577 this.write(msglist);
578 } catch (IOException e) {
Yuta HIGUCHIe933a282014-07-13 21:02:29 -0700579 log.error("Failed flushing messages", e);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800580 }
581 msglist.clear();
582 }
583 }
584
585 public static void flush_all() {
Ray Milkey269ffb92014-04-03 14:43:30 -0700586 Map<OFSwitchImpl, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800587 for (OFSwitchImpl sw : msg_buffer_map.keySet()) {
588 sw.flush();
589 }
590 }
591
592 /**
593 * Return a read lock that must be held while calling the listeners for
594 * messages from the switch. Holding the read lock prevents the active
595 * switch list from being modified out from under the listeners.
Ray Milkey269ffb92014-04-03 14:43:30 -0700596 *
597 * @return
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800598 */
599 @JsonIgnore
600 public Lock getListenerReadLock() {
601 return listenerLock.readLock();
602 }
603
604 /**
605 * Return a write lock that must be held when the controllers modifies the
606 * list of active switches. This is to ensure that the active switch list
607 * doesn't change out from under the listeners as they are handling a
608 * message from the switch.
Ray Milkey269ffb92014-04-03 14:43:30 -0700609 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800610 * @return
611 */
612 @JsonIgnore
613 public Lock getListenerWriteLock() {
614 return listenerLock.writeLock();
615 }
616
617 /**
618 * Get the IP Address for the switch
Ray Milkey269ffb92014-04-03 14:43:30 -0700619 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800620 * @return the inet address
621 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700622 @JsonSerialize(using = ToStringSerializer.class)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800623 public SocketAddress getInetAddress() {
624 return channel.getRemoteAddress();
625 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700626
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800627 /**
628 * Send NX role request message to the switch requesting the specified role.
Ray Milkey269ffb92014-04-03 14:43:30 -0700629 * <p/>
630 * This method should ONLY be called by @see RoleChanger.submitRequest().
631 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800632 * After sending the request add it to the queue of pending request. We
633 * use the queue to later verify that we indeed receive the correct reply.
Ray Milkey269ffb92014-04-03 14:43:30 -0700634 *
635 * @param sw switch to send the role request message to
636 * @param role role to request
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800637 * @param cookie an opaque value that will be stored in the pending queue so
Ray Milkey269ffb92014-04-03 14:43:30 -0700638 * RoleChanger can check for timeouts.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800639 * @return transaction id of the role request message that was sent
640 */
641 protected int sendNxRoleRequest(Role role, long cookie)
642 throws IOException {
Ray Milkey269ffb92014-04-03 14:43:30 -0700643 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800644 // Convert the role enum to the appropriate integer constant used
645 // in the NX role request message
646 int nxRole = 0;
647 switch (role) {
648 case EQUAL:
649 nxRole = OFRoleVendorData.NX_ROLE_OTHER;
650 break;
651 case MASTER:
652 nxRole = OFRoleVendorData.NX_ROLE_MASTER;
653 break;
654 case SLAVE:
655 nxRole = OFRoleVendorData.NX_ROLE_SLAVE;
656 break;
657 default:
658 log.error("Invalid Role specified for switch {}."
Ray Milkey269ffb92014-04-03 14:43:30 -0700659 + " Disconnecting.", this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800660 // TODO: should throw an error
661 return 0;
662 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700663
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800664 // Construct the role request message
Ray Milkey269ffb92014-04-03 14:43:30 -0700665 OFVendor roleRequest = (OFVendor) floodlightProvider.
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800666 getOFMessageFactory().getMessage(OFType.VENDOR);
667 int xid = this.getNextTransactionId();
668 roleRequest.setXid(xid);
669 roleRequest.setVendor(OFNiciraVendorData.NX_VENDOR_ID);
670 OFRoleRequestVendorData roleRequestData = new OFRoleRequestVendorData();
671 roleRequestData.setRole(nxRole);
672 roleRequest.setVendorData(roleRequestData);
Ray Milkey269ffb92014-04-03 14:43:30 -0700673 roleRequest.setLengthU(OFVendor.MINIMUM_LENGTH +
674 roleRequestData.getLength());
675
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800676 // Send it to the switch
677 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
678 msglist.add(roleRequest);
679 // FIXME: should this use this.write() in order for messages to
680 // be processed by handleOutgoingMessage()
681 this.channel.write(msglist);
Ray Milkey269ffb92014-04-03 14:43:30 -0700682
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800683 pendingRoleRequests.add(new PendingRoleRequestEntry(xid, role, cookie));
684 return xid;
685 }
686 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700687
688 /**
689 * Deliver a RoleReply message to this switch. Checks if the reply
690 * message matches the expected reply (head of the pending request queue).
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800691 * We require in-order delivery of replies. If there's any deviation from
Ray Milkey269ffb92014-04-03 14:43:30 -0700692 * our expectations we disconnect the switch.
693 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800694 * We must not check the received role against the controller's current
695 * role because there's no synchronization but that's fine @see RoleChanger
Ray Milkey269ffb92014-04-03 14:43:30 -0700696 * <p/>
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800697 * Will be called by the OFChannelHandler's receive loop
Ray Milkey269ffb92014-04-03 14:43:30 -0700698 *
699 * @param xid Xid of the reply message
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800700 * @param role The Role in the the reply message
701 */
702 @LogMessageDocs({
Ray Milkey269ffb92014-04-03 14:43:30 -0700703 @LogMessageDoc(level = "ERROR",
704 message = "Switch {switch}: received unexpected role reply for " +
705 "Role {role}" +
706 " Disconnecting switch",
707 explanation = "The switch sent an unexpected HA role reply",
708 recommendation = HA_CHECK_SWITCH),
709 @LogMessageDoc(level = "ERROR",
710 message = "Switch {switch}: expected role reply with " +
711 "Xid {xid}, got {xid}. Disconnecting switch",
712 explanation = "The switch sent an unexpected HA role reply",
713 recommendation = HA_CHECK_SWITCH),
714 @LogMessageDoc(level = "ERROR",
715 message = "Switch {switch}: expected role reply with " +
716 "Role {role}, got {role}. Disconnecting switch",
717 explanation = "The switch sent an unexpected HA role reply",
718 recommendation = HA_CHECK_SWITCH)
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800719 })
720 protected void deliverRoleReply(int xid, Role role) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700721 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800722 PendingRoleRequestEntry head = pendingRoleRequests.poll();
723 if (head == null) {
Jonathan Harte4022b92014-07-09 14:10:21 -0700724 // Maybe don't disconnect if the role reply we received is
725 // for the same role we are already in.
Ray Milkey269ffb92014-04-03 14:43:30 -0700726 log.error("Switch {}: received unexpected role reply for Role {}" +
727 " Disconnecting switch", this, role);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800728 this.channel.close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700729 } else if (head.xid != xid) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800730 // check xid before role!!
731 log.error("Switch {}: expected role reply with " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700732 "Xid {}, got {}. Disconnecting switch",
733 new Object[]{this, head.xid, xid});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800734 this.channel.close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700735 } else if (head.role != role) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800736 log.error("Switch {}: expected role reply with " +
Ray Milkey269ffb92014-04-03 14:43:30 -0700737 "Role {}, got {}. Disconnecting switch",
738 new Object[]{this, head.role, role});
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800739 this.channel.close();
Ray Milkey269ffb92014-04-03 14:43:30 -0700740 } else {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800741 log.debug("Received role reply message from {}, setting role to {}",
Ray Milkey269ffb92014-04-03 14:43:30 -0700742 this, role);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800743 if (this.role == null && getAttribute(SWITCH_SUPPORTS_NX_ROLE) == null) {
744 // The first role reply we received. Set the attribute
745 // that the switch supports roles
746 setAttribute(SWITCH_SUPPORTS_NX_ROLE, true);
747 }
748 this.role = role;
749 }
750 }
751 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700752
753 /**
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800754 * Checks whether the given xid matches the xid of the first pending
Ray Milkey269ffb92014-04-03 14:43:30 -0700755 * role request.
756 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800757 * @param xid
Ray Milkey269ffb92014-04-03 14:43:30 -0700758 * @return
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800759 */
Ray Milkey269ffb92014-04-03 14:43:30 -0700760 protected boolean checkFirstPendingRoleRequestXid(int xid) {
761 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800762 PendingRoleRequestEntry head = pendingRoleRequests.peek();
763 if (head == null)
764 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700765 else
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800766 return head.xid == xid;
767 }
768 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700769
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800770 /**
Ray Milkey269ffb92014-04-03 14:43:30 -0700771 * Checks whether the given request cookie matches the cookie of the first
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800772 * pending request
Ray Milkey269ffb92014-04-03 14:43:30 -0700773 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800774 * @param cookie
775 * @return
776 */
777 protected boolean checkFirstPendingRoleRequestCookie(long cookie) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700778 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800779 PendingRoleRequestEntry head = pendingRoleRequests.peek();
780 if (head == null)
781 return false;
Ray Milkey269ffb92014-04-03 14:43:30 -0700782 else
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800783 return head.cookie == cookie;
784 }
785 }
Ray Milkey269ffb92014-04-03 14:43:30 -0700786
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800787 /**
788 * Called if we receive a vendor error message indicating that roles
789 * are not supported by the switch. If the xid matches the first pending
790 * one, we'll mark the switch as not supporting roles and remove the head.
791 * Otherwise we ignore it.
Ray Milkey269ffb92014-04-03 14:43:30 -0700792 *
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800793 * @param xid
794 */
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700795 protected void deliverRoleRequestNotSupported(int xid) {
796 deliverRoleRequestNotSupportedEx(xid);
797 }
798
799 /**
800 * ONOS Extension to deliverRoleRequestNotSupported().
801 * This version return the Roll request made.
Ray Milkey269ffb92014-04-03 14:43:30 -0700802 *
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700803 * @param xid
804 * @return Role of attempted RoleRequest.
Ray Milkey269ffb92014-04-03 14:43:30 -0700805 * @see deliverRoleRequestNotSupported
HIGUCHI Yutaeae374d2013-06-17 10:39:42 -0700806 */
807 protected Role deliverRoleRequestNotSupportedEx(int xid) {
Ray Milkey269ffb92014-04-03 14:43:30 -0700808 synchronized (pendingRoleRequests) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800809 PendingRoleRequestEntry head = pendingRoleRequests.poll();
810 this.role = null;
Ray Milkey269ffb92014-04-03 14:43:30 -0700811 if (head != null && head.xid == xid) {
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800812 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
Jonathan Harta95c6d92013-03-18 16:12:27 -0700813 return head.role;
Ray Milkey269ffb92014-04-03 14:43:30 -0700814 } else {
815 log.debug("Closing {} because a role request error didn't match " +
816 "head of pendingRoleRequests queue", this);
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800817 this.channel.close();
Jonathan Harta95c6d92013-03-18 16:12:27 -0700818 return null;
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800819 }
820 }
821 }
822
823 @Override
824 public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
825 throws IOException {
826 OFMessage request = new OFFeaturesRequest();
827 request.setXid(getNextTransactionId());
828 OFFeaturesReplyFuture future =
829 new OFFeaturesReplyFuture(threadPool, this, request.getXid());
830 this.featuresFutureMap.put(request.getXid(), future);
831 List<OFMessage> msglist = new ArrayList<OFMessage>(1);
832 msglist.add(request);
833 this.channel.write(msglist);
834 return future;
835 }
836
837 @Override
838 public void deliverOFFeaturesReply(OFMessage reply) {
839 OFFeaturesReplyFuture future = this.featuresFutureMap.get(reply.getXid());
840 if (future != null) {
841 future.deliverFuture(this, reply);
842 // The future will ultimately unregister itself and call
843 // cancelFeaturesReply
844 return;
845 }
846 log.error("Switch {}: received unexpected featureReply", this);
847 }
848
849 @Override
850 public void cancelFeaturesReply(int transactionId) {
851 this.featuresFutureMap.remove(transactionId);
852 }
853
854
855 @Override
856 public int getBuffers() {
857 return buffers;
858 }
859
860
861 @Override
862 public int getActions() {
863 return actions;
864 }
865
866
867 @Override
868 public int getCapabilities() {
869 return capabilities;
870 }
871
872
873 @Override
874 public byte getTables() {
875 return tables;
876 }
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -0800877}