Package net.floodlightcontroller.core

Source Code of net.floodlightcontroller.core.OFSwitchBase

/**
*    Copyright 2012, Big Switch Networks, Inc.
*    Originally created by David Erickson, Stanford University
*
*    Licensed under the Apache License, Version 2.0 (the "License"); you may
*    not use this file except in compliance with the License. You may obtain
*    a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
*    License for the specific language governing permissions and limitations
*    under the License.
**/

package net.floodlightcontroller.core;

import java.io.IOException;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import net.floodlightcontroller.core.IFloodlightProviderService.Role;
import net.floodlightcontroller.core.annotations.LogMessageDoc;
import net.floodlightcontroller.core.annotations.LogMessageDocs;
import net.floodlightcontroller.core.internal.Controller;
import net.floodlightcontroller.core.internal.OFFeaturesReplyFuture;
import net.floodlightcontroller.core.internal.OFStatisticsFuture;
import net.floodlightcontroller.core.util.AppCookie;
import net.floodlightcontroller.core.web.serializers.DPIDSerializer;
import net.floodlightcontroller.debugcounter.IDebugCounter;
import net.floodlightcontroller.debugcounter.IDebugCounterService;
import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterException;
import net.floodlightcontroller.debugcounter.IDebugCounterService.CounterType;
import net.floodlightcontroller.debugcounter.NullDebugCounter;
import net.floodlightcontroller.devicemanager.SwitchPort;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.routing.ForwardingBase;
import net.floodlightcontroller.threadpool.IThreadPoolService;
import net.floodlightcontroller.util.LinkedHashSetWrapper;
import net.floodlightcontroller.util.MACAddress;
import net.floodlightcontroller.util.OrderedCollection;
import net.floodlightcontroller.util.TimedCache;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;

import org.jboss.netty.channel.Channel;
import org.openflow.protocol.OFFeaturesReply;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPortStatus;
import org.openflow.protocol.OFPortStatus.OFPortReason;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.OFStatisticsReply;
import org.openflow.protocol.OFStatisticsRequest;
import org.openflow.protocol.OFType;
import org.openflow.protocol.statistics.OFDescriptionStatistics;
import org.openflow.protocol.statistics.OFStatistics;
import org.openflow.protocol.statistics.OFStatisticsType;
import org.openflow.protocol.statistics.OFTableStatistics;
import org.openflow.util.HexString;
import org.openflow.util.U16;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This is the internal representation of an openflow switch.
*/
public abstract class OFSwitchBase implements IOFSwitch {
    // TODO: should we really do logging in the class or should we throw
    // exception that can then be handled by callers?
    protected static final Logger log = LoggerFactory.getLogger(OFSwitchBase.class);

    protected ConcurrentMap<Object, Object> attributes;
    protected IFloodlightProviderService floodlightProvider;
    protected IThreadPoolService threadPool;
    protected IDebugCounterService debugCounters;
    // FIXME: Don't use java.util.Date
    protected volatile Date connectedSince;

    /* Switch features from initial featuresReply */
    protected int capabilities;
    protected int buffers;
    protected int actions;
    protected byte tables;
    protected long datapathId;
    protected String stringId;

    protected short accessFlowPriority;
    protected short coreFlowPriority;

    private boolean startDriverHandshakeCalled = false;
    protected Channel channel;

    /**
     * Members hidden from subclasses
     */
    private final AtomicInteger transactionIdSource;
    private final Map<Integer,OFStatisticsFuture> statsFutureMap;
    private final Map<Integer, IOFMessageListener> iofMsgListenersMap;
    private final Map<Integer,OFFeaturesReplyFuture> featuresFutureMap;
    private volatile boolean connected;
    private volatile Role role;
    private final TimedCache<Long> timedCache;
    private final ConcurrentMap<Short, AtomicLong> portBroadcastCacheHitMap;

    private final PortManager portManager;

    // Private members for throttling
    private boolean writeThrottleEnabled = false;
    protected boolean packetInThrottleEnabled = false; // used by test
    private int packetInRateThresholdHigh =
            Integer.parseInt(System.getProperty("input_threshold", "1000"));
    private int packetInRateThresholdLow = 1;
    private int packetInRatePerMacThreshold = 50;
    private int packetInRatePerPortThreshold = 100;
    private long messageCount = 0;
    private long messageCountUniqueOFMatch = 0;
    private long lastMessageTime;
    private int currentRate = 0;
    private TimedCache<OFMatch> ofMatchCache;
    private TimedCache<Long> macCache;
    private TimedCache<Long> macBlockedCache;
    private TimedCache<Short> portCache;
    private TimedCache<Short> portBlockedCache;
    private boolean flowTableFull = false;

    protected OFDescriptionStatistics description;

    private boolean debugCountersRegistered;
    @SuppressWarnings("unused")
    private IDebugCounter ctrSwitch, ctrSwitchPktin, ctrSwitchWrite;
    private IDebugCounter ctrSwitchPktinDrops, ctrSwitchWriteDrops;

    private static final String PACKAGE = OFSwitchBase.class.getPackage().getName();


    protected final static ThreadLocal<Map<IOFSwitch,List<OFMessage>>> local_msg_buffer =
            new ThreadLocal<Map<IOFSwitch,List<OFMessage>>>() {
        @Override
        protected Map<IOFSwitch,List<OFMessage>> initialValue() {
            return new HashMap<IOFSwitch,List<OFMessage>>();
        }
    };

    public static final int OFSWITCH_APP_ID = 5;
    static {
        AppCookie.registerApp(OFSwitchBase.OFSWITCH_APP_ID, "switch");
    }

    public OFSwitchBase() {
        this.stringId = null;
        this.attributes = new ConcurrentHashMap<Object, Object>();
        this.connectedSince = null;
        this.transactionIdSource = new AtomicInteger();
        this.connected = false;
        this.statsFutureMap = new ConcurrentHashMap<Integer,OFStatisticsFuture>();
        this.featuresFutureMap = new ConcurrentHashMap<Integer,OFFeaturesReplyFuture>();
        this.iofMsgListenersMap = new ConcurrentHashMap<Integer,IOFMessageListener>();
        this.role = null;
        this.timedCache = new TimedCache<Long>(100, 5*1000 )// 5 seconds interval
        this.portBroadcastCacheHitMap = new ConcurrentHashMap<Short, AtomicLong>();
        this.description = new OFDescriptionStatistics();
        this.lastMessageTime = System.currentTimeMillis();

        this.portManager = new PortManager();

        // Defaults properties for an ideal switch
        this.setAttribute(PROP_FASTWILDCARDS, OFMatch.OFPFW_ALL);
        this.setAttribute(PROP_SUPPORTS_OFPP_FLOOD, Boolean.valueOf(true));
        this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, Boolean.valueOf(true));
        if (packetInRateThresholdHigh == 0) {
            packetInRateThresholdHigh = Integer.MAX_VALUE;
        } else {
            packetInRateThresholdLow = packetInRateThresholdHigh / 2;
        }
    }



    /**
     * Manages the ports of this switch.
     *
     * Provides methods to query and update the stored ports. The class ensures
     * that every port name and port number is unique. When updating ports
     * the class checks if port number <-> port name mappings have change due
     * to the update. If a new port P has number and port that are inconsistent
     * with the previous mapping(s) the class will delete all previous ports
     * with name or number of the new port and then add the new port.
     *
     * Port names are stored as-is but they are compared case-insensitive
     *
     * The methods that change the stored ports return a list of
     * PortChangeEvents that represent the changes that have been applied
     * to the port list so that IOFSwitchListeners can be notified about the
     * changes.
     *
     * Implementation notes:
     * - We keep several different representations of the ports to allow for
     *   fast lookups
     * - Ports are stored in unchangeable lists. When a port is modified new
     *   data structures are allocated.
     * - We use a read-write-lock for synchronization, so multiple readers are
     *   allowed.
     */
    protected class PortManager {
        private final ReentrantReadWriteLock lock;
        private List<ImmutablePort> portList;
        private List<ImmutablePort> enabledPortList;
        private List<Short> enabledPortNumbers;
        private Map<Short,ImmutablePort> portsByNumber;
        private Map<String,ImmutablePort> portsByName;




        public PortManager() {
            this.lock = new ReentrantReadWriteLock();
            this.portList = Collections.emptyList();
            this.enabledPortList = Collections.emptyList();
            this.enabledPortNumbers = Collections.emptyList();
            this.portsByName = Collections.emptyMap();
            this.portsByNumber = Collections.emptyMap();
        }

        /**
         * Set the internal data structure storing this switch's port
         * to the ports specified by newPortsByNumber
         *
         * CALLER MUST HOLD WRITELOCK
         *
         * @param newPortsByNumber
         * @throws IllegaalStateException if called without holding the
         * writelock
         */
        private void updatePortsWithNewPortsByNumber(
                Map<Short,ImmutablePort> newPortsByNumber) {
            if (!lock.writeLock().isHeldByCurrentThread()) {
                throw new IllegalStateException("Method called without " +
                                                "holding writeLock");
            }
            Map<String,ImmutablePort> newPortsByName =
                    new HashMap<String, ImmutablePort>();
            List<ImmutablePort> newPortList =
                    new ArrayList<ImmutablePort>();
            List<ImmutablePort> newEnabledPortList =
                    new ArrayList<ImmutablePort>();
            List<Short> newEnabledPortNumbers = new ArrayList<Short>();

            for(ImmutablePort p: newPortsByNumber.values()) {
                newPortList.add(p);
                newPortsByName.put(p.getName().toLowerCase(), p);
                if (p.isEnabled()) {
                    newEnabledPortList.add(p);
                    newEnabledPortNumbers.add(p.getPortNumber());
                }
            }
            portsByName = Collections.unmodifiableMap(newPortsByName);
            portsByNumber =
                    Collections.unmodifiableMap(newPortsByNumber);
            enabledPortList =
                    Collections.unmodifiableList(newEnabledPortList);
            enabledPortNumbers =
                    Collections.unmodifiableList(newEnabledPortNumbers);
            portList = Collections.unmodifiableList(newPortList);
        }

        /**
         * Handle a OFPortStatus delete message for the given port.
         * Updates the internal port maps/lists of this switch and returns
         * the PortChangeEvents caused by the delete. If the given port
         * exists as it, it will be deleted. If the name<->number for the
         * given port is inconsistent with the ports stored by this switch
         * the method will delete all ports with the number or name of the
         * given port.
         *
         * This method will increment error/warn counters and log
         *
         * @param delPort the port from the port status message that should
         * be deleted.
         * @return ordered collection of port changes applied to this switch
         */
        private OrderedCollection<PortChangeEvent>
                handlePortStatusDelete(ImmutablePort delPort) {
            lock.writeLock().lock();
            OrderedCollection<PortChangeEvent> events =
                    new LinkedHashSetWrapper<PortChangeEvent>();
            try {
                Map<Short,ImmutablePort> newPortByNumber =
                        new HashMap<Short, ImmutablePort>(portsByNumber);
                ImmutablePort prevPort =
                        portsByNumber.get(delPort.getPortNumber());
                if (prevPort == null) {
                    // so such port. Do we have a port with the name?
                    prevPort = portsByName.get(delPort.getName());
                    if (prevPort != null) {
                        newPortByNumber.remove(prevPort.getPortNumber());
                        events.add(new PortChangeEvent(prevPort,
                                                       PortChangeType.DELETE));
                    }
                } else if (prevPort.getName().equals(delPort.getName())) {
                    // port exists with consistent name-number mapping
                    newPortByNumber.remove(delPort.getPortNumber());
                    events.add(new PortChangeEvent(delPort,
                                                   PortChangeType.DELETE));
                } else {
                    // port with same number exists but its name differs. This
                    // is weird. The best we can do is to delete the existing
                    // port(s) that have delPort's name and number.
                    newPortByNumber.remove(delPort.getPortNumber());
                    events.add(new PortChangeEvent(prevPort,
                                                   PortChangeType.DELETE));
                    // is there another port that has delPort's name?
                    prevPort = portsByName.get(delPort.getName().toLowerCase());
                    if (prevPort != null) {
                        newPortByNumber.remove(prevPort.getPortNumber());
                        events.add(new PortChangeEvent(prevPort,
                                                       PortChangeType.DELETE));
                    }
                }
                updatePortsWithNewPortsByNumber(newPortByNumber);
                return events;
            } finally {
                lock.writeLock().unlock();
            }
        }

        /**
         * Handle a OFPortStatus message, update the internal data structures
         * that store ports and return the list of OFChangeEvents.
         *
         * This method will increment error/warn counters and log
         *
         * @param ps
         * @return
         */
        public OrderedCollection<PortChangeEvent> handlePortStatusMessage(OFPortStatus ps) {
            if (ps == null) {
                throw new NullPointerException("OFPortStatus message must " +
                                               "not be null");
            }
            lock.writeLock().lock();
            try {
                ImmutablePort port =
                        ImmutablePort.fromOFPhysicalPort(ps.getDesc());
                OFPortReason reason = OFPortReason.fromReasonCode(ps.getReason());
                if (reason == null) {
                    throw new IllegalArgumentException("Unknown PortStatus " +
                            "reason code " + ps.getReason());
                }

                if (log.isDebugEnabled()) {
                    log.debug("Handling OFPortStatus: {} for {}",
                              reason, port.toBriefString());
                }

                if (reason == OFPortReason.OFPPR_DELETE)
                        return handlePortStatusDelete(port);

                // We handle ADD and MODIFY the same way. Since OpenFlow
                // doesn't specify what uniquely identifies a port the
                // notion of ADD vs. MODIFY can also be hazy. So we just
                // compare the new port to the existing ones.
                Map<Short,ImmutablePort> newPortByNumber =
                    new HashMap<Short, ImmutablePort>(portsByNumber);
                OrderedCollection<PortChangeEvent> events = getSinglePortChanges(port);
                for (PortChangeEvent e: events) {
                    switch(e.type) {
                        case DELETE:
                            newPortByNumber.remove(e.port.getPortNumber());
                            break;
                        case ADD:
                            if (reason != OFPortReason.OFPPR_ADD) {
                                // weird case
                            }
                            // fall through
                        case DOWN:
                        case OTHER_UPDATE:
                        case UP:
                            // update or add the port in the map
                            newPortByNumber.put(e.port.getPortNumber(), e.port);
                            break;
                    }
                }
                updatePortsWithNewPortsByNumber(newPortByNumber);
                return events;
            } finally {
                lock.writeLock().unlock();
            }

        }

        /**
         * Given a new or modified port newPort, returns the list of
         * PortChangeEvents to "transform" the current ports stored by
         * this switch to include / represent the new port. The ports stored
         * by this switch are <b>NOT</b> updated.
         *
         * This method acquires the readlock and is thread-safe by itself.
         * Most callers will need to acquire the write lock before calling
         * this method though (if the caller wants to update the ports stored
         * by this switch)
         *
         * @param newPort the new or modified port.
         * @return the list of changes
         */
        public OrderedCollection<PortChangeEvent>
                getSinglePortChanges(ImmutablePort newPort) {
            lock.readLock().lock();
            try {
                OrderedCollection<PortChangeEvent> events =
                        new LinkedHashSetWrapper<PortChangeEvent>();
                // Check if we have a port by the same number in our
                // old map.
                ImmutablePort prevPort =
                        portsByNumber.get(newPort.getPortNumber());
                if (newPort.equals(prevPort)) {
                    // nothing has changed
                    return events;
                }

                if (prevPort != null &&
                        prevPort.getName().equals(newPort.getName())) {
                    // A simple modify of a exiting port
                    // A previous port with this number exists and it's name
                    // also matches the new port. Find the differences
                    if (prevPort.isEnabled() && !newPort.isEnabled()) {
                        events.add(new PortChangeEvent(newPort,
                                                       PortChangeType.DOWN));
                    } else if (!prevPort.isEnabled() && newPort.isEnabled()) {
                        events.add(new PortChangeEvent(newPort,
                                                       PortChangeType.UP));
                    } else {
                        events.add(new PortChangeEvent(newPort,
                                   PortChangeType.OTHER_UPDATE));
                    }
                    return events;
                }

                if (prevPort != null) {
                    // There exists a previous port with the same port
                    // number but the port name is different (otherwise we would
                    // never have gotten here)
                    // Remove the port. Name-number mapping(s) have changed
                    events.add(new PortChangeEvent(prevPort,
                                                   PortChangeType.DELETE));
                }

                // We now need to check if there exists a previous port sharing
                // the same name as the new/updated port.
                prevPort = portsByName.get(newPort.getName().toLowerCase());
                if (prevPort != null) {
                    // There exists a previous port with the same port
                    // name but the port number is different (otherwise we
                    // never have gotten here).
                    // Remove the port. Name-number mapping(s) have changed
                    events.add(new PortChangeEvent(prevPort,
                                                   PortChangeType.DELETE));
                }

                // We always need to add the new port. Either no previous port
                // existed or we just deleted previous ports with inconsistent
                // name-number mappings
                events.add(new PortChangeEvent(newPort, PortChangeType.ADD));
                return events;
            } finally {
                lock.readLock().unlock();
            }
        }

        /**
         * Compare the current ports of this switch to the newPorts list and
         * return the changes that would be applied to transfort the current
         * ports to the new ports. No internal data structures are updated
         * see {@link #compareAndUpdatePorts(List, boolean)}
         *
         * @param newPorts the list of new ports
         * @return The list of differences between the current ports and
         * newPortList
         */
        public OrderedCollection<PortChangeEvent>
                comparePorts(Collection<ImmutablePort> newPorts) {
            return compareAndUpdatePorts(newPorts, false);
        }

        /**
         * Compare the current ports of this switch to the newPorts list and
         * return the changes that would be applied to transform the current
         * ports to the new ports. No internal data structures are updated
         * see {@link #compareAndUpdatePorts(List, boolean)}
         *
         * @param newPorts the list of new ports
         * @return The list of differences between the current ports and
         * newPortList
         */
        public OrderedCollection<PortChangeEvent>
                updatePorts(Collection<ImmutablePort> newPorts) {
            return compareAndUpdatePorts(newPorts, true);
        }

        /**
         * Compare the current ports stored in this switch instance with the
         * new port list given and return the differences in the form of
         * PortChangeEvents. If the doUpdate flag is true, newPortList will
         * replace the current list of this switch (and update the port maps)
         *
         * Implementation note:
         * Since this method can optionally modify the current ports and
         * since it's not possible to upgrade a read-lock to a write-lock
         * we need to hold the write-lock for the entire operation. If this
         * becomes a problem and if compares() are common we can consider
         * splitting in two methods but this requires lots of code duplication
         *
         * @param newPorts the list of new ports.
         * @param doUpdate If true the newPortList will replace the current
         * port list for this switch. If false this switch will not be changed.
         * @return The list of differences between the current ports and
         * newPorts
         * @throws NullPointerException if newPortsList is null
         * @throws IllegalArgumentException if either port names or port numbers
         * are duplicated in the newPortsList.
         */
        private OrderedCollection<PortChangeEvent> compareAndUpdatePorts(
                Collection<ImmutablePort> newPorts,
                boolean doUpdate) {
            if (newPorts == null) {
                throw new NullPointerException("newPortsList must not be null");
            }
            lock.writeLock().lock();
            try {
                OrderedCollection<PortChangeEvent> events =
                        new LinkedHashSetWrapper<PortChangeEvent>();

                Map<Short,ImmutablePort> newPortsByNumber =
                        new HashMap<Short, ImmutablePort>();
                Map<String,ImmutablePort> newPortsByName =
                        new HashMap<String, ImmutablePort>();
                List<ImmutablePort> newEnabledPortList =
                        new ArrayList<ImmutablePort>();
                List<Short> newEnabledPortNumbers =
                        new ArrayList<Short>();
                List<ImmutablePort> newPortsList =
                        new ArrayList<ImmutablePort>(newPorts);

                for (ImmutablePort p: newPortsList) {
                    if (p == null) {
                        throw new NullPointerException("portList must not " +
                                "contain null values");
                    }

                    // Add the port to the new maps and lists and check
                    // that every port is unique
                    ImmutablePort duplicatePort;
                    duplicatePort = newPortsByNumber.put(p.getPortNumber(), p);
                    if (duplicatePort != null) {
                        String msg = String.format("Cannot have two ports " +
                                "with the same number: %s <-> %s",
                                p.toBriefString(),
                                duplicatePort.toBriefString());
                        throw new IllegalArgumentException(msg);
                    }
                    duplicatePort =
                            newPortsByName.put(p.getName().toLowerCase(), p);
                    if (duplicatePort != null) {
                        String msg = String.format("Cannot have two ports " +
                                "with the same name: %s <-> %s",
                                p.toBriefString(),
                                duplicatePort.toBriefString());
                        throw new IllegalArgumentException(msg);
                    }
                    if (p.isEnabled()) {
                        newEnabledPortList.add(p);
                        newEnabledPortNumbers.add(p.getPortNumber());
                    }

                    // get changes
                    events.addAll(getSinglePortChanges(p));
                }
                // find deleted ports
                // We need to do this after looping through all the new ports
                // to we can handle changed name<->number mappings correctly
                // We could pull it into the loop of we address this but
                // it's probably not worth it
                for (ImmutablePort oldPort: this.portList) {
                    if (!newPortsByNumber.containsKey(oldPort.getPortNumber())) {
                        PortChangeEvent ev =
                                new PortChangeEvent(oldPort,
                                                    PortChangeType.DELETE);
                        events.add(ev);
                    }
                }


                if (doUpdate) {
                    portsByName = Collections.unmodifiableMap(newPortsByName);
                    portsByNumber =
                            Collections.unmodifiableMap(newPortsByNumber);
                    enabledPortList =
                            Collections.unmodifiableList(newEnabledPortList);
                    enabledPortNumbers =
                            Collections.unmodifiableList(newEnabledPortNumbers);
                    portList = Collections.unmodifiableList(newPortsList);
                }
                return events;
            } finally {
                lock.writeLock().unlock();
            }
        }

        public ImmutablePort getPort(String name) {
            if (name == null) {
                throw new NullPointerException("Port name must not be null");
            }
            lock.readLock().lock();
            try {
                return portsByName.get(name.toLowerCase());
            } finally {
                lock.readLock().unlock();
            }
        }

        public ImmutablePort getPort(Short portNumber) {
            lock.readLock().lock();
            try {
                return portsByNumber.get(portNumber);
            } finally {
                lock.readLock().unlock();
            }
        }

        public List<ImmutablePort> getPorts() {
            lock.readLock().lock();
            try {
                return portList;
            } finally {
                lock.readLock().unlock();
            }
        }

        public List<ImmutablePort> getEnabledPorts() {
            lock.readLock().lock();
            try {
                return enabledPortList;
            } finally {
                lock.readLock().unlock();
            }
        }

        public List<Short> getEnabledPortNumbers() {
            lock.readLock().lock();
            try {
                return enabledPortNumbers;
            } finally {
                lock.readLock().unlock();
            }
        }
    }


    @Override
    public boolean attributeEquals(String name, Object other) {
        Object attr = this.attributes.get(name);
        if (attr == null)
            return false;
        return attr.equals(other);
    }


    @Override
    public Object getAttribute(String name) {
        // returns null if key doesn't exist
        return this.attributes.get(name);
    }

    @Override
    public void setAttribute(String name, Object value) {
        this.attributes.put(name, value);
        return;
    }

    @Override
    public Object removeAttribute(String name) {
        return this.attributes.remove(name);
    }

    @Override
    public boolean hasAttribute(String name) {
        return this.attributes.containsKey(name);
    }

    @Override
    @JsonIgnore
    public void setChannel(Channel channel) {
        this.channel = channel;
    }

    // For driver subclass to set throttling
    protected void enableWriteThrottle(boolean enable) {
        this.writeThrottleEnabled = enable;
    }

    @Override
    public boolean isWriteThrottleEnabled() {
        return this.writeThrottleEnabled;
    }

    @Override
    @LogMessageDocs({
        @LogMessageDoc(level="WARN",
                message="Drop throttled OF message to switch {switch}",
                explanation="The controller is sending more messages" +
                "than the switch can handle. Some messages are dropped" +
                "to prevent switch outage",
                recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
    })
    public void writeThrottled(OFMessage m, FloodlightContext bc)
            throws IOException {
        if (channel == null || !isConnected())
            return;
        /**
         * By default, channel uses an unbounded send queue. Enable throttling
         * prevents the queue from growing big.
         *
         * channel.isWritable() returns true when queue length is less than
         * high water mark (64 kbytes). Once exceeded, isWritable() becomes
         * false after queue length drops below low water mark (32 kbytes).
         */
        if (!writeThrottleEnabled || channel.isWritable()) {
            write(m, bc);
        } else {
            // Let logback duplicate filtering take care of excessive logs
            ctrSwitchWriteDrops.updateCounterNoFlush();
            log.warn("Drop throttled OF message to switch {}", this);
        }
    }

    @Override
    public void writeThrottled(List<OFMessage> msglist, FloodlightContext bc)
            throws IOException {
        if (!writeThrottleEnabled || channel.isWritable()) {
            write(msglist, bc);
        } else {
            // Let logback duplicate filtering take care of excessive logs
            ctrSwitchWriteDrops.updateCounterNoFlush(msglist.size());
            log.warn("Drop throttled OF messages to switch {}", this);
        }
    }

    @Override
    public void write(OFMessage m, FloodlightContext bc) {
        if (channel == null || !isConnected())
            return;
            //throws IOException {
        Map<IOFSwitch,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
        List<OFMessage> msg_buffer = msg_buffer_map.get(this);
        if (msg_buffer == null) {
            msg_buffer = new ArrayList<OFMessage>();
            msg_buffer_map.put(this, msg_buffer);
        }

        this.floodlightProvider.handleOutgoingMessage(this, m, bc);
        msg_buffer.add(m);

        if ((msg_buffer.size() >= Controller.BATCH_MAX_SIZE) ||
            ((m.getType() != OFType.PACKET_OUT) && (m.getType() != OFType.FLOW_MOD))) {
            this.write(msg_buffer);
            msg_buffer.clear();
        }
    }
    @Override
    @LogMessageDoc(level="WARN",
                   message="Sending OF message that modifies switch " +
                           "state while in the slave role: {switch}",
                   explanation="An application has sent a message to a switch " +
                           "that is not valid when the switch is in a slave role",
                   recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
    public void write(List<OFMessage> msglist,
                      FloodlightContext bc) {
        if (channel == null || !isConnected())
            return;
        for (OFMessage m : msglist) {
            if (role == Role.SLAVE) {
                switch (m.getType()) {
                    case PACKET_OUT:
                    case FLOW_MOD:
                    case PORT_MOD:
                        log.warn("Sending OF message that modifies switch " +
                                 "state while in the slave role: {}",
                                 m.getType().name());
                        break;
                    default:
                        break;
                }
            }
            this.floodlightProvider.handleOutgoingMessage(this, m, bc);
        }
        this.write(msglist);
    }

    /**
     * Not callable by writers, but allow IOFSwitch implementation to override
     * @param msglist
     * @throws IOException
     */
    protected void write(List<OFMessage> msglist) {
        if (channel == null || !isConnected())
            return;
        this.channel.write(msglist);
    }

    @Override
    public void disconnectOutputStream() {
        if (channel == null)
            return;
        channel.close();
    }

    @Override
    @JsonIgnore
    public void setFeaturesReply(OFFeaturesReply featuresReply) {
        if (stringId == null) {
            /* ports are updated via port status message, so we
             * only fill in ports on initial connection.
             */
            List<ImmutablePort> immutablePorts = ImmutablePort
                    .immutablePortListOf(featuresReply.getPorts());
            portManager.updatePorts(immutablePorts);
        }
        this.datapathId = featuresReply.getDatapathId();
        this.stringId = HexString.toHexString(featuresReply.getDatapathId());
        this.capabilities = featuresReply.getCapabilities();
        this.buffers = featuresReply.getBuffers();
        this.actions = featuresReply.getActions();
        this.tables = featuresReply.getTables();
}

    @Override
    @JsonIgnore
    public Collection<ImmutablePort> getEnabledPorts() {
        return portManager.getEnabledPorts();
    }

    @Override
    @JsonIgnore
    public Collection<Short> getEnabledPortNumbers() {
        return portManager.getEnabledPortNumbers();
    }

    @Override
    public ImmutablePort getPort(short portNumber) {
        return portManager.getPort(portNumber);
    }

    @Override
    public ImmutablePort getPort(String portName) {
        return portManager.getPort(portName);
    }

    @Override
    @JsonIgnore
    public OrderedCollection<PortChangeEvent>
            processOFPortStatus(OFPortStatus ps) {
        return portManager.handlePortStatusMessage(ps);
    }

    @Override
    @JsonProperty("ports")
    public Collection<ImmutablePort> getPorts() {
        return portManager.getPorts();
    }

    @Override
    public OrderedCollection<PortChangeEvent>
            comparePorts(Collection<ImmutablePort> ports) {
        return portManager.comparePorts(ports);
    }

    @Override
    @JsonIgnore
    public OrderedCollection<PortChangeEvent>
            setPorts(Collection<ImmutablePort> ports) {
        return portManager.updatePorts(ports);
    }

    @Override
    public boolean portEnabled(short portNumber) {
        ImmutablePort p = portManager.getPort(portNumber);
        if (p == null) return false;
        return p.isEnabled();
    }

    @Override
    public boolean portEnabled(String portName) {
        ImmutablePort p = portManager.getPort(portName);
        if (p == null) return false;
        return p.isEnabled();
    }

    @Override
    @JsonSerialize(using=DPIDSerializer.class)
    @JsonProperty("dpid")
    public long getId() {
        if (this.stringId == null)
            throw new RuntimeException("Features reply has not yet been set");
        return this.datapathId;
    }

    @JsonIgnore
    @Override
    public String getStringId() {
        return stringId;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        String channelString =
                (channel != null) ? channel.getRemoteAddress().toString() :
                                    "?";
        return "OFSwitchBase [" + channelString + " DPID[" + ((stringId != null) ? stringId : "?") + "]]";
    }

    @Override
    public ConcurrentMap<Object, Object> getAttributes() {
        return this.attributes;
    }

    @Override
    public Date getConnectedSince() {
        return connectedSince;
    }

    @JsonIgnore
    @Override
    public int getNextTransactionId() {
        return this.transactionIdSource.incrementAndGet();
    }

    @Override
    public void sendStatsQuery(OFStatisticsRequest request, int xid,
                                IOFMessageListener caller) throws IOException {
        request.setXid(xid);
        this.iofMsgListenersMap.put(xid, caller);
        List<OFMessage> msglist = new ArrayList<OFMessage>(1);
        msglist.add(request);
        this.write(msglist);
        return;
    }

    @Override
    public Future<List<OFStatistics>> queryStatistics(OFStatisticsRequest request) throws IOException {
        request.setXid(getNextTransactionId());
        OFStatisticsFuture future = new OFStatisticsFuture(threadPool, this, request.getXid());
        this.statsFutureMap.put(request.getXid(), future);
        List<OFMessage> msglist = new ArrayList<OFMessage>(1);
        msglist.add(request);
        this.write(msglist);
        return future;
    }

    @Override
    public void deliverStatisticsReply(OFStatisticsReply reply) {
        checkForTableStats(reply);
        OFStatisticsFuture future = this.statsFutureMap.get(reply.getXid());
        if (future != null) {
            future.deliverFuture(this, reply);
            // The future will ultimately unregister itself and call
            // cancelStatisticsReply
            return;
        }
        /* Transaction id was not found in statsFutureMap.check the other map */
        IOFMessageListener caller = this.iofMsgListenersMap.get(reply.getXid());
        if (caller != null) {
            caller.receive(this, reply, null);
        }
    }

    @LogMessageDocs({
        @LogMessageDoc(level="INFO",
            message="Switch {switch} flow table is full",
            explanation="The switch flow table at least 98% full, " +
                    "this requires attention if using reactive flow setup"),
        @LogMessageDoc(level="INFO",
            message="Switch {switch} flow table capacity back to normal",
            explanation="The switch flow table is less than 90% full")
    })
    private void checkForTableStats(OFStatisticsReply statReply) {
        if (statReply.getStatisticType() != OFStatisticsType.TABLE) {
            return;
        }
        List<? extends OFStatistics> stats = statReply.getStatistics();
        // Assume a single table only
        OFStatistics stat = stats.get(0);
        if (stat instanceof OFTableStatistics) {
            OFTableStatistics tableStat = (OFTableStatistics) stat;
            int activeCount = tableStat.getActiveCount();
            int maxEntry = tableStat.getMaximumEntries();
            log.debug("Switch {} active entries {} max entries {}",
                    new Object[] { this.stringId, activeCount, maxEntry});
            int percentFull = activeCount * 100 / maxEntry;
            if (flowTableFull && percentFull < 90) {
                log.info("Switch {} flow table capacity is back to normal",
                        toString());
                floodlightProvider.addSwitchEvent(this.datapathId,
                        "SWITCH_FLOW_TABLE_NORMAL < 90% full", false);
            } else if (percentFull >= 98) {
                log.info("Switch {} flow table is almost full", toString());
                floodlightProvider.addSwitchEvent(this.datapathId,
                        "SWITCH_FLOW_TABLE_ALMOST_FULL >= 98% full", false);
            }
        }
    }


    @Override
    public void cancelStatisticsReply(int transactionId) {
        if (null ==  this.statsFutureMap.remove(transactionId)) {
            this.iofMsgListenersMap.remove(transactionId);
        }
    }

    @Override
    public void cancelAllStatisticsReplies() {
        /* we don't need to be synchronized here. Even if another thread
         * modifies the map while we're cleaning up the future will eventuall
         * timeout */
        for (OFStatisticsFuture f : statsFutureMap.values()) {
            f.cancel(true);
        }
        statsFutureMap.clear();
        iofMsgListenersMap.clear();
    }


    /**
     * @param floodlightProvider the floodlightProvider to set
     */
    @JsonIgnore
    public void setFloodlightProvider(
            IFloodlightProviderService floodlightProvider) {
        this.floodlightProvider = floodlightProvider;
    }

    @Override
    @JsonIgnore
    public void setThreadPoolService(IThreadPoolService tp) {
        this.threadPool = tp;
    }

    @Override
    @JsonIgnore
    public void setDebugCounterService(IDebugCounterService debugCounters)
            throws CounterException {
        this.debugCounters = debugCounters;
        registerOverloadCounters();
    }

    @JsonIgnore
    @Override
    public boolean isConnected() {
        // no lock needed since we use volatile
        return connected;
    }

    @JsonIgnore
    @Override
    public boolean isActive() {
        // no lock needed since we use volatile
        return isConnected() && this.role == Role.MASTER;
    }

    @Override
    @JsonIgnore
    public void setConnected(boolean connected) {
        // No lock needed since we use volatile
        if (connected && this.connectedSince == null)
            this.connectedSince = new Date();
        else if (!connected)
            this.connectedSince = null;
        this.connected = connected;
    }

    @Override
    public Role getHARole() {
        return role;
    }

    @JsonIgnore
    @Override
    public void setHARole(Role role) {
        this.role = role;
    }

    @LogMessageDoc(level="INFO",
            message="Switch {switch} flow cleared",
            explanation="The switch flow table has been cleared, " +
                    "this normally happens on switch connection")
    @Override
    public void clearAllFlowMods() {
        if (channel == null || !isConnected())
            return;
        // Delete all pre-existing flows
        log.info("Clearing all flows on switch {}", this);
        OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
        OFMessage fm = ((OFFlowMod) floodlightProvider.getOFMessageFactory()
            .getMessage(OFType.FLOW_MOD))
                .setMatch(match)
            .setCommand(OFFlowMod.OFPFC_DELETE)
            .setOutPort(OFPort.OFPP_NONE)
            .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH));
        fm.setXid(getNextTransactionId());
        OFMessage barrierMsg = floodlightProvider.getOFMessageFactory().getMessage(
                OFType.BARRIER_REQUEST);
        barrierMsg.setXid(getNextTransactionId());
        List<OFMessage> msglist = new ArrayList<OFMessage>(2);
        msglist.add(fm);
        msglist.add(barrierMsg);
        channel.write(msglist);
    }

    @Override
    public boolean updateBroadcastCache(Long entry, Short port) {
        if (timedCache.update(entry)) {
            AtomicLong count = portBroadcastCacheHitMap.get(port);
            if(count == null) {
                AtomicLong newCount = new AtomicLong(0);
                AtomicLong retrieved;
                if((retrieved = portBroadcastCacheHitMap.putIfAbsent(port, newCount)) == null ) {
                    count = newCount;
                } else {
                    count = retrieved;
                }
            }
            count.incrementAndGet();
            return true;
        } else {
            return false;
        }
    }

    @Override
    @JsonIgnore
    public Map<Short, Long> getPortBroadcastHits() {
        Map<Short, Long> res = new HashMap<Short, Long>();
        for (Map.Entry<Short, AtomicLong> entry : portBroadcastCacheHitMap.entrySet()) {
            res.put(entry.getKey(), entry.getValue().get());
        }
        return res;
    }

    @Override
    public void flush() {
        Map<IOFSwitch,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
        List<OFMessage> msglist = msg_buffer_map.get(this);
        if ((msglist != null) && (msglist.size() > 0)) {
            /* ============================ BIG CAVEAT ===============================
             * This code currently works, but relies on undocumented behavior of
             * netty.
             *
             * The method org.jboss.netty.channel.Channel.write(Object)
             * (invoked from this.write(List<OFMessage> msg) is currently
             * documented to be <emph>asynchronous</emph>. If the method /were/ truely
             * asynchronous, this would break our code (because we are clearing the
             * msglist right after calling write.
             *
             * For now, Netty actually invokes the conversion pipeline before doing
             * anything asynchronous, so we are safe. But we should probably change
             * that behavior.
             */
            this.write(msglist);
            msglist.clear();
        }
    }

    public static void flush_all() {
        Map<IOFSwitch,List<OFMessage>> msg_buffer_map = local_msg_buffer.get();
        for (IOFSwitch sw : msg_buffer_map.keySet()) {
            sw.flush();
        }
    }


    /**
     * Get the IP Address for the switch
     * @return the inet address
     */
    @Override
    @JsonSerialize(using=ToStringSerializer.class)
    public SocketAddress getInetAddress() {
        if (channel == null)
            return null;
        return channel.getRemoteAddress();
    }

    @Override
    public Future<OFFeaturesReply> querySwitchFeaturesReply()
            throws IOException {
        OFMessage request =
                floodlightProvider.getOFMessageFactory().
                    getMessage(OFType.FEATURES_REQUEST);
        request.setXid(getNextTransactionId());
        OFFeaturesReplyFuture future =
                new OFFeaturesReplyFuture(threadPool, this, request.getXid());
        this.featuresFutureMap.put(request.getXid(), future);
        List<OFMessage> msglist = new ArrayList<OFMessage>(1);
        msglist.add(request);
        this.write(msglist);
        return future;
    }

    @Override
    public void deliverOFFeaturesReply(OFMessage reply) {
        OFFeaturesReplyFuture future = this.featuresFutureMap.get(reply.getXid());
        if (future != null) {
            future.deliverFuture(this, reply);
            // The future will ultimately unregister itself and call
            // cancelFeaturesReply
            return;
        }
        log.error("Switch {}: received unexpected featureReply", this);
    }

    @Override
    public void cancelFeaturesReply(int transactionId) {
        this.featuresFutureMap.remove(transactionId);
    }


    @Override
    public int getBuffers() {
        return buffers;
    }


    @Override
    public int getActions() {
        return actions;
    }


    @Override
    public int getCapabilities() {
        return capabilities;
    }


    @Override
    public byte getTables() {
        return tables;
    }

    @Override
    public OFDescriptionStatistics getDescriptionStatistics() {
        return new OFDescriptionStatistics(description);
    }


    @Override
    public void setFloodlightProvider(Controller controller) {
        floodlightProvider = controller;
    }


    /**
     * For switch drivers to set thresholds, all rates in per second
     * @param pktInHigh - above this start throttling
     * @param pktInLow  - below this stop throttling
     * @param pktInPerMac  - block host if unique pktIn rate reaches this
     * @param pktInPerPort - block port if unique pktIn rate reaches this
     */
    @JsonIgnore
    protected void setInputThrottleThresholds(int pktInHigh, int pktInLow,
            int pktInPerMac, int pktInPerPort) {
        packetInRateThresholdHigh = pktInHigh;
        packetInRateThresholdLow = pktInLow;
        packetInRatePerMacThreshold = pktInPerMac;
        packetInRatePerPortThreshold = pktInPerPort;
    }

    /**
     * Return if switch has exceeded the high threshold of packet in rate.
     * @return
     */
    @Override
    public boolean isOverloaded() {
        return packetInThrottleEnabled;
    }

    /**
     * Determine if this message should be dropped.
     *
     * We compute the current rate by taking a timestamp every 100 messages.
     * Could change to a more complex scheme if more accuracy is needed.
     *
     * Enable throttling if the rate goes above packetInRateThresholdHigh
     * Disable throttling when the rate drops below packetInRateThresholdLow
     *
     * While throttling is enabled, we do the following:
     *  - Remove duplicate packetIn's mapped to the same OFMatch
     *  - After filtering, if packetIn rate per host (mac) is above
     *    packetInRatePerMacThreshold, push a flow mod to block mac on port
     *  - After filtering, if packetIn rate per port is above
     *    packetInRatePerPortThreshold, push a flow mod to block port
     *  - Allow blocking flow mods have a hard timeout and expires automatically
     *
     * TODO: keep a history of all events related in input throttling
     *
     * @param ofm
     * @return
     */
    @Override
    public boolean inputThrottled(OFMessage ofm) {
        if (ofm.getType() != OFType.PACKET_IN) {
            return false;
        }
        ctrSwitchPktin.updateCounterNoFlush();
        // Compute current packet in rate
        messageCount++;
        if (messageCount % 1000 == 0) {
            long now = System.currentTimeMillis();
            if (now != lastMessageTime) {
                currentRate = (int) (1000000 / (now - lastMessageTime));
                lastMessageTime = now;
            } else {
                currentRate = Integer.MAX_VALUE;
            }
        }
        if (!packetInThrottleEnabled) {
            if (currentRate <= packetInRateThresholdHigh) {
                return false; // most common case
            }
            enablePacketInThrottle();
        } else if (currentRate < packetInRateThresholdLow) {
            disablePacketInThrottle();
            return false;
        }

        // Now we are in the slow path where we need to do filtering
        // First filter based on OFMatch
        OFPacketIn pin = (OFPacketIn)ofm;
        OFMatch match = new OFMatch();
        match.loadFromPacket(pin.getPacketData(), pin.getInPort());
        if (ofMatchCache.update(match)) {
           ctrSwitchPktinDrops.updateCounterNoFlush();
            return true;
        }

        // We have packet in with a distinct flow, check per mac rate
        messageCountUniqueOFMatch++;
        if ((messageCountUniqueOFMatch % packetInRatePerMacThreshold) == 1) {
            checkPerSourceMacRate(pin);
        }

        // Check per port rate
        if ((messageCountUniqueOFMatch % packetInRatePerPortThreshold) == 1) {
            checkPerPortRate(pin);
        }
        return false;
    }

    /**
     * We rely on the fact that packet in processing is single threaded
     * per packet-in, so no locking is necessary.
     */
    private void disablePacketInThrottle() {
        ofMatchCache = null;
        macCache = null;
        macBlockedCache = null;
        portCache = null;
        portBlockedCache = null;
        packetInThrottleEnabled = false;
        floodlightProvider.addSwitchEvent(this.datapathId,
                "SWITCH_OVERLOAD_THROTTLE_DISABLED ==>" +
                "Pktin rate " + currentRate + "/s", false);
        log.info("Packet in rate is {}, disable throttling on {}",
                currentRate, this);
    }

    private void enablePacketInThrottle() {
        ofMatchCache = new TimedCache<OFMatch>(2048, 5000); // 5 second interval
        macCache = new TimedCache<Long>(64, 1000 )// remember last second
        macBlockedCache = new TimedCache<Long>(256, 5000 )// 5 second interval
        portCache = new TimedCache<Short>(16, 1000 )// rememeber last second
        portBlockedCache = new TimedCache<Short>(64, 5000 )// 5 second interval
        packetInThrottleEnabled = true;
        messageCountUniqueOFMatch = 0;
        floodlightProvider.addSwitchEvent(this.datapathId,
                "SWITCH_OVERLOAD_THROTTLE_ENABLED ==>" +
                "Pktin rate " + currentRate + "/s", false);
        log.info("Packet in rate is {}, enable throttling on {}",
                currentRate, this);
    }

    private void registerOverloadCounters() throws CounterException {
        if (debugCountersRegistered) {
            return;
        }
        if (debugCounters == null) {
            log.error("Debug Counter Service not found");
            debugCounters = new NullDebugCounter();
            debugCountersRegistered = true;
        }
        // every level of the hierarchical counter has to be registered
        // even if they are not used
        ctrSwitch = debugCounters.registerCounter(
                                   PACKAGE , stringId,
                                   "Counter for this switch",
                                   CounterType.ALWAYS_COUNT);
        ctrSwitchPktin = debugCounters.registerCounter(
                                   PACKAGE, stringId + "/pktin",
                                   "Packet in counter for this switch",
                                   CounterType.ALWAYS_COUNT);
        ctrSwitchWrite = debugCounters.registerCounter(
                                   PACKAGE, stringId + "/write",
                                   "Write counter for this switch",
                                   CounterType.ALWAYS_COUNT);
        ctrSwitchPktinDrops = debugCounters.registerCounter(
                                   PACKAGE, stringId + "/pktin/drops",
                                   "Packet in throttle drop count",
                                   CounterType.ALWAYS_COUNT,
                                   IDebugCounterService.CTR_MDATA_WARN);
        ctrSwitchWriteDrops = debugCounters.registerCounter(
                                   PACKAGE, stringId + "/write/drops",
                                   "Switch write throttle drop count",
                                   CounterType.ALWAYS_COUNT,
                                   IDebugCounterService.CTR_MDATA_WARN);
    }

    /**
     * Check if we have sampled this mac in the last second.
     * Since we check every packetInRatePerMacThreshold packets,
     * the presence of the mac in the macCache means the rate is
     * above the threshold in a statistical sense.
     *
     * Take care not to block topology probing packets. Also don't
     * push blocking flow mod if we have already done so within the
     * last 5 seconds.
     *
     * @param pin
     * @return
     */
    private void checkPerSourceMacRate(OFPacketIn pin) {
        byte[] data = pin.getPacketData();
        byte[] mac = Arrays.copyOfRange(data, 6, 12);
        MACAddress srcMac = MACAddress.valueOf(mac);
        short ethType = (short) (((data[12] & 0xff) << 8) + (data[13] & 0xff));
        if (ethType != Ethernet.TYPE_LLDP && ethType != Ethernet.TYPE_BSN &&
                macCache.update(srcMac.toLong())) {
            // Check if we already pushed a flow in the last 5 seconds
            if (macBlockedCache.update(srcMac.toLong())) {
                return;
            }
            // write out drop flow per srcMac
            int port = pin.getInPort();
            SwitchPort swPort = new SwitchPort(getId(), port);
            ForwardingBase.blockHost(floodlightProvider,
                    swPort, srcMac.toLong(), (short) 5,
                    AppCookie.makeCookie(OFSWITCH_APP_ID, 0));
            floodlightProvider.addSwitchEvent(this.datapathId,
                    "SWITCH_PORT_BLOCKED_TEMPORARILY " +
                    "OFPort " + port + " mac " + srcMac, false);
            log.info("Excessive packet in from {} on {}, block host for 5 sec",
                    srcMac.toString(), swPort);
        }
    }

    /**
     * Works in a similar way as checkPerSourceMacRate().
     *
     * TODO Don't block ports with links?
     *
     * @param pin
     * @return
     */
    private void checkPerPortRate(OFPacketIn pin) {
        Short port = pin.getInPort();
        if (portCache.update(port)) {
            // Check if we already pushed a flow in the last 5 seconds
            if (portBlockedCache.update(port)) {
                return;
            }
            // write out drop flow per port
            SwitchPort swPort = new SwitchPort(getId(), port);
            ForwardingBase.blockHost(floodlightProvider,
                    swPort, -1L, (short) 5,
                    AppCookie.makeCookie(OFSWITCH_APP_ID, 1));
            floodlightProvider.addSwitchEvent(this.datapathId,
                    "SWITCH_PORT_BLOCKED_TEMPORARILY " +
                    "OFPort " + port, false);
            log.info("Excessive packet in from {}, block port for 5 sec",
                    swPort);
        }
    }

    @Override
    @JsonIgnore
    @LogMessageDoc(level="WARN",
        message="Switch {switch} flow table is full",
        explanation="The controller received flow table full " +
                "message from the switch, could be caused by increased " +
                "traffic pattern",
                recommendation=LogMessageDoc.REPORT_CONTROLLER_BUG)
    public void setTableFull(boolean isFull) {
        if (isFull && !flowTableFull) {
            floodlightProvider.addSwitchEvent(this.datapathId,
                    "SWITCH_FLOW_TABLE_FULL " +
                    "Table full error from switch", false);
            log.warn("Switch {} flow table is full", stringId);
        }
        flowTableFull = isFull;
    }


    @Override
    public short getAccessFlowPriority() {
        return accessFlowPriority;
    }


    @Override
    public short getCoreFlowPriority() {
        return coreFlowPriority;
    }


    @Override
    public void setAccessFlowPriority(short accessFlowPriority) {
        this.accessFlowPriority = accessFlowPriority;
    }


    @Override
    public void setCoreFlowPriority(short coreFlowPriority) {
        this.coreFlowPriority = coreFlowPriority;
    }

    @Override
    public void startDriverHandshake() {
        if (startDriverHandshakeCalled)
            throw new SwitchDriverSubHandshakeAlreadyStarted();
        startDriverHandshakeCalled = true;
    }

    @Override
    public boolean isDriverHandshakeComplete() {
        if (!startDriverHandshakeCalled)
            throw new SwitchDriverSubHandshakeNotStarted();
        return true;
    }

    @Override
    public void processDriverHandshakeMessage(OFMessage m) {
        if (startDriverHandshakeCalled)
            throw new SwitchDriverSubHandshakeCompleted(m);
        else
            throw new SwitchDriverSubHandshakeNotStarted();
    }
}
TOP

Related Classes of net.floodlightcontroller.core.OFSwitchBase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.