diff --git a/src/main/java/net/floodlightcontroller/core/util/AppCookie.java b/src/main/java/net/floodlightcontroller/core/util/AppCookie.java index 5406bbd8c9a6a2b92b423db0598d225713b83fa4..370256fb1596f98aee6f1946b4008f8f26e2f3b4 100644 --- a/src/main/java/net/floodlightcontroller/core/util/AppCookie.java +++ b/src/main/java/net/floodlightcontroller/core/util/AppCookie.java @@ -69,6 +69,13 @@ public class AppCookie { private static ConcurrentMap<Integer, String> appIdMap = new ConcurrentHashMap<Integer, String>(); + /** + * Returns a mask suitable for matching the App ID within a cookie. + */ + static public U64 getAppFieldMask() { + return U64.of(APP_ID_MASK << APP_ID_SHIFT); + } + /** * Encapsulate an application ID and a user block of stuff into a cookie * diff --git a/src/main/java/net/floodlightcontroller/firewall/Firewall.java b/src/main/java/net/floodlightcontroller/firewall/Firewall.java index 5b63189012de1a7aa544816ac4823673e79d438a..0fb28b24c46b73002f63830800bd812c3922595f 100644 --- a/src/main/java/net/floodlightcontroller/firewall/Firewall.java +++ b/src/main/java/net/floodlightcontroller/firewall/Firewall.java @@ -39,6 +39,7 @@ import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U64; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IOFMessageListener; @@ -47,6 +48,7 @@ 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.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.devicemanager.IDeviceService; @@ -76,6 +78,13 @@ import org.slf4j.LoggerFactory; */ public class Firewall implements IFirewallService, IOFMessageListener, IFloodlightModule { + private static final short APP_ID = 30; + static { + AppCookie.registerApp(APP_ID, "Firewall"); + } + private static final U64 DENY_BCAST_COOKIE = AppCookie.makeCookie(APP_ID, 0xaaaaaaaa); + private static final U64 ALLOW_BCAST_COOKIE = AppCookie.makeCookie(APP_ID, 0x55555555); + private static final U64 RULE_MISS_COOKIE = AppCookie.makeCookie(APP_ID, -1); // service modules needed protected IFloodlightProviderService floodlightProvider; @@ -309,8 +318,11 @@ IFloodlightModule { switch (msg.getType()) { case PACKET_IN: IRoutingDecision decision = null; - if (cntx != null) { + if (cntx == null) { + logger.warn("Firewall unable to request packet drop: FloodlightContext is null."); + } else { decision = IRoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION); + return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx); } break; @@ -560,6 +572,7 @@ IFloodlightModule { decision = new RoutingDecision(sw.getId(), inPort, IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE), IRoutingDecision.RoutingAction.MULTICAST); + decision.setDescriptor(ALLOW_BCAST_COOKIE); decision.addToContext(cntx); } else { if (logger.isTraceEnabled()) { @@ -569,6 +582,7 @@ IFloodlightModule { decision = new RoutingDecision(sw.getId(), inPort, IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE), IRoutingDecision.RoutingAction.DROP); + decision.setDescriptor(DENY_BCAST_COOKIE); decision.addToContext(cntx); } return Command.CONTINUE; @@ -581,6 +595,7 @@ IFloodlightModule { * else if (eth.getEtherType() == Ethernet.TYPE_ARP) { * logger.info("allowing ARP traffic"); decision = new * FirewallDecision(IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD); + * decision.setDescriptor(ALLOW_BCAST_COOKIE); * decision.addToContext(cntx); return Command.CONTINUE; } */ @@ -596,6 +611,11 @@ IFloodlightModule { IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE), IRoutingDecision.RoutingAction.DROP); decision.setMatch(rmp.match); + if (rule == null) { + decision.setDescriptor(RULE_MISS_COOKIE); + } else { + decision.setDescriptor(AppCookie.makeCookie(APP_ID, rule.ruleid)); + } decision.addToContext(cntx); if (logger.isTraceEnabled()) { if (rule == null) { @@ -610,6 +630,7 @@ IFloodlightModule { IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE), IRoutingDecision.RoutingAction.FORWARD_OR_FLOOD); decision.setMatch(rmp.match); + decision.setDescriptor(AppCookie.makeCookie(APP_ID, rule.ruleid)); decision.addToContext(cntx); if (logger.isTraceEnabled()) { logger.trace("Allow rule={} match for PacketIn={}", rule, pi); diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index 9552487f7d11cdb9a700d0d42d4b223635466311..1062f14de6ffc7109a53df84c77722ad4b83f0d9 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -76,6 +76,7 @@ import org.projectfloodlight.openflow.types.IPv4Address; import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.Masked; import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFGroup; import org.projectfloodlight.openflow.types.OFPort; @@ -88,6 +89,10 @@ import org.slf4j.LoggerFactory; public class Forwarding extends ForwardingBase implements IFloodlightModule, IOFSwitchListener, ILinkDiscoveryListener { protected static Logger log = LoggerFactory.getLogger(Forwarding.class); + private final U64 DEFAULT_FORWARDING_COOKIE = AppCookie.makeCookie(FORWARDING_APP_ID, 0); + + // This mask determines how much of each cookie will contain IRoutingDecision descriptor bits. + private final long DECISION_MASK = 0x00000000ffffffffL; // TODO: shrink this mask if you need to add more sub-fields. @Override public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { @@ -104,11 +109,11 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF return Command.CONTINUE; case FORWARD_OR_FLOOD: case FORWARD: - doForwardFlow(sw, pi, cntx, false); + doForwardFlow(sw, pi, decision, cntx, false); return Command.CONTINUE; case MULTICAST: // treat as broadcast - doFlood(sw, pi, cntx); + doFlood(sw, pi, decision, cntx); return Command.CONTINUE; case DROP: doDropFlow(sw, pi, decision, cntx); @@ -123,21 +128,105 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF } if (eth.isBroadcast() || eth.isMulticast()) { - doFlood(sw, pi, cntx); + doFlood(sw, pi, decision, cntx); } else { - doForwardFlow(sw, pi, cntx, false); + doForwardFlow(sw, pi, decision, cntx, false); } } return Command.CONTINUE; } + /** + * Builds a cookie that includes routing decision information. + * + * @param decision The routing decision providing a descriptor, or null + * @return A cookie with our app id and the required routing fields masked-in + */ + protected U64 makeForwardingCookie(IRoutingDecision decision) { + + int user_fields = 0; + + U64 decision_cookie = (decision == null) ? null : decision.getDescriptor(); + if (decision_cookie != null) { + user_fields |= AppCookie.extractUser(decision_cookie) & DECISION_MASK; + } + + // TODO: Mask in any other required fields here (e.g. link failure flowset ID) + + if (user_fields == 0) { + return DEFAULT_FORWARDING_COOKIE; + } + return AppCookie.makeCookie(FORWARDING_APP_ID, user_fields); + } + + /** + * Converts a sequence of masked IRoutingDecision descriptors into masked Forwarding cookies. + * + * This generates a list of masked cookies that can then be matched in flow-mod messages. + * + * @param maskedDescriptors A sequence of masked cookies describing IRoutingDecision descriptors + * @return A collection of masked cookies suitable for flow-mod operations + */ + protected Collection<Masked<U64>> convertRoutingDecisionDescriptors(Iterable<Masked<U64>> maskedDescriptors) { + List<Masked<U64>> result = new ArrayList<Masked<U64>>(); + + for (Masked<U64> maskedDescriptor : maskedDescriptors) { + long user_mask = AppCookie.extractUser(maskedDescriptor.getMask()) & DECISION_MASK; + long user_value = AppCookie.extractUser(maskedDescriptor.getValue()) & user_mask; + + // TODO combine in any other cookie fields you need here. + + result.add( + Masked.of( + AppCookie.makeCookie(FORWARDING_APP_ID, (int)user_value), + AppCookie.getAppFieldMask().or(U64.of(user_mask)) + ) + ); + } + + return result; + } + + /** + * On all active switches, deletes all flows matching the IRoutingDecision descriptors provided + * as arguments. + * + * @param descriptors The descriptors and masks describing which flows to delete. + */ + protected void deleteFlowsByDescriptor(Iterable<Masked<U64>> descriptors) { + Collection<Masked<U64>> masked_cookies = convertRoutingDecisionDescriptors(descriptors); + + if (masked_cookies != null && !masked_cookies.isEmpty()) { + for (DatapathId dpid : switchService.getAllSwitchDpids()) { + IOFSwitch sw = switchService.getActiveSwitch(dpid); + if (sw == null) { + continue; + } + + // Would like to swap these for loops and only build the message set once, + // but doing so would assume all switches are using the same OF protocol version. + Set<OFMessage> msgs = new HashSet<OFMessage>(); + for (Masked<U64> masked_cookie : masked_cookies) { + msgs.add( + sw.getOFFactory().buildFlowDelete() + .setCookie(masked_cookie.getValue()) + .setCookieMask(masked_cookie.getMask()) + .build() + ); + } + + sw.write(msgs); + } + } + } + protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); Match m = createMatchFromPacket(sw, inPort, cntx); OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); // this will be a drop-flow; a flow that will not output to any ports List<OFAction> actions = new ArrayList<OFAction>(); // set no action to drop - U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0); + U64 cookie = makeForwardingCookie(decision); log.info("Droppingggg"); fmb.setCookie(cookie) .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT) @@ -160,7 +249,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF } } - protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, boolean requestFlowRemovedNotifn) { + protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx, boolean requestFlowRemovedNotifn) { OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); IDevice dstDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE); DatapathId source = sw.getId(); @@ -177,7 +266,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD).getEtherType() == EthType.ARP) { log.debug("ARP flows disabled in Forwarding. Flooding ARP packet"); - doFlood(sw, pi, cntx); + doFlood(sw, pi, decision, cntx); return; } @@ -224,24 +313,24 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF */ if (dstDap == null) { log.warn("Could not locate edge attachment point for device {}. Flooding packet"); - doFlood(sw, pi, cntx); + doFlood(sw, pi, decision, cntx); return; } /* It's possible that we learned packed destination while it was in flight */ if (!topologyService.isEdge(source, inPort)) { log.debug("Packet destination is known, but packet was not received on an edge port (rx on {}/{}). Flooding packet", source, inPort); - doFlood(sw, pi, cntx); + doFlood(sw, pi, decision, cntx); return; } + U64 cookie = makeForwardingCookie(decision); Route route = routingEngineService.getRoute(source, inPort, dstDap.getSwitchDPID(), - dstDap.getPort(), U64.of(0)); //cookie = 0, i.e., default route + dstDap.getPort(), cookie); // Cookie currently ignored. May carry useful info in the future. Match m = createMatchFromPacket(sw, inPort, cntx); - U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0); if (route != null) { log.debug("pushRoute inPort={} route={} " + @@ -271,7 +360,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF } } else { log.debug("Destination unknown. Flooding packet"); - doFlood(sw, pi, cntx); + doFlood(sw, pi, decision, cntx); } } @@ -384,9 +473,10 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF * the port is blocked, in which case the packet will be dropped. * @param sw The switch that receives the OFPacketIn * @param pi The OFPacketIn that came to the switch + * @param decision The decision that caused flooding, or null * @param cntx The FloodlightContext associated with this OFPacketIn */ - protected void doFlood(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { + protected void doFlood(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); // Set Action to flood OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); @@ -615,14 +705,16 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF if (srcSw != null) { /* flows matching on src port */ msgs.add(srcSw.getOFFactory().buildFlowDelete() - .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) + .setCookie(DEFAULT_FORWARDING_COOKIE) + .setCookieMask(AppCookie.getAppFieldMask()) .setMatch(srcSw.getOFFactory().buildMatch() .setExact(MatchField.IN_PORT, u.getSrcPort()) .build()) .build()); /* flows outputting to src port */ msgs.add(srcSw.getOFFactory().buildFlowDelete() - .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) + .setCookie(DEFAULT_FORWARDING_COOKIE) + .setCookieMask(AppCookie.getAppFieldMask()) .setOutPort(u.getSrcPort()) .build()); srcSw.write(msgs); @@ -638,14 +730,16 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF /* flows matching on dst port */ msgs.clear(); msgs.add(dstSw.getOFFactory().buildFlowDelete() - .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) + .setCookie(DEFAULT_FORWARDING_COOKIE) + .setCookieMask(AppCookie.getAppFieldMask()) .setMatch(dstSw.getOFFactory().buildMatch() .setExact(MatchField.IN_PORT, u.getDstPort()) .build()) .build()); /* flows outputting to dst port */ msgs.add(dstSw.getOFFactory().buildFlowDelete() - .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) + .setCookie(DEFAULT_FORWARDING_COOKIE) + .setCookieMask(AppCookie.getAppFieldMask()) .setOutPort(u.getDstPort()) .build()); dstSw.write(msgs); diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java index 6af24c6e35a14bdcbc141c24ad020d4423f7de13..6d3b67f8e9fda5538b89cc938fb46c0ac917d252 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java @@ -20,6 +20,7 @@ package net.floodlightcontroller.routing; import java.util.List; import org.projectfloodlight.openflow.protocol.match.Match; +import org.projectfloodlight.openflow.types.U64; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.FloodlightContextStore; @@ -63,4 +64,6 @@ public interface IRoutingDecision { public void setMatch(Match match); public int getHardTimeout(); public void setHardTimeout(short hardTimeout); + public U64 getDescriptor(); + public void setDescriptor(U64 descriptor); } diff --git a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java index e72ccfc6eb56ce2652b013efbcb64fc2e4b6d4cc..8ce5c5c9415fcd14a3dcd34de0264b719b5536f7 100644 --- a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java @@ -23,6 +23,7 @@ import java.util.List; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.U64; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.devicemanager.IDevice; @@ -38,6 +39,7 @@ public class RoutingDecision implements IRoutingDecision { protected IDevice srcDevice; protected List<IDevice> destDevices; protected List<SwitchPort> broadcastIntertfaces; + protected U64 descriptor; public RoutingDecision(DatapathId swDipd, OFPort inPort, @@ -114,6 +116,16 @@ public class RoutingDecision implements IRoutingDecision { this.hardTimeout = hardTimeout; } + @Override + public U64 getDescriptor() { + return descriptor; + } + + @Override + public void setDescriptor(U64 descriptor) { + this.descriptor = descriptor; + } + @Override public void addToContext(FloodlightContext cntx) { rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION, this);