Adding a simple broadcast handler 00/1700/7
authorColin Dixon <ckd@us.ibm.com>
Mon, 10 Jun 2013 16:00:18 +0000 (11:00 -0500)
committerGerrit Code Review <gerrit@opendaylight.org>
Fri, 25 Oct 2013 03:37:51 +0000 (03:37 +0000)
1.) An interface IBroadcastHandler for enabling/disabling and controlling
    the mode of operation for a module that provides broadcast handling and
    a function to broadcast a packet.
2.) An implementation of that interface, SimpleBroadcastHandler, that both
    handles broadcast traffic received from the DataPacketService and
    provides a way to braodcast a synthetically-generated packet.

There are four modes only two of which have currently been extensively
tested: DISABLED and BROADCAST_TO_HOSTS.

Change-Id: I6ab6042ad7ec08b819a8840e8518022c7805f4e2
Signed-off-by: Colin Dixon <ckd@us.ibm.com>
opendaylight/samples/simpleforwarding/pom.xml
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/IBroadcastHandler.java [new file with mode: 0644]
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/IBroadcastPortSelector.java [new file with mode: 0644]
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/Activator.java
opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/SimpleBroadcastHandlerImpl.java [new file with mode: 0644]

index 859248b..edd146e 100644 (file)
@@ -37,6 +37,7 @@
               org.opendaylight.controller.clustering.services,
               org.opendaylight.controller.sal.action,
               org.opendaylight.controller.sal.flowprogrammer,
+              org.opendaylight.controller.sal.inventory,
               org.opendaylight.controller.sal.match,
               org.opendaylight.controller.sal.packet,
               org.opendaylight.controller.sal.routing,
diff --git a/opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/IBroadcastHandler.java b/opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/IBroadcastHandler.java
new file mode 100644 (file)
index 0000000..8140019
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013 IBM and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.samples.simpleforwarding;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+
+/**
+ * Provides support for flooding/broadcasting of packets.
+ */
+public interface IBroadcastHandler {
+
+    /**
+     * The mode to select which ports to broadcast a given packet. See the
+     * individual modes for the expected behavior.
+     */
+    static enum BroadcastMode {
+        /**
+         * Turn off broadcast handling and ignore all received data packets.
+         */
+        DISABLED,
+
+        /**
+         * sends broadcast packets out ports where there are known hosts as
+         * discovered by {@link ITopologyManager#getNodeConnectorWithHost}.
+         */
+        BROADCAST_TO_HOSTS,
+
+        /**
+         * sends broadcast packets out all non-internal links as discovered by
+         * {@link ITopologyManager#isInternal}. Also ignores ports which have
+         * {@link NodeConnector#getType} of "SW" indicating OFPP_LOCAL.
+         */
+        BROADCAST_TO_NONINTERNAL,
+
+        /**
+         * sends broadcast packets out the ports specified by an external
+         * implementation of {@link IBroadcastPortSelector}.
+         */
+        EXTERNAL_QUERY
+    };
+
+    /**
+     * Set the {@link BroadcastMode} for this {@link IBroadcastHandler}.
+     * @param m
+     */
+    void setMode(BroadcastMode m);
+
+    /**
+     * Safely flood/broadcast a {@link RawPacket} received on a given
+     * {@link NodeConnector}.
+     *
+     * @param pkt
+     *            The packet to flood/broadcast
+     * @return <tt>true</tt> if the broadcast is successful, <tt>false</tt>
+     *         otherwise
+     */
+    boolean broadcastPacket(RawPacket pkt);
+}
diff --git a/opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/IBroadcastPortSelector.java b/opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/IBroadcastPortSelector.java
new file mode 100644 (file)
index 0000000..438c921
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2013 IBM and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.samples.simpleforwarding;
+
+import java.util.Set;
+
+import org.opendaylight.controller.sal.core.NodeConnector;
+
+public interface IBroadcastPortSelector {
+    Set<NodeConnector> getBroadcastPorts();
+}
index f3a38ed..1a71511 100644 (file)
@@ -9,6 +9,9 @@
 
 package org.opendaylight.controller.samples.simpleforwarding.internal;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
+
 import org.apache.felix.dm.Component;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -18,8 +21,12 @@ import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManage
 import org.opendaylight.controller.hosttracker.IfIptoHost;
 import org.opendaylight.controller.hosttracker.IfNewHostNotify;
 import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
 import org.opendaylight.controller.sal.routing.IListenRoutingUpdates;
 import org.opendaylight.controller.sal.routing.IRouting;
+import org.opendaylight.controller.samples.simpleforwarding.IBroadcastHandler;
+import org.opendaylight.controller.samples.simpleforwarding.IBroadcastPortSelector;
 import org.opendaylight.controller.switchmanager.IInventoryListener;
 import org.opendaylight.controller.switchmanager.ISwitchManager;
 import org.opendaylight.controller.topologymanager.ITopologyManager;
@@ -39,7 +46,8 @@ public class Activator extends ComponentActivatorAbstractBase {
      * Object
      */
     public Object[] getImplementations() {
-        Object[] res = { SimpleForwardingImpl.class };
+        Object[] res = { SimpleForwardingImpl.class,
+                         SimpleBroadcastHandlerImpl.class };
         return res;
     }
 
@@ -88,6 +96,29 @@ public class Activator extends ComponentActivatorAbstractBase {
             c.add(createContainerServiceDependency(containerName).setService(
                     IRouting.class).setCallbacks("setRouting", "unsetRouting")
                     .setRequired(false));
+        }else if (imp.equals(SimpleBroadcastHandlerImpl.class)) {
+            Dictionary<String, String> props = new Hashtable<String, String>();
+            props.put("salListenerName", "simplebroadcasthandler");
+
+            // export the service
+            c.setInterface(new String[] { IBroadcastHandler.class.getName(),
+                    IListenDataPacket.class.getName() }, props);
+
+            c.add(createContainerServiceDependency(containerName).setService(
+                    IDataPacketService.class).setCallbacks("setDataPacketService",
+                   "unsetDataPacketService").setRequired(false));
+
+            c.add(createContainerServiceDependency(containerName).setService(
+                   ITopologyManager.class).setCallbacks("setTopologyManager",
+                   "unsetTopologyManager").setRequired(true));
+
+            c.add(createContainerServiceDependency(containerName).setService(
+                   IBroadcastPortSelector.class).setCallbacks("setBroadcastPortSelector",
+                   "unsetBroadcastPortSelector").setRequired(false));
+
+            c.add(createContainerServiceDependency(containerName).setService(
+                   ISwitchManager.class).setCallbacks("setSwitchManager",
+                   "unsetSwitchManager").setRequired(false));
         }
     }
 }
diff --git a/opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/SimpleBroadcastHandlerImpl.java b/opendaylight/samples/simpleforwarding/src/main/java/org/opendaylight/controller/samples/simpleforwarding/internal/SimpleBroadcastHandlerImpl.java
new file mode 100644 (file)
index 0000000..d2016b1
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2013 IBM and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.samples.simpleforwarding.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.opendaylight.controller.sal.packet.Ethernet;
+import org.opendaylight.controller.sal.packet.IDataPacketService;
+import org.opendaylight.controller.sal.packet.IListenDataPacket;
+import org.opendaylight.controller.sal.core.ConstructionException;
+import org.opendaylight.controller.sal.core.Node;
+import org.opendaylight.controller.sal.core.NodeConnector;
+import org.opendaylight.controller.sal.packet.Packet;
+import org.opendaylight.controller.sal.packet.PacketResult;
+import org.opendaylight.controller.sal.packet.RawPacket;
+import org.opendaylight.controller.sal.utils.EtherTypes;
+import org.opendaylight.controller.samples.simpleforwarding.IBroadcastHandler;
+import org.opendaylight.controller.samples.simpleforwarding.IBroadcastPortSelector;
+import org.opendaylight.controller.switchmanager.ISwitchManager;
+import org.opendaylight.controller.topologymanager.ITopologyManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The simple broadcast handler simply sends broadcast packets out all ports
+ * that are not known to belong to an internal, i.e., switch-switch, link. Note
+ * that this is *not* safe in the general case when an OpenDaylight-controlled
+ * network has L2 peering with normal a normal L2 network. It is entirely
+ * possible for a packet to be flooded to a legacy/non-controlled switch and
+ * then be reflected back into the OpenDaylight-controlled region resulting in a
+ * loop.
+ */
+public class SimpleBroadcastHandlerImpl implements IBroadcastHandler, IListenDataPacket {
+
+    private static Logger log = LoggerFactory.getLogger(SimpleBroadcastHandlerImpl.class);
+    protected IDataPacketService dataPacketService = null;
+    protected ITopologyManager topoManager = null;
+    protected ISwitchManager swMgr = null;
+    protected IBroadcastPortSelector bcastPorts = null;
+
+    protected ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+    BroadcastMode mode = BroadcastMode.DISABLED;
+
+    @Override
+    public PacketResult receiveDataPacket(RawPacket inPkt) {
+        /*
+         * note that this assumes that the protocol plugin will do appropriate
+         * filtering to ensure that this only receives packets for it's
+         * container.
+         */
+
+        if (mode == BroadcastMode.DISABLED) {
+            return PacketResult.IGNORED;
+        }
+
+        Packet decodedPkt = this.dataPacketService.decodeDataPacket(inPkt);
+        if (decodedPkt instanceof Ethernet) {
+            Ethernet eth = (Ethernet) decodedPkt;
+
+            // TODO: should we offer an option to not handle ARP since the
+            // ARPHandler already does that
+
+            // ignore LLDP
+            if (eth.getEtherType() != EtherTypes.LLDP.shortValue()) {
+
+                if (eth.isBroadcast()) {
+                    broadcastPacket(inPkt);
+                } else if (eth.isMulticast()) {
+                    // TODO: for now just treat multicast as broadcast
+                    broadcastPacket(inPkt);
+                }
+            }
+        }
+
+        return PacketResult.KEEP_PROCESSING;
+    }
+
+    @Override
+    public boolean broadcastPacket(RawPacket pkt) {
+        Set<NodeConnector> toPacketOut = new HashSet<NodeConnector>();
+
+        // make sure that topoManager/datPacketService aren't pulled out from
+        // under us
+        lock.readLock().lock();
+        if (topoManager == null || dataPacketService == null
+            || swMgr == null) {
+            return false;
+        }
+
+        // find all non-internal NodeConnectors
+        switch (mode) {
+            case DISABLED:
+                // intentionally blank; don't send the packet anywhere
+                break;
+
+            case BROADCAST_TO_HOSTS:
+                toPacketOut.addAll(topoManager.getNodeConnectorWithHost());
+                break;
+
+            case BROADCAST_TO_NONINTERNAL:
+                for (Node n : swMgr.getNodes()) {
+                    // getUpNodeConnectors will filter out NodeConnectors of type "SW"
+                    for (NodeConnector nc : swMgr.getUpNodeConnectors(n)) {
+                        if (!topoManager.isInternal(nc)) {
+                            toPacketOut.add(nc);
+                        }
+                    }
+                }
+                break;
+
+            case EXTERNAL_QUERY:
+                if (bcastPorts != null) {
+                    toPacketOut.addAll(bcastPorts.getBroadcastPorts());
+                } else {
+                    log.error("Mode set to "
+                              + BroadcastMode.EXTERNAL_QUERY
+                              + ", but no external source of broadcast ports was provided.");
+                    return false;
+                }
+                break;
+
+            default:
+                log.error("Mode " + mode + " is not supported.");
+                break;
+        }
+
+        // remove the NodeConnector it came in on
+        toPacketOut.remove(pkt.getIncomingNodeConnector());
+
+        // send it out all the node connectors
+        for (NodeConnector nc : toPacketOut) {
+            try {
+                RawPacket toSend = new RawPacket(pkt);
+                toSend.setOutgoingNodeConnector(nc);
+                dataPacketService.transmitDataPacket(toSend);
+            } catch (ConstructionException e) {
+                log.error("Could create packet: {}", e);
+            }
+        }
+
+        lock.readLock().unlock();
+
+        return true;
+    }
+
+    public void setDataPacketService(IDataPacketService s) {
+        // make sure dataPacketService doesn't change while we're in the middle
+        // of stuff
+        lock.writeLock().lock();
+        this.dataPacketService = s;
+        lock.writeLock().unlock();
+    }
+
+    public void unsetDataPacketService(IDataPacketService s) {
+        // make sure dataPacketService doesn't change while we're in the middle
+        // of stuff
+        lock.writeLock().lock();
+        if (this.dataPacketService == s) {
+            this.dataPacketService = null;
+        }
+        lock.writeLock().unlock();
+    }
+
+    public void setTopologyManager(ITopologyManager t) {
+        // make sure topoManager doesn't change while we're in the middle of
+        // stuff
+        lock.writeLock().lock();
+        this.topoManager = t;
+        lock.writeLock().unlock();
+    }
+
+    public void unsetTopologyManager(ITopologyManager t) {
+        // make sure topoManager doesn't change while we're in the middle of
+        // stuff
+        lock.writeLock().lock();
+        if (this.topoManager == t) {
+            this.topoManager = null;
+        }
+        lock.writeLock().unlock();
+    }
+
+    public void setSwitchManager(ISwitchManager i) {
+        lock.writeLock().lock();
+        this.swMgr = i;
+        lock.writeLock().unlock();
+    }
+
+    public void unsetSwitchManager(ISwitchManager i) {
+        lock.writeLock().lock();
+        if (this.swMgr == i) {
+            this.swMgr = null;
+        }
+        lock.writeLock().unlock();
+    }
+
+    public void setBroadcastPortSelector(IBroadcastPortSelector bps) {
+        lock.writeLock().lock();
+        bcastPorts = bps;
+        lock.writeLock().unlock();
+    }
+
+    public void unsetBroadcastPortSelector(IBroadcastPortSelector bps) {
+        lock.writeLock().lock();
+        if (bcastPorts == bps) {
+            this.bcastPorts = null;
+        }
+        lock.writeLock().unlock();
+    }
+
+    public void setMode(BroadcastMode m) {
+        lock.writeLock().lock();
+        mode = m;
+        lock.writeLock().unlock();
+    }
+
+}

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.