--- /dev/null
+/*
+ * 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);
+}
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;
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;
* Object
*/
public Object[] getImplementations() {
- Object[] res = { SimpleForwardingImpl.class };
+ Object[] res = { SimpleForwardingImpl.class,
+ SimpleBroadcastHandlerImpl.class };
return res;
}
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));
}
}
}
--- /dev/null
+/*
+ * 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();
+ }
+
+}