Sahil Lele

ONOS-2379 Reactive Fwd improvements prune bad flows from switches when a link goes down

Change-Id: I27de61abc6225d12fd4ba5fa36d58ec4061f9db5
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.fwd; 16 package org.onosproject.fwd;
17 17
18 +import com.google.common.collect.ImmutableSet;
18 import org.apache.felix.scr.annotations.Activate; 19 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 20 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 21 import org.apache.felix.scr.annotations.Deactivate;
...@@ -29,35 +30,51 @@ import org.onlab.packet.IPv4; ...@@ -29,35 +30,51 @@ import org.onlab.packet.IPv4;
29 import org.onlab.packet.IPv6; 30 import org.onlab.packet.IPv6;
30 import org.onlab.packet.Ip4Prefix; 31 import org.onlab.packet.Ip4Prefix;
31 import org.onlab.packet.Ip6Prefix; 32 import org.onlab.packet.Ip6Prefix;
33 +import org.onlab.packet.MacAddress;
32 import org.onlab.packet.TCP; 34 import org.onlab.packet.TCP;
33 import org.onlab.packet.UDP; 35 import org.onlab.packet.UDP;
34 import org.onlab.packet.VlanId; 36 import org.onlab.packet.VlanId;
35 import org.onosproject.cfg.ComponentConfigService; 37 import org.onosproject.cfg.ComponentConfigService;
36 import org.onosproject.core.ApplicationId; 38 import org.onosproject.core.ApplicationId;
37 import org.onosproject.core.CoreService; 39 import org.onosproject.core.CoreService;
40 +import org.onosproject.event.Event;
41 +import org.onosproject.net.ConnectPoint;
42 +import org.onosproject.net.DeviceId;
38 import org.onosproject.net.Host; 43 import org.onosproject.net.Host;
39 import org.onosproject.net.HostId; 44 import org.onosproject.net.HostId;
45 +import org.onosproject.net.Link;
40 import org.onosproject.net.Path; 46 import org.onosproject.net.Path;
41 import org.onosproject.net.PortNumber; 47 import org.onosproject.net.PortNumber;
42 import org.onosproject.net.flow.DefaultTrafficSelector; 48 import org.onosproject.net.flow.DefaultTrafficSelector;
43 import org.onosproject.net.flow.DefaultTrafficTreatment; 49 import org.onosproject.net.flow.DefaultTrafficTreatment;
50 +import org.onosproject.net.flow.FlowEntry;
51 +import org.onosproject.net.flow.FlowRule;
44 import org.onosproject.net.flow.FlowRuleService; 52 import org.onosproject.net.flow.FlowRuleService;
45 import org.onosproject.net.flow.TrafficSelector; 53 import org.onosproject.net.flow.TrafficSelector;
46 import org.onosproject.net.flow.TrafficTreatment; 54 import org.onosproject.net.flow.TrafficTreatment;
55 +import org.onosproject.net.flow.criteria.Criterion;
56 +import org.onosproject.net.flow.criteria.EthCriterion;
57 +import org.onosproject.net.flow.instructions.Instruction;
58 +import org.onosproject.net.flow.instructions.Instructions;
47 import org.onosproject.net.flowobjective.DefaultForwardingObjective; 59 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
48 import org.onosproject.net.flowobjective.FlowObjectiveService; 60 import org.onosproject.net.flowobjective.FlowObjectiveService;
49 import org.onosproject.net.flowobjective.ForwardingObjective; 61 import org.onosproject.net.flowobjective.ForwardingObjective;
50 import org.onosproject.net.host.HostService; 62 import org.onosproject.net.host.HostService;
63 +import org.onosproject.net.link.LinkEvent;
51 import org.onosproject.net.packet.InboundPacket; 64 import org.onosproject.net.packet.InboundPacket;
52 import org.onosproject.net.packet.PacketContext; 65 import org.onosproject.net.packet.PacketContext;
53 import org.onosproject.net.packet.PacketPriority; 66 import org.onosproject.net.packet.PacketPriority;
54 import org.onosproject.net.packet.PacketProcessor; 67 import org.onosproject.net.packet.PacketProcessor;
55 import org.onosproject.net.packet.PacketService; 68 import org.onosproject.net.packet.PacketService;
69 +import org.onosproject.net.topology.TopologyEvent;
70 +import org.onosproject.net.topology.TopologyListener;
56 import org.onosproject.net.topology.TopologyService; 71 import org.onosproject.net.topology.TopologyService;
57 import org.osgi.service.component.ComponentContext; 72 import org.osgi.service.component.ComponentContext;
58 import org.slf4j.Logger; 73 import org.slf4j.Logger;
59 74
60 import java.util.Dictionary; 75 import java.util.Dictionary;
76 +import java.util.List;
77 +import java.util.Objects;
61 import java.util.Set; 78 import java.util.Set;
62 79
63 import static com.google.common.base.Strings.isNullOrEmpty; 80 import static com.google.common.base.Strings.isNullOrEmpty;
...@@ -155,16 +172,21 @@ public class ReactiveForwarding { ...@@ -155,16 +172,21 @@ public class ReactiveForwarding {
155 "default is false") 172 "default is false")
156 private boolean matchIcmpFields = false; 173 private boolean matchIcmpFields = false;
157 174
175 +
158 @Property(name = "ignoreIPv4Multicast", boolValue = false, 176 @Property(name = "ignoreIPv4Multicast", boolValue = false,
159 label = "Ignore (do not forward) IPv4 multicast packets; default is false") 177 label = "Ignore (do not forward) IPv4 multicast packets; default is false")
160 private boolean ignoreIpv4McastPackets = false; 178 private boolean ignoreIpv4McastPackets = false;
161 179
180 + private final TopologyListener topologyListener = new InternalTopologyListener();
181 +
182 +
162 @Activate 183 @Activate
163 public void activate(ComponentContext context) { 184 public void activate(ComponentContext context) {
164 cfgService.registerProperties(getClass()); 185 cfgService.registerProperties(getClass());
165 appId = coreService.registerApplication("org.onosproject.fwd"); 186 appId = coreService.registerApplication("org.onosproject.fwd");
166 187
167 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2); 188 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
189 + topologyService.addListener(topologyListener);
168 readComponentConfiguration(context); 190 readComponentConfiguration(context);
169 requestIntercepts(); 191 requestIntercepts();
170 192
...@@ -177,6 +199,7 @@ public class ReactiveForwarding { ...@@ -177,6 +199,7 @@ public class ReactiveForwarding {
177 withdrawIntercepts(); 199 withdrawIntercepts();
178 flowRuleService.removeFlowRulesById(appId); 200 flowRuleService.removeFlowRulesById(appId);
179 packetService.removeProcessor(processor); 201 packetService.removeProcessor(processor);
202 + topologyService.removeListener(topologyListener);
180 processor = null; 203 processor = null;
181 log.info("Stopped"); 204 log.info("Stopped");
182 } 205 }
...@@ -383,6 +406,7 @@ public class ReactiveForwarding { ...@@ -383,6 +406,7 @@ public class ReactiveForwarding {
383 public void process(PacketContext context) { 406 public void process(PacketContext context) {
384 // Stop processing if the packet has been handled, since we 407 // Stop processing if the packet has been handled, since we
385 // can't do any more to it. 408 // can't do any more to it.
409 +
386 if (context.isHandled()) { 410 if (context.isHandled()) {
387 return; 411 return;
388 } 412 }
...@@ -646,4 +670,161 @@ public class ReactiveForwarding { ...@@ -646,4 +670,161 @@ public class ReactiveForwarding {
646 packetOut(context, portNumber); 670 packetOut(context, portNumber);
647 } 671 }
648 } 672 }
673 +
674 + private class InternalTopologyListener implements TopologyListener {
675 + @Override
676 + public void event(TopologyEvent event) {
677 + List<Event> reasons = event.reasons();
678 + if (reasons != null) {
679 + reasons.forEach(re -> {
680 + if (re instanceof LinkEvent) {
681 + LinkEvent le = (LinkEvent) re;
682 + if (le.type() == LinkEvent.Type.LINK_REMOVED) {
683 + fixBlackhole(le.subject().src());
684 + }
685 + }
686 + });
687 + }
688 + }
689 + }
690 +
691 + private void fixBlackhole(ConnectPoint egress) {
692 + Set<FlowEntry> rules = getFlowRulesFrom(egress);
693 + Set<SrcDstPair> pairs = findSrcDstPairs(rules);
694 +
695 + for (SrcDstPair sd: pairs) {
696 + // get the edge deviceID for the src host
697 + DeviceId srcId = hostService.getHost(HostId.hostId(sd.src)).location().deviceId();
698 + DeviceId dstId = hostService.getHost(HostId.hostId(sd.dst)).location().deviceId();
699 + log.trace("SRC ID is " + srcId + ", DST ID is " + dstId);
700 +
701 + cleanFlowRules(sd, egress.deviceId());
702 +
703 + Set<Path> shortestPaths =
704 + topologyService.getPaths(topologyService.currentTopology(), egress.deviceId(), srcId);
705 + backTrackBadNodes(shortestPaths, dstId, sd);
706 + }
707 + }
708 +
709 + // Backtracks from link down event to remove flows that lead to blackhole
710 + private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
711 + for (Path p: shortestPaths) {
712 + List<Link> pathLinks = p.links();
713 + for (int i = 0; i < pathLinks.size(); i = i + 1) {
714 + Link curLink = pathLinks.get(i);
715 + DeviceId curDevice = curLink.src().deviceId();
716 + log.trace("Currently inspecting device: " + curDevice);
717 +
718 + // skipping the first link because this link's src has already been pruned beforehand
719 + if (i != 0) {
720 + cleanFlowRules(sd, curDevice);
721 + }
722 +
723 + Set<Path> pathsFromCurDevice = topologyService.getPaths(topologyService.currentTopology(),
724 + curDevice, dstId);
725 + if (pickForwardPath(pathsFromCurDevice, curLink.src().port()) != null) {
726 + break;
727 + } else {
728 + if (i + 1 == pathLinks.size()) {
729 + cleanFlowRules(sd, curLink.dst().deviceId());
730 + }
731 + }
732 + }
733 + }
734 + }
735 +
736 + // Removes flow rules off specified device with specific SrcDstPair
737 + private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
738 + log.trace("Searching for flow rules to remove from: " + id);
739 + log.trace("Removing flows w/ SRC=" + pair.src + ", DST=" + pair.dst);
740 + for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
741 + boolean matchesSrc = false, matchesDst = false;
742 + for (Instruction i : r.treatment().allInstructions()) {
743 + if (i.type() == Instruction.Type.OUTPUT) {
744 + //if the flow has matching src and dst
745 + for (Criterion cr : r.selector().criteria()) {
746 + if (cr.type() == Criterion.Type.ETH_DST) {
747 + if (((EthCriterion) cr).mac().equals(pair.dst)) {
748 + matchesDst = true;
749 + }
750 + } else if (cr.type() == Criterion.Type.ETH_SRC) {
751 + if (((EthCriterion) cr).mac().equals(pair.src)) {
752 + matchesSrc = true;
753 + }
754 + }
755 + }
756 + }
757 + }
758 + if (matchesDst && matchesSrc) {
759 + log.trace("Removed flow rule from device: " + id);
760 + flowRuleService.removeFlowRules((FlowRule) r);
761 + }
762 + }
763 +
764 + }
765 +
766 + // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
767 + private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
768 + ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
769 + for (FlowEntry r: rules) {
770 + MacAddress src = null, dst = null;
771 + for (Criterion cr: r.selector().criteria()) {
772 + if (cr.type() == Criterion.Type.ETH_DST) {
773 + dst = ((EthCriterion) cr).mac();
774 + } else if (cr.type() == Criterion.Type.ETH_SRC) {
775 + src = ((EthCriterion) cr).mac();
776 + }
777 + }
778 + builder.add(new SrcDstPair(src, dst));
779 + }
780 + return builder.build();
781 + }
782 +
783 + // Returns set of flowEntries which were created by this application and which egress from the
784 + // specified connection port
785 + private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
786 + ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
787 + flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
788 + if (r.appId() == appId.id()) {
789 + r.treatment().allInstructions().forEach(i -> {
790 + if (i.type() == Instruction.Type.OUTPUT) {
791 + if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
792 + builder.add(r);
793 + }
794 + }
795 + });
796 + }
797 + });
798 +
799 + return builder.build();
800 + }
801 +
802 + // Wrapper class for a source and destination pair of MAC addresses
803 + private final class SrcDstPair {
804 + final MacAddress src;
805 + final MacAddress dst;
806 +
807 + private SrcDstPair(MacAddress src, MacAddress dst) {
808 + this.src = src;
809 + this.dst = dst;
810 + }
811 +
812 + @Override
813 + public boolean equals(Object o) {
814 + if (this == o) {
815 + return true;
816 + }
817 + if (o == null || getClass() != o.getClass()) {
818 + return false;
819 + }
820 + SrcDstPair that = (SrcDstPair) o;
821 + return Objects.equals(src, that.src) &&
822 + Objects.equals(dst, that.dst);
823 + }
824 +
825 + @Override
826 + public int hashCode() {
827 + return Objects.hash(src, dst);
828 + }
829 + }
649 } 830 }
......