/* * 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.core.ConstructionException; import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; 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.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 its * 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 toPacketOut = new HashSet(); // make sure that topoManager/datPacketService aren't pulled out from // under us lock.readLock().lock(); if (topoManager == null || dataPacketService == null || swMgr == null) { lock.readLock().unlock(); 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."); lock.readLock().unlock(); 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(); } @Override public void setMode(BroadcastMode m) { lock.writeLock().lock(); mode = m; lock.writeLock().unlock(); } }