ForwardingBase.java 18.31 KiB
/**
* Copyright 2011, 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.routing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProvider;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.util.AppCookie;
import net.floodlightcontroller.counter.CounterStore;
import net.floodlightcontroller.counter.CounterValue;
import net.floodlightcontroller.counter.ICounter;
import net.floodlightcontroller.devicemanager.DeviceNetworkAddress;
import net.floodlightcontroller.devicemanager.IDeviceManager;
import net.floodlightcontroller.devicemanager.IDeviceManagerAware;
import net.floodlightcontroller.devicemanager.SwitchPort;
import net.floodlightcontroller.devicemanager.internal.Device;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.routing.IRoutingEngine;
import net.floodlightcontroller.routing.IRoutingDecision;
import net.floodlightcontroller.routing.Link;
import net.floodlightcontroller.routing.Route;
import net.floodlightcontroller.topology.ITopology;
import net.floodlightcontroller.topology.SwitchPortTuple;
import org.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.OFType;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.util.HexString;
import org.openflow.util.U16;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class ForwardingBase implements IOFMessageListener, IDeviceManagerAware {
protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class);
public static final short FLOWMOD_DEFAULT_HARD_TIMEOUT=5; // in seconds
protected IFloodlightProvider floodlightProvider;
protected IDeviceManager deviceManager;
protected IRoutingEngine routingEngine;
protected ITopology topology;
protected CounterStore counterStore;
// flow-mod - for use in the cookie
public static final int FORWARDING_APP_ID = 2; // TODO: This must be managed by a global APP_ID class
// Comparator for sorting by SwitchCluster
public Comparator<SwitchPort> clusterIdComparator =
new Comparator<SwitchPort>() {
@Override
public int compare(SwitchPort d1, SwitchPort d2) {
Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches();
IOFSwitch sw1 = switches.get(d1.getSwitchDPID());
IOFSwitch sw2 = switches.get(d2.getSwitchDPID());
Long d1ClusterId = sw1.getSwitchClusterId();
Long d2ClusterId = sw2.getSwitchClusterId();
return d1ClusterId.compareTo(d2ClusterId);
}
};
public void startUp() {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
}
public void shutDown() {
floodlightProvider.removeOFMessageListener(OFType.PACKET_IN, this);
}
@Override
public String getName() {
return "forwarding";
}
@Override
public int getId() {
return FlListenerID.FORWARDINGBASE;
}
/**
* All subclasses must define this function if they want any specific forwarding action
* @param sw Switch that the packet came in from
* @param pi The packet that came in
* @param decision Any decision made by a policy engine
*/
public abstract Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx);
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
switch (msg.getType()) {
case PACKET_IN:
IRoutingDecision decision = null;
if (cntx != null) decision =
IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx);
}
log.error("received an unexpected message {} from switch {}", msg, sw);
return Command.CONTINUE;
}
private void updateCounterStore(IOFSwitch sw, OFFlowMod flowMod) {
if (counterStore != null) {
String packetName = flowMod.getType().toClass().getName();
packetName = packetName.substring(packetName.lastIndexOf('.')+1);
// flowmod is per switch. portid = -1
String counterName = CounterStore.createCounterName(sw.getStringId(), -1, packetName);
try {
ICounter counter = counterStore.getCounter(counterName);
if (counter == null) {
counter = counterStore.createCounter(counterName, CounterValue.CounterType.LONG);
}
counter.increment();
}
catch (IllegalArgumentException e) {
log.error("Invalid Counter, " + counterName);
}
}
}
/**
* Push routes from back to front
* @param route Route to push
* @param match OpenFlow fields to match on
* @param srcSwPort Source switch port for the first hop
* @param dstSwPort Destination switch port for final hop
* @param bufferId BufferId of the original PacketIn
* @return srcSwitchIincluded True if the source switch is included in this route
*/
public boolean pushRoute(Route route, OFMatch match,
Integer wildcard_hints,
SwitchPort srcSwPort,
SwitchPort dstSwPort,
int bufferId,
IOFSwitch srcSwitch,
OFPacketIn pi,
FloodlightContext cntx,
boolean reqeustFlowRemovedNotifn) {
long cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
return pushRoute(route, match, wildcard_hints, srcSwPort, dstSwPort,
bufferId, srcSwitch, pi, cookie, cntx, reqeustFlowRemovedNotifn);
}
/**
* Push routes from back to front
* @param route Route to push
* @param match OpenFlow fields to match on
* @param srcSwPort Source switch port for the first hop
* @param dstSwPort Destination switch port for final hop
* @param bufferId BufferId of the original PacketIn
* @param cookie The cookie to set in each flow_mod
* @return srcSwitchIincluded True if the source switch is included in this route
*/
public boolean pushRoute(Route route, OFMatch match, Integer wildcard_hints,
SwitchPort srcSwPort,
SwitchPort dstSwPort, int bufferId,
IOFSwitch srcSwitch, OFPacketIn pi, long cookie,
FloodlightContext cntx,
boolean reqeustFlowRemovedNotifn) {
boolean srcSwitchIncluded = false;
OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
OFActionOutput action = new OFActionOutput();
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(action);
fm.setIdleTimeout((short)5)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setCookie(cookie)
.setMatch(match)
.setActions(actions)
.setLengthU(OFFlowMod.MINIMUM_LENGTH+OFActionOutput.MINIMUM_LENGTH);
Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches();
IOFSwitch sw = switches.get(dstSwPort.getSwitchDPID());
((OFActionOutput)fm.getActions().get(0)).
setPort((short)dstSwPort.getPort());
if (route != null) {
for (int routeIndx = route.getPath().size() - 1; routeIndx >= 0; --routeIndx) {
Link link = route.getPath().get(routeIndx);
fm.setMatch(wildcard(match, sw, wildcard_hints));
fm.getMatch().setInputPort(link.getInPort());
try {
updateCounterStore(sw, fm);
if (log.isDebugEnabled()) {
log.debug("Pushing Route flowmod routeIndx={} sw={} inPort={} outPort={}",
new Object[] { routeIndx, sw, fm.getMatch().getInputPort(),
((OFActionOutput)fm.getActions().get(0)).getPort() });
}
sw.write(fm, cntx);
// Push the packet out the source switch
if (sw.getId() == srcSwitch.getId()) {
pushPacket(srcSwitch, match, pi, ((OFActionOutput)fm.getActions().get(0)).getPort(), cntx);
srcSwitchIncluded = true;
}
} catch (IOException e) {
log.error("Failure writing flow mod", e);
}
try {
fm = fm.clone();
} catch (CloneNotSupportedException e) {
log.error("Failure cloning flow mod", e);
}
// setup for the next loop iteration
((OFActionOutput)fm.getActions().get(0)).setPort(link.getOutPort());
if (routeIndx > 0) {
sw = floodlightProvider.getSwitches().get(route.getPath().get(routeIndx-1).getDst());
} else {
sw = floodlightProvider.getSwitches().get(route.getId().getSrc());
}
if (sw == null) {
if (log.isWarnEnabled()) {
log.warn("Unable to push route, switch at DPID {} not available",
(routeIndx > 0) ? HexString.toHexString(route.getPath()
.get(routeIndx - 1).getDst()) : HexString
.toHexString(route.getId().getSrc()));
}
return srcSwitchIncluded;
}
}
}
// set the original match for the first switch, and buffer id
fm.setMatch(match);
fm.setBufferId(bufferId);
fm.setMatch(wildcard(match, sw, wildcard_hints));
fm.getMatch().setInputPort((short)srcSwPort.getPort());
// Set the flag to request flow-mod removal notifications only for the
// source switch. The removal message is used to maintain the flow
// cache. Don't set the flag for ARP messages - TODO generalize check
if ((reqeustFlowRemovedNotifn) &&
(match.getDataLayerType() != Ethernet.TYPE_ARP)) {
fm.setFlags(OFFlowMod.OFPFF_SEND_FLOW_REM);
match.setWildcards(fm.getMatch().getWildcards());
}
updateCounterStore(sw, fm);
try {
log.debug("pushRoute flowmod sw={} inPort={} outPort={}",
new Object[] { sw, fm.getMatch().getInputPort(),
((OFActionOutput)fm.getActions().get(0)).getPort() });
log.info("Flow mod sent: Wildcard={} match={}",
Integer.toHexString(fm.getMatch().getWildcards()),
fm.getMatch().toString());
sw.write(fm, cntx);
if (sw.getId() == srcSwitch.getId()) {
pushPacket(srcSwitch, match, pi, ((OFActionOutput)fm.getActions().get(0)).getPort(), cntx);
srcSwitchIncluded = true;
}
match = fm.getMatch();
} catch (IOException e) {
log.error("Failure writing flow mod", e);
}
return srcSwitchIncluded;
}
protected OFMatch wildcard(OFMatch match, IOFSwitch sw, Integer wildcard_hints) {
if (wildcard_hints != null) {
return match.clone().setWildcards(wildcard_hints.intValue());
}
return match.clone();
}
public void pushPacket(IOFSwitch sw, OFMatch match, OFPacketIn pi, short outport, FloodlightContext cntx) {
if (pi == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("PacketOut srcSwitch={} match={} pi={}", new Object[] {sw, match, pi});
}
OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
// set actions
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(new OFActionOutput(outport, (short) 0));
po.setActions(actions)
.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
short poLength = (short)(po.getActionsLength() + OFPacketOut.MINIMUM_LENGTH);
// set buffer_id, in_port
po.setBufferId(pi.getBufferId());
po.setInPort(pi.getInPort());
// set data - only if buffer_id == -1
if (pi.getBufferId() == OFPacketOut.BUFFER_ID_NONE) {
byte[] packetData = pi.getPacketData();
poLength += packetData.length;
po.setPacketData(packetData);
}
po.setLength(poLength);
try {
sw.write(po, cntx);
} catch (IOException e) {
log.error("Failure writing packet out", e);
}
}
public static boolean blockHost(IFloodlightProvider floodlightProvider,
SwitchPortTuple sw_tup, long host_mac,
short hardTimeout) {
if ((sw_tup == null) || sw_tup.getSw() == null) {
return false;
}
IOFSwitch sw = sw_tup.getSw();
short inputPort = sw_tup.getPort().shortValue();
log.debug("blockHost sw={} port={} mac={}",
new Object[] { sw, sw_tup.getPort(), new Long(host_mac) });
// Create flow-mod based on packet-in and src-switch
OFFlowMod fm = (OFFlowMod) floodlightProvider.getOFMessageFactory().getMessage(OFType.FLOW_MOD);
OFMatch match = new OFMatch();
List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop
match.setDataLayerSource(Ethernet.toByteArray(host_mac))
.setInputPort(inputPort)
.setWildcards(OFMatch.OFPFW_ALL & ~OFMatch.OFPFW_DL_SRC & ~OFMatch.OFPFW_IN_PORT);
fm.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
.setHardTimeout((short)hardTimeout)
.setIdleTimeout((short)5)
.setBufferId(OFPacketOut.BUFFER_ID_NONE)
.setMatch(match)
.setActions(actions)
.setLengthU(OFFlowMod.MINIMUM_LENGTH); // +OFActionOutput.MINIMUM_LENGTH);
try {
log.debug("write drop flow-mod sw={} match={} flow-mod={}",
new Object[] {sw, match, fm});
sw.write(fm, null);
}
catch (IOException e) {
log.error("Failure writing deny flow mod", e);
return false;
}
return true;
}
/**
* @param floodlightProvider the floodlightProvider to set
*/
public void setFloodlightProvider(IFloodlightProvider floodlightProvider) {
this.floodlightProvider = floodlightProvider;
}
/**
* @param routingEngine the routingEngine to set
*/
public void setRoutingEngine(IRoutingEngine routingEngine) {
this.routingEngine = routingEngine;
}
/**
* @param deviceManager the deviceManager to set
*/
public void setDeviceManager(IDeviceManager deviceManager) {
this.deviceManager = deviceManager;
}
/**
* @param topology the topology to set
*/
public void setTopology(ITopology topology) {
this.topology = topology;
}
public CounterStore getCounterStore() {
return counterStore;
}
public void setCounterStore(CounterStore counterStore) {
this.counterStore = counterStore;
}
@Override
public void deviceAdded(Device device) {
// NOOP
}
@Override
public void deviceRemoved(Device device) {
// NOOP
}
@Override
public void deviceMoved(Device device, IOFSwitch oldSw, Short oldPort,
IOFSwitch sw, Short port) {
// Build flow mod to delete based on destination mac == device mac
OFMatch match = new OFMatch();
match.setDataLayerDestination(Ethernet.toByteArray(device.getMACAddress()));
match.setWildcards(OFMatch.OFPFW_ALL ^ OFMatch.OFPFW_DL_DST);
OFMessage fm = ((OFFlowMod) floodlightProvider.getOFMessageFactory()
.getMessage(OFType.FLOW_MOD))
.setCommand(OFFlowMod.OFPFC_DELETE)
.setOutPort((short) OFPort.OFPP_NONE.getValue())
.setMatch(match)
.setLength(U16.t(OFFlowMod.MINIMUM_LENGTH));
// Flush to all switches
for (IOFSwitch outSw : floodlightProvider.getSwitches().values()) {
try {
outSw.write(fm, null);
} catch (IOException e) {
log.error("Failure sending flow mod delete for moved device", e);
}
}
}
@Override
public void deviceNetworkAddressAdded(Device device,
DeviceNetworkAddress address) {
}
@Override
public void deviceNetworkAddressRemoved(Device device,
DeviceNetworkAddress address) {
}
@Override
public void deviceVlanChanged(Device device) {
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
return (type.equals(OFType.PACKET_IN) &&
(name.equals("topology") || name.equals("devicemanager")));
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
return false;
}
}