blob: 1b2b0cfa1a045b8067e92130c72a4c625af0c6ad [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;
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.Date;
25import java.util.HashMap;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30import java.util.WeakHashMap;
31import java.util.concurrent.ConcurrentHashMap;
32import java.util.concurrent.ConcurrentMap;
33import java.util.concurrent.Future;
34import java.util.concurrent.atomic.AtomicInteger;
35import java.util.concurrent.locks.Lock;
36import java.util.concurrent.locks.ReentrantReadWriteLock;
37
38import net.floodlightcontroller.core.FloodlightContext;
39import net.floodlightcontroller.core.IFloodlightProviderService;
40import net.floodlightcontroller.core.IFloodlightProviderService.Role;
41import net.floodlightcontroller.core.IOFMessageListener;
42import net.floodlightcontroller.core.IOFSwitch;
43import net.floodlightcontroller.core.SwitchDriverSubHandshakeAlreadyStarted;
44import net.floodlightcontroller.core.SwitchDriverSubHandshakeCompleted;
45import net.floodlightcontroller.core.SwitchDriverSubHandshakeNotStarted;
46import net.floodlightcontroller.core.annotations.LogMessageDoc;
47import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
48import net.floodlightcontroller.debugcounter.IDebugCounter;
49import net.floodlightcontroller.debugcounter.IDebugCounterService;
50import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
51import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
52import net.floodlightcontroller.debugcounter.NullDebugCounter;
53import net.floodlightcontroller.threadpool.IThreadPoolService;
54import net.floodlightcontroller.util.LinkedHashSetWrapper;
55import net.floodlightcontroller.util.OrderedCollection;
56import net.floodlightcontroller.util.TimedCache;
57
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;
63import org.projectfloodlight.openflow.protocol.OFCapabilities;
64import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
Jonathan Harta213bce2014-08-11 15:44:07 -070065import org.projectfloodlight.openflow.protocol.OFFactories;
66import org.projectfloodlight.openflow.protocol.OFFactory;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -070067import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
68import org.projectfloodlight.openflow.protocol.OFMessage;
69import org.projectfloodlight.openflow.protocol.OFPortConfig;
70import org.projectfloodlight.openflow.protocol.OFPortDesc;
71import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
72import org.projectfloodlight.openflow.protocol.OFPortReason;
73import org.projectfloodlight.openflow.protocol.OFPortState;
74import org.projectfloodlight.openflow.protocol.OFPortStatus;
75import org.projectfloodlight.openflow.protocol.OFStatsReply;
76import org.projectfloodlight.openflow.protocol.OFStatsRequest;
77import org.projectfloodlight.openflow.protocol.OFType;
78import org.projectfloodlight.openflow.protocol.OFVersion;
79import org.projectfloodlight.openflow.types.DatapathId;
80import org.projectfloodlight.openflow.types.OFAuxId;
81import org.projectfloodlight.openflow.types.OFGroup;
82import org.projectfloodlight.openflow.types.OFPort;
83import org.projectfloodlight.openflow.types.TableId;
84import org.projectfloodlight.openflow.types.U64;
85import org.slf4j.Logger;
86import org.slf4j.LoggerFactory;
87
88
89/**
90 * This is the internal representation of an openflow switch.
91 */
92public class OFSwitchImplBase implements IOFSwitch {
93 // TODO: should we really do logging in the class or should we throw
94 // exception that can then be handled by callers?
95 protected final static Logger log = LoggerFactory.getLogger(OFSwitchImplBase.class);
96
97 private static final String HA_CHECK_SWITCH =
98 "Check the health of the indicated switch. If the problem " +
99 "persists or occurs repeatedly, it likely indicates a defect " +
100 "in the switch HA implementation.";
101
102 protected ConcurrentMap<Object, Object> attributes;
103 protected IFloodlightProviderService floodlightProvider;
104 protected IThreadPoolService threadPool;
105 protected Date connectedSince;
106 protected String stringId;
107 protected Channel channel;
108 // transaction id used for messages sent out to this switch from
109 // this controller instance. This xid has significance only between this
110 // controller<->switch pair.
111 protected AtomicInteger transactionIdSource;
112
113 // generation id used for roleRequest messages sent to switches (see section
114 // 6.3.5 of the OF1.3.4 spec). This generationId has significance between
115 // all the controllers that this switch is connected to; and only for role
116 // request messages with role MASTER or SLAVE. The set of Controllers that
117 // this switch is connected to should coordinate the next generation id,
118 // via transactional semantics.
119 protected long generationIdSource;
120
121 // Lock to protect modification of the port maps. We only need to
122 // synchronize on modifications. For read operations we are fine since
123 // we rely on ConcurrentMaps which works for our use case.
124 //private Object portLock; XXX S remove this
125
126 // Map port numbers to the appropriate OFPortDesc
127 protected ConcurrentHashMap<Integer, OFPortDesc> portsByNumber;
128 // Map port names to the appropriate OFPhyiscalPort
129 // XXX: The OF spec doesn't specify if port names need to be unique but
130 // according it's always the case in practice.
131 protected ConcurrentHashMap<String, OFPortDesc> portsByName;
132 protected Map<Integer, OFStatisticsFuture> statsFutureMap;
133 protected Map<Integer, IOFMessageListener> iofMsgListenersMap; // XXX S why is this needed?
134 protected Map<Integer, OFFeaturesReplyFuture> featuresFutureMap;
135 protected boolean connected;
136 protected Role role;
137 protected TimedCache<Long> timedCache;
138 protected ReentrantReadWriteLock listenerLock;
139 protected ConcurrentMap<Short, Long> portBroadcastCacheHitMap;
140 /**
141 * When sending a role request message, the role request is added
142 * to this queue. If a role reply is received this queue is checked to
143 * verify that the reply matches the expected reply. We require in order
144 * delivery of replies. That's why we use a Queue.
145 * The RoleChanger uses a timeout to ensure we receive a timely reply.
146 * <p/>
147 * Need to synchronize on this instance if a request is sent, received,
148 * checked.
149 */
150 protected LinkedList<PendingRoleRequestEntry> pendingRoleRequests;
151
152 /** OpenFlow version for this switch */
153 protected OFVersion ofversion;
154 // Description stats reply describing this switch
155 private OFDescStatsReply switchDescription;
156 // Switch features from initial featuresReply
157 protected Set<OFCapabilities> capabilities;
158 protected int buffers;
159 protected Set<OFActionType> actions;
160 protected byte tables;
161 protected DatapathId datapathId;
162 private OFAuxId auxId;
163
164 private IDebugCounterService debugCounters;
165 private boolean debugCountersRegistered;
166 @SuppressWarnings("unused")
167 private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite,
168 ctrSwitchPktinDrops, ctrSwitchWriteDrops;
169
170 protected boolean startDriverHandshakeCalled = false;
171 private boolean flowTableFull = false;
172
173 private final PortManager portManager;
174
175
176
177 protected static final ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>> local_msg_buffer =
178 new ThreadLocal<Map<OFSwitchImplBase, List<OFMessage>>>() {
179 @Override
180 protected Map<OFSwitchImplBase, List<OFMessage>> initialValue() {
181 return new WeakHashMap<OFSwitchImplBase, List<OFMessage>>();
182 }
183 };
184
185
186 private static final String BASE = "switchbase";
187
188 protected static class PendingRoleRequestEntry {
189 protected int xid;
190 protected Role role;
191 // cookie is used to identify the role "generation". roleChanger uses
192 protected long cookie;
193
194 public PendingRoleRequestEntry(int xid, Role role, long cookie) {
195 this.xid = xid;
196 this.role = role;
197 this.cookie = cookie;
198 }
199 }
200
201 public OFSwitchImplBase() {
202 this.stringId = null;
203 this.attributes = new ConcurrentHashMap<Object, Object>();
204 this.connectedSince = new Date();
205 this.transactionIdSource = new AtomicInteger();
206 this.generationIdSource = 0; // XXX S this is wrong; should be negotiated
207 // XXX S no need this.portLock = new Object();
208 this.portsByNumber = new ConcurrentHashMap<Integer, OFPortDesc>();
209 this.portsByName = new ConcurrentHashMap<String, OFPortDesc>();
210 this.connected = true;
211 this.statsFutureMap = new ConcurrentHashMap<Integer, OFStatisticsFuture>();
212 this.featuresFutureMap = new ConcurrentHashMap<Integer, OFFeaturesReplyFuture>();
213 this.iofMsgListenersMap = new ConcurrentHashMap<Integer, IOFMessageListener>();
214 this.role = null;
215 this.timedCache = new TimedCache<Long>(100, 5 * 1000); // 5 seconds interval
216 this.listenerLock = new ReentrantReadWriteLock();
217 this.pendingRoleRequests = new LinkedList<OFSwitchImplBase.PendingRoleRequestEntry>();
218 this.portManager = new PortManager();
219 // by default the base impl declares no support for Nx_role_requests.
220 // OF1.0 switches like OVS that do support these messages should set the
221 // attribute in the associated switch driver.
222 setAttribute(SWITCH_SUPPORTS_NX_ROLE, false);
223
224 }
225
226 //*******************************************
227 // Setters and Getters
228 //*******************************************
229
230 @Override
231 public Object getAttribute(String name) {
232 if (this.attributes.containsKey(name)) {
233 return this.attributes.get(name);
234 }
235 return null;
236 }
237
238 @Override
239 public ConcurrentMap<Object, Object> getAttributes() {
240 return this.attributes;
241 }
242
243 @Override
244 public void setAttribute(String name, Object value) {
245 this.attributes.put(name, value);
246 return;
247 }
248
249 @Override
250 public Object removeAttribute(String name) {
251 return this.attributes.remove(name);
252 }
253
254 @Override
255 public boolean hasAttribute(String name) {
256 return this.attributes.containsKey(name);
257 }
258
259 @Override
260 @JsonSerialize(using = DPIDSerializer.class)
261 @JsonProperty("dpid")
262 public long getId() {
263 if (this.stringId == null)
264 throw new RuntimeException("Features reply has not yet been set");
265 return this.datapathId.getLong();
266 }
267
268 @JsonIgnore
269 @Override
270 public String getStringId() {
271 return stringId;
272 }
273
Jonathan Harta213bce2014-08-11 15:44:07 -0700274 @Override
275 public OFFactory getFactory() {
276 return OFFactories.getFactory(ofversion);
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700277 }
278
Jonathan Harta213bce2014-08-11 15:44:07 -0700279 @Override
280 public OFVersion getOFVersion() {
281 return ofversion;
282 }
283
284 @Override
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700285 public void setOFVersion(OFVersion ofv) {
Jonathan Harta213bce2014-08-11 15:44:07 -0700286 ofversion = ofv;
Brian O'Connorc67f9fa2014-08-07 18:17:46 -0700287 }
288
289 /**
290 * @param floodlightProvider the floodlightProvider to set
291 */
292 @JsonIgnore
293 @Override
294 public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) {
295 this.floodlightProvider = floodlightProvider;
296 }
297
298 @JsonIgnore
299 @Override
300 public void setThreadPoolService(IThreadPoolService tp) {
301 this.threadPool = tp;
302 }
303
304 @Override
305 @JsonIgnore
306 public void setDebugCounterService(IDebugCounterService debugCounters)
307 throws CounterException {
308 this.debugCounters = debugCounters;
309 registerOverloadCounters();
310 }
311
312 /* (non-Javadoc)
313 * @see java.lang.Object#toString()
314 */
315 @Override
316 public String toString() {
317 return "OFSwitchImpl [" + ((channel != null) ? channel.getRemoteAddress() : "?")
318 + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
319 }
320
321
322 //*******************************************
323 // Channel related methods
324 //*******************************************
325
326 @JsonIgnore
327 @Override
328 public void setChannel(Channel channel) {
329 this.channel = channel;
330 }
331
332 @Override
333 public void write(OFMessage m, FloodlightContext bc) throws IOException {
334 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
335 List<OFMessage> msg_buffer = msg_buffer_map.get(this);
336 if (msg_buffer == null) {
337 msg_buffer = new ArrayList<OFMessage>();
338 msg_buffer_map.put(this, msg_buffer);
339 }
340 // XXX S will change when iFloodlight provider changes
341 //this.floodlightProvider.handleOutgoingMessage(this, m, bc);
342 msg_buffer.add(m);
343
344 if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
345 ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
346 this.write(msg_buffer);
347 msg_buffer.clear();
348 }
349 }
350
351 @Override
352 @LogMessageDoc(level = "WARN",
353 message = "Sending OF message that modifies switch " +
354 "state while in the slave role: {switch}",
355 explanation = "An application has sent a message to a switch " +
356 "that is not valid when the switch is in a slave role",
357 recommendation = LogMessageDoc.REPORT_CONTROLLER_BUG)
358 public void write(List<OFMessage> msglist,
359 FloodlightContext bc) throws IOException {
360 for (OFMessage m : msglist) {
361 if (role == Role.SLAVE) {
362 switch (m.getType()) {
363 case PACKET_OUT:
364 case FLOW_MOD:
365 case PORT_MOD:
366 log.warn("Sending OF message that modifies switch " +
367 "state while in the slave role: {}",
368 m.getType().name());
369 break;
370 default:
371 break;
372 }
373 }
374 // XXX S again
375 //this.floodlightProvider.handleOutgoingMessage(this, m, bc);
376 }
377 this.write(msglist);
378 }
379
380 public void write(List<OFMessage> msglist) throws IOException {
381 this.channel.write(msglist);
382 }
383
384 @Override
385 public void disconnectSwitch() {
386 channel.close();
387 }
388
389 @Override
390 public Date getConnectedSince() {
391 return connectedSince;
392 }
393
394 @JsonIgnore
395 @Override
396 public int getNextTransactionId() {
397 return this.transactionIdSource.incrementAndGet();
398 }
399
400 @JsonIgnore
401 @Override
402 public synchronized boolean isConnected() {
403 return connected;
404 }
405
406 @Override
407 @JsonIgnore
408 public synchronized void setConnected(boolean connected) {
409 this.connected = connected;
410 }
411
412
413
414 //*******************************************
415 // Switch features related methods
416 //*******************************************
417
418 /**
419 * Set the features reply for this switch from the handshake
420 */
421 protected void setFeaturesReply(OFFeaturesReply featuresReply) {
422 if (featuresReply == null) {
423 log.error("Error setting featuresReply for switch: {}", getStringId());
424 return;
425 }
426 this.datapathId = featuresReply.getDatapathId();
427 this.capabilities = featuresReply.getCapabilities();
428 this.buffers = (int) featuresReply.getNBuffers();
429 this.tables = (byte) featuresReply.getNTables();
430 this.stringId = this.datapathId.toString();
431 if (ofversion == OFVersion.OF_13) {
432 auxId = featuresReply.getAuxiliaryId();
433 if (!auxId.equals(OFAuxId.MAIN)) {
434 log.warn("This controller does not handle auxiliary connections. "
435 + "Aux connection id {} received from switch {}",
436 auxId, getStringId());
437 }
438 }
439
440 if (ofversion == OFVersion.OF_10) {
441 this.actions = featuresReply.getActions();
442 portManager.compareAndUpdatePorts(featuresReply.getPorts(), true);
443 }
444 }
445
446 /**
447 * Set the port descriptions for this switch from the handshake for
448 * an OF1.3 switch.
449 */
450 protected void setPortDescReply(OFPortDescStatsReply pdrep) {
451 if (ofversion != OFVersion.OF_13) return;
452 if (pdrep == null) {
453 log.error("Error setting ports description for switch: {}", getStringId());
454 return;
455 }
456 portManager.updatePorts(pdrep.getEntries());
457 }
458
459 @Override
460 public int getNumBuffers() {
461 return buffers;
462 }
463 @Override
464 public Set<OFActionType> getActions() {
465 return actions;
466 }
467 @Override
468 public Set<OFCapabilities> getCapabilities() {
469 return capabilities;
470 }
471 @Override
472 public byte getNumTables() {
473 return tables;
474 }
475
476// public Future<OFFeaturesReply> getFeaturesReplyFromSwitch()
477// throws IOException {
478// // XXX S fix this later
479// OFMessage request = floodlightProvider.getOFMessageFactory_13()
480// .buildFeaturesRequest()
481// .setXid(getNextTransactionId())
482// .build();
483// OFFeaturesReplyFuture future =
484// new OFFeaturesReplyFuture(threadPool, this, (int) request.getXid());
485// this.featuresFutureMap.put((int) request.getXid(), future);
486// this.channel.write(Collections.singletonList(request));
487// return future;
488//
489// }
490//
491// public void deliverOFFeaturesReply(OFMessage reply) {
492// OFFeaturesReplyFuture future = this.featuresFutureMap.get(reply.getXid());
493// if (future != null) {
494// future.deliverFuture(this, reply);
495// // The future will ultimately unregister itself and call
496// // cancelFeaturesReply
497// return;
498// }
499// log.error("Switch {}: received unexpected featureReply", this);
500// }
501
502 @Override
503 public void cancelFeaturesReply(int transactionId) {
504 this.featuresFutureMap.remove(transactionId);
505 }
506
507 @JsonIgnore
508 public void setSwitchDescription(OFDescStatsReply desc) {
509 switchDescription = desc;
510 }
511
512 @Override
513 @JsonIgnore
514 public OFDescStatsReply getSwitchDescription() {
515 return switchDescription;
516 }
517
518 //*******************************************
519 // Switch port handling
520 //*******************************************
521
522 @Override
523 @JsonIgnore
524 public Collection<OFPortDesc> getEnabledPorts() {
525 return portManager.getEnabledPorts();
526 }
527
528 @Override
529 @JsonIgnore
530 public Collection<Integer> getEnabledPortNumbers() {
531 return portManager.getEnabledPortNumbers();
532 }
533
534 @Override
535 public OFPortDesc getPort(int portNumber) {
536 return portManager.getPort(portNumber);
537 }
538
539 @Override
540 public OFPortDesc getPort(String portName) {
541 return portManager.getPort(portName);
542 }
543
544 @Override
545 @JsonIgnore
546 public OrderedCollection<PortChangeEvent> processOFPortStatus(OFPortStatus ps) {
547 return portManager.handlePortStatusMessage(ps);
548 }
549
550 @Override
551 @JsonProperty("ports")
552 public Collection<OFPortDesc> getPorts() {
553 return portManager.getPorts();
554 }
555
556 @Override
557 public boolean portEnabled(int portNumber) {
558 return isEnabled(portManager.getPort(portNumber));
559 }
560
561 @Override
562 public boolean portEnabled(String portName) {
563 return isEnabled(portManager.getPort(portName));
564 }
565
566 private boolean isEnabled(OFPortDesc p) {
567 return (p != null &&
568 !p.getState().contains(OFPortState.LINK_DOWN) &&
569 !p.getState().contains(OFPortState.BLOCKED) &&
570 !p.getConfig().contains(OFPortConfig.PORT_DOWN));
571 }
572
573 @Override
574 public OrderedCollection<PortChangeEvent> comparePorts(Collection<OFPortDesc> ports) {
575 return portManager.comparePorts(ports);
576 }
577
578 @Override
579 @JsonIgnore
580 public OrderedCollection<PortChangeEvent> setPorts(Collection<OFPortDesc> ports) {
581 return portManager.updatePorts(ports);
582 }
583
584 /**
585 * Manages the ports of this switch.
586 *
587 * Provides methods to query and update the stored ports. The class ensures
588 * that every port name and port number is unique. When updating ports
589 * the class checks if port number <-> port name mappings have change due
590 * to the update. If a new port P has number and port that are inconsistent
591 * with the previous mapping(s) the class will delete all previous ports
592 * with name or number of the new port and then add the new port.
593 *
594 * Port names are stored as-is but they are compared case-insensitive
595 *
596 * The methods that change the stored ports return a list of
597 * PortChangeEvents that represent the changes that have been applied
598 * to the port list so that IOFSwitchListeners can be notified about the
599 * changes.
600 *
601 * Implementation notes:
602 * - We keep several different representations of the ports to allow for
603 * fast lookups
604 * - Ports are stored in unchangeable lists. When a port is modified new
605 * data structures are allocated.
606 * - We use a read-write-lock for synchronization, so multiple readers are
607 * allowed.
608 * - All port numbers have int representation (no more shorts)
609 */
610 protected class PortManager {
611 private final ReentrantReadWriteLock lock;
612 private List<OFPortDesc> portList;
613 private List<OFPortDesc> enabledPortList;
614 private List<Integer> enabledPortNumbers;
615 private Map<Integer,OFPortDesc> portsByNumber;
616 private Map<String,OFPortDesc> portsByName;
617
618 public PortManager() {
619 this.lock = new ReentrantReadWriteLock();
620 this.portList = Collections.emptyList();
621 this.enabledPortList = Collections.emptyList();
622 this.enabledPortNumbers = Collections.emptyList();
623 this.portsByName = Collections.emptyMap();
624 this.portsByNumber = Collections.emptyMap();
625 }
626
627 /**
628 * Set the internal data structure storing this switch's port
629 * to the ports specified by newPortsByNumber
630 *
631 * CALLER MUST HOLD WRITELOCK
632 *
633 * @param newPortsByNumber
634 * @throws IllegaalStateException if called without holding the
635 * writelock
636 */
637 private void updatePortsWithNewPortsByNumber(
638 Map<Integer,OFPortDesc> newPortsByNumber) {
639 if (!lock.writeLock().isHeldByCurrentThread()) {
640 throw new IllegalStateException("Method called without " +
641 "holding writeLock");
642 }
643 Map<String,OFPortDesc> newPortsByName =
644 new HashMap<String, OFPortDesc>();
645 List<OFPortDesc> newPortList =
646 new ArrayList<OFPortDesc>();
647 List<OFPortDesc> newEnabledPortList =
648 new ArrayList<OFPortDesc>();
649 List<Integer> newEnabledPortNumbers = new ArrayList<Integer>();
650
651 for(OFPortDesc p: newPortsByNumber.values()) {
652 newPortList.add(p);
653 newPortsByName.put(p.getName().toLowerCase(), p);
654 if (isEnabled(p)) {
655 newEnabledPortList.add(p);
656 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
657 }
658 }
659 portsByName = Collections.unmodifiableMap(newPortsByName);
660 portsByNumber =
661 Collections.unmodifiableMap(newPortsByNumber);
662 enabledPortList =
663 Collections.unmodifiableList(newEnabledPortList);
664 enabledPortNumbers =
665 Collections.unmodifiableList(newEnabledPortNumbers);
666 portList = Collections.unmodifiableList(newPortList);
667 }
668
669 /**
670 * Handle a OFPortStatus delete message for the given port.
671 * Updates the internal port maps/lists of this switch and returns
672 * the PortChangeEvents caused by the delete. If the given port
673 * exists as it, it will be deleted. If the name<->number for the
674 * given port is inconsistent with the ports stored by this switch
675 * the method will delete all ports with the number or name of the
676 * given port.
677 *
678 * This method will increment error/warn counters and log
679 *
680 * @param delPort the port from the port status message that should
681 * be deleted.
682 * @return ordered collection of port changes applied to this switch
683 */
684 private OrderedCollection<PortChangeEvent> handlePortStatusDelete(
685 OFPortDesc delPort) {
686 lock.writeLock().lock();
687 OrderedCollection<PortChangeEvent> events =
688 new LinkedHashSetWrapper<PortChangeEvent>();
689 try {
690 Map<Integer,OFPortDesc> newPortByNumber =
691 new HashMap<Integer, OFPortDesc>(portsByNumber);
692 OFPortDesc prevPort =
693 portsByNumber.get(delPort.getPortNo().getPortNumber());
694 if (prevPort == null) {
695 // so such port. Do we have a port with the name?
696 prevPort = portsByName.get(delPort.getName());
697 if (prevPort != null) {
698 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
699 events.add(new PortChangeEvent(prevPort,
700 PortChangeType.DELETE));
701 }
702 } else if (prevPort.getName().equals(delPort.getName())) {
703 // port exists with consistent name-number mapping
704 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
705 events.add(new PortChangeEvent(delPort,
706 PortChangeType.DELETE));
707 } else {
708 // port with same number exists but its name differs. This
709 // is weird. The best we can do is to delete the existing
710 // port(s) that have delPort's name and number.
711 newPortByNumber.remove(delPort.getPortNo().getPortNumber());
712 events.add(new PortChangeEvent(prevPort,
713 PortChangeType.DELETE));
714 // is there another port that has delPort's name?
715 prevPort = portsByName.get(delPort.getName().toLowerCase());
716 if (prevPort != null) {
717 newPortByNumber.remove(prevPort.getPortNo().getPortNumber());
718 events.add(new PortChangeEvent(prevPort,
719 PortChangeType.DELETE));
720 }
721 }
722 updatePortsWithNewPortsByNumber(newPortByNumber);
723 return events;
724 } finally {
725 lock.writeLock().unlock();
726 }
727 }
728
729 /**
730 * Handle a OFPortStatus message, update the internal data structures
731 * that store ports and return the list of OFChangeEvents.
732 *
733 * This method will increment error/warn counters and log
734 *
735 * @param ps
736 * @return
737 */
738 public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) {
739 if (ps == null) {
740 throw new NullPointerException("OFPortStatus message must " +
741 "not be null");
742 }
743 lock.writeLock().lock();
744 try {
745 OFPortReason reason = ps.getReason();
746 if (reason == null) {
747 throw new IllegalArgumentException("Unknown PortStatus " +
748 "reason code " + ps.getReason());
749 }
750
751 if (log.isDebugEnabled()) {
752 log.debug("Handling OFPortStatus: {} for {}",
753 reason, ps);
754 }
755
756 if (reason == OFPortReason.DELETE)
757 return handlePortStatusDelete(ps.getDesc());
758
759 // We handle ADD and MODIFY the same way. Since OpenFlow
760 // doesn't specify what uniquely identifies a port the
761 // notion of ADD vs. MODIFY can also be hazy. So we just
762 // compare the new port to the existing ones.
763 Map<Integer,OFPortDesc> newPortByNumber =
764 new HashMap<Integer, OFPortDesc>(portsByNumber);
765 OrderedCollection<PortChangeEvent> events =
766 getSinglePortChanges(ps.getDesc());
767 for (PortChangeEvent e: events) {
768 switch(e.type) {
769 case DELETE:
770 newPortByNumber.remove(e.port.getPortNo().getPortNumber());
771 break;
772 case ADD:
773 if (reason != OFPortReason.ADD) {
774 // weird case
775 }
776 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
777 e.port);
778 break;
779 case DOWN:
780 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
781 e.port);
782 break;
783 case OTHER_UPDATE:
784 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
785 e.port);
786 break;
787 case UP:
788 newPortByNumber.put(e.port.getPortNo().getPortNumber(),
789 e.port);
790 break;
791 }
792 }
793 updatePortsWithNewPortsByNumber(newPortByNumber);
794 return events;
795 } finally {
796 lock.writeLock().unlock();
797 }
798
799 }
800
801 /**
802 * Given a new or modified port newPort, returns the list of
803 * PortChangeEvents to "transform" the current ports stored by
804 * this switch to include / represent the new port. The ports stored
805 * by this switch are <b>NOT</b> updated.
806 *
807 * This method acquires the readlock and is thread-safe by itself.
808 * Most callers will need to acquire the write lock before calling
809 * this method though (if the caller wants to update the ports stored
810 * by this switch)
811 *
812 * @param newPort the new or modified port.
813 * @return the list of changes
814 */
815 public OrderedCollection<PortChangeEvent> getSinglePortChanges(
816 OFPortDesc newPort) {
817 lock.readLock().lock();
818 try {
819 OrderedCollection<PortChangeEvent> events =
820 new LinkedHashSetWrapper<PortChangeEvent>();
821 // Check if we have a port by the same number in our
822 // old map.
823 OFPortDesc prevPort =
824 portsByNumber.get(newPort.getPortNo().getPortNumber());
825 if (newPort.equals(prevPort)) {
826 // nothing has changed
827 return events;
828 }
829
830 if (prevPort != null &&
831 prevPort.getName().equals(newPort.getName())) {
832 // A simple modify of a existing port
833 // A previous port with this number exists and it's name
834 // also matches the new port. Find the differences
835 if (isEnabled(prevPort) && !isEnabled(newPort)) {
836 events.add(new PortChangeEvent(newPort,
837 PortChangeType.DOWN));
838 } else if (!isEnabled(prevPort) && isEnabled(newPort)) {
839 events.add(new PortChangeEvent(newPort,
840 PortChangeType.UP));
841 } else {
842 events.add(new PortChangeEvent(newPort,
843 PortChangeType.OTHER_UPDATE));
844 }
845 return events;
846 }
847
848 if (prevPort != null) {
849 // There exists a previous port with the same port
850 // number but the port name is different (otherwise we would
851 // never have gotten here)
852 // Remove the port. Name-number mapping(s) have changed
853 events.add(new PortChangeEvent(prevPort,
854 PortChangeType.DELETE));
855 }
856
857 // We now need to check if there exists a previous port sharing
858 // the same name as the new/updated port.
859 prevPort = portsByName.get(newPort.getName().toLowerCase());
860 if (prevPort != null) {
861 // There exists a previous port with the same port
862 // name but the port number is different (otherwise we
863 // never have gotten here).
864 // Remove the port. Name-number mapping(s) have changed
865 events.add(new PortChangeEvent(prevPort,
866 PortChangeType.DELETE));
867 }
868
869 // We always need to add the new port. Either no previous port
870 // existed or we just deleted previous ports with inconsistent
871 // name-number mappings
872 events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
873 return events;
874 } finally {
875 lock.readLock().unlock();
876 }
877 }
878
879 /**
880 * Compare the current ports of this switch to the newPorts list and
881 * return the changes that would be applied to transfort the current
882 * ports to the new ports. No internal data structures are updated
883 * see {@link #compareAndUpdatePorts(List, boolean)}
884 *
885 * @param newPorts the list of new ports
886 * @return The list of differences between the current ports and
887 * newPortList
888 */
889 public OrderedCollection<PortChangeEvent> comparePorts(
890 Collection<OFPortDesc> newPorts) {
891 return compareAndUpdatePorts(newPorts, false);
892 }
893
894 /**
895 * Compare the current ports of this switch to the newPorts list and
896 * return the changes that would be applied to transform the current
897 * ports to the new ports. No internal data structures are updated
898 * see {@link #compareAndUpdatePorts(List, boolean)}
899 *
900 * @param newPorts the list of new ports
901 * @return The list of differences between the current ports and
902 * newPortList
903 */
904 public OrderedCollection<PortChangeEvent> updatePorts(
905 Collection<OFPortDesc> newPorts) {
906 return compareAndUpdatePorts(newPorts, true);
907 }
908
909 /**
910 * Compare the current ports stored in this switch instance with the
911 * new port list given and return the differences in the form of
912 * PortChangeEvents. If the doUpdate flag is true, newPortList will
913 * replace the current list of this switch (and update the port maps)
914 *
915 * Implementation note:
916 * Since this method can optionally modify the current ports and
917 * since it's not possible to upgrade a read-lock to a write-lock
918 * we need to hold the write-lock for the entire operation. If this
919 * becomes a problem and if compares() are common we can consider
920 * splitting in two methods but this requires lots of code duplication
921 *
922 * @param newPorts the list of new ports.
923 * @param doUpdate If true the newPortList will replace the current
924 * port list for this switch. If false this switch will not be changed.
925 * @return The list of differences between the current ports and
926 * newPorts
927 * @throws NullPointerException if newPortsList is null
928 * @throws IllegalArgumentException if either port names or port numbers
929 * are duplicated in the newPortsList.
930 */
931 private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
932 Collection<OFPortDesc> newPorts, boolean doUpdate) {
933 if (newPorts == null) {
934 throw new NullPointerException("newPortsList must not be null");
935 }
936 lock.writeLock().lock();
937 try {
938 OrderedCollection<PortChangeEvent> events =
939 new LinkedHashSetWrapper<PortChangeEvent>();
940
941 Map<Integer,OFPortDesc> newPortsByNumber =
942 new HashMap<Integer, OFPortDesc>();
943 Map<String,OFPortDesc> newPortsByName =
944 new HashMap<String, OFPortDesc>();
945 List<OFPortDesc> newEnabledPortList =
946 new ArrayList<OFPortDesc>();
947 List<Integer> newEnabledPortNumbers =
948 new ArrayList<Integer>();
949 List<OFPortDesc> newPortsList =
950 new ArrayList<OFPortDesc>(newPorts);
951
952 for (OFPortDesc p: newPortsList) {
953 if (p == null) {
954 throw new NullPointerException("portList must not " +
955 "contain null values");
956 }
957
958 // Add the port to the new maps and lists and check
959 // that every port is unique
960 OFPortDesc duplicatePort;
961 duplicatePort = newPortsByNumber.put(
962 p.getPortNo().getPortNumber(), p);
963 if (duplicatePort != null) {
964 String msg = String.format("Cannot have two ports " +
965 "with the same number: %s <-> %s",
966 p, duplicatePort);
967 throw new IllegalArgumentException(msg);
968 }
969 duplicatePort =
970 newPortsByName.put(p.getName().toLowerCase(), p);
971 if (duplicatePort != null) {
972 String msg = String.format("Cannot have two ports " +
973 "with the same name: %s <-> %s",
974 p.toString().substring(0,80),
975 duplicatePort.toString().substring(0, 80));
976 throw new IllegalArgumentException(msg);
977 }
978 if (isEnabled(p)) {
979 newEnabledPortList.add(p);
980 newEnabledPortNumbers.add(p.getPortNo().getPortNumber());
981 }
982
983 // get changes
984 events.addAll(getSinglePortChanges(p));
985 }
986 // find deleted ports
987 // We need to do this after looping through all the new ports
988 // to we can handle changed name<->number mappings correctly
989 // We could pull it into the loop of we address this but
990 // it's probably not worth it
991 for (OFPortDesc oldPort: this.portList) {
992 if (!newPortsByNumber.containsKey(oldPort.getPortNo().getPortNumber())) {
993 PortChangeEvent ev =
994 new PortChangeEvent(oldPort,
995 PortChangeType.DELETE);
996 events.add(ev);
997 }
998 }
999
1000
1001 if (doUpdate) {
1002 portsByName = Collections.unmodifiableMap(newPortsByName);
1003 portsByNumber =
1004 Collections.unmodifiableMap(newPortsByNumber);
1005 enabledPortList =
1006 Collections.unmodifiableList(newEnabledPortList);
1007 enabledPortNumbers =
1008 Collections.unmodifiableList(newEnabledPortNumbers);
1009 portList = Collections.unmodifiableList(newPortsList);
1010 }
1011 return events;
1012 } finally {
1013 lock.writeLock().unlock();
1014 }
1015 }
1016
1017 public OFPortDesc getPort(String name) {
1018 if (name == null) {
1019 throw new NullPointerException("Port name must not be null");
1020 }
1021 lock.readLock().lock();
1022 try {
1023 return portsByName.get(name.toLowerCase());
1024 } finally {
1025 lock.readLock().unlock();
1026 }
1027 }
1028
1029 public OFPortDesc getPort(int portNumber) {
1030 lock.readLock().lock();
1031 try {
1032 return portsByNumber.get(portNumber);
1033 } finally {
1034 lock.readLock().unlock();
1035 }
1036 }
1037
1038 public List<OFPortDesc> getPorts() {
1039 lock.readLock().lock();
1040 try {
1041 return portList;
1042 } finally {
1043 lock.readLock().unlock();
1044 }
1045 }
1046
1047 public List<OFPortDesc> getEnabledPorts() {
1048 lock.readLock().lock();
1049 try {
1050 return enabledPortList;
1051 } finally {
1052 lock.readLock().unlock();
1053 }
1054 }
1055
1056 public List<Integer> getEnabledPortNumbers() {
1057 lock.readLock().lock();
1058 try {
1059 return enabledPortNumbers;
1060 } finally {
1061 lock.readLock().unlock();
1062 }
1063 }
1064 }
1065
1066
1067 //*******************************************
1068 // Switch stats handling
1069 //*******************************************
1070
1071 @Override
1072 public Future<List<OFStatsReply>> getStatistics(OFStatsRequest<?> request)
1073 throws IOException {
1074 OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this,
1075 (int)request.getXid());
1076 log.info("description STAT REQUEST XID {}", request.getXid());
1077 this.statsFutureMap.put((int)request.getXid(), future);
1078
1079 this.channel.write(Collections.singletonList(request));
1080 return future;
1081 }
1082
1083 @Override
1084 public void deliverStatisticsReply(OFMessage reply) {
1085 OFStatisticsFuture future = this.statsFutureMap.get((int)reply.getXid());
1086 if (future != null) {
1087 future.deliverFuture(this, reply);
1088 // The future will ultimately unregister itself and call
1089 // cancelStatisticsReply
1090 return;
1091 }
1092 // Transaction id was not found in statsFutureMap.check the other map
1093 IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
1094 if (caller != null) {
1095 caller.receive(this, reply, null);
1096 }
1097 }
1098
1099 @Override
1100 public void cancelStatisticsReply(int transactionId) {
1101 if (null == this.statsFutureMap.remove(transactionId)) {
1102 this.iofMsgListenersMap.remove(transactionId);
1103 }
1104 }
1105
1106 @Override
1107 public void cancelAllStatisticsReplies() {
1108 /* we don't need to be synchronized here. Even if another thread
1109 * modifies the map while we're cleaning up the future will eventuall
1110 * timeout */
1111 for (OFStatisticsFuture f : statsFutureMap.values()) {
1112 f.cancel(true);
1113 }
1114 statsFutureMap.clear();
1115 iofMsgListenersMap.clear();
1116 }
1117
1118
1119 //*******************************************
1120 // Switch role handling
1121 //*******************************************
1122
1123 // XXX S this is completely wrong. The generation id should be obtained
1124 // after coordinating with all controllers connected to this switch.
1125 // ie. should be part of registry service (account for 1.3 vs 1.0)
1126 // For now we are just generating this locally and keeping it constant.
1127 public U64 getNextGenerationId() {
1128 //TODO: Pankaj, fix nextGenerationId as part of Registry interface
1129 return U64.of(generationIdSource);
1130 }
1131
1132 @Override
1133 public Role getRole() {
1134 return role;
1135 }
1136
1137 @JsonIgnore
1138 @Override
1139 public void setRole(Role role) {
1140 this.role = role;
1141 }
1142
1143
1144 //*******************************************
1145 // Switch utility methods
1146 //*******************************************
1147
1148 private void registerOverloadCounters() throws CounterException {
1149 if (debugCountersRegistered) {
1150 return;
1151 }
1152 if (debugCounters == null) {
1153 log.error("Debug Counter Service not found");
1154 debugCounters = new NullDebugCounter();
1155 debugCountersRegistered = true;
1156 }
1157 // every level of the hierarchical counter has to be registered
1158 // even if they are not used
1159 ctrSwitch = debugCounters.registerCounter(
1160 BASE , stringId,
1161 "Counter for this switch",
1162 CounterType.ALWAYS_COUNT);
1163 ctrSwitchPktin = debugCounters.registerCounter(
1164 BASE, stringId + "/pktin",
1165 "Packet in counter for this switch",
1166 CounterType.ALWAYS_COUNT);
1167 ctrSwitchWrite = debugCounters.registerCounter(
1168 BASE, stringId + "/write",
1169 "Write counter for this switch",
1170 CounterType.ALWAYS_COUNT);
1171 ctrSwitchPktinDrops = debugCounters.registerCounter(
1172 BASE, stringId + "/pktin/drops",
1173 "Packet in throttle drop count",
1174 CounterType.ALWAYS_COUNT,
1175 IDebugCounterService.CTR_MDATA_WARN);
1176 ctrSwitchWriteDrops = debugCounters.registerCounter(
1177 BASE, stringId + "/write/drops",
1178 "Switch write throttle drop count",
1179 CounterType.ALWAYS_COUNT,
1180 IDebugCounterService.CTR_MDATA_WARN);
1181 }
1182
1183 @Override
1184 public void startDriverHandshake() throws IOException {
1185 log.debug("Starting driver handshake for sw {}", getStringId());
1186 if (startDriverHandshakeCalled)
1187 throw new SwitchDriverSubHandshakeAlreadyStarted();
1188 startDriverHandshakeCalled = true;
1189 }
1190
1191 @Override
1192 public boolean isDriverHandshakeComplete() {
1193 if (!startDriverHandshakeCalled)
1194 throw new SwitchDriverSubHandshakeNotStarted();
1195 return true;
1196 }
1197
1198 @Override
1199 public void processDriverHandshakeMessage(OFMessage m) {
1200 if (startDriverHandshakeCalled)
1201 throw new SwitchDriverSubHandshakeCompleted(m);
1202 else
1203 throw new SwitchDriverSubHandshakeNotStarted();
1204 }
1205
1206 @Override
1207 @JsonIgnore
1208 @LogMessageDoc(level="WARN",
1209 message="Switch {switch} flow table is full",
1210 explanation="The controller received flow table full " +
1211 "message from the switch, could be caused by increased " +
1212 "traffic pattern",
1213 recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
1214 public void setTableFull(boolean isFull) {
1215 if (isFull && !flowTableFull) {
1216 floodlightProvider.addSwitchEvent(this.datapathId.getLong(),
1217 "SWITCH_FLOW_TABLE_FULL " +
1218 "Table full error from switch", false);
1219 log.warn("Switch {} flow table is full", stringId);
1220 }
1221 flowTableFull = isFull;
1222 }
1223
1224 @Override
1225 @LogMessageDoc(level = "ERROR",
1226 message = "Failed to clear all flows on switch {switch}",
1227 explanation = "An I/O error occured while trying to clear " +
1228 "flows on the switch.",
1229 recommendation = LogMessageDoc.CHECK_SWITCH)
1230 public void clearAllFlowMods() {
1231 // Delete all pre-existing flows
1232
Jonathan Harta213bce2014-08-11 15:44:07 -07001233 // by default if match is not specified, then an empty list of matches
1234 // is sent, resulting in a wildcard-all flows
1235 // XXX fix this later to be sure it works for both 1.0 and 1.3
1236 OFMessage fm = getFactory()
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001237 .buildFlowDelete()
1238 .setOutPort(OFPort.ANY)
1239 .setOutGroup(OFGroup.ANY)
1240 .setTableId(TableId.ALL)
1241 .build();
1242
1243 try {
1244 channel.write(Collections.singletonList(fm));
1245 } catch (Exception e) {
1246 log.error("Failed to clear all flows on switch " + this, e);
1247 }
1248 }
1249
1250 @Override
1251 public void flush() {
1252 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1253 List<OFMessage> msglist = msg_buffer_map.get(this);
1254 if ((msglist != null) && (msglist.size() > 0)) {
1255 try {
1256 this.write(msglist);
1257 } catch (IOException e) {
1258 log.error("Failed flushing messages", e);
1259 }
1260 msglist.clear();
1261 }
1262 }
1263
1264 public static void flush_all() {
1265 Map<OFSwitchImplBase, List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
1266 for (OFSwitchImplBase sw : msg_buffer_map.keySet()) {
1267 sw.flush();
1268 }
1269 }
1270
1271 /**
1272 * Return a read lock that must be held while calling the listeners for
1273 * messages from the switch. Holding the read lock prevents the active
1274 * switch list from being modified out from under the listeners.
1275 *
1276 * @return listener read lock
1277 */
1278 @JsonIgnore
1279 public Lock getListenerReadLock() {
1280 return listenerLock.readLock();
1281 }
1282
1283 /**
1284 * Return a write lock that must be held when the controllers modifies the
1285 * list of active switches. This is to ensure that the active switch list
1286 * doesn't change out from under the listeners as they are handling a
1287 * message from the switch.
1288 *
1289 * @return listener write lock
1290 */
1291 @JsonIgnore
1292 public Lock getListenerWriteLock() {
1293 return listenerLock.writeLock();
1294 }
1295}