ONOS-2379 Reactive Fwd improvements prune bad flows from switches when a link goes down
Change-Id: I27de61abc6225d12fd4ba5fa36d58ec4061f9db5
Showing
1 changed file
with
181 additions
and
0 deletions
... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment