blob: 236ed065cf0d6eb30d0645f9cd7a779a5056cb03 [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.core.internal;
2
3/**
4 * Copyright 2012, Big Switch Networks, Inc.
5 * Originally created by David Erickson, Stanford University
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may
8 * not use this file except in compliance with the License. You may obtain
9 * a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 **/
19
20import java.io.IOException;
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -070021import java.net.SocketAddress;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070022import 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.Set;
31import java.util.WeakHashMap;
32import java.util.concurrent.ConcurrentHashMap;
33import java.util.concurrent.ConcurrentMap;
34import java.util.concurrent.Future;
35import java.util.concurrent.atomic.AtomicInteger;
36import java.util.concurrent.locks.Lock;
37import java.util.concurrent.locks.ReentrantReadWriteLock;
38
39import net.floodlightcontroller.core.FloodlightContext;
40import net.floodlightcontroller.core.IFloodlightProviderService;
41import net.floodlightcontroller.core.IFloodlightProviderService.Role;
42import net.floodlightcontroller.core.IOFMessageListener;
43import net.floodlightcontroller.core.IOFSwitch;
44import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
45import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
46import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
47import net.floodlightcontroller.core.annotations.LogMessageDoc;
48import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
49import net.floodlightcontroller.debugcounter.IDebugCounter;
50import net.floodlightcontroller.debugcounter.IDebugCounterService;
51import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
52import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
53import net.floodlightcontroller.debugcounter.NullDebugCounter;
54import net.floodlightcontroller.threadpool.IThreadPoolService;
55import net.floodlightcontroller.util.LinkedHashSetWrapper;
56import net.floodlightcontroller.util.OrderedCollection;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070057
58import org.codehaus.jackson.annotate.JsonIgnore;
59import org.codehaus.jackson.annotate.JsonProperty;
60import org.codehaus.jackson.map.annotate.JsonSerialize;
61import org.jboss.netty.channel.Channel;
62import org.projectfloodlight.openflow.protocol.OFActionType;
Saurav Dasd84178f2014-09-29 17:48:54 -070063import org.projectfloodlight.openflow.protocol.OFBarrierReply;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070064import org.projectfloodlight.openflow.protocol.OFCapabilities;
65import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
Jonathan Harta213bce2014-08-11 15:44:07 -070066import org.projectfloodlight.openflow.protocol.OFFactories;
67import org.projectfloodlight.openflow.protocol.OFFactory;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070068import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
Jonathan Hart23daf192014-08-22 10:56:58 -070069import org.projectfloodlight.openflow.protocol.OFFlowDelete.Builder;
Saurav Das91890e42014-10-03 15:54:21 -070070import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
71import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070072import org.projectfloodlight.openflow.protocol.OFMessage;
73import org.projectfloodlight.openflow.protocol.OFPortConfig;
74import org.projectfloodlight.openflow.protocol.OFPortDesc;
75import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
76import org.projectfloodlight.openflow.protocol.OFPortReason;
77import org.projectfloodlight.openflow.protocol.OFPortState;
78import org.projectfloodlight.openflow.protocol.OFPortStatus;
79import org.projectfloodlight.openflow.protocol.OFStatsReply;
80import org.projectfloodlight.openflow.protocol.OFStatsRequest;
Saurav Das91890e42014-10-03 15:54:21 -070081import org.projectfloodlight.openflow.protocol.OFStatsType;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070082import org.projectfloodlight.openflow.protocol.OFType;
83import org.projectfloodlight.openflow.protocol.OFVersion;
Saurav Das91890e42014-10-03 15:54:21 -070084import org.projectfloodlight.openflow.protocol.match.MatchField;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070085import org.projectfloodlight.openflow.types.DatapathId;
86import org.projectfloodlight.openflow.types.OFAuxId;
87import org.projectfloodlight.openflow.types.OFGroup;
88import org.projectfloodlight.openflow.types.OFPort;
89import org.projectfloodlight.openflow.types.TableId;
90import org.projectfloodlight.openflow.types.U64;
91import org.slf4j.Logger;
92import org.slf4j.LoggerFactory;
93
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070094/**
95 * This is the internal representation of an openflow switch.
96 */
97public class OFSwitchImplBase implements IOFSwitch {
98 // TODO: should we really do logging in the class or should we throw
99 // exception that can then be handled by callers?
100 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImplBase.class);
101
102 private static final String HA_CHECK_SWITCH =
103 "Check the health of the indicated switch. If the problem " +
104 "persists or occurs repeatedly, it likely indicates a defect " +
105 "in the switch HA implementation.";
106
107 protected ConcurrentMap<Object, Object> attributes;
108 protected IFloodlightProviderService floodlightProvider;
109 protected IThreadPoolService threadPool;
110 protected Date connectedSince;
111 protected String stringId;
112 protected Channel channel;
113 // transaction id used for messages sent out to this switch from
114 // this controller instance. This xid has significance only between this
115 // controller<->switch pair.
116 protected AtomicInteger transactionIdSource;
117
118 // generation id used for roleRequest messages sent to switches (see section
119 // 6.3.5 of the OF1.3.4 spec). This generationId has significance between
120 // all the controllers that this switch is connected to; and only for role
121 // request messages with role MASTER or SLAVE. The set of Controllers that
122 // this switch is connected to should coordinate the next generation id,
123 // via transactional semantics.
124 protected long generationIdSource;
125
126 // Lock to protect modification of the port maps. We only need to
127 // synchronize on modifications. For read operations we are fine since
128 // we rely on ConcurrentMaps which works for our use case.
Saurav Dasfcdad072014-08-13 14:15:21 -0700129 // private Object portLock; XXX S remove this
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700130
131 // Map port numbers to the appropriate OFPortDesc
132 protected ConcurrentHashMap<Integer, OFPortDesc> portsByNumber;
133 // Map port names to the appropriate OFPhyiscalPort
134 // XXX: The OF spec doesn't specify if port names need to be unique but
Saurav Dasfcdad072014-08-13 14:15:21 -0700135 // according it's always the case in practice.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700136 protected ConcurrentHashMap<String, OFPortDesc> portsByName;
137 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
Saurav Dasfcdad072014-08-13 14:15:21 -0700138 // XXX Consider removing the following 2 maps - not used anymore
139 protected Map<Integer, IOFMessageListener> iofMsgListenersMap;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700140 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
Saurav Dasd84178f2014-09-29 17:48:54 -0700141 protected Map<Long, OFBarrierReplyFuture> barrierFutureMap;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700142 protected boolean connected;
143 protected Role role;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700144 protected ReentrantReadWriteLock listenerLock;
145 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
146 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700147 * When sending a role request message, the role request is added to this
148 * queue. If a role reply is received this queue is checked to verify that
149 * the reply matches the expected reply. We require in order delivery of
150 * replies. That's why we use a Queue. The RoleChanger uses a timeout to
151 * ensure we receive a timely reply.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700152 * <p/>
153 * Need to synchronize on this instance if a request is sent, received,
154 * checked.
155 */
156 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
157
158 /** OpenFlow version for this switch */
159 protected OFVersion ofversion;
160 // Description stats reply describing this switch
161 private OFDescStatsReply switchDescription;
162 // Switch features from initial featuresReply
163 protected Set<OFCapabilities> capabilities;
164 protected int buffers;
165 protected Set<OFActionType> actions;
166 protected byte tables;
167 protected DatapathId datapathId;
Saurav Dasfcdad072014-08-13 14:15:21 -0700168 private OFAuxId auxId;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700169
170 private IDebugCounterService debugCounters;
Saurav Dasfcdad072014-08-13 14:15:21 -0700171 private boolean debugCountersRegistered;
172 @SuppressWarnings("unused")
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700173 private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite,
Saurav Dasfcdad072014-08-13 14:15:21 -0700174 ctrSwitchPktinDrops, ctrSwitchWriteDrops;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700175
Saurav Dasfcdad072014-08-13 14:15:21 -0700176 protected boolean startDriverHandshakeCalled = false;
177 private boolean flowTableFull = false;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700178
Saurav Dasfcdad072014-08-13 14:15:21 -0700179 private final PortManager portManager;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700180
181 protected static final ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>> local_msg_buffer =
Saurav Dasfcdad072014-08-13 14:15:21 -0700182 new ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>>() {
183 @Override
184 protected Map<OFSwitchImplBase, List<OFMessage>> initialValue() {
185 return new WeakHashMap<OFSwitchImplBase, List<OFMessage>>();
186 }
187 };
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700188
Saurav Dasfcdad072014-08-13 14:15:21 -0700189 private static final String BASE = "switchbase";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700190
191 protected static class PendingRoleRequestEntry {
192 protected int xid;
193 protected Role role;
194 // cookie is used to identify the role "generation". roleChanger uses
195 protected long cookie;
196
197 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
198 this.xid = xid;
199 this.role = role;
200 this.cookie = cookie;
201 }
202 }
203
204 public OFSwitchImplBase() {
205 this.stringId = null;
206 this.attributes = new ConcurrentHashMap<Object, Object>();
207 this.connectedSince = new Date();
208 this.transactionIdSource = new AtomicInteger();
Saurav Dasfcdad072014-08-13 14:15:21 -0700209 this.generationIdSource = 0; // XXX S this is wrong; should be
210 // negotiated
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700211 // XXX S no need this.portLock = new Object();
212 this.portsByNumber = new ConcurrentHashMap<Integer, OFPortDesc>();
213 this.portsByName = new ConcurrentHashMap<String, OFPortDesc>();
214 this.connected = true;
215 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
216 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
217 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
Saurav Dasd84178f2014-09-29 17:48:54 -0700218 this.barrierFutureMap = new ConcurrentHashMap<Long, OFBarrierReplyFuture>();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700219 this.role = null;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700220 this.listenerLock = new ReentrantReadWriteLock();
221 this.pendingRoleRequests = new LinkedList<OFSwitchImplBase.PendingRoleRequestEntry>();
222 this.portManager = new PortManager();
223 // by default the base impl declares no support for Nx_role_requests.
224 // OF1.0 switches like OVS that do support these messages should set the
225 // attribute in the associated switch driver.
226 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
227
228 }
229
Saurav Dasfcdad072014-08-13 14:15:21 -0700230 // *******************************************
231 // Setters and Getters
232 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700233
234 @Override
235 public Object getAttribute(String name) {
236 if (this.attributes.containsKey(name)) {
237 return this.attributes.get(name);
238 }
239 return null;
240 }
241
242 @Override
243 public ConcurrentMap<Object, Object> getAttributes() {
244 return this.attributes;
245 }
246
247 @Override
248 public void setAttribute(String name, Object value) {
249 this.attributes.put(name, value);
250 return;
251 }
252
253 @Override
254 public Object removeAttribute(String name) {
255 return this.attributes.remove(name);
256 }
257
258 @Override
259 public boolean hasAttribute(String name) {
260 return this.attributes.containsKey(name);
261 }
262
263 @Override
264 @JsonSerialize(using = DPIDSerializer.class)
265 @JsonProperty("dpid")
266 public long getId() {
267 if (this.stringId == null)
268 throw new RuntimeException("Features reply has not yet been set");
269 return this.datapathId.getLong();
270 }
271
272 @JsonIgnore
273 @Override
274 public String getStringId() {
275 return stringId;
276 }
277
Jonathan Harta213bce2014-08-11 15:44:07 -0700278 @Override
279 public OFFactory getFactory() {
280 return OFFactories.getFactory(ofversion);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700281 }
282
Jonathan Harta213bce2014-08-11 15:44:07 -0700283 @Override
284 public OFVersion getOFVersion() {
285 return ofversion;
286 }
287
288 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700289 public void setOFVersion(OFVersion ofv) {
Jonathan Harta213bce2014-08-11 15:44:07 -0700290 ofversion = ofv;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700291 }
292
293 /**
294 * @param floodlightProvider the floodlightProvider to set
295 */
296 @JsonIgnore
297 @Override
298 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
299 this.floodlightProvider = floodlightProvider;
300 }
301
302 @JsonIgnore
303 @Override
304 public void setThreadPoolService(IThreadPoolService tp) {
305 this.threadPool = tp;
306 }
307
308 @Override
309 @JsonIgnore
310 public void setDebugCounterService(IDebugCounterService debugCounters)
311 throws CounterException {
312 this.debugCounters = debugCounters;
313 registerOverloadCounters();
314 }
315
316 /* (non-Javadoc)
317 * @see java.lang.Object#toString()
318 */
319 @Override
320 public String toString() {
321 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?")
Saurav Dasfcdad072014-08-13 14:15:21 -0700322 + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700323 }
324
Saurav Dasfcdad072014-08-13 14:15:21 -0700325 // *******************************************
326 // Channel related methods
327 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700328
329 @JsonIgnore
330 @Override
331 public void setChannel(Channel channel) {
332 this.channel = channel;
333 }
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700334 @Override
335 public SocketAddress getChannelSocketAddress(){
336 return channel.getRemoteAddress();
337 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700338
339 @Override
340 public void write(OFMessage m, FloodlightContext bc) throws IOException {
341 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
342 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
343 if (msg_buffer == null) {
344 msg_buffer = new ArrayList<OFMessage>();
345 msg_buffer_map.put(this, msg_buffer);
346 }
347 // XXX S will change when iFloodlight provider changes
Saurav Dasfcdad072014-08-13 14:15:21 -0700348 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700349 msg_buffer.add(m);
350
351 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
352 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
353 this.write(msg_buffer);
354 msg_buffer.clear();
355 }
356 }
357
358 @Override
359 @LogMessageDoc(level = "WARN",
360 message = "Sending OF message that modifies switch " +
361 "state while in the slave role: {switch}",
362 explanation = "An application has sent a message to a switch " +
363 "that is not valid when the switch is in a slave role",
364 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
365 public void write(List<OFMessage> msglist,
Saurav Dasfcdad072014-08-13 14:15:21 -0700366 FloodlightContext bc) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700367 for (OFMessage m : msglist) {
368 if (role == Role.SLAVE) {
369 switch (m.getType()) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700370 case PACKET_OUT:
371 case FLOW_MOD:
372 case PORT_MOD:
373 log.warn("Sending OF message that modifies switch " +
374 "state while in the slave role: {}",
375 m.getType().name());
376 break;
377 default:
378 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700379 }
380 }
381 // XXX S again
Saurav Dasfcdad072014-08-13 14:15:21 -0700382 // this.floodlightProvider.handleOutgoingMessage(this, m, bc);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700383 }
384 this.write(msglist);
385 }
386
Saurav Das0a344b02014-09-26 14:18:52 -0700387 protected void write(List<OFMessage> msglist) throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700388 this.channel.write(msglist);
389 }
390
391 @Override
392 public void disconnectSwitch() {
393 channel.close();
394 }
395
396 @Override
397 public Date getConnectedSince() {
398 return connectedSince;
399 }
400
401 @JsonIgnore
402 @Override
403 public int getNextTransactionId() {
404 return this.transactionIdSource.incrementAndGet();
405 }
406
407 @JsonIgnore
408 @Override
409 public synchronized boolean isConnected() {
410 return connected;
411 }
412
413 @Override
414 @JsonIgnore
415 public synchronized void setConnected(boolean connected) {
416 this.connected = connected;
417 }
418
Saurav Dasfcdad072014-08-13 14:15:21 -0700419 // *******************************************
420 // Switch features related methods
421 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700422
423 /**
424 * Set the features reply for this switch from the handshake
425 */
426 protected void setFeaturesReply(OFFeaturesReply featuresReply) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700427 if (featuresReply == null) {
428 log.error("Error setting featuresReply for switch: {}", getStringId());
429 return;
430 }
431 this.datapathId = featuresReply.getDatapathId();
432 this.capabilities = featuresReply.getCapabilities();
433 this.buffers = (int) featuresReply.getNBuffers();
434 this.tables = (byte) featuresReply.getNTables();
435 this.stringId = this.datapathId.toString();
436 if (ofversion == OFVersion.OF_13) {
437 auxId = featuresReply.getAuxiliaryId();
438 if (!auxId.equals(OFAuxId.MAIN)) {
439 log.warn("This controller does not handle auxiliary connections. "
440 + "Aux connection id {} received from switch {}",
441 auxId, getStringId());
442 }
443 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700444
Saurav Dasfcdad072014-08-13 14:15:21 -0700445 if (ofversion == OFVersion.OF_10) {
446 this.actions = featuresReply.getActions();
447 portManager.compareAndUpdatePorts(featuresReply.getPorts(), true);
448 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700449 }
450
451 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700452 * Set the port descriptions for this switch from the handshake for an OF1.3
453 * switch.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700454 */
455 protected void setPortDescReply(OFPortDescStatsReply pdrep) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700456 if (ofversion != OFVersion.OF_13)
457 return;
458 if (pdrep == null) {
459 log.error("Error setting ports description for switch: {}", getStringId());
460 return;
461 }
462 portManager.updatePorts(pdrep.getEntries());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700463 }
464
465 @Override
466 public int getNumBuffers() {
467 return buffers;
468 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700469
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700470 @Override
471 public Set<OFActionType> getActions() {
472 return actions;
473 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700474
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700475 @Override
Saurav Dasfcdad072014-08-13 14:15:21 -0700476 public Set<OFCapabilities> getCapabilities() {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700477 return capabilities;
478 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700479
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700480 @Override
481 public byte getNumTables() {
482 return tables;
483 }
484
Saurav Dasfcdad072014-08-13 14:15:21 -0700485 // public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
486 // throws IOException {
487 // // XXX S fix this later
488 // OFMessage request = floodlightProvider.getOFMessageFactory_13()
489 // .buildFeaturesRequest()
490 // .setXid(getNextTransactionId())
491 // .build();
492 // OFFeaturesReplyFuture future =
493 // new OFFeaturesReplyFuture(threadPool, this, (int) request.getXid());
494 // this.featuresFutureMap.put((int) request.getXid(), future);
495 // this.channel.write(Collections.singletonList(request));
496 // return future;
497 //
498 // }
499 //
500 // public void deliverOFFeaturesReply(OFMessage reply) {
501 // OFFeaturesReplyFuture future =
502 // this.featuresFutureMap.get(reply.getXid());
503 // if (future != null) {
504 // future.deliverFuture(this, reply);
505 // // The future will ultimately unregister itself and call
506 // // cancelFeaturesReply
507 // return;
508 // }
509 // log.error("Switch {}: received unexpected featureReply", this);
510 // }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700511
512 @Override
513 public void cancelFeaturesReply(int transactionId) {
514 this.featuresFutureMap.remove(transactionId);
515 }
516
517 @JsonIgnore
518 public void setSwitchDescription(OFDescStatsReply desc) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700519 switchDescription = desc;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700520 }
521
522 @Override
523 @JsonIgnore
524 public OFDescStatsReply getSwitchDescription() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700525 return switchDescription;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700526 }
527
Saurav Dasfcdad072014-08-13 14:15:21 -0700528 // *******************************************
529 // Switch port handling
530 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700531
532 @Override
533 @JsonIgnore
534 public Collection<OFPortDesc> getEnabledPorts() {
535 return portManager.getEnabledPorts();
536 }
537
538 @Override
539 @JsonIgnore
540 public Collection<Integer> getEnabledPortNumbers() {
541 return portManager.getEnabledPortNumbers();
542 }
543
544 @Override
545 public OFPortDesc getPort(int portNumber) {
546 return portManager.getPort(portNumber);
547 }
548
549 @Override
550 public OFPortDesc getPort(String portName) {
551 return portManager.getPort(portName);
552 }
553
554 @Override
555 @JsonIgnore
556 public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) {
557 return portManager.handlePortStatusMessage(ps);
558 }
559
560 @Override
561 @JsonProperty("ports")
562 public Collection<OFPortDesc> getPorts() {
Saurav Dasfcdad072014-08-13 14:15:21 -0700563 return portManager.getPorts();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700564 }
565
566 @Override
567 public boolean portEnabled(int portNumber) {
568 return isEnabled(portManager.getPort(portNumber));
569 }
570
571 @Override
572 public boolean portEnabled(String portName) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700573 return isEnabled(portManager.getPort(portName));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700574 }
575
576 private boolean isEnabled(OFPortDesc p) {
Saurav Dasfcdad072014-08-13 14:15:21 -0700577 return (p != null &&
578 !p.getState().contains(OFPortState.LINK_DOWN) &&
579 !p.getState().contains(OFPortState.BLOCKED) && !p.getConfig().contains(
580 OFPortConfig.PORT_DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700581 }
582
583 @Override
584 public OrderedCollection<PortChangeEvent> comparePorts(Collection<OFPortDesc> ports) {
585 return portManager.comparePorts(ports);
586 }
587
588 @Override
589 @JsonIgnore
590 public OrderedCollection<PortChangeEvent> setPorts(Collection<OFPortDesc> ports) {
591 return portManager.updatePorts(ports);
592 }
593
594 /**
595 * Manages the ports of this switch.
Jonathan Hart23daf192014-08-22 10:56:58 -0700596 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700597 * Provides methods to query and update the stored ports. The class ensures
Saurav Dasfcdad072014-08-13 14:15:21 -0700598 * that every port name and port number is unique. When updating ports the
599 * class checks if port number <-> port name mappings have change due to the
600 * update. If a new port P has number and port that are inconsistent with
601 * the previous mapping(s) the class will delete all previous ports with
602 * name or number of the new port and then add the new port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700603 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700604 * Port names are stored as-is but they are compared case-insensitive
Jonathan Hart23daf192014-08-22 10:56:58 -0700605 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700606 * The methods that change the stored ports return a list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700607 * PortChangeEvents that represent the changes that have been applied to the
608 * port list so that IOFSwitchListeners can be notified about the changes.
Jonathan Hart23daf192014-08-22 10:56:58 -0700609 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700610 * Implementation notes: - We keep several different representations of the
611 * ports to allow for fast lookups - Ports are stored in unchangeable lists.
612 * When a port is modified new data structures are allocated. - We use a
613 * read-write-lock for synchronization, so multiple readers are allowed. -
614 * All port numbers have int representation (no more shorts)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700615 */
616 protected class PortManager {
617 private final ReentrantReadWriteLock lock;
618 private List<OFPortDesc> portList;
619 private List<OFPortDesc> enabledPortList;
620 private List<Integer> enabledPortNumbers;
Saurav Dasfcdad072014-08-13 14:15:21 -0700621 private Map<Integer, OFPortDesc> portsByNumber;
622 private Map<String, OFPortDesc> portsByName;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700623
624 public PortManager() {
625 this.lock = new ReentrantReadWriteLock();
626 this.portList = Collections.emptyList();
627 this.enabledPortList = Collections.emptyList();
628 this.enabledPortNumbers = Collections.emptyList();
629 this.portsByName = Collections.emptyMap();
630 this.portsByNumber = Collections.emptyMap();
631 }
632
633 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700634 * Set the internal data structure storing this switch's port to the
635 * ports specified by newPortsByNumber
Jonathan Hart23daf192014-08-22 10:56:58 -0700636 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700637 * CALLER MUST HOLD WRITELOCK
Jonathan Hart23daf192014-08-22 10:56:58 -0700638 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700639 * @param newPortsByNumber
640 * @throws IllegaalStateException if called without holding the
Saurav Dasfcdad072014-08-13 14:15:21 -0700641 * writelock
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700642 */
643 private void updatePortsWithNewPortsByNumber(
Saurav Dasfcdad072014-08-13 14:15:21 -0700644 Map<Integer, OFPortDesc> newPortsByNumber) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700645 if (!lock.writeLock().isHeldByCurrentThread()) {
646 throw new IllegalStateException("Method called without " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700647 "holding writeLock");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700648 }
Saurav Dasfcdad072014-08-13 14:15:21 -0700649 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700650 new HashMap<String, OFPortDesc>();
651 List<OFPortDesc> newPortList =
652 new ArrayList<OFPortDesc>();
653 List<OFPortDesc> newEnabledPortList =
654 new ArrayList<OFPortDesc>();
655 List<Integer> newEnabledPortNumbers = new ArrayList<Integer>();
656
Saurav Dasfcdad072014-08-13 14:15:21 -0700657 for (OFPortDesc p : newPortsByNumber.values()) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700658 newPortList.add(p);
659 newPortsByName.put(p.getName().toLowerCase(), p);
660 if (isEnabled(p)) {
661 newEnabledPortList.add(p);
662 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
663 }
664 }
665 portsByName = Collections.unmodifiableMap(newPortsByName);
666 portsByNumber =
667 Collections.unmodifiableMap(newPortsByNumber);
668 enabledPortList =
669 Collections.unmodifiableList(newEnabledPortList);
670 enabledPortNumbers =
671 Collections.unmodifiableList(newEnabledPortNumbers);
672 portList = Collections.unmodifiableList(newPortList);
673 }
674
675 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700676 * Handle a OFPortStatus delete message for the given port. Updates the
677 * internal port maps/lists of this switch and returns the
678 * PortChangeEvents caused by the delete. If the given port exists as
679 * it, it will be deleted. If the name<->number for the given port is
680 * inconsistent with the ports stored by this switch the method will
681 * delete all ports with the number or name of the given port.
Jonathan Hart23daf192014-08-22 10:56:58 -0700682 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700683 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700684 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700685 * @param delPort the port from the port status message that should be
686 * deleted.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700687 * @return ordered collection of port changes applied to this switch
688 */
689 private OrderedCollection<PortChangeEvent> handlePortStatusDelete(
Saurav Dasfcdad072014-08-13 14:15:21 -0700690 OFPortDesc delPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700691 lock.writeLock().lock();
692 OrderedCollection<PortChangeEvent> events =
693 new LinkedHashSetWrapper<PortChangeEvent>();
694 try {
Saurav Dasfcdad072014-08-13 14:15:21 -0700695 Map<Integer, OFPortDesc> newPortByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700696 new HashMap<Integer, OFPortDesc>(portsByNumber);
697 OFPortDesc prevPort =
698 portsByNumber.get(delPort.getPortNo().getPortNumber());
699 if (prevPort == null) {
700 // so such port. Do we have a port with the name?
701 prevPort = portsByName.get(delPort.getName());
702 if (prevPort != null) {
703 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
704 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700705 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700706 }
707 } else if (prevPort.getName().equals(delPort.getName())) {
708 // port exists with consistent name-number mapping
709 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
710 events.add(new PortChangeEvent(delPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700711 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700712 } else {
713 // port with same number exists but its name differs. This
714 // is weird. The best we can do is to delete the existing
715 // port(s) that have delPort's name and number.
716 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
717 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700718 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700719 // is there another port that has delPort's name?
720 prevPort = portsByName.get(delPort.getName().toLowerCase());
721 if (prevPort != null) {
722 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
723 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700724 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700725 }
726 }
727 updatePortsWithNewPortsByNumber(newPortByNumber);
728 return events;
729 } finally {
730 lock.writeLock().unlock();
731 }
732 }
733
734 /**
735 * Handle a OFPortStatus message, update the internal data structures
736 * that store ports and return the list of OFChangeEvents.
Jonathan Hart23daf192014-08-22 10:56:58 -0700737 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700738 * This method will increment error/warn counters and log
Jonathan Hart23daf192014-08-22 10:56:58 -0700739 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700740 * @param ps
741 * @return
742 */
743 public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) {
744 if (ps == null) {
745 throw new NullPointerException("OFPortStatus message must " +
Saurav Dasfcdad072014-08-13 14:15:21 -0700746 "not be null");
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700747 }
748 lock.writeLock().lock();
749 try {
750 OFPortReason reason = ps.getReason();
751 if (reason == null) {
752 throw new IllegalArgumentException("Unknown PortStatus " +
753 "reason code " + ps.getReason());
754 }
755
756 if (log.isDebugEnabled()) {
Saurav Das80d17392014-10-01 10:24:56 -0700757 log.debug("Handling OFPortStatus: {} for {} in sw {}",
758 reason, ps, getStringId());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700759 }
760
761 if (reason == OFPortReason.DELETE)
Saurav Dasfcdad072014-08-13 14:15:21 -0700762 return handlePortStatusDelete(ps.getDesc());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700763
764 // We handle ADD and MODIFY the same way. Since OpenFlow
765 // doesn't specify what uniquely identifies a port the
766 // notion of ADD vs. MODIFY can also be hazy. So we just
767 // compare the new port to the existing ones.
Saurav Dasfcdad072014-08-13 14:15:21 -0700768 Map<Integer, OFPortDesc> newPortByNumber =
769 new HashMap<Integer, OFPortDesc>(portsByNumber);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700770 OrderedCollection<PortChangeEvent> events =
Saurav Dasfcdad072014-08-13 14:15:21 -0700771 getSinglePortChanges(ps.getDesc());
772 for (PortChangeEvent e : events) {
773 switch (e.type) {
774 case DELETE:
775 newPortByNumber.remove(e.port.getPortNo().getPortNumber());
776 break;
777 case ADD:
778 if (reason != OFPortReason.ADD) {
779 // weird case
780 }
781 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
782 e.port);
783 break;
784 case DOWN:
785 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
786 e.port);
787 break;
788 case OTHER_UPDATE:
789 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
790 e.port);
791 break;
792 case UP:
793 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
794 e.port);
795 break;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700796 }
797 }
798 updatePortsWithNewPortsByNumber(newPortByNumber);
799 return events;
800 } finally {
801 lock.writeLock().unlock();
802 }
803
804 }
805
806 /**
807 * Given a new or modified port newPort, returns the list of
Saurav Dasfcdad072014-08-13 14:15:21 -0700808 * PortChangeEvents to "transform" the current ports stored by this
809 * switch to include / represent the new port. The ports stored by this
810 * switch are <b>NOT</b> updated.
Jonathan Hart23daf192014-08-22 10:56:58 -0700811 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700812 * This method acquires the readlock and is thread-safe by itself. Most
813 * callers will need to acquire the write lock before calling this
814 * method though (if the caller wants to update the ports stored by this
815 * switch)
Jonathan Hart23daf192014-08-22 10:56:58 -0700816 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700817 * @param newPort the new or modified port.
818 * @return the list of changes
819 */
820 public OrderedCollection<PortChangeEvent> getSinglePortChanges(
Saurav Dasfcdad072014-08-13 14:15:21 -0700821 OFPortDesc newPort) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700822 lock.readLock().lock();
823 try {
824 OrderedCollection<PortChangeEvent> events =
825 new LinkedHashSetWrapper<PortChangeEvent>();
826 // Check if we have a port by the same number in our
827 // old map.
828 OFPortDesc prevPort =
829 portsByNumber.get(newPort.getPortNo().getPortNumber());
830 if (newPort.equals(prevPort)) {
831 // nothing has changed
832 return events;
833 }
834
835 if (prevPort != null &&
836 prevPort.getName().equals(newPort.getName())) {
837 // A simple modify of a existing port
838 // A previous port with this number exists and it's name
839 // also matches the new port. Find the differences
840 if (isEnabled(prevPort) && !isEnabled(newPort)) {
841 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700842 PortChangeType.DOWN));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700843 } else if (!isEnabled(prevPort) && isEnabled(newPort)) {
844 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700845 PortChangeType.UP));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700846 } else {
847 events.add(new PortChangeEvent(newPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700848 PortChangeType.OTHER_UPDATE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700849 }
850 return events;
851 }
852
853 if (prevPort != null) {
854 // There exists a previous port with the same port
855 // number but the port name is different (otherwise we would
856 // never have gotten here)
857 // Remove the port. Name-number mapping(s) have changed
858 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700859 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700860 }
861
862 // We now need to check if there exists a previous port sharing
863 // the same name as the new/updated port.
864 prevPort = portsByName.get(newPort.getName().toLowerCase());
865 if (prevPort != null) {
866 // There exists a previous port with the same port
867 // name but the port number is different (otherwise we
868 // never have gotten here).
869 // Remove the port. Name-number mapping(s) have changed
870 events.add(new PortChangeEvent(prevPort,
Saurav Dasfcdad072014-08-13 14:15:21 -0700871 PortChangeType.DELETE));
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700872 }
873
874 // We always need to add the new port. Either no previous port
875 // existed or we just deleted previous ports with inconsistent
876 // name-number mappings
877 events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
878 return events;
879 } finally {
880 lock.readLock().unlock();
881 }
882 }
883
884 /**
885 * Compare the current ports of this switch to the newPorts list and
886 * return the changes that would be applied to transfort the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700887 * ports to the new ports. No internal data structures are updated see
888 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700889 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700890 * @param newPorts the list of new ports
891 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700892 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700893 */
894 public OrderedCollection<PortChangeEvent> comparePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700895 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700896 return compareAndUpdatePorts(newPorts, false);
897 }
898
899 /**
900 * Compare the current ports of this switch to the newPorts list and
901 * return the changes that would be applied to transform the current
Saurav Dasfcdad072014-08-13 14:15:21 -0700902 * ports to the new ports. No internal data structures are updated see
903 * {@link #compareAndUpdatePorts(List, boolean)}
Jonathan Hart23daf192014-08-22 10:56:58 -0700904 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700905 * @param newPorts the list of new ports
906 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700907 * newPortList
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700908 */
909 public OrderedCollection<PortChangeEvent> updatePorts(
Saurav Dasfcdad072014-08-13 14:15:21 -0700910 Collection<OFPortDesc> newPorts) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700911 return compareAndUpdatePorts(newPorts, true);
912 }
913
914 /**
Saurav Dasfcdad072014-08-13 14:15:21 -0700915 * Compare the current ports stored in this switch instance with the new
916 * port list given and return the differences in the form of
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700917 * PortChangeEvents. If the doUpdate flag is true, newPortList will
918 * replace the current list of this switch (and update the port maps)
Jonathan Hart23daf192014-08-22 10:56:58 -0700919 *
Saurav Dasfcdad072014-08-13 14:15:21 -0700920 * Implementation note: Since this method can optionally modify the
921 * current ports and since it's not possible to upgrade a read-lock to a
922 * write-lock we need to hold the write-lock for the entire operation.
923 * If this becomes a problem and if compares() are common we can
924 * consider splitting in two methods but this requires lots of code
925 * duplication
Jonathan Hart23daf192014-08-22 10:56:58 -0700926 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700927 * @param newPorts the list of new ports.
Saurav Dasfcdad072014-08-13 14:15:21 -0700928 * @param doUpdate If true the newPortList will replace the current port
929 * list for this switch. If false this switch will not be
930 * changed.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700931 * @return The list of differences between the current ports and
Saurav Dasfcdad072014-08-13 14:15:21 -0700932 * newPorts
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700933 * @throws NullPointerException if newPortsList is null
934 * @throws IllegalArgumentException if either port names or port numbers
Saurav Dasfcdad072014-08-13 14:15:21 -0700935 * are duplicated in the newPortsList.
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700936 */
937 private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
938 Collection<OFPortDesc> newPorts, boolean doUpdate) {
939 if (newPorts == null) {
940 throw new NullPointerException("newPortsList must not be null");
941 }
942 lock.writeLock().lock();
943 try {
944 OrderedCollection<PortChangeEvent> events =
945 new LinkedHashSetWrapper<PortChangeEvent>();
946
Saurav Dasfcdad072014-08-13 14:15:21 -0700947 Map<Integer, OFPortDesc> newPortsByNumber =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700948 new HashMap<Integer, OFPortDesc>();
Saurav Dasfcdad072014-08-13 14:15:21 -0700949 Map<String, OFPortDesc> newPortsByName =
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700950 new HashMap<String, OFPortDesc>();
951 List<OFPortDesc> newEnabledPortList =
952 new ArrayList<OFPortDesc>();
953 List<Integer> newEnabledPortNumbers =
954 new ArrayList<Integer>();
955 List<OFPortDesc> newPortsList =
956 new ArrayList<OFPortDesc>(newPorts);
957
Saurav Dasfcdad072014-08-13 14:15:21 -0700958 for (OFPortDesc p : newPortsList) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700959 if (p == null) {
960 throw new NullPointerException("portList must not " +
961 "contain null values");
962 }
963
964 // Add the port to the new maps and lists and check
965 // that every port is unique
966 OFPortDesc duplicatePort;
967 duplicatePort = newPortsByNumber.put(
Saurav Dasfcdad072014-08-13 14:15:21 -0700968 p.getPortNo().getPortNumber(), p);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700969 if (duplicatePort != null) {
970 String msg = String.format("Cannot have two ports " +
971 "with the same number: %s <-> %s",
972 p, duplicatePort);
973 throw new IllegalArgumentException(msg);
974 }
975 duplicatePort =
976 newPortsByName.put(p.getName().toLowerCase(), p);
977 if (duplicatePort != null) {
978 String msg = String.format("Cannot have two ports " +
979 "with the same name: %s <-> %s",
Saurav Dasfcdad072014-08-13 14:15:21 -0700980 p.toString().substring(0, 80),
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700981 duplicatePort.toString().substring(0, 80));
982 throw new IllegalArgumentException(msg);
983 }
984 if (isEnabled(p)) {
985 newEnabledPortList.add(p);
986 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
987 }
988
989 // get changes
990 events.addAll(getSinglePortChanges(p));
991 }
992 // find deleted ports
993 // We need to do this after looping through all the new ports
994 // to we can handle changed name<->number mappings correctly
995 // We could pull it into the loop of we address this but
996 // it's probably not worth it
Saurav Dasfcdad072014-08-13 14:15:21 -0700997 for (OFPortDesc oldPort : this.portList) {
998 if (!newPortsByNumber
999 .containsKey(oldPort.getPortNo().getPortNumber())) {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001000 PortChangeEvent ev =
1001 new PortChangeEvent(oldPort,
Saurav Dasfcdad072014-08-13 14:15:21 -07001002 PortChangeType.DELETE);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001003 events.add(ev);
1004 }
1005 }
1006
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001007 if (doUpdate) {
1008 portsByName = Collections.unmodifiableMap(newPortsByName);
1009 portsByNumber =
1010 Collections.unmodifiableMap(newPortsByNumber);
1011 enabledPortList =
1012 Collections.unmodifiableList(newEnabledPortList);
1013 enabledPortNumbers =
1014 Collections.unmodifiableList(newEnabledPortNumbers);
1015 portList = Collections.unmodifiableList(newPortsList);
1016 }
1017 return events;
1018 } finally {
1019 lock.writeLock().unlock();
1020 }
1021 }
1022
1023 public OFPortDesc getPort(String name) {
1024 if (name == null) {
1025 throw new NullPointerException("Port name must not be null");
1026 }
1027 lock.readLock().lock();
1028 try {
1029 return portsByName.get(name.toLowerCase());
1030 } finally {
1031 lock.readLock().unlock();
1032 }
1033 }
1034
1035 public OFPortDesc getPort(int portNumber) {
1036 lock.readLock().lock();
1037 try {
1038 return portsByNumber.get(portNumber);
1039 } finally {
1040 lock.readLock().unlock();
1041 }
1042 }
1043
1044 public List<OFPortDesc> getPorts() {
1045 lock.readLock().lock();
1046 try {
1047 return portList;
1048 } finally {
1049 lock.readLock().unlock();
1050 }
1051 }
1052
1053 public List<OFPortDesc> getEnabledPorts() {
1054 lock.readLock().lock();
1055 try {
1056 return enabledPortList;
1057 } finally {
1058 lock.readLock().unlock();
1059 }
1060 }
1061
1062 public List<Integer> getEnabledPortNumbers() {
1063 lock.readLock().lock();
1064 try {
1065 return enabledPortNumbers;
1066 } finally {
1067 lock.readLock().unlock();
1068 }
1069 }
1070 }
1071
Saurav Dasfcdad072014-08-13 14:15:21 -07001072 // *******************************************
1073 // Switch stats handling
1074 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001075
1076 @Override
1077 public Future<List<OFStatsReply>> getStatistics(OFStatsRequest<?> request)
Saurav Dasfcdad072014-08-13 14:15:21 -07001078 throws IOException {
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001079 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this,
Saurav Dasfcdad072014-08-13 14:15:21 -07001080 (int) request.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001081 log.info("description STAT REQUEST XID {}", request.getXid());
Saurav Dasfcdad072014-08-13 14:15:21 -07001082 this.statsFutureMap.put((int) request.getXid(), future);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001083
1084 this.channel.write(Collections.singletonList(request));
1085 return future;
1086 }
1087
Saurav Das91890e42014-10-03 15:54:21 -07001088 @SuppressWarnings("unused")
1089 private void analyzeStatsReply(OFMessage reply) {
1090 log.info("recieved stats reply (xid = {} type: {}) from sw {} ",
1091 reply.getXid(), reply.getType(), getStringId());
1092 if (reply.getType() == OFType.STATS_REPLY) {
1093 OFStatsReply sr = (OFStatsReply) reply;
1094 if (sr.getStatsType() == OFStatsType.FLOW) {
1095 OFFlowStatsReply fsr = (OFFlowStatsReply) sr;
1096 log.info("received flow stats sw {} --> {}", getStringId(), fsr);
1097 // fsr.getEntries().get(0).getMatch().getMatchFields()
1098 for (OFFlowStatsEntry e : fsr.getEntries()) {
1099 for (MatchField<?> mf : e.getMatch().getMatchFields()) {
1100 log.info("mf is exact: {} for {}: {}",
1101 e.getMatch().isExact(mf),
1102 mf.id,
1103 e.getMatch().get(mf));
1104 }
1105 }
1106
1107 }
1108 }
1109 }
1110
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001111 @Override
1112 public void deliverStatisticsReply(OFMessage reply) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001113 OFStatisticsFuture future = this.statsFutureMap.get((int) reply.getXid());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001114 if (future != null) {
1115 future.deliverFuture(this, reply);
1116 // The future will ultimately unregister itself and call
1117 // cancelStatisticsReply
1118 return;
1119 }
1120 // Transaction id was not found in statsFutureMap.check the other map
1121 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
1122 if (caller != null) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001123 caller.receive(this, reply, null);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001124 }
1125 }
1126
1127 @Override
1128 public void cancelStatisticsReply(int transactionId) {
1129 if (null == this.statsFutureMap.remove(transactionId)) {
1130 this.iofMsgListenersMap.remove(transactionId);
1131 }
1132 }
1133
1134 @Override
1135 public void cancelAllStatisticsReplies() {
1136 /* we don't need to be synchronized here. Even if another thread
1137 * modifies the map while we're cleaning up the future will eventuall
1138 * timeout */
1139 for (OFStatisticsFuture f : statsFutureMap.values()) {
1140 f.cancel(true);
1141 }
1142 statsFutureMap.clear();
1143 iofMsgListenersMap.clear();
1144 }
1145
Saurav Dasfcdad072014-08-13 14:15:21 -07001146 // *******************************************
1147 // Switch role handling
1148 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001149
1150 // XXX S this is completely wrong. The generation id should be obtained
1151 // after coordinating with all controllers connected to this switch.
1152 // ie. should be part of registry service (account for 1.3 vs 1.0)
1153 // For now we are just generating this locally and keeping it constant.
1154 public U64 getNextGenerationId() {
Saurav Dasfcdad072014-08-13 14:15:21 -07001155 // TODO: Pankaj, fix nextGenerationId as part of Registry interface
1156 return U64.of(generationIdSource);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001157 }
1158
1159 @Override
1160 public Role getRole() {
1161 return role;
1162 }
1163
1164 @JsonIgnore
1165 @Override
1166 public void setRole(Role role) {
1167 this.role = role;
1168 }
1169
Saurav Dasfcdad072014-08-13 14:15:21 -07001170 // *******************************************
1171 // Switch utility methods
1172 // *******************************************
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001173
1174 private void registerOverloadCounters() throws CounterException {
1175 if (debugCountersRegistered) {
1176 return;
1177 }
1178 if (debugCounters == null) {
1179 log.error("Debug Counter Service not found");
1180 debugCounters = new NullDebugCounter();
1181 debugCountersRegistered = true;
1182 }
1183 // every level of the hierarchical counter has to be registered
1184 // even if they are not used
1185 ctrSwitch = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001186 BASE, stringId,
1187 "Counter for this switch",
1188 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001189 ctrSwitchPktin = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001190 BASE, stringId + "/pktin",
1191 "Packet in counter for this switch",
1192 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001193 ctrSwitchWrite = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001194 BASE, stringId + "/write",
1195 "Write counter for this switch",
1196 CounterType.ALWAYS_COUNT);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001197 ctrSwitchPktinDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001198 BASE, stringId + "/pktin/drops",
1199 "Packet in throttle drop count",
1200 CounterType.ALWAYS_COUNT,
1201 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001202 ctrSwitchWriteDrops = debugCounters.registerCounter(
Saurav Dasfcdad072014-08-13 14:15:21 -07001203 BASE, stringId + "/write/drops",
1204 "Switch write throttle drop count",
1205 CounterType.ALWAYS_COUNT,
1206 IDebugCounterService.CTR_MDATA_WARN);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001207 }
1208
1209 @Override
1210 public void startDriverHandshake() throws IOException {
Saurav Dasfcdad072014-08-13 14:15:21 -07001211 log.debug("Starting driver handshake for sw {}", getStringId());
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001212 if (startDriverHandshakeCalled)
1213 throw new SwitchDriverSubHandshakeAlreadyStarted();
1214 startDriverHandshakeCalled = true;
1215 }
1216
1217 @Override
1218 public boolean isDriverHandshakeComplete() {
1219 if (!startDriverHandshakeCalled)
1220 throw new SwitchDriverSubHandshakeNotStarted();
1221 return true;
1222 }
1223
1224 @Override
1225 public void processDriverHandshakeMessage(OFMessage m) {
1226 if (startDriverHandshakeCalled)
1227 throw new SwitchDriverSubHandshakeCompleted(m);
1228 else
1229 throw new SwitchDriverSubHandshakeNotStarted();
1230 }
1231
1232 @Override
1233 @JsonIgnore
Saurav Dasfcdad072014-08-13 14:15:21 -07001234 @LogMessageDoc(level = "WARN",
1235 message = "Switch {switch} flow table is full",
1236 explanation = "The controller received flow table full " +
1237 "message from the switch, could be caused by increased " +
1238 "traffic pattern",
1239 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001240 public void setTableFull(boolean isFull) {
1241 if (isFull && !flowTableFull) {
1242 floodlightProvider.addSwitchEvent(this.datapathId.getLong(),
1243 "SWITCH_FLOW_TABLE_FULL " +
Saurav Dasfcdad072014-08-13 14:15:21 -07001244 "Table full error from switch", false);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001245 log.warn("Switch {} flow table is full", stringId);
1246 }
Saurav Dasfcdad072014-08-13 14:15:21 -07001247 flowTableFull = isFull;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001248 }
1249
1250 @Override
1251 @LogMessageDoc(level = "ERROR",
1252 message = "Failed to clear all flows on switch {switch}",
1253 explanation = "An I/O error occured while trying to clear " +
1254 "flows on the switch.",
1255 recommendation = LogMessageDoc.CHECK_SWITCH)
1256 public void clearAllFlowMods() {
1257 // Delete all pre-existing flows
1258
Jonathan Harta213bce2014-08-11 15:44:07 -07001259 // by default if match is not specified, then an empty list of matches
1260 // is sent, resulting in a wildcard-all flows
Jonathan Hart23daf192014-08-22 10:56:58 -07001261 Builder builder = getFactory()
Saurav Dasfcdad072014-08-13 14:15:21 -07001262 .buildFlowDelete()
Jonathan Hart23daf192014-08-22 10:56:58 -07001263 .setOutPort(OFPort.ANY);
1264
1265 if (ofversion.wireVersion >= OFVersion.OF_13.wireVersion) {
1266 builder.setOutGroup(OFGroup.ANY)
1267 .setTableId(TableId.ALL);
1268 }
1269
1270 OFMessage fm = builder.build();
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001271
1272 try {
1273 channel.write(Collections.singletonList(fm));
1274 } catch (Exception e) {
1275 log.error("Failed to clear all flows on switch " + this, e);
1276 }
1277 }
1278
1279 @Override
1280 public void flush() {
1281 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1282 List<OFMessage> msglist = msg_buffer_map.get(this);
1283 if ((msglist != null) && (msglist.size() > 0)) {
1284 try {
1285 this.write(msglist);
1286 } catch (IOException e) {
Saurav Dasfcdad072014-08-13 14:15:21 -07001287 log.error("Failed flushing messages", e);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001288 }
1289 msglist.clear();
1290 }
1291 }
1292
1293 public static void flush_all() {
1294 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1295 for (OFSwitchImplBase sw : msg_buffer_map.keySet()) {
1296 sw.flush();
1297 }
1298 }
1299
1300 /**
1301 * Return a read lock that must be held while calling the listeners for
1302 * messages from the switch. Holding the read lock prevents the active
1303 * switch list from being modified out from under the listeners.
Jonathan Hart23daf192014-08-22 10:56:58 -07001304 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001305 * @return listener read lock
1306 */
1307 @JsonIgnore
1308 public Lock getListenerReadLock() {
1309 return listenerLock.readLock();
1310 }
1311
1312 /**
1313 * Return a write lock that must be held when the controllers modifies the
1314 * list of active switches. This is to ensure that the active switch list
1315 * doesn't change out from under the listeners as they are handling a
1316 * message from the switch.
Jonathan Hart23daf192014-08-22 10:56:58 -07001317 *
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001318 * @return listener write lock
1319 */
1320 @JsonIgnore
1321 public Lock getListenerWriteLock() {
1322 return listenerLock.writeLock();
1323 }
Saurav Dasfc5e3eb2014-09-25 19:05:21 -07001324
1325 public String getSwitchDriverState() {
1326 return "";
1327 }
1328
Saurav Dasd84178f2014-09-29 17:48:54 -07001329 public OFBarrierReplyFuture sendBarrier() throws IOException {
1330 long xid = getNextTransactionId();
1331 OFMessage br = getFactory()
1332 .buildBarrierRequest()
1333 .setXid(xid)
1334 .build();
1335 write(Collections.singletonList(br));
1336 OFBarrierReplyFuture future = new OFBarrierReplyFuture(threadPool, this,
1337 (int) xid);
1338 barrierFutureMap.put(xid, future);
1339 return future;
1340 }
1341
1342 public void deliverBarrierReply(OFBarrierReply br) {
1343 OFBarrierReplyFuture f = barrierFutureMap.get(br.getXid());
1344 if (f != null) {
1345 f.deliverFuture(this, br);
1346 barrierFutureMap.remove(br.getXid());
1347 }
1348 }
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001349}