blob: f872e5cd4a99924b2a870a0cb8c52410ebe4d2e6 [file] [log] [blame]
package net.onrc.onos.core.packetservice;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.onrc.onos.api.packet.IPacketListener;
import net.onrc.onos.api.packet.IPacketService;
import net.onrc.onos.core.datagrid.IDatagridService;
import net.onrc.onos.core.datagrid.IEventChannel;
import net.onrc.onos.core.datagrid.IEventChannelListener;
import net.onrc.onos.core.flowprogrammer.IFlowPusherService;
import net.onrc.onos.core.packet.Ethernet;
import net.onrc.onos.core.topology.ITopologyService;
import net.onrc.onos.core.topology.Port;
import net.onrc.onos.core.topology.Switch;
import net.onrc.onos.core.topology.MutableTopology;
import net.onrc.onos.core.util.Dpid;
import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFType;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.types.OFPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
public class PacketModule implements IOFMessageListener, IPacketService,
IFloodlightModule {
private static final Logger log = LoggerFactory.getLogger(PacketModule.class);
private final CopyOnWriteArrayList<IPacketListener> listeners;
private IFloodlightProviderService floodlightProvider;
private MutableTopology mutableTopology;
private IDatagridService datagrid;
private IFlowPusherService flowPusher;
private IEventChannel<Long, PacketOutNotification> packetOutEventChannel;
private static final String PACKET_OUT_CHANNEL_NAME =
"onos.packet_out";
private PacketOutEventHandler packetOutEventHandler =
new PacketOutEventHandler();
private class PacketOutEventHandler implements
IEventChannelListener<Long, PacketOutNotification> {
@Override
public void entryAdded(PacketOutNotification value) {
Multimap<Long, Short> localPorts = HashMultimap.create();
for (IOFSwitch sw : floodlightProvider.getSwitches().values()) {
for (OFPortDesc port : sw.getEnabledPorts()) {
// XXX S fix this to int
localPorts.put(sw.getId(), port.getPortNo().getShortPortNumber());
}
}
Multimap<Long, Short> outPorts = value.calculateOutPorts(
localPorts, mutableTopology);
sendPacketToSwitches(outPorts, value.getPacketData());
}
@Override
public void entryUpdated(PacketOutNotification value) {
entryAdded(value);
}
@Override
public void entryRemoved(PacketOutNotification value) {
// Not used
}
}
public PacketModule() {
listeners = new CopyOnWriteArrayList<>();
}
@Override
public void registerPacketListener(IPacketListener listener) {
listeners.addIfAbsent(listener);
}
@Override
public void sendPacket(Ethernet eth, SwitchPort switchPort) {
SinglePacketOutNotification notification =
new SinglePacketOutNotification(eth.serialize(), 0,
switchPort.getDpid().value(), switchPort.getPortNumber().shortValue());
// TODO We shouldn't care what the destination MAC is
long dstMac = eth.getDestinationMAC().toLong();
packetOutEventChannel.addTransientEntry(dstMac, notification);
}
@Override
public void sendPacket(Ethernet eth, List<SwitchPort> switchPorts) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void broadcastPacketOutEdge(Ethernet eth) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void broadcastPacketOutEdge(Ethernet eth, SwitchPort inSwitchPort) {
BroadcastPacketOutNotification notification =
new BroadcastPacketOutNotification(eth.serialize(), 0,
inSwitchPort.getDpid().value(), inSwitchPort.getPortNumber().shortValue());
long dstMac = eth.getDestinationMAC().toLong();
packetOutEventChannel.addTransientEntry(dstMac, notification);
}
@Override
public String getName() {
return "packetmodule";
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
return false;
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
return false;
}
@Override
public Command receive(IOFSwitch sw, OFMessage msg,
FloodlightContext cntx) {
if (!(msg instanceof OFPacketIn)) {
return Command.CONTINUE;
}
Ethernet eth = IFloodlightProviderService.bcStore.
get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
// FIXME losing port number precision
short inport = (short) cntx.getStorage()
.get(IFloodlightProviderService.CONTEXT_PI_INPORT);
Switch topologySwitch;
Port inPort;
try {
mutableTopology.acquireReadLock();
Dpid dpid = new Dpid(sw.getId());
PortNumber p = PortNumber.uint16(inport);
topologySwitch = mutableTopology.getSwitch(dpid);
inPort = mutableTopology.getPort(dpid, p);
} finally {
mutableTopology.releaseReadLock();
}
if (topologySwitch == null || inPort == null) {
// We can't send packets for switches or ports that aren't in the
// topology yet
return Command.CONTINUE;
}
for (IPacketListener listener : listeners) {
listener.receive(topologySwitch, inPort, eth);
}
return Command.CONTINUE;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
List<Class<? extends IFloodlightService>> services = new ArrayList<>();
services.add(IPacketService.class);
return services;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService>
getServiceImpls() {
Map<Class<? extends IFloodlightService>, IFloodlightService> serviceImpls = new HashMap<>();
serviceImpls.put(IPacketService.class, this);
return serviceImpls;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
List<Class<? extends IFloodlightService>> dependencies = new ArrayList<>();
dependencies.add(IFloodlightProviderService.class);
dependencies.add(ITopologyService.class);
dependencies.add(IDatagridService.class);
dependencies.add(IFlowPusherService.class);
return dependencies;
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
floodlightProvider =
context.getServiceImpl(IFloodlightProviderService.class);
mutableTopology = context.getServiceImpl(ITopologyService.class)
.getTopology();
datagrid = context.getServiceImpl(IDatagridService.class);
flowPusher = context.getServiceImpl(IFlowPusherService.class);
}
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
packetOutEventChannel = datagrid.addListener(PACKET_OUT_CHANNEL_NAME,
packetOutEventHandler,
Long.class,
PacketOutNotification.class);
}
private void sendPacketToSwitches(Multimap<Long, Short> outPorts,
byte[] packetData) {
for (Long dpid : outPorts.keySet()) {
IOFSwitch sw = floodlightProvider.getSwitches().get(dpid);
if (sw == null) {
log.warn("Switch {} not found when sending packet", dpid);
continue;
}
OFFactory factory = sw.getFactory();
List<OFAction> actions = new ArrayList<>();
for (Short port : outPorts.get(dpid)) {
actions.add(factory.actions().output(OFPort.of(port), Short.MAX_VALUE));
}
OFPacketOut po = factory.buildPacketOut()
.setData(packetData)
.setActions(actions)
.build();
flowPusher.add(new Dpid(sw.getId()), po);
}
}
}