diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index 6ba995b9c24f72e63a4b4ace7f95beefda2973f4..25765b3632e88f8d2da25f824ae27fb0ca1ccfe8 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -17,7 +17,6 @@ package net.floodlightcontroller.forwarding; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -89,584 +88,575 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Forwarding extends ForwardingBase implements IFloodlightModule, IOFSwitchListener, ILinkDiscoveryListener { - protected static Logger log = LoggerFactory.getLogger(Forwarding.class); - - @Override - public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { - Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - // We found a routing decision (i.e. Firewall is enabled... it's the only thing that makes RoutingDecisions) - if (decision != null) { - if (log.isTraceEnabled()) { - log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi); - } - - switch(decision.getRoutingAction()) { - case NONE: - // don't do anything - return Command.CONTINUE; - case FORWARD_OR_FLOOD: - case FORWARD: - doForwardFlow(sw, pi, cntx, false); - return Command.CONTINUE; - case MULTICAST: - // treat as broadcast - doFlood(sw, pi, cntx); - return Command.CONTINUE; - case DROP: - doDropFlow(sw, pi, decision, cntx); - return Command.CONTINUE; - default: - log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction()); - return Command.CONTINUE; - } - } else { // No routing decision was found. Forward to destination or flood if bcast or mcast. - if (log.isTraceEnabled()) { - log.trace("No decision was made for PacketIn={}, forwarding", pi); - } - - if (eth.isBroadcast() || eth.isMulticast()) { - doFlood(sw, pi, cntx); - } else { - doForwardFlow(sw, pi, cntx, false); - } - } - - return Command.CONTINUE; - } - - 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); - log.info("Dropping"); - fmb.setCookie(cookie) - .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT) - .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) - .setBufferId(OFBufferId.NO_BUFFER) - .setMatch(m) - .setPriority(FLOWMOD_DEFAULT_PRIORITY); - - FlowModUtils.setActions(fmb, actions, sw); - + protected static Logger log = LoggerFactory.getLogger(Forwarding.class); + + @Override + public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { + Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + // We found a routing decision (i.e. Firewall is enabled... it's the only thing that makes RoutingDecisions) + if (decision != null) { + if (log.isTraceEnabled()) { + log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi); + } + + switch(decision.getRoutingAction()) { + case NONE: + // don't do anything + return Command.CONTINUE; + case FORWARD_OR_FLOOD: + case FORWARD: + doForwardFlow(sw, pi, cntx, false); + return Command.CONTINUE; + case MULTICAST: + // treat as broadcast + doFlood(sw, pi, cntx); + return Command.CONTINUE; + case DROP: + doDropFlow(sw, pi, decision, cntx); + return Command.CONTINUE; + default: + log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction()); + return Command.CONTINUE; + } + } else { // No routing decision was found. Forward to destination or flood if bcast or mcast. + if (log.isTraceEnabled()) { + log.trace("No decision was made for PacketIn={}, forwarding", pi); + } + + if (eth.isBroadcast() || eth.isMulticast()) { + doFlood(sw, pi, cntx); + } else { + doForwardFlow(sw, pi, cntx, false); + } + } + + return Command.CONTINUE; + } + + 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); + log.info("Dropping"); + fmb.setCookie(cookie) + .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT) + .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) + .setBufferId(OFBufferId.NO_BUFFER) + .setMatch(m) + .setPriority(FLOWMOD_DEFAULT_PRIORITY); + + FlowModUtils.setActions(fmb, actions, sw); + /* Configure for particular switch pipeline */ fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID); - try { - if (log.isDebugEnabled()) { - log.debug("write drop flow-mod sw={} match={} flow-mod={}", - new Object[] { sw, m, fmb.build() }); - } - boolean dampened = messageDamper.write(sw, fmb.build()); - log.debug("OFMessage dampened: {}", dampened); - } catch (IOException e) { - log.error("Failure writing drop flow mod", e); - } - } - - protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, 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(); - - if (dstDevice != null) { - IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); - - if (srcDevice == null) { - log.error("No device entry found for source device. Is the device manager running? If so, report bug."); - return; - } - - if (FLOOD_ALL_ARP_PACKETS && - 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); - return; - } - - /* Validate that the source and destination are not on the same switch port */ - boolean on_same_if = false; - for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) { - if (sw.getId().equals(dstDap.getNodeId()) && inPort.equals(dstDap.getPortId())) { - on_same_if = true; - } - break; - } - - if (on_same_if) { - log.info("Both source and destination are on the same switch/port {}/{}. Action = NOP", sw.toString(), inPort); - return; - } - - SwitchPort[] dstDaps = dstDevice.getAttachmentPoints(); - SwitchPort dstDap = null; - - /* - * Search for the true attachment point. The true AP is - * not an endpoint of a link. It is a switch port w/o an - * associated link. Note this does not necessarily hold - * true for devices that 'live' between OpenFlow islands. - * - * TODO Account for the case where a device is actually - * attached between islands (possibly on a non-OF switch - * in between two OpenFlow switches). - */ - for (SwitchPort ap : dstDaps) { - if (topologyService.isEdge(ap.getNodeId(), ap.getPortId())) { - dstDap = ap; - break; - } - } - - /* - * This should only happen (perhaps) when the controller is - * actively learning a new topology and hasn't discovered - * all links yet, or a switch was in standalone mode and the - * packet in question was captured in flight on the dst point - * of a link. - */ - if (dstDap == null) { - log.warn("Could not locate edge attachment point for device {}. Flooding packet"); - doFlood(sw, pi, 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); - return; - } - - Route route = routingEngineService.getRoute(source, - inPort, - dstDap.getNodeId(), - dstDap.getPortId(), U64.of(0)); //cookie = 0, i.e., default route - - Match m = createMatchFromPacket(sw, inPort, cntx); - U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0); - - if (route != null) { - if (log.isDebugEnabled()) { - log.debug("pushRoute inPort={} route={} " + - "destination={}:{}", - new Object[] { inPort, route, - dstDap.getNodeId(), - dstDap.getPortId()}); - } - - - log.debug("Cretaing flow rules on the route, match rule: {}", m); - pushRoute(route, m, pi, sw.getId(), cookie, - cntx, requestFlowRemovedNotifn, - OFFlowModCommand.ADD); - } else { - /* Route traverses no links --> src/dst devices on same switch */ - log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice); - Route r = new Route(srcDevice.getAttachmentPoints()[0].getNodeId(), dstDevice.getAttachmentPoints()[0].getNodeId()); - List<NodePortTuple> path = new ArrayList<NodePortTuple>(2); - path.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getNodeId(), - srcDevice.getAttachmentPoints()[0].getPortId())); - path.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getNodeId(), - dstDevice.getAttachmentPoints()[0].getPortId())); - r.setPath(path); - pushRoute(r, m, pi, sw.getId(), cookie, - cntx, requestFlowRemovedNotifn, - OFFlowModCommand.ADD); - } - } else { - log.debug("Destination unknown. Flooding packet"); - doFlood(sw, pi, cntx); - } - } - - /** - * Instead of using the Firewall's routing decision Match, which might be as general - * as "in_port" and inadvertently Match packets erroneously, construct a more - * specific Match based on the deserialized OFPacketIn's payload, which has been - * placed in the FloodlightContext already by the Controller. - * - * @param sw, the switch on which the packet was received - * @param inPort, the ingress switch port on which the packet was received - * @param cntx, the current context which contains the deserialized packet - * @return a composed Match object based on the provided information - */ - protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) { - // The packet in match will only contain the port number. - // We need to add in specifics for the hosts we're routing between. - Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); - VlanVid vlan = VlanVid.ofVlan(eth.getVlanID()); - MacAddress srcMac = eth.getSourceMACAddress(); - MacAddress dstMac = eth.getDestinationMACAddress(); - - Match.Builder mb = sw.getOFFactory().buildMatch(); - mb.setExact(MatchField.IN_PORT, inPort); - - if (FLOWMOD_DEFAULT_MATCH_MAC) { - mb.setExact(MatchField.ETH_SRC, srcMac) - .setExact(MatchField.ETH_DST, dstMac); - } - - if (FLOWMOD_DEFAULT_MATCH_VLAN) { - if (!vlan.equals(VlanVid.ZERO)) { - mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan)); - } - } - - // TODO Detect switch type and match to create hardware-implemented flow - if (eth.getEtherType() == EthType.IPv4) { /* shallow check for equality is okay for EthType */ - IPv4 ip = (IPv4) eth.getPayload(); - IPv4Address srcIp = ip.getSourceAddress(); - IPv4Address dstIp = ip.getDestinationAddress(); - - if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) { - mb.setExact(MatchField.ETH_TYPE, EthType.IPv4) - .setExact(MatchField.IPV4_SRC, srcIp) - .setExact(MatchField.IPV4_DST, dstIp); - } - - if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) { - /* - * Take care of the ethertype if not included earlier, - * since it's a prerequisite for transport ports. - */ - if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) { - mb.setExact(MatchField.ETH_TYPE, EthType.IPv4); - } - - if (ip.getProtocol().equals(IpProtocol.TCP)) { - TCP tcp = (TCP) ip.getPayload(); - mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP) - .setExact(MatchField.TCP_SRC, tcp.getSourcePort()) - .setExact(MatchField.TCP_DST, tcp.getDestinationPort()); - } else if (ip.getProtocol().equals(IpProtocol.UDP)) { - UDP udp = (UDP) ip.getPayload(); - mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP) - .setExact(MatchField.UDP_SRC, udp.getSourcePort()) - .setExact(MatchField.UDP_DST, udp.getDestinationPort()); - } - } - } else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */ - mb.setExact(MatchField.ETH_TYPE, EthType.ARP); - } else if (eth.getEtherType() == EthType.IPv6) { - IPv6 ip = (IPv6) eth.getPayload(); - IPv6Address srcIp = ip.getSourceAddress(); - IPv6Address dstIp = ip.getDestinationAddress(); - - if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) { - mb.setExact(MatchField.ETH_TYPE, EthType.IPv6) - .setExact(MatchField.IPV6_SRC, srcIp) - .setExact(MatchField.IPV6_DST, dstIp); - } - - if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) { - /* - * Take care of the ethertype if not included earlier, - * since it's a prerequisite for transport ports. - */ - if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) { - mb.setExact(MatchField.ETH_TYPE, EthType.IPv6); - } - - if (ip.getNextHeader().equals(IpProtocol.TCP)) { - TCP tcp = (TCP) ip.getPayload(); - mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP) - .setExact(MatchField.TCP_SRC, tcp.getSourcePort()) - .setExact(MatchField.TCP_DST, tcp.getDestinationPort()); - } else if (ip.getNextHeader().equals(IpProtocol.UDP)) { - UDP udp = (UDP) ip.getPayload(); - mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP) - .setExact(MatchField.UDP_SRC, udp.getSourcePort()) - .setExact(MatchField.UDP_DST, udp.getDestinationPort()); - } - } - } - return mb.build(); - } - - /** - * Creates a OFPacketOut with the OFPacketIn data that is flooded on all ports unless - * 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 cntx The FloodlightContext associated with this OFPacketIn - */ - protected void doFlood(IOFSwitch sw, OFPacketIn pi, 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(); - List<OFAction> actions = new ArrayList<OFAction>(); - Set<OFPort> broadcastPorts = this.topologyService.getSwitchBroadcastPorts(sw.getId()); - - if (broadcastPorts.isEmpty()) { - log.warn("No broadcast ports found. Using FLOOD output action"); - /* Must be a single-switch w/no links */ - broadcastPorts = Collections.singleton(OFPort.FLOOD); - } - - for (OFPort p : broadcastPorts) { - if (p.equals(inPort)) continue; - actions.add(sw.getOFFactory().actions().output(p, Integer.MAX_VALUE)); - } - pob.setActions(actions); - // log.info("actions {}",actions); - // set buffer-id, in-port and packet-data based on packet-in - pob.setBufferId(OFBufferId.NO_BUFFER); - OFMessageUtils.setInPort(pob, inPort); - pob.setData(pi.getData()); - - try { - if (log.isTraceEnabled()) { - log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}", - new Object[] {sw, pi, pob.build()}); - } - messageDamper.write(sw, pob.build()); - } catch (IOException e) { - log.error("Failure writing PacketOut switch={} packet-in={} packet-out={}", - new Object[] {sw, pi, pob.build()}, e); - } - - return; - } - - // IFloodlightModule methods - - @Override - public Collection<Class<? extends IFloodlightService>> getModuleServices() { - // We don't export any services - return null; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { - // We don't have any services - return null; - } - - @Override - public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { - Collection<Class<? extends IFloodlightService>> l = - new ArrayList<Class<? extends IFloodlightService>>(); - l.add(IFloodlightProviderService.class); - l.add(IDeviceService.class); - l.add(IRoutingService.class); - l.add(ITopologyService.class); - l.add(IDebugCounterService.class); - l.add(ILinkDiscoveryService.class); - return l; - } - - @Override - public void init(FloodlightModuleContext context) throws FloodlightModuleException { - super.init(); - this.floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class); - this.deviceManagerService = context.getServiceImpl(IDeviceService.class); - this.routingEngineService = context.getServiceImpl(IRoutingService.class); - this.topologyService = context.getServiceImpl(ITopologyService.class); - this.debugCounterService = context.getServiceImpl(IDebugCounterService.class); - this.switchService = context.getServiceImpl(IOFSwitchService.class); - this.linkService = context.getServiceImpl(ILinkDiscoveryService.class); - - Map<String, String> configParameters = context.getConfigParams(this); - String tmp = configParameters.get("hard-timeout"); - if (tmp != null) { - FLOWMOD_DEFAULT_HARD_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp); - log.info("Default hard timeout set to {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT); - } else { - log.info("Default hard timeout not configured. Using {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT); - } - tmp = configParameters.get("idle-timeout"); - if (tmp != null) { - FLOWMOD_DEFAULT_IDLE_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp); - log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT); - } else { - log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT); - } - tmp = configParameters.get("table-id"); + if (log.isDebugEnabled()) { + log.debug("write drop flow-mod sw={} match={} flow-mod={}", + new Object[] { sw, m, fmb.build() }); + } + boolean dampened = messageDamper.write(sw, fmb.build()); + log.debug("OFMessage dampened: {}", dampened); + } + + protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, 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(); + + if (dstDevice != null) { + IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); + + if (srcDevice == null) { + log.error("No device entry found for source device. Is the device manager running? If so, report bug."); + return; + } + + if (FLOOD_ALL_ARP_PACKETS && + 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); + return; + } + + /* Validate that the source and destination are not on the same switch port */ + boolean on_same_if = false; + for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) { + if (sw.getId().equals(dstDap.getNodeId()) && inPort.equals(dstDap.getPortId())) { + on_same_if = true; + } + break; + } + + if (on_same_if) { + log.info("Both source and destination are on the same switch/port {}/{}. Action = NOP", sw.toString(), inPort); + return; + } + + SwitchPort[] dstDaps = dstDevice.getAttachmentPoints(); + SwitchPort dstDap = null; + + /* + * Search for the true attachment point. The true AP is + * not an endpoint of a link. It is a switch port w/o an + * associated link. Note this does not necessarily hold + * true for devices that 'live' between OpenFlow islands. + * + * TODO Account for the case where a device is actually + * attached between islands (possibly on a non-OF switch + * in between two OpenFlow switches). + */ + for (SwitchPort ap : dstDaps) { + if (topologyService.isEdge(ap.getNodeId(), ap.getPortId())) { + dstDap = ap; + break; + } + } + + /* + * This should only happen (perhaps) when the controller is + * actively learning a new topology and hasn't discovered + * all links yet, or a switch was in standalone mode and the + * packet in question was captured in flight on the dst point + * of a link. + */ + if (dstDap == null) { + log.warn("Could not locate edge attachment point for device {}. Flooding packet"); + doFlood(sw, pi, 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); + return; + } + + Route route = routingEngineService.getRoute(source, + inPort, + dstDap.getNodeId(), + dstDap.getPortId(), U64.of(0)); //cookie = 0, i.e., default route + + Match m = createMatchFromPacket(sw, inPort, cntx); + U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0); + + if (route != null) { + if (log.isDebugEnabled()) { + log.debug("pushRoute inPort={} route={} " + + "destination={}:{}", + new Object[] { inPort, route, + dstDap.getNodeId(), + dstDap.getPortId()}); + } + + + log.debug("Cretaing flow rules on the route, match rule: {}", m); + pushRoute(route, m, pi, sw.getId(), cookie, + cntx, requestFlowRemovedNotifn, + OFFlowModCommand.ADD); + } else { + /* Route traverses no links --> src/dst devices on same switch */ + log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice); + Route r = new Route(srcDevice.getAttachmentPoints()[0].getNodeId(), dstDevice.getAttachmentPoints()[0].getNodeId()); + List<NodePortTuple> path = new ArrayList<NodePortTuple>(2); + path.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getNodeId(), + srcDevice.getAttachmentPoints()[0].getPortId())); + path.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getNodeId(), + dstDevice.getAttachmentPoints()[0].getPortId())); + r.setPath(path); + pushRoute(r, m, pi, sw.getId(), cookie, + cntx, requestFlowRemovedNotifn, + OFFlowModCommand.ADD); + } + } else { + log.debug("Destination unknown. Flooding packet"); + doFlood(sw, pi, cntx); + } + } + + /** + * Instead of using the Firewall's routing decision Match, which might be as general + * as "in_port" and inadvertently Match packets erroneously, construct a more + * specific Match based on the deserialized OFPacketIn's payload, which has been + * placed in the FloodlightContext already by the Controller. + * + * @param sw, the switch on which the packet was received + * @param inPort, the ingress switch port on which the packet was received + * @param cntx, the current context which contains the deserialized packet + * @return a composed Match object based on the provided information + */ + protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) { + // The packet in match will only contain the port number. + // We need to add in specifics for the hosts we're routing between. + Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + VlanVid vlan = VlanVid.ofVlan(eth.getVlanID()); + MacAddress srcMac = eth.getSourceMACAddress(); + MacAddress dstMac = eth.getDestinationMACAddress(); + + Match.Builder mb = sw.getOFFactory().buildMatch(); + mb.setExact(MatchField.IN_PORT, inPort); + + if (FLOWMOD_DEFAULT_MATCH_MAC) { + mb.setExact(MatchField.ETH_SRC, srcMac) + .setExact(MatchField.ETH_DST, dstMac); + } + + if (FLOWMOD_DEFAULT_MATCH_VLAN) { + if (!vlan.equals(VlanVid.ZERO)) { + mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan)); + } + } + + // TODO Detect switch type and match to create hardware-implemented flow + if (eth.getEtherType() == EthType.IPv4) { /* shallow check for equality is okay for EthType */ + IPv4 ip = (IPv4) eth.getPayload(); + IPv4Address srcIp = ip.getSourceAddress(); + IPv4Address dstIp = ip.getDestinationAddress(); + + if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv4) + .setExact(MatchField.IPV4_SRC, srcIp) + .setExact(MatchField.IPV4_DST, dstIp); + } + + if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) { + /* + * Take care of the ethertype if not included earlier, + * since it's a prerequisite for transport ports. + */ + if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv4); + } + + if (ip.getProtocol().equals(IpProtocol.TCP)) { + TCP tcp = (TCP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP) + .setExact(MatchField.TCP_SRC, tcp.getSourcePort()) + .setExact(MatchField.TCP_DST, tcp.getDestinationPort()); + } else if (ip.getProtocol().equals(IpProtocol.UDP)) { + UDP udp = (UDP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP) + .setExact(MatchField.UDP_SRC, udp.getSourcePort()) + .setExact(MatchField.UDP_DST, udp.getDestinationPort()); + } + } + } else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */ + mb.setExact(MatchField.ETH_TYPE, EthType.ARP); + } else if (eth.getEtherType() == EthType.IPv6) { + IPv6 ip = (IPv6) eth.getPayload(); + IPv6Address srcIp = ip.getSourceAddress(); + IPv6Address dstIp = ip.getDestinationAddress(); + + if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv6) + .setExact(MatchField.IPV6_SRC, srcIp) + .setExact(MatchField.IPV6_DST, dstIp); + } + + if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) { + /* + * Take care of the ethertype if not included earlier, + * since it's a prerequisite for transport ports. + */ + if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv6); + } + + if (ip.getNextHeader().equals(IpProtocol.TCP)) { + TCP tcp = (TCP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP) + .setExact(MatchField.TCP_SRC, tcp.getSourcePort()) + .setExact(MatchField.TCP_DST, tcp.getDestinationPort()); + } else if (ip.getNextHeader().equals(IpProtocol.UDP)) { + UDP udp = (UDP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP) + .setExact(MatchField.UDP_SRC, udp.getSourcePort()) + .setExact(MatchField.UDP_DST, udp.getDestinationPort()); + } + } + } + return mb.build(); + } + + /** + * Creates a OFPacketOut with the OFPacketIn data that is flooded on all ports unless + * 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 cntx The FloodlightContext associated with this OFPacketIn + */ + protected void doFlood(IOFSwitch sw, OFPacketIn pi, 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(); + List<OFAction> actions = new ArrayList<OFAction>(); + Set<OFPort> broadcastPorts = this.topologyService.getSwitchBroadcastPorts(sw.getId()); + + if (broadcastPorts.isEmpty()) { + log.warn("No broadcast ports found. Using FLOOD output action"); + /* Must be a single-switch w/no links */ + broadcastPorts = Collections.singleton(OFPort.FLOOD); + } + + for (OFPort p : broadcastPorts) { + if (p.equals(inPort)) continue; + actions.add(sw.getOFFactory().actions().output(p, Integer.MAX_VALUE)); + } + pob.setActions(actions); + // log.info("actions {}",actions); + // set buffer-id, in-port and packet-data based on packet-in + pob.setBufferId(OFBufferId.NO_BUFFER); + OFMessageUtils.setInPort(pob, inPort); + pob.setData(pi.getData()); + + if (log.isTraceEnabled()) { + log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}", + new Object[] {sw, pi, pob.build()}); + } + messageDamper.write(sw, pob.build()); + + return; + } + + // IFloodlightModule methods + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleServices() { + // We don't export any services + return null; + } + + @Override + public Map<Class<? extends IFloodlightService>, IFloodlightService> + getServiceImpls() { + // We don't have any services + return null; + } + + @Override + public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); + l.add(IFloodlightProviderService.class); + l.add(IDeviceService.class); + l.add(IRoutingService.class); + l.add(ITopologyService.class); + l.add(IDebugCounterService.class); + l.add(ILinkDiscoveryService.class); + return l; + } + + @Override + public void init(FloodlightModuleContext context) throws FloodlightModuleException { + super.init(); + this.floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class); + this.deviceManagerService = context.getServiceImpl(IDeviceService.class); + this.routingEngineService = context.getServiceImpl(IRoutingService.class); + this.topologyService = context.getServiceImpl(ITopologyService.class); + this.debugCounterService = context.getServiceImpl(IDebugCounterService.class); + this.switchService = context.getServiceImpl(IOFSwitchService.class); + this.linkService = context.getServiceImpl(ILinkDiscoveryService.class); + + Map<String, String> configParameters = context.getConfigParams(this); + String tmp = configParameters.get("hard-timeout"); + if (tmp != null) { + FLOWMOD_DEFAULT_HARD_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp); + log.info("Default hard timeout set to {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT); + } else { + log.info("Default hard timeout not configured. Using {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT); + } + tmp = configParameters.get("idle-timeout"); + if (tmp != null) { + FLOWMOD_DEFAULT_IDLE_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp); + log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT); + } else { + log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT); + } + tmp = configParameters.get("table-id"); if (tmp != null) { FLOWMOD_DEFAULT_TABLE_ID = TableId.of(ParseUtils.parseHexOrDecInt(tmp)); log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT); } else { log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT); } - tmp = configParameters.get("priority"); - if (tmp != null) { - FLOWMOD_DEFAULT_PRIORITY = ParseUtils.parseHexOrDecInt(tmp); - log.info("Default priority set to {}.", FLOWMOD_DEFAULT_PRIORITY); - } else { - log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY); - } - tmp = configParameters.get("set-send-flow-rem-flag"); - if (tmp != null) { - FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp); - log.info("Default flags will be set to SEND_FLOW_REM {}.", FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG); - } else { - log.info("Default flags will be empty."); - } - tmp = configParameters.get("match"); - if (tmp != null) { - tmp = tmp.toLowerCase(); - if (!tmp.contains("vlan") && !tmp.contains("mac") && !tmp.contains("ip") && !tmp.contains("port")) { - /* leave the default configuration -- blank or invalid 'match' value */ - } else { - FLOWMOD_DEFAULT_MATCH_VLAN = tmp.contains("vlan") ? true : false; - FLOWMOD_DEFAULT_MATCH_MAC = tmp.contains("mac") ? true : false; - FLOWMOD_DEFAULT_MATCH_IP_ADDR = tmp.contains("ip") ? true : false; - FLOWMOD_DEFAULT_MATCH_TRANSPORT = tmp.contains("port") ? true : false; - - } - } - log.info("Default flow matches set to: VLAN=" + FLOWMOD_DEFAULT_MATCH_VLAN - + ", MAC=" + FLOWMOD_DEFAULT_MATCH_MAC - + ", IP=" + FLOWMOD_DEFAULT_MATCH_IP_ADDR - + ", TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT); - - tmp = configParameters.get("flood-arp"); - if (tmp != null) { - tmp = tmp.toLowerCase(); - if (!tmp.contains("yes") && !tmp.contains("yep") && !tmp.contains("true") && !tmp.contains("ja") && !tmp.contains("stimmt")) { - FLOOD_ALL_ARP_PACKETS = false; - log.info("Not flooding ARP packets. ARP flows will be inserted for known destinations"); - } else { - FLOOD_ALL_ARP_PACKETS = true; - log.info("Flooding all ARP packets. No ARP flows will be inserted"); - } - } - - tmp = configParameters.get("remove-flows-on-link-or-port-down"); - if (tmp != null) { - REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = Boolean.parseBoolean(tmp); - } - if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) { - log.info("Flows will be removed on link/port down events"); - } else { - log.info("Flows will not be removed on link/port down events"); - } - } - - @Override - public void startUp(FloodlightModuleContext context) { - super.startUp(); - switchService.addOFSwitchListener(this); - - /* Register only if we want to remove stale flows */ - if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) { - linkService.addListener(this); - } - } - - @Override - public void switchAdded(DatapathId switchId) { - } - - @Override - public void switchRemoved(DatapathId switchId) { - } - - @Override - public void switchActivated(DatapathId switchId) { - IOFSwitch sw = switchService.getSwitch(switchId); - if (sw == null) { - log.warn("Switch {} was activated but had no switch object in the switch service. Perhaps it quickly disconnected", switchId); - return; - } - if (OFDPAUtils.isOFDPASwitch(sw)) { - sw.write(sw.getOFFactory().buildFlowDelete() - .setTableId(TableId.ALL) - .build() - ); - sw.write(sw.getOFFactory().buildGroupDelete() - .setGroup(OFGroup.ANY) - .setGroupType(OFGroupType.ALL) - .build() - ); - sw.write(sw.getOFFactory().buildGroupDelete() - .setGroup(OFGroup.ANY) - .setGroupType(OFGroupType.INDIRECT) - .build() - ); - sw.write(sw.getOFFactory().buildBarrierRequest().build()); - - List<OFPortModeTuple> portModes = new ArrayList<OFPortModeTuple>(); - for (OFPortDesc p : sw.getPorts()) { - portModes.add(OFPortModeTuple.of(p.getPortNo(), OFPortMode.ACCESS)); - } - if (log.isWarnEnabled()) { - log.warn("For OF-DPA switch {}, initializing VLAN {} on ports {}", new Object[] { switchId, VlanVid.ZERO, portModes}); - } - OFDPAUtils.addLearningSwitchPrereqs(sw, VlanVid.ZERO, portModes); - } - } - - @Override - public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) { - /* Port down events handled via linkDiscoveryUpdate(), which passes thru all events */ - } - - @Override - public void switchChanged(DatapathId switchId) { - } - - @Override - public void switchDeactivated(DatapathId switchId) { - } - - @Override - public void linkDiscoveryUpdate(List<LDUpdate> updateList) { - for (LDUpdate u : updateList) { - /* Remove flows on either side if link/port went down */ - if (u.getOperation() == UpdateOperation.LINK_REMOVED || - u.getOperation() == UpdateOperation.PORT_DOWN || - u.getOperation() == UpdateOperation.TUNNEL_PORT_REMOVED) { - Set<OFMessage> msgs = new HashSet<OFMessage>(); - - if (u.getSrc() != null && !u.getSrc().equals(DatapathId.NONE)) { - IOFSwitch srcSw = switchService.getSwitch(u.getSrc()); - /* src side of link */ - if (srcSw != null) { - /* flows matching on src port */ - msgs.add(srcSw.getOFFactory().buildFlowDelete() - .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) - .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)) - .setOutPort(u.getSrcPort()) - .build()); - srcSw.write(msgs); - log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getSrc(), u.getSrcPort() }); - } - } - - /* must be a link, not just a port down, if we have a dst switch */ - if (u.getDst() != null && !u.getDst().equals(DatapathId.NONE)) { - /* dst side of link */ - IOFSwitch dstSw = switchService.getSwitch(u.getDst()); - if (dstSw != null) { - /* flows matching on dst port */ - msgs.clear(); - msgs.add(dstSw.getOFFactory().buildFlowDelete() - .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) - .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)) - .setOutPort(u.getDstPort()) - .build()); - dstSw.write(msgs); - log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getDst(), u.getDstPort() }); - } - } - } - } - } + tmp = configParameters.get("priority"); + if (tmp != null) { + FLOWMOD_DEFAULT_PRIORITY = ParseUtils.parseHexOrDecInt(tmp); + log.info("Default priority set to {}.", FLOWMOD_DEFAULT_PRIORITY); + } else { + log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY); + } + tmp = configParameters.get("set-send-flow-rem-flag"); + if (tmp != null) { + FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp); + log.info("Default flags will be set to SEND_FLOW_REM {}.", FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG); + } else { + log.info("Default flags will be empty."); + } + tmp = configParameters.get("match"); + if (tmp != null) { + tmp = tmp.toLowerCase(); + if (!tmp.contains("vlan") && !tmp.contains("mac") && !tmp.contains("ip") && !tmp.contains("port")) { + /* leave the default configuration -- blank or invalid 'match' value */ + } else { + FLOWMOD_DEFAULT_MATCH_VLAN = tmp.contains("vlan") ? true : false; + FLOWMOD_DEFAULT_MATCH_MAC = tmp.contains("mac") ? true : false; + FLOWMOD_DEFAULT_MATCH_IP_ADDR = tmp.contains("ip") ? true : false; + FLOWMOD_DEFAULT_MATCH_TRANSPORT = tmp.contains("port") ? true : false; + + } + } + log.info("Default flow matches set to: VLAN=" + FLOWMOD_DEFAULT_MATCH_VLAN + + ", MAC=" + FLOWMOD_DEFAULT_MATCH_MAC + + ", IP=" + FLOWMOD_DEFAULT_MATCH_IP_ADDR + + ", TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT); + + tmp = configParameters.get("flood-arp"); + if (tmp != null) { + tmp = tmp.toLowerCase(); + if (!tmp.contains("yes") && !tmp.contains("yep") && !tmp.contains("true") && !tmp.contains("ja") && !tmp.contains("stimmt")) { + FLOOD_ALL_ARP_PACKETS = false; + log.info("Not flooding ARP packets. ARP flows will be inserted for known destinations"); + } else { + FLOOD_ALL_ARP_PACKETS = true; + log.info("Flooding all ARP packets. No ARP flows will be inserted"); + } + } + + tmp = configParameters.get("remove-flows-on-link-or-port-down"); + if (tmp != null) { + REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = Boolean.parseBoolean(tmp); + } + if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) { + log.info("Flows will be removed on link/port down events"); + } else { + log.info("Flows will not be removed on link/port down events"); + } + } + + @Override + public void startUp(FloodlightModuleContext context) { + super.startUp(); + switchService.addOFSwitchListener(this); + + /* Register only if we want to remove stale flows */ + if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) { + linkService.addListener(this); + } + } + + @Override + public void switchAdded(DatapathId switchId) { + } + + @Override + public void switchRemoved(DatapathId switchId) { + } + + @Override + public void switchActivated(DatapathId switchId) { + IOFSwitch sw = switchService.getSwitch(switchId); + if (sw == null) { + log.warn("Switch {} was activated but had no switch object in the switch service. Perhaps it quickly disconnected", switchId); + return; + } + if (OFDPAUtils.isOFDPASwitch(sw)) { + messageDamper.write(sw, sw.getOFFactory().buildFlowDelete() + .setTableId(TableId.ALL) + .build() + ); + messageDamper.write(sw, sw.getOFFactory().buildGroupDelete() + .setGroup(OFGroup.ANY) + .setGroupType(OFGroupType.ALL) + .build() + ); + messageDamper.write(sw, sw.getOFFactory().buildGroupDelete() + .setGroup(OFGroup.ANY) + .setGroupType(OFGroupType.INDIRECT) + .build() + ); + messageDamper.write(sw, sw.getOFFactory().buildBarrierRequest().build()); + + List<OFPortModeTuple> portModes = new ArrayList<OFPortModeTuple>(); + for (OFPortDesc p : sw.getPorts()) { + portModes.add(OFPortModeTuple.of(p.getPortNo(), OFPortMode.ACCESS)); + } + if (log.isWarnEnabled()) { + log.warn("For OF-DPA switch {}, initializing VLAN {} on ports {}", new Object[] { switchId, VlanVid.ZERO, portModes}); + } + OFDPAUtils.addLearningSwitchPrereqs(sw, VlanVid.ZERO, portModes); + } + } + + @Override + public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) { + /* Port down events handled via linkDiscoveryUpdate(), which passes thru all events */ + } + + @Override + public void switchChanged(DatapathId switchId) { + } + + @Override + public void switchDeactivated(DatapathId switchId) { + } + + @Override + public void linkDiscoveryUpdate(List<LDUpdate> updateList) { + for (LDUpdate u : updateList) { + /* Remove flows on either side if link/port went down */ + if (u.getOperation() == UpdateOperation.LINK_REMOVED || + u.getOperation() == UpdateOperation.PORT_DOWN || + u.getOperation() == UpdateOperation.TUNNEL_PORT_REMOVED) { + Set<OFMessage> msgs = new HashSet<OFMessage>(); + + if (u.getSrc() != null && !u.getSrc().equals(DatapathId.NONE)) { + IOFSwitch srcSw = switchService.getSwitch(u.getSrc()); + /* src side of link */ + if (srcSw != null) { + /* flows matching on src port */ + msgs.add(srcSw.getOFFactory().buildFlowDelete() + .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) + .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)) + .setOutPort(u.getSrcPort()) + .build()); + messageDamper.write(srcSw, msgs); + log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getSrc(), u.getSrcPort() }); + } + } + + /* must be a link, not just a port down, if we have a dst switch */ + if (u.getDst() != null && !u.getDst().equals(DatapathId.NONE)) { + /* dst side of link */ + IOFSwitch dstSw = switchService.getSwitch(u.getDst()); + if (dstSw != null) { + /* flows matching on dst port */ + msgs.clear(); + msgs.add(dstSw.getOFFactory().buildFlowDelete() + .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0)) + .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)) + .setOutPort(u.getDstPort()) + .build()); + messageDamper.write(dstSw, msgs); + log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getDst(), u.getDstPort() }); + } + } + } + } + } } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index 471761e112f116b7493878ecc23d432925085ab9..b80444d854054679d26263bec2a5f1aad7f02c24 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -17,7 +17,6 @@ package net.floodlightcontroller.routing; -import java.io.IOException; import java.util.EnumSet; import java.util.ArrayList; import java.util.Comparator; @@ -75,409 +74,398 @@ import org.slf4j.LoggerFactory; * decision. */ public abstract class ForwardingBase implements IOFMessageListener { - protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class); - - protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // TODO: find sweet spot - protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms - - public static int FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds - public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite - public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1 - - public static TableId FLOWMOD_DEFAULT_TABLE_ID = TableId.ZERO; - - protected static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false; - - protected static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true; - protected static boolean FLOWMOD_DEFAULT_MATCH_MAC = true; - protected static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true; - protected static boolean FLOWMOD_DEFAULT_MATCH_TRANSPORT = true; - - protected static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT_CONSTANT = 5; - protected static final short FLOWMOD_DEFAULT_HARD_TIMEOUT_CONSTANT = 0; - - protected static boolean FLOOD_ALL_ARP_PACKETS = false; - - protected static boolean REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = true; - - protected IFloodlightProviderService floodlightProviderService; - protected IOFSwitchService switchService; - protected IDeviceService deviceManagerService; - protected IRoutingService routingEngineService; - protected ITopologyService topologyService; - protected IDebugCounterService debugCounterService; - protected ILinkDiscoveryService linkService; - - protected OFMessageDamper messageDamper; - - // for broadcast loop suppression - protected boolean broadcastCacheFeature = true; - public final int prime1 = 2633; // for hash calculation - public final static int prime2 = 4357; // for hash calculation - public TimedCache<Long> broadcastCache = new TimedCache<Long>(100, 5*1000); // 5 seconds interval; - - // 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 - static { - AppCookie.registerApp(FORWARDING_APP_ID, "Forwarding"); - } - public static final U64 appCookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0); - - // Comparator for sorting by SwitchCluster - public Comparator<SwitchPort> clusterIdComparator = - new Comparator<SwitchPort>() { - @Override - public int compare(SwitchPort d1, SwitchPort d2) { - DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getNodeId()); - DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getNodeId()); - return d1ClusterId.compareTo(d2ClusterId); - } - }; - - protected void init() { - messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY, - EnumSet.of(OFType.FLOW_MOD), - OFMESSAGE_DAMPER_TIMEOUT); - } - - protected void startUp() { - floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this); - } - - @Override - public String getName() { - return "forwarding"; - } - - /** - * 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 = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION); - } - - return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx); - default: - break; - } - return Command.CONTINUE; - } - - /** - * 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 cookie The cookie to set in each flow_mod - * @param cntx The floodlight context - * @param requestFlowRemovedNotification if set to true then the switch would - * send a flow mod removal notification when the flow mod expires - * @param flowModCommand flow mod. command to use, e.g. OFFlowMod.OFPFC_ADD, - * OFFlowMod.OFPFC_MODIFY etc. - * @return true if a packet out was sent on the first-hop switch of this route - */ - public boolean pushRoute(Route route, Match match, OFPacketIn pi, - DatapathId pinSwitch, U64 cookie, FloodlightContext cntx, - boolean requestFlowRemovedNotification, OFFlowModCommand flowModCommand) { - - boolean packetOutSent = false; - - List<NodePortTuple> switchPortList = route.getPath(); - - for (int indx = switchPortList.size() - 1; indx > 0; indx -= 2) { - // indx and indx-1 will always have the same switch DPID. - DatapathId switchDPID = switchPortList.get(indx).getNodeId(); - IOFSwitch sw = switchService.getSwitch(switchDPID); - - if (sw == null) { - if (log.isWarnEnabled()) { - log.warn("Unable to push route, switch at DPID {} " + "not available", switchDPID); - } - return packetOutSent; - } - - // need to build flow mod based on what type it is. Cannot set command later - OFFlowMod.Builder fmb; - switch (flowModCommand) { - case ADD: - fmb = sw.getOFFactory().buildFlowAdd(); - break; - case DELETE: - fmb = sw.getOFFactory().buildFlowDelete(); - break; - case DELETE_STRICT: - fmb = sw.getOFFactory().buildFlowDeleteStrict(); - break; - case MODIFY: - fmb = sw.getOFFactory().buildFlowModify(); - break; - default: - log.error("Could not decode OFFlowModCommand. Using MODIFY_STRICT. (Should another be used as the default?)"); - case MODIFY_STRICT: - fmb = sw.getOFFactory().buildFlowModifyStrict(); - break; - } - - OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput(); - List<OFAction> actions = new ArrayList<OFAction>(); - Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion()); - - // set input and output ports on the switch - OFPort outPort = switchPortList.get(indx).getPortId(); - OFPort inPort = switchPortList.get(indx - 1).getPortId(); - mb.setExact(MatchField.IN_PORT, inPort); - aob.setPort(outPort); - aob.setMaxLen(Integer.MAX_VALUE); - actions.add(aob.build()); - - if (FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG || requestFlowRemovedNotification) { - Set<OFFlowModFlags> flags = new HashSet<>(); - flags.add(OFFlowModFlags.SEND_FLOW_REM); - fmb.setFlags(flags); - } - - fmb.setMatch(mb.build()) - .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) - .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT) - .setBufferId(OFBufferId.NO_BUFFER) - .setCookie(cookie) - .setOutPort(outPort) - .setPriority(FLOWMOD_DEFAULT_PRIORITY); - - FlowModUtils.setActions(fmb, actions, sw); - - /* Configure for particular switch pipeline */ - fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID); - - try { - if (log.isTraceEnabled()) { - log.trace("Pushing Route flowmod routeIndx={} " + - "sw={} inPort={} outPort={}", - new Object[] {indx, - sw, - fmb.getMatch().get(MatchField.IN_PORT), - outPort }); - } - - if (OFDPAUtils.isOFDPASwitch(sw)) { - OFDPAUtils.addLearningSwitchFlow(sw, cookie, - FLOWMOD_DEFAULT_PRIORITY, - FLOWMOD_DEFAULT_HARD_TIMEOUT, - FLOWMOD_DEFAULT_IDLE_TIMEOUT, - fmb.getMatch(), - null, // TODO how to determine output VLAN for lookup of L2 interface group - outPort); - } else { - messageDamper.write(sw, fmb.build()); - } - - /* Push the packet out the first hop switch */ - if (sw.getId().equals(pinSwitch) && - !fmb.getCommand().equals(OFFlowModCommand.DELETE) && - !fmb.getCommand().equals(OFFlowModCommand.DELETE_STRICT)) { - /* Use the buffered packet at the switch, if there's one stored */ - pushPacket(sw, pi, outPort, true, cntx); - packetOutSent = true; - } - } catch (IOException e) { - log.error("Failure writing flow mod", e); - } - } - - return packetOutSent; - } - - /** - * Pushes a packet-out to a switch. The assumption here is that - * the packet-in was also generated from the same switch. Thus, if the input - * port of the packet-in and the outport are the same, the function will not - * push the packet-out. - * @param sw switch that generated the packet-in, and from which packet-out is sent - * @param pi packet-in - * @param outport output port - * @param useBufferedPacket use the packet buffered at the switch, if possible - * @param cntx context of the packet - */ - protected void pushPacket(IOFSwitch sw, OFPacketIn pi, OFPort outport, boolean useBufferedPacket, FloodlightContext cntx) { - if (pi == null) { - return; - } - - // The assumption here is (sw) is the switch that generated the - // packet-in. If the input port is the same as output port, then - // the packet-out should be ignored. - if ((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)).equals(outport)) { - if (log.isDebugEnabled()) { - log.debug("Attempting to do packet-out to the same " + - "interface as packet-in. Dropping packet. " + - " SrcSwitch={}, pi={}", - new Object[]{sw, pi}); - return; - } - } - - if (log.isTraceEnabled()) { - log.trace("PacketOut srcSwitch={} pi={}", - new Object[] {sw, pi}); - } - - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); - List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(sw.getOFFactory().actions().output(outport, Integer.MAX_VALUE)); - pob.setActions(actions); - - /* Use packet in buffer if there is a buffer ID set */ - if (useBufferedPacket) { - pob.setBufferId(pi.getBufferId()); /* will be NO_BUFFER if there isn't one */ - } else { - pob.setBufferId(OFBufferId.NO_BUFFER); - } - - if (pob.getBufferId().equals(OFBufferId.NO_BUFFER)) { - byte[] packetData = pi.getData(); - pob.setData(packetData); - } - - pob.setInPort((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT))); - - try { - messageDamper.write(sw, pob.build()); - } catch (IOException e) { - log.error("Failure writing packet out", e); - } - } - - /** - * Write packetout message to sw with output actions to one or more - * output ports with inPort/outPorts passed in. - * @param packetData - * @param sw - * @param inPort - * @param ports - * @param cntx - */ - public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, - OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) { - //setting actions - List<OFAction> actions = new ArrayList<OFAction>(); - - Iterator<OFPort> j = outPorts.iterator(); - - while (j.hasNext()) { - actions.add(sw.getOFFactory().actions().output(j.next(), 0)); - } - - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); - pob.setActions(actions); - - pob.setBufferId(OFBufferId.NO_BUFFER); - pob.setInPort(inPort); - - pob.setData(packetData); - - try { - if (log.isTraceEnabled()) { - log.trace("write broadcast packet on switch-id={} " + - "interfaces={} packet-out={}", - new Object[] {sw.getId(), outPorts, pob.build()}); - } - messageDamper.write(sw, pob.build()); - - } catch (IOException e) { - log.error("Failure writing packet out", e); - } - } - - /** - * @see packetOutMultiPort - * Accepts a PacketIn instead of raw packet data. Note that the inPort - * and switch can be different than the packet in switch/port - */ - public void packetOutMultiPort(OFPacketIn pi, IOFSwitch sw, - OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) { - packetOutMultiPort(pi.getData(), sw, inPort, outPorts, cntx); - } - - /** - * @see packetOutMultiPort - * Accepts an IPacket instead of raw packet data. Note that the inPort - * and switch can be different than the packet in switch/port - */ - public void packetOutMultiPort(IPacket packet, IOFSwitch sw, - OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) { - packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx); - } - - public static boolean blockHost(IOFSwitchService switchService, - SwitchPort sw_tup, MacAddress host_mac, short hardTimeout, U64 cookie) { - - if (sw_tup == null) { - return false; - } - - IOFSwitch sw = switchService.getSwitch(sw_tup.getNodeId()); - if (sw == null) { - return false; - } - - OFPort inputPort = sw_tup.getPortId(); - if (log.isDebugEnabled()) { - log.debug("blockHost sw={} port={} mac={}", - new Object[] { sw, sw_tup.getPortId(), host_mac.getLong() }); - } - - // Create flow-mod based on packet-in and src-switch - OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); - - Match.Builder mb = sw.getOFFactory().buildMatch(); - List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop - mb.setExact(MatchField.IN_PORT, inputPort); - if (host_mac.getLong() != -1L) { - mb.setExact(MatchField.ETH_SRC, host_mac); - } - - fmb.setCookie(cookie) - .setHardTimeout(hardTimeout) - .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) - .setPriority(FLOWMOD_DEFAULT_PRIORITY) - .setBufferId(OFBufferId.NO_BUFFER) - .setMatch(mb.build()); - - FlowModUtils.setActions(fmb, actions, sw); - - log.debug("write drop flow-mod sw={} match={} flow-mod={}", - new Object[] { sw, mb.build(), fmb.build() }); - - sw.write(fmb.build()); - - return true; - } - - @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; - } + protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class); + + protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // TODO: find sweet spot + protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms + + public static int FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds + public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite + public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1 + + public static TableId FLOWMOD_DEFAULT_TABLE_ID = TableId.ZERO; + + protected static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false; + + protected static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true; + protected static boolean FLOWMOD_DEFAULT_MATCH_MAC = true; + protected static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true; + protected static boolean FLOWMOD_DEFAULT_MATCH_TRANSPORT = true; + + protected static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT_CONSTANT = 5; + protected static final short FLOWMOD_DEFAULT_HARD_TIMEOUT_CONSTANT = 0; + + protected static boolean FLOOD_ALL_ARP_PACKETS = false; + + protected static boolean REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = true; + + protected IFloodlightProviderService floodlightProviderService; + protected IOFSwitchService switchService; + protected IDeviceService deviceManagerService; + protected IRoutingService routingEngineService; + protected ITopologyService topologyService; + protected IDebugCounterService debugCounterService; + protected ILinkDiscoveryService linkService; + + protected OFMessageDamper messageDamper; + + // for broadcast loop suppression + protected boolean broadcastCacheFeature = true; + public final int prime1 = 2633; // for hash calculation + public final static int prime2 = 4357; // for hash calculation + public TimedCache<Long> broadcastCache = new TimedCache<Long>(100, 5*1000); // 5 seconds interval; + + // 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 + static { + AppCookie.registerApp(FORWARDING_APP_ID, "Forwarding"); + } + public static final U64 appCookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0); + + // Comparator for sorting by SwitchCluster + public Comparator<SwitchPort> clusterIdComparator = + new Comparator<SwitchPort>() { + @Override + public int compare(SwitchPort d1, SwitchPort d2) { + DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getNodeId()); + DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getNodeId()); + return d1ClusterId.compareTo(d2ClusterId); + } + }; + + protected void init() { + messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY, + EnumSet.of(OFType.FLOW_MOD), + OFMESSAGE_DAMPER_TIMEOUT); + } + + protected void startUp() { + floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this); + } + + @Override + public String getName() { + return "forwarding"; + } + + /** + * 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 = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION); + } + + return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx); + default: + break; + } + return Command.CONTINUE; + } + + /** + * 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 cookie The cookie to set in each flow_mod + * @param cntx The floodlight context + * @param requestFlowRemovedNotification if set to true then the switch would + * send a flow mod removal notification when the flow mod expires + * @param flowModCommand flow mod. command to use, e.g. OFFlowMod.OFPFC_ADD, + * OFFlowMod.OFPFC_MODIFY etc. + * @return true if a packet out was sent on the first-hop switch of this route + */ + public boolean pushRoute(Route route, Match match, OFPacketIn pi, + DatapathId pinSwitch, U64 cookie, FloodlightContext cntx, + boolean requestFlowRemovedNotification, OFFlowModCommand flowModCommand) { + + boolean packetOutSent = false; + + List<NodePortTuple> switchPortList = route.getPath(); + + for (int indx = switchPortList.size() - 1; indx > 0; indx -= 2) { + // indx and indx-1 will always have the same switch DPID. + DatapathId switchDPID = switchPortList.get(indx).getNodeId(); + IOFSwitch sw = switchService.getSwitch(switchDPID); + + if (sw == null) { + if (log.isWarnEnabled()) { + log.warn("Unable to push route, switch at DPID {} " + "not available", switchDPID); + } + return packetOutSent; + } + + // need to build flow mod based on what type it is. Cannot set command later + OFFlowMod.Builder fmb; + switch (flowModCommand) { + case ADD: + fmb = sw.getOFFactory().buildFlowAdd(); + break; + case DELETE: + fmb = sw.getOFFactory().buildFlowDelete(); + break; + case DELETE_STRICT: + fmb = sw.getOFFactory().buildFlowDeleteStrict(); + break; + case MODIFY: + fmb = sw.getOFFactory().buildFlowModify(); + break; + default: + log.error("Could not decode OFFlowModCommand. Using MODIFY_STRICT. (Should another be used as the default?)"); + case MODIFY_STRICT: + fmb = sw.getOFFactory().buildFlowModifyStrict(); + break; + } + + OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput(); + List<OFAction> actions = new ArrayList<OFAction>(); + Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion()); + + // set input and output ports on the switch + OFPort outPort = switchPortList.get(indx).getPortId(); + OFPort inPort = switchPortList.get(indx - 1).getPortId(); + mb.setExact(MatchField.IN_PORT, inPort); + aob.setPort(outPort); + aob.setMaxLen(Integer.MAX_VALUE); + actions.add(aob.build()); + + if (FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG || requestFlowRemovedNotification) { + Set<OFFlowModFlags> flags = new HashSet<>(); + flags.add(OFFlowModFlags.SEND_FLOW_REM); + fmb.setFlags(flags); + } + + fmb.setMatch(mb.build()) + .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) + .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(cookie) + .setOutPort(outPort) + .setPriority(FLOWMOD_DEFAULT_PRIORITY); + + FlowModUtils.setActions(fmb, actions, sw); + + /* Configure for particular switch pipeline */ + fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID); + + if (log.isTraceEnabled()) { + log.trace("Pushing Route flowmod routeIndx={} " + + "sw={} inPort={} outPort={}", + new Object[] {indx, + sw, + fmb.getMatch().get(MatchField.IN_PORT), + outPort }); + } + + if (OFDPAUtils.isOFDPASwitch(sw)) { + OFDPAUtils.addLearningSwitchFlow(sw, cookie, + FLOWMOD_DEFAULT_PRIORITY, + FLOWMOD_DEFAULT_HARD_TIMEOUT, + FLOWMOD_DEFAULT_IDLE_TIMEOUT, + fmb.getMatch(), + null, // TODO how to determine output VLAN for lookup of L2 interface group + outPort); + } else { + messageDamper.write(sw, fmb.build()); + } + + /* Push the packet out the first hop switch */ + if (sw.getId().equals(pinSwitch) && + !fmb.getCommand().equals(OFFlowModCommand.DELETE) && + !fmb.getCommand().equals(OFFlowModCommand.DELETE_STRICT)) { + /* Use the buffered packet at the switch, if there's one stored */ + pushPacket(sw, pi, outPort, true, cntx); + packetOutSent = true; + } + } + + return packetOutSent; + } + + /** + * Pushes a packet-out to a switch. The assumption here is that + * the packet-in was also generated from the same switch. Thus, if the input + * port of the packet-in and the outport are the same, the function will not + * push the packet-out. + * @param sw switch that generated the packet-in, and from which packet-out is sent + * @param pi packet-in + * @param outport output port + * @param useBufferedPacket use the packet buffered at the switch, if possible + * @param cntx context of the packet + */ + protected void pushPacket(IOFSwitch sw, OFPacketIn pi, OFPort outport, boolean useBufferedPacket, FloodlightContext cntx) { + if (pi == null) { + return; + } + + // The assumption here is (sw) is the switch that generated the + // packet-in. If the input port is the same as output port, then + // the packet-out should be ignored. + if ((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)).equals(outport)) { + if (log.isDebugEnabled()) { + log.debug("Attempting to do packet-out to the same " + + "interface as packet-in. Dropping packet. " + + " SrcSwitch={}, pi={}", + new Object[]{sw, pi}); + return; + } + } + + if (log.isTraceEnabled()) { + log.trace("PacketOut srcSwitch={} pi={}", + new Object[] {sw, pi}); + } + + OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(sw.getOFFactory().actions().output(outport, Integer.MAX_VALUE)); + pob.setActions(actions); + + /* Use packet in buffer if there is a buffer ID set */ + if (useBufferedPacket) { + pob.setBufferId(pi.getBufferId()); /* will be NO_BUFFER if there isn't one */ + } else { + pob.setBufferId(OFBufferId.NO_BUFFER); + } + + if (pob.getBufferId().equals(OFBufferId.NO_BUFFER)) { + byte[] packetData = pi.getData(); + pob.setData(packetData); + } + + pob.setInPort((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT))); + + messageDamper.write(sw, pob.build()); + } + + /** + * Write packetout message to sw with output actions to one or more + * output ports with inPort/outPorts passed in. + * @param packetData + * @param sw + * @param inPort + * @param ports + * @param cntx + */ + public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, + OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) { + //setting actions + List<OFAction> actions = new ArrayList<OFAction>(); + + Iterator<OFPort> j = outPorts.iterator(); + + while (j.hasNext()) { + actions.add(sw.getOFFactory().actions().output(j.next(), 0)); + } + + OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); + pob.setActions(actions); + + pob.setBufferId(OFBufferId.NO_BUFFER); + pob.setInPort(inPort); + + pob.setData(packetData); + + if (log.isTraceEnabled()) { + log.trace("write broadcast packet on switch-id={} " + + "interfaces={} packet-out={}", + new Object[] {sw.getId(), outPorts, pob.build()}); + } + messageDamper.write(sw, pob.build()); + } + + /** + * @see packetOutMultiPort + * Accepts a PacketIn instead of raw packet data. Note that the inPort + * and switch can be different than the packet in switch/port + */ + public void packetOutMultiPort(OFPacketIn pi, IOFSwitch sw, + OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) { + packetOutMultiPort(pi.getData(), sw, inPort, outPorts, cntx); + } + + /** + * @see packetOutMultiPort + * Accepts an IPacket instead of raw packet data. Note that the inPort + * and switch can be different than the packet in switch/port + */ + public void packetOutMultiPort(IPacket packet, IOFSwitch sw, + OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) { + packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx); + } + + public boolean blockHost(IOFSwitchService switchService, + SwitchPort sw_tup, MacAddress host_mac, short hardTimeout, U64 cookie) { + + if (sw_tup == null) { + return false; + } + + IOFSwitch sw = switchService.getSwitch(sw_tup.getNodeId()); + if (sw == null) { + return false; + } + + OFPort inputPort = sw_tup.getPortId(); + if (log.isDebugEnabled()) { + log.debug("blockHost sw={} port={} mac={}", + new Object[] { sw, sw_tup.getPortId(), host_mac.getLong() }); + } + + // Create flow-mod based on packet-in and src-switch + OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); + + Match.Builder mb = sw.getOFFactory().buildMatch(); + List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop + mb.setExact(MatchField.IN_PORT, inputPort); + if (host_mac.getLong() != -1L) { + mb.setExact(MatchField.ETH_SRC, host_mac); + } + + fmb.setCookie(cookie) + .setHardTimeout(hardTimeout) + .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT) + .setPriority(FLOWMOD_DEFAULT_PRIORITY) + .setBufferId(OFBufferId.NO_BUFFER) + .setMatch(mb.build()); + + FlowModUtils.setActions(fmb, actions, sw); + + if (log.isDebugEnabled()) { + log.debug("write drop flow-mod sw={} match={} flow-mod={}", + new Object[] { sw, mb.build(), fmb.build() }); + } + + messageDamper.write(sw, fmb.build()); + + return true; + } + + @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; + } } \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java index 047ffa06feb13953184c9ff40d0362b8ea66d196..7e53a5ad0180208facaad7226ddb3ea65256783a 100644 --- a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java +++ b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java @@ -16,7 +16,7 @@ package net.floodlightcontroller.util; -import java.io.IOException; +import java.util.Collection; import java.util.EnumSet; import java.util.Set; @@ -116,9 +116,8 @@ public class OFMessageDamper { * @param msg * @return true if the message was written to the switch, false if * the message was dampened. - * @throws IOException */ - public boolean write(IOFSwitch sw, OFMessage msg) throws IOException { + public boolean write(IOFSwitch sw, OFMessage msg) { if (!msgTypesToCache.contains(msg.getType())) { log.debug("Not dampening this type of msg {}", msg); sw.write(msg); @@ -136,4 +135,20 @@ public class OFMessageDamper { return true; } } -} + + /** + * Wrapper around {@link OFMessageDamper#write(IOFSwitch, OFMessage)}. + * @param sw + * @param msgs + * @return false if *any* message was dampened; true if no messages were dampened + */ + public boolean write(IOFSwitch sw, Collection<OFMessage> msgs) { + boolean allWritten = true; + for (OFMessage msg : msgs) { + if (!write(sw, msg)) { + allWritten = false; + } + } + return allWritten; + } +} \ No newline at end of file